[CLEANUP] Use Permission constants consistently
[Packages/TYPO3.CMS.git] / typo3 / sysext / recordlist / Classes / RecordList.php
1 <?php
2 namespace TYPO3\CMS\Recordlist;
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\Clipboard\Clipboard;
20 use TYPO3\CMS\Backend\Template\Components\ButtonBar;
21 use TYPO3\CMS\Backend\Template\DocumentTemplate;
22 use TYPO3\CMS\Backend\Template\ModuleTemplate;
23 use TYPO3\CMS\Backend\Utility\BackendUtility;
24 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
25 use TYPO3\CMS\Core\Database\ConnectionPool;
26 use TYPO3\CMS\Core\Database\Query\Restriction\HiddenRestriction;
27 use TYPO3\CMS\Core\DataHandling\DataHandler;
28 use TYPO3\CMS\Core\Imaging\Icon;
29 use TYPO3\CMS\Core\Imaging\IconFactory;
30 use TYPO3\CMS\Core\Localization\LanguageService;
31 use TYPO3\CMS\Core\Messaging\FlashMessage;
32 use TYPO3\CMS\Core\Messaging\FlashMessageService;
33 use TYPO3\CMS\Core\Page\PageRenderer;
34 use TYPO3\CMS\Core\Type\Bitmask\Permission;
35 use TYPO3\CMS\Core\TypoScript\TypoScriptService;
36 use TYPO3\CMS\Core\Utility\GeneralUtility;
37 use TYPO3\CMS\Core\Versioning\VersionState;
38
39 /**
40 * Script Class for the Web > List module; rendering the listing of records on a page
41 */
42 class RecordList
43 {
44 /**
45 * Page Id for which to make the listing
46 *
47 * @var int
48 */
49 public $id;
50
51 /**
52 * Pointer - for browsing list of records.
53 *
54 * @var int
55 */
56 public $pointer;
57
58 /**
59 * Thumbnails or not
60 *
61 * @var string
62 */
63 public $imagemode;
64
65 /**
66 * Which table to make extended listing for
67 *
68 * @var string
69 */
70 public $table;
71
72 /**
73 * Search-fields
74 *
75 * @var string
76 */
77 public $search_field;
78
79 /**
80 * Search-levels
81 *
82 * @var int
83 */
84 public $search_levels;
85
86 /**
87 * Show-limit
88 *
89 * @var int
90 */
91 public $showLimit;
92
93 /**
94 * Return URL
95 *
96 * @var string
97 */
98 public $returnUrl;
99
100 /**
101 * Clear-cache flag - if set, clears page cache for current id.
102 *
103 * @var bool
104 */
105 public $clear_cache;
106
107 /**
108 * Command: Eg. "delete" or "setCB" (for DataHandler / clipboard operations)
109 *
110 * @var string
111 */
112 public $cmd;
113
114 /**
115 * Table on which the cmd-action is performed.
116 *
117 * @var string
118 */
119 public $cmd_table;
120
121 /**
122 * Page select perms clause
123 *
124 * @var int
125 */
126 public $perms_clause;
127
128 /**
129 * Module TSconfig
130 *
131 * @var array
132 */
133 public $modTSconfig;
134
135 /**
136 * Current ids page record
137 *
138 * @var mixed[]|bool
139 */
140 public $pageinfo;
141
142 /**
143 * Document template object
144 *
145 * @var DocumentTemplate
146 */
147 public $doc;
148
149 /**
150 * Menu configuration
151 *
152 * @var string[]
153 */
154 public $MOD_MENU = [];
155
156 /**
157 * Module settings (session variable)
158 *
159 * @var string[]
160 */
161 public $MOD_SETTINGS = [];
162
163 /**
164 * Module output accumulation
165 *
166 * @var string
167 */
168 public $content;
169
170 /**
171 * The name of the module
172 *
173 * @var string
174 */
175 protected $moduleName = 'web_list';
176
177 /**
178 * @var string
179 */
180 public $body = '';
181
182 /**
183 * @var PageRenderer
184 */
185 protected $pageRenderer = null;
186
187 /**
188 * @var IconFactory
189 */
190 protected $iconFactory;
191
192 /**
193 * ModuleTemplate object
194 *
195 * @var ModuleTemplate
196 */
197 protected $moduleTemplate;
198
199 /**
200 * Constructor
201 */
202 public function __construct()
203 {
204 $this->moduleTemplate = GeneralUtility::makeInstance(ModuleTemplate::class);
205 $this->getLanguageService()->includeLLFile('EXT:lang/Resources/Private/Language/locallang_mod_web_list.xlf');
206 $this->moduleTemplate->getPageRenderer()->loadJquery();
207 $this->moduleTemplate->getPageRenderer()->loadRequireJsModule('TYPO3/CMS/Recordlist/FieldSelectBox');
208 $this->moduleTemplate->getPageRenderer()->loadRequireJsModule('TYPO3/CMS/Recordlist/Recordlist');
209 }
210
211 /**
212 * Initializing the module
213 */
214 public function init()
215 {
216 $this->iconFactory = GeneralUtility::makeInstance(IconFactory::class);
217 $backendUser = $this->getBackendUserAuthentication();
218 $this->perms_clause = $backendUser->getPagePermsClause(Permission::PAGE_SHOW);
219 // Get session data
220 $sessionData = $backendUser->getSessionData(__CLASS__);
221 $this->search_field = !empty($sessionData['search_field']) ? $sessionData['search_field'] : '';
222 // GPvars:
223 $this->id = (int)GeneralUtility::_GP('id');
224 $this->pointer = GeneralUtility::_GP('pointer');
225 $this->imagemode = GeneralUtility::_GP('imagemode');
226 $this->table = GeneralUtility::_GP('table');
227 $this->search_field = GeneralUtility::_GP('search_field');
228 $this->search_levels = (int)GeneralUtility::_GP('search_levels');
229 $this->showLimit = GeneralUtility::_GP('showLimit');
230 $this->returnUrl = GeneralUtility::sanitizeLocalUrl(GeneralUtility::_GP('returnUrl'));
231 $this->clear_cache = GeneralUtility::_GP('clear_cache');
232 $this->cmd = GeneralUtility::_GP('cmd');
233 $this->cmd_table = GeneralUtility::_GP('cmd_table');
234 $sessionData['search_field'] = $this->search_field;
235 // Initialize menu
236 $this->menuConfig();
237 // Store session data
238 $backendUser->setAndSaveSessionData(self::class, $sessionData);
239 $this->getPageRenderer()->addInlineLanguageLabelFile('EXT:lang/Resources/Private/Language/locallang_mod_web_list.xlf');
240 }
241
242 /**
243 * Initialize function menu array
244 */
245 public function menuConfig()
246 {
247 // MENU-ITEMS:
248 $this->MOD_MENU = [
249 'bigControlPanel' => '',
250 'clipBoard' => '',
251 ];
252 // Loading module configuration:
253 $this->modTSconfig = BackendUtility::getModTSconfig($this->id, 'mod.' . $this->moduleName);
254 // Clean up settings:
255 $this->MOD_SETTINGS = BackendUtility::getModuleData($this->MOD_MENU, GeneralUtility::_GP('SET'), $this->moduleName);
256 }
257
258 /**
259 * Clears page cache for the current id, $this->id
260 */
261 public function clearCache()
262 {
263 if ($this->clear_cache) {
264 $tce = GeneralUtility::makeInstance(DataHandler::class);
265 $tce->start([], []);
266 $tce->clear_cacheCmd($this->id);
267 }
268 }
269
270 /**
271 * Main function, starting the rendering of the list.
272 */
273 public function main()
274 {
275 $backendUser = $this->getBackendUserAuthentication();
276 $lang = $this->getLanguageService();
277 // Loading current page record and checking access:
278 $this->pageinfo = BackendUtility::readPageAccess($this->id, $this->perms_clause);
279 $access = is_array($this->pageinfo);
280 // Start document template object:
281 // We need to keep this due to deeply nested dependencies
282 $this->doc = GeneralUtility::makeInstance(DocumentTemplate::class);
283
284 $this->getPageRenderer()->loadRequireJsModule('TYPO3/CMS/Backend/AjaxDataHandler');
285 $calcPerms = $backendUser->calcPerms($this->pageinfo);
286 $userCanEditPage = $calcPerms & Permission::PAGE_EDIT && !empty($this->id) && ($backendUser->isAdmin() || (int)$this->pageinfo['editlock'] === 0);
287 if ($userCanEditPage) {
288 $this->getPageRenderer()->loadRequireJsModule('TYPO3/CMS/Backend/PageActions', 'function(PageActions) {
289 PageActions.setPageId(' . (int)$this->id . ');
290 PageActions.initializePageTitleRenaming();
291 }');
292 }
293 $this->getPageRenderer()->loadRequireJsModule('TYPO3/CMS/Recordlist/Tooltip');
294 // Apply predefined values for hidden checkboxes
295 // Set predefined value for DisplayBigControlPanel:
296 if ($this->modTSconfig['properties']['enableDisplayBigControlPanel'] === 'activated') {
297 $this->MOD_SETTINGS['bigControlPanel'] = true;
298 } elseif ($this->modTSconfig['properties']['enableDisplayBigControlPanel'] === 'deactivated') {
299 $this->MOD_SETTINGS['bigControlPanel'] = false;
300 }
301 // Set predefined value for Clipboard:
302 if ($this->modTSconfig['properties']['enableClipBoard'] === 'activated') {
303 $this->MOD_SETTINGS['clipBoard'] = true;
304 } elseif ($this->modTSconfig['properties']['enableClipBoard'] === 'deactivated') {
305 $this->MOD_SETTINGS['clipBoard'] = false;
306 } else {
307 if ($this->MOD_SETTINGS['clipBoard'] === null) {
308 $this->MOD_SETTINGS['clipBoard'] = true;
309 }
310 }
311
312 // Initialize the dblist object:
313 /** @var $dblist RecordList\DatabaseRecordList */
314 $dblist = GeneralUtility::makeInstance(RecordList\DatabaseRecordList::class);
315 /** @var \TYPO3\CMS\Backend\Routing\UriBuilder $uriBuilder */
316 $uriBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\UriBuilder::class);
317 $dblist->script = (string)$uriBuilder->buildUriFromRoute('web_list');
318 $dblist->calcPerms = $calcPerms;
319 $dblist->thumbs = $backendUser->uc['thumbnailsByDefault'];
320 $dblist->returnUrl = $this->returnUrl;
321 $dblist->allFields = $this->MOD_SETTINGS['bigControlPanel'] || $this->table ? 1 : 0;
322 $dblist->showClipboard = 1;
323 $dblist->disableSingleTableView = $this->modTSconfig['properties']['disableSingleTableView'];
324 $dblist->listOnlyInSingleTableMode = $this->modTSconfig['properties']['listOnlyInSingleTableView'];
325 $dblist->hideTables = $this->modTSconfig['properties']['hideTables'];
326 $dblist->hideTranslations = $this->modTSconfig['properties']['hideTranslations'];
327 $dblist->tableTSconfigOverTCA = $this->modTSconfig['properties']['table.'];
328 $dblist->allowedNewTables = GeneralUtility::trimExplode(',', $this->modTSconfig['properties']['allowedNewTables'], true);
329 $dblist->deniedNewTables = GeneralUtility::trimExplode(',', $this->modTSconfig['properties']['deniedNewTables'], true);
330 $dblist->newWizards = $this->modTSconfig['properties']['newWizards'] ? 1 : 0;
331 $dblist->pageRow = $this->pageinfo;
332 $dblist->counter++;
333 $dblist->MOD_MENU = ['bigControlPanel' => '', 'clipBoard' => ''];
334 $dblist->modTSconfig = $this->modTSconfig;
335 $clickTitleMode = trim($this->modTSconfig['properties']['clickTitleMode']);
336 $dblist->clickTitleMode = $clickTitleMode === '' ? 'edit' : $clickTitleMode;
337 if (isset($this->modTSconfig['properties']['tableDisplayOrder.'])) {
338 $typoScriptService = GeneralUtility::makeInstance(TypoScriptService::class);
339 $dblist->setTableDisplayOrder($typoScriptService->convertTypoScriptArrayToPlainArray($this->modTSconfig['properties']['tableDisplayOrder.']));
340 }
341 // Clipboard is initialized:
342 // Start clipboard
343 $dblist->clipObj = GeneralUtility::makeInstance(Clipboard::class);
344 // Initialize - reads the clipboard content from the user session
345 $dblist->clipObj->initializeClipboard();
346 // Clipboard actions are handled:
347 // CB is the clipboard command array
348 $CB = GeneralUtility::_GET('CB');
349 if ($this->cmd === 'setCB') {
350 // CBH is all the fields selected for the clipboard, CBC is the checkbox fields which were checked.
351 // By merging we get a full array of checked/unchecked elements
352 // This is set to the 'el' array of the CB after being parsed so only the table in question is registered.
353 $CB['el'] = $dblist->clipObj->cleanUpCBC(array_merge(GeneralUtility::_POST('CBH'), (array)GeneralUtility::_POST('CBC')), $this->cmd_table);
354 }
355 if (!$this->MOD_SETTINGS['clipBoard']) {
356 // If the clipboard is NOT shown, set the pad to 'normal'.
357 $CB['setP'] = 'normal';
358 }
359 // Execute commands.
360 $dblist->clipObj->setCmd($CB);
361 // Clean up pad
362 $dblist->clipObj->cleanCurrent();
363 // Save the clipboard content
364 $dblist->clipObj->endClipboard();
365 // This flag will prevent the clipboard panel in being shown.
366 // It is set, if the clickmenu-layer is active AND the extended view is not enabled.
367 $dblist->dontShowClipControlPanels = ($dblist->clipObj->current === 'normal' && !$this->modTSconfig['properties']['showClipControlPanelsDespiteOfCMlayers']);
368 // If there is access to the page or root page is used for searching, then render the list contents and set up the document template object:
369 if ($access || ($this->id === 0 && $this->search_levels !== 0 && $this->search_field !== '')) {
370 // Deleting records...:
371 // Has not to do with the clipboard but is simply the delete action. The clipboard object is used to clean up the submitted entries to only the selected table.
372 if ($this->cmd === 'delete') {
373 $items = $dblist->clipObj->cleanUpCBC(GeneralUtility::_POST('CBC'), $this->cmd_table, 1);
374 if (!empty($items)) {
375 $cmd = [];
376 foreach ($items as $iK => $value) {
377 $iKParts = explode('|', $iK);
378 $cmd[$iKParts[0]][$iKParts[1]]['delete'] = 1;
379 }
380 $tce = GeneralUtility::makeInstance(DataHandler::class);
381 $tce->start([], $cmd);
382 $tce->process_cmdmap();
383 if (isset($cmd['pages'])) {
384 BackendUtility::setUpdateSignal('updatePageTree');
385 }
386 $tce->printLogErrorMessages();
387 }
388 }
389 // Initialize the listing object, dblist, for rendering the list:
390 $this->pointer = max(0, (int)$this->pointer);
391 $dblist->start($this->id, $this->table, $this->pointer, $this->search_field, $this->search_levels, $this->showLimit);
392 $dblist->setDispFields();
393 // Render the list of tables:
394 $dblist->generateList();
395 $listUrl = $dblist->listURL();
396 // Add JavaScript functions to the page:
397
398 $this->moduleTemplate->addJavaScriptCode(
399 'RecordListInlineJS',
400 '
401 function jumpExt(URL,anchor) { //
402 var anc = anchor?anchor:"";
403 window.location.href = URL+(T3_THIS_LOCATION?"&returnUrl="+T3_THIS_LOCATION:"")+anc;
404 return false;
405 }
406 function jumpSelf(URL) { //
407 window.location.href = URL+(T3_RETURN_URL?"&returnUrl="+T3_RETURN_URL:"");
408 return false;
409 }
410 function jumpToUrl(URL) {
411 window.location.href = URL;
412 return false;
413 }
414
415 function setHighlight(id) { //
416 top.fsMod.recentIds["web"]=id;
417 top.fsMod.navFrameHighlightedID["web"]="pages"+id+"_"+top.fsMod.currentBank; // For highlighting
418
419 if (top.nav_frame && top.nav_frame.refresh_nav) {
420 top.nav_frame.refresh_nav();
421 }
422 }
423 ' . $this->moduleTemplate->redirectUrls($listUrl) . '
424 ' . $dblist->CBfunctions() . '
425 function editRecords(table,idList,addParams,CBflag) { //
426 window.location.href="' . (string)$uriBuilder->buildUriFromRoute('record_edit', ['returnUrl' => GeneralUtility::getIndpEnv('REQUEST_URI')]) . '&edit["+table+"]["+idList+"]=edit"+addParams;
427 }
428 function editList(table,idList) { //
429 var list="";
430
431 // Checking how many is checked, how many is not
432 var pointer=0;
433 var pos = idList.indexOf(",");
434 while (pos!=-1) {
435 if (cbValue(table+"|"+idList.substr(pointer,pos-pointer))) {
436 list+=idList.substr(pointer,pos-pointer)+",";
437 }
438 pointer=pos+1;
439 pos = idList.indexOf(",",pointer);
440 }
441 if (cbValue(table+"|"+idList.substr(pointer))) {
442 list+=idList.substr(pointer)+",";
443 }
444
445 return list ? list : idList;
446 }
447
448 if (top.fsMod) top.fsMod.recentIds["web"] = ' . (int)$this->id . ';
449 '
450 );
451
452 // Setting up the context sensitive menu:
453 $this->moduleTemplate->getPageRenderer()->loadRequireJsModule('TYPO3/CMS/Backend/ContextMenu');
454 }
455 // access
456 // Begin to compile the whole page, starting out with page header:
457 if (!$this->id) {
458 $title = $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'];
459 } else {
460 $title = $this->pageinfo['title'];
461 }
462 $this->body = $this->moduleTemplate->header($title);
463 $this->moduleTemplate->setTitle($title);
464
465 $output = '';
466 // Show the selector to add page translations and the list of translations of the current page
467 // but only when in "default" mode
468 if ($this->id && !$dblist->csvOutput && !$this->search_field && !$this->cmd && !$this->table) {
469 $output .= $this->languageSelector($this->id);
470 $pageTranslationsDatabaseRecordList = clone $dblist;
471 $pageTranslationsDatabaseRecordList->listOnlyInSingleTableMode = false;
472 $pageTranslationsDatabaseRecordList->disableSingleTableView = true;
473 $pageTranslationsDatabaseRecordList->deniedNewTables = ['pages'];
474 $pageTranslationsDatabaseRecordList->hideTranslations = '';
475 $pageTranslationsDatabaseRecordList->iLimit = $pageTranslationsDatabaseRecordList->itemsLimitPerTable;
476 $pageTranslationsDatabaseRecordList->showOnlyTranslatedRecords(true);
477 $output .= $pageTranslationsDatabaseRecordList->getTable('pages', $this->id);
478 }
479
480 if (!empty($dblist->HTMLcode)) {
481 $output .= $dblist->HTMLcode;
482 } else {
483 $flashMessage = GeneralUtility::makeInstance(
484 FlashMessage::class,
485 $lang->getLL('noRecordsOnThisPage'),
486 '',
487 FlashMessage::INFO
488 );
489 /** @var $flashMessageService \TYPO3\CMS\Core\Messaging\FlashMessageService */
490 $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
491 /** @var $defaultFlashMessageQueue \TYPO3\CMS\Core\Messaging\FlashMessageQueue */
492 $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
493 $defaultFlashMessageQueue->enqueue($flashMessage);
494 }
495
496 $this->body .= '<form action="' . htmlspecialchars($dblist->listURL()) . '" method="post" name="dblistForm">';
497 $this->body .= $output;
498 $this->body .= '<input type="hidden" name="cmd_table" /><input type="hidden" name="cmd" /></form>';
499 // If a listing was produced, create the page footer with search form etc:
500 if ($dblist->HTMLcode) {
501 // Making field select box (when extended view for a single table is enabled):
502 if ($dblist->table) {
503 $this->body .= $dblist->fieldSelectBox($dblist->table);
504 }
505 // Adding checkbox options for extended listing and clipboard display:
506 $this->body .= '
507
508 <!--
509 Listing options for extended view and clipboard view
510 -->
511 <div class="typo3-listOptions">
512 <form action="" method="post">';
513
514 // Add "display bigControlPanel" checkbox:
515 if ($this->modTSconfig['properties']['enableDisplayBigControlPanel'] === 'selectable') {
516 $this->body .= '<div class="checkbox">' .
517 '<label for="checkLargeControl">' .
518 BackendUtility::getFuncCheck($this->id, 'SET[bigControlPanel]', $this->MOD_SETTINGS['bigControlPanel'], '', $this->table ? '&table=' . $this->table : '', 'id="checkLargeControl"') .
519 BackendUtility::wrapInHelp('xMOD_csh_corebe', 'list_options', htmlspecialchars($lang->getLL('largeControl'))) .
520 '</label>' .
521 '</div>';
522 }
523
524 // Add "clipboard" checkbox:
525 if ($this->modTSconfig['properties']['enableClipBoard'] === 'selectable') {
526 if ($dblist->showClipboard) {
527 $this->body .= '<div class="checkbox">' .
528 '<label for="checkShowClipBoard">' .
529 BackendUtility::getFuncCheck($this->id, 'SET[clipBoard]', $this->MOD_SETTINGS['clipBoard'], '', $this->table ? '&table=' . $this->table : '', 'id="checkShowClipBoard"') .
530 BackendUtility::wrapInHelp('xMOD_csh_corebe', 'list_options', htmlspecialchars($lang->getLL('showClipBoard'))) .
531 '</label>' .
532 '</div>';
533 }
534 }
535
536 $this->body .= '
537 </form>
538 </div>';
539 }
540 // Printing clipboard if enabled
541 if ($this->MOD_SETTINGS['clipBoard'] && $dblist->showClipboard && ($dblist->HTMLcode || $dblist->clipObj->hasElements())) {
542 $this->body .= '<div class="db_list-dashboard">' . $dblist->clipObj->printClipboard() . '</div>';
543 }
544 // Additional footer content
545 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['recordlist/Modules/Recordlist/index.php']['drawFooterHook'] ?? [] as $hook) {
546 $params = [];
547 $this->body .= GeneralUtility::callUserFunction($hook, $params, $this);
548 }
549 // Setting up the buttons for docheader
550 $dblist->getDocHeaderButtons($this->moduleTemplate);
551 // searchbox toolbar
552 if (!$this->modTSconfig['properties']['disableSearchBox'] && ($dblist->HTMLcode || !empty($dblist->searchString))) {
553 $this->content = $dblist->getSearchBox();
554 $this->moduleTemplate->getPageRenderer()->loadRequireJsModule('TYPO3/CMS/Backend/ToggleSearchToolbox');
555
556 $searchButton = $this->moduleTemplate->getDocHeaderComponent()->getButtonBar()->makeLinkButton();
557 $searchButton
558 ->setHref('#')
559 ->setClasses('t3js-toggle-search-toolbox')
560 ->setTitle($lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.title.searchIcon'))
561 ->setIcon($this->iconFactory->getIcon('actions-search', Icon::SIZE_SMALL));
562 $this->moduleTemplate->getDocHeaderComponent()->getButtonBar()->addButton(
563 $searchButton,
564 ButtonBar::BUTTON_POSITION_LEFT,
565 90
566 );
567 }
568
569 if ($this->pageinfo) {
570 $this->moduleTemplate->getDocHeaderComponent()->setMetaInformation($this->pageinfo);
571 }
572
573 // Build the <body> for the module
574 $this->content .= $this->body;
575 }
576
577 /**
578 * Injects the request object for the current request or subrequest
579 * Simply calls main() and init() and outputs the content
580 *
581 * @param ServerRequestInterface $request the current request
582 * @param ResponseInterface $response
583 * @return ResponseInterface the response with the content
584 */
585 public function mainAction(ServerRequestInterface $request, ResponseInterface $response)
586 {
587 BackendUtility::lockRecords();
588 $GLOBALS['SOBE'] = $this;
589 $this->init();
590 $this->clearCache();
591 $this->main();
592 $this->moduleTemplate->setContent($this->content);
593 $response->getBody()->write($this->moduleTemplate->renderContent());
594 return $response;
595 }
596
597 /**
598 * Make selector box for creating new translation in a language
599 * Displays only languages which are not yet present for the current page and
600 * that are not disabled with page TS.
601 *
602 * @param int $id Page id for which to create a new translation record of pages
603 * @return string <select> HTML element (if there were items for the box anyways...)
604 */
605 protected function languageSelector(int $id): string
606 {
607 if ($this->getBackendUserAuthentication()->check('tables_modify', 'pages')) {
608 // First, select all
609 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_language');
610 $queryBuilder->getRestrictions()->removeAll();
611 $queryBuilder->getRestrictions()->add(GeneralUtility::makeInstance(HiddenRestriction::class));
612 $statement = $queryBuilder->select('uid', 'title')
613 ->from('sys_language')
614 ->orderBy('sorting')
615 ->execute();
616 $availableTranslations = [];
617 while ($row = $statement->fetch()) {
618 if ($this->getBackendUserAuthentication()->checkLanguageAccess($row['uid'])) {
619 $availableTranslations[(int)$row['uid']] = $row['title'];
620 }
621 }
622 // Then, subtract the languages which are already on the page:
623 $localizationParentField = $GLOBALS['TCA']['pages']['ctrl']['transOrigPointerField'];
624 $languageField = $GLOBALS['TCA']['pages']['ctrl']['languageField'];
625 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_language');
626 $queryBuilder->getRestrictions()->removeAll();
627 $queryBuilder->select('sys_language.uid AS uid', 'sys_language.title AS title')
628 ->from('sys_language')
629 ->join(
630 'sys_language',
631 'pages',
632 'pages',
633 $queryBuilder->expr()->eq('sys_language.uid', $queryBuilder->quoteIdentifier('pages.' . $languageField))
634 )
635 ->where(
636 $queryBuilder->expr()->eq(
637 'pages.deleted',
638 $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
639 ),
640 $queryBuilder->expr()->eq(
641 'pages.' . $localizationParentField,
642 $queryBuilder->createNamedParameter($this->id, \PDO::PARAM_INT)
643 ),
644 $queryBuilder->expr()->orX(
645 $queryBuilder->expr()->gte(
646 'pages.t3ver_state',
647 $queryBuilder->createNamedParameter(
648 (string)new VersionState(VersionState::DEFAULT_STATE),
649 \PDO::PARAM_INT
650 )
651 ),
652 $queryBuilder->expr()->eq(
653 'pages.t3ver_wsid',
654 $queryBuilder->createNamedParameter($this->getBackendUserAuthentication()->workspace, \PDO::PARAM_INT)
655 )
656 )
657 )
658 ->groupBy(
659 'pages.' . $languageField,
660 'sys_language.uid',
661 'sys_language.pid',
662 'sys_language.tstamp',
663 'sys_language.hidden',
664 'sys_language.title',
665 'sys_language.language_isocode',
666 'sys_language.static_lang_isocode',
667 'sys_language.flag',
668 'sys_language.sorting'
669 )
670 ->orderBy('sys_language.sorting');
671 if (!$this->getBackendUserAuthentication()->isAdmin()) {
672 $queryBuilder->andWhere(
673 $queryBuilder->expr()->eq(
674 'sys_language.hidden',
675 $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
676 )
677 );
678 }
679 $statement = $queryBuilder->execute();
680 while ($row = $statement->fetch()) {
681 unset($availableTranslations[(int)$row['uid']]);
682 }
683 // Remove disallowed languages
684 if (!empty($availableTranslations)
685 && !$this->getBackendUserAuthentication()->isAdmin()
686 && $this->getBackendUserAuthentication()->groupData['allowed_languages'] !== ''
687 ) {
688 $allowed_languages = array_flip(explode(',', $this->getBackendUserAuthentication()->groupData['allowed_languages']));
689 if (!empty($allowed_languages)) {
690 foreach ($availableTranslations as $key => $value) {
691 if (!isset($allowed_languages[$key]) && $key != 0) {
692 unset($availableTranslations[$key]);
693 }
694 }
695 }
696 }
697 // Remove disabled languages
698 $modSharedTSconfig = BackendUtility::getModTSconfig($id, 'mod.SHARED');
699 $disableLanguages = isset($modSharedTSconfig['properties']['disableLanguages'])
700 ? GeneralUtility::trimExplode(',', $modSharedTSconfig['properties']['disableLanguages'], true)
701 : [];
702 if (!empty($availableTranslations) && !empty($disableLanguages)) {
703 foreach ($disableLanguages as $language) {
704 if ($language != 0 && isset($availableTranslations[$language])) {
705 unset($availableTranslations[$language]);
706 }
707 }
708 }
709 // If any languages are left, make selector:
710 if (!empty($availableTranslations)) {
711 $output = '<option value="">' . htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:backend/Resources/Private/Language/locallang_layout.xlf:new_language')) . '</option>';
712 foreach ($availableTranslations as $languageUid => $languageTitle) {
713 // Build localize command URL to DataHandler (tce_db)
714 // which redirects to FormEngine (record_edit)
715 // which, when finished editing should return back to the current page (returnUrl)
716 $parameters = [
717 'justLocalized' => 'pages:' . $id . ':' . $languageUid,
718 'returnUrl' => GeneralUtility::getIndpEnv('REQUEST_URI')
719 ];
720 /** @var \TYPO3\CMS\Backend\Routing\UriBuilder $uriBuilder */
721 $uriBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\UriBuilder::class);
722 $redirectUrl = (string)$uriBuilder->buildUriFromRoute('record_edit', $parameters);
723 $targetUrl = BackendUtility::getLinkToDataHandlerAction(
724 '&cmd[pages][' . $id . '][localize]=' . $languageUid,
725 $redirectUrl
726 );
727
728 $output .= '<option value="' . htmlspecialchars($targetUrl) . '">' . htmlspecialchars($languageTitle) . '</option>';
729 }
730
731 return '<div class="form-inline form-inline-spaced">'
732 . '<div class="form-group">'
733 . '<select class="form-control input-sm" name="createNewLanguage" onchange="window.location.href=this.options[this.selectedIndex].value">'
734 . $output
735 . '</select></div></div>';
736 }
737 }
738 return '';
739 }
740
741 /**
742 * @return ModuleTemplate
743 */
744 public function getModuleTemplate()
745 {
746 return $this->moduleTemplate;
747 }
748
749 /**
750 * @return BackendUserAuthentication
751 */
752 protected function getBackendUserAuthentication()
753 {
754 return $GLOBALS['BE_USER'];
755 }
756
757 /**
758 * @return LanguageService
759 */
760 protected function getLanguageService()
761 {
762 return $GLOBALS['LANG'];
763 }
764
765 /**
766 * @return PageRenderer
767 */
768 protected function getPageRenderer()
769 {
770 if ($this->pageRenderer === null) {
771 $this->pageRenderer = GeneralUtility::makeInstance(PageRenderer::class);
772 }
773
774 return $this->pageRenderer;
775 }
776 }