TYPO3 CMS  TYPO3_7-6
DeletedRecords.php
Go to the documentation of this file.
1 <?php
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 
20 
25 {
31  protected $deletedRows = [];
32 
38  protected $limit = '';
39 
45  protected $table = [];
46 
52  protected $recyclerHelper;
53 
59  public $label;
60 
66  public $title;
67 
68  /************************************************************
69  * GET DATA FUNCTIONS
70  *
71  *
72  ************************************************************/
84  public function loadData($id, $table, $depth, $limit = '', $filter = '')
85  {
86  // set the limit
87  $this->limit = trim($limit);
88  if ($table) {
90  $this->table[] = $table;
91  $this->setData($id, $table, $depth, $GLOBALS['TCA'][$table]['ctrl'], $filter);
92  }
93  } else {
94  foreach ($GLOBALS['TCA'] as $tableKey => $tableValue) {
95  // only go into this table if the limit allows it
96  if ($this->limit !== '') {
97  $parts = GeneralUtility::trimExplode(',', $this->limit);
98  // abort loop if LIMIT 0,0
99  if ((int)$parts[0] === 0 && (int)$parts[1] === 0) {
100  break;
101  }
102  }
103  $this->table[] = $tableKey;
104  $this->setData($id, $tableKey, $depth, $tableValue['ctrl'], $filter);
105  }
106  }
107  return $this;
108  }
109 
119  public function getTotalCount($id, $table, $depth, $filter)
120  {
121  $deletedRecords = $this->loadData($id, $table, $depth, '', $filter)->getDeletedRows();
122  $countTotal = 0;
123  foreach ($this->table as $tableName) {
124  $countTotal += isset($deletedRecords[$tableName]) ? count($deletedRecords[$tableName]) : 0;
125  }
126  return $countTotal;
127  }
128 
139  protected function setData($id, $table, $depth, $tcaCtrl, $filter)
140  {
141  $id = (int)$id;
142  if (!array_key_exists('delete', $tcaCtrl)) {
143  return;
144  }
145  $db = $this->getDatabaseConnection();
146  // find the 'deleted' field for this table
147  $deletedField = RecyclerUtility::getDeletedField($table);
148  // create the filter WHERE-clause
149  $filterWhere = '';
150  if (trim($filter) != '') {
151  $filterWhere = ' AND (' . (\TYPO3\CMS\Core\Utility\MathUtility::canBeInterpretedAsInteger($filter) ? 'uid = ' . $filter . ' OR pid = ' . $filter . ' OR ' : '') . $tcaCtrl['label'] . ' LIKE "%' . $this->escapeValueForLike($filter, $table) . '%"' . ')';
152  }
153 
154  // get the limit
155  if (!empty($this->limit)) {
156  // count the number of deleted records for this pid
157  $deletedCount = $db->exec_SELECTcountRows('uid', $table, $deletedField . '<>0 AND pid = ' . $id . $filterWhere);
158  // split the limit
159  $parts = GeneralUtility::trimExplode(',', $this->limit);
160  $offset = $parts[0];
161  $rowCount = $parts[1];
162  // subtract the number of deleted records from the limit's offset
163  $result = $offset - $deletedCount;
164  // if the result is >= 0
165  if ($result >= 0) {
166  // store the new offset in the limit and go into the next depth
167  $offset = $result;
168  $this->limit = implode(',', [$offset, $rowCount]);
169  // do NOT query this depth; limit also does not need to be set, we set it anyways
170  $allowQuery = false;
171  $allowDepth = true;
172  $limit = '';
173  } else {
174  // the offset for the temporary limit has to remain like the original offset
175  // in case the original offset was just crossed by the amount of deleted records
176  if ($offset !== 0) {
177  $tempOffset = $offset;
178  } else {
179  $tempOffset = 0;
180  }
181  // set the offset in the limit to 0
182  $newOffset = 0;
183  // convert to negative result to the positive equivalent
184  $absResult = abs($result);
185  // if the result now is > limit's row count
186  if ($absResult > $rowCount) {
187  // use the limit's row count as the temporary limit
188  $limit = implode(',', [$tempOffset, $rowCount]);
189  // set the limit's row count to 0
190  $this->limit = implode(',', [$newOffset, 0]);
191  // do not go into new depth
192  $allowDepth = false;
193  } else {
194  // if the result now is <= limit's row count
195  // use the result as the temporary limit
196  $limit = implode(',', [$tempOffset, $absResult]);
197  // subtract the result from the row count
198  $newCount = $rowCount - $absResult;
199  // store the new result in the limit's row count
200  $this->limit = implode(',', [$newOffset, $newCount]);
201  // if the new row count is > 0
202  if ($newCount > 0) {
203  // go into new depth
204  $allowDepth = true;
205  } else {
206  // if the new row count is <= 0 (only =0 makes sense though)
207  // do not go into new depth
208  $allowDepth = false;
209  }
210  }
211  // allow query for this depth
212  $allowQuery = true;
213  }
214  } else {
215  $limit = '';
216  $allowDepth = true;
217  $allowQuery = true;
218  }
219  // query for actual deleted records
220  if ($allowQuery) {
221  $recordsToCheck = \TYPO3\CMS\Backend\Utility\BackendUtility::getRecordsByField($table, $deletedField, '1', ' AND pid = ' . $id . $filterWhere, '', '', $limit, false);
222  if ($recordsToCheck) {
223  $this->checkRecordAccess($table, $recordsToCheck);
224  }
225  }
226  // go into depth
227  if ($allowDepth && $depth >= 1) {
228  // check recursively for elements beneath this page
229  $resPages = $db->exec_SELECTquery('uid', 'pages', 'pid=' . $id, '', 'sorting');
230  if ($resPages) {
231  while ($rowPages = $db->sql_fetch_assoc($resPages)) {
232  $this->setData($rowPages['uid'], $table, $depth - 1, $tcaCtrl, $filter);
233  // some records might have been added, check if we still have the limit for further queries
234  if (!empty($this->limit)) {
235  $parts = GeneralUtility::trimExplode(',', $this->limit);
236  // abort loop if LIMIT 0,0
237  if ((int)$parts[0] === 0 && (int)$parts[1] === 0) {
238  break;
239  }
240  }
241  }
242  $db->sql_free_result($resPages);
243  }
244  }
245  $this->label[$table] = $tcaCtrl['label'];
246  $this->title[$table] = $tcaCtrl['title'];
247  }
248 
256  protected function checkRecordAccess($table, array $rows)
257  {
258  if ($table === 'pages') {
259  // The "checkAccess" method validates access to the passed table/rows. When access to
260  // a page record gets validated it is necessary to disable the "delete" field temporarily
261  // for the recycler.
262  // Else it wouldn't be possible to perform the check as many methods of BackendUtility
263  // like "BEgetRootLine", etc. will only work on non-deleted records.
264  $deleteField = $GLOBALS['TCA'][$table]['ctrl']['delete'];
265  unset($GLOBALS['TCA'][$table]['ctrl']['delete']);
266  }
267 
268  foreach ($rows as $row) {
270  $this->setDeletedRows($table, $row);
271  }
272  }
273 
274  if ($table === 'pages') {
275  $GLOBALS['TCA'][$table]['ctrl']['delete'] = $deleteField;
276  }
277  }
278 
287  protected function escapeValueForLike($value, $tableName)
288  {
289  $db = $this->getDatabaseConnection();
290  return $db->escapeStrForLike($db->quoteStr($value, $tableName), $tableName);
291  }
292 
293  /************************************************************
294  * DELETE FUNCTIONS
295  ************************************************************/
302  public function deleteData($recordsArray)
303  {
304  if (is_array($recordsArray)) {
306  $tce = GeneralUtility::makeInstance(DataHandler::class);
307  $tce->start('', '');
308  $tce->disableDeleteClause();
309  foreach ($recordsArray as $record) {
310  list($table, $uid) = explode(':', $record);
311  $tce->deleteEl($table, (int)$uid, true, true);
312  }
313  return true;
314  }
315  return false;
316  }
317 
318  /************************************************************
319  * UNDELETE FUNCTIONS
320  ************************************************************/
329  public function undeleteData($recordsArray, $recursive = false)
330  {
331  $result = false;
332  $depth = 999;
333  if (is_array($recordsArray)) {
334  $this->deletedRows = [];
335  $cmd = [];
336  foreach ($recordsArray as $record) {
337  list($table, $uid) = explode(':', $record);
338  // get all parent pages and cover them
340  if ($pid > 0) {
341  $parentUidsToRecover = $this->getDeletedParentPages($pid);
342  $count = count($parentUidsToRecover);
343  for ($i = 0; $i < $count; ++$i) {
344  $parentUid = $parentUidsToRecover[$i];
345  $cmd['pages'][$parentUid]['undelete'] = 1;
346  $affectedRecords++;
347  }
348  if (isset($cmd['pages'])) {
349  // reverse the page list to recover it from top to bottom
350  $cmd['pages'] = array_reverse($cmd['pages'], true);
351  }
352  }
353  $cmd[$table][$uid]['undelete'] = 1;
354  if ($table === 'pages' && $recursive) {
355  $this->loadData($uid, '', $depth, '');
356  $childRecords = $this->getDeletedRows();
357  if (count($childRecords) > 0) {
358  foreach ($childRecords as $childTable => $childRows) {
359  foreach ($childRows as $childRow) {
360  $cmd[$childTable][$childRow['uid']]['undelete'] = 1;
361  }
362  }
363  }
364  }
365  }
366  if ($cmd) {
367  $tce = GeneralUtility::makeInstance(DataHandler::class);
368  $tce->start([], $cmd);
369  $tce->process_cmdmap();
370  $result = true;
371  }
372  }
373  return $result;
374  }
375 
383  protected function getDeletedParentPages($uid, &$pages = [])
384  {
385  $db = $this->getDatabaseConnection();
386  $res = $db->exec_SELECTquery('uid, pid', 'pages', 'uid=' . (int)$uid . ' AND ' . $GLOBALS['TCA']['pages']['ctrl']['delete'] . '=1');
387  if ($res !== false && $db->sql_num_rows($res) > 0) {
388  $record = $db->sql_fetch_assoc($res);
389  $pages[] = $record['uid'];
390  if ((int)$record['pid'] !== 0) {
391  $this->getDeletedParentPages($record['pid'], $pages);
392  }
393  }
394 
395  return $pages;
396  }
397 
398  /************************************************************
399  * SETTER FUNCTIONS
400  ************************************************************/
408  public function setDeletedRows($table, array $row)
409  {
410  $this->deletedRows[$table][] = $row;
411  }
412 
413  /************************************************************
414  * GETTER FUNCTIONS
415  ************************************************************/
421  public function getDeletedRows()
422  {
423  return $this->deletedRows;
424  }
425 
431  public function getTable()
432  {
433  return $this->table;
434  }
435 
441  protected function getDatabaseConnection()
442  {
443  return $GLOBALS['TYPO3_DB'];
444  }
445 }
static getRecordsByField($theTable, $theField, $theValue, $whereClause='', $groupBy='', $orderBy='', $limit='', $useDeleteClause=true)
undeleteData($recordsArray, $recursive=false)
static trimExplode($delim, $string, $removeEmptyValues=false, $limit=0)
setData($id, $table, $depth, $tcaCtrl, $filter)
getTotalCount($id, $table, $depth, $filter)
loadData($id, $table, $depth, $limit='', $filter='')
$uid
Definition: server.php:38
if(TYPO3_MODE==='BE') $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsfebeuserauth.php']['frontendEditingController']['default']