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