Cleanup: Updated copyright comments
[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 /**
30 * Class that hooks into TCEmain and listens for updates to pages to update the
31 * treelist cache
32 *
33 * @author Ingo Renner <ingo@typo3.org>
34 * @package TYPO3
35 * @subpackage tslib
36 */
37 class tx_cms_treelistCacheUpdate {
38
39 // should not be manipulated from others except through the
40 // configuration provided @see __construct()
41 private $updateRequiringFields = array(
42 'pid',
43 'php_tree_stop',
44 'extendToSubpages'
45 );
46
47 /**
48 * constructor, adds update requiring fields to the default ones
49 *
50 */
51 public function __construct() {
52
53 // as enableFields can be set dynamically we add them here
54 $pagesEnableFields = $GLOBALS['TCA']['pages']['ctrl']['enablecolumns'];
55 foreach ($pagesEnableFields as $pagesEnableField) {
56 $this->updateRequiringFields[] = $pagesEnableField;
57 }
58 $this->updateRequiringFields[] = $GLOBALS['TCA']['pages']['ctrl']['delete'];
59
60 // extension can add fields to the pages table that require an
61 // update of the treelist cache, too; so we also add those
62 // example: $TYPO3_CONF_VARS['BE']['additionalTreelistUpdateFields'] .= ',my_field';
63 if (!empty($GLOBALS['TYPO3_CONF_VARS']['BE']['additionalTreelistUpdateFields'])) {
64 $additionalTreelistUpdateFields = t3lib_div::trimExplode(
65 ',',
66 $GLOBALS['TYPO3_CONF_VARS']['BE']['additionalTreelistUpdateFields'],
67 true
68 );
69
70 $this->updateRequiringFields += $additionalTreelistUpdateFields;
71 }
72
73 }
74
75 /**
76 * waits for TCEmain commands and looks for changed pages, if found further
77 * changes take place to determine whether the cache needs to be updated
78 *
79 * @param string TCEmain operation status, either 'new' or 'update'
80 * @param string the DB table the operation was carried out on
81 * @param mixed the record's uid for update records, a string to look the record's uid up after it has been created
82 * @param array array of changed fiels and their new values
83 * @param t3lib_TCEmain TCEmain parent object
84 */
85 public function processDatamap_afterDatabaseOperations($status, $table, $recordId, array $updatedFields, t3lib_TCEmain $tceMain) {
86
87 if ($table == 'pages' && $this->requiresUpdate($updatedFields)) {
88 $affectedPagePid = 0;
89 $affectedPageUid = 0;
90
91 if ($status == 'new') {
92 // detect new pages
93
94 // resolve the uid
95 $affectedPageUid = $tceMain->substNEWwithIDs[$recordId];
96 $affectedPagePid = $updatedFields['pid'];
97 } elseif ($status == 'update') {
98 // detect updated pages
99
100 $affectedPageUid = $recordId;
101
102 /*
103 when updating a page the pid is not directly available so we
104 need to retrieve it ourselves.
105 */
106 $fullPageRecord = t3lib_BEfunc::getRecord($table, $recordId);
107 $affectedPagePid = $fullPageRecord['pid'];
108 }
109
110 $clearCacheActions = $this->determineClearCacheActions(
111 $status,
112 $updatedFields
113 );
114
115 $this->processClearCacheActions(
116 $affectedPageUid,
117 $affectedPagePid,
118 $updatedFields,
119 $clearCacheActions
120 );
121 }
122 }
123
124 /**
125 * waits for TCEmain commands and looks for deleted pages, if found further
126 * changes take place to determine whether the cache needs to be updated
127 *
128 * @param string the TCE command
129 * @param string the record's table
130 * @param integer the record's uid
131 * @param array the commands value, typically an array with more detailed command information
132 * @param t3lib_TCEmain the TCEmain parent object
133 */
134 public function processCmdmap_postProcess($command, $table, $recordId, $commandValue, t3lib_TCEmain $tceMain) {
135
136 if ($table == 'pages' && $command == 'delete') {
137
138 $deletedRecord = t3lib_BEfunc::getRecord(
139 $table,
140 $recordId,
141 '*',
142 '',
143 false
144 );
145
146 $affectedPageUid = $deletedRecord['uid'];
147 $affectedPagePid = $deletedRecord['pid'];
148 // faking the updated fields
149 $updatedFields = array('deleted' => 1);
150
151 $clearCacheActions = $this->determineClearCacheActions(
152 'update',
153 $updatedFields
154 );
155
156 $this->processClearCacheActions(
157 $affectedPageUid,
158 $affectedPagePid,
159 $updatedFields,
160 $clearCacheActions
161 );
162 }
163 }
164
165 /**
166 * waits for TCEmain commands and looks for moved pages, if found further
167 * changes take place to determine whether the cache needs to be updated
168 *
169 * @param string table name of the moved record
170 * @param integer the record's uid
171 * @param integer the record's destination page id
172 * @param array the record that moved
173 * @param array array of changed fields
174 * @param t3lib_TCEmain TCEmain parent object
175 */
176 public function moveRecord_firstElementPostProcess($table, $recordId, $destinationPid, array $movedRecord, array $updatedFields, t3lib_TCEmain $tceMain) {
177
178 if ($table == 'pages' && $this->requiresUpdate($updatedFields)) {
179
180 $affectedPageUid = $recordId;
181 $affectedPageOldPid = $movedRecord['pid'];
182 $affectedPageNewPid = $updatedFields['pid'];
183
184 $clearCacheActions = $this->determineClearCacheActions(
185 'update',
186 $updatedFields
187 );
188
189 // clear treelist entries for old parent page
190 $this->processClearCacheActions(
191 $affectedPageUid,
192 $affectedPageOldPid,
193 $updatedFields,
194 $clearCacheActions
195 );
196 // clear treelist entries for new parent page
197 $this->processClearCacheActions(
198 $affectedPageUid,
199 $affectedPageNewPid,
200 $updatedFields,
201 $clearCacheActions
202 );
203 }
204 }
205
206 /**
207 * waits for TCEmain commands and looks for moved pages, if found further
208 * changes take place to determine whether the cache needs to be updated
209 *
210 * @param string table name of the moved record
211 * @param integer the record's uid
212 * @param integer the record's destination page id
213 * @param integer (negative) page id th page has been moved after
214 * @param array the record that moved
215 * @param array array of changed fields
216 * @param t3lib_TCEmain TCEmain parent object
217 */
218 public function moveRecord_afterAnotherElementPostProcess($table, $recordId, $destinationPid, $originalDestinationPid, array $movedRecord, array $updatedFields, t3lib_TCEmain $tceMain) {
219
220 if ($table == 'pages' && $this->requiresUpdate($updatedFields)) {
221
222 $affectedPageUid = $recordId;
223 $affectedPageOldPid = $movedRecord['pid'];
224 $affectedPageNewPid = $updatedFields['pid'];
225
226 $clearCacheActions = $this->determineClearCacheActions(
227 'update',
228 $updatedFields
229 );
230
231 // clear treelist entries for old parent page
232 $this->processClearCacheActions(
233 $affectedPageUid,
234 $affectedPageOldPid,
235 $updatedFields,
236 $clearCacheActions
237 );
238 // clear treelist entries for new parent page
239 $this->processClearCacheActions(
240 $affectedPageUid,
241 $affectedPageNewPid,
242 $updatedFields,
243 $clearCacheActions
244 );
245 }
246 }
247
248 /**
249 * checks whether the change requires an update of the treelist cache
250 *
251 * @param array array of changed fields
252 * @return boolean true if the treelist cache needs to be updated, false if no update to the cache is required
253 */
254 protected function requiresUpdate(array $updatedFields) {
255 $requiresUpdate = false;
256
257 $updatedFieldNames = array_keys($updatedFields);
258 foreach ($updatedFieldNames as $updatedFieldName) {
259 if (in_array($updatedFieldName, $this->updateRequiringFields)) {
260 $requiresUpdate = true;
261 break;
262 }
263 }
264
265 return $requiresUpdate;
266 }
267
268 /**
269 * calls the cache maintainance functions according to the determined actions
270 *
271 * @param integer uid of the affected page
272 * @param integer parent uid of the affected page
273 * @param array array of updated fields and their new values
274 * @param array array of actions to carry out
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 parent page id of the changed page, the page to start clearing from
307 */
308 protected function clearCacheForAllParents($affectedParentPage) {
309 $rootline = t3lib_BEfunc::BEgetRootLine($affectedParentPage);
310
311 $rootlineIds = array();
312 foreach ($rootline as $page) {
313 if($page['uid'] != 0) {
314 $rootlineIds[] = $page['uid'];
315 }
316 }
317
318 if (!empty($rootlineIds)) {
319 $rootlineIdsImploded = implode(',', $rootlineIds);
320
321 $GLOBALS['TYPO3_DB']->exec_DELETEquery(
322 'cache_treelist',
323 'pid IN(' . $rootlineIdsImploded . ')'
324 );
325 }
326 }
327
328 /**
329 * clears the treelist cache for all pages where the affected page is found
330 * in the treelist
331 *
332 * @param integer Id of the changed page
333 */
334 protected function clearCacheWhereUidInTreelist($affectedPage) {
335 $GLOBALS['TYPO3_DB']->exec_DELETEquery(
336 'cache_treelist',
337 $GLOBALS['TYPO3_DB']->listQuery(
338 'treelist',
339 $affectedPage,
340 'cache_treelist'
341 )
342 );
343 }
344
345 /**
346 * sets an expiration time for all cache entries having the changed page in
347 * the treelist.
348 *
349 * @param integer uid of the changed page
350 */
351 protected function setCacheExpiration($affectedPage, $expirationTime) {
352
353 $GLOBALS['TYPO3_DB']->exec_UPDATEquery(
354 'cache_treelist',
355 $GLOBALS['TYPO3_DB']->listQuery(
356 'treelist',
357 $affectedPage,
358 'cache_treelist'
359 ),
360 array(
361 'expires' => $expirationTime
362 )
363 );
364 }
365
366 /**
367 * removes all expired treelist cache entries
368 *
369 */
370 protected function removeExpiredCacheEntries() {
371 $GLOBALS['TYPO3_DB']->exec_DELETEquery(
372 'cache_treelist',
373 'expires <= ' . $GLOBALS['EXEC_TIME']
374 );
375 }
376
377 /**
378 * determines what happened to the page record, this is necessary to clear
379 * as less cache entries as needed later
380 *
381 * @param string TCEmain operation status, either 'new' or 'update'
382 * @param array array of updated fields
383 * @return string list of actions that happened to the page record
384 */
385 protected function determineClearCacheActions($status, $updatedFields) {
386 $actions = array();
387
388 if ($status == 'new') {
389 // new page
390 $actions['allParents'] = true;
391 } elseif ($status == 'update') {
392 $updatedFieldNames = array_keys($updatedFields);
393
394 foreach ($updatedFieldNames as $updatedFieldName) {
395 switch ($updatedFieldName) {
396 case 'pid':
397 // page moved
398 case $GLOBALS['TCA']['pages']['ctrl']['enablecolumns']['disabled']:
399 // page hidden / unhidden
400 case $GLOBALS['TCA']['pages']['ctrl']['delete']:
401 // page deleted / undeleted
402 case $GLOBALS['TCA']['pages']['ctrl']['enablecolumns']['starttime']:
403 /*
404 start time set/unset
405 Doesn't matter whether it was set or unset, in both
406 cases the cache needs to be cleared. When setting a
407 start time the page must be removed from the
408 treelist. When unsetting the start time it must
409 become listed in the tree list again.
410 */
411 case $GLOBALS['TCA']['pages']['ctrl']['enablecolumns']['fe_group']:
412 // changes to FE user group
413 case 'extendToSubpages':
414 // extendToSubpages set (apply FE access restrictions to subpages)
415 case 'php_tree_stop':
416 // php_tree_stop
417 $actions['allParents'] = TRUE;
418 $actions['uidInTreelist'] = true;
419 break;
420 case $GLOBALS['TCA']['pages']['ctrl']['enablecolumns']['endtime']:
421 /*
422 end time set/unset
423 When setting an end time the cache entry needs an
424 expiration time. When unsetting the end time the
425 page must become listed in the treelist again.
426 */
427 if($updatedFields['endtime'] > 0) {
428 $actions['setExpiration'] = true;
429 } else {
430 $actions['uidInTreelist'] = true;
431 }
432 break;
433 default:
434 if (in_array($updatedFieldName, $this->updateRequiringFields)) {
435 $actions['uidInTreelist'] = true;
436 }
437 }
438 }
439 }
440
441 return $actions;
442 }
443
444 }
445
446
447 if (defined('TYPO3_MODE') && isset($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['tslib/hooks/class.tx_cms_treelistcacheupdate.php'])) {
448 include_once($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['tslib/hooks/class.tx_cms_treelistcacheupdate.php']);
449 }
450
451 ?>