[TASK] Extract Persistence of FrontendEditing in separate class
[Packages/TYPO3.CMS.git] / typo3 / sysext / feedit / Classes / DataHandling / FrontendEditDataHandler.php
1 <?php
2 declare(strict_types = 1);
3
4 namespace TYPO3\CMS\Feedit\DataHandling;
5
6 /*
7 * This file is part of the TYPO3 CMS project.
8 *
9 * It is free software; you can redistribute it and/or modify it under
10 * the terms of the GNU General Public License, either version 2
11 * of the License, or any later version.
12 *
13 * For the full copyright and license information, please read the
14 * LICENSE.txt file that was distributed with this source code.
15 *
16 * The TYPO3 project - inspiring people to share!
17 */
18
19 use TYPO3\CMS\Backend\FrontendBackendUserAuthentication;
20 use TYPO3\CMS\Core\Database\ConnectionPool;
21 use TYPO3\CMS\Core\Database\Query\Restriction\EndTimeRestriction;
22 use TYPO3\CMS\Core\Database\Query\Restriction\FrontendGroupRestriction;
23 use TYPO3\CMS\Core\Database\Query\Restriction\HiddenRestriction;
24 use TYPO3\CMS\Core\Database\Query\Restriction\StartTimeRestriction;
25 use TYPO3\CMS\Core\DataHandling\DataHandler;
26 use TYPO3\CMS\Core\Utility\GeneralUtility;
27 use TYPO3\CMS\Frontend\View\AdminPanelView;
28
29 /**
30 * Calls DataHandler and stores data
31 */
32 class FrontendEditDataHandler
33 {
34 /**
35 * @var array
36 */
37 protected $configuration;
38
39 /**
40 * @var FrontendBackendUserAuthentication
41 */
42 protected $user;
43
44 /**
45 * FrontendEditDataHandler constructor.
46 * @param array $configuration
47 * @param FrontendBackendUserAuthentication|null $user
48 */
49 public function __construct(array $configuration, FrontendBackendUserAuthentication $user = null)
50 {
51 $this->user = $user ?: $GLOBALS['BE_USER'];
52 $this->configuration = $configuration;
53 }
54
55 /**
56 * Management of the on-page frontend editing forms and edit panels.
57 * Basically taking in the data and commands and passes them on to the proper classes as they should be.
58 *
59 * @throws \UnexpectedValueException if configuration[cmd] is not a valid command
60 */
61 public function editAction()
62 {
63 // Commands
64 list($table, $uid) = explode(':', $this->configuration['record']);
65 $uid = (int)$uid;
66 $cmd = $this->configuration['cmd'];
67 // Look for some configuration data that indicates we should save.
68 if (($this->configuration['doSave'] || $this->configuration['update'] || $this->configuration['update_close']) && is_array($this->configuration['data'])) {
69 $cmd = 'save';
70 }
71 if ($cmd === 'save' || $cmd && $table && $uid && isset($GLOBALS['TCA'][$table])) {
72 // Perform the requested editing command.
73 $cmdAction = 'do' . ucwords($cmd);
74 if (method_exists($this, $cmdAction)) {
75 call_user_func_array([$this, $cmdAction], [$table, $uid]);
76 } else {
77 throw new \UnexpectedValueException('The specified frontend edit command (' . $cmd . ') is not valid.', 1225818110);
78 }
79 }
80 }
81
82 /**
83 * Hides a specific record.
84 *
85 * @param string $table The table name for the record to hide.
86 * @param int $uid The UID for the record to hide.
87 */
88 protected function doHide(string $table, int $uid)
89 {
90 $hideField = $GLOBALS['TCA'][$table]['ctrl']['enablecolumns']['disabled'];
91 if ($hideField) {
92 $recData = [];
93 $recData[$table][$uid][$hideField] = 1;
94 $dataHandler = GeneralUtility::makeInstance(DataHandler::class);
95 $dataHandler->start($recData, []);
96 $dataHandler->process_datamap();
97 }
98 }
99
100 /**
101 * Unhides (shows) a specific record.
102 *
103 * @param string $table The table name for the record to unhide.
104 * @param int $uid The UID for the record to unhide.
105 */
106 protected function doUnhide(string $table, int $uid)
107 {
108 $hideField = $GLOBALS['TCA'][$table]['ctrl']['enablecolumns']['disabled'];
109 if ($hideField) {
110 $recData = [];
111 $recData[$table][$uid][$hideField] = 0;
112 $dataHandler = GeneralUtility::makeInstance(DataHandler::class);
113 $dataHandler->start($recData, []);
114 $dataHandler->process_datamap();
115 }
116 }
117
118 /**
119 * Moves a record up.
120 *
121 * @param string $table The table name for the record to move.
122 * @param int $uid The UID for the record to hide.
123 */
124 protected function doUp(string $table, int $uid)
125 {
126 $this->move($table, $uid, 'up');
127 }
128
129 /**
130 * Moves a record down.
131 *
132 * @param string $table The table name for the record to move.
133 * @param int $uid The UID for the record to move.
134 */
135 protected function doDown(string $table, int $uid)
136 {
137 $this->move($table, $uid, 'down');
138 }
139
140 /**
141 * Moves a record after a given element. Used for drag.
142 *
143 * @param string $table The table name for the record to move.
144 * @param int $uid The UID for the record to move.
145 */
146 protected function doMoveAfter(string $table, int $uid)
147 {
148 $afterUID = (int)$this->configuration['moveAfter'];
149 $this->move($table, $uid, '', $afterUID);
150 }
151
152 /**
153 * Moves a record
154 *
155 * @param string $table The table name for the record to move.
156 * @param int $uid The UID for the record to move.
157 * @param string $direction The direction to move, either 'up' or 'down'.
158 * @param int $afterUID The UID of record to move after. This is specified for dragging only.
159 */
160 protected function move(string $table, int $uid, string $direction = '', int $afterUID = 0)
161 {
162 $dataHandlerCommands = [];
163 $sortField = $GLOBALS['TCA'][$table]['ctrl']['sortby'];
164 if ($sortField) {
165 // Get the current record
166 // Only fetch uid, pid and the fields that are necessary to detect the sorting factors
167 if (isset($GLOBALS['TCA'][$table]['ctrl']['copyAfterDuplFields'])) {
168 $copyAfterDuplicateFields = GeneralUtility::trimExplode(',', $GLOBALS['TCA'][$table]['ctrl']['copyAfterDuplFields'], true);
169 } else {
170 $copyAfterDuplicateFields = [];
171 }
172
173 $fields = $copyAfterDuplicateFields;
174 $fields[] = 'uid';
175 $fields[] = 'pid';
176 $fields[] = $sortField;
177 $fields = array_unique($fields);
178
179 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
180 ->getQueryBuilderForTable($table);
181 $queryBuilder->getRestrictions()->removeAll();
182
183 $currentRecord = $queryBuilder
184 ->select(...$fields)
185 ->from($table)
186 ->where($queryBuilder->expr()->eq(
187 'uid',
188 $queryBuilder->createNamedParameter($uid, \PDO::PARAM_INT)
189 ))
190 ->execute()
191 ->fetch();
192
193 if (is_array($currentRecord)) {
194 // Fetch the record before or after the current one
195 // to define the data handler commands
196 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
197 ->getQueryBuilderForTable($table);
198
199 $queryBuilder
200 ->select('uid', 'pid')
201 ->from($table)
202 ->where($queryBuilder->expr()->eq(
203 'pid',
204 $queryBuilder->createNamedParameter($currentRecord['pid'], \PDO::PARAM_INT)
205 ))
206 ->setMaxResults(2);
207
208 // Disable the default restrictions (but not all) if the admin panel is in preview mode
209 if ($this->user->adminPanel instanceof AdminPanelView && $this->user->adminPanel->extGetFeAdminValue('preview')) {
210 $queryBuilder->getRestrictions()
211 ->removeByType(StartTimeRestriction::class)
212 ->removeByType(EndTimeRestriction::class)
213 ->removeByType(HiddenRestriction::class)
214 ->removeByType(FrontendGroupRestriction::class);
215 }
216
217 if (!empty($copyAfterDuplicateFields)) {
218 foreach ($copyAfterDuplicateFields as $fieldName) {
219 $queryBuilder->andWhere($queryBuilder->expr()->eq(
220 $fieldName,
221 $queryBuilder->createNamedParameter($currentRecord[$fieldName], \PDO::PARAM_STR)
222 ));
223 }
224 }
225 if (!empty($direction)) {
226 if ($direction === 'up') {
227 $queryBuilder->andWhere(
228 $queryBuilder->expr()->lt(
229 $sortField,
230 $queryBuilder->createNamedParameter($currentRecord[$sortField], \PDO::PARAM_INT)
231 )
232 );
233 $queryBuilder->orderBy($sortField, 'DESC');
234 } else {
235 $queryBuilder->andWhere(
236 $queryBuilder->expr()->gt(
237 $sortField,
238 $queryBuilder->createNamedParameter($currentRecord[$sortField], \PDO::PARAM_INT)
239 )
240 );
241 $queryBuilder->orderBy($sortField, 'ASC');
242 }
243 }
244
245 $result = $queryBuilder->execute();
246 if ($recordBefore = $result->fetch()) {
247 if ($afterUID) {
248 $dataHandlerCommands[$table][$uid]['move'] = -$afterUID;
249 } elseif ($direction === 'down') {
250 $dataHandlerCommands[$table][$uid]['move'] = -$recordBefore['uid'];
251 } elseif ($recordAfter = $result->fetch()) {
252 // Must take the second record above...
253 $dataHandlerCommands[$table][$uid]['move'] = -$recordAfter['uid'];
254 } else {
255 // ... and if that does not exist, use pid
256 $dataHandlerCommands[$table][$uid]['move'] = $currentRecord['pid'];
257 }
258 } elseif ($direction === 'up') {
259 $dataHandlerCommands[$table][$uid]['move'] = $currentRecord['pid'];
260 }
261 }
262
263 // If any data handler commands were set, execute the data handler command
264 if (!empty($dataHandlerCommands)) {
265 $dataHandler = GeneralUtility::makeInstance(DataHandler::class);
266 $dataHandler->start([], $dataHandlerCommands);
267 $dataHandler->process_cmdmap();
268 }
269 }
270 }
271
272 /**
273 * Deletes a specific record.
274 *
275 * @param string $table The table name for the record to delete.
276 * @param int $uid The UID for the record to delete.
277 */
278 protected function doDelete(string $table, int $uid)
279 {
280 $cmdData[$table][$uid]['delete'] = 1;
281 if (!empty($cmdData)) {
282 $dataHandler = GeneralUtility::makeInstance(DataHandler::class);
283 $dataHandler->start([], $cmdData);
284 $dataHandler->process_cmdmap();
285 }
286 }
287
288 /**
289 * Saves a record based on its data array.
290 *
291 * @param string $table The table name for the record to save.
292 * @param int $uid The UID for the record to save.
293 */
294 protected function doSave(string $table, int $uid)
295 {
296 $data = $this->configuration['data'];
297 if (!empty($data)) {
298 $dataHandler = GeneralUtility::makeInstance(DataHandler::class);
299 $dataHandler->start($data, []);
300 $dataHandler->process_uploads($_FILES);
301 $dataHandler->process_datamap();
302 // Save the new UID back into configuration
303 $newUID = $dataHandler->substNEWwithIDs['NEW'];
304 if ($newUID) {
305 $this->configuration['newUID'] = $newUID;
306 }
307 }
308 }
309
310 /**
311 * Saves a record based on its data array and closes it.
312 * Note: This method is only a wrapper for doSave() but is needed so
313 *
314 * @param string $table The table name for the record to save.
315 * @param int $uid The UID for the record to save.
316 */
317 protected function doSaveAndClose(string $table, int $uid)
318 {
319 $this->doSave($table, $uid);
320 }
321
322 /**
323 * Stub for closing a record. No real functionality needed since content
324 * element rendering will take care of everything.
325 *
326 * @param string $table The table name for the record to close.
327 * @param int $uid The UID for the record to close.
328 */
329 protected function doClose(string $table, int $uid)
330 {
331 }
332 }