From 88cffd4747c7f58d14a8b7d8629ba60cc59fe732 Mon Sep 17 00:00:00 2001 From: Benni Mack Date: Thu, 16 Aug 2018 21:33:49 +0200 Subject: [PATCH] [TASK] Extract Persistence of FrontendEditing in separate class FrontendEditingController calls up DataHandler if the right get/post parameters are added. This code is specific for EXT:feedit and extracted into EXT:feedit, as FrontendEditingController is mixing concerns (rendering panels and persisting). Resolves: #85877 Releases: master Change-Id: Ib39fe8b7ddcf68fb9e93d8a08564ae7f550e95f3 Reviewed-on: https://review.typo3.org/57932 Tested-by: TYPO3com Reviewed-by: Benni Mack Tested-by: Benni Mack Reviewed-by: Anja Leichsenring Tested-by: Anja Leichsenring Reviewed-by: Christian Kuhn Tested-by: Christian Kuhn --- .../DataHandling/FrontendEditDataHandler.php | 332 ++++++++++++++++++ Classes/Middleware/FrontendEditInitiator.php | 18 +- 2 files changed, 342 insertions(+), 8 deletions(-) create mode 100644 Classes/DataHandling/FrontendEditDataHandler.php diff --git a/Classes/DataHandling/FrontendEditDataHandler.php b/Classes/DataHandling/FrontendEditDataHandler.php new file mode 100644 index 0000000..b466c06 --- /dev/null +++ b/Classes/DataHandling/FrontendEditDataHandler.php @@ -0,0 +1,332 @@ +user = $user ?: $GLOBALS['BE_USER']; + $this->configuration = $configuration; + } + + /** + * Management of the on-page frontend editing forms and edit panels. + * Basically taking in the data and commands and passes them on to the proper classes as they should be. + * + * @throws \UnexpectedValueException if configuration[cmd] is not a valid command + */ + public function editAction() + { + // Commands + list($table, $uid) = explode(':', $this->configuration['record']); + $uid = (int)$uid; + $cmd = $this->configuration['cmd']; + // Look for some configuration data that indicates we should save. + if (($this->configuration['doSave'] || $this->configuration['update'] || $this->configuration['update_close']) && is_array($this->configuration['data'])) { + $cmd = 'save'; + } + if ($cmd === 'save' || $cmd && $table && $uid && isset($GLOBALS['TCA'][$table])) { + // Perform the requested editing command. + $cmdAction = 'do' . ucwords($cmd); + if (method_exists($this, $cmdAction)) { + call_user_func_array([$this, $cmdAction], [$table, $uid]); + } else { + throw new \UnexpectedValueException('The specified frontend edit command (' . $cmd . ') is not valid.', 1225818110); + } + } + } + + /** + * Hides a specific record. + * + * @param string $table The table name for the record to hide. + * @param int $uid The UID for the record to hide. + */ + protected function doHide(string $table, int $uid) + { + $hideField = $GLOBALS['TCA'][$table]['ctrl']['enablecolumns']['disabled']; + if ($hideField) { + $recData = []; + $recData[$table][$uid][$hideField] = 1; + $dataHandler = GeneralUtility::makeInstance(DataHandler::class); + $dataHandler->start($recData, []); + $dataHandler->process_datamap(); + } + } + + /** + * Unhides (shows) a specific record. + * + * @param string $table The table name for the record to unhide. + * @param int $uid The UID for the record to unhide. + */ + protected function doUnhide(string $table, int $uid) + { + $hideField = $GLOBALS['TCA'][$table]['ctrl']['enablecolumns']['disabled']; + if ($hideField) { + $recData = []; + $recData[$table][$uid][$hideField] = 0; + $dataHandler = GeneralUtility::makeInstance(DataHandler::class); + $dataHandler->start($recData, []); + $dataHandler->process_datamap(); + } + } + + /** + * Moves a record up. + * + * @param string $table The table name for the record to move. + * @param int $uid The UID for the record to hide. + */ + protected function doUp(string $table, int $uid) + { + $this->move($table, $uid, 'up'); + } + + /** + * Moves a record down. + * + * @param string $table The table name for the record to move. + * @param int $uid The UID for the record to move. + */ + protected function doDown(string $table, int $uid) + { + $this->move($table, $uid, 'down'); + } + + /** + * Moves a record after a given element. Used for drag. + * + * @param string $table The table name for the record to move. + * @param int $uid The UID for the record to move. + */ + protected function doMoveAfter(string $table, int $uid) + { + $afterUID = (int)$this->configuration['moveAfter']; + $this->move($table, $uid, '', $afterUID); + } + + /** + * Moves a record + * + * @param string $table The table name for the record to move. + * @param int $uid The UID for the record to move. + * @param string $direction The direction to move, either 'up' or 'down'. + * @param int $afterUID The UID of record to move after. This is specified for dragging only. + */ + protected function move(string $table, int $uid, string $direction = '', int $afterUID = 0) + { + $dataHandlerCommands = []; + $sortField = $GLOBALS['TCA'][$table]['ctrl']['sortby']; + if ($sortField) { + // Get the current record + // Only fetch uid, pid and the fields that are necessary to detect the sorting factors + if (isset($GLOBALS['TCA'][$table]['ctrl']['copyAfterDuplFields'])) { + $copyAfterDuplicateFields = GeneralUtility::trimExplode(',', $GLOBALS['TCA'][$table]['ctrl']['copyAfterDuplFields'], true); + } else { + $copyAfterDuplicateFields = []; + } + + $fields = $copyAfterDuplicateFields; + $fields[] = 'uid'; + $fields[] = 'pid'; + $fields[] = $sortField; + $fields = array_unique($fields); + + $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class) + ->getQueryBuilderForTable($table); + $queryBuilder->getRestrictions()->removeAll(); + + $currentRecord = $queryBuilder + ->select(...$fields) + ->from($table) + ->where($queryBuilder->expr()->eq( + 'uid', + $queryBuilder->createNamedParameter($uid, \PDO::PARAM_INT) + )) + ->execute() + ->fetch(); + + if (is_array($currentRecord)) { + // Fetch the record before or after the current one + // to define the data handler commands + $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class) + ->getQueryBuilderForTable($table); + + $queryBuilder + ->select('uid', 'pid') + ->from($table) + ->where($queryBuilder->expr()->eq( + 'pid', + $queryBuilder->createNamedParameter($currentRecord['pid'], \PDO::PARAM_INT) + )) + ->setMaxResults(2); + + // Disable the default restrictions (but not all) if the admin panel is in preview mode + if ($this->user->adminPanel instanceof AdminPanelView && $this->user->adminPanel->extGetFeAdminValue('preview')) { + $queryBuilder->getRestrictions() + ->removeByType(StartTimeRestriction::class) + ->removeByType(EndTimeRestriction::class) + ->removeByType(HiddenRestriction::class) + ->removeByType(FrontendGroupRestriction::class); + } + + if (!empty($copyAfterDuplicateFields)) { + foreach ($copyAfterDuplicateFields as $fieldName) { + $queryBuilder->andWhere($queryBuilder->expr()->eq( + $fieldName, + $queryBuilder->createNamedParameter($currentRecord[$fieldName], \PDO::PARAM_STR) + )); + } + } + if (!empty($direction)) { + if ($direction === 'up') { + $queryBuilder->andWhere( + $queryBuilder->expr()->lt( + $sortField, + $queryBuilder->createNamedParameter($currentRecord[$sortField], \PDO::PARAM_INT) + ) + ); + $queryBuilder->orderBy($sortField, 'DESC'); + } else { + $queryBuilder->andWhere( + $queryBuilder->expr()->gt( + $sortField, + $queryBuilder->createNamedParameter($currentRecord[$sortField], \PDO::PARAM_INT) + ) + ); + $queryBuilder->orderBy($sortField, 'ASC'); + } + } + + $result = $queryBuilder->execute(); + if ($recordBefore = $result->fetch()) { + if ($afterUID) { + $dataHandlerCommands[$table][$uid]['move'] = -$afterUID; + } elseif ($direction === 'down') { + $dataHandlerCommands[$table][$uid]['move'] = -$recordBefore['uid']; + } elseif ($recordAfter = $result->fetch()) { + // Must take the second record above... + $dataHandlerCommands[$table][$uid]['move'] = -$recordAfter['uid']; + } else { + // ... and if that does not exist, use pid + $dataHandlerCommands[$table][$uid]['move'] = $currentRecord['pid']; + } + } elseif ($direction === 'up') { + $dataHandlerCommands[$table][$uid]['move'] = $currentRecord['pid']; + } + } + + // If any data handler commands were set, execute the data handler command + if (!empty($dataHandlerCommands)) { + $dataHandler = GeneralUtility::makeInstance(DataHandler::class); + $dataHandler->start([], $dataHandlerCommands); + $dataHandler->process_cmdmap(); + } + } + } + + /** + * Deletes a specific record. + * + * @param string $table The table name for the record to delete. + * @param int $uid The UID for the record to delete. + */ + protected function doDelete(string $table, int $uid) + { + $cmdData[$table][$uid]['delete'] = 1; + if (!empty($cmdData)) { + $dataHandler = GeneralUtility::makeInstance(DataHandler::class); + $dataHandler->start([], $cmdData); + $dataHandler->process_cmdmap(); + } + } + + /** + * Saves a record based on its data array. + * + * @param string $table The table name for the record to save. + * @param int $uid The UID for the record to save. + */ + protected function doSave(string $table, int $uid) + { + $data = $this->configuration['data']; + if (!empty($data)) { + $dataHandler = GeneralUtility::makeInstance(DataHandler::class); + $dataHandler->start($data, []); + $dataHandler->process_uploads($_FILES); + $dataHandler->process_datamap(); + // Save the new UID back into configuration + $newUID = $dataHandler->substNEWwithIDs['NEW']; + if ($newUID) { + $this->configuration['newUID'] = $newUID; + } + } + } + + /** + * Saves a record based on its data array and closes it. + * Note: This method is only a wrapper for doSave() but is needed so + * + * @param string $table The table name for the record to save. + * @param int $uid The UID for the record to save. + */ + protected function doSaveAndClose(string $table, int $uid) + { + $this->doSave($table, $uid); + } + + /** + * Stub for closing a record. No real functionality needed since content + * element rendering will take care of everything. + * + * @param string $table The table name for the record to close. + * @param int $uid The UID for the record to close. + */ + protected function doClose(string $table, int $uid) + { + } +} diff --git a/Classes/Middleware/FrontendEditInitiator.php b/Classes/Middleware/FrontendEditInitiator.php index 4a16f54..fc958ca 100644 --- a/Classes/Middleware/FrontendEditInitiator.php +++ b/Classes/Middleware/FrontendEditInitiator.php @@ -23,6 +23,7 @@ use Psr\Http\Server\RequestHandlerInterface; use TYPO3\CMS\Backend\FrontendBackendUserAuthentication; use TYPO3\CMS\Core\FrontendEditing\FrontendEditingController; use TYPO3\CMS\Core\Utility\GeneralUtility; +use TYPO3\CMS\Feedit\DataHandling\FrontendEditDataHandler; use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController; /** @@ -56,14 +57,15 @@ class FrontendEditInitiator implements MiddlewareInterface } $controllerClassName = $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsfebeuserauth.php']['frontendEditingController'][$controllerKey] ?? ''; if (!empty($controllerClassName)) { - $frontendEditingController = GeneralUtility::makeInstance($controllerClassName); - $GLOBALS['BE_USER']->frontendEdit = $frontendEditingController; - if ($GLOBALS['BE_USER']->frontendEdit instanceof FrontendEditingController) { - $GLOBALS['BE_USER']->frontendEdit->TSFE_EDIT = $request->getParsedBody()['TSFE_EDIT'] ?? $request->getQueryParams()['TSFE_EDIT'] ?? null; - // Include classes for editing IF editing module in Admin Panel is open - if (((int)$GLOBALS['TSFE']->displayEditIcons === 1 || (int)$GLOBALS['TSFE']->displayFieldEditIcons === 1) && $this->isValidEditAction($GLOBALS['BE_USER']->frontendEdit->TSFE_EDIT)) { - $GLOBALS['BE_USER']->frontendEdit->editAction(); - } + $parameters = $request->getParsedBody()['TSFE_EDIT'] ?? $request->getQueryParams()['TSFE_EDIT'] ?? null; + $isValidEditAction = $this->isValidEditAction($parameters); + $GLOBALS['BE_USER']->frontendEdit = GeneralUtility::makeInstance( + $controllerClassName, + $parameters + ); + // Include classes for editing IF editing module in Admin Panel is open + if ($GLOBALS['BE_USER']->frontendEdit instanceof FrontendEditingController && $isValidEditAction) { + GeneralUtility::makeInstance(FrontendEditDataHandler::class, $parameters)->editAction(); } } break;