[TASK] Clean up phpDoc comments in typo3/sysext/
[Packages/TYPO3.CMS.git] / typo3 / sysext / cms / tslib / hooks / class.tx_cms_treelistcacheupdate.php
1 <?php
2 /***************************************************************
3 * Copyright notice
4 *
5 * (c) 2008-2011 Ingo Renner (ingo@typo3.org)
6 * All rights reserved
7 *
8 * This script is part of the TYPO3 project. The TYPO3 project is
9 * free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * The GNU General Public License can be found at
15 * http://www.gnu.org/copyleft/gpl.html.
16 * A copy is found in the textfile GPL.txt and important notices to the license
17 * from the author is found in LICENSE.txt distributed with these scripts.
18 *
19 *
20 * This script is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
24 *
25 * This copyright notice MUST APPEAR in all copies of the script!
26 ***************************************************************/
27
28 /**
29 * Class that hooks into TCEmain and listens for updates to pages to update the
30 * treelist cache
31 *
32 * @author Ingo Renner <ingo@typo3.org>
33 * @package TYPO3
34 * @subpackage tslib
35 */
36 class tx_cms_treelistCacheUpdate {
37
38 // Should not be manipulated from others except through the
39 // configuration provided @see __construct()
40 private $updateRequiringFields = array(
41 'pid',
42 'php_tree_stop',
43 'extendToSubpages'
44 );
45
46 /**
47 * Constructor, adds update requiring fields to the default ones
48 */
49 public function __construct() {
50
51 // As enableFields can be set dynamically we add them here
52 $pagesEnableFields = $GLOBALS['TCA']['pages']['ctrl']['enablecolumns'];
53 foreach ($pagesEnableFields as $pagesEnableField) {
54 $this->updateRequiringFields[] = $pagesEnableField;
55 }
56 $this->updateRequiringFields[] = $GLOBALS['TCA']['pages']['ctrl']['delete'];
57
58 // Extension can add fields to the pages table that require an
59 // update of the treelist cache, too; so we also add those
60 // example: $TYPO3_CONF_VARS['BE']['additionalTreelistUpdateFields'] .= ',my_field';
61 if (!empty($GLOBALS['TYPO3_CONF_VARS']['BE']['additionalTreelistUpdateFields'])) {
62 $additionalTreelistUpdateFields = t3lib_div::trimExplode(
63 ',',
64 $GLOBALS['TYPO3_CONF_VARS']['BE']['additionalTreelistUpdateFields'],
65 TRUE
66 );
67
68 $this->updateRequiringFields += $additionalTreelistUpdateFields;
69 }
70 }
71
72 /**
73 * waits for TCEmain commands and looks for changed pages, if found further
74 * changes take place to determine whether the cache needs to be updated
75 *
76 * @param string $status TCEmain operation status, either 'new' or 'update'
77 * @param string $table The DB table the operation was carried out on
78 * @param mixed $recordId The record's uid for update records, a string to look the record's uid up after it has been created
79 * @param array $updatedFields Array of changed fiels and their new values
80 * @param t3lib_TCEmain $tceMain TCEmain parent object
81 * @return void
82 */
83 public function processDatamap_afterDatabaseOperations($status, $table, $recordId, array $updatedFields, t3lib_TCEmain $tceMain) {
84
85 if ($table == 'pages' && $this->requiresUpdate($updatedFields)) {
86 $affectedPagePid = 0;
87 $affectedPageUid = 0;
88
89 if ($status == 'new') {
90 // Detect new pages
91
92 // Resolve the uid
93 $affectedPageUid = $tceMain->substNEWwithIDs[$recordId];
94 $affectedPagePid = $updatedFields['pid'];
95 } elseif ($status == 'update') {
96 // Detect updated pages
97
98 $affectedPageUid = $recordId;
99
100 // When updating a page the pid is not directly available so we
101 // need to retrieve it ourselves.
102 $fullPageRecord = t3lib_BEfunc::getRecord($table, $recordId);
103 $affectedPagePid = $fullPageRecord['pid'];
104 }
105
106 $clearCacheActions = $this->determineClearCacheActions(
107 $status,
108 $updatedFields
109 );
110
111 $this->processClearCacheActions(
112 $affectedPageUid,
113 $affectedPagePid,
114 $updatedFields,
115 $clearCacheActions
116 );
117 }
118 }
119
120 /**
121 * waits for TCEmain commands and looks for deleted pages, if found further
122 * changes take place to determine whether the cache needs to be updated
123 *
124 * @param string $command The TCE command
125 * @param string $table The record's table
126 * @param integer $recordId The record's uid
127 * @param array $commandValue The commands value, typically an array with more detailed command information
128 * @param t3lib_TCEmain $tceMain The TCEmain parent object
129 * @return void
130 */
131 public function processCmdmap_postProcess($command, $table, $recordId, $commandValue, t3lib_TCEmain $tceMain) {
132
133 if ($table == 'pages' && $command == 'delete') {
134
135 $deletedRecord = t3lib_BEfunc::getRecord(
136 $table,
137 $recordId,
138 '*',
139 '',
140 FALSE
141 );
142
143 $affectedPageUid = $deletedRecord['uid'];
144 $affectedPagePid = $deletedRecord['pid'];
145 // Faking the updated fields
146 $updatedFields = array('deleted' => 1);
147
148 $clearCacheActions = $this->determineClearCacheActions(
149 'update',
150 $updatedFields
151 );
152
153 $this->processClearCacheActions(
154 $affectedPageUid,
155 $affectedPagePid,
156 $updatedFields,
157 $clearCacheActions
158 );
159 }
160 }
161
162 /**
163 * waits for TCEmain commands and looks for moved pages, if found further
164 * changes take place to determine whether the cache needs to be updated
165 *
166 * @param string $table Table name of the moved record
167 * @param integer $recordId The record's uid
168 * @param integer $destinationPid The record's destination page id
169 * @param array $movedRecord The record that moved
170 * @param array $updatedFields Array of changed fields
171 * @param t3lib_TCEmain $tceMain TCEmain parent object
172 * @return void
173 */
174 public function moveRecord_firstElementPostProcess($table, $recordId, $destinationPid, array $movedRecord, array $updatedFields, t3lib_TCEmain $tceMain) {
175
176 if ($table == 'pages' && $this->requiresUpdate($updatedFields)) {
177
178 $affectedPageUid = $recordId;
179 $affectedPageOldPid = $movedRecord['pid'];
180 $affectedPageNewPid = $updatedFields['pid'];
181
182 $clearCacheActions = $this->determineClearCacheActions(
183 'update',
184 $updatedFields
185 );
186
187 // Clear treelist entries for old parent page
188 $this->processClearCacheActions(
189 $affectedPageUid,
190 $affectedPageOldPid,
191 $updatedFields,
192 $clearCacheActions
193 );
194 // Clear treelist entries for new parent page
195 $this->processClearCacheActions(
196 $affectedPageUid,
197 $affectedPageNewPid,
198 $updatedFields,
199 $clearCacheActions
200 );
201 }
202 }
203
204 /**
205 * Waits for TCEmain commands and looks for moved pages, if found further
206 * changes take place to determine whether the cache needs to be updated
207 *
208 * @param string $table Table name of the moved record
209 * @param integer $recordId The record's uid
210 * @param integer $destinationPid The record's destination page id
211 * @param integer $originalDestinationPid (negative) page id th page has been moved after
212 * @param array $movedRecord The record that moved
213 * @param array $updatedFields Array of changed fields
214 * @param t3lib_TCEmain $tceMain TCEmain parent object
215 * @return void
216 */
217 public function moveRecord_afterAnotherElementPostProcess($table, $recordId, $destinationPid, $originalDestinationPid, array $movedRecord, array $updatedFields, t3lib_TCEmain $tceMain) {
218
219 if ($table == 'pages' && $this->requiresUpdate($updatedFields)) {
220
221 $affectedPageUid = $recordId;
222 $affectedPageOldPid = $movedRecord['pid'];
223 $affectedPageNewPid = $updatedFields['pid'];
224
225 $clearCacheActions = $this->determineClearCacheActions(
226 'update',
227 $updatedFields
228 );
229
230 // Clear treelist entries for old parent page
231 $this->processClearCacheActions(
232 $affectedPageUid,
233 $affectedPageOldPid,
234 $updatedFields,
235 $clearCacheActions
236 );
237 // Clear treelist entries for new parent page
238 $this->processClearCacheActions(
239 $affectedPageUid,
240 $affectedPageNewPid,
241 $updatedFields,
242 $clearCacheActions
243 );
244 }
245 }
246
247 /**
248 * Checks whether the change requires an update of the treelist cache
249 *
250 * @param array $updatedFields Array of changed fields
251 * @return boolean TRUE if the treelist cache needs to be updated, FALSE if no update to the cache is required
252 */
253 protected function requiresUpdate(array $updatedFields) {
254 $requiresUpdate = FALSE;
255
256 $updatedFieldNames = array_keys($updatedFields);
257 foreach ($updatedFieldNames as $updatedFieldName) {
258 if (in_array($updatedFieldName, $this->updateRequiringFields)) {
259 $requiresUpdate = TRUE;
260 break;
261 }
262 }
263
264 return $requiresUpdate;
265 }
266
267 /**
268 * Calls the cache maintainance functions according to the determined actions
269 *
270 * @param integer $affectedPage uid of the affected page
271 * @param integer $affectedParentPage parent uid of the affected page
272 * @param array $updatedFields Array of updated fields and their new values
273 * @param array $actions Array of actions to carry out
274 * @return void
275 */
276 protected function processClearCacheActions($affectedPage, $affectedParentPage, $updatedFields, array $actions) {
277 $actionNames = array_keys($actions);
278 foreach ($actionNames as $actionName) {
279 switch ($actionName) {
280 case 'allParents':
281 $this->clearCacheForAllParents($affectedParentPage);
282 break;
283 case 'setExpiration':
284 // Only used when setting an end time for a page
285 $expirationTime = $updatedFields['endtime'];
286 $this->setCacheExpiration($affectedPage, $expirationTime);
287 break;
288 case 'uidInTreelist':
289 $this->clearCacheWhereUidInTreelist($affectedPage);
290 break;
291 }
292 }
293
294 // From time to time clean the cache from expired entries
295 // (theoretically every 1000 calls)
296 $randomNumber = rand(1, 1000);
297 if ($randomNumber == 500) {
298 $this->removeExpiredCacheEntries();
299 }
300 }
301
302 /**
303 * Clears the treelist cache for all parents of a changed page.
304 * gets called after creating a new page and after moving a page
305 *
306 * @param integer $affectedParentPage Parent page id of the changed page, the page to start clearing from
307 * @return void
308 */
309 protected function clearCacheForAllParents($affectedParentPage) {
310 $rootline = t3lib_BEfunc::BEgetRootLine($affectedParentPage);
311
312 $rootlineIds = array();
313 foreach ($rootline as $page) {
314 if ($page['uid'] != 0) {
315 $rootlineIds[] = $page['uid'];
316 }
317 }
318
319 if (!empty($rootlineIds)) {
320 $rootlineIdsImploded = implode(',', $rootlineIds);
321
322 $GLOBALS['TYPO3_DB']->exec_DELETEquery(
323 'cache_treelist',
324 'pid IN(' . $rootlineIdsImploded . ')'
325 );
326 }
327 }
328
329 /**
330 * Clears the treelist cache for all pages where the affected page is found
331 * in the treelist
332 *
333 * @param integer $affectedPage ID of the changed page
334 * @return void
335 */
336 protected function clearCacheWhereUidInTreelist($affectedPage) {
337 $GLOBALS['TYPO3_DB']->exec_DELETEquery(
338 'cache_treelist',
339 $GLOBALS['TYPO3_DB']->listQuery(
340 'treelist',
341 $affectedPage,
342 'cache_treelist'
343 )
344 );
345 }
346
347 /**
348 * Sets an expiration time for all cache entries having the changed page in
349 * the treelist.
350 *
351 * @param integer $affectedPage Uid of the changed page
352 * @param integer $expirationTime
353 * @return void
354 */
355 protected function setCacheExpiration($affectedPage, $expirationTime) {
356
357 $GLOBALS['TYPO3_DB']->exec_UPDATEquery(
358 'cache_treelist',
359 $GLOBALS['TYPO3_DB']->listQuery(
360 'treelist',
361 $affectedPage,
362 'cache_treelist'
363 ),
364 array(
365 'expires' => $expirationTime
366 )
367 );
368 }
369
370 /**
371 * Removes all expired treelist cache entries
372 *
373 * @return void
374 */
375 protected function removeExpiredCacheEntries() {
376 $GLOBALS['TYPO3_DB']->exec_DELETEquery(
377 'cache_treelist',
378 'expires <= ' . $GLOBALS['EXEC_TIME']
379 );
380 }
381
382 /**
383 * Determines what happened to the page record, this is necessary to clear
384 * as less cache entries as needed later
385 *
386 * @param string $status TCEmain operation status, either 'new' or 'update'
387 * @param array $updatedFields Array of updated fields
388 * @return string List of actions that happened to the page record
389 */
390 protected function determineClearCacheActions($status, $updatedFields) {
391 $actions = array();
392
393 if ($status == 'new') {
394 // New page
395 $actions['allParents'] = TRUE;
396 } elseif ($status == 'update') {
397 $updatedFieldNames = array_keys($updatedFields);
398
399 foreach ($updatedFieldNames as $updatedFieldName) {
400 switch ($updatedFieldName) {
401 case 'pid':
402 // Page moved
403 case $GLOBALS['TCA']['pages']['ctrl']['enablecolumns']['disabled']:
404 // Page hidden / unhidden
405 case $GLOBALS['TCA']['pages']['ctrl']['delete']:
406 // Page deleted / undeleted
407 case $GLOBALS['TCA']['pages']['ctrl']['enablecolumns']['starttime']:
408
409 // start time set/unset
410 // Doesn't matter whether it was set or unset, in both
411 // cases the cache needs to be cleared. When setting a
412 // start time the page must be removed from the
413 // treelist. When unsetting the start time it must
414 // become listed in the tree list again.
415 case $GLOBALS['TCA']['pages']['ctrl']['enablecolumns']['fe_group']:
416 // Changes to FE user group
417 case 'extendToSubpages':
418 // extendToSubpages set (apply FE access restrictions to subpages)
419 case 'php_tree_stop':
420 // php_tree_stop
421 $actions['allParents'] = TRUE;
422 $actions['uidInTreelist'] = TRUE;
423 break;
424 case $GLOBALS['TCA']['pages']['ctrl']['enablecolumns']['endtime']:
425
426 // end time set/unset
427 // When setting an end time the cache entry needs an
428 // expiration time. When unsetting the end time the
429 // page must become listed in the treelist again.
430 if ($updatedFields['endtime'] > 0) {
431 $actions['setExpiration'] = TRUE;
432 } else {
433 $actions['uidInTreelist'] = TRUE;
434 }
435 break;
436 default:
437 if (in_array($updatedFieldName, $this->updateRequiringFields)) {
438 $actions['uidInTreelist'] = TRUE;
439 }
440 }
441 }
442 }
443
444 return $actions;
445 }
446 }
447 ?>