/[drupal]/contributions/modules/cck_facets/date_facets.module
ViewVC logotype

Contents of /contributions/modules/cck_facets/date_facets.module

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


Revision 1.10 - (show annotations) (download) (as text)
Fri May 1 22:12:33 2009 UTC (6 months, 3 weeks ago) by davidlesieur
Branch: MAIN
CVS Tags: HEAD
Changes since 1.9: +4 -4 lines
File MIME type: text/x-php
Fixed warning.
1 <?php
2 // $Id: date_facets.module,v 1.9 2009/05/01 21:21:53 davidlesieur Exp $
3
4 /**
5 * @file
6 * Exposes CCK Date fields as facets.
7 */
8
9 module_load_include('inc', 'cck_facets');
10
11 // TODO: Support 'todate' column.
12 // TODO: Timezone handling.
13 // TODO: Support for CCK Date granularity.
14
15 /**
16 * Implementation of hook_cck_facets_collect().
17 */
18 function date_facets_cck_facets_collect(&$facets, $field, $domain, $env, $arg = NULL) {
19 if ($field['type'] == 'date' || $field['type'] == 'datestamp') {
20 $facet_class = $field['type'] .'_facet';
21 $category_class = $field['type'] .'_facet_category';
22
23 switch ($domain) {
24 case 'facets':
25 $facets[] = new $facet_class($field);
26 break;
27
28 case 'text':
29 // Scan the given search text for a '{field_name}:{value}'
30 // token, and create facets from it.
31 if ($date = search_query_extract($arg, $field['field_name'])) {
32 if (preg_match('/^([12][0-9][0-9][0-9])(-([01][0-9])(-([0-3][0-9]))?)?$/', $date, $matches)) {
33 if (_date_facets_check_date($matches[1], $matches[3], $matches[5])) {
34 // Create an active facet with the value found in the search text.
35 $active_path = array();
36 if (isset($matches[1])) {
37 // Found year.
38 $active_path[] = new $category_class($field, $matches[1], NULL, NULL);
39 }
40 if (isset($matches[3])) {
41 // Found month.
42 $active_path[] = new $category_class($field, $matches[1], $matches[3], NULL);
43 }
44 if (isset($matches[5])) {
45 // Found day.
46 $active_path[] = new $category_class($field, $matches[1], $matches[3], $matches[5]);
47 }
48 $facets[] = new $facet_class($field, $active_path);
49 }
50 }
51 // Remove the parsed text
52 $arg = search_query_insert($arg, $field['field_name']);
53 }
54 break;
55
56 case 'node':
57 if (isset($arg->{$field['field_name']}) && is_array($arg->{$field['field_name']})) {
58 if ($field['type'] == 'date') {
59 // Iterate through the field's multiple values.
60 foreach ($arg->{$field['field_name']} as $item) {
61 $date = array_shift($item); // Take the item's main column.
62 if (preg_match('/^([12][0-9][0-9][0-9])(-([01][0-9])(-([0-3][0-9]))?)?/', $date, $matches)) {
63 $active_path = array(
64 new date_facet_category($field, $matches[1], NULL, NULL),
65 new date_facet_category($field, $matches[1], $matches[3], NULL),
66 new date_facet_category($field, $matches[1], $matches[3], $matches[5]),
67 );
68 // Create an active facet.
69 $facets[] = new date_facet($field, $active_path);
70 }
71 }
72 }
73 elseif ($field['type'] == 'datestamp') {
74 // Iterate through the field's multiple values.
75 foreach ($arg->{$field['field_name']} as $item) {
76 $timestamp = array_shift($item); // Take the item's main column.
77 $date = date_make_date($timestamp, $field['timezone_db'], $field['type'], $field['granularity']);
78 $year = date_format_date($date, 'custom', 'Y');
79 $month = date_format_date($date, 'custom', 'n');
80 $day = date_format_date($date, 'custom', 'j');
81
82 $active_path = array(
83 new datestamp_facet_category($field, $year, NULL, NULL),
84 new datestamp_facet_category($field, $year, $month, NULL),
85 new datestamp_facet_category($field, $year, $month, $day),
86 );
87 $facets[] = new datestamp_facet($field, $active_path);
88 }
89 }
90 }
91 break;
92 }
93 }
94 return $arg;
95 }
96
97 /**
98 * A facet for Date fields.
99 *
100 * @see date_facet_category
101 */
102 class date_facet extends cck_facet {
103 /**
104 * Constructor.
105 *
106 * @param $field
107 * The field corresponding to this facet.
108 * @param $active_path
109 * Array representing the path leading to the active category, including the
110 * active category itself. Defaults to an empty array, meaning no active
111 * category.
112 */
113 function date_facet($field, $active_path = array()) {
114 parent::cck_facet($field, $active_path);
115 }
116
117 /**
118 * Returns the search text for this facet, taking into account this facet's
119 * active path.
120 */
121 function get_text() {
122 if ($category = $this->get_active_category()) {
123 return $category->get_text();
124 }
125 return '';
126 }
127
128 /**
129 * Handler for the 'count' sort criteria.
130 */
131 function build_sort_query_count(&$query) {
132 $query->add_orderby('count', 'DESC');
133 $this->build_sort_query_field($query);
134 }
135
136 /**
137 * Handler for the 'field' sort criteria.
138 */
139 function build_sort_query_field(&$query) {
140 $query->add_orderby($this->_field['field_name'] .'_year', 'ASC');
141 if ($query->has_field($this->_field['field_name'] .'_month')) {
142 $query->add_orderby($this->_field['field_name'] .'_month', 'ASC');
143 if ($query->has_field($this->_field['field_name'] .'_day')) {
144 $query->add_orderby($this->_field['field_name'] .'_day', 'ASC');
145 }
146 }
147 }
148
149 /**
150 * Updates a query for retrieving the root categories of this facet and their
151 * associated nodes within the current search results.
152 *
153 * @param $query
154 * The query object to update.
155 * @return
156 * FALSE if this facet can't have root categories.
157 */
158 function build_root_categories_query(&$query) {
159 $db_info = _cck_facets_db_info($this->_field);
160 $main_column = array_shift($db_info['columns']);
161 $query->add_table($db_info['table'], 'vid', 'n', 'vid');
162 $query->add_field(NULL, "YEAR(${db_info['table']}.${main_column['column']})", $this->_field['field_name'] .'_year');
163 $query->add_where($db_info['table'] .'.'. $main_column['column'] .' IS NOT NULL');
164 $query->add_groupby($this->_field['field_name'] .'_year');
165 return TRUE;
166 }
167
168 /**
169 * This factory method creates categories given query results that include the
170 * fields selected in get_root_categories_query() or get_subcategories_query().
171 *
172 * @param $results
173 * $results A database query result resource.
174 *
175 * @return
176 * Array of categories.
177 */
178 function build_categories($results) {
179 $categories = array();
180 while ($result = db_fetch_object($results)) {
181 $categories[] = new date_facet_category($this->_field, $result->{$this->_field['field_name'] .'_year'}, $result->{$this->_field['field_name'] .'_month'}, $result->{$this->_field['field_name'] .'_day'}, $result->count);
182 }
183 return $categories;
184 }
185 }
186
187 /**
188 * A facet category for Date fields.
189 */
190 class date_facet_category extends cck_facet_category {
191
192 /**
193 * Constructs a category for the specified date.
194 *
195 * @param $year
196 * Year corresponding to this category.
197 * @param $month
198 * Month corresponding to this category. Optional, but must be specified if
199 * $day is specified.
200 * @param $day
201 * Day corresponding to this category. Optional.
202 * @param $count
203 * The number of nodes associated to this category within the current
204 * search. Optional.
205 */
206 function date_facet_category($field, $year, $month = NULL, $day = NULL, $count = NULL) {
207 parent::cck_facet_category($field, array('year' => $year, 'month' => $month, 'day' => $day), $count);
208 }
209
210 /**
211 * Return the label of this category.
212 *
213 * @param $html
214 * TRUE when HTML is allowed in the label, FALSE otherwise. Checking this
215 * flag allows implementors to provide a rich-text label if desired, and an
216 * alternate plain text version for cases where HTML cannot be used. The
217 * implementor is responsible to ensure adequate security filtering.
218 */
219 function get_label($html = FALSE) {
220 if (isset($this->_value['day'])) {
221 // Format date with YYYY-MM-DD.
222 $timestamp = gmmktime(0, 0, 0, (int)$this->_value['month'], (int)$this->_value['day'], (int)$this->_value['year']);
223 $format = variable_get('date_facets_format_ymd', 'm/d/Y');
224 }
225 elseif (isset($this->_value['month'])) {
226 // Format date with YYYY-MM.
227 $timestamp = gmmktime(0, 0, 0, (int)$this->_value['month'], 1, (int)$this->_value['year']);
228 $format = variable_get('date_facets_format_ym', 'm/Y');
229 }
230 elseif (isset($this->_value['year'])) {
231 // Format date with YYYY.
232 $timestamp = gmmktime(0, 0, 0, 1, 1, (int)$this->_value['year']);
233 $format = variable_get('date_facets_format_y', 'Y');
234 }
235 if ($timestamp) {
236 return format_date($timestamp, 'custom', $format, 0);
237 }
238 }
239
240 /**
241 * Return the search text for this category.
242 */
243 function get_text() {
244 $text = sprintf('%04d', $this->_value['year']);
245 if (isset($this->_value['month'])) {
246 $text .= sprintf('-%02d', $this->_value['month']);
247 if (isset($this->_value['day'])) {
248 $text .= sprintf('-%02d', $this->_value['day']);
249 }
250 }
251 return $text;
252 }
253
254 /**
255 * Updates a query for retrieving the subcategories of this category and their
256 * associated nodes within the current search results.
257 *
258 * This only needs to be overridden for hierarchical facets.
259 *
260 * @param $query
261 * The query object to update.
262 * @return
263 * FALSE if this facet can't have subcategories.
264 */
265 function build_subcategories_query(&$query) {
266 if (isset($this->_value['day'])) {
267 return FALSE; // No subcategories.
268 }
269
270 $db_info = _cck_facets_db_info($this->_field);
271 $main_column = array_shift($db_info['columns']);
272 $query->add_table($db_info['table'], 'vid', 'n', 'vid');
273 if (isset($this->_value['month'])) {
274 $from = sprintf("'%04d-%02d-01 00:00:00'", $this->_value['year'], $this->_value['month']);
275 $query->add_field(NULL, "YEAR(${db_info['table']}.${main_column['column']})", $this->_field['field_name'] .'_year');
276 $query->add_field(NULL, "MONTH(${db_info['table']}.${main_column['column']})", $this->_field['field_name'] .'_month');
277 $query->add_field(NULL, "DAY(${db_info['table']}.${main_column['column']})", $this->_field['field_name'] .'_day');
278 $query->add_where("(${db_info['table']}.${main_column['column']} >= $from)");
279 $query->add_where("(${db_info['table']}.${main_column['column']} < $from + INTERVAL 1 MONTH)");
280 $query->add_groupby($this->_field['field_name'] .'_day');
281 return TRUE;
282 }
283 elseif (isset($this->_value['year'])) {
284 $from = sprintf("'%04d-01-01 00:00:00'", $this->_value['year']);
285 $query->add_field(NULL, "YEAR(${db_info['table']}.${main_column['column']})", $this->_field['field_name'] .'_year');
286 $query->add_field(NULL, "MONTH(${db_info['table']}.${main_column['column']})", $this->_field['field_name'] .'_month');
287 $query->add_where("(${db_info['table']}.${main_column['column']} >= $from)");
288 $query->add_where("(${db_info['table']}.${main_column['column']} < $from + INTERVAL 1 YEAR)");
289 $query->add_groupby($this->_field['field_name'] .'_month');
290 return TRUE;
291 }
292 return FALSE; // Unreachable, unless something is wrong...
293 }
294
295 /**
296 * Updates a query for selecting nodes matching this category.
297 *
298 * @param $query
299 * The query object to update.
300 */
301 function build_results_query(&$query) {
302 $db_info = _cck_facets_db_info($this->_field);
303 $main_column = array_shift($db_info['columns']);
304 // Since the same table might be joined multiple times in the query, we use
305 // the field's name as the table alias to create a unique table reference.
306 $table = $query->add_table($db_info['table'], 'vid', 'n', 'vid', $this->_field['field_name']);
307 if (isset($this->_value['day'])) {
308 $from = sprintf("'%04d-%02d-%02d 00:00:00'", $this->_value['year'], $this->_value['month'], $this->_value['day']);
309 $query->add_where("($table.${main_column['column']} >= $from)");
310 $query->add_where("($table.${main_column['column']} < $from + INTERVAL 1 DAY)");
311 }
312 elseif (isset($this->_value['month'])) {
313 $from = sprintf("'%04d-%02d-01 00:00:00'", $this->_value['year'], $this->_value['month']);
314 $query->add_where("($table.${main_column['column']} >= $from)");
315 $query->add_where("($table.${main_column['column']} < $from + INTERVAL 1 MONTH)");
316 }
317 elseif (isset($this->_value['year'])) {
318 $from = sprintf("'%04d-01-01 00:00:00'", $this->_value['year']);
319 $query->add_where("($table.${main_column['column']} >= $from)");
320 $query->add_where("($table.${main_column['column']} < $from + INTERVAL 1 YEAR)");
321 }
322 }
323 }
324
325 /**
326 * A facet for Datestamp fields.
327 *
328 * @see datestamp_facet_category
329 */
330 class datestamp_facet extends date_facet {
331 /**
332 * Constructor.
333 *
334 * @param $field
335 * The field corresponding to this facet.
336 * @param $active_path
337 * Array representing the path leading to the active category, including the
338 * active category itself. Defaults to an empty array, meaning no active
339 * category.
340 */
341 function datestamp_facet($field, $active_path = array()) {
342 parent::date_facet($field, $active_path);
343 }
344
345 /**
346 * Updates a query for retrieving the root categories of this facet and their
347 * associated nodes within the current search results.
348 *
349 * @param $query
350 * The query object to update.
351 * @return
352 * FALSE if this facet can't have root categories.
353 */
354 function build_root_categories_query(&$query) {
355 $db_info = _cck_facets_db_info($this->_field);
356 $main_column = array_shift($db_info['columns']);
357 $query->add_table($db_info['table'], 'vid', 'n', 'vid');
358 $query->add_field(NULL, "YEAR(FROM_UNIXTIME(${db_info['table']}.${main_column['column']}))", $this->_field['field_name'] .'_year');
359 $query->add_where($db_info['table'] .'.'. $main_column['column'] .' > 0');
360 $query->add_groupby($this->_field['field_name'] .'_year');
361 return TRUE;
362 }
363
364 /**
365 * This factory method creates categories given query results that include the
366 * fields selected in get_root_categories_query() or get_subcategories_query().
367 *
368 * @param $results
369 * $results A database query result resource.
370 *
371 * @return
372 * Array of categories.
373 */
374 function build_categories($results) {
375 $categories = array();
376 while ($result = db_fetch_object($results)) {
377 $categories[] = new datestamp_facet_category($this->_field, $result->{$this->_field['field_name'] .'_year'}, $result->{$this->_field['field_name'] .'_month'}, $result->{$this->_field['field_name'] .'_day'}, $result->count);
378 }
379 return $categories;
380 }
381 }
382
383 /**
384 * A facet category for Date fields.
385 */
386 class datestamp_facet_category extends date_facet_category {
387
388 /**
389 * Constructs a category for the specified date.
390 *
391 * @param $year
392 * Year corresponding to this category.
393 * @param $month
394 * Month corresponding to this category. Optional, but must be specified if
395 * $day is specified.
396 * @param $day
397 * Day corresponding to this category. Optional.
398 * @param $count
399 * The number of nodes associated to this category within the current
400 * search. Optional.
401 */
402 function datestamp_facet_category($field, $year, $month = NULL, $day = NULL, $count = NULL) {
403 parent::date_facet_category($field, $year, $month, $day, $count);
404 }
405
406 /**
407 * Updates a query for retrieving the subcategories of this category and their
408 * associated nodes within the current search results.
409 *
410 * This only needs to be overridden for hierarchical facets.
411 *
412 * @param $query
413 * The query object to update.
414 * @return
415 * FALSE if this facet can't have subcategories.
416 */
417 function build_subcategories_query(&$query) {
418 if (isset($this->_value['day'])) {
419 return FALSE; // No subcategories.
420 }
421
422 $db_info = _cck_facets_db_info($this->_field);
423 $main_column = array_shift($db_info['columns']);
424 $query->add_table($db_info['table'], 'vid', 'n', 'vid');
425 if (isset($this->_value['month'])) {
426 $from = sprintf("'%04d-%02d-01 00:00:00'", $this->_value['year'], $this->_value['month']);
427 $query->add_field(NULL, "YEAR(FROM_UNIXTIME(${db_info['table']}.${main_column['column']}))", $this->_field['field_name'] .'_year');
428 $query->add_field(NULL, "MONTH(FROM_UNIXTIME(${db_info['table']}.${main_column['column']}))", $this->_field['field_name'] .'_month');
429 $query->add_field(NULL, "DAY(FROM_UNIXTIME(${db_info['table']}.${main_column['column']}))", $this->_field['field_name'] .'_day');
430 $query->add_where("(${db_info['table']}.${main_column['column']} >= $from)");
431 $query->add_where("(${db_info['table']}.${main_column['column']} < $from + INTERVAL 1 MONTH)");
432 $query->add_groupby($this->_field['field_name'] .'_day');
433 return TRUE;
434 }
435 elseif (isset($this->_value['year'])) {
436 $from = sprintf("'%04d-01-01 00:00:00'", $this->_value['year']);
437 $query->add_field(NULL, "YEAR(FROM_UNIXTIME(${db_info['table']}.${main_column['column']}))", $this->_field['field_name'] .'_year');
438 $query->add_field(NULL, "MONTH(FROM_UNIXTIME(${db_info['table']}.${main_column['column']}))", $this->_field['field_name'] .'_month');
439 $query->add_where("(${db_info['table']}.${main_column['column']} >= $from)");
440 $query->add_where("(${db_info['table']}.${main_column['column']} < $from + INTERVAL 1 YEAR)");
441 $query->add_groupby($this->_field['field_name'] .'_month');
442 return TRUE;
443 }
444 return FALSE; // Unreachable, unless something is wrong...
445 }
446
447 /**
448 * Updates a query for selecting nodes matching this category.
449 *
450 * @param $query
451 * The query object to update.
452 */
453 function build_results_query(&$query) {
454 $db_info = _cck_facets_db_info($this->_field);
455 $main_column = array_shift($db_info['columns']);
456 // Since the same table might be joined multiple times in the query, we use
457 // the field's name as the table alias to create a unique table reference.
458 $table = $query->add_table($db_info['table'], 'vid', 'n', 'vid', $this->_field['field_name']);
459 if (isset($this->_value['day'])) {
460 $from = sprintf("'%04d-%02d-%02d 00:00:00'", $this->_value['year'], $this->_value['month'], $this->_value['day']);
461 $query->add_where("($table.${main_column['column']} >= UNIX_TIMESTAMP($from))");
462 $query->add_where("($table.${main_column['column']} < UNIX_TIMESTAMP($from + INTERVAL 1 DAY))");
463 }
464 elseif (isset($this->_value['month'])) {
465 $from = sprintf("'%04d-%02d-01 00:00:00'", $this->_value['year'], $this->_value['month']);
466 $query->add_where("($table.${main_column['column']} >= UNIX_TIMESTAMP($from))");
467 $query->add_where("($table.${main_column['column']} < UNIX_TIMESTAMP($from + INTERVAL 1 MONTH))");
468 }
469 elseif (isset($this->_value['year'])) {
470 $from = sprintf("'%04d-01-01 00:00:00'", $this->_value['year']);
471 $query->add_where("($table.${main_column['column']} >= UNIX_TIMESTAMP($from))");
472 $query->add_where("($table.${main_column['column']} < UNIX_TIMESTAMP($from + INTERVAL 1 YEAR))");
473 }
474 }
475 }
476
477 /**
478 * Checks the validity of a date or partial date.
479 */
480 // TODO: avoid duplication with _date_authored_facet_check_date
481 function _date_facets_check_date($year = NULL, $month = NULL, $day = NULL) {
482 if (!isset($year) || !is_numeric($year) || $year < 1) {
483 return FALSE;
484 }
485 if (isset($month) && (!is_numeric($month) || $month < 1 || $month > 12)) {
486 return FALSE;
487 }
488 if (isset($day) && (!isset($month) || !is_numeric($month) || !is_numeric($day) || !checkdate($month, $day, $year))) {
489 return FALSE;
490 }
491
492 return TRUE;
493 }

  ViewVC Help
Powered by ViewVC 1.1.2