‪TYPO3CMS  11.5
EditDocumentController.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\EventDispatcher\EventDispatcherInterface;
21 use Psr\Http\Message\ResponseInterface;
22 use Psr\Http\Message\ServerRequestInterface;
38 use TYPO3\CMS\Backend\Utility\BackendUtility;
41 use TYPO3\CMS\Core\Database\Query\QueryBuilder;
54 use TYPO3\CMS\Core\Page\PageRenderer;
66 
75 {
76  protected const ‪DOCUMENT_CLOSE_MODE_DEFAULT = 0;
77  // works like DOCUMENT_CLOSE_MODE_DEFAULT
78  protected const ‪DOCUMENT_CLOSE_MODE_REDIRECT = 1;
79  protected const ‪DOCUMENT_CLOSE_MODE_CLEAR_ALL = 3;
81 
87  protected ‪$editconf = [];
88 
95  protected ‪$columnsOnly;
96 
102  protected ‪$defVals;
103 
109  protected ‪$overrideVals;
110 
117  protected ‪$returnUrl;
118 
126  protected ‪$retUrl;
127 
133  protected ‪$closeDoc;
134 
142  protected ‪$doSave;
143 
149  protected ‪$data;
150 
156  protected ‪$cmd;
157 
163  protected ‪$mirror;
164 
171  protected ‪$returnNewPageId = false;
172 
178  protected ‪$popViewId;
179 
185  protected ‪$viewUrl;
186 
190  protected ‪$previewCode;
191 
197  protected ‪$recTitle;
198 
204  protected ‪$noView;
205 
209  protected ‪$perms_clause;
210 
217 
223  protected ‪$R_URL_parts;
224 
231  protected ‪$R_URL_getvars;
232 
238  protected ‪$R_URI;
239 
243  protected ‪$pageinfo;
244 
251  protected ‪$storeTitle = '';
252 
259  protected ‪$storeArray;
260 
266  protected ‪$storeUrl;
267 
273  protected ‪$storeUrlMd5;
274 
280  protected ‪$docDat;
281 
290  protected ‪$docHandler;
291 
297  protected ‪$elementsData;
298 
304  protected ‪$firstEl;
305 
311  protected ‪$errorC;
312 
319  protected ‪$viewId;
320 
325 
331  protected ‪$dontStoreDocumentRef = 0;
332 
338  protected ‪$previewData = [];
339 
345  protected ‪$moduleTemplate;
346 
352  protected ‪$isSavedRecord;
353 
359  protected ‪$isPageInFreeTranslationMode = false;
360 
361  protected EventDispatcherInterface ‪$eventDispatcher;
363  protected PageRenderer ‪$pageRenderer;
364  protected ‪UriBuilder ‪$uriBuilder;
366 
367  public function ‪__construct(
368  EventDispatcherInterface ‪$eventDispatcher,
370  PageRenderer ‪$pageRenderer,
373  ) {
374  $this->eventDispatcher = ‪$eventDispatcher;
375  $this->iconFactory = ‪$iconFactory;
376  $this->pageRenderer = ‪$pageRenderer;
377  $this->uriBuilder = ‪$uriBuilder;
378  $this->moduleTemplateFactory = ‪$moduleTemplateFactory;
379  }
380 
387  public function ‪mainAction(ServerRequestInterface $request): ResponseInterface
388  {
389  $this->moduleTemplate = $this->moduleTemplateFactory->create($request);
390  $this->moduleTemplate->setUiBlock(true);
391  $this->moduleTemplate->setTitle($this->‪getShortcutTitle($request));
392 
393  $this->‪getLanguageService()->includeLLFile('EXT:backend/Resources/Private/Language/locallang_alt_doc.xlf');
394 
395  // Unlock all locked records
396  BackendUtility::lockRecords();
397  if ($response = $this->‪preInit($request)) {
398  return $response;
399  }
400 
401  // Process incoming data via DataHandler?
402  $parsedBody = $request->getParsedBody();
403  if ((
404  $this->doSave
405  || isset($parsedBody['_savedok'])
406  || isset($parsedBody['_saveandclosedok'])
407  || isset($parsedBody['_savedokview'])
408  || isset($parsedBody['_savedoknew'])
409  || isset($parsedBody['_duplicatedoc'])
410  )
411  && $request->getMethod() === 'POST'
412  && $response = $this->processData($request)
413  ) {
414  return $response;
415  }
416 
417  $this->‪init($request);
418 
419  if ($request->getMethod() === 'POST') {
420  // In case save&view is requested, we have to add this information to the redirect
421  // URL, since the ImmediateAction will be added to the module body afterwards.
422  if (isset($parsedBody['_savedokview'])) {
423  $this->R_URI = rtrim($this->R_URI, '&') .
425  'showPreview' => true,
426  'popViewId' => $parsedBody['popViewId'] ?? $this->‪getPreviewPageId(),
427  ], (empty($this->R_URL_getvars) ? '?' : '&'));
428  }
429  return new RedirectResponse($this->R_URI, 302);
430  }
431 
432  $this->‪main($request);
433 
434  return new HtmlResponse($this->moduleTemplate->renderContent());
435  }
436 
443  protected function ‪preInit(ServerRequestInterface $request): ?ResponseInterface
444  {
445  if ($response = $this->‪localizationRedirect($request)) {
446  return $response;
447  }
448 
449  $parsedBody = $request->getParsedBody();
450  $queryParams = $request->getQueryParams();
451 
452  $this->editconf = $parsedBody['edit'] ?? $queryParams['edit'] ?? [];
453  $this->defVals = $parsedBody['defVals'] ?? $queryParams['defVals'] ?? null;
454  $this->overrideVals = $parsedBody['overrideVals'] ?? $queryParams['overrideVals'] ?? null;
455  $this->columnsOnly = $parsedBody['columnsOnly'] ?? $queryParams['columnsOnly'] ?? null;
456  $this->returnUrl = GeneralUtility::sanitizeLocalUrl($parsedBody['returnUrl'] ?? $queryParams['returnUrl'] ?? '');
457  $this->closeDoc = (int)($parsedBody['closeDoc'] ?? $queryParams['closeDoc'] ?? self::DOCUMENT_CLOSE_MODE_DEFAULT);
458  $this->doSave = (bool)($parsedBody['doSave'] ?? false) && $request->getMethod() === 'POST';
459  $this->returnEditConf = (bool)($parsedBody['returnEditConf'] ?? $queryParams['returnEditConf'] ?? false);
460 
461  // Set overrideVals as default values if defVals does not exist.
462  // @todo: Why?
463  if (!is_array($this->defVals) && is_array($this->overrideVals)) {
464  $this->defVals = ‪$this->overrideVals;
465  }
466  $this->‪addSlugFieldsToColumnsOnly($queryParams);
467 
468  // Set final return URL
469  $this->retUrl = $this->returnUrl ?: (string)$this->uriBuilder->buildUriFromRoute('dummy');
470 
471  // Change $this->editconf if versioning applies to any of the records
473 
474  // Prepare R_URL (request url)
475  $this->R_URL_parts = parse_url($request->getAttribute('normalizedParams')->getRequestUri()) ?: [];
476  $this->R_URL_getvars = $queryParams;
477  $this->R_URL_getvars['edit'] = ‪$this->editconf;
478 
479  // Prepare 'open documents' url, this is later modified again various times
480  $this->‪compileStoreData($request);
481  // Backend user session data of this module
482  $this->docDat = $this->‪getBackendUser()->‪getModuleData('FormEngine', 'ses');
483  $this->docHandler = $this->docDat[0] ?? [];
484 
485  // Close document if a request for closing the document has been sent
486  if ((int)$this->closeDoc > self::DOCUMENT_CLOSE_MODE_DEFAULT) {
487  if ($response = $this->‪closeDocument($this->closeDoc, $request)) {
488  return $response;
489  }
490  }
491 
492  $event = new ‪BeforeFormEnginePageInitializedEvent($this, $request);
493  $this->eventDispatcher->dispatch($event);
494  return null;
495  }
496 
502  protected function ‪addSlugFieldsToColumnsOnly(array $queryParams): void
503  {
504  ‪$data = $queryParams['edit'] ?? [];
505  ‪$data = array_keys(‪$data);
506  $table = reset(‪$data);
507  if ($this->columnsOnly && $table !== false && isset(‪$GLOBALS['TCA'][$table])) {
508  ‪$fields = ‪GeneralUtility::trimExplode(',', $this->columnsOnly, true);
509  foreach (‪$fields as $field) {
510  $postModifiers = ‪$GLOBALS['TCA'][$table]['columns'][$field]['config']['generatorOptions']['postModifiers'] ?? [];
511  if (isset(‪$GLOBALS['TCA'][$table]['columns'][$field])
512  && ‪$GLOBALS['TCA'][$table]['columns'][$field]['config']['type'] === 'slug'
513  && (!is_array($postModifiers) || $postModifiers === [])
514  ) {
515  foreach (‪$GLOBALS['TCA'][$table]['columns'][$field]['config']['generatorOptions']['fields'] ?? [] as ‪$fields) {
516  $this->columnsOnly .= ',' . (is_array(‪$fields) ? implode(',', ‪$fields) : ‪$fields);
517  }
518  }
519  }
520  }
521  }
522 
529  protected function ‪processData(ServerRequestInterface $request): ?ResponseInterface
530  {
531  $parsedBody = $request->getParsedBody();
532 
533  $beUser = $this->‪getBackendUser();
534 
535  // Processing related GET / POST vars
536  $this->data = $parsedBody['data'] ?? [];
537  $this->cmd = $parsedBody['cmd'] ?? [];
538  $this->mirror = $parsedBody['mirror'] ?? [];
539  $this->returnNewPageId = (bool)($parsedBody['returnNewPageId'] ?? false);
540 
541  // Only options related to $this->data submission are included here
542  $tce = GeneralUtility::makeInstance(DataHandler::class);
543 
544  $tce->setControl($parsedBody['control'] ?? []);
545 
546  // Set internal vars
547  if (isset($beUser->uc['neverHideAtCopy']) && $beUser->uc['neverHideAtCopy']) {
548  $tce->neverHideAtCopy = true;
549  }
550 
551  // Set default values fetched previously from GET / POST vars
552  if (is_array($this->defVals) && $this->defVals !== [] && is_array($tce->defaultValues)) {
553  $tce->defaultValues = array_merge_recursive($this->defVals, $tce->defaultValues);
554  }
555 
556  // Load DataHandler with data
557  $tce->start($this->data, $this->cmd);
558  if (is_array($this->mirror)) {
559  $tce->setMirror($this->mirror);
560  }
561 
562  // Perform the saving operation with DataHandler:
563  if ($this->doSave === true) {
564  $tce->process_datamap();
565  $tce->process_cmdmap();
566 
567  // Update the module menu for the current backend user, as they updated their UI language
568  $currentUserId = (int)($beUser->user[$beUser->userid_column] ?? 0);
569  if ($currentUserId
570  && (string)($this->data['be_users'][$currentUserId]['lang'] ?? '') !== ''
571  && $this->data['be_users'][$currentUserId]['lang'] !== $beUser->user['lang']
572  ) {
573  $newLanguageKey = $this->data['be_users'][$currentUserId]['lang'];
574  // Update the current backend user language as well
575  $beUser->user['lang'] = $newLanguageKey;
576  // Re-create LANG to have the current request updated the translated page as well
577  $this->‪getLanguageService()->init($newLanguageKey);
578  $this->‪getLanguageService()->includeLLFile('EXT:backend/Resources/Private/Language/locallang_alt_doc.xlf');
579  BackendUtility::setUpdateSignal('updateModuleMenu');
580  BackendUtility::setUpdateSignal('updateTopbar');
581  }
582  }
583  // If pages are being edited, we set an instruction about updating the page tree after this operation.
584  if ($tce->pagetreeNeedsRefresh
585  && (isset($this->data['pages']) || $beUser->workspace != 0 && !empty($this->data))
586  ) {
587  BackendUtility::setUpdateSignal('updatePageTree');
588  }
589  // If there was saved any new items, load them:
590  if (!empty($tce->substNEWwithIDs_table)) {
591  // Save the expanded/collapsed states for new inline records, if any
592  $this->‪updateInlineView($request->getParsedBody()['uc'] ?? $request->getQueryParams()['uc'] ?? null, $tce);
593  $newEditConf = [];
594  foreach ($this->editconf as $tableName => $tableCmds) {
595  $keys = array_keys($tce->substNEWwithIDs_table, $tableName);
596  if (!empty($keys)) {
597  foreach ($keys as $key) {
598  $editId = $tce->substNEWwithIDs[$key];
599  // Check if the $editId isn't a child record of an IRRE action
600  if (!(is_array($tce->newRelatedIDs[$tableName] ?? null)
601  && in_array($editId, $tce->newRelatedIDs[$tableName]))
602  ) {
603  // Translate new id to the workspace version
604  if ($versionRec = BackendUtility::getWorkspaceVersionOfRecord(
605  $beUser->workspace,
606  $tableName,
607  $editId,
608  'uid'
609  )) {
610  $editId = $versionRec['uid'];
611  }
612  $newEditConf[$tableName][$editId] = 'edit';
613  }
614  // Traverse all new records and forge the content of ->editconf so we can continue to edit these records!
615  if ($tableName === 'pages'
616  && $this->retUrl !== (string)$this->uriBuilder->buildUriFromRoute('dummy')
617  && $this->retUrl !== $this->getCloseUrl()
618  && $this->returnNewPageId
619  ) {
620  $this->retUrl .= '&id=' . $tce->substNEWwithIDs[$key];
621  }
622  }
623  } else {
624  $newEditConf[$tableName] = $tableCmds;
625  }
626  }
627  // Reset editconf if newEditConf has values
628  if (!empty($newEditConf)) {
629  $this->editconf = $newEditConf;
630  }
631  // Finally, set the editconf array in the "getvars" so they will be passed along in URLs as needed.
632  $this->R_URL_getvars['edit'] = ‪$this->editconf;
633  // Unset default values since we don't need them anymore.
634  unset($this->R_URL_getvars['defVals']);
635  // Recompile the store* values since editconf changed
636  $this->‪compileStoreData($request);
637  }
638  // See if any records was auto-created as new versions?
639  if (!empty($tce->autoVersionIdMap)) {
640  $this->‪fixWSversioningInEditConf($tce->autoVersionIdMap);
641  }
642  // If a document is saved and a new one is created right after.
643  if (isset($parsedBody['_savedoknew']) && is_array($this->editconf)) {
644  if ($redirect = $this->‪closeDocument(self::DOCUMENT_CLOSE_MODE_NO_REDIRECT, $request)) {
645  return $redirect;
646  }
647  // Find the current table
648  reset($this->editconf);
649  $nTable = (string)key($this->editconf);
650  // Finding the first id, getting the records pid+uid
651  reset($this->editconf[$nTable]);
652  $nUid = (int)key($this->editconf[$nTable]);
653  $recordFields = 'pid,uid';
654  if (BackendUtility::isTableWorkspaceEnabled($nTable)) {
655  $recordFields .= ',t3ver_oid';
656  }
657  $nRec = BackendUtility::getRecord($nTable, $nUid, $recordFields);
658  // Determine insertion mode: 'top' is self-explaining,
659  // otherwise new elements are inserted after one using a negative uid
660  $insertRecordOnTop = ($this->‪getTsConfigOption($nTable, 'saveDocNew') === 'top');
661  // Setting a blank editconf array for a new record:
662  $this->editconf = [];
663  // Determine related page ID for regular live context
664  if ((int)($nRec['t3ver_oid'] ?? 0) === 0) {
665  if ($insertRecordOnTop) {
666  $relatedPageId = $nRec['pid'];
667  } else {
668  $relatedPageId = -$nRec['uid'];
669  }
670  } else {
671  // Determine related page ID for workspace context
672  if ($insertRecordOnTop) {
673  // Fetch live version of workspace version since the pid value is always -1 in workspaces
674  $liveRecord = BackendUtility::getRecord($nTable, $nRec['t3ver_oid'], $recordFields);
675  $relatedPageId = $liveRecord['pid'];
676  } else {
677  // Use uid of live version of workspace version
678  $relatedPageId = -$nRec['t3ver_oid'];
679  }
680  }
681  $this->editconf[$nTable][$relatedPageId] = 'new';
682  // Finally, set the editconf array in the "getvars" so they will be passed along in URLs as needed.
683  $this->R_URL_getvars['edit'] = ‪$this->editconf;
684  // Recompile the store* values since editconf changed...
685  $this->‪compileStoreData($request);
686  }
687  // If a document should be duplicated.
688  if (isset($parsedBody['_duplicatedoc']) && is_array($this->editconf)) {
689  $this->‪closeDocument(self::DOCUMENT_CLOSE_MODE_NO_REDIRECT, $request);
690  // Find current table
691  reset($this->editconf);
692  $nTable = (string)key($this->editconf);
693  // Find the first id, getting the records pid+uid
694  reset($this->editconf[$nTable]);
695  $nUid = key($this->editconf[$nTable]);
697  $nUid = $tce->substNEWwithIDs[$nUid];
698  }
699 
700  $recordFields = 'pid,uid';
701  if (BackendUtility::isTableWorkspaceEnabled($nTable)) {
702  $recordFields .= ',t3ver_oid';
703  }
704  $nRec = BackendUtility::getRecord($nTable, $nUid, $recordFields);
705 
706  // Setting a blank editconf array for a new record:
707  $this->editconf = [];
708 
709  if ((int)($nRec['t3ver_oid'] ?? 0) === 0) {
710  $relatedPageId = -$nRec['uid'];
711  } else {
712  $relatedPageId = -$nRec['t3ver_oid'];
713  }
714 
715  $duplicateTce = GeneralUtility::makeInstance(DataHandler::class);
716 
717  $duplicateCmd = [
718  $nTable => [
719  $nUid => [
720  'copy' => $relatedPageId,
721  ],
722  ],
723  ];
724 
725  $duplicateTce->start([], $duplicateCmd);
726  $duplicateTce->process_cmdmap();
727 
728  $duplicateMappingArray = $duplicateTce->copyMappingArray;
729  $duplicateUid = $duplicateMappingArray[$nTable][$nUid];
730 
731  if ($nTable === 'pages') {
732  BackendUtility::setUpdateSignal('updatePageTree');
733  }
734 
735  $this->editconf[$nTable][$duplicateUid] = 'edit';
736  // Finally, set the editconf array in the "getvars" so they will be passed along in URLs as needed.
737  $this->R_URL_getvars['edit'] = ‪$this->editconf;
738  // Recompile the store* values since editconf changed...
739  $this->‪compileStoreData($request);
740 
741  // Inform the user of the duplication
742  $flashMessage = GeneralUtility::makeInstance(
743  FlashMessage::class,
744  $this->‪getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.recordDuplicated'),
745  '',
747  );
748  $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
749  $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
750  $defaultFlashMessageQueue->enqueue($flashMessage);
751  }
752  $tce->printLogErrorMessages();
753 
754  if ((int)$this->closeDoc < self::DOCUMENT_CLOSE_MODE_DEFAULT
755  || isset($parsedBody['_saveandclosedok'])
756  ) {
757  // Redirect if element should be closed after save
758  return $this->‪closeDocument((int)abs($this->closeDoc), $request);
759  }
760  return null;
761  }
762 
768  protected function ‪init(ServerRequestInterface $request): void
769  {
770  $parsedBody = $request->getParsedBody();
771  $queryParams = $request->getQueryParams();
772 
773  $beUser = $this->‪getBackendUser();
774 
775  $this->popViewId = (int)($parsedBody['popViewId'] ?? $queryParams['popViewId'] ?? 0);
776  $this->viewUrl = (string)($parsedBody['viewUrl'] ?? $queryParams['viewUrl'] ?? '');
777  $this->recTitle = (string)($parsedBody['recTitle'] ?? $queryParams['recTitle'] ?? '');
778  $this->noView = (bool)($parsedBody['noView'] ?? $queryParams['noView'] ?? false);
779  $this->perms_clause = $beUser->getPagePermsClause(‪Permission::PAGE_SHOW);
780 
781  // Preview code is implicit only generated for GET requests, having the query
782  // parameters "popViewId" (the preview page id) and "showPreview" set.
783  if ($this->popViewId && ($queryParams['showPreview'] ?? false)) {
784  // Generate the preview code (markup), which is added to the module body later
785  $this->previewCode = $this->‪generatePreviewCode();
786  // After generating the preview code, those params should not longer be applied to the form
787  // action, as this would otherwise always refresh the preview window on saving the record.
788  unset($this->R_URL_getvars['showPreview'], $this->R_URL_getvars['popViewId']);
789  }
790 
791  // Set other internal variables:
792  $this->R_URL_getvars['returnUrl'] = ‪$this->retUrl;
793  $this->R_URI = $this->R_URL_parts['path'] . ‪HttpUtility::buildQueryString($this->R_URL_getvars, '?');
794 
795  $this->pageRenderer->addInlineLanguageLabelFile('EXT:backend/Resources/Private/Language/locallang_alt_doc.xlf');
796 
797  // Set context sensitive menu
798  $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/ContextMenu');
799 
800  $event = new ‪AfterFormEnginePageInitializedEvent($this, $request);
801  $this->eventDispatcher->dispatch($event);
802  }
803 
809  protected function ‪generatePreviewCode(): ?string
810  {
811  $array_keys = array_keys($this->editconf);
812  $this->previewData['table'] = reset($array_keys) ?: null;
813  $array_keys = array_keys($this->editconf[$this->previewData['table']]);
814  $this->previewData['id'] = reset($array_keys) ?: null;
815 
816  $previewPageId = $this->‪getPreviewPageId();
817  $anchorSection = $this->‪getPreviewUrlAnchorSection();
818  $previewPageRootLine = BackendUtility::BEgetRootLine($previewPageId);
819  $previewUrlParameters = $this->‪getPreviewUrlParameters($previewPageId);
820 
821  return ‪PreviewUriBuilder::create($previewPageId, $this->viewUrl)
822  ->withRootLine($previewPageRootLine)
823  ->withSection($anchorSection)
824  ->withAdditionalQueryParameters($previewUrlParameters)
825  ->buildImmediateActionElement([‪PreviewUriBuilder::OPTION_SWITCH_FOCUS => null]);
826  }
827 
834  protected function ‪getPreviewUrlParameters(int $previewPageId): string
835  {
836  $linkParameters = [];
837  $table = ($this->previewData['table'] ?? '') ?: ($this->firstEl['table'] ?? '');
838  $recordId = ($this->previewData['id'] ?? '') ?: ($this->firstEl['uid'] ?? '');
839  $previewConfiguration = BackendUtility::getPagesTSconfig($previewPageId)['TCEMAIN.']['preview.'][$table . '.'] ?? [];
840  $recordArray = BackendUtility::getRecord($table, $recordId);
841 
842  // language handling
843  $languageField = ‪$GLOBALS['TCA'][$table]['ctrl']['languageField'] ?? '';
844  if ($languageField && !empty($recordArray[$languageField])) {
845  $recordId = $this->‪resolvePreviewRecordId($table, $recordArray, $previewConfiguration);
846  $language = $recordArray[$languageField];
847  if ($language > 0) {
848  $linkParameters['L'] = $language;
849  }
850  }
851 
852  // Always use live workspace record uid for the preview
853  if (BackendUtility::isTableWorkspaceEnabled($table) && ($recordArray['t3ver_oid'] ?? 0) > 0) {
854  $recordId = $recordArray['t3ver_oid'];
855  }
856 
857  // map record data to GET parameters
858  if (isset($previewConfiguration['fieldToParameterMap.'])) {
859  foreach ($previewConfiguration['fieldToParameterMap.'] as $field => $parameterName) {
860  $value = $recordArray[$field] ?? '';
861  if ($field === 'uid') {
862  $value = $recordId;
863  }
864  $linkParameters[$parameterName] = $value;
865  }
866  }
867 
868  // add/override parameters by configuration
869  if (isset($previewConfiguration['additionalGetParameters.'])) {
870  $additionalGetParameters = [];
872  $additionalGetParameters,
873  $previewConfiguration['additionalGetParameters.']
874  );
875  $linkParameters = array_replace($linkParameters, $additionalGetParameters);
876  }
877 
878  return ‪HttpUtility::buildQueryString($linkParameters, '&');
879  }
880 
888  protected function ‪resolvePreviewRecordId(string $table, array $recordArray, array $previewConfiguration): int
889  {
890  $l10nPointer = ‪$GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'] ?? '';
891  if ($l10nPointer
892  && !empty($recordArray[$l10nPointer])
893  && (
894  // not set -> default to true
895  !isset($previewConfiguration['useDefaultLanguageRecord'])
896  // or set -> use value
897  || $previewConfiguration['useDefaultLanguageRecord']
898  )
899  ) {
900  return (int)$recordArray[$l10nPointer];
901  }
902  return (int)$recordArray['uid'];
903  }
904 
910  protected function ‪getPreviewUrlAnchorSection(): string
911  {
912  $table = ($this->previewData['table'] ?? '') ?: ($this->firstEl['table'] ?? '');
913  $recordId = ($this->previewData['id'] ?? '') ?: ($this->firstEl['uid'] ?? '');
914 
915  return $table === 'tt_content' ? '#c' . (int)$recordId : '';
916  }
917 
923  protected function ‪getPreviewPageId(): int
924  {
925  $previewPageId = 0;
926  $table = ($this->previewData['table'] ?? '') ?: ($this->firstEl['table'] ?? '');
927  $recordId = ($this->previewData['id'] ?? '') ?: ($this->firstEl['uid'] ?? '');
928  $pageId = $this->popViewId ?: ‪$this->viewId;
929 
930  if ($table === 'pages') {
931  $currentPageId = (int)$recordId;
932  } else {
933  $currentPageId = ‪MathUtility::convertToPositiveInteger($pageId);
934  }
935 
936  $previewConfiguration = BackendUtility::getPagesTSconfig($currentPageId)['TCEMAIN.']['preview.'][$table . '.'] ?? [];
937 
938  if (isset($previewConfiguration['previewPageId'])) {
939  $previewPageId = (int)$previewConfiguration['previewPageId'];
940  }
941  // if no preview page was configured
942  if (!$previewPageId) {
943  $rootPageData = null;
944  $rootLine = BackendUtility::BEgetRootLine($currentPageId);
945  $currentPage = (array)(reset($rootLine) ?: []);
946  if ($this->‪canViewDoktype($currentPage)) {
947  // try the current page
948  $previewPageId = $currentPageId;
949  } else {
950  // or search for the root page
951  foreach ($rootLine as $page) {
952  if ($page['is_siteroot']) {
953  $rootPageData = $page;
954  break;
955  }
956  }
957  $previewPageId = isset($rootPageData)
958  ? (int)$rootPageData['uid']
959  : $currentPageId;
960  }
961  }
962 
963  $this->popViewId = $previewPageId;
964 
965  return $previewPageId;
966  }
967 
974  protected function ‪canViewDoktype(array $currentPage): bool
975  {
976  if (!isset($currentPage['uid']) || !($currentPage['doktype'] ?? false)) {
977  // In case the current page record is invalid, the element can not be viewed
978  return false;
979  }
980 
981  return !in_array((int)$currentPage['doktype'], [
985  ], true);
986  }
987 
997  protected function ‪parseAdditionalGetParameters(array &$parameters, array $typoScript)
998  {
999  foreach ($typoScript as $key => $value) {
1000  if (is_array($value)) {
1001  $key = rtrim($key, '.');
1002  $parameters[$key] = [];
1003  $this->‪parseAdditionalGetParameters($parameters[$key], $value);
1004  } else {
1005  $parameters[$key] = $value;
1006  }
1007  }
1008  }
1009 
1015  protected function ‪main(ServerRequestInterface $request): void
1016  {
1017  $body = $this->previewCode ?? '';
1018  // Begin edit
1019  if (is_array($this->editconf)) {
1020  $this->formResultCompiler = GeneralUtility::makeInstance(FormResultCompiler::class);
1021 
1022  // Creating the editing form, wrap it with buttons, document selector etc.
1023  $editForm = $this->‪makeEditForm();
1024  if ($editForm) {
1025  $this->firstEl = $this->elementsData ? reset($this->elementsData) : null;
1026  // Checking if the currently open document is stored in the list of "open documents" - if not, add it:
1027  if ((($this->docDat[1] ?? null) !== $this->storeUrlMd5 || !isset($this->docHandler[$this->storeUrlMd5]))
1028  && !$this->dontStoreDocumentRef
1029  ) {
1030  $this->docHandler[‪$this->storeUrlMd5] = [
1035  ];
1036  $this->‪getBackendUser()->‪pushModuleData('FormEngine', [$this->docHandler, $this->storeUrlMd5]);
1037  BackendUtility::setUpdateSignal('OpendocsController::updateNumber', count($this->docHandler));
1038  }
1039  $body .= $this->formResultCompiler->addCssFiles();
1040  $body .= $this->‪compileForm($editForm);
1041  $body .= $this->formResultCompiler->printNeededJSFunctions();
1042  $body .= '</form>';
1043  }
1044  }
1045 
1046  if ($this->firstEl === null) {
1047  // In case firstEl is null, no edit form could not be created. Therefore, add an
1048  // info box and remove the spinner, since it will never be resolved by FormEnigne.
1049  $this->moduleTemplate->setUiBlock(false);
1050  $body .= $this->‪getInfobox(
1051  $this->‪getLanguageService()->getLL('noEditForm.message'),
1052  $this->‪getLanguageService()->getLL('noEditForm'),
1053  );
1054  }
1055 
1056  // Access check...
1057  // The page will show only if there is a valid page and if this page may be viewed by the user
1058  $this->pageinfo = BackendUtility::readPageAccess($this->viewId, $this->perms_clause) ?: [];
1059  // Setting up the buttons and markers for doc header
1060  $this->‪resolveMetaInformation();
1061  $this->‪getButtons($request);
1062 
1063  // Create language switch options if the record is already persisted and it is a single record to edit
1064  if ($this->isSavedRecord && $this->‪isSingleRecordView()) {
1065  $this->‪languageSwitch(
1066  (string)($this->firstEl['table'] ?? ''),
1067  (int)($this->firstEl['uid'] ?? 0),
1068  isset($this->firstEl['pid']) ? (int)$this->firstEl['pid'] : null
1069  );
1070  }
1071  $this->moduleTemplate->setContent($body);
1072  }
1073 
1074  protected function ‪resolveMetaInformation(): void
1075  {
1076  $file = null;
1077  if (($this->firstEl['table'] ?? '') === 'sys_file_metadata' && (int)($this->firstEl['uid'] ?? 0) > 0) {
1078  $fileUid = (int)(BackendUtility::getRecord('sys_file_metadata', (int)$this->firstEl['uid'], 'file')['file'] ?? 0);
1079  try {
1080  $file = GeneralUtility::makeInstance(ResourceFactory::class)->getFileObject($fileUid);
1081  } catch (FileDoesNotExistException|InsufficientUserPermissionsException $e) {
1082  // do nothing when file is not accessible
1083  }
1084  }
1085  if ($file instanceof FileInterface) {
1086  $this->moduleTemplate->getDocHeaderComponent()->setMetaInformationForResource($file);
1087  } elseif ($this->pageinfo !== []) {
1088  $this->moduleTemplate->getDocHeaderComponent()->setMetaInformation($this->pageinfo);
1089  }
1090  }
1091 
1097  protected function ‪makeEditForm(): string
1098  {
1099  // Initialize variables
1100  $this->elementsData = [];
1101  $this->errorC = 0;
1102  $editForm = '';
1103  $beUser = $this->‪getBackendUser();
1104  // Traverse the GPvar edit array tables
1105  foreach ($this->editconf as $table => $conf) {
1106  if (!is_array($conf) || !(‪$GLOBALS['TCA'][$table] ?? false)) {
1107  // Skip for invalid config or in case no TCA exists
1108  continue;
1109  }
1110  if (!$beUser->check('tables_modify', $table)) {
1111  // Skip in case the user has insufficient permissions and increment the error counter
1112  $this->errorC++;
1113  continue;
1114  }
1115  // Traverse the keys/comments of each table (keys can be a comma list of uids)
1116  foreach ($conf as $cKey => $command) {
1117  if ($command !== 'edit' && $command !== 'new') {
1118  // Skip if invalid command
1119  continue;
1120  }
1121  // Get the ids:
1122  $ids = ‪GeneralUtility::trimExplode(',', $cKey, true);
1123  // Traverse the ids:
1124  foreach ($ids as $theUid) {
1125  // Don't save this document title in the document selector if the document is new.
1126  if ($command === 'new') {
1127  $this->dontStoreDocumentRef = 1;
1128  }
1129 
1130  try {
1131  $formDataGroup = GeneralUtility::makeInstance(TcaDatabaseRecord::class);
1132  $formDataCompiler = GeneralUtility::makeInstance(FormDataCompiler::class, $formDataGroup);
1133  $nodeFactory = GeneralUtility::makeInstance(NodeFactory::class);
1134 
1135  // Reset viewId - it should hold data of last entry only
1136  $this->viewId = 0;
1137 
1138  $formDataCompilerInput = [
1139  'tableName' => $table,
1140  'vanillaUid' => (int)$theUid,
1141  'command' => $command,
1142  'returnUrl' => $this->R_URI,
1143  ];
1144  if (is_array($this->overrideVals) && is_array($this->overrideVals[$table])) {
1145  $formDataCompilerInput['overrideValues'] = $this->overrideVals[$table];
1146  }
1147  if (!empty($this->defVals) && is_array($this->defVals)) {
1148  $formDataCompilerInput['defaultValues'] = ‪$this->defVals;
1149  }
1150 
1151  $formData = $formDataCompiler->compile($formDataCompilerInput);
1152 
1153  // Set this->viewId if possible
1154  if ($command === 'new'
1155  && $table !== 'pages'
1156  && !empty($formData['parentPageRow']['uid'])
1157  ) {
1158  $this->viewId = $formData['parentPageRow']['uid'];
1159  } else {
1160  if ($table === 'pages') {
1161  $this->viewId = $formData['databaseRow']['uid'];
1162  } elseif (!empty($formData['parentPageRow']['uid'])) {
1163  $this->viewId = $formData['parentPageRow']['uid'];
1164  }
1165  }
1166 
1167  // Determine if delete button can be shown
1168  $deleteAccess = false;
1169  if (
1170  $command === 'edit'
1171  || $command === 'new'
1172  ) {
1173  $permission = new Permission($formData['userPermissionOnPage']);
1174  if ($formData['tableName'] === 'pages') {
1175  $deleteAccess = $permission->get(‪Permission::PAGE_DELETE);
1176  } else {
1177  $deleteAccess = $permission->get(‪Permission::CONTENT_EDIT);
1178  }
1179  }
1180 
1181  // Display "is-locked" message
1182  if ($command === 'edit') {
1183  $lockInfo = BackendUtility::isRecordLocked($table, $formData['databaseRow']['uid']);
1184  if ($lockInfo) {
1185  $flashMessage = GeneralUtility::makeInstance(
1186  FlashMessage::class,
1187  $lockInfo['msg'],
1188  '',
1190  );
1191  $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
1192  $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
1193  $defaultFlashMessageQueue->enqueue($flashMessage);
1194  }
1195  }
1196 
1197  // Record title
1198  if (!$this->storeTitle) {
1199  $this->storeTitle = htmlspecialchars($this->recTitle ?: ($formData['recordTitle'] ?? ''));
1200  }
1201 
1202  $this->elementsData[] = [
1203  'table' => $table,
1204  'uid' => $formData['databaseRow']['uid'],
1205  'pid' => $formData['databaseRow']['pid'],
1206  'cmd' => $command,
1207  'deleteAccess' => $deleteAccess,
1208  ];
1209 
1210  if ($command !== 'new') {
1211  BackendUtility::lockRecords($table, $formData['databaseRow']['uid'], $table === 'tt_content' ? $formData['databaseRow']['pid'] : 0);
1212  }
1213 
1214  // Set list if only specific fields should be rendered. This will trigger
1215  // ListOfFieldsContainer instead of FullRecordContainer in OuterWrapContainer
1216  if ($this->columnsOnly) {
1217  if (is_array($this->columnsOnly)) {
1218  $formData['fieldListToRender'] = $this->columnsOnly[$table];
1219  } else {
1220  $formData['fieldListToRender'] = ‪$this->columnsOnly;
1221  }
1222  }
1223 
1224  $formData['renderType'] = 'outerWrapContainer';
1225  $formResult = $nodeFactory->create($formData)->render();
1226 
1227  $html = $formResult['html'];
1228 
1229  $formResult['html'] = '';
1230  $formResult['doSaveFieldName'] = 'doSave';
1231 
1232  // @todo: Put all the stuff into FormEngine as final "compiler" class
1233  // @todo: This is done here for now to not rewrite addCssFiles()
1234  // @todo: and printNeededJSFunctions() now
1235  $this->formResultCompiler->mergeResult($formResult);
1236 
1237  // Seems the pid is set as hidden field (again) at end?!
1238  if ($command === 'new') {
1239  // @todo: looks ugly
1240  $html .= LF
1241  . '<input type="hidden"'
1242  . ' name="data[' . htmlspecialchars($table) . '][' . htmlspecialchars($formData['databaseRow']['uid']) . '][pid]"'
1243  . ' value="' . (int)$formData['databaseRow']['pid'] . '" />';
1244  }
1245 
1246  $editForm .= $html;
1247  } catch (AccessDeniedException $e) {
1248  $this->errorC++;
1249  // Try to fetch error message from "recordInternals" be user object
1250  // @todo: This construct should be logged and localized and de-uglified
1251  $message = (!empty($beUser->errorMsg)) ? $beUser->errorMsg : $e->getMessage() . ' ' . $e->getCode();
1252  $title = $this->‪getLanguageService()
1253  ->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.noEditPermission');
1254  $editForm .= $this->‪getInfobox($message, $title);
1256  $editForm .= $this->‪getInfobox($e->getMessage());
1257  }
1258  } // End of for each uid
1259  }
1260  }
1261  return $editForm;
1262  }
1263 
1271  protected function ‪getInfobox(string $message, ?string $title = null): string
1272  {
1273  return '<div class="callout callout-danger">' .
1274  '<div class="media">' .
1275  '<div class="media-left">' .
1276  '<span class="fa-stack fa-lg callout-icon">' .
1277  '<i class="fa fa-circle fa-stack-2x"></i>' .
1278  '<i class="fa fa-times fa-stack-1x"></i>' .
1279  '</span>' .
1280  '</div>' .
1281  '<div class="media-body">' .
1282  ($title ? '<h4 class="callout-title">' . htmlspecialchars($title) . '</h4>' : '') .
1283  '<div class="callout-body">' . htmlspecialchars($message) . '</div>' .
1284  '</div>' .
1285  '</div>' .
1286  '</div>';
1287  }
1288 
1294  protected function ‪getButtons(ServerRequestInterface $request): void
1295  {
1296  $buttonBar = $this->moduleTemplate->getDocHeaderComponent()->getButtonBar();
1297 
1298  if (!empty($this->firstEl)) {
1299  $record = BackendUtility::getRecord($this->firstEl['table'], $this->firstEl['uid']);
1300  $TCActrl = ‪$GLOBALS['TCA'][$this->firstEl['table']]['ctrl'];
1301 
1302  $this->‪setIsSavedRecord();
1303 
1304  $sysLanguageUid = 0;
1305  if (
1306  $this->isSavedRecord
1307  && isset($TCActrl['languageField'], $record[$TCActrl['languageField']])
1308  ) {
1309  $sysLanguageUid = (int)$record[$TCActrl['languageField']];
1310  } elseif (isset($this->defVals['sys_language_uid'])) {
1311  $sysLanguageUid = (int)$this->defVals['sys_language_uid'];
1312  }
1313 
1314  $l18nParent = isset($TCActrl['transOrigPointerField'], $record[$TCActrl['transOrigPointerField']])
1315  ? (int)$record[$TCActrl['transOrigPointerField']]
1316  : 0;
1317 
1318  $this->‪setIsPageInFreeTranslationMode($record, $sysLanguageUid);
1319 
1321 
1322  // Show buttons when table is not read-only
1323  if (
1324  !$this->errorC
1325  && !(‪$GLOBALS['TCA'][$this->firstEl['table']]['ctrl']['readOnly'] ?? false)
1326  ) {
1329  if ($this->firstEl['cmd'] !== 'new') {
1331  $buttonBar,
1333  4,
1334  $sysLanguageUid,
1335  $l18nParent
1336  );
1338  $buttonBar,
1340  5,
1341  $sysLanguageUid,
1342  $l18nParent
1343  );
1344  }
1348  }
1349  }
1354  }
1355 
1359  protected function ‪setIsSavedRecord()
1360  {
1361  if (!is_bool($this->isSavedRecord)) {
1362  $this->isSavedRecord = (
1363  $this->firstEl['cmd'] !== 'new'
1364  && ‪MathUtility::canBeInterpretedAsInteger($this->firstEl['uid'])
1365  );
1366  }
1367  }
1368 
1374  protected function ‪isInconsistentLanguageHandlingAllowed(): bool
1375  {
1376  $allowInconsistentLanguageHandling = BackendUtility::getPagesTSconfig(
1377  $this->pageinfo['uid'] ?? 0
1378  )['mod']['web_layout']['allowInconsistentLanguageHandling'] ?? ['value' => '0'];
1379 
1380  return $allowInconsistentLanguageHandling['value'] === '1';
1381  }
1382 
1389  protected function ‪setIsPageInFreeTranslationMode($record, int $sysLanguageUid)
1390  {
1391  if ($this->firstEl['table'] === 'tt_content') {
1392  if (!$this->isSavedRecord) {
1393  $this->isPageInFreeTranslationMode = $this->‪getFreeTranslationMode(
1394  (int)($this->pageinfo['uid'] ?? 0),
1395  (int)($this->defVals['colPos'] ?? 0),
1396  $sysLanguageUid
1397  );
1398  } else {
1399  $this->isPageInFreeTranslationMode = $this->‪getFreeTranslationMode(
1400  (int)($this->pageinfo['uid'] ?? 0),
1401  (int)($record['colPos'] ?? 0),
1402  $sysLanguageUid
1403  );
1404  }
1405  }
1406  }
1407 
1416  protected function ‪getFreeTranslationMode(int $page, int $column, int $language): bool
1417  {
1418  $freeTranslationMode = false;
1420  if (
1421  $this->‪getConnectedContentElementTranslationsCount($page, $column, $language) === 0
1422  && $this->‪getStandAloneContentElementTranslationsCount($page, $column, $language) >= 0
1423  ) {
1424  $freeTranslationMode = true;
1425  }
1426 
1427  return $freeTranslationMode;
1428  }
1429 
1437  protected function ‪registerCloseButtonToButtonBar(ButtonBar $buttonBar, string $position, int $group)
1438  {
1439  $closeButton = $buttonBar->makeLinkButton()
1440  ->setHref('#')
1441  ->setClasses('t3js-editform-close')
1442  ->setTitle($this->‪getLanguageService()->sL(
1443  'LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:rm.closeDoc'
1444  ))
1445  ->setShowLabelText(true)
1446  ->setIcon($this->iconFactory->getIcon('actions-close', ‪Icon::SIZE_SMALL));
1447 
1448  $buttonBar->addButton($closeButton, $position, $group);
1449  }
1450 
1458  protected function ‪registerSaveButtonToButtonBar(‪ButtonBar $buttonBar, string $position, int $group)
1459  {
1460  $saveButton = $buttonBar->‪makeInputButton()
1461  ->‪setForm('EditDocumentController')
1462  ->‪setIcon($this->iconFactory->getIcon('actions-document-save', ‪Icon::SIZE_SMALL))
1463  ->setName('_savedok')
1464  ->setShowLabelText(true)
1465  ->setTitle($this->‪getLanguageService()->sL(
1466  'LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:rm.saveDoc'
1467  ))
1468  ->setValue('1');
1469 
1470  $buttonBar->‪addButton($saveButton, $position, $group);
1471  }
1472 
1480  protected function ‪registerViewButtonToButtonBar(ButtonBar $buttonBar, string $position, int $group)
1481  {
1482  if (
1483  $this->viewId // Pid to show the record
1484  && !$this->noView // Passed parameter
1485  && !empty($this->firstEl['table']) // No table
1486 
1487  // @TODO: TsConfig option should change to viewDoc
1488  && $this->‪getTsConfigOption($this->firstEl['table'], 'saveDocView')
1489  ) {
1490  $classNames = 't3js-editform-view';
1491 
1492  $pagesTSconfig = BackendUtility::getPagesTSconfig($this->pageinfo['uid'] ?? 0);
1493 
1494  if (isset($pagesTSconfig['TCEMAIN.']['preview.']['disableButtonForDokType'])) {
1495  $excludeDokTypes = ‪GeneralUtility::intExplode(
1496  ',',
1497  $pagesTSconfig['TCEMAIN.']['preview.']['disableButtonForDokType'],
1498  true
1499  );
1500  } else {
1501  // exclude sysfolders, spacers and recycler by default
1502  $excludeDokTypes = [
1506  ];
1507  }
1508 
1509  if (
1510  !in_array((int)($this->pageinfo['doktype'] ?? 0), $excludeDokTypes, true)
1511  || isset($pagesTSconfig['TCEMAIN.']['preview.'][$this->firstEl['table'] . '.']['previewPageId'])
1512  ) {
1513  $previewPageId = $this->‪getPreviewPageId();
1514  try {
1515  $previewUrl = BackendUtility::getPreviewUrl(
1516  $previewPageId,
1517  '',
1518  BackendUtility::BEgetRootLine($previewPageId),
1520  $this->viewUrl,
1521  $this->‪getPreviewUrlParameters($previewPageId)
1522  );
1523 
1524  $viewButton = $buttonBar->makeLinkButton()
1525  ->setHref($previewUrl)
1526  ->setIcon($this->iconFactory->getIcon('actions-view', ‪Icon::SIZE_SMALL))
1527  ->setShowLabelText(true)
1528  ->setTitle($this->‪getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:rm.viewDoc'));
1529 
1530  if (!$this->isSavedRecord) {
1531  if ($this->firstEl['table'] === 'pages') {
1532  $viewButton->setDataAttributes(['is-new' => '']);
1533  }
1534  }
1535 
1536  if ($classNames !== '') {
1537  $viewButton->setClasses($classNames);
1538  }
1539 
1540  $buttonBar->addButton($viewButton, $position, $group);
1541  } catch (UnableToLinkToPageException $e) {
1542  // Do not add any button
1543  }
1544  }
1545  }
1546  }
1547 
1557  protected function ‪registerNewButtonToButtonBar(
1558  ButtonBar $buttonBar,
1559  string $position,
1560  int $group,
1561  int $sysLanguageUid,
1562  int $l18nParent
1563  ) {
1564  if (
1565  $this->firstEl['table'] !== 'sys_file_metadata'
1566  && !empty($this->firstEl['table'])
1567  && (
1568  (
1569  (
1571  || $this->isPageInFreeTranslationMode
1572  )
1573  && $this->firstEl['table'] === 'tt_content'
1574  )
1575  || (
1576  $this->firstEl['table'] !== 'tt_content'
1577  && (
1578  $sysLanguageUid === 0
1579  || $l18nParent === 0
1580  )
1581  )
1582  )
1583  && $this->‪getTsConfigOption($this->firstEl['table'], 'saveDocNew')
1584  ) {
1585  $classNames = 't3js-editform-new';
1586 
1587  $newButton = $buttonBar->makeLinkButton()
1588  ->setHref('#')
1589  ->setIcon($this->iconFactory->getIcon('actions-add', ‪Icon::SIZE_SMALL))
1590  ->setShowLabelText(true)
1591  ->setTitle($this->‪getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:rm.newDoc'));
1592 
1593  if (!$this->isSavedRecord) {
1594  $newButton->setDataAttributes(['is-new' => '']);
1595  }
1596 
1597  if ($classNames !== '') {
1598  $newButton->setClasses($classNames);
1599  }
1600 
1601  $buttonBar->addButton($newButton, $position, $group);
1602  }
1603  }
1604 
1614  protected function ‪registerDuplicationButtonToButtonBar(
1615  ButtonBar $buttonBar,
1616  string $position,
1617  int $group,
1618  int $sysLanguageUid,
1619  int $l18nParent
1620  ) {
1621  if (
1622  $this->firstEl['table'] !== 'sys_file_metadata'
1623  && !empty($this->firstEl['table'])
1624  && (
1625  (
1626  (
1628  || $this->isPageInFreeTranslationMode
1629  )
1630  && $this->firstEl['table'] === 'tt_content'
1631  )
1632  || (
1633  $this->firstEl['table'] !== 'tt_content'
1634  && (
1635  $sysLanguageUid === 0
1636  || $l18nParent === 0
1637  )
1638  )
1639  )
1640  && $this->‪getTsConfigOption($this->firstEl['table'], 'showDuplicate')
1641  ) {
1642  $classNames = 't3js-editform-duplicate';
1643 
1644  $duplicateButton = $buttonBar->makeLinkButton()
1645  ->setHref('#')
1646  ->setShowLabelText(true)
1647  ->setTitle($this->‪getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:rm.duplicateDoc'))
1648  ->setIcon($this->iconFactory->getIcon('actions-document-duplicates-select', ‪Icon::SIZE_SMALL));
1649 
1650  if (!$this->isSavedRecord) {
1651  $duplicateButton->setDataAttributes(['is-new' => '']);
1652  }
1653 
1654  if ($classNames !== '') {
1655  $duplicateButton->setClasses($classNames);
1656  }
1657 
1658  $buttonBar->addButton($duplicateButton, $position, $group);
1659  }
1660  }
1661 
1669  protected function ‪registerDeleteButtonToButtonBar(ButtonBar $buttonBar, string $position, int $group)
1670  {
1671  if (
1672  $this->firstEl['deleteAccess']
1673  && !$this->‪getDisableDelete()
1674  && !$this->‪isRecordCurrentBackendUser()
1675  && $this->isSavedRecord
1676  && $this->‪isSingleRecordView()
1677  ) {
1678  $classNames = 't3js-editform-delete-record';
1680  if ($this->firstEl['table'] === 'pages') {
1681  parse_str((string)parse_url(‪$returnUrl, PHP_URL_QUERY), $queryParams);
1682  if (
1683  isset($queryParams['route'], $queryParams['id'])
1684  && (string)$this->firstEl['uid'] === (string)$queryParams['id']
1685  ) {
1686  // TODO: Use the page's pid instead of 0, this requires a clean API to manipulate the page
1687  // tree from the outside to be able to mark the pid as active
1688  ‪$returnUrl = (string)$this->uriBuilder->buildUriFromRoutePath($queryParams['route'], ['id' => 0]);
1689  }
1690  }
1691 
1692  $referenceIndex = GeneralUtility::makeInstance(ReferenceIndex::class);
1693  $numberOfReferences = $referenceIndex->getNumberOfReferencedRecords(
1694  $this->firstEl['table'],
1695  (int)$this->firstEl['uid']
1696  );
1697 
1698  $referenceCountMessage = BackendUtility::referenceCount(
1699  $this->firstEl['table'],
1700  (string)(int)$this->firstEl['uid'],
1701  $this->‪getLanguageService()->sL(
1702  'LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.referencesToRecord'
1703  ),
1704  (string)$numberOfReferences
1705  );
1706  $translationCountMessage = BackendUtility::translationCount(
1707  $this->firstEl['table'],
1708  (string)(int)$this->firstEl['uid'],
1709  $this->‪getLanguageService()->sL(
1710  'LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.translationsOfRecord'
1711  )
1712  );
1713 
1714  $deleteUrl = (string)$this->uriBuilder->buildUriFromRoute('tce_db', [
1715  'cmd' => [
1716  $this->firstEl['table'] => [
1717  $this->firstEl['uid'] => [
1718  'delete' => '1',
1719  ],
1720  ],
1721  ],
1722  'redirect' => $this->retUrl,
1723  ]);
1724 
1725  $deleteButton = $buttonBar->makeLinkButton()
1726  ->setClasses($classNames)
1727  ->setDataAttributes([
1728  'return-url' => ‪$returnUrl,
1729  'uid' => $this->firstEl['uid'],
1730  'table' => $this->firstEl['table'],
1731  'reference-count-message' => $referenceCountMessage,
1732  'translation-count-message' => $translationCountMessage,
1733  ])
1734  ->setHref($deleteUrl)
1735  ->setIcon($this->iconFactory->getIcon('actions-edit-delete', ‪Icon::SIZE_SMALL))
1736  ->setShowLabelText(true)
1737  ->setTitle($this->‪getLanguageService()->getLL('deleteItem'));
1739  $buttonBar->addButton($deleteButton, $position, $group);
1740  }
1741  }
1742 
1750  protected function ‪registerHistoryButtonToButtonBar(ButtonBar $buttonBar, string $position, int $group)
1751  {
1752  if (
1753  $this->‪isSingleRecordView()
1754  && !empty($this->firstEl['table'])
1755  && $this->‪getTsConfigOption($this->firstEl['table'], 'showHistory')
1756  ) {
1757  $historyUrl = (string)$this->uriBuilder->buildUriFromRoute('record_history', [
1758  'element' => $this->firstEl['table'] . ':' . $this->firstEl['uid'],
1759  'returnUrl' => $this->R_URI,
1760  ]);
1761  $historyButton = $buttonBar->makeLinkButton()
1762  ->setHref($historyUrl)
1763  ->setTitle($this->‪getLanguageService()->getLL('recordHistory'))
1764  ->setIcon($this->iconFactory->getIcon('actions-document-history-open', ‪Icon::SIZE_SMALL));
1765 
1766  $buttonBar->addButton($historyButton, $position, $group);
1767  }
1768  }
1769 
1777  protected function ‪registerColumnsOnlyButtonToButtonBar(ButtonBar $buttonBar, string $position, int $group)
1778  {
1779  if (
1780  $this->columnsOnly
1781  && $this->‪isSingleRecordView()
1782  ) {
1783  $columnsOnlyButton = $buttonBar->makeLinkButton()
1784  ->setHref($this->R_URI . '&columnsOnly=')
1785  ->setTitle($this->‪getLanguageService()->getLL('editWholeRecord'))
1786  ->setIcon($this->iconFactory->getIcon('actions-open', ‪Icon::SIZE_SMALL));
1787 
1788  $buttonBar->addButton($columnsOnlyButton, $position, $group);
1789  }
1790  }
1791 
1799  protected function ‪registerOpenInNewWindowButtonToButtonBar(ButtonBar $buttonBar, string $position, int $group, ServerRequestInterface $request)
1800  {
1801  $closeUrl = $this->‪getCloseUrl();
1802  if ($this->returnUrl !== $closeUrl) {
1803  // Generate a URL to the current edit form
1804  $arguments = $this->‪getUrlQueryParamsForCurrentRequest($request);
1805  $arguments['returnUrl'] = $closeUrl;
1806  $requestUri = (string)$this->uriBuilder->buildUriFromRoute('record_edit', $arguments);
1807  $openInNewWindowButton = $this->moduleTemplate->getDocHeaderComponent()->getButtonBar()
1808  ->makeLinkButton()
1809  ->setHref('#')
1810  ->setTitle($this->‪getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.openInNewWindow'))
1811  ->setIcon($this->iconFactory->getIcon('actions-window-open', ‪Icon::SIZE_SMALL))
1812  ->setDataAttributes([
1813  'dispatch-action' => 'TYPO3.WindowManager.localOpen',
1814  'dispatch-args' => GeneralUtility::jsonEncodeForHtmlAttribute([
1815  $requestUri,
1816  true, // switchFocus
1817  md5($this->R_URI), // windowName,
1818  'width=670,height=500,status=0,menubar=0,scrollbars=1,resizable=1', // windowFeatures
1819  ]),
1820  ]);
1821  $buttonBar->addButton($openInNewWindowButton, $position, $group);
1822  }
1823  }
1824 
1833  protected function ‪registerShortcutButtonToButtonBar(ButtonBar $buttonBar, string $position, int $group, ServerRequestInterface $request)
1834  {
1835  if ($this->returnUrl !== $this->‪getCloseUrl()) {
1836  $arguments = $this->‪getUrlQueryParamsForCurrentRequest($request);
1837  $shortCutButton = $this->moduleTemplate->getDocHeaderComponent()->getButtonBar()->makeShortcutButton();
1838  $shortCutButton
1839  ->setRouteIdentifier('record_edit')
1840  ->setDisplayName($this->‪getShortcutTitle($request))
1841  ->setArguments($arguments);
1842  $buttonBar->addButton($shortCutButton, $position, $group);
1843  }
1844  }
1845 
1846  protected function ‪getUrlQueryParamsForCurrentRequest(ServerRequestInterface $request): array
1847  {
1848  $queryParams = $request->getQueryParams();
1849  $potentialArguments = [
1850  'edit',
1851  'defVals',
1852  'overrideVals',
1853  'columnsOnly',
1854  'returnNewPageId',
1855  'noView',
1856  ];
1857  $arguments = [];
1858  foreach ($potentialArguments as $argument) {
1859  if (!empty($queryParams[$argument])) {
1860  $arguments[$argument] = $queryParams[$argument];
1861  }
1862  }
1863  return $arguments;
1864  }
1865 
1873  protected function ‪registerCshButtonToButtonBar(ButtonBar $buttonBar, string $position, int $group)
1874  {
1875  $cshButton = $buttonBar->makeHelpButton()->setModuleName('xMOD_csh_corebe')->setFieldName('TCEforms');
1876 
1877  $buttonBar->addButton($cshButton, $position, $group);
1878  }
1879 
1888  protected function ‪getConnectedContentElementTranslationsCount(int $page, int $column, int $language): int
1889  {
1890  $queryBuilder = $this->‪getQueryBuilderForTranslationMode($page, $column, $language);
1891 
1892  return (int)$queryBuilder
1893  ->andWhere(
1894  $queryBuilder->expr()->gt(
1895  ‪$GLOBALS['TCA']['tt_content']['ctrl']['transOrigPointerField'],
1896  $queryBuilder->createNamedParameter(0, ‪Connection::PARAM_INT)
1897  )
1898  )
1899  ->executeQuery()
1900  ->fetchOne();
1901  }
1902 
1911  protected function ‪getStandAloneContentElementTranslationsCount(int $page, int $column, int $language): int
1912  {
1913  $queryBuilder = $this->‪getQueryBuilderForTranslationMode($page, $column, $language);
1914 
1915  return (int)$queryBuilder
1916  ->andWhere(
1917  $queryBuilder->expr()->eq(
1918  ‪$GLOBALS['TCA']['tt_content']['ctrl']['transOrigPointerField'],
1919  $queryBuilder->createNamedParameter(0, ‪Connection::PARAM_INT)
1920  )
1921  )
1922  ->executeQuery()
1923  ->fetchOne();
1924  }
1925 
1934  protected function ‪getQueryBuilderForTranslationMode(int $page, int $column, int $language): QueryBuilder
1935  {
1936  $languageField = ‪$GLOBALS['TCA']['tt_content']['ctrl']['languageField'];
1937 
1938  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
1939  ->getQueryBuilderForTable('tt_content');
1940 
1941  $queryBuilder->getRestrictions()
1942  ->removeAll()
1943  ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
1944  ->add(GeneralUtility::makeInstance(WorkspaceRestriction::class, $this->‪getBackendUser()->workspace));
1945 
1946  return $queryBuilder
1947  ->count('uid')
1948  ->from('tt_content')
1949  ->where(
1950  $queryBuilder->expr()->eq(
1951  'pid',
1952  $queryBuilder->createNamedParameter($page, ‪Connection::PARAM_INT)
1953  ),
1954  $queryBuilder->expr()->eq(
1955  $languageField,
1956  $queryBuilder->createNamedParameter($language, ‪Connection::PARAM_INT)
1957  ),
1958  $queryBuilder->expr()->eq(
1959  'colPos',
1960  $queryBuilder->createNamedParameter($column, ‪Connection::PARAM_INT)
1961  )
1962  );
1963  }
1964 
1971  protected function ‪compileForm(string $editForm): string
1972  {
1973  $formContent = '
1974  <form
1975  action="' . htmlspecialchars($this->R_URI) . '"
1976  method="post"
1977  enctype="multipart/form-data"
1978  name="editform"
1979  id="EditDocumentController"
1980  >
1981  ' . $editForm . '
1982  <input type="hidden" name="returnUrl" value="' . htmlspecialchars($this->retUrl) . '" />
1983  <input type="hidden" name="viewUrl" value="' . htmlspecialchars($this->viewUrl) . '" />
1984  <input type="hidden" name="popViewId" value="' . htmlspecialchars((string)$this->viewId) . '" />
1985  <input type="hidden" name="closeDoc" value="0" />
1986  <input type="hidden" name="doSave" value="0" />';
1987  if ($this->returnNewPageId) {
1988  $formContent .= '<input type="hidden" name="returnNewPageId" value="1" />';
1989  }
1990  return $formContent;
1991  }
1992 
1999  protected function ‪updateInlineView($uc, DataHandler $dataHandler)
2000  {
2001  $backendUser = $this->‪getBackendUser();
2002  if (!isset($uc['inlineView']) || !is_array($uc['inlineView'])) {
2003  return;
2004  }
2005  $inlineView = (array)json_decode(is_string($backendUser->uc['inlineView'] ?? false) ? $backendUser->uc['inlineView'] : '', true);
2006  foreach ($uc['inlineView'] as $topTable => $topRecords) {
2007  foreach ($topRecords as $topUid => $childElements) {
2008  foreach ($childElements as $childTable => $childRecords) {
2009  $uids = array_keys($dataHandler->substNEWwithIDs_table, $childTable);
2010  if (!empty($uids)) {
2011  $newExpandedChildren = [];
2012  foreach ($childRecords as $childUid => $state) {
2013  if ($state && in_array($childUid, $uids)) {
2014  $newChildUid = $dataHandler->substNEWwithIDs[$childUid];
2015  $newExpandedChildren[] = $newChildUid;
2016  }
2017  }
2018  // Add new expanded child records to UC (if any):
2019  if (!empty($newExpandedChildren)) {
2020  $inlineViewCurrent = &$inlineView[$topTable][$topUid][$childTable];
2021  if (is_array($inlineViewCurrent)) {
2022  $inlineViewCurrent = array_unique(array_merge($inlineViewCurrent, $newExpandedChildren));
2023  } else {
2024  $inlineViewCurrent = $newExpandedChildren;
2025  }
2026  }
2027  }
2028  }
2029  }
2030  }
2031  $backendUser->uc['inlineView'] = json_encode($inlineView);
2032  $backendUser->writeUC();
2033  }
2040  protected function ‪getDisableDelete(): bool
2041  {
2042  $disableDelete = false;
2043  if ($this->firstEl['table'] === 'sys_file_metadata') {
2044  $row = BackendUtility::getRecord('sys_file_metadata', $this->firstEl['uid'], 'sys_language_uid');
2045  $languageUid = $row['sys_language_uid'];
2046  if ($languageUid === 0) {
2047  $disableDelete = true;
2048  }
2049  } else {
2050  $disableDelete = (bool)$this->‪getTsConfigOption($this->firstEl['table'] ?? '', 'disableDelete');
2051  }
2052  return $disableDelete;
2053  }
2054 
2060  protected function ‪isRecordCurrentBackendUser(): bool
2061  {
2062  $backendUser = $this->‪getBackendUser();
2063 
2064  return $this->firstEl['table'] === 'be_users'
2065  && (int)($this->firstEl['uid'] ?? 0) === (int)$backendUser->user[$backendUser->userid_column];
2066  }
2067 
2074  protected function ‪getCloseUrl(): string
2075  {
2076  return ‪PathUtility::getPublicResourceWebPath('EXT:backend/Resources/Public/Html/Close.html');
2077  }
2078 
2079  /***************************
2080  *
2081  * Localization stuff
2082  *
2083  ***************************/
2092  protected function ‪languageSwitch(string $table, int $uid, $pid = null)
2093  {
2094  $backendUser = $this->‪getBackendUser();
2095  $languageField = ‪$GLOBALS['TCA'][$table]['ctrl']['languageField'] ?? '';
2096  $transOrigPointerField = ‪$GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'] ?? '';
2097  // Table editable and activated for languages?
2098  if ($backendUser->check('tables_modify', $table)
2099  && $languageField
2100  && $transOrigPointerField
2101  ) {
2102  if ($pid === null) {
2103  $row = BackendUtility::getRecord($table, $uid, 'pid');
2104  $pid = $row['pid'];
2105  }
2106  // Get all available languages for the page
2107  // If editing a page, the translations of the current UID need to be fetched
2108  if ($table === 'pages') {
2109  $row = BackendUtility::getRecord($table, $uid, ‪$GLOBALS['TCA']['pages']['ctrl']['transOrigPointerField']);
2110  // Ensure the check is always done against the default language page
2111  $availableLanguages = $this->‪getLanguages(
2112  (int)$row[‪$GLOBALS['TCA']['pages']['ctrl']['transOrigPointerField']] ?: $uid,
2113  $table
2114  );
2115  } else {
2116  $availableLanguages = $this->‪getLanguages((int)$pid, $table);
2117  }
2118  // Remove default language, if user does not have access. This is necessary, since
2119  // the default language is always added when fetching the system languages (#88504).
2120  if (isset($availableLanguages[0]) && !$this->‪getBackendUser()->checkLanguageAccess(0)) {
2121  unset($availableLanguages[0]);
2122  }
2123  // Page available in other languages than default language?
2124  if (count($availableLanguages) > 1) {
2125  $rowsByLang = [];
2126  $fetchFields = 'uid,' . $languageField . ',' . $transOrigPointerField;
2127  // Get record in current language
2128  $rowCurrent = BackendUtility::getLiveVersionOfRecord($table, $uid, $fetchFields);
2129  if (!is_array($rowCurrent)) {
2130  $rowCurrent = BackendUtility::getRecord($table, $uid, $fetchFields);
2131  }
2132  $currentLanguage = (int)$rowCurrent[$languageField];
2133  // Disabled for records with [all] language!
2134  if ($currentLanguage > -1) {
2135  // Get record in default language if needed
2136  if ($currentLanguage && $rowCurrent[$transOrigPointerField]) {
2137  $rowsByLang[0] = BackendUtility::getLiveVersionOfRecord(
2138  $table,
2139  $rowCurrent[$transOrigPointerField],
2140  $fetchFields
2141  );
2142  if (!is_array($rowsByLang[0])) {
2143  $rowsByLang[0] = BackendUtility::getRecord(
2144  $table,
2145  $rowCurrent[$transOrigPointerField],
2146  $fetchFields
2147  );
2148  }
2149  } else {
2150  $rowsByLang[$rowCurrent[$languageField]] = $rowCurrent;
2151  }
2152  // List of language id's that should not be added to the selector
2153  $noAddOption = [];
2154  if ($rowCurrent[$transOrigPointerField] || $currentLanguage === 0) {
2155  // Get record in other languages to see what's already available
2156 
2157  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
2158  ->getQueryBuilderForTable($table);
2159 
2160  $queryBuilder->getRestrictions()
2161  ->removeAll()
2162  ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
2163  ->add(GeneralUtility::makeInstance(WorkspaceRestriction::class, $backendUser->workspace));
2164 
2165  $result = $queryBuilder->select(...‪GeneralUtility::trimExplode(',', $fetchFields, true))
2166  ->from($table)
2167  ->where(
2168  $queryBuilder->expr()->eq(
2169  'pid',
2170  $queryBuilder->createNamedParameter($pid, ‪Connection::PARAM_INT)
2171  ),
2172  $queryBuilder->expr()->gt(
2173  $languageField,
2174  $queryBuilder->createNamedParameter(0, ‪Connection::PARAM_INT)
2175  ),
2176  $queryBuilder->expr()->eq(
2177  $transOrigPointerField,
2178  $queryBuilder->createNamedParameter($rowsByLang[0]['uid'], ‪Connection::PARAM_INT)
2179  )
2180  )
2181  ->executeQuery();
2182 
2183  while ($row = $result->fetchAssociative()) {
2184  if ($backendUser->workspace !== 0 && BackendUtility::isTableWorkspaceEnabled($table)) {
2185  $workspaceVersion = BackendUtility::getWorkspaceVersionOfRecord($backendUser->workspace, $table, $row['uid'], 'uid,t3ver_state');
2186  if (!empty($workspaceVersion)) {
2187  $versionState = ‪VersionState::cast($workspaceVersion['t3ver_state']);
2188  if ($versionState->equals(‪VersionState::DELETE_PLACEHOLDER)) {
2189  // If a workspace delete placeholder exists for this translation: Mark
2190  // this language as "don't add to selector" and continue with next row,
2191  // otherwise an edit link to a delete placeholder would be created, which
2192  // does not make sense.
2193  $noAddOption[] = (int)$row[$languageField];
2194  continue;
2195  }
2196  }
2197  }
2198  $rowsByLang[$row[$languageField]] = $row;
2199  }
2200  }
2201  $languageMenu = $this->moduleTemplate->getDocHeaderComponent()->getMenuRegistry()->makeMenu();
2202  $languageMenu->setIdentifier('_langSelector');
2203  foreach ($availableLanguages as $languageId => $language) {
2204  $selectorOptionLabel = $language['title'];
2205  // Create url for creating a localized record
2206  $addOption = true;
2207  $href = '';
2208  if (!isset($rowsByLang[$languageId])) {
2209  // Translation in this language does not exist
2210  if (!isset($rowsByLang[0]['uid'])) {
2211  // Don't add option since no default row to localize from exists
2212  // TODO: Actually tt_content is able to localize from another l10n_source then L=0.
2213  // This however is currently only possible via the translation wizard.
2214  $addOption = false;
2215  } else {
2216  // Build the link to add the localization
2217  $selectorOptionLabel .= ' [' . htmlspecialchars($this->‪getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.new')) . ']';
2218  $redirectUrl = (string)$this->uriBuilder->buildUriFromRoute('record_edit', [
2219  'justLocalized' => $table . ':' . $rowsByLang[0]['uid'] . ':' . $languageId,
2220  'returnUrl' => $this->retUrl,
2221  ]);
2222  $href = BackendUtility::getLinkToDataHandlerAction(
2223  '&cmd[' . $table . '][' . $rowsByLang[0]['uid'] . '][localize]=' . $languageId,
2224  $redirectUrl
2225  );
2226  }
2227  } else {
2228  $params = [
2229  'edit[' . $table . '][' . $rowsByLang[$languageId]['uid'] . ']' => 'edit',
2230  'returnUrl' => ‪$this->retUrl,
2231  ];
2232  if ($table === 'pages') {
2233  // Disallow manual adjustment of the language field for pages
2234  $params['overrideVals'] = [
2235  'pages' => [
2236  'sys_language_uid' => $languageId,
2237  ],
2238  ];
2239  }
2240  $href = (string)$this->uriBuilder->buildUriFromRoute('record_edit', $params);
2241  }
2242  if ($addOption && !in_array($languageId, $noAddOption, true)) {
2243  $menuItem = $languageMenu->makeMenuItem()
2244  ->setTitle($selectorOptionLabel)
2245  ->setHref($href);
2246  if ($languageId === $currentLanguage) {
2247  $menuItem->setActive(true);
2248  }
2249  $languageMenu->addMenuItem($menuItem);
2250  }
2251  }
2252  $this->moduleTemplate->getDocHeaderComponent()->getMenuRegistry()->addMenu($languageMenu);
2253  }
2254  }
2255  }
2256  }
2257 
2264  protected function ‪localizationRedirect(ServerRequestInterface $request): ?ResponseInterface
2265  {
2266  $justLocalized = $request->getQueryParams()['justLocalized'] ?? null;
2267 
2268  if (empty($justLocalized)) {
2269  return null;
2270  }
2271 
2272  [$table, $origUid, $language] = explode(':', $justLocalized);
2273 
2274  if (‪$GLOBALS['TCA'][$table]
2275  && ‪$GLOBALS['TCA'][$table]['ctrl']['languageField']
2276  && ‪$GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField']
2277  ) {
2278  $parsedBody = $request->getParsedBody();
2279  $queryParams = $request->getQueryParams();
2280 
2281  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table);
2282  $queryBuilder->getRestrictions()
2283  ->removeAll()
2284  ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
2285  ->add(GeneralUtility::makeInstance(WorkspaceRestriction::class, $this->‪getBackendUser()->workspace));
2286  $localizedRecord = $queryBuilder->select('uid')
2287  ->from($table)
2288  ->where(
2289  $queryBuilder->expr()->eq(
2290  ‪$GLOBALS['TCA'][$table]['ctrl']['languageField'],
2291  $queryBuilder->createNamedParameter($language, ‪Connection::PARAM_INT)
2292  ),
2293  $queryBuilder->expr()->eq(
2294  ‪$GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'],
2295  $queryBuilder->createNamedParameter($origUid, ‪Connection::PARAM_INT)
2296  )
2297  )
2298  ->executeQuery()
2299  ->fetchAssociative();
2300  ‪$returnUrl = $parsedBody['returnUrl'] ?? $queryParams['returnUrl'] ?? '';
2301  if (is_array($localizedRecord)) {
2302  // Create redirect response to self to edit just created record
2303  return new ‪RedirectResponse(
2304  (string)$this->uriBuilder->buildUriFromRoute(
2305  'record_edit',
2306  [
2307  'edit[' . $table . '][' . $localizedRecord['uid'] . ']' => 'edit',
2308  'returnUrl' => GeneralUtility::sanitizeLocalUrl(‪$returnUrl),
2309  ]
2310  ),
2311  303
2312  );
2313  }
2314  }
2315  return null;
2316  }
2317 
2327  protected function ‪getLanguages(int $id, string $table): array
2328  {
2329  // This usually happens when a non-pages record is added after another, so we are fetching the proper page ID
2330  if ($id < 0 && $table !== 'pages') {
2331  $pageId = $this->pageinfo['uid'] ?? null;
2332  if ($pageId !== null) {
2333  $pageId = (int)$pageId;
2334  } else {
2335  $fullRecord = BackendUtility::getRecord($table, abs($id));
2336  $pageId = (int)$fullRecord['pid'];
2337  }
2338  } else {
2339  if ($table === 'pages' && $id > 0) {
2340  $fullRecord = BackendUtility::getRecordWSOL('pages', $id);
2341  $id = (int)($fullRecord['t3ver_oid'] ?: $fullRecord['uid']);
2342  }
2343  $pageId = $id;
2344  }
2345  // Fetch the current translations of this page, to only show the ones where there is a page translation
2346  $allLanguages = array_filter(
2347  GeneralUtility::makeInstance(TranslationConfigurationProvider::class)->getSystemLanguages($pageId),
2348  static fn($language) => (int)$language['uid'] !== -1
2349  );
2350  if ($table !== 'pages' && $id > 0) {
2351  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
2352  $queryBuilder->getRestrictions()->removeAll()
2353  ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
2354  ->add(GeneralUtility::makeInstance(WorkspaceRestriction::class, $this->‪getBackendUser()->workspace));
2355  $statement = $queryBuilder->select('uid', ‪$GLOBALS['TCA']['pages']['ctrl']['languageField'])
2356  ->from('pages')
2357  ->where(
2358  $queryBuilder->expr()->eq(
2359  ‪$GLOBALS['TCA']['pages']['ctrl']['transOrigPointerField'],
2360  $queryBuilder->createNamedParameter($pageId, ‪Connection::PARAM_INT)
2361  )
2362  )
2363  ->executeQuery();
2364 
2365  $availableLanguages = [];
2366 
2367  if ($allLanguages[0] ?? false) {
2368  $availableLanguages = [
2369  0 => $allLanguages[0],
2370  ];
2371  }
2372 
2373  while ($row = $statement->fetchAssociative()) {
2374  $languageId = (int)$row[‪$GLOBALS['TCA']['pages']['ctrl']['languageField']];
2375  if (isset($allLanguages[$languageId])) {
2376  $availableLanguages[$languageId] = $allLanguages[$languageId];
2377  }
2378  }
2379  return $availableLanguages;
2380  }
2381  return $allLanguages;
2382  }
2383 
2389  protected function ‪fixWSversioningInEditConf($mapArray = false): void
2390  {
2391  // Traverse the editConf array
2392  if (is_array($this->editconf)) {
2393  // Tables:
2394  foreach ($this->editconf as $table => $conf) {
2395  if (is_array($conf) && ‪$GLOBALS['TCA'][$table]) {
2396  // Traverse the keys/comments of each table (keys can be a comma list of uids)
2397  $newConf = [];
2398  foreach ($conf as $cKey => ‪$cmd) {
2399  if (‪$cmd === 'edit') {
2400  // Traverse the ids:
2401  $ids = ‪GeneralUtility::trimExplode(',', $cKey, true);
2402  foreach ($ids as $idKey => $theUid) {
2403  if (is_array($mapArray)) {
2404  if ($mapArray[$table][$theUid] ?? false) {
2405  $ids[$idKey] = $mapArray[$table][$theUid];
2406  }
2407  } else {
2408  // Default, look for versions in workspace for record:
2409  $calcPRec = $this->‪getRecordForEdit((string)$table, (int)$theUid);
2410  if (is_array($calcPRec)) {
2411  // Setting UID again if it had changed, eg. due to workspace versioning.
2412  $ids[$idKey] = $calcPRec['uid'];
2413  }
2414  }
2415  }
2416  // Add the possibly manipulated IDs to the new-build newConf array:
2417  $newConf[implode(',', $ids)] = ‪$cmd;
2418  } else {
2419  $newConf[$cKey] = ‪$cmd;
2420  }
2421  }
2422  // Store the new conf array:
2423  $this->editconf[$table] = $newConf;
2424  }
2425  }
2426  }
2427  }
2428 
2436  protected function ‪getRecordForEdit(string $table, int $theUid)
2437  {
2438  $tableSupportsVersioning = BackendUtility::isTableWorkspaceEnabled($table);
2439  // Fetch requested record:
2440  $reqRecord = BackendUtility::getRecord($table, $theUid, 'uid,pid' . ($tableSupportsVersioning ? ',t3ver_oid' : ''));
2441  if (is_array($reqRecord)) {
2442  // If workspace is OFFLINE:
2443  if ($this->‪getBackendUser()->workspace != 0) {
2444  // Check for versioning support of the table:
2445  if ($tableSupportsVersioning) {
2446  // If the record is already a version of "something" pass it by.
2447  if ($reqRecord['t3ver_oid'] > 0 || (int)($reqRecord['t3ver_state'] ?? 0) === ‪VersionState::NEW_PLACEHOLDER) {
2448  // (If it turns out not to be a version of the current workspace there will be trouble, but
2449  // that is handled inside DataHandler then and in the interface it would clearly be an error of
2450  // links if the user accesses such a scenario)
2451  return $reqRecord;
2452  }
2453  // The input record was online and an offline version must be found or made:
2454  // Look for version of this workspace:
2455  $versionRec = BackendUtility::getWorkspaceVersionOfRecord(
2456  $this->‪getBackendUser()->workspace,
2457  $table,
2458  $reqRecord['uid'],
2459  'uid,pid,t3ver_oid'
2460  );
2461  return is_array($versionRec) ? $versionRec : $reqRecord;
2462  }
2463  // This means that editing cannot occur on this record because it was not supporting versioning
2464  // which is required inside an offline workspace.
2465  return false;
2466  }
2467  // In ONLINE workspace, just return the originally requested record:
2468  return $reqRecord;
2469  }
2470  // Return FALSE because the table/uid was not found anyway.
2471  return false;
2472  }
2473 
2478  protected function ‪compileStoreData(ServerRequestInterface $request): void
2479  {
2480  $queryParams = $request->getQueryParams();
2481  $parsedBody = $request->getParsedBody();
2482 
2483  foreach (['edit', 'defVals', 'overrideVals' , 'columnsOnly' , 'noView'] as $key) {
2484  if (isset($this->R_URL_getvars[$key])) {
2485  $this->storeArray[$key] = $this->R_URL_getvars[$key];
2486  } else {
2487  $this->storeArray[$key] = $parsedBody[$key] ?? $queryParams[$key] ?? null;
2488  }
2489  }
2490 
2491  $this->storeUrl = ‪HttpUtility::buildQueryString($this->storeArray, '&');
2492  $this->storeUrlMd5 = md5($this->storeUrl);
2493  }
2494 
2502  protected function ‪getTsConfigOption(string $table, string $key): string
2503  {
2504  return \trim((string)(
2505  $this->‪getBackendUser()->getTSConfig()['options.'][$key . '.'][$table]
2506  ?? $this->‪getBackendUser()->getTSConfig()['options.'][$key]
2507  ?? ''
2508  ));
2509  }
2510 
2523  protected function ‪closeDocument($mode, ServerRequestInterface $request): ?ResponseInterface
2524  {
2525  $setupArr = [];
2526  $mode = (int)$mode;
2527  // If current document is found in docHandler,
2528  // then unset it, possibly unset it ALL and finally, write it to the session data
2529  if (isset($this->docHandler[$this->storeUrlMd5])) {
2530  // add the closing document to the recent documents
2531  $recentDocs = $this->‪getBackendUser()->‪getModuleData('opendocs::recent');
2532  if (!is_array($recentDocs)) {
2533  $recentDocs = [];
2534  }
2535  $closedDoc = $this->docHandler[‪$this->storeUrlMd5];
2536  $recentDocs = array_merge([$this->storeUrlMd5 => $closedDoc], $recentDocs);
2537  if (count($recentDocs) > 8) {
2538  $recentDocs = array_slice($recentDocs, 0, 8);
2539  }
2540  // remove it from the list of the open documents
2541  unset($this->docHandler[$this->storeUrlMd5]);
2542  if ($mode === self::DOCUMENT_CLOSE_MODE_CLEAR_ALL) {
2543  $recentDocs = array_merge($this->docHandler, $recentDocs);
2544  $this->docHandler = [];
2545  }
2546  $this->‪getBackendUser()->‪pushModuleData('opendocs::recent', $recentDocs);
2547  $this->‪getBackendUser()->‪pushModuleData('FormEngine', [$this->docHandler, $this->docDat[1]]);
2548  BackendUtility::setUpdateSignal('OpendocsController::updateNumber', count($this->docHandler));
2549  }
2550  if ($mode === self::DOCUMENT_CLOSE_MODE_NO_REDIRECT) {
2551  return null;
2552  }
2553  // If ->returnEditConf is set, then add the current content of editconf to the ->retUrl variable: used by
2554  // other scripts, like wizard_add, to know which records was created or so...
2555  if ($this->returnEditConf && $this->retUrl != (string)$this->uriBuilder->buildUriFromRoute('dummy')) {
2556  $this->retUrl .= '&returnEditConf=' . rawurlencode((string)json_encode($this->editconf));
2557  }
2558  // If mode is NOT set (means 0) OR set to 1, then make a header location redirect to $this->retUrl
2559  if ($mode === self::DOCUMENT_CLOSE_MODE_DEFAULT || $mode === self::DOCUMENT_CLOSE_MODE_REDIRECT) {
2560  return new RedirectResponse($this->retUrl, 303);
2561  }
2562  if ($this->retUrl === '') {
2563  return null;
2564  }
2565  ‪$retUrl = (string)$this->returnUrl;
2566  if (is_array($this->docHandler) && !empty($this->docHandler)) {
2567  if (!empty($setupArr[2])) {
2568  $sParts = parse_url($request->getAttribute('normalizedParams')->getRequestUri());
2569  ‪$retUrl = $sParts['path'] . '?' . $setupArr[2] . '&returnUrl=' . rawurlencode(‪$retUrl);
2570  }
2571  }
2572  return new RedirectResponse(‪$retUrl, 303);
2573  }
2574 
2581  protected function ‪getShortcutTitle(ServerRequestInterface $request): string
2582  {
2583  $queryParameters = $request->getQueryParams();
2584  $languageService = $this->‪getLanguageService();
2585 
2586  if (!is_array($queryParameters['edit'] ?? false)) {
2587  return '';
2588  }
2589 
2590  // @todo There may be a more efficient way in using FormEngine FormData.
2591  // @todo Therefore, the button initialization however has to take place at a later stage.
2592 
2593  $table = (string)key($queryParameters['edit']);
2594  $tableTitle = $languageService->sL(‪$GLOBALS['TCA'][$table]['ctrl']['title'] ?? '') ?: $table;
2595  $recordId = (int)key($queryParameters['edit'][$table]);
2596  $action = (string)($queryParameters['edit'][$table][$recordId] ?? '');
2597 
2598  if ($action === 'new') {
2599  if ($table === 'pages') {
2600  return sprintf(
2601  $languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.createNewPage'),
2602  $tableTitle
2603  );
2604  }
2605 
2606  if ($recordId === 0) {
2607  return sprintf(
2608  $languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.createNewRecordRootLevel'),
2609  $tableTitle
2610  );
2611  }
2612 
2613  $pageRecord = null;
2614  if ($recordId < 0) {
2615  $parentRecord = BackendUtility::getRecord($table, abs($recordId));
2616  if ($parentRecord['pid'] ?? false) {
2617  $pageRecord = BackendUtility::getRecord('pages', (int)($parentRecord['pid']), 'title');
2618  }
2619  } else {
2620  $pageRecord = BackendUtility::getRecord('pages', $recordId, 'title');
2621  }
2622 
2623  if ($pageRecord !== null) {
2624  $pageTitle = BackendUtility::getRecordTitle('pages', $pageRecord);
2625  if ($pageTitle !== '') {
2626  return sprintf(
2627  $languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.createNewRecord'),
2628  $tableTitle,
2629  $pageTitle
2630  );
2631  }
2632  }
2633 
2634  return $languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.createNew') . ' ' . $tableTitle;
2635  }
2637  if ($action === 'edit') {
2638  $record = BackendUtility::getRecord($table, $recordId) ?? [];
2639  $recordTitle = BackendUtility::getRecordTitle($table, $record);
2640  if ($table === 'pages') {
2641  return sprintf($languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.editPage'), $tableTitle, $recordTitle);
2642  }
2643  if (!isset($record['pid'])) {
2644  return $languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.edit');
2645  }
2646  $pageId = (int)$record['pid'];
2647  if ($pageId === 0) {
2648  return sprintf($languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.editRecordRootLevel'), $tableTitle, $recordTitle);
2649  }
2650  $pageRow = BackendUtility::getRecord('pages', $pageId) ?? [];
2651  $pageTitle = BackendUtility::getRecordTitle('pages', $pageRow);
2652  if ($recordTitle !== '') {
2653  return sprintf($languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.editRecord'), $tableTitle, $recordTitle, $pageTitle);
2654  }
2655  return sprintf($languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.editRecordNoTitle'), $tableTitle, $pageTitle);
2656  }
2657 
2658  return '';
2659  }
2660 
2667  protected function ‪isSingleRecordView(): bool
2668  {
2669  return count($this->elementsData) === 1;
2670  }
2671 
2675  protected function ‪getBackendUser()
2676  {
2677  return ‪$GLOBALS['BE_USER'];
2678  }
2679 
2680  protected function ‪getLanguageService(): LanguageService
2681  {
2682  return ‪$GLOBALS['LANG'];
2683  }
2684 }
‪TYPO3\CMS\Core\DataHandling\DataHandler
Definition: DataHandler.php:86
‪TYPO3\CMS\Backend\Controller\EditDocumentController\getPreviewUrlParameters
‪string getPreviewUrlParameters(int $previewPageId)
Definition: EditDocumentController.php:795
‪TYPO3\CMS\Backend\Template\Components\AbstractControl\setClasses
‪$this setClasses($classes)
Definition: AbstractControl.php:115
‪TYPO3\CMS\Core\Imaging\Icon\SIZE_SMALL
‪const SIZE_SMALL
Definition: Icon.php:30
‪TYPO3\CMS\Core\Utility\GeneralUtility\trimExplode
‪static list< string > trimExplode($delim, $string, $removeEmptyValues=false, $limit=0)
Definition: GeneralUtility.php:999
‪TYPO3\CMS\Backend\Controller\EditDocumentController\$columnsOnly
‪string null $columnsOnly
Definition: EditDocumentController.php:93
‪TYPO3\CMS\Core\Resource\Exception\InsufficientUserPermissionsException
Definition: InsufficientUserPermissionsException.php:23
‪TYPO3\CMS\Backend\Controller\EditDocumentController\$storeUrlMd5
‪string $storeUrlMd5
Definition: EditDocumentController.php:246
‪TYPO3\CMS\Backend\Form\FormResultCompiler
Definition: FormResultCompiler.php:31
‪TYPO3\CMS\Core\Utility\PathUtility
Definition: PathUtility.php:25
‪TYPO3\CMS\Core\Database\Connection\PARAM_INT
‪const PARAM_INT
Definition: Connection.php:49
‪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\EditDocumentController\canViewDoktype
‪bool canViewDoktype(array $currentPage)
Definition: EditDocumentController.php:935
‪TYPO3\CMS\Backend\Controller\EditDocumentController\$perms_clause
‪string $perms_clause
Definition: EditDocumentController.php:191
‪TYPO3\CMS\Backend\Controller\EditDocumentController\$returnUrl
‪string null $returnUrl
Definition: EditDocumentController.php:112
‪TYPO3\CMS\Core\Versioning\VersionState\NEW_PLACEHOLDER
‪const NEW_PLACEHOLDER
Definition: VersionState.php:53
‪TYPO3\CMS\Backend\Template\Components\Buttons\AbstractButton\setIcon
‪$this setIcon(Icon $icon)
Definition: AbstractButton.php:94
‪TYPO3\CMS\Core\Resource\FileInterface
Definition: FileInterface.php:22
‪TYPO3\CMS\Backend\Controller\EditDocumentController\isRecordCurrentBackendUser
‪bool isRecordCurrentBackendUser()
Definition: EditDocumentController.php:2021
‪TYPO3\CMS\Backend\Controller\EditDocumentController\$mirror
‪array $mirror
Definition: EditDocumentController.php:152
‪TYPO3\CMS\Backend\Controller\EditDocumentController\mainAction
‪ResponseInterface mainAction(ServerRequestInterface $request)
Definition: EditDocumentController.php:348
‪TYPO3\CMS\Backend\Controller\EditDocumentController\$moduleTemplate
‪ModuleTemplate $moduleTemplate
Definition: EditDocumentController.php:308
‪TYPO3\CMS\Backend\Routing\PreviewUriBuilder\create
‪static static create(int $pageId, string $alternativeUri=null)
Definition: PreviewUriBuilder.php:75
‪TYPO3\CMS\Backend\Controller\EditDocumentController\getRecordForEdit
‪array false getRecordForEdit(string $table, int $theUid)
Definition: EditDocumentController.php:2397
‪TYPO3\CMS\Core\Imaging\Icon
Definition: Icon.php:26
‪TYPO3\CMS\Backend\Template\ModuleTemplateFactory
Definition: ModuleTemplateFactory.php:29
‪TYPO3\CMS\Backend\Controller\EditDocumentController\$pageRenderer
‪PageRenderer $pageRenderer
Definition: EditDocumentController.php:324
‪TYPO3\CMS\Backend\Controller\EditDocumentController\$recTitle
‪string $recTitle
Definition: EditDocumentController.php:181
‪TYPO3\CMS\Backend\Controller\EditDocumentController\$previewCode
‪string null $previewCode
Definition: EditDocumentController.php:175
‪TYPO3\CMS\Core\Database\ReferenceIndex
Definition: ReferenceIndex.php:42
‪TYPO3\CMS\Backend\Controller\EditDocumentController\$firstEl
‪array $firstEl
Definition: EditDocumentController.php:273
‪TYPO3\CMS\Backend\Controller\EditDocumentController\getCloseUrl
‪string getCloseUrl()
Definition: EditDocumentController.php:2035
‪TYPO3\CMS\Backend\Controller\EditDocumentController\getTsConfigOption
‪string getTsConfigOption(string $table, string $key)
Definition: EditDocumentController.php:2463
‪TYPO3\CMS\Backend\Controller\EditDocumentController\init
‪init(ServerRequestInterface $request)
Definition: EditDocumentController.php:729
‪TYPO3\CMS\Backend\Controller\EditDocumentController\getConnectedContentElementTranslationsCount
‪int getConnectedContentElementTranslationsCount(int $page, int $column, int $language)
Definition: EditDocumentController.php:1849
‪TYPO3\CMS\Backend\Controller\EditDocumentController\$R_URL_parts
‪array $R_URL_parts
Definition: EditDocumentController.php:203
‪TYPO3\CMS\Backend\Controller\EditDocumentController\registerShortcutButtonToButtonBar
‪registerShortcutButtonToButtonBar(ButtonBar $buttonBar, string $position, int $group, ServerRequestInterface $request)
Definition: EditDocumentController.php:1794
‪TYPO3\CMS\Backend\Controller\EditDocumentController\getPreviewPageId
‪int getPreviewPageId()
Definition: EditDocumentController.php:884
‪TYPO3\CMS\Backend\Controller\EditDocumentController\$isPageInFreeTranslationMode
‪bool $isPageInFreeTranslationMode
Definition: EditDocumentController.php:320
‪TYPO3\CMS\Backend\Controller\EditDocumentController\getDisableDelete
‪bool getDisableDelete()
Definition: EditDocumentController.php:2001
‪TYPO3\CMS\Core\Utility\PathUtility\getPublicResourceWebPath
‪static string getPublicResourceWebPath(string $resourcePath, bool $prefixWithSitePath=true)
Definition: PathUtility.php:98
‪TYPO3\CMS\Backend\Controller\EditDocumentController\languageSwitch
‪languageSwitch(string $table, int $uid, $pid=null)
Definition: EditDocumentController.php:2053
‪TYPO3\CMS\Backend\Controller\EditDocumentController\addSlugFieldsToColumnsOnly
‪addSlugFieldsToColumnsOnly(array $queryParams)
Definition: EditDocumentController.php:463
‪TYPO3\CMS\Backend\Controller\EditDocumentController\DOCUMENT_CLOSE_MODE_REDIRECT
‪const DOCUMENT_CLOSE_MODE_REDIRECT
Definition: EditDocumentController.php:78
‪TYPO3\CMS\Backend\Controller\EditDocumentController\registerOpenInNewWindowButtonToButtonBar
‪registerOpenInNewWindowButtonToButtonBar(ButtonBar $buttonBar, string $position, int $group, ServerRequestInterface $request)
Definition: EditDocumentController.php:1760
‪TYPO3\CMS\Core\Resource\Exception\FileDoesNotExistException
Definition: FileDoesNotExistException.php:21
‪TYPO3\CMS\Core\Imaging\IconFactory
Definition: IconFactory.php:34
‪TYPO3\CMS\Backend\Controller\EditDocumentController\$iconFactory
‪IconFactory $iconFactory
Definition: EditDocumentController.php:323
‪TYPO3\CMS\Backend\Controller\EditDocumentController\DOCUMENT_CLOSE_MODE_DEFAULT
‪const DOCUMENT_CLOSE_MODE_DEFAULT
Definition: EditDocumentController.php:76
‪TYPO3\CMS\Core\Versioning\VersionState\DELETE_PLACEHOLDER
‪const DELETE_PLACEHOLDER
Definition: VersionState.php:61
‪TYPO3\CMS\Backend\Controller\EditDocumentController\$isSavedRecord
‪bool $isSavedRecord
Definition: EditDocumentController.php:314
‪TYPO3\CMS\Backend\Controller\EditDocumentController\$previewData
‪array $previewData
Definition: EditDocumentController.php:302
‪TYPO3\CMS\Backend\Controller\EditDocumentController\$docHandler
‪array $docHandler
Definition: EditDocumentController.php:261
‪TYPO3\CMS\Backend\Controller\EditDocumentController\isInconsistentLanguageHandlingAllowed
‪bool isInconsistentLanguageHandlingAllowed()
Definition: EditDocumentController.php:1335
‪TYPO3\CMS\Backend\Controller\EditDocumentController\$dontStoreDocumentRef
‪int $dontStoreDocumentRef
Definition: EditDocumentController.php:296
‪TYPO3\CMS\Backend\Controller\EditDocumentController\DOCUMENT_CLOSE_MODE_CLEAR_ALL
‪const DOCUMENT_CLOSE_MODE_CLEAR_ALL
Definition: EditDocumentController.php:79
‪TYPO3\CMS\Backend\Controller\EditDocumentController\getLanguages
‪array getLanguages(int $id, string $table)
Definition: EditDocumentController.php:2288
‪$fields
‪$fields
Definition: pages.php:5
‪TYPO3\CMS\Backend\Template\Components\Buttons\Action\HelpButton\setFieldName
‪HelpButton setFieldName($fieldName)
Definition: HelpButton.php:85
‪TYPO3\CMS\Backend\Controller\EditDocumentController\getQueryBuilderForTranslationMode
‪QueryBuilder getQueryBuilderForTranslationMode(int $page, int $column, int $language)
Definition: EditDocumentController.php:1895
‪TYPO3\CMS\Backend\Controller\EditDocumentController\registerSaveButtonToButtonBar
‪registerSaveButtonToButtonBar(ButtonBar $buttonBar, string $position, int $group)
Definition: EditDocumentController.php:1419
‪TYPO3\CMS\Backend\Controller\EditDocumentController\compileForm
‪string compileForm(string $editForm)
Definition: EditDocumentController.php:1932
‪TYPO3\CMS\Backend\Template\ModuleTemplate
Definition: ModuleTemplate.php:46
‪TYPO3\CMS\Backend\Template\Components\ButtonBar\makeInputButton
‪InputButton makeInputButton()
Definition: ButtonBar.php:101
‪TYPO3\CMS\Backend\Form\Exception\DatabaseRecordWorkspaceDeletePlaceholderException
Definition: DatabaseRecordWorkspaceDeletePlaceholderException.php:26
‪TYPO3\CMS\Core\Type\Bitmask\Permission
Definition: Permission.php:26
‪TYPO3\CMS\Backend\Template\Components\ButtonBar\addButton
‪$this addButton(ButtonInterface $button, $buttonPosition=self::BUTTON_POSITION_LEFT, $buttonGroup=1)
Definition: ButtonBar.php:60
‪TYPO3\CMS\Backend\Controller\EditDocumentController\registerHistoryButtonToButtonBar
‪registerHistoryButtonToButtonBar(ButtonBar $buttonBar, string $position, int $group)
Definition: EditDocumentController.php:1711
‪TYPO3\CMS\Backend\Controller\EditDocumentController\parseAdditionalGetParameters
‪parseAdditionalGetParameters(array &$parameters, array $typoScript)
Definition: EditDocumentController.php:958
‪TYPO3\CMS\Backend\Controller\EditDocumentController\compileStoreData
‪compileStoreData(ServerRequestInterface $request)
Definition: EditDocumentController.php:2439
‪TYPO3\CMS\Backend\Controller\EditDocumentController\$returnNewPageId
‪bool $returnNewPageId
Definition: EditDocumentController.php:159
‪TYPO3\CMS\Backend\Controller\EditDocumentController\getLanguageService
‪getLanguageService()
Definition: EditDocumentController.php:2641
‪TYPO3\CMS\Backend\Controller\EditDocumentController\registerViewButtonToButtonBar
‪registerViewButtonToButtonBar(ButtonBar $buttonBar, string $position, int $group)
Definition: EditDocumentController.php:1441
‪TYPO3\CMS\Backend\Controller\Event\AfterFormEnginePageInitializedEvent
Definition: AfterFormEnginePageInitializedEvent.php:27
‪TYPO3\CMS\Core\Type\Enumeration\cast
‪static static cast($value)
Definition: Enumeration.php:186
‪TYPO3\CMS\Backend\Controller\EditDocumentController\$pageinfo
‪array $pageinfo
Definition: EditDocumentController.php:220
‪TYPO3\CMS\Core\Messaging\AbstractMessage\WARNING
‪const WARNING
Definition: AbstractMessage.php:30
‪TYPO3\CMS\Backend\Controller\EditDocumentController\registerCloseButtonToButtonBar
‪registerCloseButtonToButtonBar(ButtonBar $buttonBar, string $position, int $group)
Definition: EditDocumentController.php:1398
‪TYPO3\CMS\Backend\Controller\EditDocumentController
Definition: EditDocumentController.php:75
‪TYPO3\CMS\Backend\Controller\EditDocumentController\resolvePreviewRecordId
‪int resolvePreviewRecordId(string $table, array $recordArray, array $previewConfiguration)
Definition: EditDocumentController.php:849
‪TYPO3\CMS\Backend\Controller\EditDocumentController\generatePreviewCode
‪string generatePreviewCode()
Definition: EditDocumentController.php:770
‪TYPO3\CMS\Backend\Controller\EditDocumentController\__construct
‪__construct(EventDispatcherInterface $eventDispatcher, IconFactory $iconFactory, PageRenderer $pageRenderer, UriBuilder $uriBuilder, ModuleTemplateFactory $moduleTemplateFactory)
Definition: EditDocumentController.php:328
‪TYPO3\CMS\Backend\Controller\EditDocumentController\$noView
‪bool $noView
Definition: EditDocumentController.php:187
‪TYPO3\CMS\Backend\Controller\EditDocumentController\$R_URL_getvars
‪array $R_URL_getvars
Definition: EditDocumentController.php:210
‪TYPO3\CMS\Backend\Controller\EditDocumentController\getUrlQueryParamsForCurrentRequest
‪getUrlQueryParamsForCurrentRequest(ServerRequestInterface $request)
Definition: EditDocumentController.php:1807
‪TYPO3\CMS\Backend\Controller\EditDocumentController\setIsSavedRecord
‪setIsSavedRecord()
Definition: EditDocumentController.php:1320
‪TYPO3\CMS\Backend\Routing\UriBuilder
Definition: UriBuilder.php:40
‪TYPO3\CMS\Core\Authentication\AbstractUserAuthentication\getModuleData
‪mixed getModuleData($module, $type='')
Definition: AbstractUserAuthentication.php:1054
‪TYPO3\CMS\Backend\Controller\EditDocumentController\localizationRedirect
‪ResponseInterface null localizationRedirect(ServerRequestInterface $request)
Definition: EditDocumentController.php:2225
‪TYPO3\CMS\Core\Resource\ResourceFactory
Definition: ResourceFactory.php:41
‪TYPO3\CMS\Core\Utility\HttpUtility\buildQueryString
‪static string buildQueryString(array $parameters, string $prependCharacter='', bool $skipEmptyParameters=false)
Definition: HttpUtility.php:171
‪TYPO3\CMS\Core\Authentication\AbstractUserAuthentication\pushModuleData
‪pushModuleData($module, $data, $noSave=0)
Definition: AbstractUserAuthentication.php:1034
‪TYPO3\CMS\Backend\Controller\EditDocumentController\preInit
‪ResponseInterface preInit(ServerRequestInterface $request)
Definition: EditDocumentController.php:404
‪TYPO3\CMS\Core\Domain\Repository\PageRepository\DOKTYPE_SPACER
‪const DOKTYPE_SPACER
Definition: PageRepository.php:115
‪TYPO3\CMS\Backend\Controller\EditDocumentController\getBackendUser
‪TYPO3 CMS Core Authentication BackendUserAuthentication getBackendUser()
Definition: EditDocumentController.php:2636
‪TYPO3\CMS\Backend\Controller\EditDocumentController\resolveMetaInformation
‪resolveMetaInformation()
Definition: EditDocumentController.php:1035
‪TYPO3\CMS\Backend\Controller\EditDocumentController\$editconf
‪array< string, array > $editconf
Definition: EditDocumentController.php:86
‪TYPO3\CMS\Backend\Controller\EditDocumentController\$closeDoc
‪int $closeDoc
Definition: EditDocumentController.php:126
‪TYPO3\CMS\Backend\Controller\EditDocumentController\getInfobox
‪string getInfobox(string $message, ?string $title=null)
Definition: EditDocumentController.php:1232
‪TYPO3\CMS\Backend\Controller\EditDocumentController\setIsPageInFreeTranslationMode
‪setIsPageInFreeTranslationMode($record, int $sysLanguageUid)
Definition: EditDocumentController.php:1350
‪TYPO3\CMS\Backend\Controller\Event\BeforeFormEnginePageInitializedEvent
Definition: BeforeFormEnginePageInitializedEvent.php:27
‪TYPO3\CMS\Core\Type\Bitmask\Permission\PAGE_SHOW
‪const PAGE_SHOW
Definition: Permission.php:35
‪TYPO3\CMS\Backend\Controller\EditDocumentController\isSingleRecordView
‪bool isSingleRecordView()
Definition: EditDocumentController.php:2628
‪TYPO3\CMS\Core\Domain\Repository\PageRepository\DOKTYPE_SYSFOLDER
‪const DOKTYPE_SYSFOLDER
Definition: PageRepository.php:116
‪TYPO3\CMS\Backend\Controller\EditDocumentController\getShortcutTitle
‪string getShortcutTitle(ServerRequestInterface $request)
Definition: EditDocumentController.php:2542
‪TYPO3\CMS\Backend\Template\Components\ButtonBar\makeLinkButton
‪LinkButton makeLinkButton()
Definition: ButtonBar.php:121
‪TYPO3\CMS\Backend\Template\Components\Buttons\InputButton\setForm
‪InputButton setForm($form)
Definition: InputButton.php:116
‪TYPO3\CMS\Backend\Controller\EditDocumentController\fixWSversioningInEditConf
‪fixWSversioningInEditConf($mapArray=false)
Definition: EditDocumentController.php:2350
‪TYPO3\CMS\Backend\Controller\EditDocumentController\$cmd
‪array $cmd
Definition: EditDocumentController.php:146
‪TYPO3\CMS\Backend\Controller\EditDocumentController\$storeUrl
‪string $storeUrl
Definition: EditDocumentController.php:240
‪TYPO3\CMS\Core\Utility\MathUtility\convertToPositiveInteger
‪static int convertToPositiveInteger($theInt)
Definition: MathUtility.php:56
‪TYPO3\CMS\Core\Http\RedirectResponse
Definition: RedirectResponse.php:28
‪TYPO3\CMS\Backend\Controller\EditDocumentController\makeEditForm
‪string makeEditForm()
Definition: EditDocumentController.php:1058
‪TYPO3\CMS\Core\Versioning\VersionState
Definition: VersionState.php:24
‪TYPO3\CMS\Backend\Form\NodeFactory
Definition: NodeFactory.php:37
‪TYPO3\CMS\Core\Messaging\AbstractMessage\OK
‪const OK
Definition: AbstractMessage.php:29
‪TYPO3\CMS\Backend\Controller\EditDocumentController\$defVals
‪array null $defVals
Definition: EditDocumentController.php:99
‪TYPO3\CMS\Core\Database\Connection
Definition: Connection.php:38
‪TYPO3\CMS\Backend\Form\Exception\DatabaseRecordException
Definition: DatabaseRecordException.php:24
‪TYPO3\CMS\Backend\Controller\EditDocumentController\$formResultCompiler
‪FormResultCompiler $formResultCompiler
Definition: EditDocumentController.php:290
‪TYPO3\CMS\Backend\Template\Components\ButtonBar\makeHelpButton
‪HelpButton makeHelpButton()
Definition: ButtonBar.php:151
‪TYPO3\CMS\Backend\Controller\EditDocumentController\registerDeleteButtonToButtonBar
‪registerDeleteButtonToButtonBar(ButtonBar $buttonBar, string $position, int $group)
Definition: EditDocumentController.php:1630
‪TYPO3\CMS\Backend\Configuration\TranslationConfigurationProvider
Definition: TranslationConfigurationProvider.php:37
‪TYPO3\CMS\Core\Messaging\FlashMessage
Definition: FlashMessage.php:26
‪TYPO3\CMS\Core\Type\Bitmask\Permission\CONTENT_EDIT
‪const CONTENT_EDIT
Definition: Permission.php:55
‪TYPO3\CMS\Backend\Controller\EditDocumentController\$errorC
‪int $errorC
Definition: EditDocumentController.php:279
‪TYPO3\CMS\Backend\Controller\EditDocumentController\registerNewButtonToButtonBar
‪registerNewButtonToButtonBar(ButtonBar $buttonBar, string $position, int $group, int $sysLanguageUid, int $l18nParent)
Definition: EditDocumentController.php:1518
‪TYPO3\CMS\Backend\Controller\EditDocumentController\$storeTitle
‪string $storeTitle
Definition: EditDocumentController.php:227
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Backend\Form\Exception\AccessDeniedException
Definition: AccessDeniedException.php:25
‪TYPO3\CMS\Backend\Controller\EditDocumentController\$viewUrl
‪string $viewUrl
Definition: EditDocumentController.php:171
‪TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction
Definition: DeletedRestriction.php:28
‪TYPO3\CMS\Backend\Routing\PreviewUriBuilder
Definition: PreviewUriBuilder.php:32
‪TYPO3\CMS\Core\Type\Bitmask\Permission\PAGE_DELETE
‪const PAGE_DELETE
Definition: Permission.php:45
‪TYPO3\CMS\Backend\Controller\EditDocumentController\DOCUMENT_CLOSE_MODE_NO_REDIRECT
‪const DOCUMENT_CLOSE_MODE_NO_REDIRECT
Definition: EditDocumentController.php:80
‪TYPO3\CMS\Backend\Controller\EditDocumentController\$uriBuilder
‪UriBuilder $uriBuilder
Definition: EditDocumentController.php:325
‪TYPO3\CMS\Backend\Controller\EditDocumentController\closeDocument
‪ResponseInterface null closeDocument($mode, ServerRequestInterface $request)
Definition: EditDocumentController.php:2484
‪TYPO3\CMS\Core\Utility\GeneralUtility\intExplode
‪static int[] intExplode($delimiter, $string, $removeEmptyValues=false, $limit=0)
Definition: GeneralUtility.php:927
‪TYPO3\CMS\Backend\Controller\EditDocumentController\$data
‪array $data
Definition: EditDocumentController.php:140
‪TYPO3\CMS\Backend\Controller\EditDocumentController\getButtons
‪getButtons(ServerRequestInterface $request)
Definition: EditDocumentController.php:1255
‪TYPO3\CMS\Core\Utility\MathUtility
Definition: MathUtility.php:22
‪TYPO3\CMS\Backend\Controller\EditDocumentController\$R_URI
‪string $R_URI
Definition: EditDocumentController.php:216
‪TYPO3\CMS\Backend\Template\Components\Buttons\Action\HelpButton\setModuleName
‪HelpButton setModuleName($moduleName)
Definition: HelpButton.php:63
‪TYPO3\CMS\Backend\Controller\EditDocumentController\getPreviewUrlAnchorSection
‪string getPreviewUrlAnchorSection()
Definition: EditDocumentController.php:871
‪TYPO3\CMS\Core\Utility\HttpUtility
Definition: HttpUtility.php:22
‪TYPO3\CMS\Backend\Controller\EditDocumentController\processData
‪ResponseInterface null processData(ServerRequestInterface $request)
Definition: EditDocumentController.php:490
‪TYPO3\CMS\Core\Localization\LanguageService
Definition: LanguageService.php:42
‪TYPO3\CMS\Backend\Controller\EditDocumentController\$doSave
‪bool $doSave
Definition: EditDocumentController.php:134
‪TYPO3\CMS\Core\Domain\Repository\PageRepository
Definition: PageRepository.php:53
‪TYPO3\CMS\Backend\Controller\EditDocumentController\$storeArray
‪array $storeArray
Definition: EditDocumentController.php:234
‪TYPO3\CMS\Backend\Controller\EditDocumentController\$eventDispatcher
‪EventDispatcherInterface $eventDispatcher
Definition: EditDocumentController.php:322
‪TYPO3\CMS\Core\Database\ConnectionPool
Definition: ConnectionPool.php:46
‪TYPO3\CMS\Backend\Controller\EditDocumentController\registerDuplicationButtonToButtonBar
‪registerDuplicationButtonToButtonBar(ButtonBar $buttonBar, string $position, int $group, int $sysLanguageUid, int $l18nParent)
Definition: EditDocumentController.php:1575
‪TYPO3\CMS\Backend\Controller\EditDocumentController\$moduleTemplateFactory
‪ModuleTemplateFactory $moduleTemplateFactory
Definition: EditDocumentController.php:326
‪TYPO3\CMS\Backend\Controller\EditDocumentController\$overrideVals
‪array null $overrideVals
Definition: EditDocumentController.php:105
‪TYPO3\CMS\Backend\Controller\EditDocumentController\getFreeTranslationMode
‪bool getFreeTranslationMode(int $page, int $column, int $language)
Definition: EditDocumentController.php:1377
‪TYPO3\CMS\Backend\Routing\PreviewUriBuilder\OPTION_SWITCH_FOCUS
‪const OPTION_SWITCH_FOCUS
Definition: PreviewUriBuilder.php:33
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:50
‪TYPO3\CMS\Backend\Template\Components\AbstractControl\setTitle
‪$this setTitle($title)
Definition: AbstractControl.php:128
‪TYPO3\CMS\Backend\Form\FormDataCompiler
Definition: FormDataCompiler.php:25
‪TYPO3\CMS\Backend\Controller\EditDocumentController\registerColumnsOnlyButtonToButtonBar
‪registerColumnsOnlyButtonToButtonBar(ButtonBar $buttonBar, string $position, int $group)
Definition: EditDocumentController.php:1738
‪TYPO3\CMS\Backend\Controller\EditDocumentController\$elementsData
‪array $elementsData
Definition: EditDocumentController.php:267
‪TYPO3\CMS\Backend\Form\FormDataGroup\TcaDatabaseRecord
Definition: TcaDatabaseRecord.php:25
‪TYPO3\CMS\Backend\Template\Components\ButtonBar\BUTTON_POSITION_RIGHT
‪const BUTTON_POSITION_RIGHT
Definition: ButtonBar.php:41
‪TYPO3\CMS\Backend\Controller\EditDocumentController\updateInlineView
‪updateInlineView($uc, DataHandler $dataHandler)
Definition: EditDocumentController.php:1960
‪TYPO3\CMS\Backend\Controller\EditDocumentController\$viewId
‪int $viewId
Definition: EditDocumentController.php:286
‪TYPO3\CMS\Backend\Controller
Definition: AboutController.php:16
‪TYPO3\CMS\Backend\Controller\EditDocumentController\registerCshButtonToButtonBar
‪registerCshButtonToButtonBar(ButtonBar $buttonBar, string $position, int $group)
Definition: EditDocumentController.php:1834
‪TYPO3\CMS\Backend\Controller\EditDocumentController\$returnEditConf
‪bool $returnEditConf
Definition: EditDocumentController.php:197
‪TYPO3\CMS\Core\Domain\Repository\PageRepository\DOKTYPE_RECYCLER
‪const DOKTYPE_RECYCLER
Definition: PageRepository.php:117
‪TYPO3\CMS\Backend\Controller\EditDocumentController\$docDat
‪array $docDat
Definition: EditDocumentController.php:252
‪TYPO3\CMS\Core\Messaging\FlashMessageService
Definition: FlashMessageService.php:27
‪TYPO3\CMS\Core\Http\HtmlResponse
Definition: HtmlResponse.php:26
‪TYPO3\CMS\Backend\Controller\EditDocumentController\main
‪main(ServerRequestInterface $request)
Definition: EditDocumentController.php:976
‪TYPO3\CMS\Backend\Controller\EditDocumentController\$popViewId
‪int $popViewId
Definition: EditDocumentController.php:165
‪TYPO3\CMS\Backend\Template\Components\Buttons\AbstractButton\setShowLabelText
‪$this setShowLabelText($showLabelText)
Definition: AbstractButton.php:61
‪TYPO3\CMS\Backend\Controller\EditDocumentController\getStandAloneContentElementTranslationsCount
‪int getStandAloneContentElementTranslationsCount(int $page, int $column, int $language)
Definition: EditDocumentController.php:1872
‪TYPO3\CMS\Backend\Controller\EditDocumentController\$retUrl
‪string $retUrl
Definition: EditDocumentController.php:120
‪TYPO3\CMS\Core\Database\Query\Restriction\WorkspaceRestriction
Definition: WorkspaceRestriction.php:40