2 declare(strict_types
= 1);
3 namespace TYPO3\CMS\Backend\Controller
;
6 * This file is part of the TYPO3 CMS project.
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.
12 * For the full copyright and license information, please read the
13 * LICENSE.txt file that was distributed with this source code.
15 * The TYPO3 project - inspiring people to share!
18 use Psr\Http\Message\ResponseInterface
;
19 use Psr\Http\Message\ServerRequestInterface
;
20 use TYPO3\CMS\Backend\Routing\UriBuilder
;
21 use TYPO3\CMS\Backend\Template\Components\ButtonBar
;
22 use TYPO3\CMS\Backend\Template\ModuleTemplate
;
23 use TYPO3\CMS\Backend\Tree\View\NewRecordPageTreeView
;
24 use TYPO3\CMS\Backend\Tree\View\PagePositionMap
;
25 use TYPO3\CMS\Backend\Utility\BackendUtility
;
26 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication
;
27 use TYPO3\CMS\Core\Database\ConnectionPool
;
28 use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction
;
29 use TYPO3\CMS\Core\Http\HtmlResponse
;
30 use TYPO3\CMS\Core\Http\RedirectResponse
;
31 use TYPO3\CMS\Core\Imaging\Icon
;
32 use TYPO3\CMS\Core\Localization\LanguageService
;
33 use TYPO3\CMS\Core\Type\Bitmask\Permission
;
34 use TYPO3\CMS\Core\Utility\ExtensionManagementUtility
;
35 use TYPO3\CMS\Core\Utility\GeneralUtility
;
36 use TYPO3\CMS\Core\Utility\PathUtility
;
37 use TYPO3\CMS\Frontend\Page\PageRepository
;
40 * Script class for 'db_new'
41 * @internal This class is a specific Backend controller implementation and is not considered part of the Public TYPO3 API.
43 class NewRecordController
48 protected $pageinfo = [];
53 protected $pidInfo = [];
58 protected $newRecordSortList;
63 protected $newPagesInto;
68 protected $newContentInto;
73 protected $newPagesAfter;
76 * Determines, whether "Select Position" for new page should be shown
80 protected $newPagesSelectPosition = true;
85 protected $web_list_modTSconfig;
90 protected $allowedNewTables;
95 protected $deniedNewTables;
100 protected $web_list_modTSconfig_pid;
105 protected $allowedNewTables_pid;
110 protected $deniedNewTables_pid;
125 * @see \TYPO3\CMS\Backend\Tree\View\NewRecordPageTreeView::expandNext()
133 protected $returnUrl;
140 protected $pagesOnly;
145 protected $perms_clause;
148 * Accumulated HTML output
160 * ModuleTemplate object
162 * @var ModuleTemplate
164 protected $moduleTemplate;
169 public function __construct()
171 $this->moduleTemplate
= GeneralUtility
::makeInstance(ModuleTemplate
::class);
172 $this->getLanguageService()->includeLLFile('EXT:core/Resources/Private/Language/locallang_misc.xlf');
174 // @see \TYPO3\CMS\Backend\Tree\View\NewRecordPageTreeView::expandNext()
175 $GLOBALS['SOBE'] = $this;
179 * Injects the request object for the current request or subrequest
180 * As this controller goes only through the main() method, it is rather simple for now
182 * @param ServerRequestInterface $request the current request
183 * @return ResponseInterface the response with the content
185 public function mainAction(ServerRequestInterface
$request): ResponseInterface
187 $this->init($request);
188 $response = $this->renderContent($request);
190 if (empty($response)) {
191 $response = new HtmlResponse($this->moduleTemplate
->renderContent());
198 * Constructor function for the class
200 * @param ServerRequestInterface $request
202 protected function init(ServerRequestInterface
$request): void
204 $beUser = $this->getBackendUserAuthentication();
205 // Page-selection permission clause (reading)
206 $this->perms_clause
= $beUser->getPagePermsClause(Permission
::PAGE_SHOW
);
207 // This will hide records from display - it has nothing to do with user rights!!
208 $pidList = $beUser->getTSConfig()['options.']['hideRecords.']['pages'] ??
'';
209 if (!empty($pidList)) {
210 $queryBuilder = GeneralUtility
::makeInstance(ConnectionPool
::class)
211 ->getQueryBuilderForTable('pages');
212 $this->perms_clause
.= ' AND ' . $queryBuilder->expr()->notIn(
214 GeneralUtility
::intExplode(',', $pidList)
218 $parsedBody = $request->getParsedBody();
219 $queryParams = $request->getQueryParams();
220 // The page id to operate from
221 $this->id
= (int)($parsedBody['id'] ??
$queryParams['id'] ??
0);
222 $this->returnUrl
= GeneralUtility
::sanitizeLocalUrl($parsedBody['returnUrl'] ??
$queryParams['returnUrl'] ??
'');
223 $this->pagesOnly
= $parsedBody['pagesOnly'] ??
$queryParams['pagesOnly'] ??
null;
224 // Setting up the context sensitive menu:
225 $this->moduleTemplate
->getPageRenderer()->loadRequireJsModule('TYPO3/CMS/Backend/ContextMenu');
226 $this->moduleTemplate
->getPageRenderer()->loadRequireJsModule('TYPO3/CMS/Backend/Tooltip');
227 $this->moduleTemplate
->getPageRenderer()->loadRequireJsModule('TYPO3/CMS/Backend/PageActions');
230 $this->content
.= '<h1>'
231 . $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:db_new.php.pagetitle')
233 // Id a positive id is supplied, ask for the page record with permission information contained:
235 $this->pageinfo
= BackendUtility
::readPageAccess($this->id
, $this->perms_clause
);
237 // If a page-record was returned, the user had read-access to the page.
238 if ($this->pageinfo
['uid']) {
239 // Get record of parent page
240 $this->pidInfo
= BackendUtility
::getRecord('pages', $this->pageinfo
['pid']) ?
: [];
241 // Checking the permissions for the user with regard to the parent page: Can he create new pages, new
242 // content record, new page after?
243 if ($beUser->doesUserHaveAccess($this->pageinfo
, 8)) {
244 $this->newPagesInto
= 1;
246 if ($beUser->doesUserHaveAccess($this->pageinfo
, 16)) {
247 $this->newContentInto
= 1;
249 if (($beUser->isAdmin() ||
!empty($this->pidInfo
)) && $beUser->doesUserHaveAccess($this->pidInfo
, 8)) {
250 $this->newPagesAfter
= 1;
252 } elseif ($beUser->isAdmin()) {
253 // Admins can do it all
254 $this->newPagesInto
= 1;
255 $this->newContentInto
= 1;
256 $this->newPagesAfter
= 0;
258 // People with no permission can do nothing
259 $this->newPagesInto
= 0;
260 $this->newContentInto
= 0;
261 $this->newPagesAfter
= 0;
266 * Main processing, creating the list of new record tables to select from
268 * @param ServerRequestInterface $request
269 * @return ResponseInterface|null
271 protected function renderContent(ServerRequestInterface
$request): ?ResponseInterface
273 // If there was a page - or if the user is admin (admins has access to the root) we proceed:
274 if (!empty($this->pageinfo
['uid']) ||
$this->getBackendUserAuthentication()->isAdmin()) {
275 if (empty($this->pageinfo
)) {
276 // Explicitly pass an empty array to the docHeader
277 $this->moduleTemplate
->getDocHeaderComponent()->setMetaInformation([]);
279 $this->moduleTemplate
->getDocHeaderComponent()->setMetaInformation($this->pageinfo
);
281 // Acquiring TSconfig for this module/current page:
282 $this->web_list_modTSconfig
= BackendUtility
::getPagesTSconfig($this->pageinfo
['uid'])['mod.']['web_list.'] ??
[];
283 $this->allowedNewTables
= GeneralUtility
::trimExplode(',', $this->web_list_modTSconfig
['allowedNewTables'] ??
'', true);
284 $this->deniedNewTables
= GeneralUtility
::trimExplode(',', $this->web_list_modTSconfig
['deniedNewTables'] ??
'', true);
285 // Acquiring TSconfig for this module/parent page:
286 $this->web_list_modTSconfig_pid
= BackendUtility
::getPagesTSconfig($this->pageinfo
['pid'])['mod.']['web_list.'] ??
[];
287 $this->allowedNewTables_pid
= GeneralUtility
::trimExplode(',', $this->web_list_modTSconfig_pid
['allowedNewTables'] ??
'', true);
288 $this->deniedNewTables_pid
= GeneralUtility
::trimExplode(',', $this->web_list_modTSconfig_pid
['deniedNewTables'] ??
'', true);
290 if (!$this->isRecordCreationAllowedForTable('pages')) {
291 $this->newPagesInto
= 0;
293 if (!$this->isRecordCreationAllowedForTable('pages', $this->allowedNewTables_pid
, $this->deniedNewTables_pid
)) {
294 $this->newPagesAfter
= 0;
296 // Set header-HTML and return_url
297 if (is_array($this->pageinfo
) && $this->pageinfo
['uid']) {
298 $title = strip_tags($this->pageinfo
[$GLOBALS['TCA']['pages']['ctrl']['label']]);
300 $title = $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'];
302 $this->moduleTemplate
->setTitle($title);
303 // GENERATE the HTML-output depending on mode (pagesOnly is the page wizard)
304 // Regular new element:
305 if (!$this->pagesOnly
) {
306 $this->renderNewRecordControls($request);
307 } elseif ($this->isRecordCreationAllowedForTable('pages')) {
309 $response = $this->renderPositionTree();
311 if (!empty($response)) {
315 // Add all the content to an output section
316 $this->content
.= '<div>' . $this->code
. '</div>';
317 // Setting up the buttons and markers for docheader
319 // Build the <body> for the module
320 $this->moduleTemplate
->setContent($this->content
);
327 * Create the panel of buttons for submitting the form or otherwise perform operations.
329 protected function getButtons(): void
331 $lang = $this->getLanguageService();
332 $buttonBar = $this->moduleTemplate
->getDocHeaderComponent()->getButtonBar();
333 // Regular new element:
334 if (!$this->pagesOnly
) {
336 if ($this->isRecordCreationAllowedForTable('pages')) {
337 $newPageButton = $buttonBar->makeLinkButton()
338 ->setHref(GeneralUtility
::linkThisScript(['pagesOnly' => '1']))
339 ->setTitle($lang->sL('LLL:EXT:backend/Resources/Private/Language/locallang_layout.xlf:newPage'))
340 ->setIcon($this->moduleTemplate
->getIconFactory()->getIcon('actions-page-new', Icon
::SIZE_SMALL
));
341 $buttonBar->addButton($newPageButton, ButtonBar
::BUTTON_POSITION_LEFT
, 20);
344 $cshButton = $buttonBar->makeHelpButton()->setModuleName('xMOD_csh_corebe')->setFieldName('new_regular');
345 $buttonBar->addButton($cshButton);
346 } elseif ($this->isRecordCreationAllowedForTable('pages')) {
349 $buttons['csh'] = BackendUtility
::cshItem('xMOD_csh_corebe', 'new_pages');
350 $cshButton = $buttonBar->makeHelpButton()->setModuleName('xMOD_csh_corebe')->setFieldName('new_pages');
351 $buttonBar->addButton($cshButton);
354 if ($this->returnUrl
) {
355 $returnButton = $buttonBar->makeLinkButton()
356 ->setHref($this->returnUrl
)
357 ->setTitle($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.goBack'))
358 ->setIcon($this->moduleTemplate
->getIconFactory()->getIcon('actions-view-go-back', Icon
::SIZE_SMALL
));
359 $buttonBar->addButton($returnButton, ButtonBar
::BUTTON_POSITION_LEFT
, 10);
362 if (is_array($this->pageinfo
) && $this->pageinfo
['uid']) {
364 $pagesTSconfig = BackendUtility
::getPagesTSconfig($this->pageinfo
['uid']);
365 if (isset($pagesTSconfig['TCEMAIN.']['preview.']['disableButtonForDokType'])) {
366 $excludeDokTypes = GeneralUtility
::intExplode(
368 $pagesTSconfig['TCEMAIN.']['preview.']['disableButtonForDokType'],
372 // exclude sysfolders and recycler by default
374 PageRepository
::DOKTYPE_RECYCLER
,
375 PageRepository
::DOKTYPE_SYSFOLDER
,
376 PageRepository
::DOKTYPE_SPACER
379 if (!in_array((int)$this->pageinfo
['doktype'], $excludeDokTypes, true)) {
380 $viewButton = $buttonBar->makeLinkButton()
382 ->setOnClick(BackendUtility
::viewOnClick(
383 $this->pageinfo
['uid'],
385 BackendUtility
::BEgetRootLine($this->pageinfo
['uid'])
387 ->setTitle($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.showPage'))
388 ->setIcon($this->moduleTemplate
->getIconFactory()->getIcon(
392 $buttonBar->addButton($viewButton, ButtonBar
::BUTTON_POSITION_LEFT
, 30);
398 * Renders the position map for pages wizard
400 * @return ResponseInterface|null
402 protected function renderPositionTree(): ?ResponseInterface
404 $queryBuilder = GeneralUtility
::makeInstance(ConnectionPool
::class)
405 ->getQueryBuilderForTable('pages');
406 $queryBuilder->getRestrictions()
408 ->add(GeneralUtility
::makeInstance(DeletedRestriction
::class));
409 $numberOfPages = $queryBuilder
415 if ($numberOfPages > 0) {
416 $this->code
.= '<h3>' . htmlspecialchars($this->getLanguageService()->getLL('selectPosition')) . ':</h3>';
417 /** @var \TYPO3\CMS\Backend\Tree\View\PagePositionMap $positionMap */
418 $positionMap = GeneralUtility
::makeInstance(PagePositionMap
::class, NewRecordPageTreeView
::class);
419 $this->code
.= $positionMap->positionTree(
426 /** @var \TYPO3\CMS\Backend\Routing\UriBuilder $uriBuilder */
427 $uriBuilder = GeneralUtility
::makeInstance(\TYPO3\CMS\Backend\Routing\UriBuilder
::class);
428 // No pages yet, no need to prompt for position, redirect to page creation.
435 'returnNewPageId' => 1,
436 'returnUrl' => (string)$uriBuilder->buildUriFromRoute('db_new', ['id' => $this->id
, 'pagesOnly' => '1'])
438 $url = (string)$uriBuilder->buildUriFromRoute('record_edit', $urlParameters);
441 return new RedirectResponse($url);
448 * Render controls for creating a regular new element (pages or records)
450 * @param ServerRequestInterface $request
452 protected function renderNewRecordControls(ServerRequestInterface
$request): void
454 $lang = $this->getLanguageService();
455 // Initialize array for accumulating table rows:
457 // Get TSconfig for current page
458 $pageTS = BackendUtility
::getPagesTSconfig($this->id
);
459 // Finish initializing new pages options with TSconfig
460 // Each new page option may be hidden by TSconfig
461 // Enabled option for the position of a new page
462 $this->newPagesSelectPosition
= !empty($pageTS['mod.']['wizards.']['newRecord.']['pages.']['show.']['pageSelectPosition']);
463 // Pseudo-boolean (0/1) for backward compatibility
464 $displayNewPagesIntoLink = $this->newPagesInto
&& !empty($pageTS['mod.']['wizards.']['newRecord.']['pages.']['show.']['pageInside']);
465 $displayNewPagesAfterLink = $this->newPagesAfter
&& !empty($pageTS['mod.']['wizards.']['newRecord.']['pages.']['show.']['pageAfter']);
466 // Slight spacer from header:
470 $v = $GLOBALS['TCA'][$table];
471 $pageIcon = $this->moduleTemplate
->getIconFactory()->getIconForRecord(
476 $newPageIcon = $this->moduleTemplate
->getIconFactory()->getIcon('actions-page-new', Icon
::SIZE_SMALL
)->render();
478 // New pages INSIDE this pages
480 if ($displayNewPagesIntoLink
481 && $this->isTableAllowedOnPage('pages', $this->pageinfo
)
482 && $this->getBackendUserAuthentication()->check('tables_modify', 'pages')
483 && $this->getBackendUserAuthentication()->workspaceCreateNewRecord(($this->pageinfo
['_ORIG_uid'] ?
: $this->id
), 'pages')
485 // Create link to new page inside:
486 $recordIcon = $this->moduleTemplate
->getIconFactory()->getIconForRecord($table, [], Icon
::SIZE_SMALL
)->render();
487 $newPageLinks[] = $this->renderLink(
488 $recordIcon . htmlspecialchars($lang->sL($v['ctrl']['title'])) . ' (' . htmlspecialchars($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:db_new.php.inside')) . ')',
493 // New pages AFTER this pages
494 if ($displayNewPagesAfterLink
495 && $this->isTableAllowedOnPage('pages', $this->pidInfo
)
496 && $this->getBackendUserAuthentication()->check('tables_modify', 'pages')
497 && $this->getBackendUserAuthentication()->workspaceCreateNewRecord($this->pidInfo
['uid'], 'pages')
499 $newPageLinks[] = $this->renderLink(
500 $pageIcon . htmlspecialchars($lang->sL($v['ctrl']['title'])) . ' (' . htmlspecialchars($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:db_new.php.after')) . ')',
505 // New pages at selection position
506 if ($this->newPagesSelectPosition
&& $this->isRecordCreationAllowedForTable('pages')) {
507 // Link to page-wizard:
508 $newPageLinks[] = '<a href="' . htmlspecialchars(GeneralUtility
::linkThisScript(['pagesOnly' => 1])) . '">' . $pageIcon . htmlspecialchars($lang->getLL('pageSelectPosition')) . '</a>';
510 // Assemble all new page links
511 $numPageLinks = count($newPageLinks);
512 for ($i = 0; $i < $numPageLinks; $i++
) {
513 $rowContent .= '<li>' . $newPageLinks[$i] . '</li>';
515 if ($this->isRecordCreationAllowedForTable('pages')) {
516 $rowContent = '<ul class="list-tree"><li>' . $newPageIcon . '<strong>' .
517 $lang->getLL('createNewPage') . '</strong><ul>' . $rowContent . '</ul></li>';
519 $rowContent = '<ul class="list-tree"><li><ul>' . $rowContent . '</li></ul>';
521 /** @var \TYPO3\CMS\Backend\Routing\UriBuilder $uriBuilder */
522 $uriBuilder = GeneralUtility
::makeInstance(UriBuilder
::class);
524 $startRows = [$rowContent];
526 // New tables (but not pages) INSIDE this pages
527 $isAdmin = $this->getBackendUserAuthentication()->isAdmin();
528 $newContentIcon = $this->moduleTemplate
->getIconFactory()->getIcon('actions-document-new', Icon
::SIZE_SMALL
)->render();
529 if ($this->newContentInto
) {
530 if (is_array($GLOBALS['TCA'])) {
532 foreach ($GLOBALS['TCA'] as $table => $v) {
533 $rootLevelConfiguration = isset($v['ctrl']['rootLevel']) ?
(int)$v['ctrl']['rootLevel'] : 0;
534 if ($table !== 'pages'
535 && $this->isRecordCreationAllowedForTable($table)
536 && $this->isTableAllowedOnPage($table, $this->pageinfo
)
537 && $this->getBackendUserAuthentication()->check('tables_modify', $table)
538 && ($rootLevelConfiguration === -1 ||
($this->id
xor $rootLevelConfiguration))
539 && $this->getBackendUserAuthentication()->workspaceCreateNewRecord(($this->pageinfo
['_ORIG_uid'] ?
$this->pageinfo
['_ORIG_uid'] : $this->id
), $table)
541 $newRecordIcon = $this->moduleTemplate
->getIconFactory()->getIconForRecord($table, [], Icon
::SIZE_SMALL
)->render();
544 // Create new link for record:
545 $newLink = $this->renderLink(
546 $newRecordIcon . htmlspecialchars($lang->sL($v['ctrl']['title'])),
550 // If the table is 'tt_content', add link to wizard
551 if ($table === 'tt_content') {
552 $groupName = $lang->getLL('createNewContent');
553 $rowContent = $newContentIcon
554 . '<strong>' . $lang->getLL('createNewContent') . '</strong>'
556 // If mod.newContentElementWizard.override is set, use that extension's wizard instead:
557 $moduleName = BackendUtility
::getPagesTSconfig($this->id
)['mod.']['newContentElementWizard.']['override']
558 ??
'new_content_element_wizard';
559 /** @var \TYPO3\CMS\Core\Http\NormalizedParams */
560 $normalizedParams = $request->getAttribute('normalizedParams');
561 $url = (string)$uriBuilder->buildUriFromRoute($moduleName, ['id' => $this->id
, 'returnUrl' => $normalizedParams->getRequestUri()]);
562 $rowContent .= '<li>' . $newLink . ' ' . BackendUtility
::wrapInHelp($table, '') . '</li>'
564 . '<a href="' . htmlspecialchars($url) . '" data-title="' . htmlspecialchars($this->getLanguageService()->getLL('newContentElement')) . '" class="t3js-toggle-new-content-element-wizard">'
565 . $newContentIcon . htmlspecialchars($lang->getLL('clickForWizard'))
571 if ($v['ctrl']['readOnly'] ||
$v['ctrl']['hideTable'] ||
$v['ctrl']['is_static']) {
574 if ($v['ctrl']['adminOnly'] && !$isAdmin) {
577 $nameParts = explode('_', $table);
580 if ($nameParts[0] === 'tx' ||
$nameParts[0] === 'tt') {
581 // Try to extract extension name
582 $title = (string)($v['ctrl']['title'] ??
'');
583 if (strpos($title, 'LLL:EXT:') === 0) {
584 $_EXTKEY = substr($title, 8);
585 $_EXTKEY = substr($_EXTKEY, 0, strpos($_EXTKEY, '/'));
586 if ($_EXTKEY !== '') {
587 // First try to get localisation of extension title
588 $temp = explode(':', substr($title, 9 +
strlen($_EXTKEY)));
589 $langFile = $temp[0];
590 $thisTitle = $lang->sL('LLL:EXT:' . $_EXTKEY . '/' . $langFile . ':extension.title');
591 // If no localisation available, read title from ext_emconf.php
592 $extPath = ExtensionManagementUtility
::extPath($_EXTKEY);
593 $extEmConfFile = $extPath . 'ext_emconf.php';
594 if (!$thisTitle && is_file($extEmConfFile)) {
596 include $extEmConfFile;
597 $thisTitle = $EM_CONF[$_EXTKEY]['title'];
599 $iconFile[$_EXTKEY] = '<img src="' . PathUtility
::getAbsoluteWebPath(ExtensionManagementUtility
::getExtensionIcon($extPath, true)) . '" width="16" height="16" alt="' . $thisTitle . '" />';
602 if (empty($thisTitle)) {
603 $_EXTKEY = $nameParts[1];
604 $thisTitle = $nameParts[1];
605 $iconFile[$_EXTKEY] = '';
609 $thisTitle = $lang->getLL('system_records');
610 $iconFile['system'] = $this->moduleTemplate
->getIconFactory()->getIcon('apps-pagetree-root', Icon
::SIZE_SMALL
)->render();
613 if ($groupName === '' ||
$groupName !== $_EXTKEY) {
614 $groupName = empty($v['ctrl']['groupName']) ?
$_EXTKEY : $v['ctrl']['groupName'];
616 $rowContent .= $newLink;
618 // Compile table row:
619 if ($table === 'tt_content') {
620 $startRows[] = '<li>' . $rowContent . '</li>';
622 $this->tRows
[$groupName]['title'] = $thisTitle;
623 $this->tRows
[$groupName]['html'][] = $rowContent;
624 $this->tRows
[$groupName]['table'][] = $table;
631 if (isset($pageTS['mod.']['wizards.']['newRecord.']['order'])) {
632 $this->newRecordSortList
= GeneralUtility
::trimExplode(',', $pageTS['mod.']['wizards.']['newRecord.']['order'], true);
634 uksort($this->tRows
, [$this, 'sortTableRows']);
635 // Compile table row:
637 $finalRows[] = implode('', $startRows);
638 foreach ($this->tRows
as $key => $value) {
639 $row = '<li>' . $iconFile[$key] . ' <strong>' . $value['title'] . '</strong><ul>';
640 foreach ($value['html'] as $recordKey => $record) {
641 $row .= '<li>' . $record . ' ' . BackendUtility
::wrapInHelp($value['table'][$recordKey], '') . '</li>';
643 $row .= '</ul></li>';
647 $finalRows[] = '</ul>';
649 $this->code
.= implode('', $finalRows);
653 * User array sort function used by renderNewRecordControls
655 * @param string $a First array element for compare
656 * @param string $b First array element for compare
657 * @return int -1 for lower, 0 for equal, 1 for greater
659 protected function sortTableRows(string $a, string $b): int
661 if (!empty($this->newRecordSortList
)) {
662 if (in_array($a, $this->newRecordSortList
) && in_array($b, $this->newRecordSortList
)) {
663 // Both are in the list, return relative to position in array
664 $sub = array_search($a, $this->newRecordSortList
) - array_search($b, $this->newRecordSortList
);
665 $ret = ($sub < 0 ?
-1 : $sub == 0) ?
0 : 1;
666 } elseif (in_array($a, $this->newRecordSortList
)) {
667 // First element is in array, put to top
669 } elseif (in_array($b, $this->newRecordSortList
)) {
670 // Second element is in array, put first to bottom
673 // No element is in array, return alphabetic order
674 $ret = strnatcasecmp($this->tRows
[$a]['title'], $this->tRows
[$b]['title']);
678 // Return alphabetic order
679 return strnatcasecmp($this->tRows
[$a]['title'], $this->tRows
[$b]['title']);
683 * Links the string $code to a create-new form for a record in $table created on page $pid
685 * @param string $linkText Link text
686 * @param string $table Table name (in which to create new record)
687 * @param int $pid PID value for the "&edit['.$table.']['.$pid.']=new" command (positive/negative)
688 * @param bool $addContentTable If $addContentTable is set, then a new tt_content record is created together with pages
689 * @return string The link.
691 protected function renderLink(string $linkText, string $table, int $pid, bool $addContentTable = false): string
699 'returnUrl' => $this->returnUrl
702 if ($table === 'pages' && $addContentTable) {
703 $urlParameters['tt_content']['prev'] = 'new';
704 $urlParameters['returnNewPageId'] = 1;
707 $uriBuilder = GeneralUtility
::makeInstance(UriBuilder
::class);
708 $url = (string)$uriBuilder->buildUriFromRoute('record_edit', $urlParameters);
710 return '<a href="' . htmlspecialchars($url) . '">' . $linkText . '</a>';
714 * Returns TRUE if the tablename $checkTable is allowed to be created on the page with record $pid_row
716 * @param string $table Table name to check
717 * @param array $page Potential parent page
718 * @return bool Returns TRUE if the tablename $table is allowed to be created on the $page
720 protected function isTableAllowedOnPage(string $table, array $page): bool
723 return $this->getBackendUserAuthentication()->isAdmin();
725 // be_users and be_groups may not be created anywhere but in the root.
726 if ($table === 'be_users' ||
$table === 'be_groups') {
730 $doktype = (int)$page['doktype'];
731 if (!($allowedTableList = $GLOBALS['PAGES_TYPES'][$doktype]['allowedTables'])) {
732 $allowedTableList = $GLOBALS['PAGES_TYPES']['default']['allowedTables'];
734 // If all tables or the table is listed as an allowed type, return TRUE
735 if (strstr($allowedTableList, '*') || GeneralUtility
::inList($allowedTableList, $table)) {
743 * Returns whether the record link should be shown for a table
746 * - $allowedNewTables and $deniedNewTables are empty
747 * - the table is not found in $deniedNewTables and $allowedNewTables is not set or the $table tablename is found in
750 * If $table tablename is found in $allowedNewTables and $deniedNewTables,
751 * $deniedNewTables has priority over $allowedNewTables.
753 * @param string $table Table name to test if in allowedTables
754 * @param array $allowedNewTables Array of new tables that are allowed.
755 * @param array $deniedNewTables Array of new tables that are not allowed.
756 * @return bool Returns TRUE if a link for creating new records should be displayed for $table
758 protected function isRecordCreationAllowedForTable(string $table, array $allowedNewTables = [], array $deniedNewTables = []): bool
760 if (!$this->getBackendUserAuthentication()->check('tables_modify', $table)) {
764 $allowedNewTables = $allowedNewTables ?
: $this->allowedNewTables
;
765 $deniedNewTables = $deniedNewTables ?
: $this->deniedNewTables
;
766 // No deny/allow tables are set:
767 if (empty($allowedNewTables) && empty($deniedNewTables)) {
771 return !in_array($table, $deniedNewTables) && (empty($allowedNewTables) ||
in_array($table, $allowedNewTables));
775 * Checks if sys_language records are present
779 protected function checkIfLanguagesExist(): bool
781 $queryBuilder = GeneralUtility
::makeInstance(ConnectionPool
::class)
782 ->getQueryBuilderForTable('sys_language');
783 $queryBuilder->getRestrictions()->removeAll();
785 $count = $queryBuilder
787 ->from('sys_language')
794 * @return LanguageService
796 protected function getLanguageService(): LanguageService
798 return $GLOBALS['LANG'];
802 * @return BackendUserAuthentication
804 protected function getBackendUserAuthentication(): BackendUserAuthentication
806 return $GLOBALS['BE_USER'];