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