You are here

taxonomy_facets.module in Taxonomy Facets 7.3

Same filename and directory in other branches
  1. 8 taxonomy_facets.module
  2. 7.2 taxonomy_facets.module

File

taxonomy_facets.module
View source
<?php

/**
 * @file
 * Taxo Faceted Navigation module code.
 *
 * Provides block for progressively filtering content. There is no limit on
 * number of blocks it can provide.
 */
include_once 'classes/SelectedFilters.php';
include_once 'classes/MenuTree.php';

/**
 * Implements hook_menu().
 */
function taxonomy_facets_menu() {
  $items = array();

  // First argumet in the listing url, as specified in the admin form.
  $first_arg = variable_get('taxonomy_facets_first_argument', 'items_list');

  // The node listing page, where filters can be allied.
  $items[$first_arg] = array(
    'page arguments' => array(
      1,
    ),
    'page callback' => 'taxonomy_facets_print_landing_page',
    'type' => MENU_CALLBACK,
    'access arguments' => array(
      'view taxo facets',
    ),
  );

  // Admin settings page.
  $items['admin/config/search/tax_faceted_nav'] = array(
    'title' => 'Taxo Faceted Navigation',
    'description' => 'Taxo Faceted Navigation module configuration',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'taxonomy_facets_admin_settings',
    ),
    'access arguments' => array(
      'administer site configuration',
    ),
    'file' => 'taxonomy_facets.inc',
    'type' => MENU_NORMAL_ITEM,
  );

  // Add taxo faceted block page.
  $items['admin/structure/block/add-taxofacet-block'] = array(
    'title' => 'Add taxofacet block',
    'description' => 'Add a new taxo faceted block.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'taxonomy_facets_add_block_form',
    ),
    'access arguments' => array(
      'administer blocks',
    ),
    'type' => MENU_LOCAL_ACTION,
    'file' => 'taxonomy_facets.inc',
  );
  return $items;
}

/**
 * Menu callback: display the taxo faceted block addition form.
 *
 * @see taxonomy_facets_add_block_form_submit()
 */
function taxonomy_facets_add_block_form($form, &$form_state) {
  module_load_include('inc', 'block', 'block.admin');
  $form = block_admin_configure($form, $form_state, 'taxonomy_facets', NULL);

  // Other modules should be able to use hook_form_block_add_block_form_alter()
  // to modify this form, so add a base form ID.
  $form_state['build_info']['base_form_id'] = 'block_add_block_form';

  // Prevent block_add_block_form_validate/submit() from being automatically
  // added because of the base form ID by providing these handlers manually.
  $form['#validate'] = array();
  $form['#submit'] = array(
    'taxonomy_facets_add_block_form_submit',
  );
  return $form;
}

/**
 * Implements hook_block_configure().
 */
function taxonomy_facets_block_configure($delta = '') {
  $vid = variable_get("taxonomy_facets_{$delta}_tid", 1);
  $form_state['vid'] = $vid;
  return taxonomy_facets_configure_form(array(), $form_state);
}

/**
 * Returns the configuration form.
 *
 * @param array $form_state
 *   array An associated array of configuration options should be present in the
 *   'values' key. If none are given, default configuration is assumed.
 *
 * @return array
 *   The form in Form API format.
 */
function taxonomy_facets_configure_form($form, array &$form_state) {
  $taxonomies = taxonomy_get_vocabularies();
  $vocabs = array();
  foreach ($taxonomies as $taxonomy) {
    $vocabs[$taxonomy->vid] = $taxonomy->name;
  }
  $form['vid'] = array(
    '#type' => 'select',
    '#title' => t('Taxonomy'),
    '#options' => $vocabs,
    '#default_value' => $form_state['vid'],
    '#description' => t('Select Vocabulary.'),
  );
  return $form;
}

/**
 * Save the new taxo faceted block.
 */
function taxonomy_facets_add_block_form_submit($form, array &$form_state) {

  // Determine the delta of the new block.
  $block_ids = variable_get('taxo_faceted_block_ids', array());
  $delta = empty($block_ids) ? 1 : max($block_ids) + 1;
  $form_state['values']['delta'] = $delta;

  // Add new delta to array of exsisting deltas.
  $block_ids[] = $delta;

  // Save the new array of blocks IDs.
  variable_set('taxo_faceted_block_ids', $block_ids);

  // Save the block configuration.
  taxonomy_facets_block_save($delta, $form_state['values']);

  // Run the normal new block submission.
  $query = db_insert('block')
    ->fields(array(
    'visibility',
    'pages',
    'custom',
    'title',
    'module',
    'theme',
    'region',
    'status',
    'weight',
    'delta',
    'cache',
  ));
  foreach (list_themes() as $key => $theme) {
    if ($theme->status) {
      $region = !empty($form_state['values']['regions'][$theme->name]) ? $form_state['values']['regions'][$theme->name] : BLOCK_REGION_NONE;
      $query
        ->values(array(
        'visibility' => (int) $form_state['values']['visibility'],
        'pages' => trim($form_state['values']['pages']),
        'custom' => (int) $form_state['values']['custom'],
        'title' => $form_state['values']['title'],
        'module' => $form_state['values']['module'],
        'theme' => $theme->name,
        'region' => $region == BLOCK_REGION_NONE ? '' : $region,
        'status' => 0,
        'status' => (int) ($region != BLOCK_REGION_NONE),
        'weight' => 0,
        'delta' => $delta,
        'cache' => DRUPAL_CACHE_PER_PAGE,
      ));
    }
  }
  $query
    ->execute();
  $query = db_insert('block_role')
    ->fields(array(
    'rid',
    'module',
    'delta',
  ));
  foreach (array_filter($form_state['values']['roles']) as $rid) {
    $query
      ->values(array(
      'rid' => $rid,
      'module' => $form_state['values']['module'],
      'delta' => $delta,
    ));
  }
  $query
    ->execute();
  drupal_set_message(t('The taxofaceted block has been created.'));
  cache_clear_all();
  $form_state['redirect'] = 'admin/structure/block';
}

/**
 * Implements hook_block_save().
 */
function taxonomy_facets_block_save($delta = '', $edit = array()) {
  variable_set("taxonomy_facets_{$delta}_tid", $edit['vid']);
}

/**
 * Implements hook_block_info().
 */
function taxonomy_facets_block_info() {
  $blocks = array();
  $deltas = variable_get('taxo_faceted_block_ids', array());
  foreach (array_keys(module_invoke_all('taxonomy_facets_blocks')) as $delta) {
    $deltas[] = $delta;
  }
  foreach ($deltas as $delta) {
    $vid = variable_get("taxonomy_facets_{$delta}_tid", 2);
    $taxonomy_name = check_plain(taxonomy_vocabulary_load($vid)->name);
    $blocks[$delta]['info'] = t('Taxo Faceted Filter: @taxonomy_name', array(
      '@taxonomy_name' => $taxonomy_name,
    ));

    // Menu blocks can't be cached because each menu item can have
    // a custom access callback. menu.inc manages its own caching.
    $blocks[$delta]['cache'] = DRUPAL_CACHE_PER_PAGE;
  }
  return $blocks;
}

/**
 * Generate blocks with menu items used for filtering.
 *
 * When user adds particular taxonomy as "taxo filter" to block
 * this function prints out menu for chosen taxonomy.
 *
 * @param int $delta
 *   Delta (identifier) of the filter.
 *
 * @return array
 *   Block title and content.
 */
function taxonomy_facets_block_view($delta = '') {
  $vid = variable_get("taxonomy_facets_{$delta}_tid", 1);
  $block['subject'] = check_plain(taxonomy_vocabulary_load($vid)->name);
  $block['content'] = taxonomy_facets_get_menu_tree($vid);
  return $block;
}

/**
 * Hook_theme, provides 3 theme files for theming filters.
 */
function taxonomy_facets_theme() {
  $template_path = drupal_get_path('module', 'taxonomy_facets') . '/templates';
  return array(
    'taxonomy_facets_menu_leaf_template' => array(
      'variables' => array(
        'menuLeaf' => NULL,
      ),
      'template' => 'taxonomy_facets_menu_leaf-template',
      'path' => $template_path,
    ),
    'taxonomy_facets_ul_wrapper_begin_template' => array(
      'template' => 'taxonomy_facets_ul_wrapper_begin-template',
      'path' => $template_path,
    ),
    'taxonomy_facets_ul_wrapper_end_template' => array(
      'template' => 'taxonomy_facets_ul_wrapper_end-template',
      'path' => $template_path,
    ),
    'taxonomy_facets_removefilter_template' => array(
      'variables' => array(
        'menuHed' => NULL,
      ),
      'template' => 'taxonomy_facets_removefilter-template',
      'path' => $template_path,
    ),
  );
}

/**
 * Print the page that displays list of nods when filters are applied.
 *
 * When user selects the filters print the page of node teasers.
 * Only relevant nodes are printed, i.e result set of the applied
 * filters.
 *
 * @return string
 *   Formatted list of teasers.
 */
function taxonomy_facets_print_landing_page() {
  $selected_filters = \taxonomyFacets\TaxoFacets::getInstance()
    ->getAppliedFilters();

  // Create a string of applied filters, to be displayed on the top of the page
  // also count number of filters.
  $names = '';
  $filters_number = 0;
  if ($selected_filters) {
    foreach ($selected_filters as $filter) {
      $filters_number++;
      $names .= ' ' . $filter->name . ',';
    }
  }

  // Sometimes if there are no filters on the site, the site admin wants to redirect to
  // a specific page, or to home page, this is setting in admin page
  redirect_if_no_filters($filters_number);
  $names = rtrim($names, ", ");
  $output = array();
  $output['term_heading'] = array(
    '#prefix' => '<h2>',
    '#suffix' => '</h2>',
    '#markup' => taxonomy_facets_print_names_string($filters_number, $names),
  );

  // Get all node id's of the nodes that belong to categories that are applied
  // as filters.
  if ($nids = taxonomy_facets_get_nodes_based_on_intersect_of_terms()) {
    $nodes = node_load_multiple($nids);
    $output += node_view_multiple($nodes);
    $output['pager'] = array(
      '#theme' => 'pager',
      '#weight' => 2,
    );
  }
  else {
    $output['no_content'] = array(
      '#prefix' => '<p>',
      '#markup' => t('There is currently no content classified with this combination of filters. Try removing one or more filters'),
      '#suffix' => '</p>',
    );
  }
  return $output;
}

/**
 * Utility function to extract filter from array of filters.
 *
 * For given vid, return filter element with vid equal to given vid.
 *
 * @param integer $vid
 *   Vocabular id
 *
 * @param array $filters
 *   Array of filters.
 *
 * @return array
 *   Array of info about particular filter.
 */
function taxonomy_facets_get_array_element($vid, $filters) {
  foreach ($filters as $fil) {
    if ($fil->vid == $vid) {
      return $fil;
    }
  }
  return NULL;
}

/**
 * Print out menu tree for each vocab selected to be taxo faceted filter.
 *
 *  For each vocabulary id that is passed as an argument output menu tree. Array
 *  of menu tree is passed through the theme function at the end, so themed
 *  output is produced.
 *
 * @param integer $vid
 *   Vocabulary id
 *
 * @return string
 *   Themed menu tree.
 */
function taxonomy_facets_get_menu_tree($vid) {
  $menu = new \taxonomyFacets\MenuTree($vid);
  $menu_items = null;
  $menu_tree = $menu
    ->getMenuTree();
  foreach ($menu_tree as $item) {
    if (is_string($item)) {
      $menu_items .= $item;
    }
    else {
      $menu_items .= theme('taxonomy_facets_menu_leaf_template', array(
        'menuLeaf' => $item,
      ));
    }
  }
  return $menu_items;
}
function taxonomy_facets_sort_by_vid($a, $b) {
  if ($a->vid == $b->vid) {
    return 0;
  }
  return $a->vid < $b->vid ? -1 : 1;
}

/**
 * Get term name using term id.
 *
 * @param integer $tid
 *   Term id.
 */
function taxonomy_facets_get_term_name_from_id($tid) {
  $result = db_query('SELECT vid, name FROM {taxonomy_term_data} WHERE tid = :tid', array(
    'tid' => $tid,
  ));
  $term = array();
  foreach ($result as $record) {
    $term['name'] = filter_xss($record->name);
    $term['vid'] = $record->vid;
  }
  return $term;
}

/**
 * Somtimes if there are no filters on the site, the site admin wants to redirect to
 * a specific page, or to home page, this is setting in admin page
 */
function redirect_if_no_filters($filters_number) {
  $gotohome_if_nofilters = variable_get('taxonomy_facets_redirect_to_home', FALSE);
  if ($filters_number == 0 && $gotohome_if_nofilters) {

    // but if user specifies different page redirect to that page
    if ($redirect_to_page = variable_get('taxonomy_facets_redirect_to_page', FALSE)) {
      drupal_goto($redirect_to_page);
    }
    else {
      drupal_goto();
    }
  }
}

/**
 * Formats string at the top of the page, i.e.  "Filters: Sony, LCD monitors ..."
 * @param int $filters_number
 *  Number of filters applied
 * @param $names
 *  Filters, i.e Sony, LCD monitors..
 * @return null|string
 *  Formated string
 */
function taxonomy_facets_print_names_string($filters_number = 0, $names) {
  if ($filters_number === 0) {
    $names = NULL;
  }
  elseif ($filters_number === 1) {
    $names = '<em>' . t('Filter:') . '</em>' . $names;
  }
  else {
    $names = '<em>' . t('Filters:') . '</em> ' . $names;
  }
  return $names;
}

/**
 * Get nodes tagged by given terms.
 *
 * Nodes have been associated with various terms. For terms passed in the
 * url as the argument, return all nodes that have those terms associated
 * with them.
 * Nodes that have *all* of the  terms associated will be returned,
 * i.e intersection of terms.
 *
 * @param string $text_compare
 *   (optional) string
 *
 * @param string $text_compare_middle
 *   (optional) string
 */
function taxonomy_facets_get_nodes_based_on_intersect_of_terms($text_compare = NULL, $text_compare_middle = NULL) {

  // Get node types used in taxo faceted filtering.
  $node_types = variable_get('taxonomy_facets_content_type_options', array());
  $nodeTypes = array();
  foreach ($node_types as $key => $value) {
    if ($value !== 0) {
      $nodeTypes[] = $value;
    }
  }

  // Get applied filters
  $tids = \taxonomyFacets\TaxoFacets::getInstance()
    ->getAppliedFilterTids();
  $nodes = taxonomy_facets_get_nodes($tids, $nodeTypes, $text_compare = NULL, $text_compare_middle = NULL);
  $arr_result = array();
  foreach ($nodes as $node) {
    $arr_result[] = $node->nid;
  }
  return $arr_result;

  /* TO DO - implement free text search in conjunction with faceted search
    if ($text_compare) {
    $wheres .= 'AND n.title LIKE \'' . $text_compare . '%\'';
    }

    if ($text_compare_middle) {
    $wheres .= 'AND n.title LIKE \'%' . $text_compare_middle . '%\'';
    }
    */
}
function taxonomy_facets_get_nodes($tids, $node_types, $text_compare = NULL, $text_compare_middle = NULL) {
  $query = db_select('node', 'n')
    ->fields('n', array(
    'nid',
  ));
  $cnt = 0;
  foreach ($tids as $key => $value) {
    $query
      ->innerJoin('taxonomy_index', 'ti' . $cnt, 'n.nid = ti' . $cnt . ' .nid ');
    $query
      ->condition('ti' . $cnt . '.tid', $value);
    $cnt++;
  }
  $query
    ->condition('status', 0, '>')
    ->condition('n.type', $node_types, 'IN')
    ->orderBy('n.sticky', 'DESC')
    ->orderBy('n.created', 'DESC');
  return $query
    ->execute()
    ->fetchAll();
}

/**
 * Examine term and test for any children nodes.
 *
 * @param integer $vid
 *   Vocabulary Id.
 *
 * @param integer $tid
 *   Term id
 *
 * @return boolean
 *   True if there are children taxonomy terms underneath given term.
 */
function taxonomy_facets_get_subnodes($vid, $tid) {

  // First check if term has nodes.
  $result = db_query('SELECT nid FROM {taxonomy_index} WHERE tid = :tid', array(
    ':tid' => $tid,
  ));
  if ($result
    ->rowCount()) {
    return TRUE;
  }
  else {

    // Get all children and check each child for nodes.
    $children = taxonomy_get_tree($vid, $tid);
    foreach ($children as $child) {
      $result = db_query('SELECT nid FROM {taxonomy_index} WHERE tid = :tid', array(
        ':tid' => $child->tid,
      ));
      if ($result
        ->rowCount()) {
        return TRUE;
      }
    }
  }
  return FALSE;
}

/**
 * Preprocess node url and append argument to it.
 *
 * The problem is that on the teaser listing page (landing page), when user
 * clicks on the node title and goes into the node, the left menu will
 * collapse, as a node url normally does not have information about
 * applied filters.
 * This function is used to fix this problem where the menu tree collapses
 * if you go into the node, by appending this information in the form
 * of url arguments.
 *
 * @param array $variables
 *   Array of variables to preproccess.
 *
 * @return nothing
 *   Does not return anything as $variables are passed by reference.
 */
function taxonomy_facets_preprocess_node(&$variables) {
  if ($variables['view_mode'] == 'teaser') {
    if ($filterNames = \taxonomyFacets\TaxoFacets::getInstance()
      ->getAppliedFilterNames()) {
      $variables['node_url'] = $variables['node_url'] . '/?categories=' . implode('/', $filterNames);
      global $base_url;
      $variables['content']['links']['node']['#links']['node-readmore']['href'] = $base_url . $variables['node_url'];
    }
  }
}

/**
 * Implements hook_permission().
 */
function taxonomy_facets_permission() {
  return array(
    'view taxo facets' => array(
      'title' => t('View taxonomy facets'),
    ),
  );
}

/**
 * Get available node types.
 *
 * @return array
 *  Array of node types
 */
function taxo_faceted_get_node_types() {
  $nodeTypeOptions = array();
  foreach (node_type_get_types() as $nodeType) {
    $nodeTypeOptions[$nodeType->type] = t($nodeType->name);
  }
  return $nodeTypeOptions;
}
function taxonomy_facets_node_submit($node, $form, &$form_state) {
  if (variable_get('taxonomy_facets_show_subnodes_checkbox', FALSE)) {

    // Only concerned about taxo faceted node types, as per user selection
    $node_types = variable_get('taxonomy_facets_content_type_options', array());
    $nodeTypes = array();
    foreach ($node_types as $key => $value) {
      if ($value !== 0) {
        $nodeTypes[] = $value;
      }
    }
    $type = $form_state['values']['type'];
    if (in_array($type, $nodeTypes)) {

      // Get vocabulary ids of vocabularies used for taxo facets.
      $deltas = variable_get('taxo_faceted_block_ids', array());

      // Load all taxo faceted vocabularies.
      foreach ($deltas as $delta) {
        $vid = variable_get("taxonomy_facets_{$delta}_tid", 2);
        $vocabulary = taxonomy_vocabulary_load($vid);
        $taxo_facet_vocabs[] = $vocabulary;
        $taxo_facet_vocabs_names[] = $vocabulary->machine_name;
      }

      // Get all fields of type taxonomy reference for this node type.
      foreach (field_info_instances('node', $type) as $key => $value) {
        $field_info = field_info_field($key);
        if ($field_info['type'] == 'taxonomy_term_reference') {

          // Is this field taxo facets vocabulary field.
          $vocabulary_name = $field_info['settings']['allowed_values'][0]['vocabulary'];
          if (in_array($vocabulary_name, $taxo_facet_vocabs_names)) {
            $filed_name = $field_info['field_name'];

            // Now get value of this field and perform check.
            $value = field_get_items('node', $node, $filed_name);
            taxonomy_facets_check_if_node_in_parents($value, $vocabulary_name, $filed_name);
          }
        }
      }
    }
  }
}
function taxonomy_facets_check_if_node_in_parents($values, $vocabulary_name, $filed_name) {
  $all_tids = array();
  foreach ($values as $v) {
    $all_tids[] = $v['tid'];
  }
  foreach ($values as $value) {
    $parents_tids = array();
    $parents = taxonomy_get_parents_all($value['tid']);
    foreach ($parents as $parent) {
      $parents_tids[] = $parent->tid;
    }
    $difference = array_diff($parents_tids, $all_tids);

    // Check if this tid is also in all parents.
    foreach ($difference as $diff) {
      drupal_set_message('There was a problem in the field ' . $filed_name . '. Your taxonomy term ' . taxonomy_term_load($diff)->name . ' has a parent that you have not selected. Please select all the parent terms or you node may not appear in the result of a taxo faceted filtering.
         For more info see <a href="/admin/config/search/tax_faceted_nav">Taxo faceted module admin form</a>', 'warning');
    }
    if ($difference) {
      break;
    }
  }
}

Functions

Namesort descending Description
redirect_if_no_filters Somtimes if there are no filters on the site, the site admin wants to redirect to a specific page, or to home page, this is setting in admin page
taxonomy_facets_add_block_form Menu callback: display the taxo faceted block addition form.
taxonomy_facets_add_block_form_submit Save the new taxo faceted block.
taxonomy_facets_block_configure Implements hook_block_configure().
taxonomy_facets_block_info Implements hook_block_info().
taxonomy_facets_block_save Implements hook_block_save().
taxonomy_facets_block_view Generate blocks with menu items used for filtering.
taxonomy_facets_check_if_node_in_parents
taxonomy_facets_configure_form Returns the configuration form.
taxonomy_facets_get_array_element Utility function to extract filter from array of filters.
taxonomy_facets_get_menu_tree Print out menu tree for each vocab selected to be taxo faceted filter.
taxonomy_facets_get_nodes
taxonomy_facets_get_nodes_based_on_intersect_of_terms Get nodes tagged by given terms.
taxonomy_facets_get_subnodes Examine term and test for any children nodes.
taxonomy_facets_get_term_name_from_id Get term name using term id.
taxonomy_facets_menu Implements hook_menu().
taxonomy_facets_node_submit
taxonomy_facets_permission Implements hook_permission().
taxonomy_facets_preprocess_node Preprocess node url and append argument to it.
taxonomy_facets_print_landing_page Print the page that displays list of nods when filters are applied.
taxonomy_facets_print_names_string Formats string at the top of the page, i.e. "Filters: Sony, LCD monitors ..."
taxonomy_facets_sort_by_vid
taxonomy_facets_theme Hook_theme, provides 3 theme files for theming filters.
taxo_faceted_get_node_types Get available node types.