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