[TASK] Replace sprite icon "actions-document-history-open" with the new IconFactory
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Classes / Controller / PageLayoutController.php
1 <?php
2 namespace TYPO3\CMS\Backend\Controller;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use TYPO3\CMS\Backend\Utility\BackendUtility;
18 use TYPO3\CMS\Backend\Utility\IconUtility;
19 use TYPO3\CMS\Core\Imaging\Icon;
20 use TYPO3\CMS\Core\Imaging\IconFactory;
21 use TYPO3\CMS\Core\Type\Bitmask\Permission;
22 use TYPO3\CMS\Core\Messaging\FlashMessage;
23 use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
24 use TYPO3\CMS\Core\Utility\GeneralUtility;
25 use TYPO3\CMS\Core\Utility\HttpUtility;
26 use TYPO3\CMS\Core\Utility\MathUtility;
27 use TYPO3\CMS\Fluid\View\StandaloneView;
28 use TYPO3\CMS\Fluid\ViewHelpers\Be\InfoboxViewHelper;
29 use TYPO3\CMS\Recordlist\RecordList;
30 use TYPO3\CMS\Core\DataHandling\DataHandler;
31 use TYPO3\CMS\Frontend\Page\PageRepository;
32 use TYPO3\CMS\Backend\Module\ModuleLoader;
33 use TYPO3\CMS\Backend\Template\DocumentTemplate;
34 use TYPO3\CMS\Backend\View\BackendLayoutView;
35 use TYPO3\CMS\Backend\Form\DataPreprocessor;
36 use TYPO3\CMS\Backend\Form\FormEngine;
37 use TYPO3\CMS\Core\Messaging\FlashMessageService;
38 use TYPO3\CMS\Backend\View\PageLayoutView;
39 use TYPO3\CMS\Backend\Tree\View\ContentLayoutPagePositionMap;
40 use TYPO3\CMS\Core\Versioning\VersionState;
41
42 /**
43 * Script Class for Web > Layout module
44 */
45 class PageLayoutController {
46
47 /**
48 * Page Id for which to make the listing
49 *
50 * @var int
51 */
52 public $id;
53
54 /**
55 * Pointer - for browsing list of records.
56 *
57 * @var int
58 */
59 public $pointer;
60
61 /**
62 * Thumbnails or not
63 *
64 * @var string
65 */
66 public $imagemode;
67
68 /**
69 * Search-fields
70 *
71 * @var string
72 */
73 public $search_field;
74
75 /**
76 * Search-levels
77 *
78 * @var int
79 */
80 public $search_levels;
81
82 /**
83 * Show-limit
84 *
85 * @var int
86 */
87 public $showLimit;
88
89 /**
90 * Return URL
91 *
92 * @var string
93 */
94 public $returnUrl;
95
96 /**
97 * Clear-cache flag - if set, clears page cache for current id.
98 *
99 * @var bool
100 */
101 public $clear_cache;
102
103 /**
104 * PopView id - for opening a window with the page
105 *
106 * @var bool
107 */
108 public $popView;
109
110 /**
111 * QuickEdit: Variable, that tells quick edit what to show/edit etc.
112 * Format is [tablename]:[uid] with some exceptional values for both parameters (with special meanings).
113 *
114 * @var string
115 */
116 public $edit_record;
117
118 /**
119 * QuickEdit: If set, this variable tells quick edit that the last edited record had
120 * this value as UID and we should look up the new, real uid value in sys_log.
121 *
122 * @var string
123 */
124 public $new_unique_uid;
125
126 /**
127 * Page select perms clause
128 *
129 * @var string
130 */
131 public $perms_clause;
132
133 /**
134 * Module TSconfig
135 *
136 * @var array
137 */
138 public $modTSconfig;
139
140 /**
141 * Module shared TSconfig
142 *
143 * @var array
144 */
145 public $modSharedTSconfig;
146
147 /**
148 * Current ids page record
149 *
150 * @var array
151 */
152 public $pageinfo;
153
154 /**
155 * Document template object
156 *
157 * @var \TYPO3\CMS\Backend\Template\DocumentTemplate
158 */
159 public $doc;
160
161 /**
162 * "Pseudo" Description -table name
163 *
164 * @var string
165 */
166 public $descrTable;
167
168 /**
169 * List of column-integers to edit. Is set from TSconfig, default is "1,0,2,3"
170 *
171 * @var string
172 */
173 public $colPosList;
174
175 /**
176 * Flag: If content can be edited or not.
177 *
178 * @var bool
179 */
180 public $EDIT_CONTENT;
181
182 /**
183 * Users permissions integer for this page.
184 *
185 * @var int
186 */
187 public $CALC_PERMS;
188
189 /**
190 * Currently selected language for editing content elements
191 *
192 * @var int
193 */
194 public $current_sys_language;
195
196 /**
197 * Module configuration
198 *
199 * @var array
200 */
201 public $MCONF = array();
202
203 /**
204 * Menu configuration
205 *
206 * @var array
207 */
208 public $MOD_MENU = array();
209
210 /**
211 * Module settings (session variable)
212 *
213 * @var array
214 */
215 public $MOD_SETTINGS = array();
216
217 /**
218 * Array of tables to be listed by the Web > Page module in addition to the default tables
219 *
220 * @var array
221 */
222 public $externalTables = array();
223
224 /**
225 * Module output accumulation
226 *
227 * @var string
228 */
229 public $content;
230
231 /**
232 * Function menu temporary storage
233 *
234 * @var string
235 */
236 public $topFuncMenu;
237
238 /**
239 * List of column-integers accessible to the current BE user.
240 * Is set from TSconfig, default is $colPosList
241 *
242 * @var string
243 */
244 public $activeColPosList;
245
246 /**
247 * Markers array
248 *
249 * @var array
250 */
251 protected $markers = array();
252
253 /**
254 * @var array
255 */
256 protected $eRParts = array();
257
258 /**
259 * @var string
260 */
261 protected $editSelect;
262
263 /**
264 * @var bool
265 */
266 protected $deleteButton;
267
268 /**
269 * @var bool
270 */
271 protected $undoButton;
272
273 /**
274 * @var array
275 */
276 protected $undoButtonR;
277
278 /**
279 * @var string
280 */
281 protected $R_URI;
282
283 /**
284 * @var string
285 */
286 protected $closeUrl;
287
288 /**
289 * Caches the available languages in a colPos
290 *
291 * @var array
292 */
293 protected $languagesInColumnCache = array();
294
295 /**
296 * Caches the amount of content elements as a matrix
297 *
298 * @var array
299 * @internal
300 */
301 public $contentElementCache = array();
302
303 /**
304 * @var IconFactory
305 */
306 protected $iconFactory;
307
308 /**
309 * Initializing the module
310 *
311 * @return void
312 */
313 public function init() {
314 $this->getLanguageService()->includeLLFile('EXT:backend/Resources/Private/Language/locallang_layout.xlf');
315
316 // Setting module configuration / page select clause
317 $this->MCONF = $GLOBALS['MCONF'];
318 $this->perms_clause = $this->getBackendUser()->getPagePermsClause(1);
319 // Get session data
320 $sessionData = $this->getBackendUser()->getSessionData(RecordList::class);
321 $this->search_field = !empty($sessionData['search_field']) ? $sessionData['search_field'] : '';
322 // GPvars:
323 $this->id = (int)GeneralUtility::_GP('id');
324 $this->pointer = GeneralUtility::_GP('pointer');
325 $this->imagemode = GeneralUtility::_GP('imagemode');
326 $this->clear_cache = GeneralUtility::_GP('clear_cache');
327 $this->popView = GeneralUtility::_GP('popView');
328 $this->edit_record = GeneralUtility::_GP('edit_record');
329 $this->new_unique_uid = GeneralUtility::_GP('new_unique_uid');
330 $this->search_field = GeneralUtility::_GP('search_field');
331 $this->search_levels = GeneralUtility::_GP('search_levels');
332 $this->showLimit = GeneralUtility::_GP('showLimit');
333 $this->returnUrl = GeneralUtility::sanitizeLocalUrl(GeneralUtility::_GP('returnUrl'));
334 $this->externalTables = $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['cms']['db_layout']['addTables'];
335 $sessionData['search_field'] = $this->search_field;
336 // Store session data
337 $this->getBackendUser()->setAndSaveSessionData(RecordList::class, $sessionData);
338 // Load page info array:
339 $this->pageinfo = BackendUtility::readPageAccess($this->id, $this->perms_clause);
340 // Initialize menu
341 $this->menuConfig();
342 // Setting sys language from session var:
343 $this->current_sys_language = (int)$this->MOD_SETTINGS['language'];
344 // CSH / Descriptions:
345 $this->descrTable = '_MOD_' . $this->MCONF['name'];
346
347 $this->markers['SEARCHBOX'] = '';
348 $this->markers['BUTTONLIST_ADDITIONAL'] = '';
349 }
350
351 /**
352 * Initialize menu array
353 *
354 * @return void
355 */
356 public function menuConfig() {
357 $lang = $this->getLanguageService();
358 // MENU-ITEMS:
359 $this->MOD_MENU = array(
360 'tt_content_showHidden' => '',
361 'function' => array(
362 0 => $lang->getLL('m_function_0'),
363 1 => $lang->getLL('m_function_1'),
364 2 => $lang->getLL('m_function_2')
365 ),
366 'language' => array(
367 0 => $lang->getLL('m_default')
368 )
369 );
370 // example settings:
371 // $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['cms']['db_layout']['addTables']['tx_myext'] =
372 // array ('default' => array(
373 // 'MENU' => 'LLL:EXT:tx_myext/locallang_db.xlf:menuDefault',
374 // 'fList' => 'title,description,image',
375 // 'icon' => TRUE));
376 if (is_array($this->externalTables)) {
377 foreach ($this->externalTables as $table => $tableSettings) {
378 // delete the default settings from above
379 if (is_array($this->MOD_MENU[$table])) {
380 unset($this->MOD_MENU[$table]);
381 }
382 if (is_array($tableSettings) && count($tableSettings) > 1) {
383 foreach ($tableSettings as $key => $settings) {
384 $this->MOD_MENU[$table][$key] = $lang->sL($settings['MENU']);
385 }
386 }
387 }
388 }
389 // First, select all pages_language_overlay records on the current page. Each represents a possibility for a language on the page. Add these to language selector.
390 $res = $this->exec_languageQuery($this->id);
391 while ($lRow = $this->getDatabaseConnection()->sql_fetch_assoc($res)) {
392 if ($this->getBackendUser()->checkLanguageAccess($lRow['uid'])) {
393 $this->MOD_MENU['language'][$lRow['uid']] = $lRow['hidden'] ? '(' . $lRow['title'] . ')' : $lRow['title'];
394 }
395 }
396 // Find if there are ANY languages at all (and if not, remove the language option from function menu).
397 $count = $this->getDatabaseConnection()->exec_SELECTcountRows('uid', 'sys_language', $this->getBackendUser()->isAdmin() ? '' : 'hidden=0');
398 if (!$count) {
399 unset($this->MOD_MENU['function']['2']);
400 }
401 // page/be_user TSconfig settings and blinding of menu-items
402 $this->modSharedTSconfig = BackendUtility::getModTSconfig($this->id, 'mod.SHARED');
403 $this->modTSconfig = BackendUtility::getModTSconfig($this->id, 'mod.' . $this->MCONF['name']);
404 if ($this->modTSconfig['properties']['QEisDefault']) {
405 ksort($this->MOD_MENU['function']);
406 }
407 $this->MOD_MENU['function'] = BackendUtility::unsetMenuItems($this->modTSconfig['properties'], $this->MOD_MENU['function'], 'menu.function');
408 // Remove QuickEdit as option if page type is not...
409 if (!GeneralUtility::inList($GLOBALS['TYPO3_CONF_VARS']['FE']['content_doktypes'] . ',6', $this->pageinfo['doktype'])) {
410 unset($this->MOD_MENU['function'][0]);
411 }
412 // Setting alternative default label:
413 if (($this->modSharedTSconfig['properties']['defaultLanguageLabel'] || $this->modTSconfig['properties']['defaultLanguageLabel']) && isset($this->MOD_MENU['language'][0])) {
414 $this->MOD_MENU['language'][0] = $this->modTSconfig['properties']['defaultLanguageLabel'] ? $this->modSharedTSconfig['properties']['defaultLanguageLabel'] : $this->modSharedTSconfig['properties']['defaultLanguageLabel'];
415 }
416 // Clean up settings
417 $this->MOD_SETTINGS = BackendUtility::getModuleData($this->MOD_MENU, GeneralUtility::_GP('SET'), 'web_layout');
418 // For all elements to be shown in draft workspaces & to also show hidden elements by default if user hasn't disabled the option
419 if ($this->getBackendUser()->workspace != 0 || $this->MOD_SETTINGS['tt_content_showHidden'] !== '0') {
420 $this->MOD_SETTINGS['tt_content_showHidden'] = 1;
421 }
422 }
423
424 /**
425 * Clears page cache for the current id, $this->id
426 *
427 * @return void
428 */
429 public function clearCache() {
430 if ($this->clear_cache) {
431 $tce = GeneralUtility::makeInstance(DataHandler::class);
432 $tce->stripslashes_values = FALSE;
433 $tce->start(array(), array());
434 $tce->clear_cacheCmd($this->id);
435 }
436 }
437
438 /**
439 * Generate the flashmessages for current pid
440 *
441 * @return string HTML content with flashmessages
442 */
443 protected function getHeaderFlashMessagesForCurrentPid() {
444 $content = '';
445 $lang = $this->getLanguageService();
446 // If page is a folder
447 if ($this->pageinfo['doktype'] == PageRepository::DOKTYPE_SYSFOLDER) {
448 // Access to list module
449 $moduleLoader = GeneralUtility::makeInstance(ModuleLoader::class);
450 $moduleLoader->load($GLOBALS['TBE_MODULES']);
451 $modules = $moduleLoader->modules;
452 if (is_array($modules['web']['sub']['list'])) {
453 $title = $lang->getLL('goToListModule');
454 $message = '<p>' . $lang->getLL('goToListModuleMessage') . '</p>';
455 $message .= '<a class="btn btn-info" href="javascript:top.goToModule(\'web_list\',1);">' . $lang->getLL('goToListModule') . '</a>';
456
457 $view = GeneralUtility::makeInstance(StandaloneView::class);
458 $view->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName('EXT:backend/Resources/Private/Templates/InfoBox.html'));
459 $view->assignMultiple(array(
460 'title' => $title,
461 'message' => $message,
462 'state' => InfoboxViewHelper::STATE_INFO
463 ));
464 $content .= $view->render();
465 }
466 }
467 // If content from different pid is displayed
468 if ($this->pageinfo['content_from_pid']) {
469 $contentPage = BackendUtility::getRecord('pages', (int)$this->pageinfo['content_from_pid']);
470 $title = BackendUtility::getRecordTitle('pages', $contentPage);
471 $linkToPid = $this->local_linkThisScript(array('id' => $this->pageinfo['content_from_pid']));
472 $link = '<a href="' . $linkToPid . '">' . htmlspecialchars($title) . ' (PID ' . (int)$this->pageinfo['content_from_pid'] . ')</a>';
473 $flashMessage = GeneralUtility::makeInstance(FlashMessage::class, sprintf($lang->getLL('content_from_pid_title'), $link), '', FlashMessage::INFO);
474 $content .= $flashMessage->render();
475 }
476 return $content;
477 }
478
479 /**
480 *
481 * @return string $title
482 */
483 protected function getLocalizedPageTitle() {
484 if ($this->current_sys_language > 0) {
485 $overlayRecord = $this->getDatabaseConnection()->exec_SELECTgetSingleRow(
486 'title',
487 'pages_language_overlay',
488 'pid = ' . (int)$this->id .
489 ' AND sys_language_uid = ' . (int)$this->current_sys_language .
490 BackendUtility::deleteClause('pages_language_overlay') .
491 BackendUtility::versioningPlaceholderClause('pages_language_overlay'),
492 '',
493 '',
494 '',
495 'sys_language_uid'
496 );
497 return $overlayRecord['title'];
498 } else {
499 return $this->pageinfo['title'];
500 }
501 }
502
503 /**
504 * Main function.
505 * Creates some general objects and calls other functions for the main rendering of module content.
506 *
507 * @return void
508 */
509 public function main() {
510 $lang = $this->getLanguageService();
511 // Access check...
512 // The page will show only if there is a valid page and if this page may be viewed by the user
513 $access = is_array($this->pageinfo) ? 1 : 0;
514 if ($this->id && $access) {
515 // Initialize permission settings:
516 $this->CALC_PERMS = $this->getBackendUser()->calcPerms($this->pageinfo);
517 $this->EDIT_CONTENT = $this->pageIsNotLockedForEditors();
518
519 // Start document template object:
520 $this->doc = GeneralUtility::makeInstance(DocumentTemplate::class);
521 $this->doc->setModuleTemplate('EXT:backend/Resources/Private/Templates/db_layout.html');
522
523 // override the default jumpToUrl
524 $this->doc->JScodeArray['jumpToUrl'] = '
525 function jumpToUrl(URL,formEl) {
526 if (document.editform && TBE_EDITOR.isFormChanged) { // Check if the function exists... (works in all browsers?)
527 if (!TBE_EDITOR.isFormChanged()) {
528 window.location.href = URL;
529 } else if (formEl) {
530 if (formEl.type=="checkbox") formEl.checked = formEl.checked ? 0 : 1;
531 }
532 } else {
533 window.location.href = URL;
534 }
535 }
536 ';
537
538 $this->doc->JScode .= $this->doc->wrapScriptTags('
539 if (top.fsMod) top.fsMod.recentIds["web"] = ' . (int)$this->id . ';
540 if (top.fsMod) top.fsMod.navFrameHighlightedID["web"] = "pages' . (int)$this->id . '_"+top.fsMod.currentBank; ' . (int)$this->id . ';
541 ' . ($this->popView ? BackendUtility::viewOnClick($this->id, '', BackendUtility::BEgetRootLine($this->id)) : '') . '
542
543 function deleteRecord(table,id,url) { //
544 if (confirm(' . GeneralUtility::quoteJSvalue($lang->getLL('deleteWarning')) . ')) {
545 window.location.href = ' . GeneralUtility::quoteJSvalue(BackendUtility::getModuleUrl('tce_db') . '&cmd[') . '+table+"]["+id+"][delete]=1&redirect="+escape(url)+"&vC=' . $this->getBackendUser()->veriCode() . BackendUtility::getUrlToken('tceAction') . '&prErr=1&uPT=1";
546 }
547 return false;
548 }
549 ');
550 $this->doc->JScode .= $this->doc->wrapScriptTags('
551 var DTM_array = new Array();
552 var DTM_origClass = new String();
553
554 // if tabs are used in a popup window the array might not exists
555 if(!top.DTM_currentTabs) {
556 top.DTM_currentTabs = new Array();
557 }
558
559 function DTM_activate(idBase,index,doToogle) { //
560 // Hiding all:
561 if (DTM_array[idBase]) {
562 for(cnt = 0; cnt < DTM_array[idBase].length ; cnt++) {
563 if (DTM_array[idBase][cnt] != idBase+"-"+index) {
564 document.getElementById(DTM_array[idBase][cnt]+"-DIV").className = "tab-pane";
565 document.getElementById(DTM_array[idBase][cnt]+"-MENU").attributes.getNamedItem("class").value = "tab";
566 }
567 }
568 }
569
570 // Showing one:
571 if (document.getElementById(idBase+"-"+index+"-DIV")) {
572 if (doToogle && document.getElementById(idBase+"-"+index+"-DIV").className === "tab-pane active") {
573 document.getElementById(idBase+"-"+index+"-DIV").className = "tab-pane";
574 if(DTM_origClass=="") {
575 document.getElementById(idBase+"-"+index+"-MENU").attributes.getNamedItem("class").value = "tab";
576 } else {
577 DTM_origClass = "tab";
578 }
579 top.DTM_currentTabs[idBase] = -1;
580 } else {
581 document.getElementById(idBase+"-"+index+"-DIV").className = "tab-pane active";
582 if(DTM_origClass=="") {
583 document.getElementById(idBase+"-"+index+"-MENU").attributes.getNamedItem("class").value = "active";
584 } else {
585 DTM_origClass = "active";
586 }
587 top.DTM_currentTabs[idBase] = index;
588 }
589 }
590 }
591 function DTM_toggle(idBase,index,isInit) { //
592 // Showing one:
593 if (document.getElementById(idBase+"-"+index+"-DIV")) {
594 if (document.getElementById(idBase+"-"+index+"-DIV").style.display == "block") {
595 document.getElementById(idBase+"-"+index+"-DIV").className = "tab-pane";
596 if(isInit) {
597 document.getElementById(idBase+"-"+index+"-MENU").attributes.getNamedItem("class").value = "tab";
598 } else {
599 DTM_origClass = "tab";
600 }
601 top.DTM_currentTabs[idBase+"-"+index] = 0;
602 } else {
603 document.getElementById(idBase+"-"+index+"-DIV").className = "tab-pane active";
604 if(isInit) {
605 document.getElementById(idBase+"-"+index+"-MENU").attributes.getNamedItem("class").value = "active";
606 } else {
607 DTM_origClass = "active";
608 }
609 top.DTM_currentTabs[idBase+"-"+index] = 1;
610 }
611 }
612 }
613 ');
614 // Setting doc-header
615 $this->doc->form = '<form action="' . htmlspecialchars(
616 BackendUtility::getModuleUrl(
617 'web_layout', array('id' => $this->id, 'imagemode' => $this->imagemode)
618 )) . '" method="post">';
619 // Creating the top function menu:
620 $this->topFuncMenu = BackendUtility::getFuncMenu($this->id, 'SET[function]', $this->MOD_SETTINGS['function'], $this->MOD_MENU['function'], '', '');
621 $languageMenu = count($this->MOD_MENU['language']) > 1 ? $lang->sL('LLL:EXT:lang/locallang_general.xlf:LGL.language', TRUE) . BackendUtility::getFuncMenu($this->id, 'SET[language]', $this->current_sys_language, $this->MOD_MENU['language'], '', '') : '';
622 // Find backend layout / coumns
623 $backendLayout = GeneralUtility::callUserFunction(BackendLayoutView::class . '->getSelectedBackendLayout', $this->id, $this);
624 if (!empty($backendLayout['__colPosList'])) {
625 $this->colPosList = implode(',', $backendLayout['__colPosList']);
626 }
627 // Removing duplicates, if any
628 $this->colPosList = array_unique(GeneralUtility::intExplode(',', $this->colPosList));
629 // Accessible columns
630 if (isset($this->modSharedTSconfig['properties']['colPos_list']) && trim($this->modSharedTSconfig['properties']['colPos_list']) !== '') {
631 $this->activeColPosList = array_unique(GeneralUtility::intExplode(',', trim($this->modSharedTSconfig['properties']['colPos_list'])));
632 // Match with the list which is present in the colPosList for the current page
633 if (!empty($this->colPosList) && !empty($this->activeColPosList)) {
634 $this->activeColPosList = array_unique(array_intersect(
635 $this->activeColPosList,
636 $this->colPosList
637 ));
638 }
639 } else {
640 $this->activeColPosList = $this->colPosList;
641 }
642 $this->activeColPosList = implode(',', $this->activeColPosList);
643 $this->colPosList = implode(',', $this->colPosList);
644
645 $body = '';
646 $body .= $this->getHeaderFlashMessagesForCurrentPid();
647 // Render the primary module content:
648 if ($this->MOD_SETTINGS['function'] == 0) {
649 // QuickEdit
650 $body .= $this->renderQuickEdit();
651 } else {
652 // Page title
653 $body .= $this->doc->header($this->getLocalizedPageTitle());
654 // All other listings
655 $body .= $this->renderListContent();
656 }
657 // Setting up the buttons and markers for docheader
658 $docHeaderButtons = $this->getButtons($this->MOD_SETTINGS['function'] == 0 ? 'quickEdit' : '');
659 $this->markers['CSH'] = $docHeaderButtons['csh'];
660 $this->markers['TOP_FUNCTION_MENU'] = $this->topFuncMenu . $this->editSelect;
661 $this->markers['LANGSELECTOR'] = $languageMenu;
662 $this->markers['CONTENT'] = $body;
663 // Build the <body> for the module
664 $this->content .= $this->doc->moduleBody($this->pageinfo, $docHeaderButtons, $this->markers);
665 // Renders the module page
666 $this->content = $this->doc->render($lang->getLL('title'), $this->content);
667 } else {
668 // If no access or id value, create empty document:
669 $this->doc = GeneralUtility::makeInstance(DocumentTemplate::class);
670 $this->doc->setModuleTemplate('EXT:backend/Resources/Private/Templates/db_layout.html');
671 $this->doc->JScode = $this->doc->wrapScriptTags('
672 if (top.fsMod) top.fsMod.recentIds["web"] = ' . (int)$this->id . ';
673 ');
674
675 $body = $this->doc->header($GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename']);
676
677 $title = $lang->getLL('clickAPage_header');
678 $message = $lang->getLL('clickAPage_content');
679
680 $view = GeneralUtility::makeInstance(StandaloneView::class);
681 $view->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName('EXT:backend/Resources/Private/Templates/InfoBox.html'));
682 $view->assignMultiple(array(
683 'title' => $title,
684 'message' => $message,
685 'state' => InfoboxViewHelper::STATE_INFO
686 ));
687 $body .= $view->render();
688
689 // Setting up the buttons and markers for docheader
690 $docHeaderButtons = array(
691 'view' => '',
692 'history_page' => '',
693 'new_content' => '',
694 'move_page' => '',
695 'move_record' => '',
696 'new_page' => '',
697 'edit_page' => '',
698 'csh' => '',
699 'shortcut' => '',
700 'cache' => '',
701 'savedok' => '',
702 'savedokshow' => '',
703 'closedok' => '',
704 'deletedok' => '',
705 'undo' => '',
706 'history_record' => '',
707 'edit_language' => ''
708 );
709 $this->markers['CSH'] = '';
710 $this->markers['TOP_FUNCTION_MENU'] = '';
711 $this->markers['LANGSELECTOR'] = '';
712 $this->markers['CONTENT'] = $body;
713 $this->content .= $this->doc->moduleBody($this->pageinfo, $docHeaderButtons, $this->markers);
714 // Renders the module page
715 $this->content = $this->doc->render($lang->getLL('title'), $this->content);
716 }
717 }
718
719 /**
720 * Rendering the quick-edit view.
721 *
722 * @return string
723 */
724 public function renderQuickEdit() {
725 $databaseConnection = $this->getDatabaseConnection();
726 $beUser = $this->getBackendUser();
727 $lang = $this->getLanguageService();
728 // Alternative template
729 $this->doc->setModuleTemplate('EXT:backend/Resources/Private/Templates/db_layout_quickedit.html');
730 // Alternative form tag; Quick Edit submits its content to tce_db.php.
731 $this->doc->form = '<form action="' . htmlspecialchars(BackendUtility::getModuleUrl('tce_db', ['prErr' => 1, 'uPT' => 1])) . '" method="post" enctype="multipart/form-data" name="editform" onsubmit="return TBE_EDITOR.checkSubmit(1);">';
732 // Setting up the context sensitive menu:
733 $this->doc->getContextMenuCode();
734 // Set the edit_record value for internal use in this function:
735 $edit_record = $this->edit_record;
736 // If a command to edit all records in a column is issue, then select all those elements, and redirect to FormEngine
737 if (substr($edit_record, 0, 9) == '_EDIT_COL') {
738 $res = $databaseConnection->exec_SELECTquery('*', 'tt_content', 'pid=' . (int)$this->id . ' AND colPos=' . (int)substr($edit_record, 10) . ' AND sys_language_uid=' . (int)$this->current_sys_language . ($this->MOD_SETTINGS['tt_content_showHidden'] ? '' : BackendUtility::BEenableFields('tt_content')) . BackendUtility::deleteClause('tt_content') . BackendUtility::versioningPlaceholderClause('tt_content'), '', 'sorting');
739 $idListA = array();
740 while ($cRow = $databaseConnection->sql_fetch_assoc($res)) {
741 $idListA[] = $cRow['uid'];
742 }
743 $url = BackendUtility::getModuleUrl('record_edit', array(
744 'edit[tt_content][' . implode(',', $idListA) . ']' => 'edit',
745 'returnUrl' => $this->local_linkThisScript(array('edit_record' => ''))
746 ));
747 HttpUtility::redirect($url);
748 }
749 // If the former record edited was the creation of a NEW record, this will look up the created records uid:
750 if ($this->new_unique_uid) {
751 $res = $databaseConnection->exec_SELECTquery('*', 'sys_log', 'userid=' . (int)$beUser->user['uid'] . ' AND NEWid=' . $databaseConnection->fullQuoteStr($this->new_unique_uid, 'sys_log'));
752 $sys_log_row = $databaseConnection->sql_fetch_assoc($res);
753 if (is_array($sys_log_row)) {
754 $edit_record = $sys_log_row['tablename'] . ':' . $sys_log_row['recuid'];
755 }
756 }
757 // Creating the selector box, allowing the user to select which element to edit:
758 $opt = array();
759 $is_selected = 0;
760 $languageOverlayRecord = '';
761 if ($this->current_sys_language) {
762 list($languageOverlayRecord) = BackendUtility::getRecordsByField('pages_language_overlay', 'pid', $this->id, 'AND sys_language_uid=' . (int)$this->current_sys_language);
763 }
764 if (is_array($languageOverlayRecord)) {
765 $inValue = 'pages_language_overlay:' . $languageOverlayRecord['uid'];
766 $is_selected += (int)$edit_record == $inValue;
767 $opt[] = '<option value="' . $inValue . '"' . ($edit_record == $inValue ? ' selected="selected"' : '') . '>[ ' . $lang->getLL('editLanguageHeader', TRUE) . ' ]</option>';
768 } else {
769 $inValue = 'pages:' . $this->id;
770 $is_selected += (int)$edit_record == $inValue;
771 $opt[] = '<option value="' . $inValue . '"' . ($edit_record == $inValue ? ' selected="selected"' : '') . '>[ ' . $lang->getLL('editPageProperties', TRUE) . ' ]</option>';
772 }
773 // Selecting all content elements from this language and allowed colPos:
774 $whereClause = 'pid=' . (int)$this->id . ' AND sys_language_uid=' . (int)$this->current_sys_language . ' AND colPos IN (' . $this->colPosList . ')' . ($this->MOD_SETTINGS['tt_content_showHidden'] ? '' : BackendUtility::BEenableFields('tt_content')) . BackendUtility::deleteClause('tt_content') . BackendUtility::versioningPlaceholderClause('tt_content');
775 if (!$this->getBackendUser()->user['admin']) {
776 $whereClause .= ' AND editlock = 0';
777 }
778 $res = $databaseConnection->exec_SELECTquery('*', 'tt_content', $whereClause, '', 'colPos,sorting');
779 $colPos = NULL;
780 $first = 1;
781 // Page is the pid if no record to put this after.
782 $prev = $this->id;
783 while ($cRow = $databaseConnection->sql_fetch_assoc($res)) {
784 BackendUtility::workspaceOL('tt_content', $cRow);
785 if (is_array($cRow)) {
786 if ($first) {
787 if (!$edit_record) {
788 $edit_record = 'tt_content:' . $cRow['uid'];
789 }
790 $first = 0;
791 }
792 if (!isset($colPos) || $cRow['colPos'] !== $colPos) {
793 $colPos = $cRow['colPos'];
794 $opt[] = '<option value=""></option>';
795 $opt[] = '<option value="_EDIT_COL:' . $colPos . '">__' . $lang->sL(BackendUtility::getLabelFromItemlist('tt_content', 'colPos', $colPos), TRUE) . ':__</option>';
796 }
797 $inValue = 'tt_content:' . $cRow['uid'];
798 $is_selected += (int)$edit_record == $inValue;
799 $opt[] = '<option value="' . $inValue . '"' . ($edit_record == $inValue ? ' selected="selected"' : '') . '>' . htmlspecialchars(GeneralUtility::fixed_lgd_cs(($cRow['header'] ? $cRow['header'] : '[' . $lang->sL('LLL:EXT:lang/locallang_core.xlf:labels.no_title') . '] ' . strip_tags($cRow['bodytext'])), $beUser->uc['titleLen'])) . '</option>';
800 $prev = -$cRow['uid'];
801 }
802 }
803 // If edit_record is not set (meaning, no content elements was found for this language) we simply set it to create a new element:
804 if (!$edit_record) {
805 $edit_record = 'tt_content:new/' . $prev . '/' . $colPos;
806 $inValue = 'tt_content:new/' . $prev . '/' . $colPos;
807 $is_selected += (int)$edit_record == $inValue;
808 $opt[] = '<option value="' . $inValue . '"' . ($edit_record == $inValue ? ' selected="selected"' : '') . '>[ ' . $lang->getLL('newLabel', 1) . ' ]</option>';
809 }
810 // If none is yet selected...
811 if (!$is_selected) {
812 $opt[] = '<option value=""></option>';
813 $opt[] = '<option value="' . $edit_record . '" selected="selected">[ ' . $lang->getLL('newLabel', TRUE) . ' ]</option>';
814 }
815 // Splitting the edit-record cmd value into table/uid:
816 $this->eRParts = explode(':', $edit_record);
817 // Delete-button flag?
818 $this->deleteButton = MathUtility::canBeInterpretedAsInteger($this->eRParts[1]) && $edit_record && ($this->eRParts[0] != 'pages' && $this->EDIT_CONTENT || $this->eRParts[0] == 'pages' && $this->CALC_PERMS & Permission::PAGE_DELETE);
819 // If undo-button should be rendered (depends on available items in sys_history)
820 $this->undoButton = FALSE;
821 $undoRes = $databaseConnection->exec_SELECTquery('tstamp', 'sys_history', 'tablename=' . $databaseConnection->fullQuoteStr($this->eRParts[0], 'sys_history') . ' AND recuid=' . (int)$this->eRParts[1], '', 'tstamp DESC', '1');
822 if ($this->undoButtonR = $databaseConnection->sql_fetch_assoc($undoRes)) {
823 $this->undoButton = TRUE;
824 }
825 // Setting up the Return URL for coming back to THIS script (if links take the user to another script)
826 $R_URL_parts = parse_url(GeneralUtility::getIndpEnv('REQUEST_URI'));
827 $R_URL_getvars = GeneralUtility::_GET();
828 unset($R_URL_getvars['popView']);
829 unset($R_URL_getvars['new_unique_uid']);
830 $R_URL_getvars['edit_record'] = $edit_record;
831 $this->R_URI = $R_URL_parts['path'] . '?' . GeneralUtility::implodeArrayForUrl('', $R_URL_getvars);
832 // Setting close url/return url for exiting this script:
833 // Goes to 'Columns' view if close is pressed (default)
834 $this->closeUrl = $this->local_linkThisScript(array('SET' => array('function' => 1)));
835 if ($this->returnUrl) {
836 $this->closeUrl = $this->returnUrl;
837 }
838 // Return-url for JavaScript:
839 $retUrlStr = $this->returnUrl ? '+\'&returnUrl=\'+' . GeneralUtility::quoteJSvalue(rawurlencode($this->returnUrl)) : '';
840 // Drawing the edit record selectbox
841 $this->editSelect = '<select name="edit_record" onchange="' . htmlspecialchars('jumpToUrl(' . GeneralUtility::quoteJSvalue(
842 BackendUtility::getModuleUrl('web_layout') . '&id=' . $this->id . '&edit_record='
843 ) . '+escape(this.options[this.selectedIndex].value)' . $retUrlStr . ',this);') . '">' . implode('', $opt) . '</select>';
844 $content = '';
845 // Creating editing form:
846 if ($beUser->check('tables_modify', $this->eRParts[0]) && $edit_record && ($this->eRParts[0] !== 'pages' && $this->EDIT_CONTENT || $this->eRParts[0] === 'pages' && $this->CALC_PERMS & Permission::PAGE_SHOW)) {
847 // Splitting uid parts for special features, if new:
848 list($uidVal, $ex_pid, $ex_colPos) = explode('/', $this->eRParts[1]);
849 // Convert $uidVal to workspace version if any:
850 if ($uidVal != 'new') {
851 if ($draftRecord = BackendUtility::getWorkspaceVersionOfRecord($beUser->workspace, $this->eRParts[0], $uidVal, 'uid')) {
852 $uidVal = $draftRecord['uid'];
853 }
854 }
855 // Initializing transfer-data object:
856 $trData = GeneralUtility::makeInstance(DataPreprocessor::class);
857 $trData->addRawData = TRUE;
858 $trData->defVals[$this->eRParts[0]] = array(
859 'colPos' => (int)$ex_colPos,
860 'sys_language_uid' => (int)$this->current_sys_language
861 );
862 $trData->lockRecords = 1;
863 // 'new'
864 $trData->fetchRecord($this->eRParts[0], $uidVal == 'new' ? $this->id : $uidVal, $uidVal);
865 $new_unique_uid = '';
866 // Getting/Making the record:
867 reset($trData->regTableItems_data);
868 $rec = current($trData->regTableItems_data);
869 if ($uidVal == 'new') {
870 $new_unique_uid = uniqid('NEW', TRUE);
871 $rec['uid'] = $new_unique_uid;
872 $rec['pid'] = (int)$ex_pid ?: $this->id;
873 $recordAccess = TRUE;
874 } else {
875 $rec['uid'] = $uidVal;
876 // Checking internals access:
877 $recordAccess = $beUser->recordEditAccessInternals($this->eRParts[0], $uidVal);
878 }
879 if (!$recordAccess) {
880 // If no edit access, print error message:
881 $content = $this->doc->section($lang->getLL('noAccess'), $lang->getLL('noAccess_msg') . '<br /><br />' . ($beUser->errorMsg ? 'Reason: ' . $beUser->errorMsg . '<br /><br />' : ''), 0, 1);
882 } elseif (is_array($rec)) {
883 // If the record is an array (which it will always be... :-)
884 // Create instance of TCEforms, setting defaults:
885 $tceForms = GeneralUtility::makeInstance(FormEngine::class);
886 // Render form, wrap it:
887 $panel = '';
888 $panel .= $tceForms->getMainFields($this->eRParts[0], $rec);
889 $panel = $tceForms->wrapTotal($panel, $rec, $this->eRParts[0]);
890 // Add hidden fields:
891 $theCode = $panel;
892 if ($uidVal == 'new') {
893 $theCode .= '<input type="hidden" name="data[' . $this->eRParts[0] . '][' . $rec['uid'] . '][pid]" value="' . $rec['pid'] . '" />';
894 }
895 $theCode .= '
896 <input type="hidden" name="_serialNumber" value="' . md5(microtime()) . '" />
897 <input type="hidden" name="edit_record" value="' . $edit_record . '" />
898 <input type="hidden" name="redirect" value="' . htmlspecialchars(($uidVal == 'new' ? BackendUtility::getModuleUrl(
899 'web_layout',
900 array(
901 'id' => $this->id,
902 'new_unique_uid' => $new_unique_uid,
903 'returnUrl' => $this->returnUrl
904 )
905 ) : $this->R_URI)) . '" />
906 ' . FormEngine::getHiddenTokenField('tceAction');
907 // Add JavaScript as needed around the form:
908 $theCode = $tceForms->printNeededJSFunctions_top() . $theCode . $tceForms->printNeededJSFunctions();
909 // Add warning sign if record was "locked":
910 if ($lockInfo = BackendUtility::isRecordLocked($this->eRParts[0], $rec['uid'])) {
911 /** @var \TYPO3\CMS\Core\Messaging\FlashMessage $flashMessage */
912 $flashMessage = GeneralUtility::makeInstance(FlashMessage::class, htmlspecialchars($lockInfo['msg']), '', FlashMessage::WARNING);
913 /** @var $flashMessageService \TYPO3\CMS\Core\Messaging\FlashMessageService */
914 $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
915 /** @var $defaultFlashMessageQueue \TYPO3\CMS\Core\Messaging\FlashMessageQueue */
916 $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
917 $defaultFlashMessageQueue->enqueue($flashMessage);
918 }
919 // Add whole form as a document section:
920 $content = $this->doc->section('', $theCode);
921 }
922 } else {
923 // If no edit access, print error message:
924 $content = $this->doc->section($lang->getLL('noAccess'), $lang->getLL('noAccess_msg') . '<br /><br />', 0, 1);
925 }
926 // Bottom controls (function menus):
927 $q_count = $this->getNumberOfHiddenElements();
928 if ($q_count) {
929 $h_func_b = '<div class="checkbox">' .
930 '<label for="checkTt_content_showHidden">' .
931 BackendUtility::getFuncCheck($this->id, 'SET[tt_content_showHidden]', $this->MOD_SETTINGS['tt_content_showHidden'], '', '', 'id="checkTt_content_showHidden"') .
932 (!$q_count ? ('<span class="text-muted">' . $lang->getLL('hiddenCE', TRUE) . '</span>') : $lang->getLL('hiddenCE', TRUE) . ' (' . $q_count . ')') .
933 '</label>' .
934 '</div>';
935
936 $content .= $this->doc->section('', $h_func_b, 0, 0);
937 $content .= $this->doc->spacer(10);
938 }
939
940 // Select element matrix:
941 if ($this->eRParts[0] == 'tt_content' && MathUtility::canBeInterpretedAsInteger($this->eRParts[1])) {
942 $posMap = GeneralUtility::makeInstance(ContentLayoutPagePositionMap::class);
943 $posMap->cur_sys_language = $this->current_sys_language;
944 $HTMLcode = '';
945 // CSH:
946 $HTMLcode .= BackendUtility::cshItem($this->descrTable, 'quickEdit_selElement', NULL, '|<br />');
947 $HTMLcode .= $posMap->printContentElementColumns($this->id, $this->eRParts[1], $this->colPosList, $this->MOD_SETTINGS['tt_content_showHidden'], $this->R_URI);
948 $content .= $this->doc->spacer(20);
949 $content .= $this->doc->section($lang->getLL('CEonThisPage'), $HTMLcode, 0, 1);
950 $content .= $this->doc->spacer(20);
951 }
952 return $content;
953 }
954
955 /**
956 * Rendering all other listings than QuickEdit
957 *
958 * @return string
959 */
960 public function renderListContent() {
961 /** @var $dbList \TYPO3\CMS\Backend\View\PageLayoutView */
962 $dbList = GeneralUtility::makeInstance(PageLayoutView::class);
963 $dbList->thumbs = $this->imagemode;
964 $dbList->no_noWrap = 1;
965 $dbList->descrTable = $this->descrTable;
966 $this->pointer = MathUtility::forceIntegerInRange($this->pointer, 0, 100000);
967 $dbList->script = BackendUtility::getModuleUrl('web_layout');
968 $dbList->showIcon = 0;
969 $dbList->setLMargin = 0;
970 $dbList->doEdit = $this->EDIT_CONTENT;
971 $dbList->ext_CALC_PERMS = $this->CALC_PERMS;
972 $dbList->agePrefixes = $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:labels.minutesHoursDaysYears');
973 $dbList->id = $this->id;
974 $dbList->nextThree = MathUtility::forceIntegerInRange($this->modTSconfig['properties']['editFieldsAtATime'], 0, 10);
975 $dbList->option_newWizard = $this->modTSconfig['properties']['disableNewContentElementWizard'] ? 0 : 1;
976 $dbList->defLangBinding = $this->modTSconfig['properties']['defLangBinding'] ? 1 : 0;
977 if (!$dbList->nextThree) {
978 $dbList->nextThree = 1;
979 }
980 $dbList->externalTables = $this->externalTables;
981 // Create menu for selecting a table to jump to (this is, if more than just pages/tt_content elements are found on the page!)
982 // also fills $dbList->activeTables
983 $dbList->getTableMenu($this->id);
984 // Initialize other variables:
985 $h_func = '';
986 $tableOutput = array();
987 $tableJSOutput = array();
988 $CMcounter = 0;
989 // Traverse the list of table names which has records on this page (that array is populated
990 // by the $dblist object during the function getTableMenu()):
991 foreach ($dbList->activeTables as $table => $value) {
992 $h_func_b = '';
993 if (!isset($dbList->externalTables[$table])) {
994 $q_count = $this->getNumberOfHiddenElements();
995
996 if ($q_count > 0) {
997 $h_func_b =
998 '<div class="checkbox">'
999 . '<label for="checkTt_content_showHidden">'
1000 . '<input type="checkbox" id="checkTt_content_showHidden" class="checkbox" name="SET[tt_content_showHidden]" value="1" ' . ($this->MOD_SETTINGS['tt_content_showHidden'] ? 'checked="checked"' : '') . ' />'
1001 . $this->getLanguageService()->getLL('hiddenCE', TRUE) . ' (<span class="t3js-hidden-counter">' . $q_count . '</span>)'
1002 . '</label>'
1003 . '</div>';
1004 }
1005 // Boolean: Display up/down arrows and edit icons for tt_content records
1006 $dbList->tt_contentConfig['showCommands'] = 1;
1007 // Boolean: Display info-marks or not
1008 $dbList->tt_contentConfig['showInfo'] = 1;
1009 // Setting up the tt_content columns to show:
1010 if (is_array($GLOBALS['TCA']['tt_content']['columns']['colPos']['config']['items'])) {
1011 $colList = array();
1012 $tcaItems = GeneralUtility::callUserFunction(BackendLayoutView::class . '->getColPosListItemsParsed', $this->id, $this);
1013 foreach ($tcaItems as $temp) {
1014 $colList[] = $temp[1];
1015 }
1016 } else {
1017 // ... should be impossible that colPos has no array. But this is the fallback should it make any sense:
1018 $colList = array('1', '0', '2', '3');
1019 }
1020 if ($this->colPosList !== '') {
1021 $colList = array_intersect(GeneralUtility::intExplode(',', $this->colPosList), $colList);
1022 }
1023 // The order of the rows: Default is left(1), Normal(0), right(2), margin(3)
1024 $dbList->tt_contentConfig['cols'] = implode(',', $colList);
1025 $dbList->tt_contentConfig['activeCols'] = $this->activeColPosList;
1026 $dbList->tt_contentConfig['showHidden'] = $this->MOD_SETTINGS['tt_content_showHidden'];
1027 $dbList->tt_contentConfig['sys_language_uid'] = (int)$this->current_sys_language;
1028 // If the function menu is set to "Language":
1029 if ($this->MOD_SETTINGS['function'] == 2) {
1030 $dbList->tt_contentConfig['languageMode'] = 1;
1031 $dbList->tt_contentConfig['languageCols'] = $this->MOD_MENU['language'];
1032 $dbList->tt_contentConfig['languageColsPointer'] = $this->current_sys_language;
1033 }
1034 } else {
1035 if (isset($this->MOD_SETTINGS) && isset($this->MOD_MENU)) {
1036 $h_func = BackendUtility::getFuncMenu($this->id, 'SET[' . $table . ']', $this->MOD_SETTINGS[$table], $this->MOD_MENU[$table], '', '');
1037 } else {
1038 $h_func = '';
1039 }
1040 }
1041 // Start the dblist object:
1042 $dbList->itemsLimitSingleTable = 1000;
1043 $dbList->start($this->id, $table, $this->pointer, $this->search_field, $this->search_levels, $this->showLimit);
1044 $dbList->counter = $CMcounter;
1045 $dbList->ext_function = $this->MOD_SETTINGS['function'];
1046 // Render versioning selector:
1047 $dbList->HTMLcode .= $this->doc->getVersionSelector($this->id);
1048 // Generate the list of elements here:
1049 $dbList->generateList();
1050 // Adding the list content to the tableOutput variable:
1051 $tableOutput[$table] = ($h_func ? $h_func . '<br /><span style="width: 1px; height: 4px; display: inline-block;"></span><br />' : '') . $dbList->HTMLcode . ($h_func_b ? '<span style="width: 1px; height: 10px; display:inline-block;"></span><br />' . $h_func_b : '');
1052 // ... and any accumulated JavaScript goes the same way!
1053 $tableJSOutput[$table] = $dbList->JScode;
1054 // Increase global counter:
1055 $CMcounter += $dbList->counter;
1056 // Reset variables after operation:
1057 $dbList->HTMLcode = '';
1058 $dbList->JScode = '';
1059 $h_func = '';
1060 }
1061 // END: traverse tables
1062 // For Context Sensitive Menus:
1063 $this->doc->getContextMenuCode();
1064 // Init the content
1065 $content = '';
1066 // Additional header content
1067 $headerContentHook = $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['cms/layout/db_layout.php']['drawHeaderHook'];
1068 if (is_array($headerContentHook)) {
1069 foreach ($headerContentHook as $hook) {
1070 $params = array();
1071 $content .= GeneralUtility::callUserFunction($hook, $params, $this);
1072 }
1073 }
1074 // Add the content for each table we have rendered (traversing $tableOutput variable)
1075 foreach ($tableOutput as $table => $output) {
1076 $content .= $this->doc->section('', $output, TRUE, TRUE, 0, TRUE);
1077 $content .= $this->doc->sectionEnd();
1078 }
1079 // Making search form:
1080 if (!$this->modTSconfig['properties']['disableSearchBox'] && !empty($tableOutput)) {
1081 $this->markers['BUTTONLIST_ADDITIONAL'] = '<a href="#" onclick="toggleSearchToolbox(); return false;" title="' . $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:labels.title.searchIcon', TRUE) . '">'.IconUtility::getSpriteIcon('apps-toolbar-menu-search').'</a>';
1082 $this->markers['SEARCHBOX'] = $dbList->getSearchBox(0);
1083 }
1084 // Additional footer content
1085 $footerContentHook = $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['cms/layout/db_layout.php']['drawFooterHook'];
1086 if (is_array($footerContentHook)) {
1087 foreach ($footerContentHook as $hook) {
1088 $params = array();
1089 $content .= GeneralUtility::callUserFunction($hook, $params, $this);
1090 }
1091 }
1092 return $content;
1093 }
1094
1095 /**
1096 * Print accumulated content of module
1097 *
1098 * @return void
1099 */
1100 public function printContent() {
1101 echo $this->content;
1102 }
1103
1104 /***************************
1105 *
1106 * Sub-content functions, rendering specific parts of the module content.
1107 *
1108 ***************************/
1109 /**
1110 * Create the panel of buttons for submitting the form or otherwise perform operations.
1111 *
1112 * @param string $function Identifier for function of module
1113 * @return array all available buttons as an assoc. array
1114 */
1115 protected function getButtons($function = '') {
1116 /** @var IconFactory $iconFactory */
1117 $iconFactory = GeneralUtility::makeInstance(IconFactory::class);
1118
1119 $lang = $this->getLanguageService();
1120 $buttons = array(
1121 'view' => '',
1122 'history_page' => '',
1123 'new_content' => '',
1124 'move_page' => '',
1125 'move_record' => '',
1126 'new_page' => '',
1127 'edit_page' => '',
1128 'edit_language' => '',
1129 'csh' => '',
1130 'shortcut' => '',
1131 'cache' => '',
1132 'savedok' => '',
1133 'save_close' => '',
1134 'savedokshow' => '',
1135 'closedok' => '',
1136 'deletedok' => '',
1137 'undo' => '',
1138 'history_record' => ''
1139 );
1140 // View page
1141 if (!VersionState::cast($this->pageinfo['t3ver_state'])->equals(VersionState::DELETE_PLACEHOLDER)) {
1142 $buttons['view'] = '<a href="#" onclick="' . htmlspecialchars(BackendUtility::viewOnClick($this->pageinfo['uid'], '', BackendUtility::BEgetRootLine($this->pageinfo['uid']))) . '" title="' . $lang->sL('LLL:EXT:lang/locallang_core.xlf:labels.showPage', TRUE) . '">' . IconUtility::getSpriteIcon('actions-document-view') . '</a>';
1143 }
1144 // Shortcut
1145 if ($this->getBackendUser()->mayMakeShortcut()) {
1146 $buttons['shortcut'] = $this->doc->makeShortcutIcon('id, edit_record, pointer, new_unique_uid, search_field, search_levels, showLimit', implode(',', array_keys($this->MOD_MENU)), $this->MCONF['name']);
1147 }
1148 // Cache
1149 if (!$this->modTSconfig['properties']['disableAdvanced']) {
1150 $buttons['cache'] = '<a href="' . htmlspecialchars(BackendUtility::getModuleUrl('web_layout', array('id' => $this->pageinfo['uid'], 'clear_cache' => '1'))) . '" title="' . $lang->sL('LLL:EXT:lang/locallang_core.xlf:labels.clear_cache', TRUE) . '">' . IconUtility::getSpriteIcon('actions-system-cache-clear') . '</a>';
1151 }
1152 if (!$this->modTSconfig['properties']['disableIconToolbar']) {
1153 // Move record
1154 if (MathUtility::canBeInterpretedAsInteger($this->eRParts[1])) {
1155 $urlParameters = [
1156 'table' => $this->eRParts[0],
1157 'uid' => $this->eRParts[1],
1158 'returnUrl' => GeneralUtility::getIndpEnv('REQUEST_URI')
1159 ];
1160 $buttons['move_record'] = '<a href="' . htmlspecialchars(BackendUtility::getModuleUrl('move_element', $urlParameters)) . '">' . IconUtility::getSpriteIcon(('actions-' . ($this->eRParts[0] == 'tt_content' ? 'document' : 'page') . '-move'), array('class' => 'c-inputButton', 'title' => $lang->getLL(('move_' . ($this->eRParts[0] == 'tt_content' ? 'record' : 'page')), TRUE))) . '</a>';
1161 }
1162
1163 // Edit page properties and page language overlay icons
1164 if ($this->pageIsNotLockedForEditors()) {
1165
1166 // Edit localized page_language_overlay only when one specific language is selected
1167 if ($this->MOD_SETTINGS['function'] == 1 && $this->current_sys_language > 0) {
1168 $overlayRecord = $this->getDatabaseConnection()->exec_SELECTgetSingleRow(
1169 'uid',
1170 'pages_language_overlay',
1171 'pid = ' . (int)$this->id . ' ' .
1172 'AND sys_language_uid = ' . (int)$this->current_sys_language .
1173 BackendUtility::deleteClause('pages_language_overlay') .
1174 BackendUtility::versioningPlaceholderClause('pages_language_overlay'),
1175 '',
1176 '',
1177 '',
1178 'sys_language_uid'
1179 );
1180
1181 $editLanguageOnClick = htmlspecialchars(BackendUtility::editOnClick('&edit[pages_language_overlay][' . $overlayRecord['uid'] . ']=edit'));
1182 $buttons['edit_language'] = '<a href="#" ' .
1183 'onclick="' . $editLanguageOnClick . '"' .
1184 'title="' . $lang->getLL('editPageLanguageOverlayProperties', TRUE) . '">' .
1185 IconUtility::getSpriteIcon('mimetypes-x-content-page-language-overlay') .
1186 '</a>';
1187 }
1188
1189
1190 // Edit page properties
1191 $editPageOnClick = htmlspecialchars(BackendUtility::editOnClick('&edit[pages][' . $this->id . ']=edit'));
1192 $buttons['edit_page'] = '<a href="#" ' .
1193 'onclick="' . $editPageOnClick . '"' .
1194 'title="' . $lang->getLL('editPageProperties', TRUE) . '">' .
1195 IconUtility::getSpriteIcon('actions-page-open') .
1196 '</a>';
1197 }
1198
1199 // Add CSH (Context Sensitive Help) icon to tool bar
1200 if ($function == 'quickEdit') {
1201 $buttons['csh'] = BackendUtility::cshItem($this->descrTable, 'quickEdit');
1202 } else {
1203 $buttons['csh'] = BackendUtility::cshItem($this->descrTable, 'columns_' . $this->MOD_SETTINGS['function']);
1204 }
1205 if ($function == 'quickEdit') {
1206 // Save record
1207 $buttons['savedok'] = '<button class="c-inputButton" name="_savedok_x">'
1208 . IconUtility::getSpriteIcon('actions-document-save', array('title' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:rm.saveDoc', TRUE)))
1209 . '</button>';
1210 // Save and close
1211 $buttons['save_close'] = '<button class="c-inputButton" name="_saveandclosedok_x">'
1212 . IconUtility::getSpriteIcon('actions-document-save-close', array('title' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:rm.saveCloseDoc', TRUE)))
1213 . '</button>';
1214 // Save record and show page
1215 $buttons['savedokshow'] = '<a href="#" onclick="' . htmlspecialchars('document.editform.redirect.value+=\'&popView=1\'; TBE_EDITOR.checkAndDoSubmit(1); return false;') . '" title="' . $lang->sL('LLL:EXT:lang/locallang_core.xlf:rm.saveDocShow', TRUE) . '">' . IconUtility::getSpriteIcon('actions-document-save-view') . '</a>';
1216 // Close record
1217 $buttons['closedok'] = '<a href="#" onclick="' . htmlspecialchars('jumpToUrl(' . GeneralUtility::quoteJSvalue($this->closeUrl) . '); return false;') . '" title="' . $lang->sL('LLL:EXT:lang/locallang_core.xlf:rm.closeDoc', TRUE) . '">' . IconUtility::getSpriteIcon('actions-document-close') . '</a>';
1218 // Delete record
1219 if ($this->deleteButton) {
1220 $buttons['deletedok'] = '<a href="#" onclick="' . htmlspecialchars('return deleteRecord(' . GeneralUtility::quoteJSvalue($this->eRParts[0]) . ',' . GeneralUtility::quoteJSvalue($this->eRParts[1]) . ',' . GeneralUtility::quoteJSvalue(GeneralUtility::getIndpEnv('SCRIPT_NAME') . '?id=' . $this->id) . ');') . '" title="' . $lang->getLL('deleteItem', TRUE) . '">' . IconUtility::getSpriteIcon('actions-edit-delete') . '</a>';
1221 }
1222 if ($this->undoButton) {
1223 // Undo button
1224 $buttons['undo'] = '<a href="#"
1225 onclick="' . htmlspecialchars('window.location.href=' .
1226 GeneralUtility::quoteJSvalue(
1227 BackendUtility::getModuleUrl(
1228 'record_history',
1229 array(
1230 'element' => $this->eRParts[0] . ':' . $this->eRParts[1],
1231 'revert' => 'ALL_FIELDS',
1232 'sumUp' => -1,
1233 'returnUrl' => $this->R_URI,
1234 )
1235 )
1236 ) . '; return false;') . '"
1237 title="' . htmlspecialchars(sprintf($lang->getLL('undoLastChange'), BackendUtility::calcAge($GLOBALS['EXEC_TIME'] - $this->undoButtonR['tstamp'], $lang->sL('LLL:EXT:lang/locallang_core.xlf:labels.minutesHoursDaysYears')))) . '">' . IconUtility::getSpriteIcon('actions-edit-undo') . '</a>';
1238 // History button
1239 $buttons['history_record'] = '<a href="#"
1240 onclick="' . htmlspecialchars('jumpToUrl(' .
1241 GeneralUtility::quoteJSvalue(
1242 BackendUtility::getModuleUrl(
1243 'record_history',
1244 array(
1245 'element' => $this->eRParts[0] . ':' . $this->eRParts[1],
1246 'returnUrl' => $this->R_URI,
1247 )
1248 ) . '#latest'
1249 ) . ');return false;') . '"
1250 title="' . $lang->getLL('recordHistory', TRUE) . '">' . $iconFactory->getIcon('actions-document-history-open', Icon::SIZE_SMALL) . '</a>';
1251 }
1252 }
1253 }
1254 return $buttons;
1255 }
1256
1257 /*******************************
1258 *
1259 * Other functions
1260 *
1261 ******************************/
1262 /**
1263 * Returns the number of hidden elements (including those hidden by start/end times)
1264 * on the current page (for the current sys_language)
1265 *
1266 * @return int
1267 */
1268 public function getNumberOfHiddenElements() {
1269 return $this->getDatabaseConnection()->exec_SELECTcountRows(
1270 'uid',
1271 'tt_content',
1272 'pid=' . (int)$this->id . ' AND sys_language_uid=' . (int)$this->current_sys_language . BackendUtility::BEenableFields('tt_content', 1) . BackendUtility::deleteClause('tt_content') . BackendUtility::versioningPlaceholderClause('tt_content')
1273 );
1274 }
1275
1276 /**
1277 * Returns URL to the current script.
1278 * In particular the "popView" and "new_unique_uid" Get vars are unset.
1279 *
1280 * @param array $params Parameters array, merged with global GET vars.
1281 * @return string URL
1282 */
1283 public function local_linkThisScript($params) {
1284 $params['popView'] = '';
1285 $params['new_unique_uid'] = '';
1286 return GeneralUtility::linkThisScript($params);
1287 }
1288
1289 /**
1290 * Returns a SQL query for selecting sys_language records.
1291 *
1292 * @param int $id Page id: If zero, the query will select all sys_language records from root level which are NOT hidden. If set to another value, the query will select all sys_language records that has a pages_language_overlay record on that page (and is not hidden, unless you are admin user)
1293 * @return string Return query string.
1294 */
1295 public function exec_languageQuery($id) {
1296 if ($id) {
1297 $exQ = BackendUtility::deleteClause('pages_language_overlay') .
1298 ($this->getBackendUser()->isAdmin() ? '' : ' AND sys_language.hidden=0');
1299 return $this->getDatabaseConnection()->exec_SELECTquery(
1300 'sys_language.*',
1301 'pages_language_overlay,sys_language',
1302 'pages_language_overlay.sys_language_uid=sys_language.uid AND pages_language_overlay.pid=' . (int)$id . $exQ .
1303 BackendUtility::versioningPlaceholderClause('pages_language_overlay'),
1304 'pages_language_overlay.sys_language_uid,sys_language.uid,sys_language.pid,sys_language.tstamp,sys_language.hidden,sys_language.title,sys_language.language_isocode,sys_language.static_lang_isocode,sys_language.flag',
1305 'sys_language.title'
1306 );
1307 } else {
1308 return $this->getDatabaseConnection()->exec_SELECTquery(
1309 'sys_language.*',
1310 'sys_language',
1311 'sys_language.hidden=0',
1312 '',
1313 'sys_language.title'
1314 );
1315 }
1316 }
1317
1318 /**
1319 * Get used languages in a colPos of a page
1320 *
1321 * @param int $pageId
1322 * @param int $colPos
1323 * @return bool|\mysqli_result|object
1324 */
1325 public function getUsedLanguagesInPageAndColumn($pageId, $colPos) {
1326 if (!isset($languagesInColumnCache[$pageId])) {
1327 $languagesInColumnCache[$pageId] = array();
1328 }
1329 if (!isset($languagesInColumnCache[$pageId][$colPos])) {
1330 $languagesInColumnCache[$pageId][$colPos] = array();
1331 }
1332
1333 if (empty($languagesInColumnCache[$pageId][$colPos])) {
1334 $exQ = BackendUtility::deleteClause('tt_content') .
1335 ($this->getBackendUser()->isAdmin() ? '' : ' AND sys_language.hidden=0');
1336
1337 $databaseConnection = $this->getDatabaseConnection();
1338 $res = $databaseConnection->exec_SELECTquery(
1339 'sys_language.*',
1340 'tt_content,sys_language',
1341 'tt_content.sys_language_uid=sys_language.uid AND tt_content.colPos = ' . (int)$colPos . ' AND tt_content.pid=' . (int)$pageId . $exQ .
1342 BackendUtility::versioningPlaceholderClause('tt_content'),
1343 'tt_content.sys_language_uid,sys_language.uid,sys_language.pid,sys_language.tstamp,sys_language.hidden,sys_language.title,sys_language.language_isocode,sys_language.static_lang_isocode,sys_language.flag',
1344 'sys_language.title'
1345 );
1346 while ($row = $databaseConnection->sql_fetch_assoc($res)) {
1347 $languagesInColumnCache[$pageId][$colPos][$row['uid']] = $row;
1348 }
1349 $databaseConnection->sql_free_result($res);
1350 }
1351
1352 return $languagesInColumnCache[$pageId][$colPos];
1353 }
1354
1355 /**
1356 * Check if a column of a page for a language is empty. Translation records are ignored here!
1357 *
1358 * @param int $colPos
1359 * @param int $languageId
1360 * @return bool
1361 */
1362 public function isColumnEmpty($colPos, $languageId) {
1363 foreach ($this->contentElementCache[$languageId][$colPos] as $uid => $row) {
1364 if ((int)$row['l18n_parent'] === 0) {
1365 return FALSE;
1366 }
1367 }
1368 return TRUE;
1369 }
1370
1371 /**
1372 * Get elements for a column and a language
1373 *
1374 * @param int $pageId
1375 * @param int $colPos
1376 * @param int $languageId
1377 * @return array
1378 */
1379 public function getElementsFromColumnAndLanguage($pageId, $colPos, $languageId) {
1380 if (!isset($this->contentElementCache[$languageId][$colPos])) {
1381 $languageId = (int)$languageId;
1382 $whereClause = 'tt_content.pid=' . (int)$pageId . ' AND tt_content.colPos=' . (int)$colPos . ' AND tt_content.sys_language_uid=' . $languageId . BackendUtility::deleteClause('tt_content');
1383 if ($languageId > 0) {
1384 $whereClause .= ' AND tt_content.l18n_parent=0 AND sys_language.uid=' . $languageId . ($this->getBackendUser()->isAdmin() ? '' : ' AND sys_language.hidden=0');
1385 }
1386
1387 $databaseConnection = $this->getDatabaseConnection();
1388 $res = $databaseConnection->exec_SELECTquery(
1389 'tt_content.uid',
1390 'tt_content,sys_language',
1391 $whereClause
1392 );
1393 while ($row = $databaseConnection->sql_fetch_assoc($res)) {
1394 $this->contentElementCache[$languageId][$colPos][$row['uid']] = $row;
1395 }
1396 $databaseConnection->sql_free_result($res);
1397 }
1398 if (is_array($this->contentElementCache[$languageId][$colPos])) {
1399 return array_keys($this->contentElementCache[$languageId][$colPos]);
1400 }
1401 return array();
1402 }
1403
1404 /**
1405 * Check the editlock access
1406 *
1407 * @return bool
1408 */
1409 public function pageIsNotLockedForEditors() {
1410 return !($this->pageinfo['editlock'] && ($this->CALC_PERMS & Permission::PAGE_EDIT));
1411 }
1412
1413 /**
1414 * Returns LanguageService
1415 *
1416 * @return \TYPO3\CMS\Lang\LanguageService
1417 */
1418 protected function getLanguageService() {
1419 return $GLOBALS['LANG'];
1420 }
1421
1422 /**
1423 * Returns the current BE user.
1424 *
1425 * @return \TYPO3\CMS\Core\Authentication\BackendUserAuthentication
1426 */
1427 protected function getBackendUser() {
1428 return $GLOBALS['BE_USER'];
1429 }
1430
1431 /**
1432 * Returns the database connection
1433 *
1434 * @return \TYPO3\CMS\Core\Database\DatabaseConnection
1435 */
1436 protected function getDatabaseConnection() {
1437 return $GLOBALS['TYPO3_DB'];
1438 }
1439
1440 }