[TASK] Re-work/simplify copyright header in PHP files - Part 2
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / FrontendEditing / FrontendEditingController.php
1 <?php
2 namespace TYPO3\CMS\Core\FrontendEditing;
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 * Controller class for frontend editing.
18 *
19 * @author Jeff Segars <jeff@webempoweredchurch.org>
20 * @author David Slayback <dave@webempoweredchurch.org>
21 */
22 class FrontendEditingController {
23
24 /**
25 * GET/POST parameters for the FE editing.
26 * Accessed as $GLOBALS['BE_USER']->frontendEdit->TSFE_EDIT, thus public
27 *
28 * @var array
29 */
30 public $TSFE_EDIT;
31
32 /**
33 * @var \TYPO3\CMS\Core\DataHandling\DataHandler
34 */
35 protected $tce;
36
37 /**
38 * Initializes configuration options.
39 *
40 * @return void
41 */
42 public function initConfigOptions() {
43 $this->TSFE_EDIT = \TYPO3\CMS\Core\Utility\GeneralUtility::_GP('TSFE_EDIT');
44 // Include classes for editing IF editing module in Admin Panel is open
45 if ($GLOBALS['BE_USER']->isFrontendEditingActive()) {
46 if ($this->isEditAction()) {
47 $this->editAction();
48 }
49 }
50 }
51
52 /**
53 * Generates the "edit panels" which can be shown for a page or records on a page when the Admin Panel is enabled for a backend users surfing the frontend.
54 * With the "edit panel" the user will see buttons with links to editing, moving, hiding, deleting the element
55 * This function is used for the cObject EDITPANEL and the stdWrap property ".editPanel"
56 *
57 * @param string $content A content string containing the content related to the edit panel. For cObject "EDITPANEL" this is empty but not so for the stdWrap property. The edit panel is appended to this string and returned.
58 * @param array $conf TypoScript configuration properties for the editPanel
59 * @param string $currentRecord The "table:uid" of the record being shown. If empty string then $this->currentRecord is used. For new records (set by $conf['newRecordFromTable']) it's auto-generated to "[tablename]:NEW
60 * @param array $dataArray Alternative data array to use. Default is $this->data
61 * @return string The input content string with the editPanel appended. This function returns only an edit panel appended to the content string if a backend user is logged in (and has the correct permissions). Otherwise the content string is directly returned.
62 */
63 public function displayEditPanel($content, array $conf, $currentRecord, array $dataArray) {
64 if ($conf['newRecordFromTable']) {
65 $currentRecord = $conf['newRecordFromTable'] . ':NEW';
66 $conf['allow'] = 'new';
67 $checkEditAccessInternals = FALSE;
68 } else {
69 $checkEditAccessInternals = TRUE;
70 }
71 list($table, $uid) = explode(':', $currentRecord);
72 // Page ID for new records, 0 if not specified
73 $newRecordPid = (int)$conf['newRecordInPid'];
74 if (!$conf['onlyCurrentPid'] || $dataArray['pid'] == $GLOBALS['TSFE']->id) {
75 if ($table == 'pages') {
76 $newUid = $uid;
77 } else {
78 if ($conf['newRecordFromTable']) {
79 $newUid = $GLOBALS['TSFE']->id;
80 if ($newRecordPid) {
81 $newUid = $newRecordPid;
82 }
83 } else {
84 $newUid = -1 * $uid;
85 }
86 }
87 }
88 if ($GLOBALS['TSFE']->displayEditIcons && $table && $this->allowedToEdit($table, $dataArray, $conf, $checkEditAccessInternals) && $this->allowedToEditLanguage($table, $dataArray)) {
89 $editClass = $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/classes/class.frontendedit.php']['edit'];
90 if ($editClass) {
91 $edit = \TYPO3\CMS\Core\Utility\GeneralUtility::getUserObj($editClass, FALSE);
92 if (is_object($edit)) {
93 $allowedActions = $this->getAllowedEditActions($table, $conf, $dataArray['pid']);
94 $content = $edit->editPanel($content, $conf, $currentRecord, $dataArray, $table, $allowedActions, $newUid, $this->getHiddenFields($dataArray));
95 }
96 }
97 }
98 return $content;
99 }
100
101 /**
102 * Adds an edit icon to the content string. The edit icon links to alt_doc.php with proper parameters for editing the table/fields of the context.
103 * This implements TYPO3 context sensitive editing facilities. Only backend users will have access (if properly configured as well).
104 *
105 * @param string $content The content to which the edit icons should be appended
106 * @param string $params The parameters defining which table and fields to edit. Syntax is [tablename]:[fieldname],[fieldname],[fieldname],... OR [fieldname],[fieldname],[fieldname],... (basically "[tablename]:" is optional, default table is the one of the "current record" used in the function). The fieldlist is sent as "&columnsOnly=" parameter to alt_doc.php
107 * @param array $conf TypoScript properties for configuring the edit icons.
108 * @param string $currentRecord The "table:uid" of the record being shown. If empty string then $this->currentRecord is used. For new records (set by $conf['newRecordFromTable']) it's auto-generated to "[tablename]:NEW
109 * @param array $dataArray Alternative data array to use. Default is $this->data
110 * @param string $addUrlParamStr Additional URL parameters for the link pointing to alt_doc.php
111 * @return string The input content string, possibly with edit icons added (not necessarily in the end but just after the last string of normal content.
112 */
113 public function displayEditIcons($content, $params, array $conf = array(), $currentRecord = '', array $dataArray = array(), $addUrlParamStr = '') {
114 // Check incoming params:
115 list($currentRecordTable, $currentRecordUID) = explode(':', $currentRecord);
116 list($fieldList, $table) = array_reverse(\TYPO3\CMS\Core\Utility\GeneralUtility::trimExplode(':', $params, TRUE));
117 // Reverse the array because table is optional
118 if (!$table) {
119 $table = $currentRecordTable;
120 } elseif ($table != $currentRecordTable) {
121 // If the table is set as the first parameter, and does not match the table of the current record, then just return.
122 return $content;
123 }
124 $editUid = $dataArray['_LOCALIZED_UID'] ?: $currentRecordUID;
125 // Edit icons imply that the editing action is generally allowed, assuming page and content element permissions permit it.
126 if (!array_key_exists('allow', $conf)) {
127 $conf['allow'] = 'edit';
128 }
129 if ($GLOBALS['TSFE']->displayFieldEditIcons && $table && $this->allowedToEdit($table, $dataArray, $conf) && $fieldList && $this->allowedToEditLanguage($table, $dataArray)) {
130 $editClass = $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/classes/class.frontendedit.php']['edit'];
131 if ($editClass) {
132 $edit = \TYPO3\CMS\Core\Utility\GeneralUtility::getUserObj($editClass);
133 if (is_object($edit)) {
134 $content = $edit->editIcons($content, $params, $conf, $currentRecord, $dataArray, $addUrlParamStr, $table, $editUid, $fieldList);
135 }
136 }
137 }
138 return $content;
139 }
140
141 /*****************************************************
142 *
143 * Frontend Editing
144 *
145 ****************************************************/
146 /**
147 * Returns TRUE if an edit-action is sent from the Admin Panel
148 *
149 * @return boolean
150 * @see index_ts.php
151 */
152 public function isEditAction() {
153 if (is_array($this->TSFE_EDIT)) {
154 if ($this->TSFE_EDIT['cancel']) {
155 unset($this->TSFE_EDIT['cmd']);
156 } else {
157 $cmd = (string) $this->TSFE_EDIT['cmd'];
158 if (($cmd != 'edit' || is_array($this->TSFE_EDIT['data']) && ($this->TSFE_EDIT['doSave'] || $this->TSFE_EDIT['update'] || $this->TSFE_EDIT['update_close'])) && $cmd != 'new') {
159 // $cmd can be a command like "hide" or "move". If $cmd is "edit" or "new" it's an indication to show the formfields. But if data is sent with update-flag then $cmd = edit is accepted because edit may be sent because of .keepGoing flag.
160 return TRUE;
161 }
162 }
163 }
164 return FALSE;
165 }
166
167 /**
168 * Returns TRUE if an edit form is shown on the page.
169 * Used from index_ts.php where a TRUE return-value will result in classes etc. being included.
170 *
171 * @return boolean
172 * @see index_ts.php
173 */
174 public function isEditFormShown() {
175 if (is_array($this->TSFE_EDIT)) {
176 $cmd = (string) $this->TSFE_EDIT['cmd'];
177 if ($cmd == 'edit' || $cmd == 'new') {
178 return TRUE;
179 }
180 }
181 }
182
183 /**
184 * Management of the on-page frontend editing forms and edit panels.
185 * Basically taking in the data and commands and passes them on to the proper classes as they should be.
186 *
187 * @return void
188 * @throws UnexpectedValueException if TSFE_EDIT[cmd] is not a valid command
189 * @see index_ts.php
190 */
191 public function editAction() {
192 // Commands
193 list($table, $uid) = explode(':', $this->TSFE_EDIT['record']);
194 $uid = (int)$uid;
195 $cmd = $this->TSFE_EDIT['cmd'];
196 // Look for some TSFE_EDIT data that indicates we should save.
197 if (($this->TSFE_EDIT['doSave'] || $this->TSFE_EDIT['update'] || $this->TSFE_EDIT['update_close']) && is_array($this->TSFE_EDIT['data'])) {
198 $cmd = 'save';
199 }
200 if ($cmd == 'save' || $cmd && $table && $uid && isset($GLOBALS['TCA'][$table])) {
201 // Hook for defining custom editing actions. Naming is incorrect, but preserves compatibility.
202 if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsfebeuserauth.php']['extEditAction'])) {
203 $_params = array();
204 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsfebeuserauth.php']['extEditAction'] as $_funcRef) {
205 \TYPO3\CMS\Core\Utility\GeneralUtility::callUserFunction($_funcRef, $_params, $this);
206 }
207 }
208 // Perform the requested editing command.
209 $cmdAction = 'do' . ucwords($cmd);
210 if (is_callable(array($this, $cmdAction))) {
211 $this->{$cmdAction}($table, $uid);
212 } else {
213 throw new \UnexpectedValueException('The specified frontend edit command (' . $cmd . ') is not valid.', 1225818120);
214 }
215 }
216 }
217
218 /**
219 * Hides a specific record.
220 *
221 * @param string $table The table name for the record to hide.
222 * @param integer $uid The UID for the record to hide.
223 * @return void
224 */
225 public function doHide($table, $uid) {
226 $hideField = $GLOBALS['TCA'][$table]['ctrl']['enablecolumns']['disabled'];
227 if ($hideField) {
228 $recData = array();
229 $recData[$table][$uid][$hideField] = 1;
230 $this->initializeTceMain();
231 $this->tce->start($recData, array());
232 $this->tce->process_datamap();
233 }
234 }
235
236 /**
237 * Unhides (shows) a specific record.
238 *
239 * @param string $table The table name for the record to unhide.
240 * @param integer $uid The UID for the record to unhide.
241 * @return void
242 */
243 public function doUnhide($table, $uid) {
244 $hideField = $GLOBALS['TCA'][$table]['ctrl']['enablecolumns']['disabled'];
245 if ($hideField) {
246 $recData = array();
247 $recData[$table][$uid][$hideField] = 0;
248 $this->initializeTceMain();
249 $this->tce->start($recData, array());
250 $this->tce->process_datamap();
251 }
252 }
253
254 /**
255 * Moves a record up.
256 *
257 * @param string $table The table name for the record to move.
258 * @param integer $uid The UID for the record to hide.
259 * @return void
260 */
261 public function doUp($table, $uid) {
262 $this->move($table, $uid, 'up');
263 }
264
265 /**
266 * Moves a record down.
267 *
268 * @param string $table The table name for the record to move.
269 * @param integer $uid The UID for the record to move.
270 * @return void
271 */
272 public function doDown($table, $uid) {
273 $this->move($table, $uid, 'down');
274 }
275
276 /**
277 * Moves a record after a given element. Used for drag.
278 *
279 * @param string $table The table name for the record to move.
280 * @param integer $uid The UID for the record to move.
281 * @return void
282 */
283 public function doMoveAfter($table, $uid) {
284 $afterUID = $GLOBALS['BE_USER']->frontendEdit->TSFE_EDIT['moveAfter'];
285 $this->move($table, $uid, '', $afterUID);
286 }
287
288 /**
289 * Moves a record
290 *
291 * @param string $table The table name for the record to move.
292 * @param integer $uid The UID for the record to move.
293 * @param string $direction The direction to move, either 'up' or 'down'.
294 * @param integer $afterUID The UID of record to move after. This is specified for dragging only.
295 * @return void
296 */
297 protected function move($table, $uid, $direction = '', $afterUID = 0) {
298 $cmdData = array();
299 $sortField = $GLOBALS['TCA'][$table]['ctrl']['sortby'];
300 if ($sortField) {
301 // Get self
302 $fields = array_unique(\TYPO3\CMS\Core\Utility\GeneralUtility::trimExplode(',', $GLOBALS['TCA'][$table]['ctrl']['copyAfterDuplFields'] . ',uid,pid,' . $sortField, TRUE));
303 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery(implode(',', $fields), $table, 'uid=' . $uid);
304 if ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
305 // Record before or after
306 if ($GLOBALS['BE_USER']->adminPanel instanceof \TYPO3\CMS\Frontend\View\AdminPanelView && $GLOBALS['BE_USER']->adminPanel->extGetFeAdminValue('preview')) {
307 $ignore = array('starttime' => 1, 'endtime' => 1, 'disabled' => 1, 'fe_group' => 1);
308 }
309 $copyAfterFieldsQuery = '';
310 if ($GLOBALS['TCA'][$table]['ctrl']['copyAfterDuplFields']) {
311 $cAFields = \TYPO3\CMS\Core\Utility\GeneralUtility::trimExplode(',', $GLOBALS['TCA'][$table]['ctrl']['copyAfterDuplFields'], TRUE);
312 foreach ($cAFields as $fieldName) {
313 $copyAfterFieldsQuery .= ' AND ' . $fieldName . '="' . $row[$fieldName] . '"';
314 }
315 }
316 if (!empty($direction)) {
317 if ($direction == 'up') {
318 $operator = '<';
319 $order = 'DESC';
320 } else {
321 $operator = '>';
322 $order = 'ASC';
323 }
324 $sortCheck = ' AND ' . $sortField . $operator . (int)$row[$sortField];
325 }
326 $GLOBALS['TYPO3_DB']->sql_free_result($res);
327 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid,pid', $table, 'pid=' . (int)$row['pid'] . $sortCheck . $copyAfterFieldsQuery . $GLOBALS['TSFE']->sys_page->enableFields($table, '', $ignore), '', $sortField . ' ' . $order, '2');
328 if ($row2 = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
329 if ($afterUID) {
330 $cmdData[$table][$uid]['move'] = -$afterUID;
331 } elseif ($direction == 'down') {
332 $cmdData[$table][$uid]['move'] = -$row2['uid'];
333 } elseif ($row3 = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
334 // Must take the second record above...
335 $cmdData[$table][$uid]['move'] = -$row3['uid'];
336 } else {
337 // ... and if that does not exist, use pid
338 $cmdData[$table][$uid]['move'] = $row['pid'];
339 }
340 } elseif ($direction == 'up') {
341 $cmdData[$table][$uid]['move'] = $row['pid'];
342 }
343 $GLOBALS['TYPO3_DB']->sql_free_result($res);
344 }
345 if (!empty($cmdData)) {
346 $this->initializeTceMain();
347 $this->tce->start(array(), $cmdData);
348 $this->tce->process_cmdmap();
349 }
350 }
351 }
352
353 /**
354 * Deletes a specific record.
355 *
356 * @param string $table The table name for the record to delete.
357 * @param integer $uid The UID for the record to delete.
358 * @return void
359 */
360 public function doDelete($table, $uid) {
361 $cmdData[$table][$uid]['delete'] = 1;
362 if (count($cmdData)) {
363 $this->initializeTceMain();
364 $this->tce->start(array(), $cmdData);
365 $this->tce->process_cmdmap();
366 }
367 }
368
369 /**
370 * Saves a record based on its data array.
371 *
372 * @param string $table The table name for the record to save.
373 * @param integer $uid The UID for the record to save.
374 * @return void
375 */
376 public function doSave($table, $uid) {
377 $data = $this->TSFE_EDIT['data'];
378 if (!empty($data)) {
379 $this->initializeTceMain();
380 $this->tce->start($data, array());
381 $this->tce->process_uploads($_FILES);
382 $this->tce->process_datamap();
383 // Save the new UID back into TSFE_EDIT
384 $newUID = $this->tce->substNEWwithIDs['NEW'];
385 if ($newUID) {
386 $GLOBALS['BE_USER']->frontendEdit->TSFE_EDIT['newUID'] = $newUID;
387 }
388 }
389 }
390
391 /**
392 * Saves a record based on its data array and closes it.
393 *
394 * @param string $table The table name for the record to save.
395 * @param integer $uid The UID for the record to save.
396 * @return void
397 * @note This method is only a wrapper for doSave() but is needed so
398 */
399 public function doSaveAndClose($table, $uid) {
400 $this->doSave($table, $uid);
401 }
402
403 /**
404 * Stub for closing a record. No real functionality needed since content
405 * element rendering will take care of everything.
406 *
407 * @param string $table The table name for the record to close.
408 * @param integer $uid The UID for the record to close.
409 * @return void
410 */
411 public function doClose($table, $uid) {
412
413 }
414
415 /**
416 * Checks whether the user has access to edit the language for the
417 * requested record.
418 *
419 * @param string $table The name of the table.
420 * @param array $currentRecord The record.
421 * @return boolean
422 */
423 protected function allowedToEditLanguage($table, array $currentRecord) {
424 // If no access right to record languages, return immediately
425 if ($table === 'pages') {
426 $lang = $GLOBALS['TSFE']->sys_language_uid;
427 } elseif ($table === 'tt_content') {
428 $lang = $GLOBALS['TSFE']->sys_language_content;
429 } elseif ($GLOBALS['TCA'][$table]['ctrl']['languageField']) {
430 $lang = $currentRecord[$GLOBALS['TCA'][$table]['ctrl']['languageField']];
431 } else {
432 $lang = -1;
433 }
434 if ($GLOBALS['BE_USER']->checkLanguageAccess($lang)) {
435 $languageAccess = TRUE;
436 } else {
437 $languageAccess = FALSE;
438 }
439 return $languageAccess;
440 }
441
442 /**
443 * Checks whether the user is allowed to edit the requested table.
444 *
445 * @param string $table The name of the table.
446 * @param array $dataArray The data array.
447 * @param array $conf The configuration array for the edit panel.
448 * @param boolean $checkEditAccessInternals Boolean indicating whether recordEditAccessInternals should not be checked. Defaults
449 * @return boolean
450 */
451 protected function allowedToEdit($table, array $dataArray, array $conf, $checkEditAccessInternals = TRUE) {
452 // Unless permissions specifically allow it, editing is not allowed.
453 $mayEdit = FALSE;
454 if ($checkEditAccessInternals) {
455 $editAccessInternals = $GLOBALS['BE_USER']->recordEditAccessInternals($table, $dataArray, FALSE, FALSE);
456 } else {
457 $editAccessInternals = TRUE;
458 }
459 if ($editAccessInternals) {
460 if ($table == 'pages') {
461 // 2 = permission to edit the page
462 if ($GLOBALS['BE_USER']->isAdmin() || $GLOBALS['BE_USER']->doesUserHaveAccess($dataArray, 2)) {
463 $mayEdit = TRUE;
464 }
465 } else {
466 // 16 = permission to edit content on the page
467 if ($GLOBALS['BE_USER']->isAdmin() || $GLOBALS['BE_USER']->doesUserHaveAccess(\TYPO3\CMS\Backend\Utility\BackendUtility::getRecord('pages', $dataArray['pid']), 16)) {
468 $mayEdit = TRUE;
469 }
470 }
471 if (!$conf['onlyCurrentPid'] || $dataArray['pid'] == $GLOBALS['TSFE']->id) {
472 // Permissions:
473 $types = \TYPO3\CMS\Core\Utility\GeneralUtility::trimExplode(',', \TYPO3\CMS\Core\Utility\GeneralUtility::strtolower($conf['allow']), TRUE);
474 $allow = array_flip($types);
475 $perms = $GLOBALS['BE_USER']->calcPerms($GLOBALS['TSFE']->page);
476 if ($table == 'pages') {
477 $allow = $this->getAllowedEditActions($table, $conf, $dataArray['pid'], $allow);
478 // Can only display editbox if there are options in the menu
479 if (count($allow)) {
480 $mayEdit = TRUE;
481 }
482 } else {
483 $mayEdit = count($allow) && $perms & 16;
484 }
485 }
486 }
487 return $mayEdit;
488 }
489
490 /**
491 * Takes an array of generally allowed actions and filters that list based on page and content permissions.
492 *
493 * @param string $table The name of the table.
494 * @param array $conf The configuration array.
495 * @param integer $pid The PID where editing will occur.
496 * @param string $allow Comma-separated list of actions that are allowed in general.
497 * @return array
498 */
499 protected function getAllowedEditActions($table, array $conf, $pid, $allow = '') {
500 if (!$allow) {
501 $types = \TYPO3\CMS\Core\Utility\GeneralUtility::trimExplode(',', \TYPO3\CMS\Core\Utility\GeneralUtility::strtolower($conf['allow']), TRUE);
502 $allow = array_flip($types);
503 }
504 if (!$conf['onlyCurrentPid'] || $pid == $GLOBALS['TSFE']->id) {
505 // Permissions
506 $types = \TYPO3\CMS\Core\Utility\GeneralUtility::trimExplode(',', \TYPO3\CMS\Core\Utility\GeneralUtility::strtolower($conf['allow']), TRUE);
507 $allow = array_flip($types);
508 $perms = $GLOBALS['BE_USER']->calcPerms($GLOBALS['TSFE']->page);
509 if ($table == 'pages') {
510 // Rootpage
511 if (count($GLOBALS['TSFE']->config['rootLine']) == 1) {
512 unset($allow['move']);
513 unset($allow['hide']);
514 unset($allow['delete']);
515 }
516 if (!($perms & 2)) {
517 unset($allow['edit']);
518 unset($allow['move']);
519 unset($allow['hide']);
520 }
521 if (!($perms & 4)) {
522 unset($allow['delete']);
523 }
524 if (!($perms & 8)) {
525 unset($allow['new']);
526 }
527 }
528 }
529 return $allow;
530 }
531
532 /**
533 * Adds any extra Javascript includes needed for Front-end editing
534 *
535 * @return string
536 */
537 public function getJavascriptIncludes() {
538 // No extra JS includes needed
539 return '';
540 }
541
542 /**
543 * Gets the hidden fields (array key=field name, value=field value) to be used in the edit panel for a particular content element.
544 * In the normal case, no hidden fields are needed but special controllers such as TemplaVoila need to track flexform pointers, etc.
545 *
546 * @param array $dataArray The data array for a specific content element.
547 * @return array
548 */
549 public function getHiddenFields(array $dataArray) {
550 // No special hidden fields needed.
551 return array();
552 }
553
554 /**
555 * Initializes \TYPO3\CMS\Core\DataHandling\DataHandler since it is used on modification actions.
556 *
557 * @return void
558 */
559 protected function initializeTceMain() {
560 if (!isset($this->tce)) {
561 $this->tce = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\DataHandling\\DataHandler');
562 $this->tce->stripslashes_values = 0;
563 }
564 }
565
566 }