TYPO3  7.6
DeletedRecords.php
Go to the documentation of this file.
1 <?php
2 namespace TYPO3\CMS\Recycler\Domain\Model;
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 = array();
32 
38  protected $limit = '';
39 
45  protected $table = array();
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 += count($deletedRecords[$tableName]);
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(',', array($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(',', array($tempOffset, $rowCount));
189  // set the limit's row count to 0
190  $this->limit = implode(',', array($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(',', array($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(',', array($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  foreach ($rows as $row) {
260  $this->setDeletedRows($table, $row);
261  }
262  }
263  }
264 
273  protected function escapeValueForLike($value, $tableName)
274  {
275  $db = $this->getDatabaseConnection();
276  return $db->escapeStrForLike($db->quoteStr($value, $tableName), $tableName);
277  }
278 
279  /************************************************************
280  * DELETE FUNCTIONS
281  ************************************************************/
288  public function deleteData($recordsArray)
289  {
290  if (is_array($recordsArray)) {
292  $tce = GeneralUtility::makeInstance(DataHandler::class);
293  $tce->start('', '');
294  $tce->disableDeleteClause();
295  foreach ($recordsArray as $record) {
296  list($table, $uid) = explode(':', $record);
297  $tce->deleteEl($table, (int)$uid, true, true);
298  }
299  return true;
300  }
301  return false;
302  }
303 
304  /************************************************************
305  * UNDELETE FUNCTIONS
306  ************************************************************/
315  public function undeleteData($recordsArray, $recursive = false)
316  {
317  $result = false;
318  $depth = 999;
319  if (is_array($recordsArray)) {
320  $this->deletedRows = array();
321  $cmd = array();
322  foreach ($recordsArray as $record) {
323  list($table, $uid) = explode(':', $record);
324  $cmd[$table][$uid]['undelete'] = 1;
325  if ($table === 'pages' && $recursive) {
326  $this->loadData($uid, '', $depth, '');
327  $childRecords = $this->getDeletedRows();
328  if (count($childRecords) > 0) {
329  foreach ($childRecords as $childTable => $childRows) {
330  foreach ($childRows as $childRow) {
331  $cmd[$childTable][$childRow['uid']]['undelete'] = 1;
332  }
333  }
334  }
335  }
336  }
337  if ($cmd) {
338  $tce = GeneralUtility::makeInstance(DataHandler::class);
339  $tce->start(array(), $cmd);
340  $tce->process_cmdmap();
341  $result = true;
342  }
343  }
344  return $result;
345  }
346 
347  /************************************************************
348  * SETTER FUNCTIONS
349  ************************************************************/
357  public function setDeletedRows($table, array $row)
358  {
359  $this->deletedRows[$table][] = $row;
360  }
361 
362  /************************************************************
363  * GETTER FUNCTIONS
364  ************************************************************/
370  public function getDeletedRows()
371  {
372  return $this->deletedRows;
373  }
374 
380  public function getTable()
381  {
382  return $this->table;
383  }
384 
390  protected function getDatabaseConnection()
391  {
392  return $GLOBALS['TYPO3_DB'];
393  }
394 }