‪TYPO3CMS  10.4
SiteConfigurationController.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
4 
5 /*
6  * This file is part of the TYPO3 CMS project.
7  *
8  * It is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU General Public License, either version 2
10  * of the License, or any later version.
11  *
12  * For the full copyright and license information, please read the
13  * LICENSE.txt file that was distributed with this source code.
14  *
15  * The TYPO3 project - inspiring people to share!
16  */
17 
19 
20 use Psr\Http\Message\ResponseInterface;
21 use Psr\Http\Message\ServerRequestInterface;
51 use TYPO3Fluid\Fluid\View\ViewInterface;
52 
61 {
62  protected const ‪ALLOWED_ACTIONS = ['overview', 'edit', 'save', 'delete'];
63 
67  protected ‪$moduleTemplate;
68 
72  protected ‪$view;
73 
77  protected ‪$siteFinder;
78 
82  public function ‪__construct()
83  {
84  $this->siteFinder = GeneralUtility::makeInstance(SiteFinder::class);
85  $this->moduleTemplate = GeneralUtility::makeInstance(ModuleTemplate::class);
86  }
87 
94  public function ‪handleRequest(ServerRequestInterface $request): ResponseInterface
95  {
96  // forcing uncached sites will re-initialize `SiteFinder`
97  // which is used later by FormEngine (implicit behavior)
98  $this->siteFinder->getAllSites(false);
99  $this->moduleTemplate->getPageRenderer()->loadRequireJsModule('TYPO3/CMS/Backend/ContextMenu');
100  $this->moduleTemplate->getPageRenderer()->loadRequireJsModule('TYPO3/CMS/Backend/Modal');
101  $action = $request->getQueryParams()['action'] ?? $request->getParsedBody()['action'] ?? 'overview';
102 
103  if (!in_array($action, self::ALLOWED_ACTIONS, true)) {
104  return new ‪HtmlResponse('Action not allowed', 400);
105  }
106 
107  $this->‪initializeView($action);
108 
109  $result = $this->{$action . 'Action'}($request);
110  if ($result instanceof ResponseInterface) {
111  return $result;
112  }
113  $this->moduleTemplate->setContent($this->view->render());
114  return new HtmlResponse($this->moduleTemplate->renderContent());
115  }
116 
121  protected function ‪overviewAction(): void
122  {
124  $allSites = $this->siteFinder->getAllSites();
125  $pages = $this->‪getAllSitePages();
126  $unassignedSites = [];
127  foreach ($allSites as $identifier => $site) {
128  $rootPageId = $site->getRootPageId();
129  if (isset($pages[$rootPageId])) {
130  $pages[$rootPageId]['siteIdentifier'] = $identifier;
131  $pages[$rootPageId]['siteConfiguration'] = $site;
132  } else {
133  $unassignedSites[] = $site;
134  }
135  }
136 
137  $this->view->assignMultiple([
138  'pages' => $pages,
139  'unassignedSites' => $unassignedSites,
140  'duplicatedEntryPoints' => $this->‪getDuplicatedEntryPoints($allSites, $pages),
141  ]);
142  }
143 
150  protected function ‪editAction(ServerRequestInterface $request): void
151  {
153 
154  // Put site and friends TCA into global TCA
155  // @todo: We might be able to get rid of that later
156  ‪$GLOBALS['TCA'] = array_merge(‪$GLOBALS['TCA'], GeneralUtility::makeInstance(SiteTcaConfiguration::class)->getTca());
157 
158  $siteIdentifier = $request->getQueryParams()['site'] ?? null;
159  $pageUid = (int)($request->getQueryParams()['pageUid'] ?? 0);
160 
161  if (empty($siteIdentifier) && empty($pageUid)) {
162  throw new \RuntimeException('Either site identifier to edit a config or page uid to add new config must be set', 1521561148);
163  }
164  $isNewConfig = empty($siteIdentifier);
165 
166  $defaultValues = [];
167  if ($isNewConfig) {
168  $defaultValues['site']['rootPageId'] = $pageUid;
169  }
170 
171  $allSites = $this->siteFinder->getAllSites();
172  if (!$isNewConfig && !isset($allSites[$siteIdentifier])) {
173  throw new \RuntimeException('Existing config for site ' . $siteIdentifier . ' not found', 1521561226);
174  }
175 
176  $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
177  $returnUrl = $uriBuilder->buildUriFromRoute('site_configuration');
178 
179  $formDataGroup = GeneralUtility::makeInstance(SiteConfigurationDataGroup::class);
180  $formDataCompiler = GeneralUtility::makeInstance(FormDataCompiler::class, $formDataGroup);
181  $formDataCompilerInput = [
182  'tableName' => 'site',
183  'vanillaUid' => $isNewConfig ? $pageUid : $allSites[$siteIdentifier]->getRootPageId(),
184  'command' => $isNewConfig ? 'new' : 'edit',
185  'returnUrl' => (string)$returnUrl,
186  'customData' => [
187  'siteIdentifier' => $isNewConfig ? '' : $siteIdentifier,
188  ],
189  'defaultValues' => $defaultValues,
190  ];
191  $formData = $formDataCompiler->compile($formDataCompilerInput);
192  $nodeFactory = GeneralUtility::makeInstance(NodeFactory::class);
193  $formData['renderType'] = 'outerWrapContainer';
194  $formResult = $nodeFactory->create($formData)->render();
195  // Needed to be set for 'onChange="reload"' and reload on type change to work
196  $formResult['doSaveFieldName'] = 'doSave';
197  $formResultCompiler = GeneralUtility::makeInstance(FormResultCompiler::class);
198  $formResultCompiler->mergeResult($formResult);
199  $formResultCompiler->addCssFiles();
200  // Always add rootPageId as additional field to have a reference for new records
201  $this->view->assign('rootPageId', $isNewConfig ? $pageUid : $allSites[$siteIdentifier]->getRootPageId());
202  $this->view->assign('returnUrl', $returnUrl);
203  $this->view->assign('formEngineHtml', $formResult['html']);
204  $this->view->assign('formEngineFooter', $formResultCompiler->printNeededJSFunctions());
205  }
206 
214  protected function ‪saveAction(ServerRequestInterface $request): ResponseInterface
215  {
216  // Put site and friends TCA into global TCA
217  // @todo: We might be able to get rid of that later
218  ‪$GLOBALS['TCA'] = array_merge(‪$GLOBALS['TCA'], GeneralUtility::makeInstance(SiteTcaConfiguration::class)->getTca());
219 
220  $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
221  $siteTca = GeneralUtility::makeInstance(SiteTcaConfiguration::class)->getTca();
222 
223  $overviewRoute = $uriBuilder->buildUriFromRoute('site_configuration', ['action' => 'overview']);
224  $parsedBody = $request->getParsedBody();
225  if (isset($parsedBody['closeDoc']) && (int)$parsedBody['closeDoc'] === 1) {
226  // Closing means no save, just redirect to overview
227  return new RedirectResponse($overviewRoute);
228  }
229  $isSave = $parsedBody['_savedok'] ?? $parsedBody['doSave'] ?? false;
230  $isSaveClose = $parsedBody['_saveandclosedok'] ?? false;
231  if (!$isSave && !$isSaveClose) {
232  throw new \RuntimeException('Either save or save and close', 1520370364);
233  }
234 
235  if (!isset($parsedBody['data']['site']) || !is_array($parsedBody['data']['site'])) {
236  throw new \RuntimeException('No site data or site identifier given', 1521030950);
237  }
238 
239  $data = $parsedBody['data'];
240  // This can be NEW123 for new records
241  $pageId = (int)key($data['site']);
242  $sysSiteRow = current($data['site']);
243  $siteIdentifier = $sysSiteRow['identifier'] ?? '';
244 
245  $isNewConfiguration = false;
246  $currentIdentifier = '';
247  try {
248  $currentSite = $this->siteFinder->getSiteByRootPageId($pageId);
249  $currentSiteConfiguration = $currentSite->getConfiguration();
250  $currentIdentifier = $currentSite->getIdentifier();
251  } catch (SiteNotFoundException $e) {
252  $currentSiteConfiguration = [];
253  $isNewConfiguration = true;
254  $pageId = (int)$parsedBody['rootPageId'];
255  if (!$pageId > 0) {
256  // Early validation of rootPageId - it must always be given and greater than 0
257  throw new \RuntimeException('No root page id found', 1521719709);
258  }
259  }
260 
261  // Validate site identifier and do not store or further process it
262  $siteIdentifier = $this->‪validateAndProcessIdentifier($isNewConfiguration, $siteIdentifier, $pageId);
263  unset($sysSiteRow['identifier']);
264 
265  try {
266  $newSysSiteData = [];
267  // Hard set rootPageId: This is TCA readOnly and not transmitted by FormEngine, but is also the "uid" of the site record
268  $newSysSiteData['rootPageId'] = $pageId;
269  foreach ($sysSiteRow as $fieldName => $fieldValue) {
270  $type = $siteTca['site']['columns'][$fieldName]['config']['type'];
271  switch ($type) {
272  case 'input':
273  case 'text':
274  $fieldValue = $this->‪validateAndProcessValue('site', $fieldName, $fieldValue);
275  $newSysSiteData[$fieldName] = $fieldValue;
276  break;
277 
278  case 'inline':
279  $newSysSiteData[$fieldName] = [];
280  $childRowIds = ‪GeneralUtility::trimExplode(',', $fieldValue, true);
281  if (!isset($siteTca['site']['columns'][$fieldName]['config']['foreign_table'])) {
282  throw new \RuntimeException('No foreign_table found for inline type', 1521555037);
283  }
284  $foreignTable = $siteTca['site']['columns'][$fieldName]['config']['foreign_table'];
285  foreach ($childRowIds as $childRowId) {
286  $childRowData = [];
287  if (!isset($data[$foreignTable][$childRowId])) {
288  if (!empty($currentSiteConfiguration[$fieldName][$childRowId])) {
289  // A collapsed inline record: Fetch data from existing config
290  $newSysSiteData[$fieldName][] = $currentSiteConfiguration[$fieldName][$childRowId];
291  continue;
292  }
293  throw new \RuntimeException('No data found for table ' . $foreignTable . ' with id ' . $childRowId, 1521555177);
294  }
295  $childRow = $data[$foreignTable][$childRowId];
296  foreach ($childRow as $childFieldName => $childFieldValue) {
297  if ($childFieldName === 'pid') {
298  // pid is added by inline by default, but not relevant for yml storage
299  continue;
300  }
301  $type = $siteTca[$foreignTable]['columns'][$childFieldName]['config']['type'];
302  switch ($type) {
303  case 'input':
304  case 'select':
305  case 'text':
306  $childRowData[$childFieldName] = $childFieldValue;
307  break;
308  case 'check':
309  $childRowData[$childFieldName] = (bool)$childFieldValue;
310  break;
311  default:
312  throw new \RuntimeException('TCA type ' . $type . ' not implemented in site handling', 1521555340);
313  }
314  }
315  $newSysSiteData[$fieldName][] = $childRowData;
316  }
317  break;
318 
319  case 'select':
321  $fieldValue = (int)$fieldValue;
322  } elseif (is_array($fieldValue)) {
323  $fieldValue = implode(',', $fieldValue);
324  }
325 
326  $newSysSiteData[$fieldName] = $fieldValue;
327  break;
328 
329  case 'check':
330  $newSysSiteData[$fieldName] = (bool)$fieldValue;
331  break;
332 
333  default:
334  throw new \RuntimeException('TCA type "' . $type . '" is not implemented in site handling', 1521032781);
335  }
336  }
337 
338  // keep root config objects not given via GUI
339  // this way extension authors are able to use their own objects on root level
340  // that are not configurable via GUI
341  // however: we overwrite the full subset of any GUI object to make sure we have a clean state
342  $newSysSiteData = array_merge($currentSiteConfiguration, $newSysSiteData);
343  $newSiteConfiguration = $this->‪validateFullStructure($newSysSiteData);
344 
345  // Persist the configuration
346  $siteConfigurationManager = GeneralUtility::makeInstance(SiteConfiguration::class);
347  if (!$isNewConfiguration && $currentIdentifier !== $siteIdentifier) {
348  $siteConfigurationManager->rename($currentIdentifier, $siteIdentifier);
349  }
350  $siteConfigurationManager->write($siteIdentifier, $newSiteConfiguration, true);
351  } catch (SiteValidationErrorException $e) {
352  // Do not store new config if a validation error is thrown, but redirect only to show a generated flash message
353  }
354 
355  $saveRoute = $uriBuilder->buildUriFromRoute('site_configuration', ['action' => 'edit', 'site' => $siteIdentifier]);
356  if ($isSaveClose) {
357  return new RedirectResponse($overviewRoute);
358  }
359  return new RedirectResponse($saveRoute);
360  }
361 
370  protected function ‪validateAndProcessIdentifier(bool $isNew, string $identifier, int $rootPageId)
371  {
372  $languageService = $this->‪getLanguageService();
373  // Normal "eval" processing of field first
374  $identifier = $this->‪validateAndProcessValue('site', 'identifier', $identifier);
375  if ($isNew) {
376  // Verify no other site with this identifier exists. If so, find a new unique name as
377  // identifier and show a flash message the identifier has been adapted
378  try {
379  $this->siteFinder->getSiteByIdentifier($identifier);
380  // Force this identifier to be unique
381  $originalIdentifier = $identifier;
382  $identifier = ‪StringUtility::getUniqueId($identifier . '-');
383  $message = sprintf(
384  $languageService->sL('LLL:EXT:backend/Resources/Private/Language/locallang_siteconfiguration.xlf:validation.identifierRenamed.message'),
385  $originalIdentifier,
386  $identifier
387  );
388  $messageTitle = $languageService->sL('LLL:EXT:backend/Resources/Private/Language/locallang_siteconfiguration.xlf:validation.identifierRenamed.title');
389  $flashMessage = GeneralUtility::makeInstance(FlashMessage::class, $message, $messageTitle, ‪FlashMessage::WARNING, true);
390  $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
391  $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
392  $defaultFlashMessageQueue->enqueue($flashMessage);
393  } catch (SiteNotFoundException $e) {
394  // Do nothing, this new identifier is ok
395  }
396  } else {
397  // If this is an existing config, the site for this identifier must have the same rootPageId, otherwise
398  // a user tried to rename a site identifier to a different site that already exists. If so, we do not rename
399  // the site and show a flash message
400  try {
401  $site = $this->siteFinder->getSiteByIdentifier($identifier);
402  if ($site->getRootPageId() !== $rootPageId) {
403  // Find original value and keep this
404  $origSite = $this->siteFinder->getSiteByRootPageId($rootPageId);
405  $originalIdentifier = $identifier;
406  $identifier = $origSite->getIdentifier();
407  $message = sprintf(
408  $languageService->sL('LLL:EXT:backend/Resources/Private/Language/locallang_siteconfiguration.xlf:validation.identifierExists.message'),
409  $originalIdentifier,
410  $identifier
411  );
412  $messageTitle = $languageService->sL('LLL:EXT:backend/Resources/Private/Language/locallang_siteconfiguration.xlf:validation.identifierExists.title');
413  $flashMessage = GeneralUtility::makeInstance(FlashMessage::class, $message, $messageTitle, ‪FlashMessage::WARNING, true);
414  $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
415  $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
416  $defaultFlashMessageQueue->enqueue($flashMessage);
417  }
418  } catch (SiteNotFoundException $e) {
419  // User is renaming identifier which does not exist yet. That's ok
420  }
421  }
422  return $identifier;
423  }
424 
437  protected function ‪validateAndProcessValue(string $tableName, string $fieldName, $fieldValue)
438  {
439  $languageService = $this->‪getLanguageService();
440  $fieldConfig = ‪$GLOBALS['TCA'][$tableName]['columns'][$fieldName]['config'];
441  $handledEvals = [];
442  if (!empty($fieldConfig['eval'])) {
443  $evalArray = ‪GeneralUtility::trimExplode(',', $fieldConfig['eval'], true);
444  // Processing
445  if (in_array('alphanum_x', $evalArray, true)) {
446  $handledEvals[] = 'alphanum_x';
447  $fieldValue = preg_replace('/[^a-zA-Z0-9_-]/', '', $fieldValue);
448  }
449  if (in_array('lower', $evalArray, true)) {
450  $handledEvals[] = 'lower';
451  $fieldValue = mb_strtolower($fieldValue, 'utf-8');
452  }
453  if (in_array('trim', $evalArray, true)) {
454  $handledEvals[] = 'trim';
455  $fieldValue = trim($fieldValue);
456  }
457  if (in_array('int', $evalArray, true)) {
458  $handledEvals[] = 'int';
459  $fieldValue = (int)$fieldValue;
460  }
461  // Validation throws - these should be handled client side already,
462  // eg. 'required' being set and receiving empty, shouldn't happen server side
463  if (in_array('required', $evalArray, true)) {
464  $handledEvals[] = 'required';
465  if (empty($fieldValue)) {
466  $message = sprintf(
467  $languageService->sL('LLL:EXT:backend/Resources/Private/Language/locallang_siteconfiguration.xlf:validation.required.message'),
468  $fieldName
469  );
470  $messageTitle = $languageService->sL('LLL:EXT:backend/Resources/Private/Language/locallang_siteconfiguration.xlf:validation.required.title');
471  $flashMessage = GeneralUtility::makeInstance(FlashMessage::class, $message, $messageTitle, ‪FlashMessage::WARNING, true);
472  $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
473  $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
474  $defaultFlashMessageQueue->enqueue($flashMessage);
475  throw new SiteValidationErrorException(
476  'Field ' . $fieldName . ' is set to required, but received empty.',
477  1521726421
478  );
479  }
480  }
481  if (!empty(array_diff($evalArray, $handledEvals))) {
482  throw new \RuntimeException('At least one not implemented \'eval\' in list ' . $fieldConfig['eval'], 1522491734);
483  }
484  }
485  if (isset($fieldConfig['range']['lower'])) {
486  $fieldValue = (int)$fieldValue < (int)$fieldConfig['range']['lower'] ? (int)$fieldConfig['range']['lower'] : (int)$fieldValue;
487  }
488  if (isset($fieldConfig['range']['upper'])) {
489  $fieldValue = (int)$fieldValue > (int)$fieldConfig['range']['upper'] ? (int)$fieldConfig['range']['upper'] : (int)$fieldValue;
490  }
491  return $fieldValue;
492  }
493 
502  protected function ‪validateFullStructure(array $newSysSiteData): array
503  {
504  $languageService = $this->‪getLanguageService();
505  // Verify there are not two error handlers with the same error code
506  if (isset($newSysSiteData['errorHandling']) && is_array($newSysSiteData['errorHandling'])) {
507  $uniqueCriteria = [];
508  $validChildren = [];
509  foreach ($newSysSiteData['errorHandling'] as $child) {
510  if (!isset($child['errorCode'])) {
511  throw new \RuntimeException('No errorCode found', 1521788518);
512  }
513  if (!in_array((int)$child['errorCode'], $uniqueCriteria, true)) {
514  $uniqueCriteria[] = (int)$child['errorCode'];
515  $child['errorCode'] = (int)$child['errorCode'];
516  $validChildren[] = $child;
517  } else {
518  $message = sprintf(
519  $languageService->sL('LLL:EXT:backend/Resources/Private/Language/locallang_siteconfiguration.xlf:validation.duplicateErrorCode.message'),
520  $child['errorCode']
521  );
522  $messageTitle = $languageService->sL('LLL:EXT:backend/Resources/Private/Language/locallang_siteconfiguration.xlf:validation.duplicateErrorCode.title');
523  $flashMessage = GeneralUtility::makeInstance(FlashMessage::class, $message, $messageTitle, ‪FlashMessage::WARNING, true);
524  $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
525  $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
526  $defaultFlashMessageQueue->enqueue($flashMessage);
527  }
528  }
529  $newSysSiteData['errorHandling'] = $validChildren;
530  }
531 
532  // Verify there is only one inline child per sys_language record configured.
533  if (!isset($newSysSiteData['languages']) || !is_array($newSysSiteData['languages']) || count($newSysSiteData['languages']) < 1) {
534  throw new \RuntimeException(
535  'No default language definition found. The interface does not allow this. Aborting',
536  1521789306
537  );
538  }
539  $uniqueCriteria = [];
540  $validChildren = [];
541  foreach ($newSysSiteData['languages'] as $child) {
542  if (!isset($child['languageId'])) {
543  throw new \RuntimeException('languageId not found', 1521789455);
544  }
545  if (!in_array((int)$child['languageId'], $uniqueCriteria, true)) {
546  $uniqueCriteria[] = (int)$child['languageId'];
547  $child['languageId'] = (int)$child['languageId'];
548  $validChildren[] = $child;
549  } else {
550  $message = sprintf(
551  $languageService->sL('LLL:EXT:backend/Resources/Private/Language/locallang_siteconfiguration.xlf:validation.duplicateLanguageId.title'),
552  $child['languageId']
553  );
554  $messageTitle = $languageService->sL('LLL:EXT:backend/Resources/Private/Language/locallang_siteconfiguration.xlf:validation.duplicateLanguageId.title');
555  $flashMessage = GeneralUtility::makeInstance(FlashMessage::class, $message, $messageTitle, ‪FlashMessage::WARNING, true);
556  $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
557  $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
558  $defaultFlashMessageQueue->enqueue($flashMessage);
559  }
560  }
561  $newSysSiteData['languages'] = $validChildren;
562 
563  // cleanup configuration
564  foreach ($newSysSiteData as $identifier => $value) {
565  if (is_array($value) && empty($value)) {
566  unset($newSysSiteData[$identifier]);
567  }
568  }
569 
570  return $newSysSiteData;
571  }
572 
579  protected function ‪deleteAction(ServerRequestInterface $request): ResponseInterface
580  {
581  $siteIdentifier = $request->getQueryParams()['site'] ?? '';
582  if (empty($siteIdentifier)) {
583  throw new \RuntimeException('Not site identifier given', 1521565182);
584  }
585  // Verify site does exist, method throws if not
586  GeneralUtility::makeInstance(SiteConfiguration::class)->delete($siteIdentifier);
587  $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
588  $overviewRoute = $uriBuilder->buildUriFromRoute('site_configuration', ['action' => 'overview']);
589  return new RedirectResponse($overviewRoute);
590  }
591 
597  protected function ‪initializeView(string $templateName): void
598  {
599  $this->view = GeneralUtility::makeInstance(StandaloneView::class);
600  $this->view->setTemplate($templateName);
601  $this->view->setTemplateRootPaths(['EXT:backend/Resources/Private/Templates/SiteConfiguration']);
602  $this->view->setPartialRootPaths(['EXT:backend/Resources/Private/Partials']);
603  $this->view->setLayoutRootPaths(['EXT:backend/Resources/Private/Layouts']);
604  }
605 
609  protected function ‪configureEditViewDocHeader(): void
610  {
611  $iconFactory = $this->moduleTemplate->getIconFactory();
612  $buttonBar = $this->moduleTemplate->getDocHeaderComponent()->getButtonBar();
613  $lang = $this->‪getLanguageService();
614  $closeButton = $buttonBar->makeLinkButton()
615  ->setHref('#')
616  ->setClasses('t3js-editform-close')
617  ->setTitle($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:rm.closeDoc'))
618  ->setShowLabelText(true)
619  ->setIcon($iconFactory->getIcon('actions-close', ‪Icon::SIZE_SMALL));
620  $saveButton = $buttonBar->makeInputButton()
621  ->setTitle($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:rm.saveDoc'))
622  ->setName('_savedok')
623  ->setValue('1')
624  ->setShowLabelText(true)
625  ->setForm('siteConfigurationController')
626  ->setIcon($iconFactory->getIcon('actions-document-save', ‪Icon::SIZE_SMALL));
627  $buttonBar->addButton($closeButton);
628  $buttonBar->addButton($saveButton, ‪ButtonBar::BUTTON_POSITION_LEFT, 2);
629  }
630 
634  protected function ‪configureOverViewDocHeader(): void
635  {
636  $iconFactory = $this->moduleTemplate->getIconFactory();
637  $buttonBar = $this->moduleTemplate->getDocHeaderComponent()->getButtonBar();
638  $reloadButton = $buttonBar->makeLinkButton()
639  ->setHref(GeneralUtility::getIndpEnv('REQUEST_URI'))
640  ->setTitle($this->‪getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.reload'))
641  ->setIcon($iconFactory->getIcon('actions-refresh', ‪Icon::SIZE_SMALL));
642  $buttonBar->addButton($reloadButton, ‪ButtonBar::BUTTON_POSITION_RIGHT);
643  if ($this->‪getBackendUser()->mayMakeShortcut()) {
644  $getVars = ['id', 'route'];
645  $shortcutButton = $buttonBar->makeShortcutButton()
646  ->setModuleName('site_configuration')
647  ->setGetVariables($getVars);
648  $buttonBar->addButton($shortcutButton, ‪ButtonBar::BUTTON_POSITION_RIGHT);
649  }
650  }
651 
657  protected function ‪getAllSitePages(): array
658  {
659  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
660  $queryBuilder->getRestrictions()->removeByType(HiddenRestriction::class);
661  $queryBuilder->getRestrictions()->add(GeneralUtility::makeInstance(WorkspaceRestriction::class, 0));
662  $statement = $queryBuilder
663  ->select('*')
664  ->from('pages')
665  ->where(
666  $queryBuilder->expr()->eq('sys_language_uid', 0),
667  $queryBuilder->expr()->orX(
668  $queryBuilder->expr()->andX(
669  $queryBuilder->expr()->eq('pid', 0),
670  $queryBuilder->expr()->neq('doktype', ‪PageRepository::DOKTYPE_SYSFOLDER)
671  ),
672  $queryBuilder->expr()->eq('is_siteroot', 1)
673  )
674  )
675  ->orderBy('pid')
676  ->addOrderBy('sorting')
677  ->execute();
678 
679  $pages = [];
680  while ($row = $statement->fetch()) {
681  $row['rootline'] = ‪BackendUtility::BEgetRootLine((int)$row['uid']);
682  array_pop($row['rootline']);
683  $row['rootline'] = array_reverse($row['rootline']);
684  $i = 0;
685  foreach ($row['rootline'] as &$record) {
686  $record['margin'] = $i++ * 20;
687  }
688  $pages[(int)$row['uid']] = $row;
689  }
690  return $pages;
691  }
692 
700  protected function ‪getDuplicatedEntryPoints(array $allSites, array $pages): array
701  {
702  $duplicatedEntryPoints = [];
703 
704  foreach ($allSites as $identifier => $site) {
705  if (!isset($pages[$site->getRootPageId()])) {
706  continue;
707  }
708  foreach ($site->getAllLanguages() as $language) {
709  $base = $language->getBase();
710  $entryPoint = rtrim((string)$language->getBase(), '/');
711  $scheme = $base->getScheme() ? $base->getScheme() . '://' : '//';
712  $entryPointWithoutScheme = str_replace($scheme, '', $entryPoint);
713  if (!isset($duplicatedEntryPoints[$entryPointWithoutScheme][$entryPoint])) {
714  $duplicatedEntryPoints[$entryPointWithoutScheme][$entryPoint] = 1;
715  } else {
716  $duplicatedEntryPoints[$entryPointWithoutScheme][$entryPoint]++;
717  }
718  }
719  }
720  return array_filter($duplicatedEntryPoints, static function (array $variants): bool {
721  return count($variants) > 1 || reset($variants) > 1;
722  }, ARRAY_FILTER_USE_BOTH);
723  }
724 
728  protected function ‪getLanguageService(): LanguageService
729  {
730  return ‪$GLOBALS['LANG'];
731  }
732 
736  protected function ‪getBackendUser(): BackendUserAuthentication
737  {
738  return ‪$GLOBALS['BE_USER'];
739  }
740 }
‪TYPO3\CMS\Backend\Controller\SiteConfigurationController\$siteFinder
‪SiteFinder $siteFinder
Definition: SiteConfigurationController.php:74
‪TYPO3\CMS\Core\Imaging\Icon\SIZE_SMALL
‪const SIZE_SMALL
Definition: Icon.php:30
‪TYPO3\CMS\Core\Database\Query\Restriction\HiddenRestriction
Definition: HiddenRestriction.php:27
‪TYPO3\CMS\Backend\Configuration\SiteTcaConfiguration
Definition: SiteTcaConfiguration.php:33
‪TYPO3\CMS\Backend\Form\FormResultCompiler
Definition: FormResultCompiler.php:30
‪TYPO3\CMS\Backend\Template\Components\ButtonBar\BUTTON_POSITION_LEFT
‪const BUTTON_POSITION_LEFT
Definition: ButtonBar.php:36
‪TYPO3\CMS\Core\Utility\MathUtility\canBeInterpretedAsInteger
‪static bool canBeInterpretedAsInteger($var)
Definition: MathUtility.php:74
‪TYPO3\CMS\Backend\Template\Components\ButtonBar
Definition: ButtonBar.php:32
‪TYPO3\CMS\Backend\Controller\SiteConfigurationController\validateAndProcessValue
‪mixed validateAndProcessValue(string $tableName, string $fieldName, $fieldValue)
Definition: SiteConfigurationController.php:434
‪TYPO3\CMS\Core\Imaging\Icon
Definition: Icon.php:26
‪TYPO3\CMS\Backend\Controller\SiteConfigurationController\$moduleTemplate
‪ModuleTemplate $moduleTemplate
Definition: SiteConfigurationController.php:66
‪TYPO3\CMS\Core\Exception\SiteNotFoundException
Definition: SiteNotFoundException.php:26
‪TYPO3\CMS\Core\Site\SiteFinder
Definition: SiteFinder.php:31
‪TYPO3\CMS\Backend\Utility\BackendUtility\BEgetRootLine
‪static array BEgetRootLine($uid, $clause='', $workspaceOL=false, array $additionalFields=[])
Definition: BackendUtility.php:343
‪TYPO3\CMS\Core\Configuration\SiteConfiguration
Definition: SiteConfiguration.php:41
‪TYPO3\CMS\Backend\Template\ModuleTemplate
Definition: ModuleTemplate.php:43
‪TYPO3\CMS\Core\Site\Entity\Site
Definition: Site.php:40
‪TYPO3\CMS\Backend\Controller\SiteConfigurationController\__construct
‪__construct()
Definition: SiteConfigurationController.php:79
‪TYPO3\CMS\Backend\Controller\SiteConfigurationController\configureOverViewDocHeader
‪configureOverViewDocHeader()
Definition: SiteConfigurationController.php:631
‪TYPO3\CMS\Backend\Controller\SiteConfigurationController\validateAndProcessIdentifier
‪mixed validateAndProcessIdentifier(bool $isNew, string $identifier, int $rootPageId)
Definition: SiteConfigurationController.php:367
‪TYPO3\CMS\Core\Messaging\AbstractMessage\WARNING
‪const WARNING
Definition: AbstractMessage.php:30
‪TYPO3\CMS\Backend\Controller\SiteConfigurationController\overviewAction
‪overviewAction()
Definition: SiteConfigurationController.php:118
‪TYPO3\CMS\Backend\Controller\SiteConfigurationController\saveAction
‪ResponseInterface saveAction(ServerRequestInterface $request)
Definition: SiteConfigurationController.php:211
‪TYPO3\CMS\Backend\Controller\SiteConfigurationController
Definition: SiteConfigurationController.php:61
‪TYPO3\CMS\Backend\Controller\SiteConfigurationController\deleteAction
‪ResponseInterface deleteAction(ServerRequestInterface $request)
Definition: SiteConfigurationController.php:576
‪TYPO3\CMS\Backend\Routing\UriBuilder
Definition: UriBuilder.php:38
‪TYPO3\CMS\Backend\Exception\SiteValidationErrorException
Definition: SiteValidationErrorException.php:26
‪TYPO3\CMS\Core\Authentication\BackendUserAuthentication
Definition: BackendUserAuthentication.php:62
‪TYPO3\CMS\Core\Domain\Repository\PageRepository\DOKTYPE_SYSFOLDER
‪const DOKTYPE_SYSFOLDER
Definition: PageRepository.php:109
‪TYPO3\CMS\Backend\Utility\BackendUtility
Definition: BackendUtility.php:75
‪TYPO3\CMS\Backend\Controller\SiteConfigurationController\$view
‪ViewInterface $view
Definition: SiteConfigurationController.php:70
‪TYPO3\CMS\Backend\Controller\SiteConfigurationController\getAllSitePages
‪array getAllSitePages()
Definition: SiteConfigurationController.php:654
‪TYPO3\CMS\Core\Http\RedirectResponse
Definition: RedirectResponse.php:28
‪TYPO3\CMS\Core\Utility\GeneralUtility\trimExplode
‪static string[] trimExplode($delim, $string, $removeEmptyValues=false, $limit=0)
Definition: GeneralUtility.php:1059
‪TYPO3\CMS\Backend\Form\NodeFactory
Definition: NodeFactory.php:37
‪TYPO3\CMS\Core\Utility\StringUtility\getUniqueId
‪static string getUniqueId($prefix='')
Definition: StringUtility.php:92
‪TYPO3\CMS\Core\Messaging\FlashMessage
Definition: FlashMessage.php:24
‪TYPO3\CMS\Fluid\View\StandaloneView
Definition: StandaloneView.php:34
‪TYPO3\CMS\Backend\Controller\SiteConfigurationController\editAction
‪editAction(ServerRequestInterface $request)
Definition: SiteConfigurationController.php:147
‪TYPO3\CMS\Backend\Controller\SiteConfigurationController\initializeView
‪initializeView(string $templateName)
Definition: SiteConfigurationController.php:594
‪TYPO3\CMS\Backend\Controller\SiteConfigurationController\validateFullStructure
‪array validateFullStructure(array $newSysSiteData)
Definition: SiteConfigurationController.php:499
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:5
‪TYPO3\CMS\Backend\Controller\SiteConfigurationController\handleRequest
‪ResponseInterface handleRequest(ServerRequestInterface $request)
Definition: SiteConfigurationController.php:91
‪TYPO3\CMS\Backend\Controller\SiteConfigurationController\ALLOWED_ACTIONS
‪const ALLOWED_ACTIONS
Definition: SiteConfigurationController.php:62
‪TYPO3\CMS\Core\Utility\MathUtility
Definition: MathUtility.php:22
‪TYPO3\CMS\Core\Localization\LanguageService
Definition: LanguageService.php:42
‪TYPO3\CMS\Core\Domain\Repository\PageRepository
Definition: PageRepository.php:52
‪TYPO3\CMS\Core\Database\ConnectionPool
Definition: ConnectionPool.php:46
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:46
‪TYPO3\CMS\Backend\Form\FormDataCompiler
Definition: FormDataCompiler.php:25
‪TYPO3\CMS\Core\Utility\StringUtility
Definition: StringUtility.php:22
‪TYPO3\CMS\Backend\Template\Components\ButtonBar\BUTTON_POSITION_RIGHT
‪const BUTTON_POSITION_RIGHT
Definition: ButtonBar.php:41
‪TYPO3\CMS\Backend\Form\FormDataGroup\SiteConfigurationDataGroup
Definition: SiteConfigurationDataGroup.php:33
‪TYPO3\CMS\Backend\Controller
Definition: AbstractFormEngineAjaxController.php:18
‪TYPO3\CMS\Core\Messaging\FlashMessageService
Definition: FlashMessageService.php:27
‪TYPO3\CMS\Backend\Controller\SiteConfigurationController\getLanguageService
‪LanguageService getLanguageService()
Definition: SiteConfigurationController.php:725
‪TYPO3\CMS\Backend\Controller\SiteConfigurationController\getDuplicatedEntryPoints
‪array getDuplicatedEntryPoints(array $allSites, array $pages)
Definition: SiteConfigurationController.php:697
‪TYPO3\CMS\Backend\Controller\SiteConfigurationController\getBackendUser
‪BackendUserAuthentication getBackendUser()
Definition: SiteConfigurationController.php:733
‪TYPO3\CMS\Backend\Controller\SiteConfigurationController\configureEditViewDocHeader
‪configureEditViewDocHeader()
Definition: SiteConfigurationController.php:606
‪TYPO3\CMS\Core\Http\HtmlResponse
Definition: HtmlResponse.php:26
‪TYPO3\CMS\Core\Database\Query\Restriction\WorkspaceRestriction
Definition: WorkspaceRestriction.php:39