cd8c5fb15e396d8f9ace4ddbe9fb3208e73208b6
[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(1);
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 $dblist->script = BackendUtility::getModuleUrl('web_list');
316 $dblist->calcPerms = $calcPerms;
317 $dblist->thumbs = $backendUser->uc['thumbnailsByDefault'];
318 $dblist->returnUrl = $this->returnUrl;
319 $dblist->allFields = $this->MOD_SETTINGS['bigControlPanel'] || $this->table ? 1 : 0;
320 $dblist->showClipboard = 1;
321 $dblist->disableSingleTableView = $this->modTSconfig['properties']['disableSingleTableView'];
322 $dblist->listOnlyInSingleTableMode = $this->modTSconfig['properties']['listOnlyInSingleTableView'];
323 $dblist->hideTables = $this->modTSconfig['properties']['hideTables'];
324 $dblist->hideTranslations = $this->modTSconfig['properties']['hideTranslations'];
325 $dblist->tableTSconfigOverTCA = $this->modTSconfig['properties']['table.'];
326 $dblist->allowedNewTables = GeneralUtility::trimExplode(',', $this->modTSconfig['properties']['allowedNewTables'], true);
327 $dblist->deniedNewTables = GeneralUtility::trimExplode(',', $this->modTSconfig['properties']['deniedNewTables'], true);
328 $dblist->newWizards = $this->modTSconfig['properties']['newWizards'] ? 1 : 0;
329 $dblist->pageRow = $this->pageinfo;
330 $dblist->counter++;
331 $dblist->MOD_MENU = ['bigControlPanel' => '', 'clipBoard' => ''];
332 $dblist->modTSconfig = $this->modTSconfig;
333 $clickTitleMode = trim($this->modTSconfig['properties']['clickTitleMode']);
334 $dblist->clickTitleMode = $clickTitleMode === '' ? 'edit' : $clickTitleMode;
335 if (isset($this->modTSconfig['properties']['tableDisplayOrder.'])) {
336 $typoScriptService = GeneralUtility::makeInstance(TypoScriptService::class);
337 $dblist->setTableDisplayOrder($typoScriptService->convertTypoScriptArrayToPlainArray($this->modTSconfig['properties']['tableDisplayOrder.']));
338 }
339 // Clipboard is initialized:
340 // Start clipboard
341 $dblist->clipObj = GeneralUtility::makeInstance(Clipboard::class);
342 // Initialize - reads the clipboard content from the user session
343 $dblist->clipObj->initializeClipboard();
344 // Clipboard actions are handled:
345 // CB is the clipboard command array
346 $CB = GeneralUtility::_GET('CB');
347 if ($this->cmd === 'setCB') {
348 // CBH is all the fields selected for the clipboard, CBC is the checkbox fields which were checked.
349 // By merging we get a full array of checked/unchecked elements
350 // This is set to the 'el' array of the CB after being parsed so only the table in question is registered.
351 $CB['el'] = $dblist->clipObj->cleanUpCBC(array_merge(GeneralUtility::_POST('CBH'), (array)GeneralUtility::_POST('CBC')), $this->cmd_table);
352 }
353 if (!$this->MOD_SETTINGS['clipBoard']) {
354 // If the clipboard is NOT shown, set the pad to 'normal'.
355 $CB['setP'] = 'normal';
356 }
357 // Execute commands.
358 $dblist->clipObj->setCmd($CB);
359 // Clean up pad
360 $dblist->clipObj->cleanCurrent();
361 // Save the clipboard content
362 $dblist->clipObj->endClipboard();
363 // This flag will prevent the clipboard panel in being shown.
364 // It is set, if the clickmenu-layer is active AND the extended view is not enabled.
365 $dblist->dontShowClipControlPanels = ($dblist->clipObj->current === 'normal' && !$this->modTSconfig['properties']['showClipControlPanelsDespiteOfCMlayers']);
366 // 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:
367 if ($access || ($this->id === 0 && $this->search_levels !== 0 && $this->search_field !== '')) {
368 // Deleting records...:
369 // 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.
370 if ($this->cmd === 'delete') {
371 $items = $dblist->clipObj->cleanUpCBC(GeneralUtility::_POST('CBC'), $this->cmd_table, 1);
372 if (!empty($items)) {
373 $cmd = [];
374 foreach ($items as $iK => $value) {
375 $iKParts = explode('|', $iK);
376 $cmd[$iKParts[0]][$iKParts[1]]['delete'] = 1;
377 }
378 $tce = GeneralUtility::makeInstance(DataHandler::class);
379 $tce->start([], $cmd);
380 $tce->process_cmdmap();
381 if (isset($cmd['pages'])) {
382 BackendUtility::setUpdateSignal('updatePageTree');
383 }
384 $tce->printLogErrorMessages();
385 }
386 }
387 // Initialize the listing object, dblist, for rendering the list:
388 $this->pointer = max(0, (int)$this->pointer);
389 $dblist->start($this->id, $this->table, $this->pointer, $this->search_field, $this->search_levels, $this->showLimit);
390 $dblist->setDispFields();
391 // Render the list of tables:
392 $dblist->generateList();
393 $listUrl = $dblist->listURL();
394 // Add JavaScript functions to the page:
395
396 $this->moduleTemplate->addJavaScriptCode(
397 'RecordListInlineJS',
398 '
399 function jumpExt(URL,anchor) { //
400 var anc = anchor?anchor:"";
401 window.location.href = URL+(T3_THIS_LOCATION?"&returnUrl="+T3_THIS_LOCATION:"")+anc;
402 return false;
403 }
404 function jumpSelf(URL) { //
405 window.location.href = URL+(T3_RETURN_URL?"&returnUrl="+T3_RETURN_URL:"");
406 return false;
407 }
408 function jumpToUrl(URL) {
409 window.location.href = URL;
410 return false;
411 }
412
413 function setHighlight(id) { //
414 top.fsMod.recentIds["web"]=id;
415 top.fsMod.navFrameHighlightedID["web"]="pages"+id+"_"+top.fsMod.currentBank; // For highlighting
416
417 if (top.nav_frame && top.nav_frame.refresh_nav) {
418 top.nav_frame.refresh_nav();
419 }
420 }
421 ' . $this->moduleTemplate->redirectUrls($listUrl) . '
422 ' . $dblist->CBfunctions() . '
423 function editRecords(table,idList,addParams,CBflag) { //
424 window.location.href="' . BackendUtility::getModuleUrl('record_edit', ['returnUrl' => GeneralUtility::getIndpEnv('REQUEST_URI')]) . '&edit["+table+"]["+idList+"]=edit"+addParams;
425 }
426 function editList(table,idList) { //
427 var list="";
428
429 // Checking how many is checked, how many is not
430 var pointer=0;
431 var pos = idList.indexOf(",");
432 while (pos!=-1) {
433 if (cbValue(table+"|"+idList.substr(pointer,pos-pointer))) {
434 list+=idList.substr(pointer,pos-pointer)+",";
435 }
436 pointer=pos+1;
437 pos = idList.indexOf(",",pointer);
438 }
439 if (cbValue(table+"|"+idList.substr(pointer))) {
440 list+=idList.substr(pointer)+",";
441 }
442
443 return list ? list : idList;
444 }
445
446 if (top.fsMod) top.fsMod.recentIds["web"] = ' . (int)$this->id . ';
447 '
448 );
449
450 // Setting up the context sensitive menu:
451 $this->moduleTemplate->getPageRenderer()->loadRequireJsModule('TYPO3/CMS/Backend/ContextMenu');
452 }
453 // access
454 // Begin to compile the whole page, starting out with page header:
455 if (!$this->id) {
456 $title = $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'];
457 } else {
458 $title = $this->pageinfo['title'];
459 }
460 $this->body = $this->moduleTemplate->header($title);
461 $this->moduleTemplate->setTitle($title);
462
463 $output = '';
464 // Show the selector to add page translations and the list of translations of the current page
465 // but only when in "default" mode
466 if ($this->id && !$dblist->csvOutput && !$this->search_field && !$this->cmd && !$this->table) {
467 $output .= $this->languageSelector($this->id);
468 $pageTranslationsDatabaseRecordList = clone $dblist;
469 $pageTranslationsDatabaseRecordList->listOnlyInSingleTableMode = false;
470 $pageTranslationsDatabaseRecordList->disableSingleTableView = true;
471 $pageTranslationsDatabaseRecordList->deniedNewTables = ['pages'];
472 $pageTranslationsDatabaseRecordList->hideTranslations = '';
473 $pageTranslationsDatabaseRecordList->iLimit = $pageTranslationsDatabaseRecordList->itemsLimitPerTable;
474 $pageTranslationsDatabaseRecordList->showOnlyTranslatedRecords(true);
475 $output .= $pageTranslationsDatabaseRecordList->getTable('pages', $this->id);
476 }
477
478 if (!empty($dblist->HTMLcode)) {
479 $output .= $dblist->HTMLcode;
480 } else {
481 $flashMessage = GeneralUtility::makeInstance(
482 FlashMessage::class,
483 $lang->getLL('noRecordsOnThisPage'),
484 '',
485 FlashMessage::INFO
486 );
487 /** @var $flashMessageService \TYPO3\CMS\Core\Messaging\FlashMessageService */
488 $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
489 /** @var $defaultFlashMessageQueue \TYPO3\CMS\Core\Messaging\FlashMessageQueue */
490 $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
491 $defaultFlashMessageQueue->enqueue($flashMessage);
492 }
493
494 $this->body .= '<form action="' . htmlspecialchars($dblist->listURL()) . '" method="post" name="dblistForm">';
495 $this->body .= $output;
496 $this->body .= '<input type="hidden" name="cmd_table" /><input type="hidden" name="cmd" /></form>';
497 // If a listing was produced, create the page footer with search form etc:
498 if ($dblist->HTMLcode) {
499 // Making field select box (when extended view for a single table is enabled):
500 if ($dblist->table) {
501 $this->body .= $dblist->fieldSelectBox($dblist->table);
502 }
503 // Adding checkbox options for extended listing and clipboard display:
504 $this->body .= '
505
506 <!--
507 Listing options for extended view and clipboard view
508 -->
509 <div class="typo3-listOptions">
510 <form action="" method="post">';
511
512 // Add "display bigControlPanel" checkbox:
513 if ($this->modTSconfig['properties']['enableDisplayBigControlPanel'] === 'selectable') {
514 $this->body .= '<div class="checkbox">' .
515 '<label for="checkLargeControl">' .
516 BackendUtility::getFuncCheck($this->id, 'SET[bigControlPanel]', $this->MOD_SETTINGS['bigControlPanel'], '', $this->table ? '&table=' . $this->table : '', 'id="checkLargeControl"') .
517 BackendUtility::wrapInHelp('xMOD_csh_corebe', 'list_options', htmlspecialchars($lang->getLL('largeControl'))) .
518 '</label>' .
519 '</div>';
520 }
521
522 // Add "clipboard" checkbox:
523 if ($this->modTSconfig['properties']['enableClipBoard'] === 'selectable') {
524 if ($dblist->showClipboard) {
525 $this->body .= '<div class="checkbox">' .
526 '<label for="checkShowClipBoard">' .
527 BackendUtility::getFuncCheck($this->id, 'SET[clipBoard]', $this->MOD_SETTINGS['clipBoard'], '', $this->table ? '&table=' . $this->table : '', 'id="checkShowClipBoard"') .
528 BackendUtility::wrapInHelp('xMOD_csh_corebe', 'list_options', htmlspecialchars($lang->getLL('showClipBoard'))) .
529 '</label>' .
530 '</div>';
531 }
532 }
533
534 $this->body .= '
535 </form>
536 </div>';
537 }
538 // Printing clipboard if enabled
539 if ($this->MOD_SETTINGS['clipBoard'] && $dblist->showClipboard && ($dblist->HTMLcode || $dblist->clipObj->hasElements())) {
540 $this->body .= '<div class="db_list-dashboard">' . $dblist->clipObj->printClipboard() . '</div>';
541 }
542 // Additional footer content
543 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['recordlist/Modules/Recordlist/index.php']['drawFooterHook'] ?? [] as $hook) {
544 $params = [];
545 $this->body .= GeneralUtility::callUserFunction($hook, $params, $this);
546 }
547 // Setting up the buttons for docheader
548 $dblist->getDocHeaderButtons($this->moduleTemplate);
549 // searchbox toolbar
550 if (!$this->modTSconfig['properties']['disableSearchBox'] && ($dblist->HTMLcode || !empty($dblist->searchString))) {
551 $this->content = $dblist->getSearchBox();
552 $this->moduleTemplate->getPageRenderer()->loadRequireJsModule('TYPO3/CMS/Backend/ToggleSearchToolbox');
553
554 $searchButton = $this->moduleTemplate->getDocHeaderComponent()->getButtonBar()->makeLinkButton();
555 $searchButton
556 ->setHref('#')
557 ->setClasses('t3js-toggle-search-toolbox')
558 ->setTitle($lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.title.searchIcon'))
559 ->setIcon($this->iconFactory->getIcon('actions-search', Icon::SIZE_SMALL));
560 $this->moduleTemplate->getDocHeaderComponent()->getButtonBar()->addButton(
561 $searchButton,
562 ButtonBar::BUTTON_POSITION_LEFT,
563 90
564 );
565 }
566
567 if ($this->pageinfo) {
568 $this->moduleTemplate->getDocHeaderComponent()->setMetaInformation($this->pageinfo);
569 }
570
571 // Build the <body> for the module
572 $this->content .= $this->body;
573 }
574
575 /**
576 * Injects the request object for the current request or subrequest
577 * Simply calls main() and init() and outputs the content
578 *
579 * @param ServerRequestInterface $request the current request
580 * @param ResponseInterface $response
581 * @return ResponseInterface the response with the content
582 */
583 public function mainAction(ServerRequestInterface $request, ResponseInterface $response)
584 {
585 BackendUtility::lockRecords();
586 $GLOBALS['SOBE'] = $this;
587 $this->init();
588 $this->clearCache();
589 $this->main();
590 $this->moduleTemplate->setContent($this->content);
591 $response->getBody()->write($this->moduleTemplate->renderContent());
592 return $response;
593 }
594
595 /**
596 * Make selector box for creating new translation in a language
597 * Displays only languages which are not yet present for the current page and
598 * that are not disabled with page TS.
599 *
600 * @param int $id Page id for which to create a new translation record of pages
601 * @return string <select> HTML element (if there were items for the box anyways...)
602 */
603 protected function languageSelector(int $id): string
604 {
605 if ($this->getBackendUserAuthentication()->check('tables_modify', 'pages')) {
606 // First, select all
607 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_language');
608 $queryBuilder->getRestrictions()->removeAll();
609 $queryBuilder->getRestrictions()->add(GeneralUtility::makeInstance(HiddenRestriction::class));
610 $statement = $queryBuilder->select('uid', 'title')
611 ->from('sys_language')
612 ->orderBy('sorting')
613 ->execute();
614 $availableTranslations = [];
615 while ($row = $statement->fetch()) {
616 if ($this->getBackendUserAuthentication()->checkLanguageAccess($row['uid'])) {
617 $availableTranslations[(int)$row['uid']] = $row['title'];
618 }
619 }
620 // Then, subtract the languages which are already on the page:
621 $localizationParentField = $GLOBALS['TCA']['pages']['ctrl']['transOrigPointerField'];
622 $languageField = $GLOBALS['TCA']['pages']['ctrl']['languageField'];
623 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_language');
624 $queryBuilder->getRestrictions()->removeAll();
625 $queryBuilder->select('sys_language.uid AS uid', 'sys_language.title AS title')
626 ->from('sys_language')
627 ->join(
628 'sys_language',
629 'pages',
630 'pages',
631 $queryBuilder->expr()->eq('sys_language.uid', $queryBuilder->quoteIdentifier('pages.' . $languageField))
632 )
633 ->where(
634 $queryBuilder->expr()->eq(
635 'pages.deleted',
636 $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
637 ),
638 $queryBuilder->expr()->eq(
639 'pages.' . $localizationParentField,
640 $queryBuilder->createNamedParameter($this->id, \PDO::PARAM_INT)
641 ),
642 $queryBuilder->expr()->orX(
643 $queryBuilder->expr()->gte(
644 'pages.t3ver_state',
645 $queryBuilder->createNamedParameter(
646 (string)new VersionState(VersionState::DEFAULT_STATE),
647 \PDO::PARAM_INT
648 )
649 ),
650 $queryBuilder->expr()->eq(
651 'pages.t3ver_wsid',
652 $queryBuilder->createNamedParameter($this->getBackendUserAuthentication()->workspace, \PDO::PARAM_INT)
653 )
654 )
655 )
656 ->groupBy(
657 'pages.' . $languageField,
658 'sys_language.uid',
659 'sys_language.pid',
660 'sys_language.tstamp',
661 'sys_language.hidden',
662 'sys_language.title',
663 'sys_language.language_isocode',
664 'sys_language.static_lang_isocode',
665 'sys_language.flag',
666 'sys_language.sorting'
667 )
668 ->orderBy('sys_language.sorting');
669 if (!$this->getBackendUserAuthentication()->isAdmin()) {
670 $queryBuilder->andWhere(
671 $queryBuilder->expr()->eq(
672 'sys_language.hidden',
673 $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
674 )
675 );
676 }
677 $statement = $queryBuilder->execute();
678 while ($row = $statement->fetch()) {
679 unset($availableTranslations[(int)$row['uid']]);
680 }
681 // Remove disallowed languages
682 if (!empty($availableTranslations)
683 && !$this->getBackendUserAuthentication()->isAdmin()
684 && $this->getBackendUserAuthentication()->groupData['allowed_languages'] !== ''
685 ) {
686 $allowed_languages = array_flip(explode(',', $this->getBackendUserAuthentication()->groupData['allowed_languages']));
687 if (!empty($allowed_languages)) {
688 foreach ($availableTranslations as $key => $value) {
689 if (!isset($allowed_languages[$key]) && $key != 0) {
690 unset($availableTranslations[$key]);
691 }
692 }
693 }
694 }
695 // Remove disabled languages
696 $modSharedTSconfig = BackendUtility::getModTSconfig($id, 'mod.SHARED');
697 $disableLanguages = isset($modSharedTSconfig['properties']['disableLanguages'])
698 ? GeneralUtility::trimExplode(',', $modSharedTSconfig['properties']['disableLanguages'], 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 $redirectUrl = BackendUtility::getModuleUrl('record_edit', $parameters);
719 $targetUrl = BackendUtility::getLinkToDataHandlerAction(
720 '&cmd[pages][' . $id . '][localize]=' . $languageUid,
721 $redirectUrl
722 );
723
724 $output .= '<option value="' . htmlspecialchars($targetUrl) . '">' . htmlspecialchars($languageTitle) . '</option>';
725 }
726
727 return '<div class="form-inline form-inline-spaced">'
728 . '<div class="form-group">'
729 . '<select class="form-control input-sm" name="createNewLanguage" onchange="window.location.href=this.options[this.selectedIndex].value">'
730 . $output
731 . '</select></div></div>';
732 }
733 }
734 return '';
735 }
736
737 /**
738 * @return ModuleTemplate
739 */
740 public function getModuleTemplate()
741 {
742 return $this->moduleTemplate;
743 }
744
745 /**
746 * @return BackendUserAuthentication
747 */
748 protected function getBackendUserAuthentication()
749 {
750 return $GLOBALS['BE_USER'];
751 }
752
753 /**
754 * @return LanguageService
755 */
756 protected function getLanguageService()
757 {
758 return $GLOBALS['LANG'];
759 }
760
761 /**
762 * @return PageRenderer
763 */
764 protected function getPageRenderer()
765 {
766 if ($this->pageRenderer === null) {
767 $this->pageRenderer = GeneralUtility::makeInstance(PageRenderer::class);
768 }
769
770 return $this->pageRenderer;
771 }
772 }