1fd33451a7b652d732b63a1c514aca9eb589aaa3
[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\Template\Components\ButtonBar;
20 use TYPO3\CMS\Backend\Template\ModuleTemplate;
21 use TYPO3\CMS\Backend\Tree\View\NewRecordPageTreeView;
22 use TYPO3\CMS\Backend\Tree\View\PagePositionMap;
23 use TYPO3\CMS\Backend\Utility\BackendUtility;
24 use TYPO3\CMS\Core\Database\ConnectionPool;
25 use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
26 use TYPO3\CMS\Core\Imaging\Icon;
27 use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
28 use TYPO3\CMS\Core\Utility\GeneralUtility;
29 use TYPO3\CMS\Core\Utility\HttpUtility;
30 use TYPO3\CMS\Core\Utility\PathUtility;
31 use TYPO3\CMS\Frontend\Page\PageRepository;
32
33 /**
34 * Script class for 'db_new'
35 */
36 class NewRecordController
37 {
38 /**
39 * @var array
40 */
41 public $pageinfo;
42
43 /**
44 * @var array
45 */
46 public $pidInfo;
47
48 /**
49 * @var array
50 */
51 protected $newRecordSortList;
52
53 /**
54 * @var int
55 */
56 public $newPagesInto;
57
58 /**
59 * @var int
60 */
61 public $newContentInto;
62
63 /**
64 * @var int
65 */
66 public $newPagesAfter;
67
68 /**
69 * Determines, whether "Select Position" for new page should be shown
70 *
71 * @var bool
72 */
73 protected $newPagesSelectPosition = true;
74
75 /**
76 * @var array
77 */
78 public $web_list_modTSconfig;
79
80 /**
81 * @var array
82 */
83 public $allowedNewTables;
84
85 /**
86 * @var array
87 */
88 public $deniedNewTables;
89
90 /**
91 * @var array
92 */
93 public $web_list_modTSconfig_pid;
94
95 /**
96 * @var array
97 */
98 public $allowedNewTables_pid;
99
100 /**
101 * @var array
102 */
103 public $deniedNewTables_pid;
104
105 /**
106 * @var string
107 */
108 public $code;
109
110 /**
111 * @var string
112 */
113 public $R_URI;
114
115 /**
116 * @var int
117 */
118 public $id;
119
120 /**
121 * @var string
122 */
123 public $returnUrl;
124
125 /**
126 * pagesOnly flag.
127 *
128 * @var int
129 */
130 public $pagesOnly;
131
132 /**
133 * @var string
134 */
135 public $perms_clause;
136
137 /**
138 * Accumulated HTML output
139 *
140 * @var string
141 */
142 public $content;
143
144 /**
145 * @var array
146 */
147 public $tRows;
148
149 /**
150 * ModuleTemplate object
151 *
152 * @var ModuleTemplate
153 */
154 protected $moduleTemplate;
155
156 /**
157 * Constructor
158 */
159 public function __construct()
160 {
161 $this->moduleTemplate = GeneralUtility::makeInstance(ModuleTemplate::class);
162 $GLOBALS['SOBE'] = $this;
163 $this->getLanguageService()->includeLLFile('EXT:lang/Resources/Private/Language/locallang_misc.xlf');
164 $this->init();
165 }
166
167 /**
168 * Constructor function for the class
169 */
170 protected function init()
171 {
172 $beUser = $this->getBackendUserAuthentication();
173 // Page-selection permission clause (reading)
174 $this->perms_clause = $beUser->getPagePermsClause(1);
175 // This will hide records from display - it has nothing to do with user rights!!
176 if ($pidList = $beUser->getTSConfigVal('options.hideRecords.pages')) {
177 if (!empty($pidList)) {
178 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
179 ->getQueryBuilderForTable('pages');
180 $this->perms_clause .= ' AND ' . $queryBuilder->expr()->notIn(
181 'pages.uid',
182 GeneralUtility::intExplode(',', $pidList)
183 );
184 }
185 }
186 // Setting GPvars:
187 // The page id to operate from
188 $this->id = (int)GeneralUtility::_GP('id');
189 $this->returnUrl = GeneralUtility::sanitizeLocalUrl(GeneralUtility::_GP('returnUrl'));
190 $this->pagesOnly = GeneralUtility::_GP('pagesOnly');
191 // Setting up the context sensitive menu:
192 $this->moduleTemplate->getPageRenderer()->loadRequireJsModule('TYPO3/CMS/Backend/ContextMenu');
193 $this->moduleTemplate->getPageRenderer()->loadRequireJsModule('TYPO3/CMS/Backend/Tooltip');
194 $this->moduleTemplate->getPageRenderer()->loadRequireJsModule(
195 'TYPO3/CMS/Backend/Wizard/NewContentElement',
196 'function(NewContentElement) {
197 require([\'jquery\'], function($) {
198 $(function() {
199 $(\'.t3js-toggle-new-content-element-wizard\').click(function() {
200 var $me = $(this);
201 NewContentElement.wizard($me.data(\'url\'), $me.data(\'title\'));
202 });
203 });
204 });
205 }'
206 );
207 // Creating content
208 $this->content = '';
209 $this->content .= '<h1>'
210 . $this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:db_new.php.pagetitle')
211 . '</h1>';
212 // Id a positive id is supplied, ask for the page record with permission information contained:
213 if ($this->id > 0) {
214 $this->pageinfo = BackendUtility::readPageAccess($this->id, $this->perms_clause);
215 }
216 // If a page-record was returned, the user had read-access to the page.
217 if ($this->pageinfo['uid']) {
218 // Get record of parent page
219 $this->pidInfo = BackendUtility::getRecord('pages', $this->pageinfo['pid']);
220 // Checking the permissions for the user with regard to the parent page: Can he create new pages, new
221 // content record, new page after?
222 if ($beUser->doesUserHaveAccess($this->pageinfo, 8)) {
223 $this->newPagesInto = 1;
224 }
225 if ($beUser->doesUserHaveAccess($this->pageinfo, 16)) {
226 $this->newContentInto = 1;
227 }
228 if (($beUser->isAdmin() || is_array($this->pidInfo)) && $beUser->doesUserHaveAccess($this->pidInfo, 8)) {
229 $this->newPagesAfter = 1;
230 }
231 } elseif ($beUser->isAdmin()) {
232 // Admins can do it all
233 $this->newPagesInto = 1;
234 $this->newContentInto = 1;
235 $this->newPagesAfter = 0;
236 } else {
237 // People with no permission can do nothing
238 $this->newPagesInto = 0;
239 $this->newContentInto = 0;
240 $this->newPagesAfter = 0;
241 }
242 }
243
244 /**
245 * Injects the request object for the current request or subrequest
246 * As this controller goes only through the main() method, it is rather simple for now
247 *
248 * @param ServerRequestInterface $request the current request
249 * @param ResponseInterface $response
250 * @return ResponseInterface the response with the content
251 */
252 public function mainAction(ServerRequestInterface $request, ResponseInterface $response)
253 {
254 $this->main();
255
256 $response->getBody()->write($this->moduleTemplate->renderContent());
257 return $response;
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 // No pages yet, no need to prompt for position, redirect to page creation.
427 $urlParameters = [
428 'edit' => [
429 'pages' => [
430 0 => 'new'
431 ]
432 ],
433 'returnNewPageId' => 1,
434 'returnUrl' => BackendUtility::getModuleUrl('db_new', ['id' => $this->id, 'pagesOnly' => '1'])
435 ];
436 $url = BackendUtility::getModuleUrl('record_edit', $urlParameters);
437 @ob_end_clean();
438 HttpUtility::redirect($url);
439 }
440 }
441
442 /**
443 * Create a regular new element (pages and records)
444 */
445 public function regularNew()
446 {
447 $lang = $this->getLanguageService();
448 // Initialize array for accumulating table rows:
449 $this->tRows = [];
450 // Get TSconfig for current page
451 $pageTS = BackendUtility::getPagesTSconfig($this->id);
452 // Finish initializing new pages options with TSconfig
453 // Each new page option may be hidden by TSconfig
454 // Enabled option for the position of a new page
455 $this->newPagesSelectPosition = !empty($pageTS['mod.']['wizards.']['newRecord.']['pages.']['show.']['pageSelectPosition']);
456 // Pseudo-boolean (0/1) for backward compatibility
457 $displayNewPagesIntoLink = $this->newPagesInto && !empty($pageTS['mod.']['wizards.']['newRecord.']['pages.']['show.']['pageInside']);
458 $displayNewPagesAfterLink = $this->newPagesAfter && !empty($pageTS['mod.']['wizards.']['newRecord.']['pages.']['show.']['pageAfter']);
459 // Slight spacer from header:
460 $this->code .= '';
461 // New Page
462 $table = 'pages';
463 $v = $GLOBALS['TCA'][$table];
464 $pageIcon = $this->moduleTemplate->getIconFactory()->getIconForRecord(
465 $table,
466 [],
467 Icon::SIZE_SMALL
468 )->render();
469 $newPageIcon = $this->moduleTemplate->getIconFactory()->getIcon('actions-page-new', Icon::SIZE_SMALL)->render();
470 $rowContent = '';
471 // New pages INSIDE this pages
472 $newPageLinks = [];
473 if ($displayNewPagesIntoLink
474 && $this->isTableAllowedForThisPage($this->pageinfo, 'pages')
475 && $this->getBackendUserAuthentication()->check('tables_modify', 'pages')
476 && $this->getBackendUserAuthentication()->workspaceCreateNewRecord(($this->pageinfo['_ORIG_uid'] ?: $this->id), 'pages')
477 ) {
478 // Create link to new page inside:
479 $recordIcon = $this->moduleTemplate->getIconFactory()->getIconForRecord($table, [], Icon::SIZE_SMALL)->render();
480 $newPageLinks[] = $this->linkWrap(
481 $recordIcon . htmlspecialchars($lang->sL($v['ctrl']['title'])) . ' (' . htmlspecialchars($lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:db_new.php.inside')) . ')',
482 $table,
483 $this->id
484 );
485 }
486 // New pages AFTER this pages
487 if ($displayNewPagesAfterLink
488 && $this->isTableAllowedForThisPage($this->pidInfo, 'pages')
489 && $this->getBackendUserAuthentication()->check('tables_modify', 'pages')
490 && $this->getBackendUserAuthentication()->workspaceCreateNewRecord($this->pidInfo['uid'], 'pages')
491 ) {
492 $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);
493 }
494 // New pages at selection position
495 if ($this->newPagesSelectPosition && $this->showNewRecLink('pages')) {
496 // Link to page-wizard:
497 $newPageLinks[] = '<a href="' . htmlspecialchars(GeneralUtility::linkThisScript(['pagesOnly' => 1])) . '">' . $pageIcon . htmlspecialchars($lang->getLL('pageSelectPosition')) . '</a>';
498 }
499 // Assemble all new page links
500 $numPageLinks = count($newPageLinks);
501 for ($i = 0; $i < $numPageLinks; $i++) {
502 $rowContent .= '<li>' . $newPageLinks[$i] . '</li>';
503 }
504 if ($this->showNewRecLink('pages')) {
505 $rowContent = '<ul class="list-tree"><li>' . $newPageIcon . '<strong>' .
506 $lang->getLL('createNewPage') . '</strong><ul>' . $rowContent . '</ul></li>';
507 } else {
508 $rowContent = '<ul class="list-tree"><li><ul>' . $rowContent . '</li></ul>';
509 }
510 // Compile table row
511 $startRows = [$rowContent];
512 $iconFile = [];
513 // New tables (but not pages) INSIDE this pages
514 $isAdmin = $this->getBackendUserAuthentication()->isAdmin();
515 $newContentIcon = $this->moduleTemplate->getIconFactory()->getIcon('actions-document-new', Icon::SIZE_SMALL)->render();
516 if ($this->newContentInto) {
517 if (is_array($GLOBALS['TCA'])) {
518 $groupName = '';
519 foreach ($GLOBALS['TCA'] as $table => $v) {
520 $rootLevelConfiguration = isset($v['ctrl']['rootLevel']) ? (int)$v['ctrl']['rootLevel'] : 0;
521 if ($table !== 'pages'
522 && $this->showNewRecLink($table)
523 && $this->isTableAllowedForThisPage($this->pageinfo, $table)
524 && $this->getBackendUserAuthentication()->check('tables_modify', $table)
525 && ($rootLevelConfiguration === -1 || ($this->id xor $rootLevelConfiguration))
526 && $this->getBackendUserAuthentication()->workspaceCreateNewRecord(($this->pageinfo['_ORIG_uid'] ? $this->pageinfo['_ORIG_uid'] : $this->id), $table)
527 ) {
528 $newRecordIcon = $this->moduleTemplate->getIconFactory()->getIconForRecord($table, [], Icon::SIZE_SMALL)->render();
529 $rowContent = '';
530 $thisTitle = '';
531 // Create new link for record:
532 $newLink = $this->linkWrap($newRecordIcon . htmlspecialchars($lang->sL($v['ctrl']['title'])), $table, $this->id);
533 // If the table is 'tt_content', create link to wizard
534 if ($table === 'tt_content') {
535 $groupName = $lang->getLL('createNewContent');
536 $rowContent = $newContentIcon
537 . '<strong>' . $lang->getLL('createNewContent') . '</strong>'
538 . '<ul>';
539 // If mod.newContentElementWizard.override is set, use that extension's wizard instead:
540 $tsConfig = BackendUtility::getModTSconfig($this->id, 'mod');
541 $moduleName = isset($tsConfig['properties']['newContentElementWizard.']['override'])
542 ? $tsConfig['properties']['newContentElementWizard.']['override']
543 : 'new_content_element_wizard';
544 $url = BackendUtility::getModuleUrl($moduleName, ['id' => $this->id, 'returnUrl' => GeneralUtility::getIndpEnv('REQUEST_URI')]);
545 $rowContent .= '<li>' . $newLink . ' ' . BackendUtility::wrapInHelp($table, '') . '</li>'
546 . '<li>'
547 . '<a href="#" data-url="' . htmlspecialchars($url) . '" data-title="' . htmlspecialchars($this->getLanguageService()->getLL('newContentElement')) . '" class="t3js-toggle-new-content-element-wizard">'
548 . $newContentIcon . htmlspecialchars($lang->getLL('clickForWizard'))
549 . '</a>'
550 . '</li>'
551 . '</ul>';
552 } else {
553 // Get the title
554 if ($v['ctrl']['readOnly'] || $v['ctrl']['hideTable'] || $v['ctrl']['is_static']) {
555 continue;
556 }
557 if ($v['ctrl']['adminOnly'] && !$isAdmin) {
558 continue;
559 }
560 $nameParts = explode('_', $table);
561 $thisTitle = '';
562 $_EXTKEY = '';
563 if ($nameParts[0] === 'tx' || $nameParts[0] === 'tt') {
564 // Try to extract extension name
565 if (substr($v['ctrl']['title'], 0, 8) === 'LLL:EXT:') {
566 $_EXTKEY = substr($v['ctrl']['title'], 8);
567 $_EXTKEY = substr($_EXTKEY, 0, strpos($_EXTKEY, '/'));
568 if ($_EXTKEY !== '') {
569 // First try to get localisation of extension title
570 $temp = explode(':', substr($v['ctrl']['title'], 9 + strlen($_EXTKEY)));
571 $langFile = $temp[0];
572 $thisTitle = $lang->sL('LLL:EXT:' . $_EXTKEY . '/' . $langFile . ':extension.title');
573 // If no localisation available, read title from ext_emconf.php
574 $extPath = ExtensionManagementUtility::extPath($_EXTKEY);
575 $extEmConfFile = $extPath . 'ext_emconf.php';
576 if (!$thisTitle && is_file($extEmConfFile)) {
577 $EM_CONF = [];
578 include $extEmConfFile;
579 $thisTitle = $EM_CONF[$_EXTKEY]['title'];
580 }
581 $iconFile[$_EXTKEY] = '<img src="' . PathUtility::getAbsoluteWebPath(ExtensionManagementUtility::getExtensionIcon($extPath, true)) . '" ' . 'width="16" height="16" ' . 'alt="' . $thisTitle . '" />';
582 }
583 }
584 if (empty($thisTitle)) {
585 $_EXTKEY = $nameParts[1];
586 $thisTitle = $nameParts[1];
587 $iconFile[$_EXTKEY] = '';
588 }
589 } else {
590 $_EXTKEY = 'system';
591 $thisTitle = $lang->getLL('system_records');
592 $iconFile['system'] = $this->moduleTemplate->getIconFactory()->getIcon('apps-pagetree-root', Icon::SIZE_SMALL)->render();
593 }
594
595 if ($groupName === '' || $groupName !== $_EXTKEY) {
596 $groupName = empty($v['ctrl']['groupName']) ? $_EXTKEY : $v['ctrl']['groupName'];
597 }
598 $rowContent .= $newLink;
599 }
600 // Compile table row:
601 if ($table === 'tt_content') {
602 $startRows[] = '<li>' . $rowContent . '</li>';
603 } else {
604 $this->tRows[$groupName]['title'] = $thisTitle;
605 $this->tRows[$groupName]['html'][] = $rowContent;
606 $this->tRows[$groupName]['table'][] = $table;
607 }
608 }
609 }
610 }
611 }
612 // User sort
613 if (isset($pageTS['mod.']['wizards.']['newRecord.']['order'])) {
614 $this->newRecordSortList = GeneralUtility::trimExplode(',', $pageTS['mod.']['wizards.']['newRecord.']['order'], true);
615 }
616 uksort($this->tRows, [$this, 'sortNewRecordsByConfig']);
617 // Compile table row:
618 $finalRows = [];
619 $finalRows[] = implode('', $startRows);
620 foreach ($this->tRows as $key => $value) {
621 $row = '<li>' . $iconFile[$key] . ' <strong>' . $value['title'] . '</strong><ul>';
622 foreach ($value['html'] as $recordKey => $record) {
623 $row .= '<li>' . $record . ' ' . BackendUtility::wrapInHelp($value['table'][$recordKey], '') . '</li>';
624 }
625 $row .= '</ul></li>';
626 $finalRows[] = $row;
627 }
628
629 $finalRows[] = '</ul>';
630 // Make table:
631 $this->code .= implode('', $finalRows);
632 }
633
634 /**
635 * User array sort function used by regularNew
636 *
637 * @param string $a First array element for compare
638 * @param string $b First array element for compare
639 * @return int -1 for lower, 0 for equal, 1 for greater
640 */
641 public function sortNewRecordsByConfig($a, $b)
642 {
643 if (!empty($this->newRecordSortList)) {
644 if (in_array($a, $this->newRecordSortList) && in_array($b, $this->newRecordSortList)) {
645 // Both are in the list, return relative to position in array
646 $sub = array_search($a, $this->newRecordSortList) - array_search($b, $this->newRecordSortList);
647 $ret = ($sub < 0 ? -1 : $sub == 0) ? 0 : 1;
648 } elseif (in_array($a, $this->newRecordSortList)) {
649 // First element is in array, put to top
650 $ret = -1;
651 } elseif (in_array($b, $this->newRecordSortList)) {
652 // Second element is in array, put first to bottom
653 $ret = 1;
654 } else {
655 // No element is in array, return alphabetic order
656 $ret = strnatcasecmp($this->tRows[$a]['title'], $this->tRows[$b]['title']);
657 }
658 return $ret;
659 }
660 // Return alphabetic order
661 return strnatcasecmp($this->tRows[$a]['title'], $this->tRows[$b]['title']);
662 }
663
664 /**
665 * Links the string $code to a create-new form for a record in $table created on page $pid
666 *
667 * @param string $linkText Link text
668 * @param string $table Table name (in which to create new record)
669 * @param int $pid PID value for the "&edit['.$table.']['.$pid.']=new" command (positive/negative)
670 * @param bool $addContentTable If $addContentTable is set, then a new tt_content record is created together with pages
671 * @return string The link.
672 */
673 public function linkWrap($linkText, $table, $pid, $addContentTable = false)
674 {
675 $urlParameters = [
676 'edit' => [
677 $table => [
678 $pid => 'new'
679 ]
680 ],
681 'returnUrl' => $this->returnUrl
682 ];
683 if ($table === 'pages' && $addContentTable) {
684 $urlParameters['tt_content']['prev'] = 'new';
685 $urlParameters['returnNewPageId'] = 1;
686 } elseif ($table === 'pages') {
687 $urlParameters['overrideVals']['pages']['doktype'] = (int)$this->pageinfo['doktype'];
688 }
689 $url = BackendUtility::getModuleUrl('record_edit', $urlParameters);
690 return '<a href="' . htmlspecialchars($url) . '">' . $linkText . '</a>';
691 }
692
693 /**
694 * Returns TRUE if the tablename $checkTable is allowed to be created on the page with record $pid_row
695 *
696 * @param array $pid_row Record for parent page.
697 * @param string $checkTable Table name to check
698 * @return bool Returns TRUE if the tablename $checkTable is allowed to be created on the page with record $pid_row
699 */
700 public function isTableAllowedForThisPage($pid_row, $checkTable)
701 {
702 if (!is_array($pid_row)) {
703 return $this->getBackendUserAuthentication()->isAdmin();
704 }
705 // be_users and be_groups may not be created anywhere but in the root.
706 if ($checkTable === 'be_users' || $checkTable === 'be_groups') {
707 return false;
708 }
709 // Checking doktype:
710 $doktype = (int)$pid_row['doktype'];
711 if (!($allowedTableList = $GLOBALS['PAGES_TYPES'][$doktype]['allowedTables'])) {
712 $allowedTableList = $GLOBALS['PAGES_TYPES']['default']['allowedTables'];
713 }
714 // If all tables or the table is listed as an allowed type, return TRUE
715 if (strstr($allowedTableList, '*') || GeneralUtility::inList($allowedTableList, $checkTable)) {
716 return true;
717 }
718
719 return false;
720 }
721
722 /**
723 * Returns TRUE if:
724 * - $allowedNewTables and $deniedNewTables are empty
725 * - the table is not found in $deniedNewTables and $allowedNewTables is not set or the $table tablename is found in
726 * $allowedNewTables
727 *
728 * If $table tablename is found in $allowedNewTables and $deniedNewTables, $deniedNewTables
729 * has priority over $allowedNewTables.
730 *
731 * @param string $table Table name to test if in allowedTables
732 * @param array $allowedNewTables Array of new tables that are allowed.
733 * @param array $deniedNewTables Array of new tables that are not allowed.
734 *
735 * @return bool Returns TRUE if a link for creating new records should be displayed for $table
736 */
737 public function showNewRecLink($table, array $allowedNewTables = [], array $deniedNewTables = [])
738 {
739 if (!$this->getBackendUserAuthentication()->check('tables_modify', $table)) {
740 return false;
741 }
742
743 $allowedNewTables = $allowedNewTables ?: $this->allowedNewTables;
744 $deniedNewTables = $deniedNewTables ?: $this->deniedNewTables;
745 // No deny/allow tables are set:
746 if (empty($allowedNewTables) && empty($deniedNewTables)) {
747 return true;
748 }
749
750 return !in_array($table, $deniedNewTables) && (empty($allowedNewTables) || in_array($table, $allowedNewTables));
751 }
752
753 /**
754 * Checks if sys_language records are present
755 *
756 * @return bool
757 */
758 protected function checkIfLanguagesExist()
759 {
760 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
761 ->getQueryBuilderForTable('sys_language');
762 $queryBuilder->getRestrictions()->removeAll();
763
764 $count = $queryBuilder
765 ->count('uid')
766 ->from('sys_language')
767 ->execute()
768 ->fetchColumn(0);
769 return (bool)$count;
770 }
771
772 /**
773 * Return language service instance
774 *
775 * @return \TYPO3\CMS\Core\Localization\LanguageService
776 */
777 protected function getLanguageService()
778 {
779 return $GLOBALS['LANG'];
780 }
781
782 /**
783 * Returns the global BackendUserAuthentication object.
784 *
785 * @return \TYPO3\CMS\Core\Authentication\BackendUserAuthentication
786 */
787 protected function getBackendUserAuthentication()
788 {
789 return $GLOBALS['BE_USER'];
790 }
791 }