PageLayoutController.php 56.3 KB
Newer Older
1
2
3
<?php
namespace TYPO3\CMS\Backend\Controller;

4
/*
5
 * This file is part of the TYPO3 CMS project.
6
 *
7
8
9
 * It is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License, either version 2
 * of the License, or any later version.
10
 *
11
12
 * For the full copyright and license information, please read the
 * LICENSE.txt file that was distributed with this source code.
13
 *
14
15
 * The TYPO3 project - inspiring people to share!
 */
16

Nicole Cordes's avatar
Nicole Cordes committed
17
18
use TYPO3\CMS\Backend\Utility\BackendUtility;
use TYPO3\CMS\Backend\Utility\IconUtility;
19
20
use TYPO3\CMS\Core\Imaging\Icon;
use TYPO3\CMS\Core\Imaging\IconFactory;
21
use TYPO3\CMS\Core\Page\PageRenderer;
22
use TYPO3\CMS\Core\Type\Bitmask\Permission;
Nicole Cordes's avatar
Nicole Cordes committed
23
24
25
use TYPO3\CMS\Core\Messaging\FlashMessage;
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
26
use TYPO3\CMS\Core\Utility\HttpUtility;
Nicole Cordes's avatar
Nicole Cordes committed
27
use TYPO3\CMS\Core\Utility\MathUtility;
28
use TYPO3\CMS\Fluid\View\StandaloneView;
29
use TYPO3\CMS\Fluid\ViewHelpers\Be\InfoboxViewHelper;
30
31
32
33
34
35
36
37
38
39
40
use TYPO3\CMS\Recordlist\RecordList;
use TYPO3\CMS\Core\DataHandling\DataHandler;
use TYPO3\CMS\Frontend\Page\PageRepository;
use TYPO3\CMS\Backend\Module\ModuleLoader;
use TYPO3\CMS\Backend\Template\DocumentTemplate;
use TYPO3\CMS\Backend\View\BackendLayoutView;
use TYPO3\CMS\Backend\Form\DataPreprocessor;
use TYPO3\CMS\Backend\Form\FormEngine;
use TYPO3\CMS\Core\Messaging\FlashMessageService;
use TYPO3\CMS\Backend\View\PageLayoutView;
use TYPO3\CMS\Backend\Tree\View\ContentLayoutPagePositionMap;
41
use TYPO3\CMS\Core\Versioning\VersionState;
Nicole Cordes's avatar
Nicole Cordes committed
42

43
44
45
46
47
/**
 * Script Class for Web > Layout module
 */
class PageLayoutController {

48
49
50
51
52
	/**
	 * Page Id for which to make the listing
	 *
	 * @var int
	 */
53
54
	public $id;

55
56
57
58
59
	/**
	 * Pointer - for browsing list of records.
	 *
	 * @var int
	 */
60
61
	public $pointer;

62
63
64
65
66
	/**
	 * Thumbnails or not
	 *
	 * @var string
	 */
67
68
	public $imagemode;

69
70
71
72
73
	/**
	 * Search-fields
	 *
	 * @var string
	 */
74
75
	public $search_field;

76
77
78
79
80
	/**
	 * Search-levels
	 *
	 * @var int
	 */
81
82
	public $search_levels;

83
84
85
86
87
	/**
	 * Show-limit
	 *
	 * @var int
	 */
88
89
	public $showLimit;

90
91
92
93
94
	/**
	 * Return URL
	 *
	 * @var string
	 */
95
96
	public $returnUrl;

97
98
99
100
101
	/**
	 * Clear-cache flag - if set, clears page cache for current id.
	 *
	 * @var bool
	 */
102
103
	public $clear_cache;

104
105
106
107
108
	/**
	 * PopView id - for opening a window with the page
	 *
	 * @var bool
	 */
109
110
	public $popView;

111
112
113
114
115
116
	/**
	 * QuickEdit: Variable, that tells quick edit what to show/edit etc.
	 * Format is [tablename]:[uid] with some exceptional values for both parameters (with special meanings).
	 *
	 * @var string
	 */
117
118
	public $edit_record;

119
120
121
122
123
124
125
126
	/**
	 * QuickEdit: If set, this variable tells quick edit that the last edited record had
	 * this value as UID and we should look up the new, real uid value in sys_log.
	 *
	 * @var string
	 */
	public $new_unique_uid;

127
128
129
130
131
	/**
	 * Page select perms clause
	 *
	 * @var string
	 */
132
133
	public $perms_clause;

134
135
136
137
138
	/**
	 * Module TSconfig
	 *
	 * @var array
	 */
139
140
	public $modTSconfig;

141
142
143
144
145
146
147
	/**
	 * Module shared TSconfig
	 *
	 * @var array
	 */
	public $modSharedTSconfig;

148
149
150
151
152
	/**
	 * Current ids page record
	 *
	 * @var array
	 */
153
154
155
156
157
	public $pageinfo;

	/**
	 * Document template object
	 *
158
	 * @var \TYPO3\CMS\Backend\Template\DocumentTemplate
159
160
161
	 */
	public $doc;

162
163
164
165
166
	/**
	 * "Pseudo" Description -table name
	 *
	 * @var string
	 */
167
168
	public $descrTable;

169
170
171
172
173
	/**
	 * List of column-integers to edit. Is set from TSconfig, default is "1,0,2,3"
	 *
	 * @var string
	 */
174
175
	public $colPosList;

176
177
178
179
180
	/**
	 * Flag: If content can be edited or not.
	 *
	 * @var bool
	 */
181
182
	public $EDIT_CONTENT;

183
184
185
186
187
	/**
	 * Users permissions integer for this page.
	 *
	 * @var int
	 */
188
189
	public $CALC_PERMS;

190
191
192
193
194
	/**
	 * Currently selected language for editing content elements
	 *
	 * @var int
	 */
195
196
	public $current_sys_language;

197
198
199
200
201
	/**
	 * Module configuration
	 *
	 * @var array
	 */
202
203
	public $MCONF = array();

204
205
206
207
208
	/**
	 * Menu configuration
	 *
	 * @var array
	 */
209
210
	public $MOD_MENU = array();

211
212
213
214
215
	/**
	 * Module settings (session variable)
	 *
	 * @var array
	 */
216
217
	public $MOD_SETTINGS = array();

218
219
220
221
222
	/**
	 * Array of tables to be listed by the Web > Page module in addition to the default tables
	 *
	 * @var array
	 */
223
224
	public $externalTables = array();

225
226
227
228
229
	/**
	 * Module output accumulation
	 *
	 * @var string
	 */
230
231
	public $content;

232
233
234
235
236
	/**
	 * Function menu temporary storage
	 *
	 * @var string
	 */
237
238
	public $topFuncMenu;

239
240
241
242
243
244
245
246
	/**
	 * List of column-integers accessible to the current BE user.
	 * Is set from TSconfig, default is $colPosList
	 *
	 * @var string
	 */
	public $activeColPosList;

247
	/**
248
249
250
	 * Markers array
	 *
	 * @var array
251
252
253
	 */
	protected $markers = array();

254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
	/**
	 * @var array
	 */
	protected $eRParts = array();

	/**
	 * @var string
	 */
	protected $editSelect;

	/**
	 * @var bool
	 */
	protected $deleteButton;

	/**
	 * @var bool
	 */
	protected $undoButton;

	/**
	 * @var array
	 */
	protected $undoButtonR;

	/**
	 * @var string
	 */
	protected $R_URI;

	/**
	 * @var string
	 */
	protected $closeUrl;

289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
	/**
	 * Caches the available languages in a colPos
	 *
	 * @var array
	 */
	protected $languagesInColumnCache = array();

	/**
	 * Caches the amount of content elements as a matrix
	 *
	 * @var array
	 * @internal
	 */
	public $contentElementCache = array();

304
305
306
307
308
	/**
	 * @var IconFactory
	 */
	protected $iconFactory;

309
310
311
312
313
314
	/**
	 * Initializing the module
	 *
	 * @return void
	 */
	public function init() {
315
		$this->iconFactory = GeneralUtility::makeInstance(IconFactory::class);
Wouter Wolters's avatar
Wouter Wolters committed
316
		$this->getLanguageService()->includeLLFile('EXT:backend/Resources/Private/Language/locallang_layout.xlf');
317

318
319
		// Setting module configuration / page select clause
		$this->MCONF = $GLOBALS['MCONF'];
320
		$this->perms_clause = $this->getBackendUser()->getPagePermsClause(1);
321
		// Get session data
322
		$sessionData = $this->getBackendUser()->getSessionData(RecordList::class);
323
		$this->search_field = !empty($sessionData['search_field']) ? $sessionData['search_field'] : '';
324
		// GPvars:
325
		$this->id = (int)GeneralUtility::_GP('id');
Nicole Cordes's avatar
Nicole Cordes committed
326
327
328
329
330
		$this->pointer = GeneralUtility::_GP('pointer');
		$this->imagemode = GeneralUtility::_GP('imagemode');
		$this->clear_cache = GeneralUtility::_GP('clear_cache');
		$this->popView = GeneralUtility::_GP('popView');
		$this->edit_record = GeneralUtility::_GP('edit_record');
331
		$this->new_unique_uid = GeneralUtility::_GP('new_unique_uid');
332
		$this->search_field = GeneralUtility::_GP('search_field');
Nicole Cordes's avatar
Nicole Cordes committed
333
334
335
		$this->search_levels = GeneralUtility::_GP('search_levels');
		$this->showLimit = GeneralUtility::_GP('showLimit');
		$this->returnUrl = GeneralUtility::sanitizeLocalUrl(GeneralUtility::_GP('returnUrl'));
336
		$this->externalTables = $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['cms']['db_layout']['addTables'];
337
		$sessionData['search_field'] = $this->search_field;
338
		// Store session data
339
		$this->getBackendUser()->setAndSaveSessionData(RecordList::class, $sessionData);
340
		// Load page info array:
Nicole Cordes's avatar
Nicole Cordes committed
341
		$this->pageinfo = BackendUtility::readPageAccess($this->id, $this->perms_clause);
342
343
344
		// Initialize menu
		$this->menuConfig();
		// Setting sys language from session var:
345
		$this->current_sys_language = (int)$this->MOD_SETTINGS['language'];
346
347
		// CSH / Descriptions:
		$this->descrTable = '_MOD_' . $this->MCONF['name'];
348
349
350

		$this->markers['SEARCHBOX'] = '';
		$this->markers['BUTTONLIST_ADDITIONAL'] = '';
351
352
353
354
355
356
357
358
	}

	/**
	 * Initialize menu array
	 *
	 * @return void
	 */
	public function menuConfig() {
359
		$lang = $this->getLanguageService();
360
361
362
363
		// MENU-ITEMS:
		$this->MOD_MENU = array(
			'tt_content_showHidden' => '',
			'function' => array(
364
365
366
				0 => $lang->getLL('m_function_0'),
				1 => $lang->getLL('m_function_1'),
				2 => $lang->getLL('m_function_2')
367
368
			),
			'language' => array(
369
				0 => $lang->getLL('m_default')
370
371
372
373
374
			)
		);
		// example settings:
		// 	$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['cms']['db_layout']['addTables']['tx_myext'] =
		//		array ('default' => array(
375
		//				'MENU' => 'LLL:EXT:tx_myext/locallang_db.xlf:menuDefault',
376
377
378
379
380
381
382
383
384
385
		//				'fList' =>  'title,description,image',
		//				'icon' => TRUE));
		if (is_array($this->externalTables)) {
			foreach ($this->externalTables as $table => $tableSettings) {
				// delete the default settings from above
				if (is_array($this->MOD_MENU[$table])) {
					unset($this->MOD_MENU[$table]);
				}
				if (is_array($tableSettings) && count($tableSettings) > 1) {
					foreach ($tableSettings as $key => $settings) {
386
						$this->MOD_MENU[$table][$key] = $lang->sL($settings['MENU']);
387
388
389
390
391
392
					}
				}
			}
		}
		// 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.
		$res = $this->exec_languageQuery($this->id);
393
394
395
		while ($lRow = $this->getDatabaseConnection()->sql_fetch_assoc($res)) {
			if ($this->getBackendUser()->checkLanguageAccess($lRow['uid'])) {
				$this->MOD_MENU['language'][$lRow['uid']] = $lRow['hidden'] ? '(' . $lRow['title'] . ')' : $lRow['title'];
396
397
398
			}
		}
		// Find if there are ANY languages at all (and if not, remove the language option from function menu).
399
		$count = $this->getDatabaseConnection()->exec_SELECTcountRows('uid', 'sys_language', $this->getBackendUser()->isAdmin() ? '' : 'hidden=0');
400
401
402
403
		if (!$count) {
			unset($this->MOD_MENU['function']['2']);
		}
		// page/be_user TSconfig settings and blinding of menu-items
Nicole Cordes's avatar
Nicole Cordes committed
404
405
		$this->modSharedTSconfig = BackendUtility::getModTSconfig($this->id, 'mod.SHARED');
		$this->modTSconfig = BackendUtility::getModTSconfig($this->id, 'mod.' . $this->MCONF['name']);
406
407
408
		if ($this->modTSconfig['properties']['QEisDefault']) {
			ksort($this->MOD_MENU['function']);
		}
Nicole Cordes's avatar
Nicole Cordes committed
409
		$this->MOD_MENU['function'] = BackendUtility::unsetMenuItems($this->modTSconfig['properties'], $this->MOD_MENU['function'], 'menu.function');
410
		// Remove QuickEdit as option if page type is not...
411
		if (!GeneralUtility::inList($GLOBALS['TYPO3_CONF_VARS']['FE']['content_doktypes'] . ',6', $this->pageinfo['doktype'])) {
412
413
414
415
416
417
418
			unset($this->MOD_MENU['function'][0]);
		}
		// Setting alternative default label:
		if (($this->modSharedTSconfig['properties']['defaultLanguageLabel'] || $this->modTSconfig['properties']['defaultLanguageLabel']) && isset($this->MOD_MENU['language'][0])) {
			$this->MOD_MENU['language'][0] = $this->modTSconfig['properties']['defaultLanguageLabel'] ? $this->modSharedTSconfig['properties']['defaultLanguageLabel'] : $this->modSharedTSconfig['properties']['defaultLanguageLabel'];
		}
		// Clean up settings
419
		$this->MOD_SETTINGS = BackendUtility::getModuleData($this->MOD_MENU, GeneralUtility::_GP('SET'), 'web_layout');
420
		// For all elements to be shown in draft workspaces & to also show hidden elements by default if user hasn't disabled the option
421
		if ($this->getBackendUser()->workspace != 0 || $this->MOD_SETTINGS['tt_content_showHidden'] !== '0') {
422
423
424
425
426
427
428
429
430
431
432
			$this->MOD_SETTINGS['tt_content_showHidden'] = 1;
		}
	}

	/**
	 * Clears page cache for the current id, $this->id
	 *
	 * @return void
	 */
	public function clearCache() {
		if ($this->clear_cache) {
433
434
			$tce = GeneralUtility::makeInstance(DataHandler::class);
			$tce->stripslashes_values = FALSE;
435
436
437
438
439
440
441
442
443
444
445
446
			$tce->start(array(), array());
			$tce->clear_cacheCmd($this->id);
		}
	}

	/**
	 * Generate the flashmessages for current pid
	 *
	 * @return string HTML content with flashmessages
	 */
	protected function getHeaderFlashMessagesForCurrentPid() {
		$content = '';
447
		$lang = $this->getLanguageService();
448
		// If page is a folder
449
		if ($this->pageinfo['doktype'] == PageRepository::DOKTYPE_SYSFOLDER) {
450
			// Access to list module
451
			$moduleLoader = GeneralUtility::makeInstance(ModuleLoader::class);
452
453
454
			$moduleLoader->load($GLOBALS['TBE_MODULES']);
			$modules = $moduleLoader->modules;
			if (is_array($modules['web']['sub']['list'])) {
455
456
457
				$title = $lang->getLL('goToListModule');
				$message = '<p>' . $lang->getLL('goToListModuleMessage') . '</p>';
				$message .= '<a class="btn btn-info" href="javascript:top.goToModule(\'web_list\',1);">' . $lang->getLL('goToListModule') . '</a>';
458
459
460
461

				$view = GeneralUtility::makeInstance(StandaloneView::class);
				$view->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName('EXT:backend/Resources/Private/Templates/InfoBox.html'));
				$view->assignMultiple(array(
462
463
					'title' => $title,
					'message' => $message,
464
465
466
					'state' => InfoboxViewHelper::STATE_INFO
				));
				$content .= $view->render();
467
468
469
470
			}
		}
		// If content from different pid is displayed
		if ($this->pageinfo['content_from_pid']) {
471
			$contentPage = BackendUtility::getRecord('pages', (int)$this->pageinfo['content_from_pid']);
Nicole Cordes's avatar
Nicole Cordes committed
472
			$title = BackendUtility::getRecordTitle('pages', $contentPage);
473
			$linkToPid = $this->local_linkThisScript(array('id' => $this->pageinfo['content_from_pid']));
474
			$link = '<a href="' . $linkToPid . '">' . htmlspecialchars($title) . ' (PID ' . (int)$this->pageinfo['content_from_pid'] . ')</a>';
475
			$flashMessage = GeneralUtility::makeInstance(FlashMessage::class, sprintf($lang->getLL('content_from_pid_title'), $link), '', FlashMessage::INFO);
476
477
478
479
480
			$content .= $flashMessage->render();
		}
		return $content;
	}

481
482
483
484
485
486
	/**
	 *
	 * @return string $title
	 */
	protected function getLocalizedPageTitle() {
		if ($this->current_sys_language > 0) {
487
			$overlayRecord = $this->getDatabaseConnection()->exec_SELECTgetSingleRow(
488
489
				'title',
				'pages_language_overlay',
490
491
				'pid = ' . (int)$this->id .
						' AND sys_language_uid = ' . (int)$this->current_sys_language .
Nicole Cordes's avatar
Nicole Cordes committed
492
493
						BackendUtility::deleteClause('pages_language_overlay') .
						BackendUtility::versioningPlaceholderClause('pages_language_overlay'),
494
495
496
497
498
499
500
501
502
503
504
				'',
				'',
				'',
				'sys_language_uid'
			);
			return $overlayRecord['title'];
		} else {
			return $this->pageinfo['title'];
		}
	}

505
506
507
508
509
510
511
	/**
	 * Main function.
	 * Creates some general objects and calls other functions for the main rendering of module content.
	 *
	 * @return void
	 */
	public function main() {
512
		$lang = $this->getLanguageService();
513
514
515
516
517
		// Access check...
		// The page will show only if there is a valid page and if this page may be viewed by the user
		$access = is_array($this->pageinfo) ? 1 : 0;
		if ($this->id && $access) {
			// Initialize permission settings:
518
			$this->CALC_PERMS = $this->getBackendUser()->calcPerms($this->pageinfo);
519
520
			$this->EDIT_CONTENT = $this->pageIsNotLockedForEditors();

521
			// Start document template object:
522
			$this->doc = GeneralUtility::makeInstance(DocumentTemplate::class);
523
			$this->doc->setModuleTemplate('EXT:backend/Resources/Private/Templates/db_layout.html');
524
525
526
527

			// override the default jumpToUrl
			$this->doc->JScodeArray['jumpToUrl'] = '
				function jumpToUrl(URL,formEl) {
528
					if (document.editform && TBE_EDITOR.isFormChanged)	{	// Check if the function exists... (works in all browsers?)
529
						if (!TBE_EDITOR.isFormChanged()) {
530
531
532
533
							window.location.href = URL;
						} else if (formEl) {
							if (formEl.type=="checkbox") formEl.checked = formEl.checked ? 0 : 1;
						}
534
535
536
					} else {
						window.location.href = URL;
					}
537
				}
538
539
540
541
542
';

			$this->doc->JScode .= $this->doc->wrapScriptTags('
				if (top.fsMod) top.fsMod.recentIds["web"] = ' . (int)$this->id . ';
				if (top.fsMod) top.fsMod.navFrameHighlightedID["web"] = "pages' . (int)$this->id . '_"+top.fsMod.currentBank; ' . (int)$this->id . ';
543
			' . ($this->popView ? BackendUtility::viewOnClick($this->id, '', BackendUtility::BEgetRootLine($this->id)) : '') . '
544
545

				function deleteRecord(table,id,url) {	//
546
					if (confirm(' . GeneralUtility::quoteJSvalue($lang->getLL('deleteWarning')) . ')) {
547
						window.location.href = ' . GeneralUtility::quoteJSvalue(BackendUtility::getModuleUrl('tce_db') . '&cmd[') . '+table+"]["+id+"][delete]=1&redirect="+escape(url)+"&vC=' . $this->getBackendUser()->veriCode() . '&prErr=1&uPT=1";
548
549
550
551
552
					}
					return false;
				}
			');
			$this->doc->JScode .= $this->doc->wrapScriptTags('
553
				var DTM_array = [];
554
				var DTM_origClass = "";
555
556
557

					// if tabs are used in a popup window the array might not exists
				if(!top.DTM_currentTabs) {
558
					top.DTM_currentTabs = [];
559
560
561
562
563
564
565
				}

				function DTM_activate(idBase,index,doToogle) {	//
						// Hiding all:
					if (DTM_array[idBase]) {
						for(cnt = 0; cnt < DTM_array[idBase].length ; cnt++) {
							if (DTM_array[idBase][cnt] != idBase+"-"+index) {
Benjamin Kott's avatar
Benjamin Kott committed
566
567
								document.getElementById(DTM_array[idBase][cnt]+"-DIV").className = "tab-pane";
								document.getElementById(DTM_array[idBase][cnt]+"-MENU").attributes.getNamedItem("class").value = "tab";
568
569
570
571
572
573
							}
						}
					}

						// Showing one:
					if (document.getElementById(idBase+"-"+index+"-DIV")) {
Benjamin Kott's avatar
Benjamin Kott committed
574
575
						if (doToogle && document.getElementById(idBase+"-"+index+"-DIV").className === "tab-pane active") {
							document.getElementById(idBase+"-"+index+"-DIV").className = "tab-pane";
576
							if(DTM_origClass=="") {
Benjamin Kott's avatar
Benjamin Kott committed
577
								document.getElementById(idBase+"-"+index+"-MENU").attributes.getNamedItem("class").value = "tab";
578
579
580
581
582
							} else {
								DTM_origClass = "tab";
							}
							top.DTM_currentTabs[idBase] = -1;
						} else {
Benjamin Kott's avatar
Benjamin Kott committed
583
							document.getElementById(idBase+"-"+index+"-DIV").className = "tab-pane active";
584
							if(DTM_origClass=="") {
Benjamin Kott's avatar
Benjamin Kott committed
585
								document.getElementById(idBase+"-"+index+"-MENU").attributes.getNamedItem("class").value = "active";
586
							} else {
587
								DTM_origClass = "active";
588
589
590
591
592
593
594
595
596
							}
							top.DTM_currentTabs[idBase] = index;
						}
					}
				}
				function DTM_toggle(idBase,index,isInit) {	//
						// Showing one:
					if (document.getElementById(idBase+"-"+index+"-DIV")) {
						if (document.getElementById(idBase+"-"+index+"-DIV").style.display == "block") {
Benjamin Kott's avatar
Benjamin Kott committed
597
							document.getElementById(idBase+"-"+index+"-DIV").className = "tab-pane";
598
							if(isInit) {
Benjamin Kott's avatar
Benjamin Kott committed
599
								document.getElementById(idBase+"-"+index+"-MENU").attributes.getNamedItem("class").value = "tab";
600
601
602
603
604
							} else {
								DTM_origClass = "tab";
							}
							top.DTM_currentTabs[idBase+"-"+index] = 0;
						} else {
Benjamin Kott's avatar
Benjamin Kott committed
605
							document.getElementById(idBase+"-"+index+"-DIV").className = "tab-pane active";
606
							if(isInit) {
Benjamin Kott's avatar
Benjamin Kott committed
607
								document.getElementById(idBase+"-"+index+"-MENU").attributes.getNamedItem("class").value = "active";
608
							} else {
609
								DTM_origClass = "active";
610
611
612
613
614
615
616
							}
							top.DTM_currentTabs[idBase+"-"+index] = 1;
						}
					}
				}
			');
			// Setting doc-header
617
618
619
620
			$this->doc->form = '<form action="' . htmlspecialchars(
				BackendUtility::getModuleUrl(
					'web_layout', array('id' => $this->id, 'imagemode' =>  $this->imagemode)
				)) . '" method="post">';
621
			// Creating the top function menu:
622
			$this->topFuncMenu = BackendUtility::getFuncMenu($this->id, 'SET[function]', $this->MOD_SETTINGS['function'], $this->MOD_MENU['function'], '', '');
623
			$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'], '', '') : '';
624
			// Find backend layout / coumns
625
			$backendLayout = GeneralUtility::callUserFunction(BackendLayoutView::class . '->getSelectedBackendLayout', $this->id, $this);
626
			if (!empty($backendLayout['__colPosList'])) {
627
628
629
				$this->colPosList = implode(',', $backendLayout['__colPosList']);
			}
			// Removing duplicates, if any
630
631
			$this->colPosList = array_unique(GeneralUtility::intExplode(',', $this->colPosList));
			// Accessible columns
632
			if (isset($this->modSharedTSconfig['properties']['colPos_list']) && trim($this->modSharedTSconfig['properties']['colPos_list']) !== '') {
633
634
				$this->activeColPosList = array_unique(GeneralUtility::intExplode(',', trim($this->modSharedTSconfig['properties']['colPos_list'])));
				// Match with the list which is present in the colPosList for the current page
635
636
				if (!empty($this->colPosList) && !empty($this->activeColPosList)) {
					$this->activeColPosList = array_unique(array_intersect(
637
638
						$this->activeColPosList,
						$this->colPosList
639
					));
640
				}
641
642
			} else {
				$this->activeColPosList = $this->colPosList;
643
			}
644
			$this->activeColPosList = implode(',', $this->activeColPosList);
645
646
			$this->colPosList = implode(',', $this->colPosList);

647
			$body = '';
648
649
650
651
652
653
			$body .= $this->getHeaderFlashMessagesForCurrentPid();
			// Render the primary module content:
			if ($this->MOD_SETTINGS['function'] == 0) {
				// QuickEdit
				$body .= $this->renderQuickEdit();
			} else {
654
655
				// Page title
				$body .= $this->doc->header($this->getLocalizedPageTitle());
656
657
658
659
660
				// All other listings
				$body .= $this->renderListContent();
			}
			// Setting up the buttons and markers for docheader
			$docHeaderButtons = $this->getButtons($this->MOD_SETTINGS['function'] == 0 ? 'quickEdit' : '');
661
662
			$this->markers['CSH'] = $docHeaderButtons['csh'];
			$this->markers['TOP_FUNCTION_MENU'] = $this->topFuncMenu . $this->editSelect;
663
			$this->markers['LANGSELECTOR'] = $languageMenu;
664
			$this->markers['CONTENT'] = $body;
665
			// Build the <body> for the module
666
			$this->content .= $this->doc->moduleBody($this->pageinfo, $docHeaderButtons, $this->markers);
667
			// Renders the module page
668
			$this->content = $this->doc->render($lang->getLL('title'), $this->content);
669
670
		} else {
			// If no access or id value, create empty document:
671
			$this->doc = GeneralUtility::makeInstance(DocumentTemplate::class);
672
			$this->doc->setModuleTemplate('EXT:backend/Resources/Private/Templates/db_layout.html');
673
			$this->doc->JScode = $this->doc->wrapScriptTags('
674
				if (top.fsMod) top.fsMod.recentIds["web"] = ' . (int)$this->id . ';
675
			');
676
677

			$body = $this->doc->header($GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename']);
678

679
680
			$title = $lang->getLL('clickAPage_header');
			$message = $lang->getLL('clickAPage_content');
681
682
683
684

			$view = GeneralUtility::makeInstance(StandaloneView::class);
			$view->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName('EXT:backend/Resources/Private/Templates/InfoBox.html'));
			$view->assignMultiple(array(
685
686
				'title' => $title,
				'message' => $message,
687
688
689
				'state' => InfoboxViewHelper::STATE_INFO
			));
			$body .= $view->render();
690

691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
			// Setting up the buttons and markers for docheader
			$docHeaderButtons = array(
				'view' => '',
				'history_page' => '',
				'new_content' => '',
				'move_page' => '',
				'move_record' => '',
				'new_page' => '',
				'edit_page' => '',
				'csh' => '',
				'shortcut' => '',
				'cache' => '',
				'savedok' => '',
				'savedokshow' => '',
				'closedok' => '',
				'deletedok' => '',
				'undo' => '',
708
709
				'history_record' => '',
				'edit_language' => ''
710
			);
Benni Mack's avatar
Benni Mack committed
711
			$this->markers['CSH'] = '';
712
713
714
715
			$this->markers['TOP_FUNCTION_MENU'] = '';
			$this->markers['LANGSELECTOR'] = '';
			$this->markers['CONTENT'] = $body;
			$this->content .= $this->doc->moduleBody($this->pageinfo, $docHeaderButtons, $this->markers);
716
			// Renders the module page
717
			$this->content = $this->doc->render($lang->getLL('title'), $this->content);
718
719
720
721
722
723
		}
	}

	/**
	 * Rendering the quick-edit view.
	 *
724
	 * @return string
725
726
	 */
	public function renderQuickEdit() {
727
728
729
		$databaseConnection = $this->getDatabaseConnection();
		$beUser = $this->getBackendUser();
		$lang = $this->getLanguageService();
730
		// Alternative template
731
		$this->doc->setModuleTemplate('EXT:backend/Resources/Private/Templates/db_layout_quickedit.html');
732
		// Alternative form tag; Quick Edit submits its content to tce_db.php.
733
		$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);">';
734
735
736
737
		// Setting up the context sensitive menu:
		$this->doc->getContextMenuCode();
		// Set the edit_record value for internal use in this function:
		$edit_record = $this->edit_record;
738
		// If a command to edit all records in a column is issue, then select all those elements, and redirect to FormEngine
739
		if (substr($edit_record, 0, 9) == '_EDIT_COL') {
740
			$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');
741
			$idListA = array();
742
			while ($cRow = $databaseConnection->sql_fetch_assoc($res)) {
743
744
				$idListA[] = $cRow['uid'];
			}
745
746
			$url = BackendUtility::getModuleUrl('record_edit', array(
				'edit[tt_content][' . implode(',', $idListA) . ']' => 'edit',
747
				'returnUrl' => $this->local_linkThisScript(array('edit_record' => ''))
748
			));
749
			HttpUtility::redirect($url);
750
		}
751
752
		// If the former record edited was the creation of a NEW record, this will look up the created records uid:
		if ($this->new_unique_uid) {
753
754
			$res = $databaseConnection->exec_SELECTquery('*', 'sys_log', 'userid=' . (int)$beUser->user['uid'] . ' AND NEWid=' . $databaseConnection->fullQuoteStr($this->new_unique_uid, 'sys_log'));
			$sys_log_row = $databaseConnection->sql_fetch_assoc($res);
755
756
757
758
			if (is_array($sys_log_row)) {
				$edit_record = $sys_log_row['tablename'] . ':' . $sys_log_row['recuid'];
			}
		}
759
760
761
762
763
		// Creating the selector box, allowing the user to select which element to edit:
		$opt = array();
		$is_selected = 0;
		$languageOverlayRecord = '';
		if ($this->current_sys_language) {
764
			list($languageOverlayRecord) = BackendUtility::getRecordsByField('pages_language_overlay', 'pid', $this->id, 'AND sys_language_uid=' . (int)$this->current_sys_language);
765
766
767
		}
		if (is_array($languageOverlayRecord)) {
			$inValue = 'pages_language_overlay:' . $languageOverlayRecord['uid'];
768
			$is_selected += (int)$edit_record == $inValue;
769
			$opt[] = '<option value="' . $inValue . '"' . ($edit_record == $inValue ? ' selected="selected"' : '') . '>[ ' . $lang->getLL('editLanguageHeader', TRUE) . ' ]</option>';
770
771
		} else {
			$inValue = 'pages:' . $this->id;
772
			$is_selected += (int)$edit_record == $inValue;
773
			$opt[] = '<option value="' . $inValue . '"' . ($edit_record == $inValue ? ' selected="selected"' : '') . '>[ ' . $lang->getLL('editPageProperties', TRUE) . ' ]</option>';
774
775
		}
		// Selecting all content elements from this language and allowed colPos:
776
777
778
779
780
		$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');
		if (!$this->getBackendUser()->user['admin']) {
			$whereClause .= ' AND editlock = 0';
		}
		$res = $databaseConnection->exec_SELECTquery('*', 'tt_content', $whereClause, '', 'colPos,sorting');
781
		$colPos = NULL;
782
783
784
		$first = 1;
		// Page is the pid if no record to put this after.
		$prev = $this->id;
785
		while ($cRow = $databaseConnection->sql_fetch_assoc($res)) {
Nicole Cordes's avatar
Nicole Cordes committed
786
			BackendUtility::workspaceOL('tt_content', $cRow);
787
788
789
790
791
792
793
			if (is_array($cRow)) {
				if ($first) {
					if (!$edit_record) {
						$edit_record = 'tt_content:' . $cRow['uid'];
					}
					$first = 0;
				}
794
				if (!isset($colPos) || $cRow['colPos'] !== $colPos) {
795
796
					$colPos = $cRow['colPos'];
					$opt[] = '<option value=""></option>';
797
					$opt[] = '<option value="_EDIT_COL:' . $colPos . '">__' . $lang->sL(BackendUtility::getLabelFromItemlist('tt_content', 'colPos', $colPos), TRUE) . ':__</option>';
798
799
				}
				$inValue = 'tt_content:' . $cRow['uid'];
800
				$is_selected += (int)$edit_record == $inValue;
801
				$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>';
802
803
804
805
806
				$prev = -$cRow['uid'];
			}
		}
		// If edit_record is not set (meaning, no content elements was found for this language) we simply set it to create a new element:
		if (!$edit_record) {
807
808
			$edit_record = 'tt_content:new/' . $prev . '/' . $colPos;
			$inValue = 'tt_content:new/' . $prev . '/' . $colPos;
809
			$is_selected += (int)$edit_record == $inValue;
810
			$opt[] = '<option value="' . $inValue . '"' . ($edit_record == $inValue ? ' selected="selected"' : '') . '>[ ' . $lang->getLL('newLabel', 1) . ' ]</option>';
811
812
813
814
		}
		// If none is yet selected...
		if (!$is_selected) {
			$opt[] = '<option value=""></option>';
815
			$opt[] = '<option value="' . $edit_record . '"  selected="selected">[ ' . $lang->getLL('newLabel', TRUE) . ' ]</option>';
816
817
818
819
		}
		// Splitting the edit-record cmd value into table/uid:
		$this->eRParts = explode(':', $edit_record);
		// Delete-button flag?
820
		$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);
821
		// If undo-button should be rendered (depends on available items in sys_history)
822
823
824
825
		$this->undoButton = FALSE;
		$undoRes = $databaseConnection->exec_SELECTquery('tstamp', 'sys_history', 'tablename=' . $databaseConnection->fullQuoteStr($this->eRParts[0], 'sys_history') . ' AND recuid=' . (int)$this->eRParts[1], '', 'tstamp DESC', '1');
		if ($this->undoButtonR = $databaseConnection->sql_fetch_assoc($undoRes)) {
			$this->undoButton = TRUE;
826
827
		}
		// Setting up the Return URL for coming back to THIS script (if links take the user to another script)
Nicole Cordes's avatar
Nicole Cordes committed
828
829
		$R_URL_parts = parse_url(GeneralUtility::getIndpEnv('REQUEST_URI'));
		$R_URL_getvars = GeneralUtility::_GET();
830
		unset($R_URL_getvars['popView']);
831
		unset($R_URL_getvars['new_unique_uid']);
832
		$R_URL_getvars['edit_record'] = $edit_record;
Nicole Cordes's avatar
Nicole Cordes committed
833
		$this->R_URI = $R_URL_parts['path'] . '?' . GeneralUtility::implodeArrayForUrl('', $R_URL_getvars);
834
835
836
837
838
839
840
		// Setting close url/return url for exiting this script:
		// Goes to 'Columns' view if close is pressed (default)
		$this->closeUrl = $this->local_linkThisScript(array('SET' => array('function' => 1)));
		if ($this->returnUrl) {
			$this->closeUrl = $this->returnUrl;
		}
		// Return-url for JavaScript:
841
		$retUrlStr = $this->returnUrl ? '+\'&returnUrl=\'+' . GeneralUtility::quoteJSvalue(rawurlencode($this->returnUrl)) : '';
842
		// Drawing the edit record selectbox
843
844
845
		$this->editSelect = '<select name="edit_record" onchange="' . htmlspecialchars('jumpToUrl(' . GeneralUtility::quoteJSvalue(
			BackendUtility::getModuleUrl('web_layout') . '&id=' . $this->id . '&edit_record='
		) . '+escape(this.options[this.selectedIndex].value)' . $retUrlStr . ',this);') . '">' . implode('', $opt) . '</select>';
846
		$content = '';
847
		// Creating editing form:
848
		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)) {
849
850
851
852
			// Splitting uid parts for special features, if new:
			list($uidVal, $ex_pid, $ex_colPos) = explode('/', $this->eRParts[1]);
			// Convert $uidVal to workspace version if any:
			if ($uidVal != 'new') {
853
				if ($draftRecord = BackendUtility::getWorkspaceVersionOfRecord($beUser->workspace, $this->eRParts[0], $uidVal, 'uid')) {
854
855
856
857
					$uidVal = $draftRecord['uid'];
				}
			}
			// Initializing transfer-data object:
858
			$trData = GeneralUtility::makeInstance(DataPreprocessor::class);
859
860
			$trData->addRawData = TRUE;
			$trData->defVals[$this->eRParts[0]] = array(
861
862
				'colPos' => (int)$ex_colPos,
				'sys_language_uid' => (int)$this->current_sys_language
863
864
865
866
			);
			$trData->lockRecords = 1;
			// 'new'
			$trData->fetchRecord($this->eRParts[0], $uidVal == 'new' ? $this->id : $uidVal, $uidVal);
867
			$new_unique_uid = '';
868
869
870
871
			// Getting/Making the record:
			reset($trData->regTableItems_data);
			$rec = current($trData->regTableItems_data);
			if ($uidVal == 'new') {
872
				$new_unique_uid = uniqid('NEW', TRUE);
873
				$rec['uid'] = $new_unique_uid;
874
				$rec['pid'] = (int)$ex_pid ?: $this->id;
875
876
877
878
				$recordAccess = TRUE;
			} else {
				$rec['uid'] = $uidVal;
				// Checking internals access:
879
				$recordAccess = $beUser->recordEditAccessInternals($this->eRParts[0], $uidVal);
880
881
882
			}
			if (!$recordAccess) {
				// If no edit access, print error message:
883
				$content = $this->doc->section($lang->getLL('noAccess'), $lang->getLL('noAccess_msg') . '<br /><br />' . ($beUser->errorMsg ? 'Reason: ' . $beUser->errorMsg . '<br /><br />' : ''), 0, 1);
884
885
886
			} elseif (is_array($rec)) {
				// If the record is an array (which it will always be... :-)
				// Create instance of TCEforms, setting defaults:
887
				$tceForms = GeneralUtility::makeInstance(FormEngine::class);
888
889
				// Render form, wrap it:
				$panel = '';
890
891
				$panel .= $tceForms->getMainFields($this->eRParts[0], $rec);
				$panel = $tceForms->wrapTotal($panel, $rec, $this->eRParts[0]);
892
893
894
				// Add hidden fields:
				$theCode = $panel;
				if ($uidVal == 'new') {
895
					$theCode .= '<input type="hidden" name="data[' . $this->eRParts[0] . '][' . $rec['uid'] . '][pid]" value="' . $rec['pid'] . '" />';
896
				}
897
898
899
				$theCode .= '
					<input type="hidden" name="_serialNumber" value="' . md5(microtime()) . '" />
					<input type="hidden" name="edit_record" value="' . $edit_record . '" />
900
901
902
903
904
905
906
907
					<input type="hidden" name="redirect" value="' . htmlspecialchars(($uidVal == 'new' ? BackendUtility::getModuleUrl(
						'web_layout',
						array(
							'id' => $this->id,
							'new_unique_uid' => $new_unique_uid,
							'returnUrl' => $this->returnUrl
						)
					) : $this->R_URI)) . '" />
908
					';
909
				// Add JavaScript as needed around the form:
910
				$theCode = $tceForms->printNeededJSFunctions_top() . $theCode . $tceForms->printNeededJSFunctions();
911
				// Add warning sign if record was "locked":
Nicole Cordes's avatar
Nicole Cordes committed
912
				if ($lockInfo = BackendUtility::isRecordLocked($this->eRParts[0], $rec['uid'])) {
913
914
					/** @var \TYPO3\CMS\Core\Messaging\FlashMessage $flashMessage */
					$flashMessage = GeneralUtility::makeInstance(FlashMessage::class, htmlspecialchars($lockInfo['msg']), '', FlashMessage::WARNING);
915
					/** @var $flashMessageService \TYPO3\CMS\Core\Messaging\FlashMessageService */
916
					$flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
917
918
919
					/** @var $defaultFlashMessageQueue \TYPO3\CMS\Core\Messaging\FlashMessageQueue */
					$defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
					$defaultFlashMessageQueue->enqueue($flashMessage);
920
921
				}
				// Add whole form as a document section:
922
				$content = $this->doc->section('', $theCode);
923
924
925
			}
		} else {
			// If no edit access, print error message:
926
			$content = $this->doc->section($lang->getLL('noAccess'), $lang->getLL('noAccess_msg') . '<br /><br />', 0, 1);
927
928
929
		}
		// Bottom controls (function menus):
		$q_count = $this->getNumberOfHiddenElements();
930
931
932
933
934
935
936
937
938
939
940
		if ($q_count) {
			$h_func_b = '<div class="checkbox">' .
				'<label for="checkTt_content_showHidden">' .
				BackendUtility::getFuncCheck($this->id, 'SET[tt_content_showHidden]', $this->MOD_SETTINGS['tt_content_showHidden'], '', '', 'id="checkTt_content_showHidden"') .
				(!$q_count ? ('<span class="text-muted">' . $lang->getLL('hiddenCE', TRUE) . '</span>') : $lang->getLL('hiddenCE', TRUE) . ' (' . $q_count . ')') .
				'</label>' .
				'</div>';

			$content .= $this->doc->section('', $h_func_b, 0, 0);
			$content .= $this->doc->spacer(10);
		}
941

942
		// Select element matrix:
Nicole Cordes's avatar
Nicole Cordes committed
943
		if ($this->eRParts[0] == 'tt_content' && MathUtility::canBeInterpretedAsInteger($this->eRParts[1])) {
944
			$posMap = GeneralUtility::makeInstance(ContentLayoutPagePositionMap::class);
945
946
947
			$posMap->cur_sys_language = $this->current_sys_language;
			$HTMLcode = '';
			// CSH:
Benni Mack's avatar
Benni Mack committed
948
			$HTMLcode .= BackendUtility::cshItem($this->descrTable, 'quickEdit_selElement', NULL, '|<br />');
949
950
			$HTMLcode .= $posMap->printContentElementColumns($this->id, $this->eRParts[1], $this->colPosList, $this->MOD_SETTINGS['tt_content_showHidden'], $this->R_URI);
			$content .= $this->doc->spacer(20);
951
			$content .= $this->doc->section($lang->getLL('CEonThisPage'), $HTMLcode, 0, 1);
952
953
954
955
956
957
958
959
			$content .= $this->doc->spacer(20);
		}
		return $content;
	}

	/**
	 * Rendering all other listings than QuickEdit
	 *
960
	 * @return string
961
962
	 */
	public function renderListContent() {
963
964
965
966
967
		/** @var $dbList \TYPO3\CMS\Backend\View\PageLayoutView */
		$dbList = GeneralUtility::makeInstance(PageLayoutView::class);
		$dbList->thumbs = $this->imagemode;
		$dbList->no_noWrap = 1;
		$dbList->descrTable = $this->descrTable;
Nicole Cordes's avatar
Nicole Cordes committed
968
		$this->pointer = MathUtility::forceIntegerInRange($this->pointer, 0, 100000);
969
970
971
972
973
974
975
976
977
978
979
980
		$dbList->script = BackendUtility::getModuleUrl('web_layout');
		$dbList->showIcon = 0;
		$dbList->setLMargin = 0;
		$dbList->doEdit = $this->EDIT_CONTENT;
		$dbList->ext_CALC_PERMS = $this->CALC_PERMS;
		$dbList->agePrefixes = $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:labels.minutesHoursDaysYears');
		$dbList->id = $this->id;
		$dbList->nextThree = MathUtility::forceIntegerInRange($this->modTSconfig['properties']['editFieldsAtATime'], 0, 10);
		$dbList->option_newWizard = $this->modTSconfig['properties']['disableNewContentElementWizard'] ? 0 : 1;
		$dbList->defLangBinding = $this->modTSconfig['properties']['defLangBinding'] ? 1 : 0;
		if (!$dbList->nextThree) {
			$dbList->nextThree = 1;
981
		}
982
		$dbList->externalTables = $this->externalTables;
983
		// Create menu for selecting a table to jump to (this is, if more than just pages/tt_content elements are found on the page!)
984
985
		// also fills $dbList->activeTables
		$dbList->getTableMenu($this->id);
986
987
988
989
990
991
992
		// Initialize other variables:
		$h_func = '';
		$tableOutput = array();
		$tableJSOutput = array();
		$CMcounter = 0;
		// Traverse the list of table names which has records on this page (that array is populated
		// by the $dblist object during the function getTableMenu()):
993
994
995
		foreach ($dbList->activeTables as $table => $value) {
			$h_func_b = '';
			if (!isset($dbList->externalTables[$table])) {
996
				$q_count = $this->getNumberOfHiddenElements();
997

998
999
1000
				if ($q_count > 0) {
					$h_func_b =
						'<div class="checkbox">'