You are here

hs_taxonomy_views.module in Hierarchical Select 5.3

Same filename and directory in other branches
  1. 6.3 modules/hs_taxonomy_views.module

Implementation of the Hierarchical Select API for the Taxonomy module's Views exposed filters.

File

modules/hs_taxonomy_views.module
View source
<?php

/**
 * @file
 * Implementation of the Hierarchical Select API for the Taxonomy module's
 * Views exposed filters.
 */

//----------------------------------------------------------------------------

// Core hooks.

/**
 * Implementation of hook_menu().
 */
function hs_taxonomy_views_menu($may_cache) {
  $items = array();
  if (!$may_cache && arg(0) == 'admin' && arg(1) == 'build' && arg(2) == 'views' && is_string(arg(3)) && arg(4) == 'hs_config' && is_numeric(arg(5))) {
    $view_name = arg(3);
    $vid = arg(5);

    // Vocabulary ID, not View ID!
    $items[] = array(
      'path' => "admin/build/views/{$view_name}/hs_config/{$vid}",
      'title' => t('Hierarchical Select configuration for !view', array(
        '!view' => $view_name,
      )),
      'callback' => 'drupal_get_form',
      'callback arguments' => array(
        'hs_taxonomy_views_config_form',
        $view_name,
        $vid,
      ),
      'access' => user_access('administer views'),
      'type' => MENU_NORMAL_ITEM,
    );
  }
  return $items;
}

/**
 * Implementation of hook_form_alter().
 */
function hs_taxonomy_views_form_alter($form_id, &$form) {

  // Change the exposed filters of Views. Only affects hierarchical vocabulary
  // filters.
  if (in_array($form_id, array(
    'views_filters',
    'views_filterblock',
  ))) {
    $hs_exposed_filters_found = 0;

    // Find the ids and vocabulary ids of the exposed filters.
    foreach ($form['view']['#value']->exposed_filter as $id => $filter) {
      if (preg_match("/term_node_(\\d+)\\.tid/", $filter['field'], $matches)) {
        $vid = $matches[1];

        // Only apply Hierarchical Select if it's enabled for this vocabulary.
        if (variable_get("taxonomy_hierarchical_select_{$vid}", 0)) {
          $hs_exposed_filters_found++;
          $vocabulary = taxonomy_get_vocabulary($vid);
          $view = $form['view']['#value'];

          // Make it use a hierarchical select.
          require_once drupal_get_path('module', 'hierarchical_select') . '/includes/common.inc';
          unset($form["filter{$id}"]['#options']);
          unset($form["filter{$id}"]['#theme']);
          unset($form["filter{$id}"]['#size']);
          $form["filter{$id}"]['#type'] = 'hierarchical_select';
          $defaults_override = array(
            'module' => 'hs_taxonomy_views',
            'params' => array(
              'optional' => (bool) $view->exposed_filter[$id]['optional'],
              'vid' => $vid,
              'exclude_tid' => NULL,
              'root_term' => NULL,
            ),
            // When the **ALL** option is selected, nothing else should be.
            'exclusive_lineages' => array(
              '**ALL**',
            ),
            // This is a GET form, so also render the flat select.
            'render_flat_select' => 1,
          );
          hierarchical_select_common_config_apply($form["filter{$id}"], "taxonomy-views-{$view->name}-{$vid}", $defaults_override);

          // Inherit #required from the exposed filter settings.
          $form["filter{$id}"]['#required'] = !(bool) $view->exposed_filter[$id]['optional'];

          // Put the altered exposed filters in a separate table row.
          hierarchical_select_common_views_exposed_filters_reposition();
        }
      }
    }
    if ($hs_exposed_filters_found > 0) {

      // Views will remove the form_id in views_filters_process(), but we need
      // it for Hierarchical Select to work, so put it back.
      $form['copy_of_form_id'] = $form['form_id'] + array(
        '#parents' => array(
          'form_id',
        ),
      );
    }
  }

  // Alter the edit view form: add a link to the Hierarchical Select
  // configuration when appropriate and to mark which settings are now managed
  // by the Hierarchical Select configuration.
  if ($form_id == 'views_edit_view') {
    foreach ($form['exposed_filter'] as $filter_id => $filter) {
      if (is_numeric($filter_id)) {
        $id = $form['exposed_filter'][$filter_id]['id']['#default_value'];
        if (preg_match("/term_node_(\\d+)\\.tid/", $id, $matches)) {
          $vid = $matches[1];
          if (variable_get("taxonomy_hierarchical_select_{$vid}", 0)) {
            $view = $form['#parameters'][1];
            $link = l(t('Configure Hierarchical Select'), "admin/build/views/{$view->name}/hs_config/{$vid}");
            $form['exposed_filter'][$filter_id]['name']['#value'] .= '<br />' . $link;

            // Alter the form to support the current Hierarchical Select
            // config.
            require_once drupal_get_path('module', 'hierarchical_select') . '/includes/common.inc';
            $config_id = "taxonomy-views-{$view->name}-{$vid}";
            $config = hierarchical_select_common_config_get($config_id);
            $text = t('This setting is now managed by the<br />Hierarchical Select configuration!');

            // Exposed filter's "Force single" setting.
            $form['exposed_filter'][$filter_id]['single']['#description'] = $text;

            // Additional settings when save_lineage is enabled.
            if ($config['save_lineage']) {

              // Filter's "Operator" setting.
              $form['filter'][$filter_id]['operator']['#description'] = $text;

              // Exposed filter's "Lock Operator" setting.
              $form['exposed_filter'][$filter_id]['operator']['#description'] = $text;
            }
          }
        }
      }
    }
  }
}

/**
 * Implementation of hook_requirements().
 */
function hs_taxonomy_views_requirements($phase) {
  $requirements = array();
  if ($phase == 'runtime') {
    $pattern = <<<EOT
function _views_build_filters_form(\$view) {
  // When the form is retrieved through an AJAX callback, the cache hasn't
  // been loaded yet. The cache is necesssary for _views_get_filters().
  views_load_cache();
EOT;
    $views_with_patch_257004 = preg_match('#' . preg_quote($pattern) . '#m', file_get_contents(drupal_get_path('module', 'views') . '/views.module'));
    if ($views_with_patch_257004) {
      $value = t('The Views module is new enough.');
      $description = '';
      $severity = REQUIREMENT_OK;
    }
    else {
      $value = t('The Views module is outdated.');
      $description = t("The version of Views that you have installed is either\n        older than May 11, 2008, or doesn't have the obligatory patch applied.\n        Please apply the <a href=\"!patch_url\">patch</a> or update to a newer\n        version of the Views module!", array(
        '!patch_url' => 'http://drupal.org/files/issues/hs_compatibility.patch',
      ));
      $severity = REQUIREMENT_ERROR;
    }
    $requirements['hs_taxonomy_views'] = array(
      'title' => t('Hierarchical Select Views Taxonomy'),
      'value' => $value,
      'description' => $description,
      'severity' => $severity,
    );
  }
  return $requirements;
}

//----------------------------------------------------------------------------

// Forms API callbacks.

/**
 * Form definition; configuration form for Hierarchical Select as the widget
 * for a Taxonomy exposed filter.
 *
 * @param $view_name
 *   Name of a view. Provides necessary context.
 * @param $vid
 *   A vocabulary id. Provides necessary context.
 */
function hs_taxonomy_views_config_form($view_name, $vid) {
  require_once drupal_get_path('module', 'hierarchical_select') . '/includes/common.inc';

  // Find the exposed filter, we need this to set the default value of
  // $config['dropbox']['status'].
  $view = views_get_view($view_name);
  foreach ($view->exposed_filter as $filter) {
    if ($filter['id'] == "term_node_{$vid}.tid") {
      $exposed_filter = $filter;
      break;
    }
  }

  // Add the Hierarchical Select config form.
  $module = 'hs_taxonomy_views';
  $params = array(
    'optional' => (bool) $view->exposed_filter[$id]['optional'],
    'vid' => $vid,
    'exclude_tid' => NULL,
    'root_term' => NULL,
  );
  $config_id = "taxonomy-views-{$view_name}-{$vid}";
  $vocabulary = taxonomy_get_vocabulary($vid);
  $defaults = array(
    // Enable the save_lineage setting by default if the multiple parents
    // vocabulary option is enabled.
    'save_lineage' => (int) ($vocabulary->hierarchy == 2),
    'dropbox' => array(
      'status' => !$exposed_filter['single'],
    ),
    'editability' => array(
      'max_levels' => _hs_taxonomy_hierarchical_select_get_depth($vid),
    ),
  );
  $strings = array(
    'hierarchy' => t('vocabulary'),
    'hierarchies' => t('vocabularies'),
    'item' => t('term'),
    'items' => t('terms'),
    'item_type' => t('term type'),
    'entity' => t('node'),
    'entities' => t('nodes'),
  );
  $max_hierarchy_depth = _hs_taxonomy_hierarchical_select_get_depth($vid);
  $preview_is_required = !(bool) $exposed_filter['optional'];
  $form['hierarchical_select_config'] = hierarchical_select_common_config_form($module, $params, $config_id, $defaults, $strings, $max_hierarchy_depth, $preview_is_required);
  $form['hierarchical_select_config']['save_lineage']['#description'] .= '<br />' . t('When you enable the %save_lineage setting, you will have to resave the
    "Edit view" form as well!', array(
    '%save_lineage' => $form['hierarchical_select_config']['save_lineage']['#options'][1],
  ));
  $form['link'] = array(
    '#value' => l('Back to the view configuration', "admin/build/views/{$view_name}/edit"),
    '#prefix' => '<div class="hierarchical-select-config-back-link">',
    '#suffix' => '</div>',
    '#weight' => -5,
  );
  $form['save'] = array(
    '#type' => 'submit',
    '#value' => t('Save'),
  );

  // Add the the submit handler for the Hierarchical Select config form.
  $parents = array(
    'hierarchical_select_config',
  );
  $form['#submit']['hierarchical_select_common_config_form_submit'] = array(
    $parents,
  );
  $form['#submit']['hs_taxonomy_views_common_config_form_submit'] = array(
    $view_name,
    $vid,
  );
  return $form;
}

/**
 * Additional submit callback to redirect the user to the "Edit view" form.
 *
 * @param $form_id
 * @param $form_values
 * @param $view_name
 *   Name of a view. Provides necessary context.
 * @param $vid
 *   A vocabulary id. Provides necessary context.
 */
function hs_taxonomy_views_common_config_form_submit($form_id, $form_values, $view_name, $vid) {
  $view_id = db_result(db_query("SELECT vid FROM {view_view} WHERE name = '%s'", $view_name));
  $field = 'term_node_' . $vid . '.tid';
  $filter = db_fetch_object(db_query("SELECT operator FROM {view_filter} WHERE vid = %d AND field = '%s'", $view_id, $field));
  $exposed_filter = db_fetch_object(db_query("SELECT operator, single FROM {view_exposed_filter} WHERE vid = %d AND field = '%s'", $view_id, $field));

  // Overrides when save_lineage is enabled.
  if ($form_values['hierarchical_select_config']['save_lineage']) {

    // "Operator" must always be 'AND'.
    $filter->operator = 'AND';

    // The exposed filter must be locked to 'AND'.
    $exposed_filter->operator = 1;

    // "Force single" must be disabled.
    $exposed_filter->single = 0;
  }
  else {

    // "Force single" must be enabled.
    $exposed_filter->single = 1;
  }

  // Overrides when the dropbox is enabled.
  if ($form_values['hierarchical_select_config']['dropbox']['status']) {

    // "Force single" must be disabled.
    $exposed_filter->single = 0;
  }
  if ($view_id === FALSE) {
    drupal_set_message(t("Could not update the view because it doesn't live in the database."), 'error');
  }
  else {
    db_query("UPDATE {view_filter} SET operator = '%s' WHERE vid = %d AND field = '%s'", $filter->operator, $view_id, $field);
    db_query("UPDATE {view_exposed_filter} SET operator = %d, single = %d WHERE vid = %d AND field = '%s'", $exposed_filter->operator, $exposed_filter->single, $view_id, $field);
    cache_clear_all('views_urls', 'cache_views');
    drupal_set_message(t("Updated the View's exposed filter according to the settings you made."));
  }
}

//----------------------------------------------------------------------------

// Hierarchical Select hooks.

/**
 * Implementation of hook_hierarchical_select_params().
 */
function hs_taxonomy_views_hierarchical_select_params() {
  return array(
    'optional',
  ) + hs_taxonomy_hierarchical_select_params();
}

/**
 * Implementation of hook_hierarchical_select_root_level().
 */
function hs_taxonomy_views_hierarchical_select_root_level($params) {
  $root_level = $params['optional'] ? array(
    '**ALL**' => '<' . t('all') . '>',
  ) : array();
  $root_level += hs_taxonomy_hierarchical_select_root_level($params);
  return $root_level;
}

/**
 * Implementation of hook_hierarchical_select_children().
 */
function hs_taxonomy_views_hierarchical_select_children($parent, $params) {
  return $parent == '**ALL**' ? array() : hs_taxonomy_hierarchical_select_children($parent, $params);
}

/**
 * Implementation of hook_hierarchical_select_lineage().
 */
function hs_taxonomy_views_hierarchical_select_lineage($item, $params) {
  return $item == '**ALL**' ? array(
    $item,
  ) : hs_taxonomy_hierarchical_select_lineage($item, $params);
}

/**
 * Implementation of hook_hierarchical_select_valid_item().
 */
function hs_taxonomy_views_hierarchical_select_valid_item($item, $params) {
  return $item == '**ALL**' || hs_taxonomy_hierarchical_select_valid_item($item, $params);
}

/**
 * Implementation of hook_hierarchical_select_item_get_label().
 */
function hs_taxonomy_views_hierarchical_select_item_get_label($item, $params) {
  return $item == '**ALL**' ? '<' . t('all') . '>' : hs_taxonomy_hierarchical_select_item_get_label($item, $params);
}

/**
 * Implementation of hook_hierarchical_select_create_item().
 */

// No implementation. This doesn't make sense for exposed filters: if you were
// able to create new items in the hierarchy, how could you then possibly find
// anything for that item?

/**
 * Implementation of hook_hierarchical_select_entity_count().
 */
function hs_taxonomy_views_hierarchical_select_entity_count($item, $params) {
  if ($item == '**ALL**') {
    return db_result(db_query('SELECT COUNT(DISTINCT(t.nid)) FROM {term_node} t INNER JOIN {node} n ON t.nid = n.nid WHERE n.status = 1'));
  }
  else {
    return hs_taxonomy_hierarchical_select_entity_count($item, $params);
  }
}

/**
 * Implementation of hook_hierarchical_select_implementation_info().
 */
function hs_taxonomy_views_hierarchical_select_implementation_info() {
  return array(
    'hierarchy type' => t('Taxonomy'),
    'entity type' => t('Node'),
  );
}

/**
 * Implementation of hook_hierarchical_select_config_info().
 */
function hs_taxonomy_views_hierarchical_select_config_info() {
  static $config_info;
  if (!isset($config_info)) {
    $config_info = array();
    views_load_cache();
    $result = db_query("SELECT vid, name FROM {view_view} ORDER BY name");
    while ($view = db_fetch_object($result)) {
      $view = views_get_view($view->name);
      foreach ($view->exposed_filter as $filter_id => $filter) {
        $vid = _hs_taxonomy_views_parse_vocabulary_id_from_id($filter['id']);
        if ($vid) {
          $vocabulary = taxonomy_get_vocabulary($vid);
          $config_id = "taxonomy-views-{$view->name}-{$vid}";
          $config_info[$config_id] = array(
            'config_id' => $config_id,
            'hierarchy type' => t('Taxonomy'),
            'hierarchy' => t($vocabulary->name),
            'entity type' => t('Node'),
            'entity' => '',
            'context type' => t('Views exposed filter'),
            'context' => t($view->name),
            'edit link' => "admin/build/views/{$view->name}/hs_config/{$vid}",
          );
        }
      }
    }
  }
  return $config_info;
}

/**
 * Given an id of the form "term_node_1.tid", with 1 the vid, get the vid.
 *
 * @return
 *   When no valid id was given, FALSE, otherwise the vid.
 */
function _hs_taxonomy_views_parse_vocabulary_id_from_id($id) {
  $vid = FALSE;
  if (preg_match("/term_node_(\\d+)\\.tid/", $id, $matches)) {
    $vid = $matches[1];
  }
  return $vid;
}

Functions

Namesort descending Description
hs_taxonomy_views_common_config_form_submit Additional submit callback to redirect the user to the "Edit view" form.
hs_taxonomy_views_config_form Form definition; configuration form for Hierarchical Select as the widget for a Taxonomy exposed filter.
hs_taxonomy_views_form_alter Implementation of hook_form_alter().
hs_taxonomy_views_hierarchical_select_children Implementation of hook_hierarchical_select_children().
hs_taxonomy_views_hierarchical_select_config_info Implementation of hook_hierarchical_select_config_info().
hs_taxonomy_views_hierarchical_select_entity_count Implementation of hook_hierarchical_select_entity_count().
hs_taxonomy_views_hierarchical_select_implementation_info Implementation of hook_hierarchical_select_implementation_info().
hs_taxonomy_views_hierarchical_select_item_get_label Implementation of hook_hierarchical_select_item_get_label().
hs_taxonomy_views_hierarchical_select_lineage Implementation of hook_hierarchical_select_lineage().
hs_taxonomy_views_hierarchical_select_params Implementation of hook_hierarchical_select_params().
hs_taxonomy_views_hierarchical_select_root_level Implementation of hook_hierarchical_select_root_level().
hs_taxonomy_views_hierarchical_select_valid_item Implementation of hook_hierarchical_select_valid_item().
hs_taxonomy_views_menu Implementation of hook_menu().
hs_taxonomy_views_requirements Implementation of hook_requirements().
_hs_taxonomy_views_parse_vocabulary_id_from_id Given an id of the form "term_node_1.tid", with 1 the vid, get the vid.