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