TYPO3 CMS  TYPO3_6-2
DocumentationService.php
Go to the documentation of this file.
1 <?php
3 
17 use \TYPO3\CMS\Core\Utility\GeneralUtility;
18 
25 
31  public function getOfficialDocuments() {
32  $documents = array();
33 
34  $json = GeneralUtility::getUrl('https://docs.typo3.org/typo3cms/documents.json');
35  if ($json) {
36  $documents = json_decode($json, TRUE);
37  foreach ($documents as &$document) {
38  $document['icon'] = \TYPO3\CMS\Documentation\Utility\MiscUtility::getIcon($document['key']);
39  }
40 
41  // Cache file locally to be able to create a composer.json file when fetching a document
42  $absoluteCacheFilename = GeneralUtility::getFileAbsFileName('typo3temp/documents.json');
43  GeneralUtility::writeFile($absoluteCacheFilename, $json);
44  }
45  return $documents;
46  }
47 
53  public function getLocalExtensions() {
54  $documents = array();
55 
56  foreach ($GLOBALS['TYPO3_LOADED_EXT'] as $extensionKey => $extensionData) {
57  $absoluteExtensionPath = GeneralUtility::getFileAbsFileName($extensionData['siteRelPath']);
58  if (is_file($absoluteExtensionPath . 'README.rst') || is_file($absoluteExtensionPath . 'Documentation' . DIRECTORY_SEPARATOR . 'Index.rst')) {
60  if ($extensionData['type'] === 'S') {
61  $version = TYPO3_branch;
62  } else {
63  $version = substr($metadata['release'], -4) === '-dev' ? 'latest' : $metadata['release'];
64  }
65 
66  $documentKey = 'typo3cms.extensions.' . $extensionKey;
67  $documents[] = array(
68  'title' => $metadata['title'],
69  'icon' => \TYPO3\CMS\Documentation\Utility\MiscUtility::getIcon($documentKey),
70  'type' => 'Extension',
71  'key' => $documentKey,
72  'shortcut' => $extensionKey,
73  'url' => 'https://docs.typo3.org/typo3cms/extensions/' . $extensionKey . '/',
74  'version' => $version,
75  );
76  }
77  }
78 
79  return $documents;
80  }
81 
98  public function fetchNearestDocument($url, $key, $version = 'latest', $language = 'default') {
99  // In case we could not find a working combination
100  $success = FALSE;
101 
102  $packages = $this->getAvailablePackages($url);
103  if (count($packages) == 0) {
104  return $success;
105  }
106 
107  $languages = array($language);
108  if ($language !== 'default') {
109  $languages[] = 'default';
110  }
111  foreach ($languages as $language) {
112  // Step 1)
113  if (isset($packages[$version][$language])) {
114  $success |= $this->fetchDocument($url, $key, $version, $language);
115  // Fetch next language
116  continue;
117  } else {
118  foreach ($packages[$version] as $locale => $_) {
119  if (GeneralUtility::isFirstPartOfStr($locale, $language)) {
120  $success |= $this->fetchDocument($url, $key, $version, $locale);
121  // Fetch next language (jump current foreach up to the loop of $languages)
122  continue 2;
123  }
124  }
125  }
126  // Step 2)
127  if (preg_match('/^(\d+\.\d+)\.\d+$/', $version, $matches)) {
128  // Instead of a 3-digit version, try to get it on 2 digits
129  $shortVersion = $matches[1];
130  if (isset($packages[$shortVersion][$language])) {
131  $success |= $this->fetchDocument($url, $key, $shortVersion, $language);
132  // Fetch next language
133  continue;
134  }
135  }
136  // Step 3)
137  if ($version !== 'latest' && isset($packages['latest'][$language])) {
138  $success |= $this->fetchDocument($url, $key, 'latest', $language);
139  // Fetch next language
140  continue;
141  }
142  }
143 
144  return $success;
145  }
146 
156  public function fetchDocument($url, $key, $version = 'latest', $language = 'default') {
157  $result = FALSE;
158  $url = rtrim($url, '/') . '/';
159 
160  $packagePrefix = substr($key, strrpos($key, '.') + 1);
161  $languageSegment = str_replace('_', '-', strtolower($language));
162  $packageName = sprintf('%s-%s-%s.zip', $packagePrefix, $version, $languageSegment);
163  $packageUrl = $url . 'packages/' . $packageName;
164  $absolutePathToZipFile = GeneralUtility::getFileAbsFileName('typo3temp/' . $packageName);
165 
166  $packages = $this->getAvailablePackages($url);
167  if (count($packages) == 0 || !isset($packages[$version][$language])) {
168  return FALSE;
169  }
170 
171  // Check if a local version of the package is already present
172  $hasArchive = FALSE;
173  if (is_file($absolutePathToZipFile)) {
174  $localMd5 = md5_file($absolutePathToZipFile);
175  $remoteMd5 = $packages[$version][$language];
176  $hasArchive = $localMd5 === $remoteMd5;
177  }
178 
179  if (!$hasArchive) {
180  $content = GeneralUtility::getUrl($packageUrl);
181  if (!empty($content)) {
182  GeneralUtility::writeFile($absolutePathToZipFile, $content);
183  }
184  }
185 
186  if (is_file($absolutePathToZipFile)) {
187  $absoluteDocumentPath = GeneralUtility::getFileAbsFileName('typo3conf/Documentation/');
188 
189  $result = $this->unzipDocumentPackage($absolutePathToZipFile, $absoluteDocumentPath);
190 
191  // Create a composer.json file
192  $absoluteCacheFilename = GeneralUtility::getFileAbsFileName('typo3temp/documents.json');
193  $documents = json_decode(file_get_contents($absoluteCacheFilename), TRUE);
194  foreach ($documents as $document) {
195  if ($document['key'] === $key) {
196  $composerData = array(
197  'name' => $document['title'],
198  'type' => 'documentation',
199  'description' => 'TYPO3 ' . $document['type'],
200  );
201  $relativeComposerFilename = $key . '/' . $language . '/composer.json';
202  $absoluteComposerFilename = GeneralUtility::getFileAbsFileName('typo3conf/Documentation/' . $relativeComposerFilename);
203  GeneralUtility::writeFile($absoluteComposerFilename, json_encode($composerData));
204  break;
205  }
206  }
207  }
208 
209  return $result;
210  }
211 
219  protected function getAvailablePackages($url) {
220  $packages = array();
221  $url = rtrim($url, '/') . '/';
222  $indexUrl = $url . 'packages/packages.xml';
223 
224  $remote = GeneralUtility::getUrl($indexUrl);
225  if ($remote) {
226  $packages = $this->parsePackagesXML($remote);
227  }
228 
229  return $packages;
230  }
231 
239  protected function parsePackagesXML($string) {
240  // Disables the functionality to allow external entities to be loaded when parsing the XML, must be kept
241  $previousValueOfEntityLoader = libxml_disable_entity_loader(TRUE);
242  $data = json_decode(json_encode((array)simplexml_load_string($string)), TRUE);
243  libxml_disable_entity_loader($previousValueOfEntityLoader);
244  if (count($data) != 2) {
245  throw new \TYPO3\CMS\Documentation\Exception\XmlParser('Error in XML parser while decoding packages XML file.', 1374222437);
246  }
247 
248  // SimpleXML does not properly handle arrays with only 1 item
249  if ($data['languagePackIndex']['languagepack'][0] === NULL) {
250  $data['languagePackIndex']['languagepack'] = array($data['languagePackIndex']['languagepack']);
251  }
252 
253  $packages = array();
254  foreach ($data['languagePackIndex']['languagepack'] as $languagePack) {
255  $language = $languagePack['@attributes']['language'];
256  $version = $languagePack['@attributes']['version'];
257  $packages[$version][$language] = $languagePack['md5'];
258  }
259 
260  return $packages;
261  }
262 
271  protected function unzipDocumentPackage($file, $path) {
272  $zip = zip_open($file);
273  if (is_resource($zip)) {
274  $result = TRUE;
275 
276  if (!is_dir($path)) {
278  }
279 
280  while (($zipEntry = zip_read($zip)) !== FALSE) {
281  $zipEntryName = zip_entry_name($zipEntry);
282  if (strpos($zipEntryName, '/') !== FALSE) {
283  $zipEntryPathSegments = explode('/', $zipEntryName);
284  $fileName = array_pop($zipEntryPathSegments);
285  // It is a folder, because the last segment is empty, let's create it
286  if (empty($fileName)) {
287  GeneralUtility::mkdir_deep($path, implode('/', $zipEntryPathSegments));
288  } else {
289  $absoluteTargetPath = GeneralUtility::getFileAbsFileName($path . implode('/', $zipEntryPathSegments) . '/' . $fileName);
290  if (strlen(trim($absoluteTargetPath)) > 0) {
291  $return = GeneralUtility::writeFile(
292  $absoluteTargetPath, zip_entry_read($zipEntry, zip_entry_filesize($zipEntry))
293  );
294  if ($return === FALSE) {
295  throw new \TYPO3\CMS\Documentation\Exception\Document('Could not write file ' . $zipEntryName, 1374161546);
296  }
297  } else {
298  throw new \TYPO3\CMS\Documentation\Exception\Document('Could not write file ' . $zipEntryName, 1374161532);
299  }
300  }
301  } else {
302  throw new \TYPO3\CMS\Documentation\Exception\Document('Extension directory missing in zip file!', 1374161519);
303  }
304  }
305  } else {
306  throw new \TYPO3\CMS\Documentation\Exception\Document('Unable to open zip file ' . $file, 1374161508);
307  }
308 
309  return $result;
310  }
311 
312 }
fetchDocument($url, $key, $version='latest', $language='default')
static mkdir_deep($directory, $deepDirectory='')
static writeFile($file, $content, $changePermissions=FALSE)
static isFirstPartOfStr($str, $partStr)
$remote
Definition: server.php:68
if($list_of_literals) if(!empty($literals)) if(!empty($literals)) $result
Analyse literals to prepend the N char to them if their contents aren&#39;t numeric.
static getUrl($url, $includeHeader=0, $requestHeaders=FALSE, &$report=NULL)
fetchNearestDocument($url, $key, $version='latest', $language='default')
if(!defined('TYPO3_MODE')) $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauth.php']['logoff_pre_processing'][]
static getFileAbsFileName($filename, $onlyRelative=TRUE, $relToTYPO3_mainDir=FALSE)