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