2dcc33d56fb13be61b6533f5e45bdadd9616658f
[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
464 // Additional header content
465 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['recordlist/Modules/Recordlist/index.php']['drawHeaderHook'] ?? [] as $hook) {
466 $params = [];
467 $this->body .= GeneralUtility::callUserFunction($hook, $params, $this);
468 }
469
470 $this->moduleTemplate->setTitle($title);
471
472 $output = '';
473 // Show the selector to add page translations and the list of translations of the current page
474 // but only when in "default" mode
475 if ($this->id && !$dblist->csvOutput && !$this->search_field && !$this->cmd && !$this->table) {
476 $output .= $this->languageSelector($this->id);
477 $pageTranslationsDatabaseRecordList = clone $dblist;
478 $pageTranslationsDatabaseRecordList->listOnlyInSingleTableMode = false;
479 $pageTranslationsDatabaseRecordList->disableSingleTableView = true;
480 $pageTranslationsDatabaseRecordList->deniedNewTables = ['pages'];
481 $pageTranslationsDatabaseRecordList->hideTranslations = '';
482 $pageTranslationsDatabaseRecordList->iLimit = $pageTranslationsDatabaseRecordList->itemsLimitPerTable;
483 $pageTranslationsDatabaseRecordList->showOnlyTranslatedRecords(true);
484 $output .= $pageTranslationsDatabaseRecordList->getTable('pages', $this->id);
485 }
486
487 if (!empty($dblist->HTMLcode)) {
488 $output .= $dblist->HTMLcode;
489 } else {
490 $flashMessage = GeneralUtility::makeInstance(
491 FlashMessage::class,
492 $lang->getLL('noRecordsOnThisPage'),
493 '',
494 FlashMessage::INFO
495 );
496 /** @var $flashMessageService \TYPO3\CMS\Core\Messaging\FlashMessageService */
497 $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
498 /** @var $defaultFlashMessageQueue \TYPO3\CMS\Core\Messaging\FlashMessageQueue */
499 $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
500 $defaultFlashMessageQueue->enqueue($flashMessage);
501 }
502
503 $this->body .= '<form action="' . htmlspecialchars($dblist->listURL()) . '" method="post" name="dblistForm">';
504 $this->body .= $output;
505 $this->body .= '<input type="hidden" name="cmd_table" /><input type="hidden" name="cmd" /></form>';
506 // If a listing was produced, create the page footer with search form etc:
507 if ($dblist->HTMLcode) {
508 // Making field select box (when extended view for a single table is enabled):
509 if ($dblist->table) {
510 $this->body .= $dblist->fieldSelectBox($dblist->table);
511 }
512 // Adding checkbox options for extended listing and clipboard display:
513 $this->body .= '
514
515 <!--
516 Listing options for extended view and clipboard view
517 -->
518 <div class="typo3-listOptions">
519 <form action="" method="post">';
520
521 // Add "display bigControlPanel" checkbox:
522 if ($this->modTSconfig['properties']['enableDisplayBigControlPanel'] === 'selectable') {
523 $this->body .= '<div class="checkbox">' .
524 '<label for="checkLargeControl">' .
525 BackendUtility::getFuncCheck($this->id, 'SET[bigControlPanel]', $this->MOD_SETTINGS['bigControlPanel'], '', $this->table ? '&table=' . $this->table : '', 'id="checkLargeControl"') .
526 BackendUtility::wrapInHelp('xMOD_csh_corebe', 'list_options', htmlspecialchars($lang->getLL('largeControl'))) .
527 '</label>' .
528 '</div>';
529 }
530
531 // Add "clipboard" checkbox:
532 if ($this->modTSconfig['properties']['enableClipBoard'] === 'selectable') {
533 if ($dblist->showClipboard) {
534 $this->body .= '<div class="checkbox">' .
535 '<label for="checkShowClipBoard">' .
536 BackendUtility::getFuncCheck($this->id, 'SET[clipBoard]', $this->MOD_SETTINGS['clipBoard'], '', $this->table ? '&table=' . $this->table : '', 'id="checkShowClipBoard"') .
537 BackendUtility::wrapInHelp('xMOD_csh_corebe', 'list_options', htmlspecialchars($lang->getLL('showClipBoard'))) .
538 '</label>' .
539 '</div>';
540 }
541 }
542
543 $this->body .= '
544 </form>
545 </div>';
546 }
547 // Printing clipboard if enabled
548 if ($this->MOD_SETTINGS['clipBoard'] && $dblist->showClipboard && ($dblist->HTMLcode || $dblist->clipObj->hasElements())) {
549 $this->body .= '<div class="db_list-dashboard">' . $dblist->clipObj->printClipboard() . '</div>';
550 }
551 // Additional footer content
552 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['recordlist/Modules/Recordlist/index.php']['drawFooterHook'] ?? [] as $hook) {
553 $params = [];
554 $this->body .= GeneralUtility::callUserFunction($hook, $params, $this);
555 }
556 // Setting up the buttons for docheader
557 $dblist->getDocHeaderButtons($this->moduleTemplate);
558 // searchbox toolbar
559 if (!$this->modTSconfig['properties']['disableSearchBox'] && ($dblist->HTMLcode || !empty($dblist->searchString))) {
560 $this->content = $dblist->getSearchBox();
561 $this->moduleTemplate->getPageRenderer()->loadRequireJsModule('TYPO3/CMS/Backend/ToggleSearchToolbox');
562
563 $searchButton = $this->moduleTemplate->getDocHeaderComponent()->getButtonBar()->makeLinkButton();
564 $searchButton
565 ->setHref('#')
566 ->setClasses('t3js-toggle-search-toolbox')
567 ->setTitle($lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.title.searchIcon'))
568 ->setIcon($this->iconFactory->getIcon('actions-search', Icon::SIZE_SMALL));
569 $this->moduleTemplate->getDocHeaderComponent()->getButtonBar()->addButton(
570 $searchButton,
571 ButtonBar::BUTTON_POSITION_LEFT,
572 90
573 );
574 }
575
576 if ($this->pageinfo) {
577 $this->moduleTemplate->getDocHeaderComponent()->setMetaInformation($this->pageinfo);
578 }
579
580 // Build the <body> for the module
581 $this->content .= $this->body;
582 }
583
584 /**
585 * Injects the request object for the current request or subrequest
586 * Simply calls main() and init() and outputs the content
587 *
588 * @param ServerRequestInterface $request the current request
589 * @param ResponseInterface $response
590 * @return ResponseInterface the response with the content
591 */
592 public function mainAction(ServerRequestInterface $request, ResponseInterface $response)
593 {
594 BackendUtility::lockRecords();
595 $GLOBALS['SOBE'] = $this;
596 $this->init();
597 $this->clearCache();
598 $this->main();
599 $this->moduleTemplate->setContent($this->content);
600 $response->getBody()->write($this->moduleTemplate->renderContent());
601 return $response;
602 }
603
604 /**
605 * Make selector box for creating new translation in a language
606 * Displays only languages which are not yet present for the current page and
607 * that are not disabled with page TS.
608 *
609 * @param int $id Page id for which to create a new translation record of pages
610 * @return string <select> HTML element (if there were items for the box anyways...)
611 */
612 protected function languageSelector(int $id): string
613 {
614 if ($this->getBackendUserAuthentication()->check('tables_modify', 'pages')) {
615 // First, select all
616 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_language');
617 $queryBuilder->getRestrictions()->removeAll();
618 $queryBuilder->getRestrictions()->add(GeneralUtility::makeInstance(HiddenRestriction::class));
619 $statement = $queryBuilder->select('uid', 'title')
620 ->from('sys_language')
621 ->orderBy('sorting')
622 ->execute();
623 $availableTranslations = [];
624 while ($row = $statement->fetch()) {
625 if ($this->getBackendUserAuthentication()->checkLanguageAccess($row['uid'])) {
626 $availableTranslations[(int)$row['uid']] = $row['title'];
627 }
628 }
629 // Then, subtract the languages which are already on the page:
630 $localizationParentField = $GLOBALS['TCA']['pages']['ctrl']['transOrigPointerField'];
631 $languageField = $GLOBALS['TCA']['pages']['ctrl']['languageField'];
632 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_language');
633 $queryBuilder->getRestrictions()->removeAll();
634 $queryBuilder->select('sys_language.uid AS uid', 'sys_language.title AS title')
635 ->from('sys_language')
636 ->join(
637 'sys_language',
638 'pages',
639 'pages',
640 $queryBuilder->expr()->eq('sys_language.uid', $queryBuilder->quoteIdentifier('pages.' . $languageField))
641 )
642 ->where(
643 $queryBuilder->expr()->eq(
644 'pages.deleted',
645 $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
646 ),
647 $queryBuilder->expr()->eq(
648 'pages.' . $localizationParentField,
649 $queryBuilder->createNamedParameter($this->id, \PDO::PARAM_INT)
650 ),
651 $queryBuilder->expr()->orX(
652 $queryBuilder->expr()->gte(
653 'pages.t3ver_state',
654 $queryBuilder->createNamedParameter(
655 (string)new VersionState(VersionState::DEFAULT_STATE),
656 \PDO::PARAM_INT
657 )
658 ),
659 $queryBuilder->expr()->eq(
660 'pages.t3ver_wsid',
661 $queryBuilder->createNamedParameter($this->getBackendUserAuthentication()->workspace, \PDO::PARAM_INT)
662 )
663 )
664 )
665 ->groupBy(
666 'pages.' . $languageField,
667 'sys_language.uid',
668 'sys_language.pid',
669 'sys_language.tstamp',
670 'sys_language.hidden',
671 'sys_language.title',
672 'sys_language.language_isocode',
673 'sys_language.static_lang_isocode',
674 'sys_language.flag',
675 'sys_language.sorting'
676 )
677 ->orderBy('sys_language.sorting');
678 if (!$this->getBackendUserAuthentication()->isAdmin()) {
679 $queryBuilder->andWhere(
680 $queryBuilder->expr()->eq(
681 'sys_language.hidden',
682 $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
683 )
684 );
685 }
686 $statement = $queryBuilder->execute();
687 while ($row = $statement->fetch()) {
688 unset($availableTranslations[(int)$row['uid']]);
689 }
690 // Remove disallowed languages
691 if (!empty($availableTranslations)
692 && !$this->getBackendUserAuthentication()->isAdmin()
693 && $this->getBackendUserAuthentication()->groupData['allowed_languages'] !== ''
694 ) {
695 $allowed_languages = array_flip(explode(',', $this->getBackendUserAuthentication()->groupData['allowed_languages']));
696 if (!empty($allowed_languages)) {
697 foreach ($availableTranslations as $key => $value) {
698 if (!isset($allowed_languages[$key]) && $key != 0) {
699 unset($availableTranslations[$key]);
700 }
701 }
702 }
703 }
704 // Remove disabled languages
705 $modSharedTSconfig = BackendUtility::getModTSconfig($id, 'mod.SHARED');
706 $disableLanguages = isset($modSharedTSconfig['properties']['disableLanguages'])
707 ? GeneralUtility::trimExplode(',', $modSharedTSconfig['properties']['disableLanguages'], true)
708 : [];
709 if (!empty($availableTranslations) && !empty($disableLanguages)) {
710 foreach ($disableLanguages as $language) {
711 if ($language != 0 && isset($availableTranslations[$language])) {
712 unset($availableTranslations[$language]);
713 }
714 }
715 }
716 // If any languages are left, make selector:
717 if (!empty($availableTranslations)) {
718 $output = '<option value="">' . htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:backend/Resources/Private/Language/locallang_layout.xlf:new_language')) . '</option>';
719 foreach ($availableTranslations as $languageUid => $languageTitle) {
720 // Build localize command URL to DataHandler (tce_db)
721 // which redirects to FormEngine (record_edit)
722 // which, when finished editing should return back to the current page (returnUrl)
723 $parameters = [
724 'justLocalized' => 'pages:' . $id . ':' . $languageUid,
725 'returnUrl' => GeneralUtility::getIndpEnv('REQUEST_URI')
726 ];
727 /** @var \TYPO3\CMS\Backend\Routing\UriBuilder $uriBuilder */
728 $uriBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\UriBuilder::class);
729 $redirectUrl = (string)$uriBuilder->buildUriFromRoute('record_edit', $parameters);
730 $targetUrl = BackendUtility::getLinkToDataHandlerAction(
731 '&cmd[pages][' . $id . '][localize]=' . $languageUid,
732 $redirectUrl
733 );
734
735 $output .= '<option value="' . htmlspecialchars($targetUrl) . '">' . htmlspecialchars($languageTitle) . '</option>';
736 }
737
738 return '<div class="form-inline form-inline-spaced">'
739 . '<div class="form-group">'
740 . '<select class="form-control input-sm" name="createNewLanguage" onchange="window.location.href=this.options[this.selectedIndex].value">'
741 . $output
742 . '</select></div></div>';
743 }
744 }
745 return '';
746 }
747
748 /**
749 * @return ModuleTemplate
750 */
751 public function getModuleTemplate()
752 {
753 return $this->moduleTemplate;
754 }
755
756 /**
757 * @return BackendUserAuthentication
758 */
759 protected function getBackendUserAuthentication()
760 {
761 return $GLOBALS['BE_USER'];
762 }
763
764 /**
765 * @return LanguageService
766 */
767 protected function getLanguageService()
768 {
769 return $GLOBALS['LANG'];
770 }
771
772 /**
773 * @return PageRenderer
774 */
775 protected function getPageRenderer()
776 {
777 if ($this->pageRenderer === null) {
778 $this->pageRenderer = GeneralUtility::makeInstance(PageRenderer::class);
779 }
780
781 return $this->pageRenderer;
782 }
783 }