/[drupal]/contributions/modules/biblio_facets/biblio_facets.module
ViewVC logotype

Contents of /contributions/modules/biblio_facets/biblio_facets.module

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


Revision 1.12 - (show annotations) (download) (as text)
Tue May 20 02:00:25 2008 UTC (18 months, 1 week ago) by davidlesieur
Branch: MAIN
CVS Tags: DRUPAL-5--1-0-BETA2, HEAD
Changes since 1.11: +2 -2 lines
File MIME type: text/x-php
Domain 'all' in hook_faceted_search_collect() becomes 'facets' to distinguish from other types of filters.
1 <?php
2 // $Id: biblio_facets.module,v 1.11 2008/04/14 17:37:16 davidlesieur Exp $
3
4 /**
5 * @file
6 * Provides facets based on Biblio fields.
7 */
8
9 require_once('./'. drupal_get_path('module', 'faceted_search') .'/faceted_search.inc');
10
11 /**
12 * Implementation of hook_faceted_search_collect().
13 */
14 function biblio_facets_faceted_search_collect(&$facets, $domain, $env_id, $selection, $arg = NULL) {
15 switch ($domain) {
16 case 'facets':
17 $fields = _biblio_facets_get_fields();
18 foreach ($fields as $field) {
19 $key = _biblio_facets_field_to_key($field->tid, $field->fid);
20 if (!isset($selection) || isset($selection['biblio_field'][$key])) {
21 if (module_exists('biblio_normalize') && biblio_normalize_is_normalized($field->tid, $field->fid)) {
22 $facets[] = new biblio_normalized_field_facet($field);
23 }
24 else {
25 $facets[] = new biblio_field_facet($field);
26 }
27 }
28 }
29 break;
30
31 case 'text':
32 // Scan the given search text for a 'biblio_field:{tid}.{fid}:"{value}"'
33 // token, and create facets from it.
34 if ($found_text = _biblio_facets_quoted_query_extract($arg, 'biblio_field')) {
35 // Extract separate facets. Using a regex rather than a simple
36 // explode(',',$found_text) because commas might be used in field values and
37 // we want to ignore those when splitting the text.
38 $specifiers = array();
39 if (preg_match_all('/((.+?[.].+?):".+?")(,| |$)/', $found_text, $specifiers)) {
40 $fields = _biblio_facets_get_fields();
41 foreach ($specifiers[1] as $index => $specifier) {
42 $key = $specifiers[2][$index];
43 if (isset($key) && isset($fields[$key]) && (!isset($selection) || isset($selection['biblio_field'][$key]))) {
44 list($tid, $fid, $value) = _biblio_facets_key_to_field($specifier);
45 if (isset($tid) && is_numeric($tid) && $tid >= 0 && isset($fid) && is_numeric($fid) && $fid > 0 && isset($value)) {
46 // Create an active facet with the value found in the search text.
47 if (module_exists('biblio_normalize') && biblio_normalize_is_normalized($tid, $fid)) {
48 $active_category = new biblio_normalized_field_category($fields[$key], $value);
49 $facets[] = new biblio_normalized_field_facet($fields[$key], $active_category);
50 }
51 else {
52 $active_category = new biblio_field_category($fields[$key], $value);
53 $facets[] = new biblio_field_facet($fields[$key], $active_category);
54 }
55 }
56 }
57 }
58 }
59 // Remove the parsed text
60 $arg = _biblio_facets_quoted_query_insert($arg, 'biblio_field');
61 }
62 return $arg;
63
64 case 'node':
65 if ($arg->type == 'biblio') {
66 $fields = _biblio_facets_get_fields();
67 foreach ($fields as $field) {
68 $key = _biblio_facets_field_to_key($field->tid, $field->fid);
69 if ((!isset($selection) || isset($selection['biblio_field'][$key])) && ($field->tid == 0 || $arg->biblio_type == $field->tid) && !empty($arg->{$field->name})) {
70 // Create a facet with the found field value as the active category.
71 if (module_exists('biblio_normalize') && biblio_normalize_is_normalized($field->tid, $field->fid)) {
72 $values = biblio_normalize_get_normalized_values($arg, $field);
73 foreach ($values as $value) {
74 $active_category = new biblio_normalized_field_category($field, $value);
75 $facets[] = new biblio_normalized_field_facet($field, $active_category);
76 }
77 }
78 else {
79 $active_category = new biblio_field_category($field, $arg->{$field->name});
80 $facets[] = new biblio_field_facet($field, $active_category);
81 }
82 }
83 }
84 }
85 break;
86 }
87 }
88
89 /**
90 * A facet for Biblio fields. Each facet represents a Biblio field.
91 *
92 * @see biblio_field_category
93 */
94 class biblio_field_facet extends faceted_search_facet {
95 /**
96 * Field object corresponding to this facet.
97 *
98 * @see biblio_facets_get_fields
99 */
100 var $_field = NULL;
101
102 /**
103 * Constructor. Optionally assigns the active category of the facet.
104 *
105 * @param $field
106 * Field object corresponding to this facet.
107 *
108 * @param $active_category
109 * Optional. This facet's active category.
110 */
111 function biblio_field_facet($field, $active_category = NULL) {
112 $this->_field = $field;
113 $active_path = array();
114 if (isset($active_category)) {
115 $active_path[] = $active_category;
116 }
117 parent::faceted_search_facet('biblio_field', $active_path);
118 parent::set_weight($field->weight); // Assign default weight.
119 }
120
121 /**
122 * Return the id of this facet.
123 */
124 function get_id() {
125 return _biblio_facets_field_to_key($this->_field->tid, $this->_field->fid);
126 }
127
128 /**
129 * Return the label of this facet. The implementor is responsible to ensure
130 * adequate security filtering.
131 */
132 function get_label() {
133 return check_plain($this->_field->title);
134 }
135
136 /**
137 * Return the name of the type using this field to help site administrators
138 * differenciate fields that use the same name.
139 */
140 function get_help() {
141 return $this->_field->typename;
142 }
143
144 /**
145 * Returns the available sort options for this facet.
146 */
147 function get_sort_options() {
148 $options = parent::get_sort_options();
149 $options['field'] = check_plain($this->_field->title);
150 return $options;
151 }
152
153 /**
154 * Handler for the 'count' sort criteria.
155 */
156 function build_sort_query_count(&$query) {
157 $query->add_orderby('count', 'DESC');
158 $query->add_orderby(db_escape_string($this->_field->name), 'ASC');
159 }
160
161 /**
162 * Handler for the 'field' sort criteria.
163 */
164 function build_sort_query_field(&$query) {
165 $query->add_orderby(db_escape_string($this->_field->name), 'ASC');
166 }
167
168 /**
169 * Return the search text for this facet, taking into account this facet's
170 * active path.
171 */
172 function get_text() {
173 if ($category = $this->get_active_category()) {
174 return _biblio_facets_field_to_key($this->_field->tid, $this->_field->fid, $category->_value);
175 }
176 return '';
177 }
178
179 /**
180 * Updates a query for retrieving the root categories of this facet and their
181 * associated nodes within the current search results.
182 *
183 * @param $query
184 * The query object to update.
185 *
186 * @return
187 * FALSE if this facet can't have root categories.
188 */
189 function build_root_categories_query(&$query) {
190 $query->add_table('biblio', 'vid', 'n', 'vid');
191 $query->add_field('biblio', db_escape_string($this->_field->name));
192 $query->add_where("n.type = 'biblio'");
193 $query->add_where('CHAR_LENGTH(biblio.'. db_escape_string($this->_field->name) .') > 0');
194 $query->add_groupby('biblio.'. db_escape_string($this->_field->name));
195 if ($this->_field->tid == 0) {
196 // If tid is zero, it means the field is common to all types. Don't check
197 // for a specific type, but make sure we check only for visible ones.
198 $query->add_where('biblio.biblio_type IN ('. implode(', ', array_keys(biblio_facets_types())) .')');
199 }
200 else {
201 $query->add_where('biblio.biblio_type = %d', $this->_field->tid);
202 }
203 return TRUE;
204 }
205
206 /**
207 * This factory method creates categories given query results that include the
208 * fields selected in get_root_categories_query() or get_subcategories_query().
209 *
210 * @param $results
211 * $results A database query result resource.
212 *
213 * @return
214 * Array of categories.
215 */
216 function build_categories($results) {
217 $categories = array();
218 while ($result = db_fetch_object($results)) {
219 $field_name = 'biblio_'. db_escape_string($this->_field->name);
220 // Add a new category.
221 $categories[] = new biblio_field_category(
222 $this->_field,
223 $result->$field_name,
224 $result->count
225 );
226 }
227 return $categories;
228 }
229 }
230
231 /**
232 * A facet for normalized Biblio fields. Each facet represents a Biblio field
233 * that's been normalized with the Biblio Normalize module.
234 *
235 * @see biblio_normalized_field_category
236 */
237 class biblio_normalized_field_facet extends biblio_field_facet {
238
239 /**
240 * Constructor. Optionally assigns the active category of the facet.
241 *
242 * @param $field
243 * Field object corresponding to this facet.
244 *
245 * @param $active_category
246 * Optional. This facet's active category.
247 */
248 function biblio_normalized_field_facet($field, $active_category = NULL) {
249 parent::biblio_field_facet($field, $active_category);
250 }
251
252 /**
253 * Handler for the 'count' sort criteria.
254 */
255 function build_sort_query_count(&$query) {
256 $query->add_orderby('count', 'DESC');
257 $query->add_orderby('biblio_normalize.value', 'ASC');
258 }
259
260 /**
261 * Handler for the 'field' sort criteria.
262 */
263 function build_sort_query_field(&$query) {
264 $query->add_orderby('biblio_normalize.value', 'ASC');
265 }
266
267 /**
268 * Updates a query for retrieving the root categories of this facet and their
269 * associated nodes within the current search results.
270 *
271 * @param $query
272 * The query object to update.
273 *
274 * @return
275 * FALSE if this facet can't have root categories.
276 */
277 function build_root_categories_query(&$query) {
278 $query->add_table('biblio_normalize', 'nid', 'n', 'nid');
279 $query->add_field('biblio_normalize', 'value');
280 $query->add_where("n.type = 'biblio'");
281 $query->add_where('biblio_normalize.fid = %d', $this->_field->fid);
282 $query->add_groupby('biblio_normalize_value');
283 if ($this->_field->tid == 0) {
284 // If tid is zero, it means the field is common to all types. Don't check
285 // for a specific type, but make sure we check only for visible ones.
286 $query->add_where('biblio_normalize.tid IN ('. implode(', ', array_keys(biblio_facets_types())) .')');
287 }
288 else {
289 $query->add_where('biblio_normalize.tid = %d', $this->_field->tid);
290 }
291 return TRUE;
292 }
293
294 /**
295 * This factory method creates categories given query results that include the
296 * fields selected in get_root_categories_query() or get_subcategories_query().
297 *
298 * @param $results
299 * $results A database query result resource.
300 *
301 * @return
302 * Array of categories.
303 */
304 function build_categories($results) {
305 $categories = array();
306 while ($result = db_fetch_object($results)) {
307 // Add a new category.
308 $categories[] = new biblio_normalized_field_category(
309 $this->_field,
310 $result->biblio_normalize_value,
311 $result->count
312 );
313 }
314 return $categories;
315 }
316 }
317
318 /**
319 * A facet category for Biblio fields.
320 *
321 * @see biblio_field_facet
322 */
323 class biblio_field_category extends faceted_search_category {
324 var $_field_tid = NULL;
325 var $_field_fid = NULL;
326 var $_field_name = NULL;
327 var $_value = '';
328
329 /**
330 * Constructor.
331 */
332 function biblio_field_category($field, $value, $count = NULL) {
333 parent::faceted_search_category($count);
334 $this->_field_tid = $field->tid;
335 $this->_field_fid = $field->fid;
336 $this->_field_name = $field->name;
337 $this->_value = $value;
338 }
339
340 /**
341 * Return the label of this category.
342 *
343 * @param $html
344 * TRUE when HTML is allowed in the label, FALSE otherwise. Checking this
345 * flag allows implementors to provide a rich-text label if desired, and an
346 * alternate plain text version for cases where HTML cannot be used. The
347 * implementor is responsible to ensure adequate security filtering.
348 */
349 function get_label($html = FALSE) {
350 return check_plain($this->_value);
351 }
352
353 /**
354 * Updates a query for selecting nodes matching this category.
355 *
356 * @param $query
357 * The query object to update.
358 */
359 function build_results_query(&$query) {
360 // Using tid and field name in table alias to avoid duplicate table names in JOINs
361 $alias = 'biblio_'. $this->_field_tid .'_'. $this->_field_fid;
362 $query->add_table('biblio', 'vid', 'n', 'vid', $alias);
363 $query->add_where("$alias.". db_escape_string($this->_field_name) ." = '%s'", $this->_value);
364 if ($this->_field_tid == 0) {
365 // If tid is zero, it means the field is common to all types. Don't check
366 // for a specific type, but make sure we check only for visible ones.
367 $query->add_where("$alias.biblio_type IN (". implode(', ', array_keys(biblio_facets_types())) .')');
368 }
369 else {
370 $query->add_where("$alias.biblio_type = %d", $this->_field_tid);
371 }
372 }
373 }
374
375 /**
376 * A facet category for normalized Biblio fields.
377 *
378 * @see biblio_field_facet
379 */
380 class biblio_normalized_field_category extends biblio_field_category {
381
382 /**
383 * Constructor.
384 */
385 function biblio_normalized_field_category($field, $value, $count = NULL) {
386 parent::biblio_field_category($field, $value, $count);
387 }
388
389 /**
390 * Updates a query for selecting nodes matching this category.
391 *
392 * @param $query
393 * The query object to update.
394 */
395 function build_results_query(&$query) {
396 // Using tid and field name in table alias to avoid duplicate table names in JOINs
397 $alias = 'biblio_normalize_'. $this->_field_tid .'_'. $this->_field_fid;
398 $query->add_table('biblio_normalize', 'nid', 'n', 'nid', $alias);
399 $query->add_where("$alias.value = '%s'", $this->_value);
400 if ($this->_field_tid == 0) {
401 // If tid is zero, it means the field is common to all types. Don't check
402 // for a specific type, but make sure we check only for visible ones.
403 $query->add_where("$alias.tid IN (". implode(', ', array_keys(biblio_facets_types())) .')');
404 }
405 else {
406 $query->add_where("$alias.tid = %d", $this->_field_tid);
407 }
408 }
409 }
410
411 // --------------------------------------------------------------------------
412 // Internal stuff
413
414 /**
415 * Retrieves a specified Biblio type, or all visible Biblio types.
416 *
417 * @param $tid
418 * Optional. The id of the Biblio type to retrieve. If omitted, all
419 * visible Biblio types are returned.
420 *
421 * @return
422 * Type object if a specific $tid was requested, or an array of type objects
423 * otherwise.
424 */
425 function biblio_facets_types($tid = 0) {
426 static $types = NULL;
427 if (!isset($types)) {
428 $types = array();
429 $results = db_query('SELECT tid, name, weight FROM {biblio_types} WHERE tid > 0 AND visible = 1 ORDER BY weight, name', $tid);
430 while ($type = db_fetch_object($results)) {
431 $types[$type->tid] = $type;
432 }
433 }
434 if ($tid == 0) {
435 return $types;
436 }
437 else {
438 return $types[$tid];
439 }
440 }
441
442 /**
443 * Retrieves all fields provided by Biblio (common fields and
444 * type-specific fields).
445 *
446 * @return
447 * Array of field objects. A field object has the following attributes: tid
448 * (Biblio type id), fid (Biblio field id), title, name (database column in
449 * the Biblio table), weight. A tid of 0 denotes a field that's common to all
450 * Biblio types.
451 */
452 function _biblio_facets_get_fields() {
453 $fields = array();
454 // Note: The Biblio module has a visible attribute at the field level, but
455 // doesn't seem to use it at the moment. If that changes, we might have to add
456 // a condition for field visibility.
457 $results = db_query('SELECT 0 AS tid, f.fid, f.title, f.name, f.weight, \'common\' AS typename FROM {biblio_fields} f WHERE f.common = 1 UNION SELECT d.tid, d.fid, d.title, f.name, d.weight, t.name AS typename FROM {biblio_type_details} d INNER JOIN {biblio_types} t ON t.tid = d.tid INNER JOIN {biblio_fields} f ON f.fid = d.fid WHERE t.visible = 1');
458 while ($field = db_fetch_object($results)) {
459 $key = _biblio_facets_field_to_key($field->tid, $field->fid);
460 $fields[$key] = $field;
461 }
462 return $fields;
463 }
464
465 /**
466 * Returns a composite key with the specified field info.
467 */
468 function _biblio_facets_field_to_key($tid, $fid, $value = NULL) {
469 $key = $tid .'.'. $fid;
470 if (isset($value)) {
471 // Escape double-quotes in the value and enclose it between double-quotes.
472 $key .= ':"'. strtr($value, '"', '""') .'"';
473 }
474 return $key;
475 }
476
477 /**
478 * Extracts field info from the specified composite key.
479 *
480 * @return
481 * Array with tid, fid, and additional value (if any).
482 */
483 function _biblio_facets_key_to_field($key) {
484 $components = explode(':', $key, 2);
485 if (isset($components[1])) {
486 // Unescape double-quotes, then trim surrounding double-quotes from value.
487 $value = trim(strtr($components[1], '""', '"'), '"');
488 }
489 $components = explode('.', $components[0], 2);
490 if (isset($value)) {
491 $components[] = $value;
492 }
493 return $components;
494 }
495
496 // TODO: Use faceted_search_quoted_query_extract()
497 function _biblio_facets_quoted_query_extract($keys, $option) {
498 // Based on search_query_extract, but keys terminate on a double-quote
499 // followed by a space rather than a single space.
500 if (preg_match('/(^| )'. $option .':(.*?)(" |$)/i', $keys, $matches)) {
501 return $matches[2];
502 }
503 }
504
505 // TODO: Use faceted_search_quoted_query_insert()
506 function _biblio_facets_quoted_query_insert($keys, $option, $value = '') {
507 // Based on search_query_insert, but keys terminate on a double-quote followed
508 // by a space rather than a single space.
509 if (search_query_extract($keys, $option)) {
510 $keys = trim(preg_replace('/(^| )'. $option .':(.*?)(" |$)/i', ' ', $keys));
511 }
512 if ($value != '') {
513 $keys .= ' '. $option .':'. $value;
514 }
515 return $keys;
516 }
517

  ViewVC Help
Powered by ViewVC 1.1.2