‪TYPO3CMS  9.5
DocumentationFile.php
Go to the documentation of this file.
1 <?php
2 declare(strict_types = 1);
3 
5 
6 /*
7  * This file is part of the TYPO3 CMS project.
8  *
9  * It is free software; you can redistribute it and/or modify it under
10  * the terms of the GNU General Public License, either version 2
11  * of the License, or any later version.
12  *
13  * For the full copyright and license information, please read the
14  * LICENSE.txt file that was distributed with this source code.
15  *
16  * The TYPO3 project - inspiring people to share!
17  */
18 
19 use Symfony\Component\Finder\Finder;
20 use Symfony\Component\Finder\SplFileInfo;
25 
31 {
35  protected ‪$registry;
36 
40  protected ‪$tagsTotal = [];
41 
48  protected ‪$changelogPath = '';
49 
55  public function ‪__construct(‪Registry ‪$registry = null, $changelogDir = '')
56  {
57  $this->registry = ‪$registry;
58  if ($this->registry === null) {
59  $this->registry = new ‪Registry();
60  }
61  $this->changelogPath = $changelogDir !== '' ? $changelogDir : realpath(‪ExtensionManagementUtility::extPath('core') . 'Documentation/Changelog');
62  $this->changelogPath = str_replace('\\', '/', $this->changelogPath);
63  }
64 
72  public function ‪findDocumentationDirectories(string $path): array
73  {
74  if (strcasecmp($path, $this->changelogPath) < 0 || strpos($path, $this->changelogPath) === false) {
75  throw new \InvalidArgumentException('the given path does not belong to the changelog dir. Aborting', 1537158043);
76  }
77 
78  ‪$finder = new Finder();
80  ->depth(0)
81  ->sortByName()
82  ->in($path);
83 
84  $directories = [];
85  foreach (‪$finder->directories() as $directory) {
87  $directories[] = $directory->getBasename();
88  }
89 
90  return $directories;
91  }
92 
100  public function ‪findDocumentationFiles(string $path): array
101  {
102  if (strcasecmp($path, $this->changelogPath) < 0 || strpos($path, $this->changelogPath) === false) {
103  throw new \InvalidArgumentException('the given path does not belong to the changelog dir. Aborting', 1485425530);
104  }
105 
106  $documentationFiles = $this->‪getDocumentationFilesForVersion($path);
107  $this->tagsTotal = $this->‪collectTagTotal($documentationFiles);
108 
109  return $documentationFiles;
110  }
111 
119  public function ‪getListEntry(string $file): array
120  {
121  if (strcasecmp($file, $this->changelogPath) < 0 || strpos($file, $this->changelogPath) === false) {
122  throw new \InvalidArgumentException('the given file does not belong to the changelog dir. Aborting', 1485425531);
123  }
124  $lines = file($file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
125  $headline = $this->‪extractHeadline($lines);
126  $entry['version'] = ‪PathUtility::basename(‪PathUtility::dirname($file));
127  $entry['headline'] = $headline;
128  $entry['filepath'] = $file;
129  $entry['filename'] = pathinfo($file)['filename'];
130  $entry['tags'] = $this->‪extractTags($lines);
131  $entry['class'] = 'default';
132  foreach ($entry['tags'] as $key => $tag) {
133  if (strpos($tag, 'cat:') === 0) {
134  $substr = substr($tag, 4);
135  $entry['class'] = strtolower($substr);
136  $entry['tags'][$key] = $substr;
137  }
138  }
139  $entry['tagList'] = implode(',', $entry['tags']);
140  $entry['content'] = file_get_contents($file);
141  $entry['parsedContent'] = $this->‪parseContent($entry['content']);
142  $entry['file_hash'] = md5($entry['content']);
143  if ($entry['version'] !== '') {
144  $entry['url']['documentation'] = sprintf(
145  'https://docs.typo3.org/c/typo3/cms-core/master/en-us/Changelog/%s/%s.html',
146  $entry['version'],
147  $entry['filename']
148  );
149  }
150  $issueId = $this->‪parseIssueId($entry['filename']);
151  if ($issueId) {
152  $entry['url']['issue'] = sprintf('https://forge.typo3.org/issues/%s', $issueId);
153  }
154 
155  return [md5($file) => $entry];
156  }
157 
164  protected function ‪isRelevantFile(array $fileInfo): bool
165  {
166  $isRelevantFile = $fileInfo['extension'] === 'rst' && $fileInfo['filename'] !== 'Index';
167  // file might be ignored by users choice
168  if ($isRelevantFile && $this->‪isFileIgnoredByUsersChoice($fileInfo['basename'])) {
169  $isRelevantFile = false;
170  }
171 
172  return $isRelevantFile;
173  }
174 
181  protected function ‪extractTags(array $file): array
182  {
183  $tags = $this->‪extractTagsFromFile($file);
184  // Headline starting with the category like Breaking, Important or Feature
185  $tags[] = $this->‪extractCategoryFromHeadline($file);
186  natcasesort($tags);
187 
188  return $tags;
189  }
190 
198  protected function ‪extractTagsFromFile(array $file): array
199  {
200  foreach ($file as $line) {
201  if (strpos($line, '.. index::') === 0) {
202  $tagString = substr($line, strlen('.. index:: '));
203  return GeneralUtility::trimExplode(',', $tagString, true);
204  }
205  }
206 
207  return [];
208  }
209 
218  protected function ‪extractCategoryFromHeadline(array $lines): string
219  {
220  $headline = $this->‪extractHeadline($lines);
221  if (strpos($headline, ':') !== false) {
222  return 'cat:' . substr($headline, 0, strpos($headline, ':'));
223  }
224 
225  return '';
226  }
227 
234  protected function ‪extractHeadline(array $lines): string
235  {
236  $index = 0;
237  while (strpos($lines[$index], '..') === 0 || strpos($lines[$index], '==') === 0) {
238  $index++;
239  }
240  return trim($lines[$index]);
241  }
242 
248  protected function ‪versionHasDocumentationFiles(string $docDirectory, string $version): bool
249  {
250  $absolutePath = str_replace('\\', '/', $docDirectory) . '/' . $version;
251  ‪$finder = $this->‪getDocumentFinder()->in($absolutePath);
252 
253  return ‪$finder->files()->count() > 0;
254  }
255 
262  protected function ‪getDocumentationFilesForVersion(string $docDirectory): array
263  {
264  $documentationFiles = [[]];
265  $absolutePath = str_replace('\\', '/', $docDirectory);
266  ‪$finder = $this->‪getDocumentFinder()->in($absolutePath);
267 
268  foreach (‪$finder->files() as $file) {
270  $documentationFiles[] = $this->‪getListEntry($file->getPathname());
271  }
272 
273  return array_merge(...$documentationFiles);
274  }
275 
282  protected function ‪collectTagTotal($documentationFiles): array
283  {
284  $tags = [[]];
285  foreach ($documentationFiles as $fileArray) {
286  $tags[] = $fileArray['tags'];
287  }
288 
289  return array_unique(array_merge(...$tags));
290  }
291 
297  public function ‪getTagsTotal(): array
298  {
299  return ‪$this->tagsTotal;
300  }
301 
308  protected function ‪isFileIgnoredByUsersChoice(string $filename): bool
309  {
310  $isFileIgnoredByUsersChoice = false;
311 
312  $ignoredFiles = $this->registry->get('upgradeAnalysisIgnoreFilter', 'ignoredDocumentationFiles');
313  if (is_array($ignoredFiles)) {
314  foreach ($ignoredFiles as $filePath) {
315  if ($filePath !== null && strlen($filePath) > 0) {
316  if (strpos($filePath, $filename) !== false) {
317  $isFileIgnoredByUsersChoice = true;
318  break;
319  }
320  }
321  }
322  }
323  return $isFileIgnoredByUsersChoice;
324  }
325 
331  protected function ‪parseContent(string $rstContent): string
332  {
333  $content = htmlspecialchars($rstContent);
334  $content = preg_replace('/:issue:`([\d]*)`/', '<a href="https://forge.typo3.org/issues/\\1" target="_blank" rel="noopener noreferrer">\\1</a>', $content);
335  $content = preg_replace('/#([\d]*)/', '#<a href="https://forge.typo3.org/issues/\\1" target="_blank" rel="noopener noreferrer">\\1</a>', $content);
336  $content = preg_replace('/(\n([=]*)\n(.*)\n([=]*)\n)/', '', $content, 1);
337  $content = preg_replace('/.. index::(.*)/', '', $content);
338  $content = preg_replace('/.. include::(.*)/', '', $content);
339  return trim($content);
340  }
341 
347  protected function ‪parseIssueId(string $filename): ?string
348  {
349  return GeneralUtility::trimExplode('-', $filename)[1] ?? null;
350  }
351 
355  protected function ‪getDocumentFinder(): Finder
356  {
357  ‪$finder = new Finder();
358  ‪$finder
359  ->depth(0)
360  ->sortByName()
361  ->name('/^(Feature|Breaking|Deprecation|Important)\-\d+.+\.rst$/i');
362 
363  return ‪$finder;
364  }
365 }
‪TYPO3\CMS\Core\Utility\PathUtility
Definition: PathUtility.php:23
‪TYPO3\CMS\Install\UpgradeAnalysis
Definition: DocumentationFile.php:4
‪TYPO3\CMS\Install\UpgradeAnalysis\DocumentationFile
Definition: DocumentationFile.php:31
‪TYPO3\CMS\Core\Utility\PathUtility\dirname
‪static string dirname($path)
Definition: PathUtility.php:185
‪TYPO3\CMS\Install\UpgradeAnalysis\DocumentationFile\extractTagsFromFile
‪array extractTagsFromFile(array $file)
Definition: DocumentationFile.php:195
‪$finder
‪$finder
Definition: annotationChecker.php:102
‪TYPO3\CMS\Core\Registry
Definition: Registry.php:32
‪TYPO3\CMS\Install\UpgradeAnalysis\DocumentationFile\isFileIgnoredByUsersChoice
‪bool isFileIgnoredByUsersChoice(string $filename)
Definition: DocumentationFile.php:305
‪TYPO3\CMS\Install\UpgradeAnalysis\DocumentationFile\versionHasDocumentationFiles
‪bool versionHasDocumentationFiles(string $docDirectory, string $version)
Definition: DocumentationFile.php:245
‪TYPO3\CMS\Install\UpgradeAnalysis\DocumentationFile\extractHeadline
‪string extractHeadline(array $lines)
Definition: DocumentationFile.php:231
‪TYPO3\CMS\Install\UpgradeAnalysis\DocumentationFile\$changelogPath
‪string $changelogPath
Definition: DocumentationFile.php:45
‪TYPO3\CMS\Install\UpgradeAnalysis\DocumentationFile\parseIssueId
‪string null parseIssueId(string $filename)
Definition: DocumentationFile.php:344
‪TYPO3\CMS\Install\UpgradeAnalysis\DocumentationFile\getTagsTotal
‪array getTagsTotal()
Definition: DocumentationFile.php:294
‪TYPO3\CMS\Core\Utility\PathUtility\basename
‪static string basename($path)
Definition: PathUtility.php:164
‪TYPO3\CMS\Install\UpgradeAnalysis\DocumentationFile\__construct
‪__construct(Registry $registry=null, $changelogDir='')
Definition: DocumentationFile.php:52
‪TYPO3\CMS\Install\UpgradeAnalysis\DocumentationFile\getDocumentFinder
‪Finder getDocumentFinder()
Definition: DocumentationFile.php:352
‪TYPO3\CMS\Install\UpgradeAnalysis\DocumentationFile\findDocumentationFiles
‪array findDocumentationFiles(string $path)
Definition: DocumentationFile.php:97
‪TYPO3\CMS\Core\Utility\ExtensionManagementUtility
Definition: ExtensionManagementUtility.php:36
‪TYPO3\CMS\Install\UpgradeAnalysis\DocumentationFile\extractCategoryFromHeadline
‪string extractCategoryFromHeadline(array $lines)
Definition: DocumentationFile.php:215
‪TYPO3\CMS\Install\UpgradeAnalysis\DocumentationFile\getDocumentationFilesForVersion
‪array getDocumentationFilesForVersion(string $docDirectory)
Definition: DocumentationFile.php:259
‪TYPO3\CMS\Install\UpgradeAnalysis\DocumentationFile\$registry
‪Registry $registry
Definition: DocumentationFile.php:34
‪TYPO3\CMS\Install\UpgradeAnalysis\DocumentationFile\parseContent
‪string parseContent(string $rstContent)
Definition: DocumentationFile.php:328
‪TYPO3\CMS\Install\UpgradeAnalysis\DocumentationFile\collectTagTotal
‪array collectTagTotal($documentationFiles)
Definition: DocumentationFile.php:279
‪TYPO3\CMS\Core\Utility\ExtensionManagementUtility\extPath
‪static string extPath($key, $script='')
Definition: ExtensionManagementUtility.php:149
‪TYPO3\CMS\Install\UpgradeAnalysis\DocumentationFile\isRelevantFile
‪bool isRelevantFile(array $fileInfo)
Definition: DocumentationFile.php:161
‪TYPO3\CMS\Install\UpgradeAnalysis\DocumentationFile\extractTags
‪array extractTags(array $file)
Definition: DocumentationFile.php:178
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:45
‪TYPO3\CMS\Install\UpgradeAnalysis\DocumentationFile\$tagsTotal
‪array $tagsTotal
Definition: DocumentationFile.php:38
‪TYPO3\CMS\Install\UpgradeAnalysis\DocumentationFile\getListEntry
‪array getListEntry(string $file)
Definition: DocumentationFile.php:116
‪TYPO3\CMS\Install\UpgradeAnalysis\DocumentationFile\findDocumentationDirectories
‪string[] findDocumentationDirectories(string $path)
Definition: DocumentationFile.php:69