‪TYPO3CMS  11.5
RecyclerAjaxController.php
Go to the documentation of this file.
1 <?php
2 
3 /*
4  * This file is part of the TYPO3 CMS project.
5  *
6  * It is free software; you can redistribute it and/or modify it under
7  * the terms of the GNU General Public License, either version 2
8  * of the License, or any later version.
9  *
10  * For the full copyright and license information, please read the
11  * LICENSE.txt file that was distributed with this source code.
12  *
13  * The TYPO3 project - inspiring people to share!
14  */
15 
17 
18 use Psr\Http\Message\ResponseInterface;
19 use Psr\Http\Message\ServerRequestInterface;
20 use TYPO3\CMS\Backend\Utility\BackendUtility;
40 
46 {
52  protected ‪$conf = [];
53 
57  protected ‪$runtimeCache;
58 
62  protected ‪$tce;
63 
64  public function ‪__construct()
65  {
66  $this->runtimeCache = $this->‪getMemoryCache();
67  $this->tce = GeneralUtility::makeInstance(DataHandler::class);
68  }
69 
76  public function ‪dispatch(ServerRequestInterface $request): ResponseInterface
77  {
78  $parsedBody = $request->getParsedBody();
79  $queryParams = $request->getQueryParams();
80 
81  $this->conf['action'] = $parsedBody['action'] ?? $queryParams['action'] ?? null;
82  $this->conf['table'] = $parsedBody['table'] ?? $queryParams['table'] ?? '';
83  $this->conf['limit'] = ‪MathUtility::forceIntegerInRange(
84  (int)($this->‪getBackendUser()->getTSConfig()['mod.']['recycler.']['recordsPageLimit'] ?? 25),
85  1
86  );
87  $this->conf['start'] = (int)($parsedBody['start'] ?? $queryParams['start'] ?? 0);
88  $this->conf['filterTxt'] = $parsedBody['filterTxt'] ?? $queryParams['filterTxt'] ?? '';
89  $this->conf['startUid'] = (int)($parsedBody['startUid'] ?? $queryParams['startUid'] ?? 0);
90  $this->conf['depth'] = (int)($parsedBody['depth'] ?? $queryParams['depth'] ?? 0);
91  $this->conf['records'] = $parsedBody['records'] ?? $queryParams['records'] ?? null;
92  $this->conf['recursive'] = (bool)($parsedBody['recursive'] ?? $queryParams['recursive'] ?? false);
93 
94  $extPath = ‪ExtensionManagementUtility::extPath('recycler');
95  $view = GeneralUtility::makeInstance(StandaloneView::class);
96  $view->setPartialRootPaths(['default' => $extPath . 'Resources/Private/Partials']);
97 
98  $content = null;
99  // Determine the scripts to execute
100  switch ($this->conf['action']) {
101  case 'getTables':
102  $this->‪setDataInSession(['depthSelection' => $this->conf['depth']]);
103 
104  $model = GeneralUtility::makeInstance(Tables::class);
105  $content = $model->getTables($this->conf['startUid'], $this->conf['depth']);
106  break;
107  case 'getDeletedRecords':
108  $this->‪setDataInSession([
109  'tableSelection' => $this->conf['table'],
110  'depthSelection' => $this->conf['depth'],
111  'resultLimit' => $this->conf['limit'],
112  ]);
113 
114  $model = GeneralUtility::makeInstance(DeletedRecords::class);
115  $model->loadData($this->conf['startUid'], $this->conf['table'], $this->conf['depth'], $this->conf['start'] . ',' . $this->conf['limit'], $this->conf['filterTxt']);
116  $deletedRowsArray = $model->getDeletedRows();
117 
118  $model = GeneralUtility::makeInstance(DeletedRecords::class);
119  $totalDeleted = $model->getTotalCount($this->conf['startUid'], $this->conf['table'], $this->conf['depth'], $this->conf['filterTxt']);
120 
121  $allowDelete = $this->‪getBackendUser()->isAdmin()
122  ?: (bool)($this->‪getBackendUser()->getTSConfig()['mod.']['recycler.']['allowDelete'] ?? false);
123 
124  $view->setTemplatePathAndFilename($extPath . 'Resources/Private/Templates/Ajax/RecordsTable.html');
125  $view->assign('showTableHeader', empty($this->conf['table']));
126  $view->assign('showTableName', $this->‪getBackendUser()->shallDisplayDebugInformation());
127  $view->assign('allowDelete', $allowDelete);
128  $view->assign('groupedRecords', $this->‪transform($deletedRowsArray));
129  $content = [
130  'rows' => $view->render(),
131  'totalItems' => $totalDeleted,
132  ];
133  break;
134  case 'undoRecords':
135  if (empty($this->conf['records']) || !is_array($this->conf['records'])) {
136  $content = [
137  'success' => false,
138  'message' => ‪LocalizationUtility::translate('flashmessage.delete.norecordsselected', 'recycler'),
139  ];
140  break;
141  }
142 
143  $model = GeneralUtility::makeInstance(DeletedRecords::class);
144  $affectedRecords = $model->undeleteData($this->conf['records'], $this->conf['recursive']);
145  $messageKey = 'flashmessage.undo.' . ($affectedRecords !== false ? 'success' : 'failure') . '.' . ((int)$affectedRecords === 1 ? 'singular' : 'plural');
146  $content = [
147  'success' => true,
148  'message' => sprintf((string)‪LocalizationUtility::translate($messageKey, 'recycler'), $affectedRecords),
149  ];
150  break;
151  case 'deleteRecords':
152  if (empty($this->conf['records']) || !is_array($this->conf['records'])) {
153  $content = [
154  'success' => false,
155  'message' => ‪LocalizationUtility::translate('flashmessage.delete.norecordsselected', 'recycler'),
156  ];
157  break;
158  }
159 
160  $model = GeneralUtility::makeInstance(DeletedRecords::class);
161  $success = $model->deleteData($this->conf['records'] ?? null);
162  $affectedRecords = count($this->conf['records']);
163  $messageKey = 'flashmessage.delete.' . ($success ? 'success' : 'failure') . '.' . ($affectedRecords === 1 ? 'singular' : 'plural');
164  $content = [
165  'success' => true,
166  'message' => sprintf((string)‪LocalizationUtility::translate($messageKey, 'recycler'), $affectedRecords),
167  ];
168  break;
169  }
170  return new ‪JsonResponse($content);
171  }
172 
179  protected function ‪transform(array $deletedRowsArray): array
180  {
181  $groupedRecords = [];
182  $lang = $this->‪getLanguageService();
183  $iconFactory = GeneralUtility::makeInstance(IconFactory::class);
184 
185  foreach ($deletedRowsArray as $table => $rows) {
186  $groupedRecords[$table]['information'] = [
187  'table' => $table,
188  'title' => isset(‪$GLOBALS['TCA'][$table]['ctrl']['title']) ? $lang->sL(‪$GLOBALS['TCA'][$table]['ctrl']['title']) : BackendUtility::getNoRecordTitle(),
189  ];
190  foreach ($rows as $row) {
191  $pageTitle = $this->‪getPageTitle((int)$row['pid']);
192  $userIdField = ‪$GLOBALS['TCA'][$table]['ctrl']['cruser_id'] ?? '';
193  $backendUserName = $this->‪getBackendUserInformation($userIdField !== '' ? (int)$row[$userIdField] : 0);
194  $userIdWhoDeleted = $this->‪getUserWhoDeleted($table, (int)$row['uid']);
195 
196  $groupedRecords[$table]['records'][] = [
197  'uid' => $row['uid'],
198  'pid' => $row['pid'],
199  'icon' => $iconFactory->getIconForRecord($table, $row, ‪Icon::SIZE_SMALL)->render(),
200  'pageTitle' => $pageTitle,
201  'crdate' => isset(‪$GLOBALS['TCA'][$table]['ctrl']['crdate']) ? BackendUtility::datetime($row[‪$GLOBALS['TCA'][$table]['ctrl']['crdate']]) : '',
202  'tstamp' => isset(‪$GLOBALS['TCA'][$table]['ctrl']['tstamp']) ? BackendUtility::datetime($row[‪$GLOBALS['TCA'][$table]['ctrl']['tstamp']]) : '',
203  'owner' => $backendUserName,
204  'owner_uid' => $userIdField !== '' ? $row[$userIdField] : 0,
205  'title' => BackendUtility::getRecordTitle($table, $row),
206  'path' => ‪RecyclerUtility::getRecordPath((int)$row['pid']),
207  'delete_user_uid' => $userIdWhoDeleted,
208  'delete_user' => $this->‪getBackendUserInformation($userIdWhoDeleted),
209  'isParentDeleted' => $table === 'pages' && ‪RecyclerUtility::isParentPageDeleted((int)$row['pid']),
210  ];
211  }
212  }
213 
214  return $groupedRecords;
215  }
216 
220  protected function ‪getPageTitle(int $pageId): string
221  {
222  $cacheId = 'recycler-pagetitle-' . $pageId;
223  $pageTitle = $this->runtimeCache->get($cacheId);
224  if ($pageTitle === false) {
225  if ($pageId === 0) {
226  $pageTitle = ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'];
227  } else {
228  $recordInfo = $this->tce->recordInfo('pages', $pageId, 'title');
229  $pageTitle = $recordInfo['title'];
230  }
231  $this->runtimeCache->set($cacheId, $pageTitle);
232  }
233  return $pageTitle;
234  }
235 
239  protected function ‪getBackendUserInformation(int $userId): string
240  {
241  if ($userId === 0) {
242  return '';
243  }
244  $cacheId = 'recycler-user-' . $userId;
245  $username = $this->runtimeCache->get($cacheId);
246  if ($username === false) {
247  $backendUser = BackendUtility::getRecord('be_users', $userId, 'username', '', false);
248  if ($backendUser === null) {
249  $username = sprintf(
250  '[%s]',
251  ‪LocalizationUtility::translate('LLL:EXT:recycler/Resources/Private/Language/locallang.xlf:record.deleted')
252  );
253  } else {
254  $username = $backendUser['username'];
255  }
256  $this->runtimeCache->set($cacheId, $username);
257  }
258  return $username;
259  }
260 
264  protected function ‪getUserWhoDeleted(string $table, int $uid): int
265  {
266  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_history');
267  $queryBuilder->select('userid')
268  ->from('sys_history')
269  ->where(
270  $queryBuilder->expr()->eq(
271  'tablename',
272  $queryBuilder->createNamedParameter($table, ‪Connection::PARAM_STR)
273  ),
274  $queryBuilder->expr()->eq(
275  'usertype',
276  $queryBuilder->createNamedParameter('BE', ‪Connection::PARAM_STR)
277  ),
278  $queryBuilder->expr()->eq(
279  'recuid',
280  $queryBuilder->createNamedParameter($uid, ‪Connection::PARAM_INT)
281  ),
282  $queryBuilder->expr()->eq(
283  'actiontype',
284  $queryBuilder->createNamedParameter(‪RecordHistoryStore::ACTION_DELETE, ‪Connection::PARAM_INT)
285  )
286  )
287  ->setMaxResults(1);
288 
289  return (int)$queryBuilder->executeQuery()->fetchOne();
290  }
291 
297  protected function ‪setDataInSession(array $data): void
298  {
299  $beUser = $this->‪getBackendUser();
300  $recyclerUC = $beUser->uc['tx_recycler'] ?? [];
301  if (!empty(array_diff_assoc($data, $recyclerUC))) {
302  $beUser->uc['tx_recycler'] = array_merge($recyclerUC, $data);
303  $beUser->writeUC();
304  }
305  }
306 
307  protected function ‪getMemoryCache(): FrontendInterface
308  {
309  return $this->‪getCacheManager()->getCache('runtime');
310  }
311 
312  protected function ‪getCacheManager(): ‪CacheManager
313  {
314  return GeneralUtility::makeInstance(CacheManager::class);
315  }
316 
317  protected function ‪getBackendUser(): ‪BackendUserAuthentication
318  {
319  return ‪$GLOBALS['BE_USER'];
320  }
321 
322  protected function ‪getLanguageService(): ‪LanguageService
323  {
324  return ‪$GLOBALS['LANG'];
325  }
326 }
‪TYPO3\CMS\Core\DataHandling\DataHandler
Definition: DataHandler.php:86
‪TYPO3\CMS\Core\Imaging\Icon\SIZE_SMALL
‪const SIZE_SMALL
Definition: Icon.php:30
‪TYPO3\CMS\Recycler\Controller\RecyclerAjaxController\getLanguageService
‪getLanguageService()
Definition: RecyclerAjaxController.php:319
‪TYPO3\CMS\Core\Database\Connection\PARAM_INT
‪const PARAM_INT
Definition: Connection.php:49
‪TYPO3\CMS\Extbase\Utility\LocalizationUtility
Definition: LocalizationUtility.php:33
‪TYPO3\CMS\Core\Imaging\Icon
Definition: Icon.php:26
‪TYPO3\CMS\Recycler\Controller\RecyclerAjaxController\getPageTitle
‪getPageTitle(int $pageId)
Definition: RecyclerAjaxController.php:217
‪TYPO3\CMS\Recycler\Controller\RecyclerAjaxController\__construct
‪__construct()
Definition: RecyclerAjaxController.php:61
‪TYPO3\CMS\Recycler\Controller\RecyclerAjaxController\$conf
‪array $conf
Definition: RecyclerAjaxController.php:51
‪TYPO3\CMS\Recycler\Domain\Model\Tables
Definition: Tables.php:28
‪TYPO3\CMS\Recycler\Controller\RecyclerAjaxController\$runtimeCache
‪FrontendInterface $runtimeCache
Definition: RecyclerAjaxController.php:55
‪TYPO3\CMS\Core\Utility\MathUtility\forceIntegerInRange
‪static int forceIntegerInRange($theInt, $min, $max=2000000000, $defaultValue=0)
Definition: MathUtility.php:32
‪TYPO3\CMS\Recycler\Utility\RecyclerUtility\getRecordPath
‪static string getRecordPath($uid)
Definition: RecyclerUtility.php:85
‪TYPO3\CMS\Core\Imaging\IconFactory
Definition: IconFactory.php:34
‪TYPO3\CMS\Recycler\Controller\RecyclerAjaxController\getCacheManager
‪getCacheManager()
Definition: RecyclerAjaxController.php:309
‪TYPO3\CMS\Recycler\Controller\RecyclerAjaxController\getUserWhoDeleted
‪getUserWhoDeleted(string $table, int $uid)
Definition: RecyclerAjaxController.php:261
‪TYPO3\CMS\Recycler\Controller\RecyclerAjaxController\setDataInSession
‪setDataInSession(array $data)
Definition: RecyclerAjaxController.php:294
‪TYPO3\CMS\Core\Database\Connection\PARAM_STR
‪const PARAM_STR
Definition: Connection.php:54
‪TYPO3\CMS\Recycler\Controller
Definition: RecyclerAjaxController.php:16
‪TYPO3\CMS\Recycler\Controller\RecyclerAjaxController\getBackendUser
‪getBackendUser()
Definition: RecyclerAjaxController.php:314
‪TYPO3\CMS\Core\Utility\ExtensionManagementUtility
Definition: ExtensionManagementUtility.php:43
‪TYPO3\CMS\Core\DataHandling\History\RecordHistoryStore
Definition: RecordHistoryStore.php:31
‪TYPO3\CMS\Recycler\Controller\RecyclerAjaxController\dispatch
‪ResponseInterface dispatch(ServerRequestInterface $request)
Definition: RecyclerAjaxController.php:73
‪TYPO3\CMS\Recycler\Controller\RecyclerAjaxController\getMemoryCache
‪getMemoryCache()
Definition: RecyclerAjaxController.php:304
‪TYPO3\CMS\Extbase\Utility\LocalizationUtility\translate
‪static string null translate(string $key, ?string $extensionName=null, array $arguments=null, string $languageKey=null, array $alternativeLanguageKeys=null)
Definition: LocalizationUtility.php:67
‪TYPO3\CMS\Core\Cache\CacheManager
Definition: CacheManager.php:36
‪TYPO3\CMS\Core\Authentication\BackendUserAuthentication
Definition: BackendUserAuthentication.php:62
‪TYPO3\CMS\Recycler\Controller\RecyclerAjaxController\transform
‪transform(array $deletedRowsArray)
Definition: RecyclerAjaxController.php:176
‪TYPO3\CMS\Core\Database\Connection
Definition: Connection.php:38
‪TYPO3\CMS\Core\Cache\Frontend\FrontendInterface
Definition: FrontendInterface.php:22
‪TYPO3\CMS\Recycler\Domain\Model\DeletedRecords
Definition: DeletedRecords.php:38
‪TYPO3\CMS\Recycler\Controller\RecyclerAjaxController\$tce
‪DataHandler $tce
Definition: RecyclerAjaxController.php:59
‪TYPO3\CMS\Fluid\View\StandaloneView
Definition: StandaloneView.php:31
‪TYPO3\CMS\Recycler\Utility\RecyclerUtility\isParentPageDeleted
‪static bool isParentPageDeleted($pid)
Definition: RecyclerUtility.php:143
‪TYPO3\CMS\Core\DataHandling\History\RecordHistoryStore\ACTION_DELETE
‪const ACTION_DELETE
Definition: RecordHistoryStore.php:35
‪TYPO3\CMS\Core\Http\JsonResponse
Definition: JsonResponse.php:28
‪TYPO3\CMS\Recycler\Utility\RecyclerUtility
Definition: RecyclerUtility.php:29
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Recycler\Controller\RecyclerAjaxController
Definition: RecyclerAjaxController.php:46
‪TYPO3\CMS\Core\Utility\MathUtility
Definition: MathUtility.php:22
‪TYPO3\CMS\Core\Utility\ExtensionManagementUtility\extPath
‪static string extPath($key, $script='')
Definition: ExtensionManagementUtility.php:142
‪TYPO3\CMS\Core\Localization\LanguageService
Definition: LanguageService.php:42
‪TYPO3\CMS\Core\Database\ConnectionPool
Definition: ConnectionPool.php:46
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:50
‪TYPO3\CMS\Recycler\Controller\RecyclerAjaxController\getBackendUserInformation
‪getBackendUserInformation(int $userId)
Definition: RecyclerAjaxController.php:236