/[drupal]/contributions/modules/category/category.inc
ViewVC logotype

Contents of /contributions/modules/category/category.inc

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.71 - (show annotations) (download) (as text)
Wed Aug 5 04:52:53 2009 UTC (3 months, 3 weeks ago) by jaza
Branch: MAIN
CVS Tags: DRUPAL-6--2-0-RC1, HEAD
Changes since 1.70: +239 -106 lines
File MIME type: text/x-php
#158598 by JirkaRybka: a massive collection of bug fixes, D6 upgrades, and performance / usability improvements and other tasks for the category package. This commit includes patches from the following threads:
 - #370641: Port category_views to D6
 - #370633: Port category_breadcrumb to D6
 - #484624: Fix all broken category/container previews, and add category_display defaults
 - #481280: Generated menu items vs. menu administration and weights
 - #457688: Get rid of Menu wrapper, moving functionality to category_menu
 - #484084: Update README.txt and friends
 - #483978: Remove t() from database schema descriptions
 - #501378: PERFORMANCE! Central caching for category API functions
 - #521680: Missing argument error on category/### paths
 - #521714: Missing JavaScript file in Taxonomy wrapper
Lots and lots of thanks go to JirkaRybka for this monumental cleanup effort.
1 <?php
2 // $Id: category.inc,v 1.70 2008/07/21 09:30:48 jaza Exp $
3
4 /**
5 * @file
6 * API functions for the category system as provided by the category module.
7 * This file is required in order for the category module to work.
8 *
9 * Other modules should only call functions in this file when interfacing
10 * with the category system - functions in category.module itself should not
11 * need to be called directly.
12 */
13
14 /**
15 * Return an array of all container objects.
16 *
17 * @param $type
18 * If set, return only those containers associated with this node type.
19 */
20 function category_get_containers($type = NULL) {
21 $cache_key = 'c_get_conts'. $type;
22 $containers = category_cache_op('get', 0, $cache_key);
23 if (!isset($containers)) {
24
25 $type_sql = $type ? " WHERE nt.type = '%s'" : "";
26 $result = db_query(db_rewrite_sql("SELECT n.nid, n.title, cn.*, c.weight, c.depth, n.status, nt.type FROM {category_cont} cn INNER JOIN {category} c ON cn.cid = c.cid INNER JOIN {node} n ON cn.cid = n.nid LEFT JOIN {category_cont_node_types} nt ON cn.cid = nt.cid". $type_sql ." ORDER BY c.weight, n.title", 'n', 'nid'), $type);
27
28 $containers = array();
29 $node_types = array();
30 while ($container = db_fetch_object($result)) {
31 // If no node types are associated with a container, the LEFT JOIN will
32 // return a NULL value for type.
33 if (isset($container->type)) {
34 $node_types[$container->cid][$container->type] = $container->type;
35 unset($container->type);
36 $container->nodes = $node_types[$container->cid];
37 }
38 elseif (!isset($container->nodes)) {
39 $container->nodes = array();
40 }
41
42 if (empty($container->admin_title)) {
43 $container->admin_title = '';
44 $container->has_admin_title = FALSE;
45 }
46 else {
47 $container->has_admin_title = TRUE;
48 }
49 $containers[$container->cid] = $container;
50 }
51
52 category_cache_op('set', 0, $cache_key, $containers);
53 }
54
55 return $containers;
56 }
57
58 /**
59 * Find all categories associated to the given node, within one container.
60 */
61 function category_node_get_categories_by_container($node, $cnid, $key = 'cid') {
62 $cache_key = 'c_node_get_cats_by_cont'. $node->vid .':'. $cnid .':'. $key;
63 $categories = category_cache_op('get', $node->nid, $cache_key);
64 if (!isset($categories)) {
65
66 $result = db_query(db_rewrite_sql('SELECT c.*, n.title FROM {category} c INNER JOIN {category_node} r ON c.cid = r.cid INNER JOIN {node} n ON c.cid = n.nid WHERE c.cnid = %d AND r.vid = %d AND n.status = 1 ORDER BY c.weight'), $cnid, $node->vid);
67 $categories = array();
68 while ($category = db_fetch_object($result)) {
69 $categories[$category->$key] = $category;
70 }
71
72 category_cache_op('set', $node->nid, $cache_key, $categories);
73 }
74 return $categories;
75 }
76
77 /**
78 * Find all categories associated to the given node, ordered by container and category weight.
79 */
80 function category_node_get_categories($node, $key = 'cid') {
81 $cache_key = 'cat_node_get_cats'. $node->vid .':'. $key;
82 $categories = category_cache_op('get', $node->nid, $cache_key);
83 if (!isset($categories)) {
84
85 $categories = array();
86
87 $result = db_query(db_rewrite_sql('SELECT n.nid, nr.teaser AS description, r.vid AS node_id, c.*, n.title FROM {category} c INNER JOIN {category_node} r ON c.cid = r.cid INNER JOIN {category} cn ON c.cnid = cn.cid INNER JOIN {node} n ON c.cid = n.nid INNER JOIN {node_revisions} nr ON n.vid = nr.vid INNER JOIN {node} cnn ON cn.cid = cnn.nid WHERE n.status = 1 AND r.vid = %d ORDER BY cn.weight, cnn.title, c.weight, n.title', 'n', 'nid'), $node->vid);
88
89 while ($category = db_fetch_object($result)) {
90 $categories[$category->$key] = $category;
91 }
92
93 category_cache_op('set', $node->nid, $cache_key, $categories);
94 }
95 return $categories;
96 }
97
98 /**
99 * Find all category objects related to a given category ID.
100 */
101 function category_get_related($cid, $key = 'cid') {
102 if ($cid) {
103 $cache_key = 'cat_get_related'. $key;
104 $related = category_cache_op('get', $cid, $cache_key);
105 if (!isset($related)) {
106
107 $result = db_query(db_rewrite_sql('SELECT c.*, n.title, cid1, cid2 FROM {category_relation}, {category} c, {node} n WHERE (c.cid = cid1 OR c.cid = cid2) AND (cid1 = %d OR cid2 = %d) AND c.cid = n.nid AND c.cid != %d AND n.status = 1 ORDER BY c.weight, n.title'), $cid, $cid, $cid);
108 $related = array();
109 while ($category = db_fetch_object($result)) {
110 $related[$category->$key] = $category;
111 }
112
113 category_cache_op('set', $cid, $cache_key, $related);
114 }
115 return $related;
116 }
117 else {
118 return array();
119 }
120 }
121
122 /**
123 * Find all parents of a given category ID.
124 * This function will return the parents in an indexed array sorted by the parents' weight and title.
125 */
126 function category_get_parents($cid, $key = 'cid', $distant = TRUE) {
127 if (!$cid) {
128 return array();
129 }
130
131 $cache_key = 'cat_get_parents'. ($distant ? 'd' : 'n');
132 $parents = category_cache_op('get', 0, $cache_key);
133 if (!isset($parents)) {
134
135 $distant_sql = $distant ? '' : ' AND (c.cnid = cc.cnid OR c.cnid = 0)';
136 $result = db_query(db_rewrite_sql('SELECT c.*, h.cid AS child, n.title, cn.admin_title FROM {category} c INNER JOIN {category_hierarchy} h ON c.cid = h.parent INNER JOIN {node} n ON c.cid = n.nid LEFT JOIN {category_cont} cn ON c.cid = cn.cid INNER JOIN {category} cc ON h.cid = cc.cid WHERE n.status = 1'. $distant_sql .' ORDER BY c.weight, n.title'), $cid);
137 $parents = array();
138 while ($parent = db_fetch_object($result)) {
139 if (empty($parent->admin_title)) {
140 $parent->admin_title = $parent->title;
141 }
142 $parents[$parent->child][$parent->$key] = $parent;
143 }
144
145 category_cache_op('set', 0, $cache_key, $parents);
146 }
147
148 $no_parent = new StdClass();
149 $no_parent->cid = 0;
150 $no_parent->cnid = 0;
151 $no_parent->weight = 0;
152 $no_parent->depth = 0;
153 $no_parent->title = '';
154 $no_parent->admin_title = '';
155 $no_parent->child = $cid;
156 return isset($parents[$cid]) ? $parents[$cid] : array(0 => $no_parent);
157 }
158
159 /**
160 * Find all ancestors of a given category ID.
161 */
162 function category_get_parents_all($cid, $distant = FALSE) {
163 $parents = array();
164 if ($cid) {
165 $parents[] = category_get_category($cid);
166 $n = 0;
167 while ($parent = category_get_parents($parents[$n]->cid, 'cid', $distant)) {
168 $parents = array_merge($parents, $parent);
169 $n++;
170 }
171 }
172 return $parents;
173 }
174
175 /**
176 * Find all children of a category ID.
177 */
178 function category_get_children($cid, $cnid = 0, $key = 'cid') {
179
180 if ($cnid) {
181 $cache_key = 'cat_get_children_cn'. $cnid .':'. $key;
182 $children = category_cache_op('get', $cid, $cache_key);
183 if (!isset($children)) {
184
185 $result = db_query(db_rewrite_sql('SELECT c.*, n.title FROM {category} c INNER JOIN {category_hierarchy} h ON c.cid = h.cid INNER JOIN {node} n ON c.cid = n.nid WHERE h.parent = %d AND n.status = 1 AND c.cnid = %d ORDER BY c.weight, n.title'), $cid, $cnid);
186
187 $children = array();
188 while ($category = db_fetch_object($result)) {
189 $children[$category->$key] = $category;
190 }
191
192 category_cache_op('set', $cid, $cache_key, $children);
193 }
194 return $children;
195 }
196
197 $cache_key = 'cat_get_children_c0'. $key;
198 $children = category_cache_op('get', 0, $cache_key);
199 if (!isset($children)) {
200
201 $result = db_query(db_rewrite_sql('SELECT c.*, h.parent, n.title FROM {category} c INNER JOIN {category_hierarchy} h ON c.cid = h.cid INNER JOIN {node} n ON c.cid = n.nid WHERE n.status = 1 ORDER BY c.weight, n.title'), $cid);
202
203 $children = array();
204 while ($category = db_fetch_object($result)) {
205 $children[$category->parent][$category->$key] = $category;
206 }
207
208 category_cache_op('set', 0, $cache_key, $children);
209 }
210
211 return isset($children[$cid]) ? $children[$cid] : array();
212 }
213
214 /**
215 * Create a hierarchical representation of a set of categories.
216 *
217 * @param $cnid
218 * Which container to generate the tree for.
219 *
220 * @param $parent
221 * The node ID under which to generate the tree. If 0, generate the tree
222 * for everything under the container.
223 *
224 * @param $depth
225 * Internal use only.
226 *
227 * @param $max_depth
228 * The number of levels of the tree to return. Leave NULL to return all levels.
229 *
230 * @param $distant
231 * Whether other containers or categories under them should be returned as
232 * part of the tree. Default is false.
233 *
234 * @return
235 * An array of all category objects in the tree. Each category object is extended
236 * to have "depth" and "parents" attributes in addition to its normal ones.
237 */
238 function category_get_tree($cnid, $parent = NULL, $depth = -1, $max_depth = NULL, $distant = FALSE) {
239 if (empty($cnid) && $depth == -1) {
240 $distant = TRUE;
241 }
242 if (!isset($parent)) {
243 $parent = $cnid;
244 }
245
246 $depth++;
247
248 // We cache trees, so it's not CPU-intensive to call get_tree() on a category
249 // and its children, too.
250 $cache_key = 'cat_get_tree'. $depth .':'. ($distant ? 'd' : 'n');
251 $data = category_cache_op('get', $cnid, $cache_key);
252 if (isset($data)) {
253 $children = $data['children'];
254 $categories = $data['categories'];
255 $parents = $data['parents'];
256 }
257 else {
258
259 $distant_sql = ($distant) ? '' : 'AND c.cnid = %d ';
260
261 $children = array();
262 $categories = array();
263 $parents = array();
264 $result = db_query(db_rewrite_sql('SELECT c.cid, c.cnid, c.weight, c.depth, h.parent, n.title, cn.admin_title FROM {category} c INNER JOIN {category_hierarchy} h ON c.cid = h.cid INNER JOIN {node} n ON c.cid = n.nid LEFT JOIN {category_cont} cn ON c.cid = cn.cid LEFT JOIN {category} c2 ON h.parent = c2.cid WHERE (c2.cid IS NOT NULL OR h.parent = 0) AND n.status = 1 '. $distant_sql .'ORDER BY c.weight, n.title', 'n', 'nid'), $cnid);
265
266 while ($category = db_fetch_object($result)) {
267 if (!$distant && $depth == 0) {
268 $parent_cat = category_get_category($category->parent);
269 if ($parent_cat->cid != $cnid && $parent_cat->cnid != $cnid) {
270 $category->parent = $cnid;
271 }
272 }
273
274 $children[$category->parent][] = $category->cid;
275 $categories[$category->cid] = $category;
276 $parents[$category->cid][] = $category->parent;
277 }
278 category_cache_op('set', $cnid, $cache_key, array('children' => $children, 'categories' => $categories, 'parents' => $parents));
279 }
280
281 $max_depth = (is_null($max_depth) && !empty($children)) ? count($children) : $max_depth;
282 $tree = array();
283 if (!empty($children) && !empty($children[$parent])) {
284 foreach ($children[$parent] as $child) {
285 if ($max_depth > $depth) {
286 $cat = drupal_clone($categories[$child]);
287 $cat->depth = $depth;
288 // The "parent" attribute is not useful, as it would show one parent only.
289 unset($cat->parent);
290 $cat->parents = $parents[$child];
291
292 if (!$cat->cnid && empty($cat->admin_title)) {
293 $cat->admin_title = $cat->title;
294 }
295 $tree[] = $cat;
296
297 if (!empty($children[$child])) {
298 $tree = array_merge($tree, category_get_tree($cnid, $child, $depth, $max_depth, $distant));
299 }
300 }
301 }
302 }
303
304 return $tree;
305 }
306
307 /**
308 * Return an array of synonyms of the given category ID.
309 */
310 function category_get_synonyms($cid) {
311 if ($cid) {
312 $cache_key = 'cat_get_synonyms';
313 $synonyms = category_cache_op('get', $cid, $cache_key);
314 if (!isset($synonyms)) {
315
316 $synonyms = array();
317 $result = db_query('SELECT name FROM {category_synonym} WHERE cid = %d', $cid);
318 while ($synonym = db_fetch_array($result)) {
319 $synonyms[] = $synonym['name'];
320 }
321
322 category_cache_op('set', $cid, $cache_key, $synonyms);
323 }
324 return $synonyms;
325 }
326 else {
327 return array();
328 }
329 }
330
331 /**
332 * Return the category object that has the same given string as a synonym.
333 */
334 function category_get_synonym_root($synonym) {
335 return db_fetch_object(db_query("SELECT c.*, n.title FROM {category} c INNER JOIN {category_synonym} s ON c.cid = s.cid AND s.name = '%s' INNER JOIN {node} n ON c.cid = n.nid", $synonym));
336 }
337
338 /**
339 * Given a category id, count the number of published nodes in it.
340 */
341 function category_category_count_nodes($cid, $type = 0) {
342 $cache_key = 'cat_cat_count_nodes'. $type;
343 $count = category_cache_op('get', 0, $cache_key);
344 if (!isset($count)) {
345 // $type == 0 always evaluates true if $type is a string
346 if (is_numeric($type)) {
347 $result = db_query(db_rewrite_sql('SELECT c.cid, COUNT(n.nid) AS cnt FROM {category_node} c INNER JOIN {node} n ON c.nid = n.nid WHERE n.status = 1 GROUP BY c.cid'));
348 }
349 else {
350 $result = db_query(db_rewrite_sql("SELECT c.cid, COUNT(n.nid) AS cnt FROM {category_node} c INNER JOIN {node} n ON c.nid = n.nid WHERE n.status = 1 AND n.type = '%s' GROUP BY c.cid"), $type);
351 }
352 while ($category = db_fetch_object($result)) {
353 $count[$category->cid] = $category->cnt;
354 }
355 category_cache_op('set', 0, $cache_key, $count);
356 }
357 $children_count = 0;
358 foreach (_category_category_children($cid) as $c) {
359 $children_count += category_category_count_nodes($c, $type);
360 }
361 return $children_count + (isset($count[$cid]) ? $count[$cid] : 0);
362 }
363
364 /**
365 * Helper for category_category_count_nodes().
366 */
367 function _category_category_children($cid) {
368 $cache_key = 'cat_cat_childr';
369 $children = category_cache_op('get', 0, $cache_key);
370 if (!isset($children)) {
371 $result = db_query('SELECT cid, parent FROM {category_hierarchy}');
372 while ($category = db_fetch_object($result)) {
373 $children[$category->parent][] = $category->cid;
374 }
375 category_cache_op('set', 0, $cache_key, $children);
376 }
377 return isset($children[$cid]) ? $children[$cid] : array();
378 }
379
380 /**
381 * Try to map a string to an existing category, as for glossary use.
382 *
383 * Provides a case-insensitive and trimmed mapping, to maximize the
384 * likelihood of a successful match.
385 *
386 * @param title
387 * Title of the category to search for.
388 *
389 * @return
390 * An array of matching category objects.
391 */
392 function category_get_category_by_name($title) {
393 $db_result = db_query(db_rewrite_sql("SELECT n.nid, n.title, c.* FROM {category} c INNER JOIN {node} n ON c.cid = n.nid WHERE LOWER('%s') LIKE LOWER(n.title)", 'n', 'nid'), trim($title));
394 $result = array();
395 while ($category = db_fetch_object($db_result)) {
396 $result[] = $category;
397 }
398
399 return $result;
400 }
401
402 /**
403 * Return the container object matching a container ID.
404 *
405 * @param $cnid
406 * The container's ID
407 *
408 * @return
409 * The container object with all of its metadata, if exists, NULL otherwise.
410 * Results are cached.
411 */
412 function category_get_container($cnid) {
413 $cache_key = 'cat_get_cont';
414 $container = category_cache_op('get', $cnid, $cache_key);
415 if (!isset($container)) {
416 // Initialize so if this container does not exist, we have
417 // that cached, and we will not try to load this later.
418 $container = FALSE;
419 // Try to load the data and fill up the object.
420 $result = db_query(db_rewrite_sql('SELECT n.nid, cn.*, n.title, c.weight, c.depth, ct.type, c.weight AS cont_weight, c.depth AS cont_depth FROM {category} c INNER JOIN {category_cont} cn ON c.cid = cn.cid INNER JOIN {node} n ON c.cid = n.nid LEFT JOIN {category_cont_node_types} ct ON c.cid = ct.cid WHERE c.cid = %d ORDER BY c.weight, n.title', 'n', 'nid'), $cnid);
421 $node_types = array();
422
423 while ($cont = db_fetch_object($result)) {
424 // Find all allowed node types for this container.
425 if (!empty($cont->type)) {
426 $node_types[$cont->type] = $cont->type;
427 }
428 unset($cont->type);
429 $cont->nodes = $node_types;
430
431 // If no admin title is specified, inherit the container title.
432 if (empty($cont->admin_title)) {
433 $cont->admin_title = '';
434 $cont->has_admin_title = FALSE;
435 }
436 else {
437 $cont->has_admin_title = TRUE;
438 }
439
440 $container = $cont;
441 }
442 category_cache_op('set', $cnid, $cache_key, $container);
443 }
444
445 // Return NULL if this vocabulary does not exist.
446 return (!empty($container) ? $container : NULL);
447 }
448
449 /**
450 * Retrieve the category object matching a category ID.
451 *
452 * @param $cid
453 * The node/category ID of the category to be retrieved.
454 *
455 * @return
456 * Populated category object if the category is found, NULL otherwise.
457 */
458 function category_get_category($cid) {
459 $cache_key = 'cat_get_cat';
460 $category = category_cache_op('get', $cid, $cache_key);
461 if (!isset($category)) {
462 $category = db_fetch_object(db_query('SELECT n.nid, n.title, c.* FROM {category} c INNER JOIN {node} n ON c.cid = n.nid WHERE cid = %d', $cid));
463 if (empty($category)) {
464 $category = FALSE;
465 }
466 category_cache_op('set', $cid, $cache_key, $category);
467 }
468
469 return (!empty($category) ? $category : NULL);
470 }
471
472 /**
473 * Finds all nodes that match selected category conditions.
474 *
475 * @param $cids
476 * An array of category IDs to match.
477 * @param $operator
478 * How to interpret multiple IDs in the array. Can be "or" or "and".
479 * @param $depth
480 * How many levels deep to traverse the category tree. Can be a nonnegative
481 * integer or "all".
482 * @param $pager
483 * Whether the nodes are to be used with a pager (the case on most Drupal
484 * pages) or not (in an XML feed, for example).
485 * @param $distant
486 * Whether other containers or categories under them should be returned as
487 * part of the tree. Default is false.
488 * @param $order
489 * The order clause for the query that retrieve the nodes.
490 * @return
491 * A resource identifier pointing to the query results.
492 */
493 function category_select_nodes($cids = array(), $operator = 'or', $depth = 0, $pager = TRUE, $distant = FALSE, $order = 'n.sticky DESC, n.created DESC') {
494 if (count($cids) > 0) {
495 // For each category ID, generate an array of descendant category IDs to the right depth.
496 $descendant_cids = array();
497 if ($depth === 'all' || $depth < 0) {
498 $depth = NULL;
499 }
500 foreach ($cids as $index => $cid) {
501 $category = category_get_category($cid);
502 $cnid = $category->cnid ? $category->cnid : $cid;
503 $tree = category_get_tree($cnid, $cid, -1, $depth, $distant);
504 $descendant_cids[] = array_merge(array($cid), array_map('_category_get_cid_from_category', $tree));
505 }
506
507 $args = array();
508 if ($operator == 'or') {
509 $args = call_user_func_array('array_merge', $descendant_cids);
510 $placeholders = db_placeholders($args, 'int');
511 $sql = 'SELECT DISTINCT(n.nid), n.sticky, n.title, n.created FROM {node} n INNER JOIN {category_node} cn ON n.nid = cn.nid WHERE cn.cid IN ('. $placeholders .') AND n.status = 1 ORDER BY '. $order;
512 $sql_count = 'SELECT COUNT(DISTINCT(n.nid)) FROM {node} n INNER JOIN {category_node} cn ON n.nid = cn.nid WHERE cn.cid IN ('. $placeholders .') AND n.status = 1';
513 }
514 else {
515 $joins = '';
516 $wheres = '';
517 foreach ($descendant_cids as $index => $cids) {
518 $joins .= ' INNER JOIN {category_node} cn'. $index .' ON n.nid = cn'. $index .'.nid';
519 $wheres .= ' AND cn'. $index .'.cid IN ('. db_placeholders($cids, 'int') .')';
520 $args = array_merge($args, $cids);
521 }
522 $sql = 'SELECT DISTINCT(n.nid), n.sticky, n.title, n.created FROM {node} n '. $joins .' WHERE n.status = 1 '. $wheres .' ORDER BY '. $order;
523 $sql_count = 'SELECT COUNT(n.nid) FROM {node} n '. $joins .' WHERE n.status = 1 ' . $wheres;
524 }
525 $sql = db_rewrite_sql($sql);
526 $sql_count = db_rewrite_sql($sql_count);
527 if ($pager) {
528 $result = pager_query($sql, variable_get('default_nodes_main', 10), 0, $sql_count, $args);
529 }
530 else {
531 $result = db_query_range($sql, $args, 0, variable_get('feed_default_items', 10));
532 }
533 }
534
535 return $result;
536 }
537
538 /**
539 * Accepts the result of a pager_query() call, such as that performed by
540 * category_select_nodes(), and formats each node along with a pager.
541 */
542 function category_render_nodes($result, $category_display = NULL) {
543 $output = '';
544 $has_rows = FALSE;
545 while ($node = db_fetch_object($result)) {
546 $output .= node_view(node_load($node->nid), 1);
547 $has_rows = TRUE;
548 }
549 if ($has_rows) {
550 $output .= theme('pager', NULL, variable_get('default_nodes_main', 10), 0);
551 }
552 else {
553 if (!module_exists('category_display') || !empty($category_display->emptymsg)) {
554 $output .= '<p>'. t('There are currently no posts in this category.') .'</p>';
555 }
556 }
557
558 return $output;
559 }
560
561 /**
562 * Parses a comma or plus separated string of category IDs.
563 *
564 * @param $str_cids
565 * A string of category IDs, separated by plus or comma.
566 * comma (,) means AND
567 * plus (+) means OR
568 *
569 * @return an associative array with an operator key (either 'and'
570 * or 'or') and a cid key containing an array of the category IDs.
571 */
572 function category_categories_parse_string($str_cids) {
573 $categories = array('operator' => '', 'cids' => array());
574 if (preg_match('/^([0-9]+[+ ])+[0-9]+$/', $str_cids)) {
575 $categories['operator'] = 'or';
576 // The '+' character in a query string may be parsed as ' '.
577 $categories['cids'] = preg_split('/[+ ]/', $str_cids);
578 }
579 else if (preg_match('/^([0-9]+,)*[0-9]+$/', $str_cids)) {
580 $categories['operator'] = 'and';
581 $categories['cids'] = explode(',', $str_cids);
582 }
583 return $categories;
584 }
585
586 /**
587 * Central caching function for all kinds of node-related category data,
588 * so that all data for a given node may be loaded by single query from
589 * cache table into static variable, and then returned by various API
590 * functions without any queries needed. The cache is stored per-node,
591 * and flushed only when given node is saved or deleted. The $nid of '0'
592 * is used for global summary-data, and flushed on every node save.
593 * While saving a category or container, we flush the whole cache, as
594 * this affects the whole hiearchy, and happens rarely. This should not
595 * be used for data affected by other factors (such as current user's
596 * permissions), unless the condition is incorporated into the cache key.
597 *
598 * @param op
599 * Operation to be performed - either 'get', 'set', or 'flush'.
600 *
601 * @param nid
602 * Node id to handle data for. 0 is general data, 'all' flushes all.
603 *
604 * @param key
605 * Name of node-data subset to work with. (Usually an API function identificator.)
606 *
607 * @param data
608 * Data for the 'set' operation (will get added to the existing per-node record).
609 *
610 * @return
611 * Cached data, or NULL if none.
612 */
613 function category_cache_op($op, $nid, $key = '', $data = NULL) {
614 static $cache;
615 if (!isset($cache)) {
616 $cache = array('n' => array(), 'g' => array());
617 }
618
619 // Flush cache if requested
620 if ($op == 'flush') {
621 if ($nid === 'all') {
622 // Total flush: Empty {cache_category} and static cache entirely
623 cache_clear_all('*', 'cache_category', TRUE);
624 $cache = array('n' => array(), 'g' => array());
625 }
626 else {
627 // Node flush: Delete entries for given node
628 cache_clear_all('n'. $nid, 'cache_category');
629 unset($cache['n'][$nid]);
630 // Delete also all entries for global data, because we don't really
631 // know how these might be affected.
632 cache_clear_all('g', 'cache_category', TRUE);
633 $cache['g'] = array();
634 }
635 return;
636 }
637
638 // For global data, we use the $key instead of $nid as identificator, for
639 // better granularity of data loading (as these are larger pieces). Both
640 // node/global parts of the cache are separated, to allow for all-global
641 // flush.
642 $part = ($nid == 0) ? 'g' : 'n';
643 $id = ($nid == 0) ? $key : $nid;
644
645 // Load existing record, if not available in static variable already.
646 if (!isset($cache[$part][$id])) {
647 $entry = cache_get($part . $id, 'cache_category');
648 if ($entry) {
649 $cache[$part][$id] = $entry->data;
650 }
651 else {
652 $cache[$part][$id] = array();
653 }
654 }
655
656 // Save new data if required.
657 if ($op == 'set') {
658 // Store serialized data, to ensure a fresh copy being always returned from
659 // static cache, without any references to the original (which might be
660 // further changed, after it got set to the cache). This is to avoid issues
661 // with PHP5 always passing and assigning objects as references, and/or with
662 // any nested by-reference elements.
663 $cache[$part][$id][$key] = serialize($data);
664 cache_set($part . $id, $cache[$part][$id], 'cache_category');
665 }
666
667 // Return data for given node/key combination.
668 return isset($cache[$part][$id][$key]) ? unserialize($cache[$part][$id][$key]) : NULL;
669 }

  ViewVC Help
Powered by ViewVC 1.1.2