‪TYPO3CMS  ‪main
InstallStatusReport.php
Go to the documentation of this file.
1 <?php
2 
3 /*
4  * This file is part of the TYPO3 CMS project.
5  *
6  * It is free software; you can redistribute it and/or modify it under
7  * the terms of the GNU General Public License, either version 2
8  * of the License, or any later version.
9  *
10  * For the full copyright and license information, please read the
11  * LICENSE.txt file that was distributed with this source code.
12  *
13  * The TYPO3 project - inspiring people to share!
14  */
15 
17 
24 use TYPO3\CMS\Install\Service\CoreVersionService;
29 
35 {
36  private const ‪WRAP_FLAT = 1;
37  private const ‪WRAP_NESTED = 2;
38 
39  public function ‪__construct(private readonly ‪UpgradeWizardsService $upgradeWizardsService) {}
40 
46  public function ‪getStatus(): array
47  {
48  return [
49  'FileSystem' => $this->‪getFileSystemStatus(),
50  'RemainingUpdates' => $this->‪getRemainingUpdatesStatus(),
51  'NewVersion' => $this->‪getNewVersionStatus(),
52  ];
53  }
54 
55  public function ‪getLabel(): string
56  {
57  return 'typo3';
58  }
59 
65  private function ‪getFileSystemStatus(): ‪Status
66  {
67  $languageService = $this->‪getLanguageService();
68  $value = $languageService->sL('LLL:EXT:install/Resources/Private/Language/Report/locallang.xlf:status_writable');
69  $message = '';
70  $severity = ContextualFeedbackSeverity::OK;
71  // Requirement level
72  // -1 = not required, but if it exists may be writable or not
73  // 0 = not required, if it exists the dir should be writable
74  // 1 = required, doesn't have to be writable
75  // 2 = required, has to be writable
76  $varPath = ‪Environment::getVarPath();
77  $sitePath = ‪Environment::getPublicPath();
78  $rootPath = ‪Environment::getProjectPath();
79  $checkWritable = [
80  $sitePath . '/typo3temp/' => 2,
81  $sitePath . '/typo3temp/assets/' => 2,
82  $sitePath . '/typo3temp/assets/compressed/' => 2,
83  // only needed when GraphicalFunctions is used
84  $sitePath . '/typo3temp/assets/images/' => 0,
85  // used in PageGenerator (inlineStyle2Temp) and Backend + Language JS files
86  $sitePath . '/typo3temp/assets/css/' => 2,
87  $sitePath . '/typo3temp/assets/js/' => 2,
88  // fallback storage of FAL
89  $sitePath . '/typo3temp/assets/_processed_/' => 0,
90  $varPath => 2,
91  $varPath . '/transient/' => 2,
92  $varPath . '/charset/' => 2,
93  $varPath . '/lock/' => 2,
94  $sitePath . '/typo3conf/' => 2,
96  $sitePath . '/' . ‪$GLOBALS['TYPO3_CONF_VARS']['BE']['fileadminDir'] => -1,
97  $sitePath . '/' . ‪$GLOBALS['TYPO3_CONF_VARS']['BE']['fileadminDir'] . '_temp_/' => 0,
98  ];
99 
100  // Check for writable extension folder files in non-composer mode only
102  $checkWritable[‪Environment::getExtensionsPath()] = 0;
103  }
104 
105  foreach ($checkWritable as $path => $requirementLevel) {
106  $relPath = substr($path, strlen($rootPath) + 1);
107  if (!@is_dir($path)) {
108  // If the directory is missing, try to create it
110  }
111  if (!@is_dir($path)) {
112  if ($requirementLevel > 0) {
113  // directory is required
114  $value = $languageService->sL('LLL:EXT:install/Resources/Private/Language/Report/locallang.xlf:status_missingDirectory');
115  $message .= sprintf($languageService->sL('LLL:EXT:install/Resources/Private/Language/Report/locallang.xlf:status_directoryDoesNotExistCouldNotCreate'), $relPath) . '<br />';
116  $severity = ContextualFeedbackSeverity::ERROR;
117  } else {
118  $message .= sprintf($languageService->sL('LLL:EXT:install/Resources/Private/Language/Report/locallang.xlf:status_directoryDoesNotExist'), $relPath);
119  if ($requirementLevel == 0) {
120  $message .= ' ' . $languageService->sL('LLL:EXT:install/Resources/Private/Language/Report/locallang.xlf:status_directoryShouldAlsoBeWritable');
121  }
122  $message .= '<br />';
123  if ($severity->value < ContextualFeedbackSeverity::WARNING->value) {
124  $value = $languageService->sL('LLL:EXT:install/Resources/Private/Language/Report/locallang.xlf:status_nonExistingDirectory');
125  $severity = ContextualFeedbackSeverity::WARNING;
126  }
127  }
128  } else {
129  if (!is_writable($path)) {
130  switch ($requirementLevel) {
131  case 0:
132  $message .= sprintf(
133  $languageService->sL('LLL:EXT:install/Resources/Private/Language/Report/locallang.xlf:status_directoryShouldBeWritable'),
134  $path
135  ) . '<br />';
136  if ($severity->value < ContextualFeedbackSeverity::WARNING->value) {
137  $value = $languageService->sL('LLL:EXT:install/Resources/Private/Language/Report/locallang.xlf:status_recommendedWritableDirectory');
138  $severity = ContextualFeedbackSeverity::WARNING;
139  }
140  break;
141  case 2:
142  $value = $languageService->sL('LLL:EXT:install/Resources/Private/Language/Report/locallang.xlf:status_requiredWritableDirectory');
143  $message .= sprintf(
144  $languageService->sL('LLL:EXT:install/Resources/Private/Language/Report/locallang.xlf:status_directoryMustBeWritable'),
145  $path
146  ) . '<br />';
147  $severity = ContextualFeedbackSeverity::ERROR;
148  break;
149  default:
150  }
151  }
152  }
153  }
154  return GeneralUtility::makeInstance(Status::class, $languageService->sL('LLL:EXT:install/Resources/Private/Language/Report/locallang.xlf:status_fileSystem'), $value, $message, $severity);
155  }
156 
163  protected function ‪getIncompleteWizards(): array
164  {
165  $incompleteWizards = $this->upgradeWizardsService->getUpgradeWizardsList();
166  $incompleteWizards = array_filter(
167  $incompleteWizards,
168  static function ($wizard) {
169  return $wizard['shouldRenderWizard'];
170  }
171  );
172  return $incompleteWizards;
173  }
174 
181  {
182  $languageService = $this->‪getLanguageService();
183  $value = $languageService->sL('LLL:EXT:reports/Resources/Private/Language/locallang_reports.xlf:status_updateComplete');
184  $message = '';
185  $severity = ContextualFeedbackSeverity::OK;
186  $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
187  // check if there are update wizards left to perform
188  $incompleteWizards = $this->‪getIncompleteWizards();
189  if (count($incompleteWizards)) {
190  // At least one incomplete wizard was found
191  $value = $languageService->sL('LLL:EXT:reports/Resources/Private/Language/locallang_reports.xlf:status_updateIncomplete');
192  $severity = ContextualFeedbackSeverity::WARNING;
193  ‪$url = (string)$uriBuilder->buildUriFromRoute('tools_toolsupgrade');
194  $message = sprintf($languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:warning.install_update'), '<a href="' . htmlspecialchars(‪$url) . '">', '</a>');
195  }
196 
197  return GeneralUtility::makeInstance(Status::class, $languageService->sL('LLL:EXT:install/Resources/Private/Language/Report/locallang.xlf:status_remainingUpdates'), $value, $message, $severity);
198  }
199 
205  private function ‪getNewVersionStatus(): ‪Status
206  {
207  $typoVersion = GeneralUtility::makeInstance(Typo3Version::class);
208  $languageService = $this->‪getLanguageService();
209  $coreVersionService = GeneralUtility::makeInstance(CoreVersionService::class);
210 
211  // No updates for development versions
212  if (!$coreVersionService->isInstalledVersionAReleasedVersion()) {
213  return GeneralUtility::makeInstance(Status::class, 'TYPO3', $typoVersion->getVersion(), $languageService->sL('LLL:EXT:install/Resources/Private/Language/Report/locallang.xlf:status_isDevelopmentVersion'), ContextualFeedbackSeverity::NOTICE);
214  }
215 
216  try {
217  $versionMaintenanceWindow = $coreVersionService->getMaintenanceWindow();
218  } catch (‪RemoteFetchException $remoteFetchException) {
219  return GeneralUtility::makeInstance(
220  Status::class,
221  'TYPO3',
222  $typoVersion->getVersion(),
223  $languageService->sL(
224  'LLL:EXT:install/Resources/Private/Language/Report/locallang.xlf:status_remoteFetchException'
225  ),
226  ContextualFeedbackSeverity::NOTICE
227  );
228  }
229 
230  if (!$versionMaintenanceWindow->isSupportedByCommunity() && !$versionMaintenanceWindow->isSupportedByElts()) {
231  // Version is not maintained
232  $message = $languageService->sL('LLL:EXT:install/Resources/Private/Language/Report/locallang.xlf:status_versionOutdated');
233  $status = ContextualFeedbackSeverity::ERROR;
234  } else {
235  $message = '';
236  $status = ContextualFeedbackSeverity::OK;
237 
238  // There is an update available
239  $availableReleases = [];
240  $latestRelease = $coreVersionService->getYoungestPatchRelease();
241  $isCurrentVersionElts = $coreVersionService->isCurrentInstalledVersionElts();
242 
243  if ($coreVersionService->isPatchReleaseSuitableForUpdate($latestRelease)) {
244  $availableReleases[] = $latestRelease;
245  }
246 
247  if (!$versionMaintenanceWindow->isSupportedByCommunity()) {
248  if ($latestRelease->isElts()) {
249  $latestCommunityDrivenRelease = $coreVersionService->getYoungestCommunityPatchRelease();
250  if ($coreVersionService->isPatchReleaseSuitableForUpdate($latestCommunityDrivenRelease)) {
251  $availableReleases[] = $latestCommunityDrivenRelease;
252  }
253  } elseif (!$isCurrentVersionElts) {
254  // Inform user about ELTS being available soon if:
255  // - regular support ran out
256  // - the current installed version is no ELTS
257  // - no ELTS update was released, yet
258  $message = sprintf(
259  $languageService->sL('LLL:EXT:install/Resources/Private/Language/Report/locallang.xlf:status_elts_information'),
260  $typoVersion->getVersion(),
261  '<a href="https://typo3.com/elts" target="_blank" rel="noopener">https://typo3.com/elts</a>'
262  );
263  $status = ContextualFeedbackSeverity::WARNING;
264  }
265  }
266 
267  if ($availableReleases !== []) {
268  $messages = [];
269  $status = ContextualFeedbackSeverity::WARNING;
270  foreach ($availableReleases as $availableRelease) {
271  $versionString = $availableRelease->getVersion();
272  if ($availableRelease->isElts()) {
273  $versionString .= ' ELTS';
274  }
275  if ($coreVersionService->isUpdateSecurityRelevant($availableRelease)) {
276  $status = ContextualFeedbackSeverity::ERROR;
277  $updateMessage = sprintf($languageService->sL('LLL:EXT:install/Resources/Private/Language/Report/locallang.xlf:status_newVersionSecurityRelevant'), $versionString);
278  } else {
279  $updateMessage = sprintf($languageService->sL('LLL:EXT:install/Resources/Private/Language/Report/locallang.xlf:status_newVersion'), $versionString);
280  }
281 
282  if ($availableRelease->isElts()) {
283  if ($isCurrentVersionElts) {
284  $updateMessage .= ' ' . sprintf(
285  $languageService->sL('LLL:EXT:install/Resources/Private/Language/Report/locallang.xlf:status_elts_download'),
286  '<a href="https://my.typo3.org" target="_blank" rel="noopener">my.typo3.org</a>'
287  );
288  } else {
289  $updateMessage .= ' ' . sprintf(
290  $languageService->sL('LLL:EXT:install/Resources/Private/Language/Report/locallang.xlf:status_elts_subscribe'),
291  $coreVersionService->getInstalledVersion(),
292  '<a href="https://typo3.com/elts" target="_blank" rel="noopener">https://typo3.com/elts</a>'
293  );
294  }
295  }
296  $messages[] = $updateMessage;
297  }
298  $message = $this->‪wrapList($messages, count($messages) > 1 ? self::WRAP_NESTED : self::WRAP_FLAT);
299  }
300  }
301 
302  return GeneralUtility::makeInstance(Status::class, 'TYPO3', $typoVersion->getVersion(), $message, $status);
303  }
304 
305  private function ‪wrapList(array $items, int $style): string
306  {
307  if ($style === self::WRAP_NESTED) {
308  return sprintf(
309  '<ul>%s</ul>',
310  implode('', $this->‪wrapItems($items, '<li>', '</li>'))
311  );
312  }
313  return sprintf(
314  '<p>%s</p>',
315  implode('', $this->‪wrapItems($items, '<br>', ''))
316  );
317  }
318 
319  private function ‪wrapItems(array $items, string $before, string $after): array
320  {
321  return array_map(
322  static function (string $item) use ($before, $after): string {
323  return $before . $item . $after;
324  },
325  array_filter($items)
326  );
327  }
328 
330  {
331  return ‪$GLOBALS['LANG'];
332  }
333 }
‪TYPO3\CMS\Install\Report\InstallStatusReport\WRAP_FLAT
‪const WRAP_FLAT
Definition: InstallStatusReport.php:36
‪TYPO3\CMS\Reports\StatusProviderInterface
Definition: StatusProviderInterface.php:22
‪TYPO3\CMS\Core\Utility\GeneralUtility\mkdir
‪static bool mkdir(string $newFolder)
Definition: GeneralUtility.php:1633
‪TYPO3\CMS\Install\Report\InstallStatusReport\__construct
‪__construct(private readonly UpgradeWizardsService $upgradeWizardsService)
Definition: InstallStatusReport.php:39
‪TYPO3\CMS\Core\Information\Typo3Version
Definition: Typo3Version.php:21
‪TYPO3\CMS\Install\Report\InstallStatusReport
Definition: InstallStatusReport.php:35
‪TYPO3\CMS\Core\Core\Environment\isComposerMode
‪static isComposerMode()
Definition: Environment.php:137
‪TYPO3\CMS\Core\Core\Environment\getPublicPath
‪static getPublicPath()
Definition: Environment.php:187
‪TYPO3\CMS\Install\Report
Definition: EnvironmentStatusReport.php:16
‪TYPO3\CMS\Core\Core\Environment\getLabelsPath
‪static getLabelsPath()
Definition: Environment.php:233
‪TYPO3\CMS\Install\Report\InstallStatusReport\getFileSystemStatus
‪Status getFileSystemStatus()
Definition: InstallStatusReport.php:65
‪TYPO3\CMS\Core\Core\Environment\getExtensionsPath
‪static getExtensionsPath()
Definition: Environment.php:253
‪TYPO3\CMS\Install\Report\InstallStatusReport\getNewVersionStatus
‪Status getNewVersionStatus()
Definition: InstallStatusReport.php:205
‪TYPO3\CMS\Core\Core\Environment\getVarPath
‪static getVarPath()
Definition: Environment.php:197
‪TYPO3\CMS\Install\Service\UpgradeWizardsService
Definition: UpgradeWizardsService.php:40
‪TYPO3\CMS\Core\Type\ContextualFeedbackSeverity
‪ContextualFeedbackSeverity
Definition: ContextualFeedbackSeverity.php:25
‪TYPO3\CMS\Install\Report\InstallStatusReport\getLanguageService
‪getLanguageService()
Definition: InstallStatusReport.php:329
‪TYPO3\CMS\Reports\Status
Definition: Status.php:24
‪TYPO3\CMS\Core\Core\Environment\getProjectPath
‪static string getProjectPath()
Definition: Environment.php:160
‪TYPO3\CMS\Install\Report\InstallStatusReport\getIncompleteWizards
‪getIncompleteWizards()
Definition: InstallStatusReport.php:163
‪TYPO3\CMS\Backend\Routing\UriBuilder
Definition: UriBuilder.php:44
‪TYPO3\CMS\Install\Report\InstallStatusReport\wrapItems
‪wrapItems(array $items, string $before, string $after)
Definition: InstallStatusReport.php:319
‪TYPO3\CMS\Install\Service\Exception\RemoteFetchException
Definition: RemoteFetchException.php:23
‪TYPO3\CMS\Install\Report\InstallStatusReport\wrapList
‪wrapList(array $items, int $style)
Definition: InstallStatusReport.php:305
‪TYPO3\CMS\Install\Report\InstallStatusReport\getStatus
‪Status[] getStatus()
Definition: InstallStatusReport.php:46
‪TYPO3\CMS\Install\Report\InstallStatusReport\getRemainingUpdatesStatus
‪Status getRemainingUpdatesStatus()
Definition: InstallStatusReport.php:180
‪TYPO3\CMS\Webhooks\Message\$url
‪identifier readonly UriInterface $url
Definition: LoginErrorOccurredMessage.php:36
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Core\Core\Environment
Definition: Environment.php:41
‪TYPO3\CMS\Core\Localization\LanguageService
Definition: LanguageService.php:46
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:52
‪TYPO3\CMS\Install\Report\InstallStatusReport\getLabel
‪getLabel()
Definition: InstallStatusReport.php:55
‪TYPO3\CMS\Install\Report\InstallStatusReport\WRAP_NESTED
‪const WRAP_NESTED
Definition: InstallStatusReport.php:37