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