| 1 |
<?php
|
| 2 |
// $Id: taxonomy_filter.module,v 1.20 2009/07/25 23:39:02 solotandem Exp $
|
| 3 |
|
| 4 |
/**
|
| 5 |
* @file
|
| 6 |
* The core Taxonomy Filter module containing the API bits.
|
| 7 |
*/
|
| 8 |
|
| 9 |
/*
|
| 10 |
TODO - Overall tasks before release:
|
| 11 |
Refactor data structures etc - weed out redundancies
|
| 12 |
Investigate block caching when n_a modules installed
|
| 13 |
Depth handling - both on the outgoing links and on term selection
|
| 14 |
Check for unused functions
|
| 15 |
Add back some sql_rewrite wrappers on database queries for sites that use access control on terms
|
| 16 |
Documentation and commenting
|
| 17 |
Add explanation text to admin UI
|
| 18 |
Add Drupal 5 upgrade code
|
| 19 |
Implement tests (maybe later once I understand it)
|
| 20 |
*/
|
| 21 |
|
| 22 |
|
| 23 |
|
| 24 |
// Define constants for variable defaults
|
| 25 |
define('TAXONOMY_FILTER_INPUT', 'taxonomy/term/%tids/%depth!feed'); // 'taxonomy/term/%tids/%depth/!feed'
|
| 26 |
define('TAXONOMY_FILTER_OUTPUT', 'taxonomy/term/%tids/%depth');
|
| 27 |
define('TAXONOMY_FILTER_CURRENT_BLOCK_TITLE', 'Search results');
|
| 28 |
define('TAXONOMY_FILTER_REFINE_BLOCK_TITLE', 'Refine your search');
|
| 29 |
|
| 30 |
module_load_include('inc', 'taxonomy_filter', 'taxonomy_filter.base');
|
| 31 |
|
| 32 |
/**
|
| 33 |
* Implementation of hook_menu().
|
| 34 |
*/
|
| 35 |
function taxonomy_filter_menu() {
|
| 36 |
$items = array();
|
| 37 |
$items['admin/settings/taxonomy_filter'] = array(
|
| 38 |
'title' => 'Taxonomy filter',
|
| 39 |
'description' => 'Configure the taxonomy filter module.',
|
| 40 |
'page callback' => 'taxonomy_filter_admin_list',
|
| 41 |
'access arguments' => array('administer site configuration'),
|
| 42 |
'file' => 'taxonomy_filter.admin.inc',
|
| 43 |
);
|
| 44 |
$items['admin/settings/taxonomy_filter/menus'] = array(
|
| 45 |
'title' => 'Menus',
|
| 46 |
'weight' => -5,
|
| 47 |
'type' => MENU_DEFAULT_LOCAL_TASK,
|
| 48 |
);
|
| 49 |
$items['admin/settings/taxonomy_filter/general'] = array(
|
| 50 |
'title' => 'General',
|
| 51 |
'weight' => 0,
|
| 52 |
'description' => 'General settings.',
|
| 53 |
'page callback' => 'drupal_get_form',
|
| 54 |
'page arguments' => array('taxonomy_filter_admin_general'),
|
| 55 |
'access arguments' => array('administer site configuration'),
|
| 56 |
'file' => 'taxonomy_filter.admin.inc',
|
| 57 |
'type' => MENU_LOCAL_TASK,
|
| 58 |
);
|
| 59 |
$items['admin/settings/taxonomy_filter/mappings'] = array(
|
| 60 |
'title' => 'Mappings',
|
| 61 |
'weight' => 7,
|
| 62 |
'description' => 'Map menus to vocabs.',
|
| 63 |
'page callback' => 'taxonomy_filter_admin_mappings',
|
| 64 |
'access arguments' => array('administer site configuration'),
|
| 65 |
'parent' => 'admin/settings/taxonomy_filter/',
|
| 66 |
'file' => 'taxonomy_filter.admin.inc',
|
| 67 |
'type' => MENU_LOCAL_TASK,
|
| 68 |
);
|
| 69 |
$items['admin/settings/taxonomy_filter/advanced'] = array(
|
| 70 |
'title' => 'Advanced',
|
| 71 |
'weight' => 10,
|
| 72 |
'description' => 'Advanced settings.',
|
| 73 |
'page callback' => 'drupal_get_form',
|
| 74 |
'page arguments' => array('taxonomy_filter_admin_advanced'),
|
| 75 |
'access arguments' => array('administer site configuration'),
|
| 76 |
'file' => 'taxonomy_filter.admin.inc',
|
| 77 |
'type' => MENU_LOCAL_TASK,
|
| 78 |
);
|
| 79 |
$items['admin/settings/taxonomy_filter/%/edit'] = array(
|
| 80 |
// TODO - fix breadcrumb for this page
|
| 81 |
'title' => 'Edit Taxonomy Filter menu',
|
| 82 |
'page callback' => 'drupal_get_form',
|
| 83 |
'page arguments' => array('taxonomy_filter_admin_menu_edit_form', 3),
|
| 84 |
'access arguments' => array('administer site configuration'),
|
| 85 |
'file' => 'taxonomy_filter.admin.inc',
|
| 86 |
'type' => MENU_CALLBACK,
|
| 87 |
);
|
| 88 |
$items['admin/settings/taxonomy_filter/%/delete'] = array(
|
| 89 |
'title' => 'Delete Taxonomy Filter menu',
|
| 90 |
'page callback' => 'drupal_get_form',
|
| 91 |
'page arguments' => array('taxonomy_filter_admin_menu_delete_confirm', 3),
|
| 92 |
'access arguments' => array('administer site configuration'),
|
| 93 |
'file' => 'taxonomy_filter.admin.inc',
|
| 94 |
'type' => MENU_CALLBACK,
|
| 95 |
);
|
| 96 |
$items['admin/settings/taxonomy_filter/mappings/%/edit'] = array(
|
| 97 |
'title' => 'Edit Taxonomy Filter mappings',
|
| 98 |
'page callback' => 'drupal_get_form',
|
| 99 |
'page arguments' => array('taxonomy_filter_admin_mappings_edit_form', 4),
|
| 100 |
'access arguments' => array('administer site configuration'),
|
| 101 |
'parent' => 'admin/settings/taxonomy_filter/mappings',
|
| 102 |
'file' => 'taxonomy_filter.admin.inc',
|
| 103 |
'type' => MENU_CALLBACK,
|
| 104 |
);
|
| 105 |
return $items;
|
| 106 |
}
|
| 107 |
|
| 108 |
/**
|
| 109 |
* Implementation of hook_taxonomy().
|
| 110 |
*/
|
| 111 |
function taxonomy_filter_taxonomy($op, $type, $object = NULL) {
|
| 112 |
// TODO: when manual block caching is implemented, the cache will need to be flushed
|
| 113 |
// after taxonomy changes.
|
| 114 |
if ($type == 'vocabulary') {
|
| 115 |
taxonomy_filter_update_mappings();
|
| 116 |
}
|
| 117 |
}
|
| 118 |
|
| 119 |
/**
|
| 120 |
* Implementation of hook_enable().
|
| 121 |
*/
|
| 122 |
function taxonomy_filter_enable() {
|
| 123 |
$filters = variable_get('taxonomy_filter_input', TAXONOMY_FILTER_INPUT);
|
| 124 |
taxonomy_filter_parse_input_filters($filters);
|
| 125 |
taxonomy_filter_update_mappings();
|
| 126 |
}
|
| 127 |
|
| 128 |
/**
|
| 129 |
* Implementation of hook_help().
|
| 130 |
*/
|
| 131 |
function taxonomy_filter_help() {
|
| 132 |
// TODO?
|
| 133 |
}
|
| 134 |
|
| 135 |
/**
|
| 136 |
* Implementation of hook_forms().
|
| 137 |
*/
|
| 138 |
function taxonomy_filter_forms() {
|
| 139 |
// use this for building menu settings forms?
|
| 140 |
}
|
| 141 |
|
| 142 |
/**
|
| 143 |
* Implementation of hook_theme().
|
| 144 |
*/
|
| 145 |
function taxonomy_filter_theme($existing, $type, $theme, $path) {
|
| 146 |
return array(
|
| 147 |
'taxonomy_filter_admin_list_form_templates' => array(
|
| 148 |
'arguments' => array('form' => array()),
|
| 149 |
'file' => 'taxonomy_filter.theme.inc',
|
| 150 |
),
|
| 151 |
'taxonomy_filter_admin_list_form_menus' => array(
|
| 152 |
'arguments' => array('form' => array()),
|
| 153 |
'file' => 'taxonomy_filter.theme.inc',
|
| 154 |
),
|
| 155 |
'taxonomy_filter_admin_mappings_table' => array(
|
| 156 |
'arguments' => array('form' => array()),
|
| 157 |
'file' => 'taxonomy_filter.theme.inc',
|
| 158 |
),
|
| 159 |
// Current criteria
|
| 160 |
'taxonomy_filter_block_current_content' => array(
|
| 161 |
'arguments' => array('terms' => NULL, 'block_info' => NULL),
|
| 162 |
'file' => 'taxonomy_filter.theme.inc',
|
| 163 |
),
|
| 164 |
'taxonomy_filter_current_item' => array(
|
| 165 |
'arguments' => array('item' => NULL, 'block_info' => NULL),
|
| 166 |
'file' => 'taxonomy_filter.theme.inc',
|
| 167 |
'template' => 'taxonomy-filter-item',
|
| 168 |
),
|
| 169 |
// Refine criteria
|
| 170 |
'taxonomy_filter_block_content' => array(
|
| 171 |
'arguments' => array('sections' => NULL, 'block_info' => NULL),
|
| 172 |
'file' => 'taxonomy_filter.theme.inc',
|
| 173 |
),
|
| 174 |
'taxonomy_filter_section' => array(
|
| 175 |
'arguments' => array('section' => NULL, 'block_info' => NULL),
|
| 176 |
'file' => 'taxonomy_filter.theme.inc',
|
| 177 |
'template' => 'taxonomy-filter-section',
|
| 178 |
),
|
| 179 |
'taxonomy_filter_item' => array(
|
| 180 |
'arguments' => array('item' => NULL, 'section_info' => NULL, 'block_info' => NULL),
|
| 181 |
'file' => 'taxonomy_filter.theme.inc',
|
| 182 |
'template' => 'taxonomy-filter-item',
|
| 183 |
),
|
| 184 |
'taxonomy_filter_link' => array(
|
| 185 |
'arguments' => array('name' => NULL, 'attributes' => NULL),
|
| 186 |
'file' => 'taxonomy_filter.theme.inc',
|
| 187 |
),
|
| 188 |
);
|
| 189 |
}
|
| 190 |
|
| 191 |
/**
|
| 192 |
* Implementation of hook_block().
|
| 193 |
*/
|
| 194 |
function taxonomy_filter_block($op = 'list', $delta = 0, $edit = array()) {
|
| 195 |
if ($op == 'list') {
|
| 196 |
$blocks = array();
|
| 197 |
$blocks[0] = array(
|
| 198 |
'info' => t('Taxonomy filter - refine criteria'),
|
| 199 |
// cache per page as menu is different for each url
|
| 200 |
// no point also caching per user or role - it would only vary if a node
|
| 201 |
// access module is installed and that bypasses block caching anyway.
|
| 202 |
// See: http://drupal.org/node/80951 and http://drupal.org/node/186636
|
| 203 |
// TODO: (later) implement manual per user or role caching
|
| 204 |
'cache' => BLOCK_CACHE_PER_PAGE,
|
| 205 |
);
|
| 206 |
$blocks[1] = array(
|
| 207 |
'info' => t('Taxonomy filter - current criteria'),
|
| 208 |
'cache' => BLOCK_CACHE_PER_PAGE,
|
| 209 |
'weight' => -20,
|
| 210 |
);
|
| 211 |
return $blocks;
|
| 212 |
}
|
| 213 |
elseif ($op == 'view') {
|
| 214 |
$block = array();
|
| 215 |
// Check url to see if we should display on this page.
|
| 216 |
$url_tokens = taxonomy_filter_get_url_tokens();
|
| 217 |
if ($url_tokens['tids'] && $url_tokens['op'] != 'feed') {
|
| 218 |
// TODO Depth returns as '3!feed' - parsing is incorrect - what happened in 5.x??? Same thing!
|
| 219 |
$depth = _taxonomy_filter_validate_depth($url_tokens['depth']);
|
| 220 |
$tids = _taxonomy_filter_validate_tids($url_tokens['tids']);
|
| 221 |
switch($delta) {
|
| 222 |
case 0:
|
| 223 |
// Refine search criteria block.
|
| 224 |
if (count($tids) > 0) {
|
| 225 |
$block_data = taxonomy_filter_block_refine($tids, $depth);
|
| 226 |
$block = array(
|
| 227 |
'subject' => filter_xss_admin($block_data['title']),
|
| 228 |
'content' => theme('taxonomy_filter_block_content', $block_data['sections'], $block_data['info']),
|
| 229 |
);
|
| 230 |
}
|
| 231 |
break;
|
| 232 |
|
| 233 |
case 1:
|
| 234 |
// Current search criteria block.
|
| 235 |
$block_data = taxonomy_filter_block_current_NEW($tids, $depth);
|
| 236 |
$block = array(
|
| 237 |
'subject' => filter_xss_admin($block_data['title']),
|
| 238 |
'content' => theme('taxonomy_filter_block_content', $block_data['sections'], $block_data['info']),
|
| 239 |
);
|
| 240 |
break;
|
| 241 |
}
|
| 242 |
return $block;
|
| 243 |
}
|
| 244 |
}
|
| 245 |
}
|
| 246 |
|
| 247 |
function taxonomy_filter_block_current($url_tids, $url_depth = NULL) {
|
| 248 |
// drupal_set_title('Search results'); // User-setting or based on url alias or content?
|
| 249 |
$terms = array();
|
| 250 |
foreach ($url_tids as $tid) {
|
| 251 |
$terms[] = taxonomy_get_term($tid);
|
| 252 |
}
|
| 253 |
foreach ($terms as $term) {
|
| 254 |
$term->vocab_name = taxonomy_vocabulary_load($term->vid)->name;
|
| 255 |
}
|
| 256 |
$block['title'] = t('Search results'); // TODO Use caption in settings.
|
| 257 |
$block['terms'] = $terms;
|
| 258 |
$block['info'] = array(
|
| 259 |
'url_tids' => $url_tids,
|
| 260 |
'url_depth' => $url_depth,
|
| 261 |
);
|
| 262 |
return $block;
|
| 263 |
}
|
| 264 |
|
| 265 |
/**
|
| 266 |
* Return array for current search results block.
|
| 267 |
*
|
| 268 |
* @param array $url_tids Term IDs.
|
| 269 |
* @param integer $url_depth Depth of taxonomy terms to display (NA).
|
| 270 |
* @return array Block content.
|
| 271 |
*/
|
| 272 |
function taxonomy_filter_block_current_NEW($url_tids, $url_depth = NULL) {
|
| 273 |
$terms = array();
|
| 274 |
foreach ($url_tids as $tid) {
|
| 275 |
$terms[] = taxonomy_get_term($tid);
|
| 276 |
}
|
| 277 |
$block['sections'] = array();
|
| 278 |
$block['info'] = array(
|
| 279 |
'url_tids' => $url_tids,
|
| 280 |
'url_depth' => $url_depth,
|
| 281 |
);
|
| 282 |
$sections_info = taxonomy_filter_sections_info_NEW($block['info']);
|
| 283 |
if (!$sections_info) {
|
| 284 |
return $block;
|
| 285 |
}
|
| 286 |
foreach ($sections_info as $section_info) {
|
| 287 |
$module = $section_info['module']; // TODO Implement other menu templates?
|
| 288 |
// TODO Context menu is not implemented for the current search block.
|
| 289 |
$section_info['module'] = $module == 'tf_context' ? 'taxonomy_filter' : $module;
|
| 290 |
$section_terms = array();
|
| 291 |
foreach ($terms as $term) {
|
| 292 |
if ($term->vid == $section_info['vid']) {
|
| 293 |
$section_terms[] = $term;
|
| 294 |
}
|
| 295 |
}
|
| 296 |
$items = array();
|
| 297 |
if (count($section_terms) > 0) {
|
| 298 |
$items = taxonomy_filter_section_items($section_terms, $section_info, $block['info']);
|
| 299 |
}
|
| 300 |
|
| 301 |
if (!count($items)) {
|
| 302 |
continue;
|
| 303 |
}
|
| 304 |
$section = array(
|
| 305 |
'title' => $section_info['section_title'],
|
| 306 |
'items' => $items,
|
| 307 |
'info' => $section_info,
|
| 308 |
);
|
| 309 |
drupal_alter('tf_section', $section, $block['info']);
|
| 310 |
$block['sections'][] = $section;
|
| 311 |
}
|
| 312 |
drupal_alter('tf_block', $block);
|
| 313 |
$settings = variable_get('taxonomy_filter_general', array());
|
| 314 |
$block['title'] = isset($settings['current_block_title']) ? t($settings['current_block_title']) : t(TAXONOMY_FILTER_CURRENT_BLOCK_TITLE); // JB NEW Override tf_multi
|
| 315 |
return $block;
|
| 316 |
}
|
| 317 |
|
| 318 |
function taxonomy_filter_sections_info_NEW($block_info) {
|
| 319 |
$all_mappings = variable_get('taxonomy_filter_mappings', array());
|
| 320 |
$menus = variable_get('taxonomy_filter_menus', array());
|
| 321 |
|
| 322 |
$sections = array();
|
| 323 |
$url_tids = $block_info['url_tids'];
|
| 324 |
$sql = "SELECT DISTINCT(vid) FROM {term_data} WHERE tid IN (". db_placeholders($url_tids) .")";
|
| 325 |
$result = db_query($sql, $url_tids);
|
| 326 |
while ($row = db_fetch_object($result)) {
|
| 327 |
$vid = $row->vid;
|
| 328 |
|
| 329 |
$menu_id = $all_mappings[$vid]['current_menu'] ? $all_mappings[$vid]['current_menu'] : $all_mappings[$vid]['refine_menu'];
|
| 330 |
$menu = $menus[$menu_id];
|
| 331 |
$section = array(
|
| 332 |
// TODO - this needs some refactoring / clean up
|
| 333 |
'section_title' => check_plain($all_mappings[$vid]['vocab']),
|
| 334 |
'vid' => $vid,
|
| 335 |
'menu_id' => $menu_id,
|
| 336 |
'module' => $menu['module'], // use $module_tf_section to get data
|
| 337 |
'section_settings' => $menu,
|
| 338 |
'class' => array('section', $menu['module']), // TODO - should we use template name instead of module name?
|
| 339 |
);
|
| 340 |
$sections[] = $section;
|
| 341 |
}
|
| 342 |
return $sections;
|
| 343 |
}
|
| 344 |
|
| 345 |
// Rename from taxonomy_filter_block_data
|
| 346 |
|
| 347 |
/**
|
| 348 |
* Block builder.
|
| 349 |
*
|
| 350 |
* @param array $url_tids Term ids in page URL.
|
| 351 |
* @param integer $url_depth Depth of terms to display.
|
| 352 |
* @return array Block content.
|
| 353 |
*/
|
| 354 |
function taxonomy_filter_block_refine($url_tids, $url_depth = NULL) {
|
| 355 |
// drupal_set_title('Search results');
|
| 356 |
|
| 357 |
$first_term = taxonomy_get_term($url_tids[0]);
|
| 358 |
// TODO Do we want to offer this option???
|
| 359 |
$block['title'] = t('Select topics tagged with \'@term\' and:', array('@term' => $first_term->name));
|
| 360 |
|
| 361 |
$settings = variable_get('taxonomy_filter_general', array());
|
| 362 |
$block['title'] = isset($settings['refine_block_title']) ? t($settings['refine_block_title']) : t(TAXONOMY_FILTER_REFINE_BLOCK_TITLE); // JB NEW Override tf_multi
|
| 363 |
|
| 364 |
$block['sections'] = array();
|
| 365 |
$block['info'] = array(
|
| 366 |
'url_tids' => $url_tids,
|
| 367 |
'url_depth' => $url_depth,
|
| 368 |
);
|
| 369 |
$sections_info = taxonomy_filter_sections_info($block['info']);
|
| 370 |
if (!$sections_info) {
|
| 371 |
return $block;
|
| 372 |
}
|
| 373 |
foreach ($sections_info as $section_info) {
|
| 374 |
$module = $section_info['module'];
|
| 375 |
$items = taxonomy_filter_invoke($module, 'tf_section', $section_info, $block['info']);
|
| 376 |
if (!count($items)) {
|
| 377 |
continue;
|
| 378 |
}
|
| 379 |
$section = array(
|
| 380 |
'title' => $section_info['section_title'],
|
| 381 |
'items' => $items,
|
| 382 |
'info' => $section_info,
|
| 383 |
);
|
| 384 |
drupal_alter('tf_section', $section, $block['info']);
|
| 385 |
$block['sections'][] = $section;
|
| 386 |
}
|
| 387 |
drupal_alter('tf_block', $block);
|
| 388 |
return $block;
|
| 389 |
}
|
| 390 |
|
| 391 |
/**
|
| 392 |
* Section builder (where section is a vocabulary).
|
| 393 |
*
|
| 394 |
* @param array $block_info Block information.
|
| 395 |
* @return array $sections
|
| 396 |
* Section information - title, menu id, module that renders this menu template, etc.
|
| 397 |
*/
|
| 398 |
function taxonomy_filter_sections_info($block_info) {
|
| 399 |
$sections = array();
|
| 400 |
$url_tids = $block_info['url_tids'];
|
| 401 |
$sql = "SELECT DISTINCT(vid) FROM {term_data} WHERE tid IN (". db_placeholders($url_tids) .")";
|
| 402 |
$result = db_query($sql, $url_tids);
|
| 403 |
while ($row = db_fetch_object($result)) {
|
| 404 |
$term_vids[] = $row->vid;
|
| 405 |
}
|
| 406 |
$all_mappings = variable_get('taxonomy_filter_mappings', array());
|
| 407 |
$menus = variable_get('taxonomy_filter_menus', array());
|
| 408 |
foreach ($all_mappings as $vid => $vid_mapping) {
|
| 409 |
$menu_id = $vid_mapping['refine_menu'];
|
| 410 |
// array_intersect is not strict enough; we need a subset relationship
|
| 411 |
if (!array_diff($term_vids, $vid_mapping['mappings']) && $menu_id != 0) {
|
| 412 |
$menu = $menus[$menu_id];
|
| 413 |
$section = array(
|
| 414 |
// TODO - this needs some refactoring / clean up
|
| 415 |
'section_title' => $vid_mapping['vocab'],
|
| 416 |
'vid' => $vid,
|
| 417 |
'menu_id' => $menu_id,
|
| 418 |
'module' => $menu['module'], // use $module_tf_section to get data
|
| 419 |
'section_settings' => $menu,
|
| 420 |
'class' => array('section', $menu['module']), // TODO - should we use template name instead of module name?
|
| 421 |
);
|
| 422 |
$sections[] = $section;
|
| 423 |
}
|
| 424 |
}
|
| 425 |
return $sections;
|
| 426 |
}
|
| 427 |
|
| 428 |
/**
|
| 429 |
* Return list of terms to display in this section.
|
| 430 |
*
|
| 431 |
* @param array $section_info Section information.
|
| 432 |
* @param array $block_info Block information.
|
| 433 |
* @return array Section items.
|
| 434 |
*/
|
| 435 |
function taxonomy_filter_tf_section($section_info, $block_info) {
|
| 436 |
// Should we use taxonomy_get_tree for these functions?
|
| 437 |
$context = NULL;
|
| 438 |
$terms = array();
|
| 439 |
$vid = $section_info['vid'];
|
| 440 |
$tids = $block_info['url_tids'];
|
| 441 |
// $menu_id = $section_info['menu_id']; // Not used
|
| 442 |
|
| 443 |
$select = "SELECT td.vid, td.tid, td.name, COUNT(DISTINCT(n.nid)) AS count" .
|
| 444 |
" FROM {term_data} td" .
|
| 445 |
" INNER JOIN {term_node} tn ON tn.tid = td.tid";
|
| 446 |
|
| 447 |
$joins = '';
|
| 448 |
// By replacing %d with multiple %d's we could run this query at one time (from another function above)
|
| 449 |
$wheres = ' WHERE td.vid IN (%d)';
|
| 450 |
foreach ($tids as $index => $tid) {
|
| 451 |
$joins .= ' INNER JOIN {term_node} tn' . $index . ' ON tn.nid = tn' . $index . '.nid';
|
| 452 |
$wheres .= ' AND tn' . $index . '.tid IN ('. $tid .')';
|
| 453 |
}
|
| 454 |
$joins .= " INNER JOIN {node} n ON tn.nid = n.nid";
|
| 455 |
$wheres .= ' AND n.status = 1 AND n.moderate = 0';
|
| 456 |
$group = ' GROUP BY td.vid, td.tid, td.name, td.weight';
|
| 457 |
$order = ' ORDER BY vid, weight, name';
|
| 458 |
|
| 459 |
$sql = $select . $joins . $wheres . $group . $order;
|
| 460 |
$sql = db_rewrite_sql($sql);
|
| 461 |
|
| 462 |
$result = db_query($sql, $vid);
|
| 463 |
while ($term = db_fetch_object($result)) {
|
| 464 |
$terms[] = $term;
|
| 465 |
}
|
| 466 |
// If only one term, then the section is not needed as all existing nodes have this one term value.
|
| 467 |
// Unset the section in the calling routine.
|
| 468 |
$items = array();
|
| 469 |
if (count($terms) > 0) {
|
| 470 |
$items = taxonomy_filter_section_items($terms, $section_info, $block_info, $context);
|
| 471 |
}
|
| 472 |
return $items;
|
| 473 |
}
|
| 474 |
|
| 475 |
/**
|
| 476 |
* Item builder.
|
| 477 |
*
|
| 478 |
* @param array $terms Term objects to produce items for.
|
| 479 |
* @param array $section_info Section information.
|
| 480 |
* @param array $block_info Block information.
|
| 481 |
* @param unknown_type $context
|
| 482 |
* @return array $items Item information.
|
| 483 |
*/
|
| 484 |
function taxonomy_filter_section_items($terms, $section_info, $block_info, $context = NULL) {
|
| 485 |
$items = array();
|
| 486 |
$module = $section_info['module'];
|
| 487 |
foreach ($terms as $term) {
|
| 488 |
$item_tid = $term->tid;
|
| 489 |
$link_tids = taxonomy_filter_get_link_tids($block_info['url_tids'], $item_tid, $context);
|
| 490 |
$item = array(
|
| 491 |
'title' => check_plain($term->name),
|
| 492 |
'item_attributes' => array(),
|
| 493 |
'link_attributes' => array(),
|
| 494 |
'info' => array(
|
| 495 |
'item_tid' => $item_tid,
|
| 496 |
'link_tids' => $link_tids,
|
| 497 |
),
|
| 498 |
);
|
| 499 |
if (isset($term->count)) {
|
| 500 |
$item['info']['tf_count'] = $term->count;
|
| 501 |
}
|
| 502 |
// if ($term->template_settings) {
|
| 503 |
// TODO - should this be template name not module name?
|
| 504 |
$item['info'][$module] = ''; // $term->template_settings;
|
| 505 |
// }
|
| 506 |
drupal_alter('tf_item', $item, $section_info, $block_info, $context);
|
| 507 |
// TODO Combine this with above alter after stabilizing module with tests.
|
| 508 |
drupal_alter('tf_item_2', $item, $section_info, $block_info, $term, $context);
|
| 509 |
$items[] = $item;
|
| 510 |
}
|
| 511 |
return $items;
|
| 512 |
}
|
| 513 |
|
| 514 |
function taxonomy_filter_get_link_tids($url_tids, $item_tid, $context = NULL) {
|
| 515 |
$link_tids = array();
|
| 516 |
if (!in_array($item_tid, $url_tids)) {
|
| 517 |
// only return link tids for unselected items
|
| 518 |
// $link_tids = array_merge($url_tids, array($item_tid)); // TEMP Code change made on 2009-04-28.
|
| 519 |
$link_tids = array($url_tids[0], $item_tid); // TEMP Revert to code prior to 2009-04-28.
|
| 520 |
}
|
| 521 |
drupal_alter('tf_link_tids', $link_tids, $item_tid, $context);
|
| 522 |
return $link_tids;
|
| 523 |
}
|
| 524 |
|
| 525 |
/**
|
| 526 |
* generates paths for items.
|
| 527 |
*/
|
| 528 |
function _taxonomy_filter_format_path($tids, $depth, $op = NULL) {
|
| 529 |
// if no tids return empty path (TODO do we need this check?)
|
| 530 |
if (!$tids) return '';
|
| 531 |
$template = variable_get('taxonomy_filter_output', TAXONOMY_FILTER_OUTPUT);
|
| 532 |
$tid_str = implode(',', $tids);
|
| 533 |
$depth_str = ($depth) ? $depth : '';
|
| 534 |
$op_str = ($op) ? $op : '';
|
| 535 |
|
| 536 |
$raw_url = str_replace('%tids', $tid_str, $template);
|
| 537 |
$raw_url = str_replace('%depth', $depth_str, $raw_url);
|
| 538 |
$raw_url = str_replace('%op', $op_str, $raw_url);
|
| 539 |
$raw_url = str_replace('//', '/', $raw_url);
|
| 540 |
$raw_url = trim($raw_url, '/');
|
| 541 |
|
| 542 |
$url = drupal_get_path_alias(str_replace('%2C', ',', url($raw_url)));
|
| 543 |
return $url;
|
| 544 |
}
|
| 545 |
|
| 546 |
/**
|
| 547 |
* Check the current url against the list of urls to listen on.
|
| 548 |
*
|
| 549 |
* @return array Matched or unmatched tokens.
|
| 550 |
*/
|
| 551 |
function taxonomy_filter_get_url_tokens() {
|
| 552 |
static $tokens;
|
| 553 |
|
| 554 |
if (!is_null($tokens)) {
|
| 555 |
return $tokens;
|
| 556 |
}
|
| 557 |
|
| 558 |
// TODO: make this extensible by other modules?
|
| 559 |
$filter_array = variable_get('taxonomy_filter_input_parsed', array());
|
| 560 |
$args = array();
|
| 561 |
$argnum = 0;
|
| 562 |
// create an array from arg(0), arg(1) etc
|
| 563 |
while ($arg = arg($argnum)) {
|
| 564 |
$args[] = $arg;
|
| 565 |
$argnum++;
|
| 566 |
}
|
| 567 |
foreach ($filter_array as $filter_line) {
|
| 568 |
$tids = NULL; // reset url variables
|
| 569 |
$depth = NULL;
|
| 570 |
$op = NULL;
|
| 571 |
$min = min(count($args), count($filter_line)); // TODO - this needs rethinking for checking against extra params
|
| 572 |
for ($i = 0; $i < $min; $i++) {
|
| 573 |
$filter_bit = $filter_line[$i];
|
| 574 |
$filter_bit_prefix = (strpos('/*%!', $filter_bit{0})) ? $filter_bit{0} : '' ; // uses a dummy '/' as pos 0
|
| 575 |
if ($filter_bit_prefix == '') {
|
| 576 |
if ($filter_bit != $args[$i]) {
|
| 577 |
break;
|
| 578 |
}
|
| 579 |
}
|
| 580 |
elseif ($filter_bit_prefix == '!') {
|
| 581 |
if ($filter_bit == '!'. $args[$i]) {
|
| 582 |
break;
|
| 583 |
}
|
| 584 |
}
|
| 585 |
if ($filter_bit == '%tids') {
|
| 586 |
$tids = $args[$i];
|
| 587 |
}
|
| 588 |
elseif ($filter_bit == '%depth') {
|
| 589 |
$depth = $args[$i];
|
| 590 |
}
|
| 591 |
elseif ($filter_bit == '%op') {
|
| 592 |
$op = $args[$i];
|
| 593 |
}
|
| 594 |
}
|
| 595 |
if ($tids) {
|
| 596 |
$tokens = array('tids' => $tids, 'depth' => $depth, 'op' => $op);
|
| 597 |
return $tokens;
|
| 598 |
}
|
| 599 |
}
|
| 600 |
$tokens = array('tids' => NULL, 'depth' => NULL, 'op' => NULL);
|
| 601 |
return $tokens;
|
| 602 |
}
|
| 603 |
|
| 604 |
/**
|
| 605 |
* Validate and convert the term ids in the url to an array.
|
| 606 |
*/
|
| 607 |
function _taxonomy_filter_validate_tids($raw_tids) {
|
| 608 |
$tids = array();
|
| 609 |
foreach (preg_split('/[+ ,]/', $raw_tids) as $tid) {
|
| 610 |
if (is_numeric($tid) && (int) $tid > 0) {
|
| 611 |
$tids[] = (int) $tid;
|
| 612 |
}
|
| 613 |
}
|
| 614 |
return $tids;
|
| 615 |
}
|
| 616 |
|
| 617 |
/**
|
| 618 |
* Validate the depth parameter in the current path.
|
| 619 |
*/
|
| 620 |
function _taxonomy_filter_validate_depth($raw_depth) {
|
| 621 |
if (is_numeric($raw_depth)) {
|
| 622 |
$depth = ($raw_depth > 0) ? min(9, (int) $raw_depth): 0;
|
| 623 |
}
|
| 624 |
else {
|
| 625 |
$depth = ($raw_depth == 'all') ? 'all' : NULL;
|
| 626 |
}
|
| 627 |
return $depth;
|
| 628 |
}
|
| 629 |
|
| 630 |
function _taxonomy_filter_get_vocabs() {
|
| 631 |
// TODO: use static array for performance?
|
| 632 |
// TODO: supply vocab weight for further calcs?
|
| 633 |
// TODO: use taxonomy_get_vocabularies?
|
| 634 |
$vocabs = array();
|
| 635 |
$result = db_query('SELECT v.vid, v.name FROM {vocabulary} v ORDER BY v.weight, v.name');
|
| 636 |
while ($voc = db_fetch_object($result)) {
|
| 637 |
$vocabs[$voc->vid] = $voc->name;
|
| 638 |
}
|
| 639 |
return $vocabs;
|
| 640 |
}
|
| 641 |
|
| 642 |
/**
|
| 643 |
* Update mappings variable for vocabulary changes.
|
| 644 |
*/
|
| 645 |
function taxonomy_filter_update_mappings() {
|
| 646 |
$vocabs = _taxonomy_filter_get_vocabs();
|
| 647 |
$old_mappings = variable_get('taxonomy_filter_mappings', array());
|
| 648 |
|
| 649 |
// Check for any deleted vocabularies.
|
| 650 |
$vids = array_keys($vocabs);
|
| 651 |
foreach ($old_mappings as $vid => &$old_mapping) {
|
| 652 |
if (!array_key_exists($vid, $vocabs)) {
|
| 653 |
// Delete the mapping variable entry for the deleted vocabulary.
|
| 654 |
unset($old_mappings[$vid]);
|
| 655 |
}
|
| 656 |
elseif ($deleted_vids = array_diff($old_mapping['mappings'], $vids)) {
|
| 657 |
// Delete any mappings pointing to the deleted vocabulary.
|
| 658 |
foreach ($deleted_vids as $index => $deleted_vid) {
|
| 659 |
$key = array_search($deleted_vid, $old_mapping['mappings']);
|
| 660 |
unset($old_mapping['mappings'][$key]);
|
| 661 |
}
|
| 662 |
}
|
| 663 |
}
|
| 664 |
|
| 665 |
// Recreate the entire variable to synchronize the filter menu "display"
|
| 666 |
// order to a vocabulary weight change.
|
| 667 |
$new_mappings = array();
|
| 668 |
foreach ($vocabs as $vid => $name) {
|
| 669 |
if (array_key_exists($vid, $old_mappings)) {
|
| 670 |
// For existing mappings:
|
| 671 |
// - store the current vocabulary name (it may have changed).
|
| 672 |
// - create a mappings item if not present.
|
| 673 |
$new_mappings[$vid] = array(
|
| 674 |
'vocab' => $name,
|
| 675 |
'refine_menu' => $old_mappings[$vid]['refine_menu'],
|
| 676 |
'current_menu' => $old_mappings[$vid]['current_menu'],
|
| 677 |
'mappings' => isset($old_mappings[$vid]['mappings']) ? $old_mappings[$vid]['mappings'] : array(),
|
| 678 |
);
|
| 679 |
}
|
| 680 |
else {
|
| 681 |
// For missing mappings, add default values.
|
| 682 |
$new_mappings[$vid] = array(
|
| 683 |
'vocab' => $name,
|
| 684 |
'refine_menu' => 0,
|
| 685 |
'current_menu' => 0,
|
| 686 |
'mappings' => array(),
|
| 687 |
);
|
| 688 |
}
|
| 689 |
}
|
| 690 |
|
| 691 |
// Save the mappings.
|
| 692 |
variable_set('taxonomy_filter_mappings', $new_mappings);
|
| 693 |
}
|
| 694 |
|
| 695 |
/**
|
| 696 |
* Breaks the input filter patterns into component pieces.
|
| 697 |
*/
|
| 698 |
function taxonomy_filter_parse_input_filters($filters) {
|
| 699 |
$input_bits = array();
|
| 700 |
$lines = preg_split('/[\n\r]+/', trim($filters));
|
| 701 |
foreach ($lines as $line) {
|
| 702 |
$temp = array();
|
| 703 |
$bits = explode('/', trim($line, '/'));
|
| 704 |
foreach ($bits as $bit) {
|
| 705 |
$temp[] = $bit;
|
| 706 |
}
|
| 707 |
$input_bits[] = $temp;
|
| 708 |
}
|
| 709 |
variable_set('taxonomy_filter_input_parsed', $input_bits);
|
| 710 |
}
|
| 711 |
|
| 712 |
function taxonomy_filter_invoke() {
|
| 713 |
// Just like module_invoke() but falls back on base menu template implementation.
|
| 714 |
$args = func_get_args();
|
| 715 |
$module = $args[0];
|
| 716 |
$hook = $args[1];
|
| 717 |
unset($args[0], $args[1]);
|
| 718 |
$function = $module .'_'. $hook;
|
| 719 |
if (module_hook($module, $hook)) {
|
| 720 |
return call_user_func_array($function, $args);
|
| 721 |
}
|
| 722 |
elseif (module_hook('taxonomy_filter', $hook)) {
|
| 723 |
return call_user_func_array('taxonomy_filter_'. $hook, $args);
|
| 724 |
}
|
| 725 |
}
|
| 726 |
|
| 727 |
/**
|
| 728 |
* Get all tids in the url up to and including the current tid based on the
|
| 729 |
* order selected by the user.
|
| 730 |
*
|
| 731 |
* @param array $tids
|
| 732 |
* @param integer $this_tid
|
| 733 |
* @return array
|
| 734 |
*/
|
| 735 |
function _taxonomy_filter_tids_upto($tids, $this_tid) {
|
| 736 |
$length = 1;
|
| 737 |
foreach ($tids as $tid) {
|
| 738 |
if ($tid == $this_tid) {
|
| 739 |
break;
|
| 740 |
}
|
| 741 |
$length++;
|
| 742 |
}
|
| 743 |
/*
|
| 744 |
* On the last url tid omit a link to select all tids up to this tid as this
|
| 745 |
* content is already being displayed.
|
| 746 |
*/
|
| 747 |
if ($length == count($tids)) {
|
| 748 |
return null;
|
| 749 |
}
|
| 750 |
return array_slice($tids, 0, $length);
|
| 751 |
}
|
| 752 |
|
| 753 |
/**
|
| 754 |
* Return variables table entries for the 'taxonomy_filter_menus' key.
|
| 755 |
*
|
| 756 |
* TODO Is this useful?
|
| 757 |
*
|
| 758 |
* @param integer $menu_id First level index.
|
| 759 |
* @param string $key Second level index.
|
| 760 |
* @return array Values.
|
| 761 |
*/
|
| 762 |
function taxonomy_filter_menu_settings($menu_id, $key) {
|
| 763 |
$menus = variable_get('taxonomy_filter_menus', array());
|
| 764 |
$settings = array();
|
| 765 |
if (isset($menus[$menu_id]) && isset($menus[$menu_id][$key])) {
|
| 766 |
$settings = $menus[$menu_id][$key];
|
| 767 |
}
|
| 768 |
return $settings;
|
| 769 |
}
|
| 770 |
|
| 771 |
/**
|
| 772 |
* Process variables for page.tpl.php.
|
| 773 |
*
|
| 774 |
* This preprocess function works because the page hook is in a template.
|
| 775 |
* If the page hook was in a theme function then this would not be possible.
|
| 776 |
*/
|
| 777 |
function taxonomy_filter_preprocess_page(&$variables) {
|
| 778 |
// Check url to see if we should display on this page.
|
| 779 |
$url_tokens = taxonomy_filter_get_url_tokens();
|
| 780 |
if ($url_tokens['tids'] && $url_tokens['op'] != 'feed') {
|
| 781 |
$settings = variable_get('taxonomy_filter_general', array());
|
| 782 |
if (isset($settings['display_current_atop_content']) && $settings['display_current_atop_content']) {
|
| 783 |
$variables['title'] = isset($settings['current_block_title']) ? t($settings['current_block_title']) : t(TAXONOMY_FILTER_CURRENT_BLOCK_TITLE);
|
| 784 |
$depth = _taxonomy_filter_validate_depth($url_tokens['depth']);
|
| 785 |
$tids = _taxonomy_filter_validate_tids($url_tokens['tids']);
|
| 786 |
$block_data = taxonomy_filter_block_current($tids, $depth);
|
| 787 |
|
| 788 |
$new = '';
|
| 789 |
$new .= '<div class="clear-block block block-taxonomy_filter" id="block-taxonomy_filter-2">';
|
| 790 |
$new .= '<div class="content">';
|
| 791 |
$new .= '<ul>';
|
| 792 |
$new .= theme('taxonomy_filter_block_current_content', $block_data['terms'], $block_data['info']);
|
| 793 |
$new .= '</ul>';
|
| 794 |
$new .= '</div>';
|
| 795 |
$new .= '</div>';
|
| 796 |
$variables['content'] = $new . $variables['content'];
|
| 797 |
}
|
| 798 |
}
|
| 799 |
}
|