TYPO3CMS  8
 All Classes Namespaces Files Functions Variables Pages
FrontendEditingController.php
Go to the documentation of this file.
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 
25 
30 {
37  public $TSFE_EDIT;
38 
42  protected $tce;
43 
49  public function initConfigOptions()
50  {
51  $this->TSFE_EDIT = GeneralUtility::_GP('TSFE_EDIT');
52  // Include classes for editing IF editing module in Admin Panel is open
53  if ($GLOBALS['BE_USER']->isFrontendEditingActive()) {
54  if ($this->isEditAction()) {
55  $this->editAction();
56  }
57  }
58  }
59 
71  public function displayEditPanel($content, array $conf, $currentRecord, array $dataArray)
72  {
73  if ($conf['newRecordFromTable']) {
74  $currentRecord = $conf['newRecordFromTable'] . ':NEW';
75  $conf['allow'] = 'new';
76  $checkEditAccessInternals = false;
77  } else {
78  $checkEditAccessInternals = true;
79  }
80  list($table, $uid) = explode(':', $currentRecord);
81  // Page ID for new records, 0 if not specified
82  $newRecordPid = (int)$conf['newRecordInPid'];
83  if (!$conf['onlyCurrentPid'] || $dataArray['pid'] == $GLOBALS['TSFE']->id) {
84  if ($table == 'pages') {
85  $newUid = $uid;
86  } else {
87  if ($conf['newRecordFromTable']) {
88  $newUid = $GLOBALS['TSFE']->id;
89  if ($newRecordPid) {
90  $newUid = $newRecordPid;
91  }
92  } else {
93  $newUid = -1 * $uid;
94  }
95  }
96  }
97  if ($GLOBALS['TSFE']->displayEditIcons && $table && $this->allowedToEdit($table, $dataArray, $conf, $checkEditAccessInternals) && $this->allowedToEditLanguage($table, $dataArray)) {
98  $editClass = $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/classes/class.frontendedit.php']['edit'];
99  if ($editClass) {
100  $edit = GeneralUtility::getUserObj($editClass);
101  if (is_object($edit)) {
102  $allowedActions = $this->getAllowedEditActions($table, $conf, $dataArray['pid']);
103  $content = $edit->editPanel($content, $conf, $currentRecord, $dataArray, $table, $allowedActions, $newUid, $this->getHiddenFields($dataArray));
104  }
105  }
106  }
107  return $content;
108  }
109 
122  public function displayEditIcons($content, $params, array $conf = [], $currentRecord = '', array $dataArray = [], $addUrlParamStr = '')
123  {
124  // Check incoming params:
125  list($currentRecordTable, $currentRecordUID) = explode(':', $currentRecord);
126  list($fieldList, $table) = array_reverse(GeneralUtility::trimExplode(':', $params, true));
127  // Reverse the array because table is optional
128  if (!$table) {
129  $table = $currentRecordTable;
130  } elseif ($table != $currentRecordTable) {
131  // If the table is set as the first parameter, and does not match the table of the current record, then just return.
132  return $content;
133  }
134  $editUid = $dataArray['_LOCALIZED_UID'] ?: $currentRecordUID;
135  // Edit icons imply that the editing action is generally allowed, assuming page and content element permissions permit it.
136  if (!array_key_exists('allow', $conf)) {
137  $conf['allow'] = 'edit';
138  }
139  if ($GLOBALS['TSFE']->displayFieldEditIcons && $table && $this->allowedToEdit($table, $dataArray, $conf) && $fieldList && $this->allowedToEditLanguage($table, $dataArray)) {
140  $editClass = $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/classes/class.frontendedit.php']['edit'];
141  if ($editClass) {
142  $edit = GeneralUtility::getUserObj($editClass);
143  if (is_object($edit)) {
144  $content = $edit->editIcons($content, $params, $conf, $currentRecord, $dataArray, $addUrlParamStr, $table, $editUid, $fieldList);
145  }
146  }
147  }
148  return $content;
149  }
150 
151  /*****************************************************
152  *
153  * Frontend Editing
154  *
155  ****************************************************/
162  public function isEditAction()
163  {
164  if (is_array($this->TSFE_EDIT)) {
165  if ($this->TSFE_EDIT['cancel']) {
166  unset($this->TSFE_EDIT['cmd']);
167  } else {
168  $cmd = (string)$this->TSFE_EDIT['cmd'];
169  if (($cmd != 'edit' || is_array($this->TSFE_EDIT['data']) && ($this->TSFE_EDIT['doSave'] || $this->TSFE_EDIT['update'] || $this->TSFE_EDIT['update_close'])) && $cmd != 'new') {
170  // $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.
171  return true;
172  }
173  }
174  }
175  return false;
176  }
177 
185  public function isEditFormShown()
186  {
187  if (is_array($this->TSFE_EDIT)) {
188  $cmd = (string)$this->TSFE_EDIT['cmd'];
189  if ($cmd == 'edit' || $cmd == 'new') {
190  return true;
191  }
192  }
193  }
194 
203  public function editAction()
204  {
205  // Commands
206  list($table, $uid) = explode(':', $this->TSFE_EDIT['record']);
207  $uid = (int)$uid;
208  $cmd = $this->TSFE_EDIT['cmd'];
209  // Look for some TSFE_EDIT data that indicates we should save.
210  if (($this->TSFE_EDIT['doSave'] || $this->TSFE_EDIT['update'] || $this->TSFE_EDIT['update_close']) && is_array($this->TSFE_EDIT['data'])) {
211  $cmd = 'save';
212  }
213  if ($cmd == 'save' || $cmd && $table && $uid && isset($GLOBALS['TCA'][$table])) {
214  // Hook for defining custom editing actions. Naming is incorrect, but preserves compatibility.
215  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsfebeuserauth.php']['extEditAction'])) {
216  $_params = [];
217  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsfebeuserauth.php']['extEditAction'] as $_funcRef) {
218  GeneralUtility::callUserFunction($_funcRef, $_params, $this);
219  }
220  }
221  // Perform the requested editing command.
222  $cmdAction = 'do' . ucwords($cmd);
223  if (is_callable([$this, $cmdAction])) {
224  $this->{$cmdAction}($table, $uid);
225  } else {
226  throw new \UnexpectedValueException('The specified frontend edit command (' . $cmd . ') is not valid.', 1225818120);
227  }
228  }
229  }
230 
238  public function doHide($table, $uid)
239  {
240  $hideField = $GLOBALS['TCA'][$table]['ctrl']['enablecolumns']['disabled'];
241  if ($hideField) {
242  $recData = [];
243  $recData[$table][$uid][$hideField] = 1;
244  $this->initializeTceMain();
245  $this->tce->start($recData, []);
246  $this->tce->process_datamap();
247  }
248  }
249 
257  public function doUnhide($table, $uid)
258  {
259  $hideField = $GLOBALS['TCA'][$table]['ctrl']['enablecolumns']['disabled'];
260  if ($hideField) {
261  $recData = [];
262  $recData[$table][$uid][$hideField] = 0;
263  $this->initializeTceMain();
264  $this->tce->start($recData, []);
265  $this->tce->process_datamap();
266  }
267  }
268 
276  public function doUp($table, $uid)
277  {
278  $this->move($table, $uid, 'up');
279  }
280 
288  public function doDown($table, $uid)
289  {
290  $this->move($table, $uid, 'down');
291  }
292 
300  public function doMoveAfter($table, $uid)
301  {
302  $afterUID = $GLOBALS['BE_USER']->frontendEdit->TSFE_EDIT['moveAfter'];
303  $this->move($table, $uid, '', $afterUID);
304  }
305 
315  protected function move($table, $uid, $direction = '', $afterUID = 0)
316  {
317  $dataHandlerCommands = [];
318  $sortField = $GLOBALS['TCA'][$table]['ctrl']['sortby'];
319  if ($sortField) {
320  // Get the current record
321  // Only fetch uid, pid and the fields that are necessary to detect the sorting factors
322  if (isset($GLOBALS['TCA'][$table]['ctrl']['copyAfterDuplFields'])) {
323  $copyAfterDuplicateFields = GeneralUtility::trimExplode(',', $GLOBALS['TCA'][$table]['ctrl']['copyAfterDuplFields'], true);
324  } else {
325  $copyAfterDuplicateFields = [];
326  }
327 
328  $fields = $copyAfterDuplicateFields;
329  $fields[] = 'uid';
330  $fields[] = 'pid';
331  $fields[] = $sortField;
332  $fields = array_unique($fields);
333 
334  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
335  ->getQueryBuilderForTable($table);
336  $queryBuilder->getRestrictions()->removeAll();
337 
338  $currentRecord = $queryBuilder
339  ->select(...$fields)
340  ->from($table)
341  ->where($queryBuilder->expr()->eq(
342  'uid',
343  $queryBuilder->createNamedParameter($uid, \PDO::PARAM_INT)
344  ))
345  ->execute()
346  ->fetch();
347 
348  if (is_array($currentRecord)) {
349  // Fetch the record before or after the current one
350  // to define the data handler commands
351  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
352  ->getQueryBuilderForTable($table);
353 
354  $queryBuilder
355  ->select('uid', 'pid')
356  ->from($table)
357  ->where($queryBuilder->expr()->eq(
358  'pid',
359  $queryBuilder->createNamedParameter($currentRecord['pid'], \PDO::PARAM_INT)
360  ))
361  ->setMaxResults(2);
362 
363  // Disable the default restrictions (but not all) if the admin panel is in preview mode
364  if ($GLOBALS['BE_USER']->adminPanel instanceof AdminPanelView && $GLOBALS['BE_USER']->adminPanel->extGetFeAdminValue('preview')) {
365  $queryBuilder->getRestrictions()
366  ->removeByType(StartTimeRestriction::class)
367  ->removeByType(EndTimeRestriction::class)
368  ->removeByType(HiddenRestriction::class)
369  ->removeByType(FrontendGroupRestriction::class);
370  }
371 
372  if (!empty($copyAfterDuplicateFields)) {
373  foreach ($copyAfterDuplicateFields as $fieldName) {
374  $queryBuilder->andWhere($queryBuilder->expr()->eq(
375  $fieldName,
376  $queryBuilder->createNamedParameter($currentRecord[$fieldName], \PDO::PARAM_STR)
377  ));
378  }
379  }
380  if (!empty($direction)) {
381  if ($direction === 'up') {
382  $queryBuilder->andWhere(
383  $queryBuilder->expr()->lt(
384  $sortField,
385  $queryBuilder->createNamedParameter($currentRecord[$sortField], \PDO::PARAM_INT)
386  )
387  );
388  $queryBuilder->orderBy($sortField, 'DESC');
389  } else {
390  $queryBuilder->andWhere(
391  $queryBuilder->expr()->gt(
392  $sortField,
393  $queryBuilder->createNamedParameter($currentRecord[$sortField], \PDO::PARAM_INT)
394  )
395  );
396  $queryBuilder->orderBy($sortField, 'ASC');
397  }
398  }
399 
400  $result = $queryBuilder->execute();
401  if ($recordBefore = $result->fetch()) {
402  if ($afterUID) {
403  $dataHandlerCommands[$table][$uid]['move'] = -$afterUID;
404  } elseif ($direction === 'down') {
405  $dataHandlerCommands[$table][$uid]['move'] = -$recordBefore['uid'];
406  } elseif ($recordAfter = $result->fetch()) {
407  // Must take the second record above...
408  $dataHandlerCommands[$table][$uid]['move'] = -$recordAfter['uid'];
409  } else {
410  // ... and if that does not exist, use pid
411  $dataHandlerCommands[$table][$uid]['move'] = $currentRecord['pid'];
412  }
413  } elseif ($direction === 'up') {
414  $dataHandlerCommands[$table][$uid]['move'] = $currentRecord['pid'];
415  }
416  }
417 
418  // If any data handler commands were set, execute the data handler command
419  if (!empty($dataHandlerCommands)) {
420  $this->initializeTceMain();
421  $this->tce->start([], $dataHandlerCommands);
422  $this->tce->process_cmdmap();
423  }
424  }
425  }
426 
434  public function doDelete($table, $uid)
435  {
436  $cmdData[$table][$uid]['delete'] = 1;
437  if (!empty($cmdData)) {
438  $this->initializeTceMain();
439  $this->tce->start([], $cmdData);
440  $this->tce->process_cmdmap();
441  }
442  }
443 
451  public function doSave($table, $uid)
452  {
453  $data = $this->TSFE_EDIT['data'];
454  if (!empty($data)) {
455  $this->initializeTceMain();
456  $this->tce->start($data, []);
457  $this->tce->process_uploads($_FILES);
458  $this->tce->process_datamap();
459  // Save the new UID back into TSFE_EDIT
460  $newUID = $this->tce->substNEWwithIDs['NEW'];
461  if ($newUID) {
462  $GLOBALS['BE_USER']->frontendEdit->TSFE_EDIT['newUID'] = $newUID;
463  }
464  }
465  }
466 
475  public function doSaveAndClose($table, $uid)
476  {
477  $this->doSave($table, $uid);
478  }
479 
488  public function doClose($table, $uid)
489  {
490  }
491 
500  protected function allowedToEditLanguage($table, array $currentRecord)
501  {
502  // If no access right to record languages, return immediately
503  if ($table === 'pages') {
504  $lang = $GLOBALS['TSFE']->sys_language_uid;
505  } elseif ($table === 'tt_content') {
506  $lang = $GLOBALS['TSFE']->sys_language_content;
507  } elseif ($GLOBALS['TCA'][$table]['ctrl']['languageField']) {
508  $lang = $currentRecord[$GLOBALS['TCA'][$table]['ctrl']['languageField']];
509  } else {
510  $lang = -1;
511  }
512  if ($GLOBALS['BE_USER']->checkLanguageAccess($lang)) {
513  $languageAccess = true;
514  } else {
515  $languageAccess = false;
516  }
517  return $languageAccess;
518  }
519 
529  protected function allowedToEdit($table, array $dataArray, array $conf, $checkEditAccessInternals = true)
530  {
531  // Unless permissions specifically allow it, editing is not allowed.
532  $mayEdit = false;
533  if ($checkEditAccessInternals) {
534  $editAccessInternals = $GLOBALS['BE_USER']->recordEditAccessInternals($table, $dataArray, false, false);
535  } else {
536  $editAccessInternals = true;
537  }
538  if ($editAccessInternals) {
539  if ($table == 'pages') {
540  // 2 = permission to edit the page
541  if ($GLOBALS['BE_USER']->isAdmin() || $GLOBALS['BE_USER']->doesUserHaveAccess($dataArray, 2)) {
542  $mayEdit = true;
543  }
544  } else {
545  // 16 = permission to edit content on the page
546  if ($GLOBALS['BE_USER']->isAdmin() || $GLOBALS['BE_USER']->doesUserHaveAccess(\TYPO3\CMS\Backend\Utility\BackendUtility::getRecord('pages', $dataArray['pid']), 16)) {
547  $mayEdit = true;
548  }
549  }
550  if (!$conf['onlyCurrentPid'] || $dataArray['pid'] == $GLOBALS['TSFE']->id) {
551  // Permissions:
552  $types = GeneralUtility::trimExplode(',', strtolower($conf['allow']), true);
553  $allow = array_flip($types);
554  $perms = $GLOBALS['BE_USER']->calcPerms($GLOBALS['TSFE']->page);
555  if ($table == 'pages') {
556  $allow = $this->getAllowedEditActions($table, $conf, $dataArray['pid'], $allow);
557  // Can only display editbox if there are options in the menu
558  if (!empty($allow)) {
559  $mayEdit = true;
560  }
561  } else {
562  $mayEdit = !empty($allow) && $perms & Permission::CONTENT_EDIT;
563  }
564  }
565  }
566  return $mayEdit;
567  }
568 
578  protected function getAllowedEditActions($table, array $conf, $pid, $allow = '')
579  {
580  if (!$allow) {
581  $types = GeneralUtility::trimExplode(',', strtolower($conf['allow']), true);
582  $allow = array_flip($types);
583  }
584  if (!$conf['onlyCurrentPid'] || $pid == $GLOBALS['TSFE']->id) {
585  // Permissions
586  $types = GeneralUtility::trimExplode(',', strtolower($conf['allow']), true);
587  $allow = array_flip($types);
588  $perms = $GLOBALS['BE_USER']->calcPerms($GLOBALS['TSFE']->page);
589  if ($table == 'pages') {
590  // Rootpage
591  if (count($GLOBALS['TSFE']->config['rootLine']) === 1) {
592  unset($allow['move']);
593  unset($allow['hide']);
594  unset($allow['delete']);
595  }
596  if (!($perms & Permission::PAGE_EDIT) || !$GLOBALS['BE_USER']->checkLanguageAccess(0)) {
597  unset($allow['edit']);
598  unset($allow['move']);
599  unset($allow['hide']);
600  }
601  if (!($perms & Permission::PAGE_DELETE)) {
602  unset($allow['delete']);
603  }
604  if (!($perms & Permission::PAGE_NEW)) {
605  unset($allow['new']);
606  }
607  }
608  }
609  return $allow;
610  }
611 
617  public function getJavascriptIncludes()
618  {
619  // No extra JS includes needed
620  return '';
621  }
622 
630  public function getHiddenFields(array $dataArray)
631  {
632  // No special hidden fields needed.
633  return [];
634  }
635 
641  protected function initializeTceMain()
642  {
643  if (!isset($this->tce)) {
644  $this->tce = GeneralUtility::makeInstance(\TYPO3\CMS\Core\DataHandling\DataHandler::class);
645  }
646  }
647 }
displayEditIcons($content, $params, array $conf=[], $currentRecord= '', array $dataArray=[], $addUrlParamStr= '')
static trimExplode($delim, $string, $removeEmptyValues=false, $limit=0)
allowedToEdit($table, array $dataArray, array $conf, $checkEditAccessInternals=true)
displayEditPanel($content, array $conf, $currentRecord, array $dataArray)
if(TYPO3_MODE=== 'BE') $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsfebeuserauth.php']['frontendEditingController']['default']
static makeInstance($className,...$constructorArguments)
static callUserFunction($funcName, &$params, &$ref, $_= '', $errorMode=0)