[TASK] Create own response instance in controller actions
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Classes / Controller / NewRecordController.php
1 <?php
2 namespace TYPO3\CMS\Backend\Controller;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use Psr\Http\Message\ResponseInterface;
18 use Psr\Http\Message\ServerRequestInterface;
19 use TYPO3\CMS\Backend\Routing\UriBuilder;
20 use TYPO3\CMS\Backend\Template\Components\ButtonBar;
21 use TYPO3\CMS\Backend\Template\ModuleTemplate;
22 use TYPO3\CMS\Backend\Tree\View\NewRecordPageTreeView;
23 use TYPO3\CMS\Backend\Tree\View\PagePositionMap;
24 use TYPO3\CMS\Backend\Utility\BackendUtility;
25 use TYPO3\CMS\Core\Database\ConnectionPool;
26 use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
27 use TYPO3\CMS\Core\Http\HtmlResponse;
28 use TYPO3\CMS\Core\Imaging\Icon;
29 use TYPO3\CMS\Core\Type\Bitmask\Permission;
30 use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
31 use TYPO3\CMS\Core\Utility\GeneralUtility;
32 use TYPO3\CMS\Core\Utility\HttpUtility;
33 use TYPO3\CMS\Core\Utility\PathUtility;
34 use TYPO3\CMS\Frontend\Page\PageRepository;
35
36 /**
37 * Script class for 'db_new'
38 */
39 class NewRecordController
40 {
41 /**
42 * @var array
43 */
44 public $pageinfo;
45
46 /**
47 * @var array
48 */
49 public $pidInfo;
50
51 /**
52 * @var array
53 */
54 protected $newRecordSortList;
55
56 /**
57 * @var int
58 */
59 public $newPagesInto;
60
61 /**
62 * @var int
63 */
64 public $newContentInto;
65
66 /**
67 * @var int
68 */
69 public $newPagesAfter;
70
71 /**
72 * Determines, whether "Select Position" for new page should be shown
73 *
74 * @var bool
75 */
76 protected $newPagesSelectPosition = true;
77
78 /**
79 * @var array
80 */
81 public $web_list_modTSconfig;
82
83 /**
84 * @var array
85 */
86 public $allowedNewTables;
87
88 /**
89 * @var array
90 */
91 public $deniedNewTables;
92
93 /**
94 * @var array
95 */
96 public $web_list_modTSconfig_pid;
97
98 /**
99 * @var array
100 */
101 public $allowedNewTables_pid;
102
103 /**
104 * @var array
105 */
106 public $deniedNewTables_pid;
107
108 /**
109 * @var string
110 */
111 public $code;
112
113 /**
114 * @var string
115 */
116 public $R_URI;
117
118 /**
119 * @var int
120 */
121 public $id;
122
123 /**
124 * @var string
125 */
126 public $returnUrl;
127
128 /**
129 * pagesOnly flag.
130 *
131 * @var int
132 */
133 public $pagesOnly;
134
135 /**
136 * @var string
137 */
138 public $perms_clause;
139
140 /**
141 * Accumulated HTML output
142 *
143 * @var string
144 */
145 public $content;
146
147 /**
148 * @var array
149 */
150 public $tRows;
151
152 /**
153 * ModuleTemplate object
154 *
155 * @var ModuleTemplate
156 */
157 protected $moduleTemplate;
158
159 /**
160 * Constructor
161 */
162 public function __construct()
163 {
164 $this->moduleTemplate = GeneralUtility::makeInstance(ModuleTemplate::class);
165 $GLOBALS['SOBE'] = $this;
166 $this->getLanguageService()->includeLLFile('EXT:lang/Resources/Private/Language/locallang_misc.xlf');
167 $this->init();
168 }
169
170 /**
171 * Constructor function for the class
172 */
173 protected function init()
174 {
175 $beUser = $this->getBackendUserAuthentication();
176 // Page-selection permission clause (reading)
177 $this->perms_clause = $beUser->getPagePermsClause(Permission::PAGE_SHOW);
178 // This will hide records from display - it has nothing to do with user rights!!
179 if ($pidList = $beUser->getTSConfigVal('options.hideRecords.pages')) {
180 if (!empty($pidList)) {
181 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
182 ->getQueryBuilderForTable('pages');
183 $this->perms_clause .= ' AND ' . $queryBuilder->expr()->notIn(
184 'pages.uid',
185 GeneralUtility::intExplode(',', $pidList)
186 );
187 }
188 }
189 // Setting GPvars:
190 // The page id to operate from
191 $this->id = (int)GeneralUtility::_GP('id');
192 $this->returnUrl = GeneralUtility::sanitizeLocalUrl(GeneralUtility::_GP('returnUrl'));
193 $this->pagesOnly = GeneralUtility::_GP('pagesOnly');
194 // Setting up the context sensitive menu:
195 $this->moduleTemplate->getPageRenderer()->loadRequireJsModule('TYPO3/CMS/Backend/ContextMenu');
196 $this->moduleTemplate->getPageRenderer()->loadRequireJsModule('TYPO3/CMS/Backend/Tooltip');
197 $this->moduleTemplate->getPageRenderer()->loadRequireJsModule(
198 'TYPO3/CMS/Backend/Wizard/NewContentElement',
199 'function(NewContentElement) {
200 require([\'jquery\'], function($) {
201 $(function() {
202 $(\'.t3js-toggle-new-content-element-wizard\').click(function() {
203 var $me = $(this);
204 NewContentElement.wizard($me.data(\'url\'), $me.data(\'title\'));
205 });
206 });
207 });
208 }'
209 );
210 // Creating content
211 $this->content = '';
212 $this->content .= '<h1>'
213 . $this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:db_new.php.pagetitle')
214 . '</h1>';
215 // Id a positive id is supplied, ask for the page record with permission information contained:
216 if ($this->id > 0) {
217 $this->pageinfo = BackendUtility::readPageAccess($this->id, $this->perms_clause);
218 }
219 // If a page-record was returned, the user had read-access to the page.
220 if ($this->pageinfo['uid']) {
221 // Get record of parent page
222 $this->pidInfo = BackendUtility::getRecord('pages', $this->pageinfo['pid']);
223 // Checking the permissions for the user with regard to the parent page: Can he create new pages, new
224 // content record, new page after?
225 if ($beUser->doesUserHaveAccess($this->pageinfo, 8)) {
226 $this->newPagesInto = 1;
227 }
228 if ($beUser->doesUserHaveAccess($this->pageinfo, 16)) {
229 $this->newContentInto = 1;
230 }
231 if (($beUser->isAdmin() || is_array($this->pidInfo)) && $beUser->doesUserHaveAccess($this->pidInfo, 8)) {
232 $this->newPagesAfter = 1;
233 }
234 } elseif ($beUser->isAdmin()) {
235 // Admins can do it all
236 $this->newPagesInto = 1;
237 $this->newContentInto = 1;
238 $this->newPagesAfter = 0;
239 } else {
240 // People with no permission can do nothing
241 $this->newPagesInto = 0;
242 $this->newContentInto = 0;
243 $this->newPagesAfter = 0;
244 }
245 }
246
247 /**
248 * Injects the request object for the current request or subrequest
249 * As this controller goes only through the main() method, it is rather simple for now
250 *
251 * @param ServerRequestInterface $request the current request
252 * @return ResponseInterface the response with the content
253 */
254 public function mainAction(ServerRequestInterface $request): ResponseInterface
255 {
256 $this->main();
257 return new HtmlResponse($this->moduleTemplate->renderContent());
258 }
259
260 /**
261 * Main processing, creating the list of new record tables to select from
262 */
263 public function main()
264 {
265 // If there was a page - or if the user is admin (admins has access to the root) we proceed:
266 if (!empty($this->pageinfo['uid']) || $this->getBackendUserAuthentication()->isAdmin()) {
267 if (empty($this->pageinfo)) {
268 // Explicitly pass an empty array to the docHeader
269 $this->moduleTemplate->getDocHeaderComponent()->setMetaInformation([]);
270 } else {
271 $this->moduleTemplate->getDocHeaderComponent()->setMetaInformation($this->pageinfo);
272 }
273 // Acquiring TSconfig for this module/current page:
274 $this->web_list_modTSconfig = BackendUtility::getModTSconfig($this->pageinfo['uid'], 'mod.web_list');
275 $this->allowedNewTables = GeneralUtility::trimExplode(
276 ',',
277 $this->web_list_modTSconfig['properties']['allowedNewTables'],
278 true
279 );
280 $this->deniedNewTables = GeneralUtility::trimExplode(
281 ',',
282 $this->web_list_modTSconfig['properties']['deniedNewTables'],
283 true
284 );
285 // Acquiring TSconfig for this module/parent page:
286 $this->web_list_modTSconfig_pid = BackendUtility::getModTSconfig($this->pageinfo['pid'], 'mod.web_list');
287 $this->allowedNewTables_pid = GeneralUtility::trimExplode(
288 ',',
289 $this->web_list_modTSconfig_pid['properties']['allowedNewTables'],
290 true
291 );
292 $this->deniedNewTables_pid = GeneralUtility::trimExplode(
293 ',',
294 $this->web_list_modTSconfig_pid['properties']['deniedNewTables'],
295 true
296 );
297 // More init:
298 if (!$this->showNewRecLink('pages')) {
299 $this->newPagesInto = 0;
300 }
301 if (!$this->showNewRecLink('pages', $this->allowedNewTables_pid, $this->deniedNewTables_pid)) {
302 $this->newPagesAfter = 0;
303 }
304 // Set header-HTML and return_url
305 if (is_array($this->pageinfo) && $this->pageinfo['uid']) {
306 $title = strip_tags($this->pageinfo[$GLOBALS['TCA']['pages']['ctrl']['label']]);
307 } else {
308 $title = $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'];
309 }
310 $this->moduleTemplate->setTitle($title);
311 // GENERATE the HTML-output depending on mode (pagesOnly is the page wizard)
312 // Regular new element:
313 if (!$this->pagesOnly) {
314 $this->regularNew();
315 } elseif ($this->showNewRecLink('pages')) {
316 // Pages only wizard
317 $this->pagesOnly();
318 }
319 // Add all the content to an output section
320 $this->content .= '<div>' . $this->code . '</div>';
321 // Setting up the buttons and markers for docheader
322 $this->getButtons();
323 // Build the <body> for the module
324 $this->moduleTemplate->setContent($this->content);
325 }
326 }
327
328 /**
329 * Create the panel of buttons for submitting the form or otherwise perform operations.
330 */
331 protected function getButtons()
332 {
333 $lang = $this->getLanguageService();
334 $buttonBar = $this->moduleTemplate->getDocHeaderComponent()->getButtonBar();
335 // Regular new element:
336 if (!$this->pagesOnly) {
337 // New page
338 if ($this->showNewRecLink('pages')) {
339 $newPageButton = $buttonBar->makeLinkButton()
340 ->setHref(GeneralUtility::linkThisScript(['pagesOnly' => '1']))
341 ->setTitle($lang->sL('LLL:EXT:backend/Resources/Private/Language/locallang_layout.xlf:newPage'))
342 ->setIcon($this->moduleTemplate->getIconFactory()->getIcon('actions-page-new', Icon::SIZE_SMALL));
343 $buttonBar->addButton($newPageButton, ButtonBar::BUTTON_POSITION_LEFT, 20);
344 }
345 // CSH
346 $cshButton = $buttonBar->makeHelpButton()->setModuleName('xMOD_csh_corebe')->setFieldName('new_regular');
347 $buttonBar->addButton($cshButton);
348 } elseif ($this->showNewRecLink('pages')) {
349 // Pages only wizard
350 // CSH
351 $buttons['csh'] = BackendUtility::cshItem('xMOD_csh_corebe', 'new_pages');
352 $cshButton = $buttonBar->makeHelpButton()->setModuleName('xMOD_csh_corebe')->setFieldName('new_pages');
353 $buttonBar->addButton($cshButton);
354 }
355 // Back
356 if ($this->returnUrl) {
357 $returnButton = $buttonBar->makeLinkButton()
358 ->setHref($this->returnUrl)
359 ->setTitle($lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.goBack'))
360 ->setIcon($this->moduleTemplate->getIconFactory()->getIcon('actions-view-go-back', Icon::SIZE_SMALL));
361 $buttonBar->addButton($returnButton, ButtonBar::BUTTON_POSITION_LEFT, 10);
362 }
363
364 if (is_array($this->pageinfo) && $this->pageinfo['uid']) {
365 // View
366 $pagesTSconfig = BackendUtility::getPagesTSconfig($this->pageinfo['uid']);
367 if (isset($pagesTSconfig['TCEMAIN.']['preview.']['disableButtonForDokType'])) {
368 $excludeDokTypes = GeneralUtility::intExplode(
369 ',',
370 $pagesTSconfig['TCEMAIN.']['preview.']['disableButtonForDokType'],
371 true
372 );
373 } else {
374 // exclude sysfolders and recycler by default
375 $excludeDokTypes = [
376 PageRepository::DOKTYPE_RECYCLER,
377 PageRepository::DOKTYPE_SYSFOLDER,
378 PageRepository::DOKTYPE_SPACER
379 ];
380 }
381 if (!in_array((int)$this->pageinfo['doktype'], $excludeDokTypes, true)) {
382 $viewButton = $buttonBar->makeLinkButton()
383 ->setHref('#')
384 ->setOnClick(BackendUtility::viewOnClick(
385 $this->pageinfo['uid'],
386 '',
387 BackendUtility::BEgetRootLine($this->pageinfo['uid'])
388 ))
389 ->setTitle($lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.showPage'))
390 ->setIcon($this->moduleTemplate->getIconFactory()->getIcon(
391 'actions-view-page',
392 Icon::SIZE_SMALL
393 ));
394 $buttonBar->addButton($viewButton, ButtonBar::BUTTON_POSITION_LEFT, 30);
395 }
396 }
397 }
398
399 /**
400 * Creates the position map for pages wizard
401 */
402 public function pagesOnly()
403 {
404 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
405 ->getQueryBuilderForTable('sys_language');
406 $queryBuilder->getRestrictions()
407 ->removeAll()
408 ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
409 $numberOfPages = $queryBuilder
410 ->count('*')
411 ->from('pages')
412 ->execute()
413 ->fetchColumn(0);
414
415 if ($numberOfPages > 0) {
416 $this->code .= '<h3>' . htmlspecialchars($this->getLanguageService()->getLL('selectPosition')) . ':</h3>';
417 $positionMap = GeneralUtility::makeInstance(PagePositionMap::class, NewRecordPageTreeView::class);
418 /** @var $positionMap \TYPO3\CMS\Backend\Tree\View\PagePositionMap */
419 $this->code .= $positionMap->positionTree(
420 $this->id,
421 $this->pageinfo,
422 $this->perms_clause,
423 $this->returnUrl
424 );
425 } else {
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.
429 $urlParameters = [
430 'edit' => [
431 'pages' => [
432 0 => 'new'
433 ]
434 ],
435 'returnNewPageId' => 1,
436 'returnUrl' => (string)$uriBuilder->buildUriFromRoute('db_new', ['id' => $this->id, 'pagesOnly' => '1'])
437 ];
438 $url = (string)$uriBuilder->buildUriFromRoute('record_edit', $urlParameters);
439 @ob_end_clean();
440 HttpUtility::redirect($url);
441 }
442 }
443
444 /**
445 * Create a regular new element (pages and records)
446 */
447 public function regularNew()
448 {
449 $lang = $this->getLanguageService();
450 // Initialize array for accumulating table rows:
451 $this->tRows = [];
452 // Get TSconfig for current page
453 $pageTS = BackendUtility::getPagesTSconfig($this->id);
454 // Finish initializing new pages options with TSconfig
455 // Each new page option may be hidden by TSconfig
456 // Enabled option for the position of a new page
457 $this->newPagesSelectPosition = !empty($pageTS['mod.']['wizards.']['newRecord.']['pages.']['show.']['pageSelectPosition']);
458 // Pseudo-boolean (0/1) for backward compatibility
459 $displayNewPagesIntoLink = $this->newPagesInto && !empty($pageTS['mod.']['wizards.']['newRecord.']['pages.']['show.']['pageInside']);
460 $displayNewPagesAfterLink = $this->newPagesAfter && !empty($pageTS['mod.']['wizards.']['newRecord.']['pages.']['show.']['pageAfter']);
461 // Slight spacer from header:
462 $this->code .= '';
463 // New Page
464 $table = 'pages';
465 $v = $GLOBALS['TCA'][$table];
466 $pageIcon = $this->moduleTemplate->getIconFactory()->getIconForRecord(
467 $table,
468 [],
469 Icon::SIZE_SMALL
470 )->render();
471 $newPageIcon = $this->moduleTemplate->getIconFactory()->getIcon('actions-page-new', Icon::SIZE_SMALL)->render();
472 $rowContent = '';
473 // New pages INSIDE this pages
474 $newPageLinks = [];
475 if ($displayNewPagesIntoLink
476 && $this->isTableAllowedForThisPage($this->pageinfo, 'pages')
477 && $this->getBackendUserAuthentication()->check('tables_modify', 'pages')
478 && $this->getBackendUserAuthentication()->workspaceCreateNewRecord(($this->pageinfo['_ORIG_uid'] ?: $this->id), 'pages')
479 ) {
480 // Create link to new page inside:
481 $recordIcon = $this->moduleTemplate->getIconFactory()->getIconForRecord($table, [], Icon::SIZE_SMALL)->render();
482 $newPageLinks[] = $this->linkWrap(
483 $recordIcon . htmlspecialchars($lang->sL($v['ctrl']['title'])) . ' (' . htmlspecialchars($lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:db_new.php.inside')) . ')',
484 $table,
485 $this->id
486 );
487 }
488 // New pages AFTER this pages
489 if ($displayNewPagesAfterLink
490 && $this->isTableAllowedForThisPage($this->pidInfo, 'pages')
491 && $this->getBackendUserAuthentication()->check('tables_modify', 'pages')
492 && $this->getBackendUserAuthentication()->workspaceCreateNewRecord($this->pidInfo['uid'], 'pages')
493 ) {
494 $newPageLinks[] = $this->linkWrap($pageIcon . htmlspecialchars($lang->sL($v['ctrl']['title'])) . ' (' . htmlspecialchars($lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:db_new.php.after')) . ')', 'pages', -$this->id);
495 }
496 // New pages at selection position
497 if ($this->newPagesSelectPosition && $this->showNewRecLink('pages')) {
498 // Link to page-wizard:
499 $newPageLinks[] = '<a href="' . htmlspecialchars(GeneralUtility::linkThisScript(['pagesOnly' => 1])) . '">' . $pageIcon . htmlspecialchars($lang->getLL('pageSelectPosition')) . '</a>';
500 }
501 // Assemble all new page links
502 $numPageLinks = count($newPageLinks);
503 for ($i = 0; $i < $numPageLinks; $i++) {
504 $rowContent .= '<li>' . $newPageLinks[$i] . '</li>';
505 }
506 if ($this->showNewRecLink('pages')) {
507 $rowContent = '<ul class="list-tree"><li>' . $newPageIcon . '<strong>' .
508 $lang->getLL('createNewPage') . '</strong><ul>' . $rowContent . '</ul></li>';
509 } else {
510 $rowContent = '<ul class="list-tree"><li><ul>' . $rowContent . '</li></ul>';
511 }
512 /** @var \TYPO3\CMS\Backend\Routing\UriBuilder $uriBuilder */
513 $uriBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\UriBuilder::class);
514 // Compile table row
515 $startRows = [$rowContent];
516 $iconFile = [];
517 // New tables (but not pages) INSIDE this pages
518 $isAdmin = $this->getBackendUserAuthentication()->isAdmin();
519 $newContentIcon = $this->moduleTemplate->getIconFactory()->getIcon('actions-document-new', Icon::SIZE_SMALL)->render();
520 if ($this->newContentInto) {
521 if (is_array($GLOBALS['TCA'])) {
522 $groupName = '';
523 foreach ($GLOBALS['TCA'] as $table => $v) {
524 $rootLevelConfiguration = isset($v['ctrl']['rootLevel']) ? (int)$v['ctrl']['rootLevel'] : 0;
525 if ($table !== 'pages'
526 && $this->showNewRecLink($table)
527 && $this->isTableAllowedForThisPage($this->pageinfo, $table)
528 && $this->getBackendUserAuthentication()->check('tables_modify', $table)
529 && ($rootLevelConfiguration === -1 || ($this->id xor $rootLevelConfiguration))
530 && $this->getBackendUserAuthentication()->workspaceCreateNewRecord(($this->pageinfo['_ORIG_uid'] ? $this->pageinfo['_ORIG_uid'] : $this->id), $table)
531 ) {
532 $newRecordIcon = $this->moduleTemplate->getIconFactory()->getIconForRecord($table, [], Icon::SIZE_SMALL)->render();
533 $rowContent = '';
534 $thisTitle = '';
535 // Create new link for record:
536 $newLink = $this->linkWrap($newRecordIcon . htmlspecialchars($lang->sL($v['ctrl']['title'])), $table, $this->id);
537 // If the table is 'tt_content', create link to wizard
538 if ($table === 'tt_content') {
539 $groupName = $lang->getLL('createNewContent');
540 $rowContent = $newContentIcon
541 . '<strong>' . $lang->getLL('createNewContent') . '</strong>'
542 . '<ul>';
543 // If mod.newContentElementWizard.override is set, use that extension's wizard instead:
544 $tsConfig = BackendUtility::getModTSconfig($this->id, 'mod');
545 $moduleName = $tsConfig['properties']['newContentElementWizard.']['override'] ?? 'new_content_element_wizard';
546 $url = (string)$uriBuilder->buildUriFromRoute($moduleName, ['id' => $this->id, 'returnUrl' => GeneralUtility::getIndpEnv('REQUEST_URI')]);
547 $rowContent .= '<li>' . $newLink . ' ' . BackendUtility::wrapInHelp($table, '') . '</li>'
548 . '<li>'
549 . '<a href="#" data-url="' . htmlspecialchars($url) . '" data-title="' . htmlspecialchars($this->getLanguageService()->getLL('newContentElement')) . '" class="t3js-toggle-new-content-element-wizard">'
550 . $newContentIcon . htmlspecialchars($lang->getLL('clickForWizard'))
551 . '</a>'
552 . '</li>'
553 . '</ul>';
554 } else {
555 // Get the title
556 if ($v['ctrl']['readOnly'] || $v['ctrl']['hideTable'] || $v['ctrl']['is_static']) {
557 continue;
558 }
559 if ($v['ctrl']['adminOnly'] && !$isAdmin) {
560 continue;
561 }
562 $nameParts = explode('_', $table);
563 $thisTitle = '';
564 $_EXTKEY = '';
565 if ($nameParts[0] === 'tx' || $nameParts[0] === 'tt') {
566 // Try to extract extension name
567 if (substr($v['ctrl']['title'], 0, 8) === 'LLL:EXT:') {
568 $_EXTKEY = substr($v['ctrl']['title'], 8);
569 $_EXTKEY = substr($_EXTKEY, 0, strpos($_EXTKEY, '/'));
570 if ($_EXTKEY !== '') {
571 // First try to get localisation of extension title
572 $temp = explode(':', substr($v['ctrl']['title'], 9 + strlen($_EXTKEY)));
573 $langFile = $temp[0];
574 $thisTitle = $lang->sL('LLL:EXT:' . $_EXTKEY . '/' . $langFile . ':extension.title');
575 // If no localisation available, read title from ext_emconf.php
576 $extPath = ExtensionManagementUtility::extPath($_EXTKEY);
577 $extEmConfFile = $extPath . 'ext_emconf.php';
578 if (!$thisTitle && is_file($extEmConfFile)) {
579 $EM_CONF = [];
580 include $extEmConfFile;
581 $thisTitle = $EM_CONF[$_EXTKEY]['title'];
582 }
583 $iconFile[$_EXTKEY] = '<img src="' . PathUtility::getAbsoluteWebPath(ExtensionManagementUtility::getExtensionIcon($extPath, true)) . '" ' . 'width="16" height="16" ' . 'alt="' . $thisTitle . '" />';
584 }
585 }
586 if (empty($thisTitle)) {
587 $_EXTKEY = $nameParts[1];
588 $thisTitle = $nameParts[1];
589 $iconFile[$_EXTKEY] = '';
590 }
591 } else {
592 $_EXTKEY = 'system';
593 $thisTitle = $lang->getLL('system_records');
594 $iconFile['system'] = $this->moduleTemplate->getIconFactory()->getIcon('apps-pagetree-root', Icon::SIZE_SMALL)->render();
595 }
596
597 if ($groupName === '' || $groupName !== $_EXTKEY) {
598 $groupName = empty($v['ctrl']['groupName']) ? $_EXTKEY : $v['ctrl']['groupName'];
599 }
600 $rowContent .= $newLink;
601 }
602 // Compile table row:
603 if ($table === 'tt_content') {
604 $startRows[] = '<li>' . $rowContent . '</li>';
605 } else {
606 $this->tRows[$groupName]['title'] = $thisTitle;
607 $this->tRows[$groupName]['html'][] = $rowContent;
608 $this->tRows[$groupName]['table'][] = $table;
609 }
610 }
611 }
612 }
613 }
614 // User sort
615 if (isset($pageTS['mod.']['wizards.']['newRecord.']['order'])) {
616 $this->newRecordSortList = GeneralUtility::trimExplode(',', $pageTS['mod.']['wizards.']['newRecord.']['order'], true);
617 }
618 uksort($this->tRows, [$this, 'sortNewRecordsByConfig']);
619 // Compile table row:
620 $finalRows = [];
621 $finalRows[] = implode('', $startRows);
622 foreach ($this->tRows as $key => $value) {
623 $row = '<li>' . $iconFile[$key] . ' <strong>' . $value['title'] . '</strong><ul>';
624 foreach ($value['html'] as $recordKey => $record) {
625 $row .= '<li>' . $record . ' ' . BackendUtility::wrapInHelp($value['table'][$recordKey], '') . '</li>';
626 }
627 $row .= '</ul></li>';
628 $finalRows[] = $row;
629 }
630
631 $finalRows[] = '</ul>';
632 // Make table:
633 $this->code .= implode('', $finalRows);
634 }
635
636 /**
637 * User array sort function used by regularNew
638 *
639 * @param string $a First array element for compare
640 * @param string $b First array element for compare
641 * @return int -1 for lower, 0 for equal, 1 for greater
642 */
643 public function sortNewRecordsByConfig($a, $b)
644 {
645 if (!empty($this->newRecordSortList)) {
646 if (in_array($a, $this->newRecordSortList) && in_array($b, $this->newRecordSortList)) {
647 // Both are in the list, return relative to position in array
648 $sub = array_search($a, $this->newRecordSortList) - array_search($b, $this->newRecordSortList);
649 $ret = ($sub < 0 ? -1 : $sub == 0) ? 0 : 1;
650 } elseif (in_array($a, $this->newRecordSortList)) {
651 // First element is in array, put to top
652 $ret = -1;
653 } elseif (in_array($b, $this->newRecordSortList)) {
654 // Second element is in array, put first to bottom
655 $ret = 1;
656 } else {
657 // No element is in array, return alphabetic order
658 $ret = strnatcasecmp($this->tRows[$a]['title'], $this->tRows[$b]['title']);
659 }
660 return $ret;
661 }
662 // Return alphabetic order
663 return strnatcasecmp($this->tRows[$a]['title'], $this->tRows[$b]['title']);
664 }
665
666 /**
667 * Links the string $code to a create-new form for a record in $table created on page $pid
668 *
669 * @param string $linkText Link text
670 * @param string $table Table name (in which to create new record)
671 * @param int $pid PID value for the "&edit['.$table.']['.$pid.']=new" command (positive/negative)
672 * @param bool $addContentTable If $addContentTable is set, then a new tt_content record is created together with pages
673 * @return string The link.
674 */
675 public function linkWrap($linkText, $table, $pid, $addContentTable = false)
676 {
677 $urlParameters = [
678 'edit' => [
679 $table => [
680 $pid => 'new'
681 ]
682 ],
683 'returnUrl' => $this->returnUrl
684 ];
685 if ($table === 'pages' && $addContentTable) {
686 $urlParameters['tt_content']['prev'] = 'new';
687 $urlParameters['returnNewPageId'] = 1;
688 } elseif ($table === 'pages') {
689 $urlParameters['overrideVals']['pages']['doktype'] = (int)$this->pageinfo['doktype'];
690 }
691 $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
692 $url = (string)$uriBuilder->buildUriFromRoute('record_edit', $urlParameters);
693 return '<a href="' . htmlspecialchars($url) . '">' . $linkText . '</a>';
694 }
695
696 /**
697 * Returns TRUE if the tablename $checkTable is allowed to be created on the page with record $pid_row
698 *
699 * @param array $pid_row Record for parent page.
700 * @param string $checkTable Table name to check
701 * @return bool Returns TRUE if the tablename $checkTable is allowed to be created on the page with record $pid_row
702 */
703 public function isTableAllowedForThisPage($pid_row, $checkTable)
704 {
705 if (!is_array($pid_row)) {
706 return $this->getBackendUserAuthentication()->isAdmin();
707 }
708 // be_users and be_groups may not be created anywhere but in the root.
709 if ($checkTable === 'be_users' || $checkTable === 'be_groups') {
710 return false;
711 }
712 // Checking doktype:
713 $doktype = (int)$pid_row['doktype'];
714 if (!($allowedTableList = $GLOBALS['PAGES_TYPES'][$doktype]['allowedTables'])) {
715 $allowedTableList = $GLOBALS['PAGES_TYPES']['default']['allowedTables'];
716 }
717 // If all tables or the table is listed as an allowed type, return TRUE
718 if (strstr($allowedTableList, '*') || GeneralUtility::inList($allowedTableList, $checkTable)) {
719 return true;
720 }
721
722 return false;
723 }
724
725 /**
726 * Returns TRUE if:
727 * - $allowedNewTables and $deniedNewTables are empty
728 * - the table is not found in $deniedNewTables and $allowedNewTables is not set or the $table tablename is found in
729 * $allowedNewTables
730 *
731 * If $table tablename is found in $allowedNewTables and $deniedNewTables, $deniedNewTables
732 * has priority over $allowedNewTables.
733 *
734 * @param string $table Table name to test if in allowedTables
735 * @param array $allowedNewTables Array of new tables that are allowed.
736 * @param array $deniedNewTables Array of new tables that are not allowed.
737 *
738 * @return bool Returns TRUE if a link for creating new records should be displayed for $table
739 */
740 public function showNewRecLink($table, array $allowedNewTables = [], array $deniedNewTables = [])
741 {
742 if (!$this->getBackendUserAuthentication()->check('tables_modify', $table)) {
743 return false;
744 }
745
746 $allowedNewTables = $allowedNewTables ?: $this->allowedNewTables;
747 $deniedNewTables = $deniedNewTables ?: $this->deniedNewTables;
748 // No deny/allow tables are set:
749 if (empty($allowedNewTables) && empty($deniedNewTables)) {
750 return true;
751 }
752
753 return !in_array($table, $deniedNewTables) && (empty($allowedNewTables) || in_array($table, $allowedNewTables));
754 }
755
756 /**
757 * Checks if sys_language records are present
758 *
759 * @return bool
760 */
761 protected function checkIfLanguagesExist()
762 {
763 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
764 ->getQueryBuilderForTable('sys_language');
765 $queryBuilder->getRestrictions()->removeAll();
766
767 $count = $queryBuilder
768 ->count('uid')
769 ->from('sys_language')
770 ->execute()
771 ->fetchColumn(0);
772 return (bool)$count;
773 }
774
775 /**
776 * Return language service instance
777 *
778 * @return \TYPO3\CMS\Core\Localization\LanguageService
779 */
780 protected function getLanguageService()
781 {
782 return $GLOBALS['LANG'];
783 }
784
785 /**
786 * Returns the global BackendUserAuthentication object.
787 *
788 * @return \TYPO3\CMS\Core\Authentication\BackendUserAuthentication
789 */
790 protected function getBackendUserAuthentication()
791 {
792 return $GLOBALS['BE_USER'];
793 }
794 }