[TASK] Remove superfluous parenthesis in sysext frontend
[Packages/TYPO3.CMS.git] / typo3 / sysext / frontend / Classes / Hooks / TreelistCacheUpdateHooks.php
1 <?php
2 namespace TYPO3\CMS\Frontend\Hooks;
3
4 /***************************************************************
5 * Copyright notice
6 *
7 * (c) 2008-2011 Ingo Renner (ingo@typo3.org)
8 * All rights reserved
9 *
10 * This script is part of the TYPO3 project. The TYPO3 project is
11 * free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * The GNU General Public License can be found at
17 * http://www.gnu.org/copyleft/gpl.html.
18 * A copy is found in the textfile GPL.txt and important notices to the license
19 * from the author is found in LICENSE.txt distributed with these scripts.
20 *
21 *
22 * This script is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
26 *
27 * This copyright notice MUST APPEAR in all copies of the script!
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 TreelistCacheUpdateHooks {
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 public function __construct() {
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 // Extension can add fields to the pages table that require an
58 // update of the treelist cache, too; so we also add those
59 // example: $TYPO3_CONF_VARS['BE']['additionalTreelistUpdateFields'] .= ',my_field';
60 if (!empty($GLOBALS['TYPO3_CONF_VARS']['BE']['additionalTreelistUpdateFields'])) {
61 $additionalTreelistUpdateFields = \TYPO3\CMS\Core\Utility\GeneralUtility::trimExplode(',', $GLOBALS['TYPO3_CONF_VARS']['BE']['additionalTreelistUpdateFields'], TRUE);
62 $this->updateRequiringFields += $additionalTreelistUpdateFields;
63 }
64 }
65
66 /**
67 * waits for TCEmain commands and looks for changed pages, if found further
68 * changes take place to determine whether the cache needs to be updated
69 *
70 * @param string $status TCEmain operation status, either 'new' or 'update'
71 * @param string $table The DB table the operation was carried out on
72 * @param mixed $recordId The record's uid for update records, a string to look the record's uid up after it has been created
73 * @param array $updatedFields Array of changed fiels and their new values
74 * @param \TYPO3\CMS\Core\DataHandler\DataHandler $tceMain TCEmain parent object
75 * @return void
76 */
77 public function processDatamap_afterDatabaseOperations($status, $table, $recordId, array $updatedFields, \TYPO3\CMS\Core\DataHandler\DataHandler $tceMain) {
78 if ($table == 'pages' && $this->requiresUpdate($updatedFields)) {
79 $affectedPagePid = 0;
80 $affectedPageUid = 0;
81 if ($status == 'new') {
82 // Detect new pages
83 // Resolve the uid
84 $affectedPageUid = $tceMain->substNEWwithIDs[$recordId];
85 $affectedPagePid = $updatedFields['pid'];
86 } elseif ($status == 'update') {
87 // Detect updated pages
88 $affectedPageUid = $recordId;
89 // When updating a page the pid is not directly available so we
90 // need to retrieve it ourselves.
91 $fullPageRecord = \TYPO3\CMS\Backend\Utility\BackendUtility::getRecord($table, $recordId);
92 $affectedPagePid = $fullPageRecord['pid'];
93 }
94 $clearCacheActions = $this->determineClearCacheActions($status, $updatedFields);
95 $this->processClearCacheActions($affectedPageUid, $affectedPagePid, $updatedFields, $clearCacheActions);
96 }
97 }
98
99 /**
100 * waits for TCEmain commands and looks for deleted pages, if found further
101 * changes take place to determine whether the cache needs to be updated
102 *
103 * @param string $command The TCE command
104 * @param string $table The record's table
105 * @param integer $recordId The record's uid
106 * @param array $commandValue The commands value, typically an array with more detailed command information
107 * @param \TYPO3\CMS\Core\DataHandler\DataHandler $tceMain The TCEmain parent object
108 * @return void
109 */
110 public function processCmdmap_postProcess($command, $table, $recordId, $commandValue, \TYPO3\CMS\Core\DataHandler\DataHandler $tceMain) {
111 if ($table == 'pages' && $command == 'delete') {
112 $deletedRecord = \TYPO3\CMS\Backend\Utility\BackendUtility::getRecord($table, $recordId, '*', '', FALSE);
113 $affectedPageUid = $deletedRecord['uid'];
114 $affectedPagePid = $deletedRecord['pid'];
115 // Faking the updated fields
116 $updatedFields = array('deleted' => 1);
117 $clearCacheActions = $this->determineClearCacheActions('update', $updatedFields);
118 $this->processClearCacheActions($affectedPageUid, $affectedPagePid, $updatedFields, $clearCacheActions);
119 }
120 }
121
122 /**
123 * waits for TCEmain commands and looks for moved pages, if found further
124 * changes take place to determine whether the cache needs to be updated
125 *
126 * @param string $table Table name of the moved record
127 * @param integer $recordId The record's uid
128 * @param integer $destinationPid The record's destination page id
129 * @param array $movedRecord The record that moved
130 * @param array $updatedFields Array of changed fields
131 * @param \TYPO3\CMS\Core\DataHandler\DataHandler $tceMain TCEmain parent object
132 * @return void
133 */
134 public function moveRecord_firstElementPostProcess($table, $recordId, $destinationPid, array $movedRecord, array $updatedFields, \TYPO3\CMS\Core\DataHandler\DataHandler $tceMain) {
135 if ($table == 'pages' && $this->requiresUpdate($updatedFields)) {
136 $affectedPageUid = $recordId;
137 $affectedPageOldPid = $movedRecord['pid'];
138 $affectedPageNewPid = $updatedFields['pid'];
139 $clearCacheActions = $this->determineClearCacheActions('update', $updatedFields);
140 // Clear treelist entries for old parent page
141 $this->processClearCacheActions($affectedPageUid, $affectedPageOldPid, $updatedFields, $clearCacheActions);
142 // Clear treelist entries for new parent page
143 $this->processClearCacheActions($affectedPageUid, $affectedPageNewPid, $updatedFields, $clearCacheActions);
144 }
145 }
146
147 /**
148 * Waits for TCEmain commands and looks for moved pages, if found further
149 * changes take place to determine whether the cache needs to be updated
150 *
151 * @param string $table Table name of the moved record
152 * @param integer $recordId The record's uid
153 * @param integer $destinationPid The record's destination page id
154 * @param integer $originalDestinationPid (negative) page id th page has been moved after
155 * @param array $movedRecord The record that moved
156 * @param array $updatedFields Array of changed fields
157 * @param \TYPO3\CMS\Core\DataHandler\DataHandler $tceMain TCEmain parent object
158 * @return void
159 */
160 public function moveRecord_afterAnotherElementPostProcess($table, $recordId, $destinationPid, $originalDestinationPid, array $movedRecord, array $updatedFields, \TYPO3\CMS\Core\DataHandler\DataHandler $tceMain) {
161 if ($table == 'pages' && $this->requiresUpdate($updatedFields)) {
162 $affectedPageUid = $recordId;
163 $affectedPageOldPid = $movedRecord['pid'];
164 $affectedPageNewPid = $updatedFields['pid'];
165 $clearCacheActions = $this->determineClearCacheActions('update', $updatedFields);
166 // Clear treelist entries for old parent page
167 $this->processClearCacheActions($affectedPageUid, $affectedPageOldPid, $updatedFields, $clearCacheActions);
168 // Clear treelist entries for new parent page
169 $this->processClearCacheActions($affectedPageUid, $affectedPageNewPid, $updatedFields, $clearCacheActions);
170 }
171 }
172
173 /**
174 * Checks whether the change requires an update of the treelist cache
175 *
176 * @param array $updatedFields Array of changed fields
177 * @return boolean TRUE if the treelist cache needs to be updated, FALSE if no update to the cache is required
178 */
179 protected function requiresUpdate(array $updatedFields) {
180 $requiresUpdate = FALSE;
181 $updatedFieldNames = array_keys($updatedFields);
182 foreach ($updatedFieldNames as $updatedFieldName) {
183 if (in_array($updatedFieldName, $this->updateRequiringFields)) {
184 $requiresUpdate = TRUE;
185 break;
186 }
187 }
188 return $requiresUpdate;
189 }
190
191 /**
192 * Calls the cache maintainance functions according to the determined actions
193 *
194 * @param integer $affectedPage uid of the affected page
195 * @param integer $affectedParentPage parent uid of the affected page
196 * @param array $updatedFields Array of updated fields and their new values
197 * @param array $actions Array of actions to carry out
198 * @return void
199 */
200 protected function processClearCacheActions($affectedPage, $affectedParentPage, $updatedFields, array $actions) {
201 $actionNames = array_keys($actions);
202 foreach ($actionNames as $actionName) {
203 switch ($actionName) {
204 case 'allParents':
205 $this->clearCacheForAllParents($affectedParentPage);
206 break;
207 case 'setExpiration':
208 // Only used when setting an end time for a page
209 $expirationTime = $updatedFields['endtime'];
210 $this->setCacheExpiration($affectedPage, $expirationTime);
211 break;
212 case 'uidInTreelist':
213 $this->clearCacheWhereUidInTreelist($affectedPage);
214 break;
215 }
216 }
217 // From time to time clean the cache from expired entries
218 // (theoretically every 1000 calls)
219 $randomNumber = rand(1, 1000);
220 if ($randomNumber == 500) {
221 $this->removeExpiredCacheEntries();
222 }
223 }
224
225 /**
226 * Clears the treelist cache for all parents of a changed page.
227 * gets called after creating a new page and after moving a page
228 *
229 * @param integer $affectedParentPage Parent page id of the changed page, the page to start clearing from
230 * @return void
231 */
232 protected function clearCacheForAllParents($affectedParentPage) {
233 $rootline = \TYPO3\CMS\Backend\Utility\BackendUtility::BEgetRootLine($affectedParentPage);
234 $rootlineIds = array();
235 foreach ($rootline as $page) {
236 if ($page['uid'] != 0) {
237 $rootlineIds[] = $page['uid'];
238 }
239 }
240 if (!empty($rootlineIds)) {
241 $rootlineIdsImploded = implode(',', $rootlineIds);
242 $GLOBALS['TYPO3_DB']->exec_DELETEquery('cache_treelist', 'pid IN(' . $rootlineIdsImploded . ')');
243 }
244 }
245
246 /**
247 * Clears the treelist cache for all pages where the affected page is found
248 * in the treelist
249 *
250 * @param integer $affectedPage ID of the changed page
251 * @return void
252 */
253 protected function clearCacheWhereUidInTreelist($affectedPage) {
254 $GLOBALS['TYPO3_DB']->exec_DELETEquery('cache_treelist', $GLOBALS['TYPO3_DB']->listQuery('treelist', $affectedPage, 'cache_treelist'));
255 }
256
257 /**
258 * Sets an expiration time for all cache entries having the changed page in
259 * the treelist.
260 *
261 * @param integer $affectedPage Uid of the changed page
262 * @param integer $expirationTime
263 * @return void
264 */
265 protected function setCacheExpiration($affectedPage, $expirationTime) {
266 $GLOBALS['TYPO3_DB']->exec_UPDATEquery('cache_treelist', $GLOBALS['TYPO3_DB']->listQuery('treelist', $affectedPage, 'cache_treelist'), array(
267 'expires' => $expirationTime
268 ));
269 }
270
271 /**
272 * Removes all expired treelist cache entries
273 *
274 * @return void
275 */
276 protected function removeExpiredCacheEntries() {
277 $GLOBALS['TYPO3_DB']->exec_DELETEquery('cache_treelist', 'expires <= ' . $GLOBALS['EXEC_TIME']);
278 }
279
280 /**
281 * Determines what happened to the page record, this is necessary to clear
282 * as less cache entries as needed later
283 *
284 * @param string $status TCEmain operation status, either 'new' or 'update'
285 * @param array $updatedFields Array of updated fields
286 * @return string List of actions that happened to the page record
287 */
288 protected function determineClearCacheActions($status, $updatedFields) {
289 $actions = array();
290 if ($status == 'new') {
291 // New page
292 $actions['allParents'] = TRUE;
293 } elseif ($status == 'update') {
294 $updatedFieldNames = array_keys($updatedFields);
295 foreach ($updatedFieldNames as $updatedFieldName) {
296 switch ($updatedFieldName) {
297 case 'pid':
298
299 case $GLOBALS['TCA']['pages']['ctrl']['enablecolumns']['disabled']:
300
301 case $GLOBALS['TCA']['pages']['ctrl']['delete']:
302
303 case $GLOBALS['TCA']['pages']['ctrl']['enablecolumns']['starttime']:
304
305 case $GLOBALS['TCA']['pages']['ctrl']['enablecolumns']['fe_group']:
306
307 case 'extendToSubpages':
308
309 case 'php_tree_stop':
310 // php_tree_stop
311 $actions['allParents'] = TRUE;
312 $actions['uidInTreelist'] = TRUE;
313 break;
314 case $GLOBALS['TCA']['pages']['ctrl']['enablecolumns']['endtime']:
315 // end time set/unset
316 // When setting an end time the cache entry needs an
317 // expiration time. When unsetting the end time the
318 // page must become listed in the treelist again.
319 if ($updatedFields['endtime'] > 0) {
320 $actions['setExpiration'] = TRUE;
321 } else {
322 $actions['uidInTreelist'] = TRUE;
323 }
324 break;
325 default:
326 if (in_array($updatedFieldName, $this->updateRequiringFields)) {
327 $actions['uidInTreelist'] = TRUE;
328 }
329 }
330 }
331 }
332 return $actions;
333 }
334
335 }
336
337
338 ?>