Test Failed
Branch master (7b1793)
by Tymoteusz
36:50 queued 18:38
created

NewRecordController::mainAction()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 3
nc 1
nop 2
dl 0
loc 6
rs 9.4285
c 0
b 0
f 0
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\Imaging\Icon;
28
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
29
use TYPO3\CMS\Core\Utility\GeneralUtility;
30
use TYPO3\CMS\Core\Utility\HttpUtility;
31
use TYPO3\CMS\Core\Utility\PathUtility;
32
use TYPO3\CMS\Frontend\Page\PageRepository;
33
34
/**
35
 * Script class for 'db_new'
36
 */
37
class NewRecordController
38
{
39
    /**
40
     * @var array
41
     */
42
    public $pageinfo;
43
44
    /**
45
     * @var array
46
     */
47
    public $pidInfo;
48
49
    /**
50
     * @var array
51
     */
52
    protected $newRecordSortList;
53
54
    /**
55
     * @var int
56
     */
57
    public $newPagesInto;
58
59
    /**
60
     * @var int
61
     */
62
    public $newContentInto;
63
64
    /**
65
     * @var int
66
     */
67
    public $newPagesAfter;
68
69
    /**
70
     * Determines, whether "Select Position" for new page should be shown
71
     *
72
     * @var bool
73
     */
74
    protected $newPagesSelectPosition = true;
75
76
    /**
77
     * @var array
78
     */
79
    public $web_list_modTSconfig;
80
81
    /**
82
     * @var array
83
     */
84
    public $allowedNewTables;
85
86
    /**
87
     * @var array
88
     */
89
    public $deniedNewTables;
90
91
    /**
92
     * @var array
93
     */
94
    public $web_list_modTSconfig_pid;
95
96
    /**
97
     * @var array
98
     */
99
    public $allowedNewTables_pid;
100
101
    /**
102
     * @var array
103
     */
104
    public $deniedNewTables_pid;
105
106
    /**
107
     * @var string
108
     */
109
    public $code;
110
111
    /**
112
     * @var string
113
     */
114
    public $R_URI;
115
116
    /**
117
     * @var int
118
     */
119
    public $id;
120
121
    /**
122
     * @var string
123
     */
124
    public $returnUrl;
125
126
    /**
127
     * pagesOnly flag.
128
     *
129
     * @var int
130
     */
131
    public $pagesOnly;
132
133
    /**
134
     * @var string
135
     */
136
    public $perms_clause;
137
138
    /**
139
     * Accumulated HTML output
140
     *
141
     * @var string
142
     */
143
    public $content;
144
145
    /**
146
     * @var array
147
     */
148
    public $tRows;
149
150
    /**
151
     * ModuleTemplate object
152
     *
153
     * @var ModuleTemplate
154
     */
155
    protected $moduleTemplate;
156
157
    /**
158
     * Constructor
159
     */
160 View Code Duplication
    public function __construct()
161
    {
162
        $this->moduleTemplate = GeneralUtility::makeInstance(ModuleTemplate::class);
163
        $GLOBALS['SOBE'] = $this;
164
        $this->getLanguageService()->includeLLFile('EXT:lang/Resources/Private/Language/locallang_misc.xlf');
165
        $this->init();
166
    }
167
168
    /**
169
     * Constructor function for the class
170
     */
171
    protected function init()
172
    {
173
        $beUser = $this->getBackendUserAuthentication();
174
        // Page-selection permission clause (reading)
175
        $this->perms_clause = $beUser->getPagePermsClause(1);
176
        // This will hide records from display - it has nothing to do with user rights!!
177
        if ($pidList = $beUser->getTSConfigVal('options.hideRecords.pages')) {
178
            if (!empty($pidList)) {
179
                $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
180
                    ->getQueryBuilderForTable('pages');
181
                $this->perms_clause .= ' AND ' . $queryBuilder->expr()->notIn(
182
                    'pages.uid',
183
                    GeneralUtility::intExplode(',', $pidList)
184
                );
185
            }
186
        }
187
        // Setting GPvars:
188
        // The page id to operate from
189
        $this->id = (int)GeneralUtility::_GP('id');
190
        $this->returnUrl = GeneralUtility::sanitizeLocalUrl(GeneralUtility::_GP('returnUrl'));
191
        $this->pagesOnly = GeneralUtility::_GP('pagesOnly');
0 ignored issues
show
Documentation Bug introduced by
It seems like TYPO3\CMS\Core\Utility\G...ility::_GP('pagesOnly') can also be of type string. However, the property $pagesOnly is declared as type integer. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
192
        // Setting up the context sensitive menu:
193
        $this->moduleTemplate->getPageRenderer()->loadRequireJsModule('TYPO3/CMS/Backend/ContextMenu');
194
        $this->moduleTemplate->getPageRenderer()->loadRequireJsModule('TYPO3/CMS/Backend/Tooltip');
195
        $this->moduleTemplate->getPageRenderer()->loadRequireJsModule(
196
            'TYPO3/CMS/Backend/Wizard/NewContentElement',
197
            'function(NewContentElement) {
198
                require([\'jquery\'], function($) {
199
                    $(function() {
200
                        $(\'.t3js-toggle-new-content-element-wizard\').click(function() {
201
                            var $me = $(this);
202
                            NewContentElement.wizard($me.data(\'url\'), $me.data(\'title\'));
203
                        });
204
                    });
205
                });
206
            }'
207
        );
208
        // Creating content
209
        $this->content = '';
210
        $this->content .= '<h1>'
211
            . $this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:db_new.php.pagetitle')
212
            . '</h1>';
213
        // Id a positive id is supplied, ask for the page record with permission information contained:
214
        if ($this->id > 0) {
215
            $this->pageinfo = BackendUtility::readPageAccess($this->id, $this->perms_clause);
216
        }
217
        // If a page-record was returned, the user had read-access to the page.
218
        if ($this->pageinfo['uid']) {
219
            // Get record of parent page
220
            $this->pidInfo = BackendUtility::getRecord('pages', $this->pageinfo['pid']);
221
            // Checking the permissions for the user with regard to the parent page: Can he create new pages, new
222
            // content record, new page after?
223
            if ($beUser->doesUserHaveAccess($this->pageinfo, 8)) {
224
                $this->newPagesInto = 1;
225
            }
226
            if ($beUser->doesUserHaveAccess($this->pageinfo, 16)) {
227
                $this->newContentInto = 1;
228
            }
229
            if (($beUser->isAdmin() || is_array($this->pidInfo)) && $beUser->doesUserHaveAccess($this->pidInfo, 8)) {
230
                $this->newPagesAfter = 1;
231
            }
232
        } elseif ($beUser->isAdmin()) {
233
            // Admins can do it all
234
            $this->newPagesInto = 1;
235
            $this->newContentInto = 1;
236
            $this->newPagesAfter = 0;
237
        } else {
238
            // People with no permission can do nothing
239
            $this->newPagesInto = 0;
240
            $this->newContentInto = 0;
241
            $this->newPagesAfter = 0;
242
        }
243
    }
244
245
    /**
246
     * Injects the request object for the current request or subrequest
247
     * As this controller goes only through the main() method, it is rather simple for now
248
     *
249
     * @param ServerRequestInterface $request the current request
250
     * @param ResponseInterface $response
251
     * @return ResponseInterface the response with the content
252
     */
253
    public function mainAction(ServerRequestInterface $request, ResponseInterface $response)
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

253
    public function mainAction(/** @scrutinizer ignore-unused */ ServerRequestInterface $request, ResponseInterface $response)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
254
    {
255
        $this->main();
256
257
        $response->getBody()->write($this->moduleTemplate->renderContent());
258
        return $response;
259
    }
260
261
    /**
262
     * Main processing, creating the list of new record tables to select from
263
     */
264
    public function main()
265
    {
266
        // If there was a page - or if the user is admin (admins has access to the root) we proceed:
267
        if (!empty($this->pageinfo['uid']) || $this->getBackendUserAuthentication()->isAdmin()) {
268
            if (empty($this->pageinfo)) {
269
                // Explicitly pass an empty array to the docHeader
270
                $this->moduleTemplate->getDocHeaderComponent()->setMetaInformation([]);
271
            } else {
272
                $this->moduleTemplate->getDocHeaderComponent()->setMetaInformation($this->pageinfo);
273
            }
274
            // Acquiring TSconfig for this module/current page:
275
            $this->web_list_modTSconfig = BackendUtility::getModTSconfig($this->pageinfo['uid'], 'mod.web_list');
276
            $this->allowedNewTables = GeneralUtility::trimExplode(
277
                ',',
278
                $this->web_list_modTSconfig['properties']['allowedNewTables'],
279
                true
280
            );
281
            $this->deniedNewTables = GeneralUtility::trimExplode(
282
                ',',
283
                $this->web_list_modTSconfig['properties']['deniedNewTables'],
284
                true
285
            );
286
            // Acquiring TSconfig for this module/parent page:
287
            $this->web_list_modTSconfig_pid = BackendUtility::getModTSconfig($this->pageinfo['pid'], 'mod.web_list');
288
            $this->allowedNewTables_pid = GeneralUtility::trimExplode(
289
                ',',
290
                $this->web_list_modTSconfig_pid['properties']['allowedNewTables'],
291
                true
292
            );
293
            $this->deniedNewTables_pid = GeneralUtility::trimExplode(
294
                ',',
295
                $this->web_list_modTSconfig_pid['properties']['deniedNewTables'],
296
                true
297
            );
298
            // More init:
299
            if (!$this->showNewRecLink('pages')) {
300
                $this->newPagesInto = 0;
301
            }
302
            if (!$this->showNewRecLink('pages', $this->allowedNewTables_pid, $this->deniedNewTables_pid)) {
303
                $this->newPagesAfter = 0;
304
            }
305
            // Set header-HTML and return_url
306
            if (is_array($this->pageinfo) && $this->pageinfo['uid']) {
307
                $title = strip_tags($this->pageinfo[$GLOBALS['TCA']['pages']['ctrl']['label']]);
308
            } else {
309
                $title = $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'];
310
            }
311
            $this->moduleTemplate->setTitle($title);
312
            // GENERATE the HTML-output depending on mode (pagesOnly is the page wizard)
313
            // Regular new element:
314
            if (!$this->pagesOnly) {
315
                $this->regularNew();
316
            } elseif ($this->showNewRecLink('pages')) {
317
                // Pages only wizard
318
                $this->pagesOnly();
319
            }
320
            // Add all the content to an output section
321
            $this->content .= '<div>' . $this->code . '</div>';
322
            // Setting up the buttons and markers for docheader
323
            $this->getButtons();
324
            // Build the <body> for the module
325
            $this->moduleTemplate->setContent($this->content);
326
        }
327
    }
328
329
    /**
330
     * Create the panel of buttons for submitting the form or otherwise perform operations.
331
     */
332
    protected function getButtons()
333
    {
334
        $lang = $this->getLanguageService();
335
        $buttonBar = $this->moduleTemplate->getDocHeaderComponent()->getButtonBar();
336
        // Regular new element:
337
        if (!$this->pagesOnly) {
338
            // New page
339
            if ($this->showNewRecLink('pages')) {
340
                $newPageButton = $buttonBar->makeLinkButton()
341
                    ->setHref(GeneralUtility::linkThisScript(['pagesOnly' => '1']))
342
                    ->setTitle($lang->sL('LLL:EXT:backend/Resources/Private/Language/locallang_layout.xlf:newPage'))
343
                    ->setIcon($this->moduleTemplate->getIconFactory()->getIcon('actions-page-new', Icon::SIZE_SMALL));
344
                $buttonBar->addButton($newPageButton, ButtonBar::BUTTON_POSITION_LEFT, 20);
345
            }
346
            // CSH
347
            $cshButton = $buttonBar->makeHelpButton()->setModuleName('xMOD_csh_corebe')->setFieldName('new_regular');
348
            $buttonBar->addButton($cshButton);
349
        } elseif ($this->showNewRecLink('pages')) {
350
            // Pages only wizard
351
            // CSH
352
            $buttons['csh'] = BackendUtility::cshItem('xMOD_csh_corebe', 'new_pages');
0 ignored issues
show
Comprehensibility Best Practice introduced by
$buttons was never initialized. Although not strictly required by PHP, it is generally a good practice to add $buttons = array(); before regardless.
Loading history...
353
            $cshButton = $buttonBar->makeHelpButton()->setModuleName('xMOD_csh_corebe')->setFieldName('new_pages');
354
            $buttonBar->addButton($cshButton);
355
        }
356
        // Back
357
        if ($this->returnUrl) {
358
            $returnButton = $buttonBar->makeLinkButton()
359
                ->setHref($this->returnUrl)
360
                ->setTitle($lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.goBack'))
361
                ->setIcon($this->moduleTemplate->getIconFactory()->getIcon('actions-view-go-back', Icon::SIZE_SMALL));
362
            $buttonBar->addButton($returnButton, ButtonBar::BUTTON_POSITION_LEFT, 10);
363
        }
364
365
        if (is_array($this->pageinfo) && $this->pageinfo['uid']) {
366
            // View
367
            $pagesTSconfig = BackendUtility::getPagesTSconfig($this->pageinfo['uid']);
368 View Code Duplication
            if (isset($pagesTSconfig['TCEMAIN.']['preview.']['disableButtonForDokType'])) {
369
                $excludeDokTypes = GeneralUtility::intExplode(
370
                    ',',
371
                    $pagesTSconfig['TCEMAIN.']['preview.']['disableButtonForDokType'],
372
                    true
373
                );
374
            } else {
375
                // exclude sysfolders and recycler by default
376
                $excludeDokTypes = [
377
                    PageRepository::DOKTYPE_RECYCLER,
378
                    PageRepository::DOKTYPE_SYSFOLDER,
379
                    PageRepository::DOKTYPE_SPACER
380
                ];
381
            }
382
            if (!in_array((int)$this->pageinfo['doktype'], $excludeDokTypes, true)) {
383
                $viewButton = $buttonBar->makeLinkButton()
384
                    ->setHref('#')
385
                    ->setOnClick(BackendUtility::viewOnClick(
386
                        $this->pageinfo['uid'],
387
                        '',
388
                        BackendUtility::BEgetRootLine($this->pageinfo['uid'])
389
                    ))
390
                    ->setTitle($lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.showPage'))
391
                    ->setIcon($this->moduleTemplate->getIconFactory()->getIcon(
392
                        'actions-view-page',
393
                        Icon::SIZE_SMALL
394
                    ));
395
                $buttonBar->addButton($viewButton, ButtonBar::BUTTON_POSITION_LEFT, 30);
396
            }
397
        }
398
    }
399
400
    /**
401
     * Creates the position map for pages wizard
402
     */
403
    public function pagesOnly()
404
    {
405
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
406
            ->getQueryBuilderForTable('sys_language');
407
        $queryBuilder->getRestrictions()
408
            ->removeAll()
409
            ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
410
        $numberOfPages = $queryBuilder
411
            ->count('*')
412
            ->from('pages')
413
            ->execute()
414
            ->fetchColumn(0);
415
416
        if ($numberOfPages > 0) {
417
            $this->code .= '<h3>' . htmlspecialchars($this->getLanguageService()->getLL('selectPosition')) . ':</h3>';
418
            $positionMap = GeneralUtility::makeInstance(PagePositionMap::class, NewRecordPageTreeView::class);
0 ignored issues
show
Bug introduced by
TYPO3\CMS\Backend\Tree\V...cordPageTreeView::class of type string is incompatible with the type array<integer,mixed> expected by parameter $constructorArguments of TYPO3\CMS\Core\Utility\G...Utility::makeInstance(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

418
            $positionMap = GeneralUtility::makeInstance(PagePositionMap::class, /** @scrutinizer ignore-type */ NewRecordPageTreeView::class);
Loading history...
419
            /** @var $positionMap \TYPO3\CMS\Backend\Tree\View\PagePositionMap */
420
            $this->code .= $positionMap->positionTree(
421
                $this->id,
422
                $this->pageinfo,
423
                $this->perms_clause,
424
                $this->returnUrl
425
            );
426
        } else {
427
            /** @var \TYPO3\CMS\Backend\Routing\UriBuilder $uriBuilder */
428
            $uriBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\UriBuilder::class);
429
            // No pages yet, no need to prompt for position, redirect to page creation.
430
            $urlParameters = [
431
                'edit' => [
432
                    'pages' => [
433
                        0 => 'new'
434
                    ]
435
                ],
436
                'returnNewPageId' => 1,
437
                'returnUrl' => (string)$uriBuilder->buildUriFromRoute('db_new', ['id' => $this->id, 'pagesOnly' => '1'])
438
            ];
439
            $url = (string)$uriBuilder->buildUriFromRoute('record_edit', $urlParameters);
440
            @ob_end_clean();
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for ob_end_clean(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

440
            /** @scrutinizer ignore-unhandled */ @ob_end_clean();

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
441
            HttpUtility::redirect($url);
442
        }
443
    }
444
445
    /**
446
     * Create a regular new element (pages and records)
447
     */
448
    public function regularNew()
449
    {
450
        $lang = $this->getLanguageService();
451
        // Initialize array for accumulating table rows:
452
        $this->tRows = [];
453
        // Get TSconfig for current page
454
        $pageTS = BackendUtility::getPagesTSconfig($this->id);
455
        // Finish initializing new pages options with TSconfig
456
        // Each new page option may be hidden by TSconfig
457
        // Enabled option for the position of a new page
458
        $this->newPagesSelectPosition = !empty($pageTS['mod.']['wizards.']['newRecord.']['pages.']['show.']['pageSelectPosition']);
459
        // Pseudo-boolean (0/1) for backward compatibility
460
        $displayNewPagesIntoLink = $this->newPagesInto && !empty($pageTS['mod.']['wizards.']['newRecord.']['pages.']['show.']['pageInside']);
461
        $displayNewPagesAfterLink = $this->newPagesAfter && !empty($pageTS['mod.']['wizards.']['newRecord.']['pages.']['show.']['pageAfter']);
462
        // Slight spacer from header:
463
        $this->code .= '';
464
        // New Page
465
        $table = 'pages';
466
        $v = $GLOBALS['TCA'][$table];
467
        $pageIcon = $this->moduleTemplate->getIconFactory()->getIconForRecord(
468
            $table,
469
            [],
470
            Icon::SIZE_SMALL
471
        )->render();
472
        $newPageIcon = $this->moduleTemplate->getIconFactory()->getIcon('actions-page-new', Icon::SIZE_SMALL)->render();
473
        $rowContent = '';
474
        // New pages INSIDE this pages
475
        $newPageLinks = [];
476
        if ($displayNewPagesIntoLink
477
            && $this->isTableAllowedForThisPage($this->pageinfo, 'pages')
478
            && $this->getBackendUserAuthentication()->check('tables_modify', 'pages')
479
            && $this->getBackendUserAuthentication()->workspaceCreateNewRecord(($this->pageinfo['_ORIG_uid'] ?: $this->id), 'pages')
480
        ) {
481
            // Create link to new page inside:
482
            $recordIcon = $this->moduleTemplate->getIconFactory()->getIconForRecord($table, [], Icon::SIZE_SMALL)->render();
483
            $newPageLinks[] = $this->linkWrap(
484
                $recordIcon . htmlspecialchars($lang->sL($v['ctrl']['title'])) . ' (' . htmlspecialchars($lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:db_new.php.inside')) . ')',
485
                $table,
486
                $this->id
487
            );
488
        }
489
        // New pages AFTER this pages
490
        if ($displayNewPagesAfterLink
491
            && $this->isTableAllowedForThisPage($this->pidInfo, 'pages')
492
            && $this->getBackendUserAuthentication()->check('tables_modify', 'pages')
493
            && $this->getBackendUserAuthentication()->workspaceCreateNewRecord($this->pidInfo['uid'], 'pages')
494
        ) {
495
            $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);
496
        }
497
        // New pages at selection position
498
        if ($this->newPagesSelectPosition && $this->showNewRecLink('pages')) {
499
            // Link to page-wizard:
500
            $newPageLinks[] = '<a href="' . htmlspecialchars(GeneralUtility::linkThisScript(['pagesOnly' => 1])) . '">' . $pageIcon . htmlspecialchars($lang->getLL('pageSelectPosition')) . '</a>';
501
        }
502
        // Assemble all new page links
503
        $numPageLinks = count($newPageLinks);
504
        for ($i = 0; $i < $numPageLinks; $i++) {
505
            $rowContent .= '<li>' . $newPageLinks[$i] . '</li>';
506
        }
507
        if ($this->showNewRecLink('pages')) {
508
            $rowContent = '<ul class="list-tree"><li>' . $newPageIcon . '<strong>' .
509
                $lang->getLL('createNewPage') . '</strong><ul>' . $rowContent . '</ul></li>';
510
        } else {
511
            $rowContent = '<ul class="list-tree"><li><ul>' . $rowContent . '</li></ul>';
512
        }
513
        /** @var \TYPO3\CMS\Backend\Routing\UriBuilder $uriBuilder */
514
        $uriBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\UriBuilder::class);
515
        // Compile table row
516
        $startRows = [$rowContent];
517
        $iconFile = [];
518
        // New tables (but not pages) INSIDE this pages
519
        $isAdmin = $this->getBackendUserAuthentication()->isAdmin();
520
        $newContentIcon = $this->moduleTemplate->getIconFactory()->getIcon('actions-document-new', Icon::SIZE_SMALL)->render();
521
        if ($this->newContentInto) {
522
            if (is_array($GLOBALS['TCA'])) {
523
                $groupName = '';
524
                foreach ($GLOBALS['TCA'] as $table => $v) {
525
                    $rootLevelConfiguration = isset($v['ctrl']['rootLevel']) ? (int)$v['ctrl']['rootLevel'] : 0;
526
                    if ($table !== 'pages'
527
                        && $this->showNewRecLink($table)
528
                        && $this->isTableAllowedForThisPage($this->pageinfo, $table)
529
                        && $this->getBackendUserAuthentication()->check('tables_modify', $table)
530
                        && ($rootLevelConfiguration === -1 || ($this->id xor $rootLevelConfiguration))
531
                        && $this->getBackendUserAuthentication()->workspaceCreateNewRecord(($this->pageinfo['_ORIG_uid'] ? $this->pageinfo['_ORIG_uid'] : $this->id), $table)
532
                    ) {
533
                        $newRecordIcon = $this->moduleTemplate->getIconFactory()->getIconForRecord($table, [], Icon::SIZE_SMALL)->render();
534
                        $rowContent = '';
535
                        $thisTitle = '';
536
                        // Create new link for record:
537
                        $newLink = $this->linkWrap($newRecordIcon . htmlspecialchars($lang->sL($v['ctrl']['title'])), $table, $this->id);
538
                        // If the table is 'tt_content', create link to wizard
539
                        if ($table === 'tt_content') {
540
                            $groupName = $lang->getLL('createNewContent');
541
                            $rowContent = $newContentIcon
542
                                . '<strong>' . $lang->getLL('createNewContent') . '</strong>'
543
                                . '<ul>';
544
                            // If mod.newContentElementWizard.override is set, use that extension's wizard instead:
545
                            $tsConfig = BackendUtility::getModTSconfig($this->id, 'mod');
546
                            $moduleName = isset($tsConfig['properties']['newContentElementWizard.']['override'])
547
                                ? $tsConfig['properties']['newContentElementWizard.']['override']
548
                                : 'new_content_element_wizard';
549
                            $url = (string)$uriBuilder->buildUriFromRoute($moduleName, ['id' => $this->id, 'returnUrl' => GeneralUtility::getIndpEnv('REQUEST_URI')]);
550
                            $rowContent .= '<li>' . $newLink . ' ' . BackendUtility::wrapInHelp($table, '') . '</li>'
551
                                . '<li>'
552
                                . '<a href="#" data-url="' . htmlspecialchars($url) . '" data-title="' . htmlspecialchars($this->getLanguageService()->getLL('newContentElement')) . '" class="t3js-toggle-new-content-element-wizard">'
553
                                . $newContentIcon . htmlspecialchars($lang->getLL('clickForWizard'))
554
                                . '</a>'
555
                                . '</li>'
556
                                . '</ul>';
557
                        } else {
558
                            // Get the title
559
                            if ($v['ctrl']['readOnly'] || $v['ctrl']['hideTable'] || $v['ctrl']['is_static']) {
560
                                continue;
561
                            }
562
                            if ($v['ctrl']['adminOnly'] && !$isAdmin) {
563
                                continue;
564
                            }
565
                            $nameParts = explode('_', $table);
566
                            $thisTitle = '';
567
                            $_EXTKEY = '';
568
                            if ($nameParts[0] === 'tx' || $nameParts[0] === 'tt') {
569
                                // Try to extract extension name
570
                                if (substr($v['ctrl']['title'], 0, 8) === 'LLL:EXT:') {
571
                                    $_EXTKEY = substr($v['ctrl']['title'], 8);
572
                                    $_EXTKEY = substr($_EXTKEY, 0, strpos($_EXTKEY, '/'));
573
                                    if ($_EXTKEY !== '') {
574
                                        // First try to get localisation of extension title
575
                                        $temp = explode(':', substr($v['ctrl']['title'], 9 + strlen($_EXTKEY)));
576
                                        $langFile = $temp[0];
577
                                        $thisTitle = $lang->sL('LLL:EXT:' . $_EXTKEY . '/' . $langFile . ':extension.title');
578
                                        // If no localisation available, read title from ext_emconf.php
579
                                        $extPath = ExtensionManagementUtility::extPath($_EXTKEY);
580
                                        $extEmConfFile = $extPath . 'ext_emconf.php';
581
                                        if (!$thisTitle && is_file($extEmConfFile)) {
582
                                            $EM_CONF = [];
583
                                            include $extEmConfFile;
584
                                            $thisTitle = $EM_CONF[$_EXTKEY]['title'];
585
                                        }
586
                                        $iconFile[$_EXTKEY] = '<img src="' . PathUtility::getAbsoluteWebPath(ExtensionManagementUtility::getExtensionIcon($extPath, true)) . '" ' . 'width="16" height="16" ' . 'alt="' . $thisTitle . '" />';
587
                                    }
588
                                }
589
                                if (empty($thisTitle)) {
590
                                    $_EXTKEY = $nameParts[1];
591
                                    $thisTitle = $nameParts[1];
592
                                    $iconFile[$_EXTKEY] = '';
593
                                }
594
                            } else {
595
                                $_EXTKEY = 'system';
596
                                $thisTitle = $lang->getLL('system_records');
597
                                $iconFile['system'] = $this->moduleTemplate->getIconFactory()->getIcon('apps-pagetree-root', Icon::SIZE_SMALL)->render();
598
                            }
599
600
                            if ($groupName === '' || $groupName !== $_EXTKEY) {
601
                                $groupName = empty($v['ctrl']['groupName']) ? $_EXTKEY : $v['ctrl']['groupName'];
602
                            }
603
                            $rowContent .= $newLink;
604
                        }
605
                        // Compile table row:
606
                        if ($table === 'tt_content') {
607
                            $startRows[] = '<li>' . $rowContent . '</li>';
608
                        } else {
609
                            $this->tRows[$groupName]['title'] = $thisTitle;
610
                            $this->tRows[$groupName]['html'][] = $rowContent;
611
                            $this->tRows[$groupName]['table'][] = $table;
612
                        }
613
                    }
614
                }
615
            }
616
        }
617
        // User sort
618
        if (isset($pageTS['mod.']['wizards.']['newRecord.']['order'])) {
619
            $this->newRecordSortList = GeneralUtility::trimExplode(',', $pageTS['mod.']['wizards.']['newRecord.']['order'], true);
620
        }
621
        uksort($this->tRows, [$this, 'sortNewRecordsByConfig']);
622
        // Compile table row:
623
        $finalRows = [];
624
        $finalRows[] = implode('', $startRows);
625
        foreach ($this->tRows as $key => $value) {
626
            $row = '<li>' . $iconFile[$key] . ' <strong>' . $value['title'] . '</strong><ul>';
627
            foreach ($value['html'] as $recordKey => $record) {
628
                $row .= '<li>' . $record . ' ' . BackendUtility::wrapInHelp($value['table'][$recordKey], '') . '</li>';
629
            }
630
            $row .= '</ul></li>';
631
            $finalRows[] = $row;
632
        }
633
634
        $finalRows[] = '</ul>';
635
        // Make table:
636
        $this->code .= implode('', $finalRows);
637
    }
638
639
    /**
640
     * User array sort function used by regularNew
641
     *
642
     * @param string $a First array element for compare
643
     * @param string $b First array element for compare
644
     * @return int -1 for lower, 0 for equal, 1 for greater
645
     */
646
    public function sortNewRecordsByConfig($a, $b)
647
    {
648
        if (!empty($this->newRecordSortList)) {
649
            if (in_array($a, $this->newRecordSortList) && in_array($b, $this->newRecordSortList)) {
650
                // Both are in the list, return relative to position in array
651
                $sub = array_search($a, $this->newRecordSortList) - array_search($b, $this->newRecordSortList);
652
                $ret = ($sub < 0 ? -1 : $sub == 0) ? 0 : 1;
653
            } elseif (in_array($a, $this->newRecordSortList)) {
654
                // First element is in array, put to top
655
                $ret = -1;
656
            } elseif (in_array($b, $this->newRecordSortList)) {
657
                // Second element is in array, put first to bottom
658
                $ret = 1;
659
            } else {
660
                // No element is in array, return alphabetic order
661
                $ret = strnatcasecmp($this->tRows[$a]['title'], $this->tRows[$b]['title']);
662
            }
663
            return $ret;
664
        }
665
        // Return alphabetic order
666
        return strnatcasecmp($this->tRows[$a]['title'], $this->tRows[$b]['title']);
667
    }
668
669
    /**
670
     * Links the string $code to a create-new form for a record in $table created on page $pid
671
     *
672
     * @param string $linkText Link text
673
     * @param string $table Table name (in which to create new record)
674
     * @param int $pid PID value for the "&edit['.$table.']['.$pid.']=new" command (positive/negative)
675
     * @param bool $addContentTable If $addContentTable is set, then a new tt_content record is created together with pages
676
     * @return string The link.
677
     */
678
    public function linkWrap($linkText, $table, $pid, $addContentTable = false)
679
    {
680
        $urlParameters = [
681
            'edit' => [
682
                $table => [
683
                    $pid => 'new'
684
                ]
685
            ],
686
            'returnUrl' => $this->returnUrl
687
        ];
688
        if ($table === 'pages' && $addContentTable) {
689
            $urlParameters['tt_content']['prev'] = 'new';
690
            $urlParameters['returnNewPageId'] = 1;
691
        } elseif ($table === 'pages') {
692
            $urlParameters['overrideVals']['pages']['doktype'] = (int)$this->pageinfo['doktype'];
693
        }
694
        $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
695
        $url = (string)$uriBuilder->buildUriFromRoute('record_edit', $urlParameters);
696
        return '<a href="' . htmlspecialchars($url) . '">' . $linkText . '</a>';
697
    }
698
699
    /**
700
     * Returns TRUE if the tablename $checkTable is allowed to be created on the page with record $pid_row
701
     *
702
     * @param array $pid_row Record for parent page.
703
     * @param string $checkTable Table name to check
704
     * @return bool Returns TRUE if the tablename $checkTable is allowed to be created on the page with record $pid_row
705
     */
706
    public function isTableAllowedForThisPage($pid_row, $checkTable)
707
    {
708
        if (!is_array($pid_row)) {
709
            return $this->getBackendUserAuthentication()->isAdmin();
710
        }
711
        // be_users and be_groups may not be created anywhere but in the root.
712
        if ($checkTable === 'be_users' || $checkTable === 'be_groups') {
713
            return false;
714
        }
715
        // Checking doktype:
716
        $doktype = (int)$pid_row['doktype'];
717
        if (!($allowedTableList = $GLOBALS['PAGES_TYPES'][$doktype]['allowedTables'])) {
718
            $allowedTableList = $GLOBALS['PAGES_TYPES']['default']['allowedTables'];
719
        }
720
        // If all tables or the table is listed as an allowed type, return TRUE
721
        if (strstr($allowedTableList, '*') || GeneralUtility::inList($allowedTableList, $checkTable)) {
722
            return true;
723
        }
724
725
        return false;
726
    }
727
728
    /**
729
     * Returns TRUE if:
730
     * - $allowedNewTables and $deniedNewTables are empty
731
     * - the table is not found in $deniedNewTables and $allowedNewTables is not set or the $table tablename is found in
732
     *   $allowedNewTables
733
     *
734
     * If $table tablename is found in $allowedNewTables and $deniedNewTables, $deniedNewTables
735
     * has priority over $allowedNewTables.
736
     *
737
     * @param string $table Table name to test if in allowedTables
738
     * @param array $allowedNewTables Array of new tables that are allowed.
739
     * @param array $deniedNewTables Array of new tables that are not allowed.
740
     *
741
     * @return bool Returns TRUE if a link for creating new records should be displayed for $table
742
     */
743
    public function showNewRecLink($table, array $allowedNewTables = [], array $deniedNewTables = [])
744
    {
745
        if (!$this->getBackendUserAuthentication()->check('tables_modify', $table)) {
746
            return false;
747
        }
748
749
        $allowedNewTables = $allowedNewTables ?: $this->allowedNewTables;
750
        $deniedNewTables = $deniedNewTables ?: $this->deniedNewTables;
751
        // No deny/allow tables are set:
752
        if (empty($allowedNewTables) && empty($deniedNewTables)) {
753
            return true;
754
        }
755
756
        return !in_array($table, $deniedNewTables) && (empty($allowedNewTables) || in_array($table, $allowedNewTables));
757
    }
758
759
    /**
760
     * Checks if sys_language records are present
761
     *
762
     * @return bool
763
     */
764 View Code Duplication
    protected function checkIfLanguagesExist()
765
    {
766
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
767
            ->getQueryBuilderForTable('sys_language');
768
        $queryBuilder->getRestrictions()->removeAll();
769
770
        $count = $queryBuilder
771
            ->count('uid')
772
            ->from('sys_language')
773
            ->execute()
774
            ->fetchColumn(0);
775
        return (bool)$count;
776
    }
777
778
    /**
779
     * Return language service instance
780
     *
781
     * @return \TYPO3\CMS\Core\Localization\LanguageService
782
     */
783
    protected function getLanguageService()
784
    {
785
        return $GLOBALS['LANG'];
786
    }
787
788
    /**
789
     * Returns the global BackendUserAuthentication object.
790
     *
791
     * @return \TYPO3\CMS\Core\Authentication\BackendUserAuthentication
792
     */
793
    protected function getBackendUserAuthentication()
794
    {
795
        return $GLOBALS['BE_USER'];
796
    }
797
}
798