You are here

tvi.module in Taxonomy Views Integrator 7

Same filename and directory in other branches
  1. 8 tvi.module
  2. 6 tvi.module

Allow to define views to be used instead of default drupal behavior on taxonomy terms pages.

File

tvi.module
View source
<?php

/**
 * @file
 * Allow to define views to be used instead of default drupal behavior on
 * taxonomy terms pages.
 */

// -----------------------------------------------------------------------------
// Constants
// -----------------------------------------------------------------------------

/**
 * Default view display name.
 */
define('TVI_DEFAULT_DISPLAY', 'default');

/**
 * Taxonomy setting types.
 */
define('TVI_TYPE_ALL', 'all');
define('TVI_TYPE_TERM', 'term');
define('TVI_TYPE_VOCAB', 'vocab');

/**
 * Used in tvi_get_term_info(...).
 */
define('TVI_DATATYPE_ALL', 'all');
define('TVI_DATATYPE_TERM', 'term');
define('TVI_DATATYPE_VIEW', 'view');
define("TVI_DATATYPE_SETTINGS", 'settings');

// TODO - as a work-around to files[] not being included on form submit.
module_load_include('inc', 'tvi', 'includes/tvi.admin');
module_load_include('inc', 'tvi', 'includes/tvi.query');

// -----------------------------------------------------------------------------
// Drupal hooks
// -----------------------------------------------------------------------------

/**
 * Implements hook_modules_disabled().
 */
function tvi_modules_disabled($modules) {
  if (in_array('uuid', $modules)) {
    tvi_include('query');
    _tvi_convert_uuids_to_tids();
  }
}

/**
 * Implements hook_modules_enabled().
 */
function tvi_modules_enabled($modules) {
  if (in_array('uuid', $modules)) {
    tvi_include('query');
    _tvi_convert_tids_to_uuids();
  }
}

/**
 * Implements hook_permission().
 */
function tvi_permission() {
  $permissions = array();
  $permissions['administer taxonomy views integrator'] = array(
    'title' => t('Administer Taxonomy Views Integrator'),
  );
  foreach (taxonomy_get_vocabularies() as $vocabulary) {
    $permissions['define view for vocabulary ' . $vocabulary->machine_name] = array(
      'title' => t('Define the view for the vocabulary %vocabulary', array(
        '%vocabulary' => $vocabulary->name,
      )),
    );
    $permissions['define view for terms in ' . $vocabulary->machine_name] = array(
      'title' => t('Define the view for terms in %vocabulary', array(
        '%vocabulary' => $vocabulary->name,
      )),
    );
  }
  return $permissions;
}

/**
 * Implements hook_menu().
 */
function tvi_menu() {
  $items['admin/config/user-interface/tvi'] = array(
    'title' => 'TVI settings',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'tvi_settings_form',
    ),
    'access arguments' => array(
      'administer taxonomy views integrator',
    ),
    'file' => 'includes/tvi.admin.inc',
  );
  return $items;
}

/**
 * Implements hook_menu_alter().
 */
function tvi_menu_alter(&$items) {
  $items['taxonomy/term/%taxonomy_term']['page callback'] = 'tvi_render_view';
  $items['taxonomy/term/%taxonomy_term']['page arguments'] = array(
    2,
  );
  $items['taxonomy/term/%taxonomy_term']['access callback'] = 'tvi_render_view_access';
  $items['taxonomy/term/%taxonomy_term']['access arguments'] = array(
    2,
  );

  // Avoid views to override tvi.
  unset($items['taxonomy/term/%']);

  // Views might have completely wiped the original title callback, so restore
  // it. This is useful for breadcrumb-generating modules such as Crumbs or
  // Easy breadcrumbs.
  $items['taxonomy/term/%taxonomy_term']['title callback'] = 'taxonomy_term_title';
  $items['taxonomy/term/%taxonomy_term']['title arguments'] = array(
    2,
  );
}

/**
 * Implements hook_form_alter().
 *
 * Used to add some items to the taxonomy term and vocab edit pages
 */
function tvi_form_alter(&$form, $form_state, $form_id) {
  $forms = array(
    'taxonomy_form_term',
    'taxonomy_form_vocabulary',
  );
  if (in_array($form_id, $forms) && !isset($form_state['confirm_delete']) && !isset($form_state['confirm_parents'])) {
    tvi_include('admin');
    if ($form_id == 'taxonomy_form_term' && user_access('define view for terms in ' . $form['#vocabulary']->machine_name)) {
      tvi_term_form($form);
    }
    elseif (user_access('define view for vocabulary ' . $form['#vocabulary']->machine_name)) {
      tvi_vocab_form($form);
    }
  }
}

/**
 * Implements hook_taxonomy_vocabulary_delete().
 *
 * Remove TVI settings when vocabularies are deleted.
 */
function tvi_taxonomy_vocabulary_delete($vocabulary) {
  tvi_include('query');
  tvi_remove_settings($vocabulary->vid, TVI_TYPE_VOCAB);
}

/**
 * Implements hook_taxonomy_term_delete().
 *
 * Remove TVI settings when terms are deleted.
 */
function tvi_taxonomy_term_delete($term) {
  tvi_include('query');
  tvi_remove_settings($term->tid, TVI_TYPE_TERM);
}

/**
 * Implements hook_theme().
 */
function tvi_theme() {
  return array(
    'tvi_breadcrumb' => array(
      'arguments' => array(
        'term' => NULL,
        'view' => NULL,
      ),
    ),
    'tvi_term_description' => array(
      'arguments' => array(
        'term' => NULL,
      ),
    ),
  );
}

/**
 * Return the taxonomy page breadcrumb (for active view overrides).
 *
 * The algorithm we use is based off of $views->get_breadcrumb(), but has a few
 * important differences.  Override this if you have your own breadcrumb method.
 *
 * @param object $term
 *   The term object.
 * @param object $view
 *   The view definition object.
 *
 * @return array
 *   The array defining the breadcrumb content.
 *
 * @see tvi_get_breadcrumb()
 */
function theme_tvi_breadcrumb($term, $view) {
  return tvi_get_breadcrumb($term, $view);
}

/**
 * Return the taxonomy description (for active view overrides).
 *
 * @param object $term
 *   The term object.
 *
 * @return string
 *   The term description.
 */
function theme_tvi_term_description($term) {
  if (is_object($term)) {
    return '<div class="tvi-term-desc">' . filter_xss_admin($term->description) . '</div>';
  }
  return '';
}

// -----------------------------------------------------------------------------
// TVI callbacks
// -----------------------------------------------------------------------------

/**
 * Replace taxonomy page callback.
 *
 * If more or less than one term is given then pass the request off
 * to the original taxonomy module page callback.
 *
 * @param int|object $tid
 *   The term tid or the term object.
 * @param null|int $depth
 *   The shown depth.
 *
 * @return array
 *   The build array.
 */
function tvi_render_view($tid, $depth = NULL) {
  if (is_object($tid)) {
    $tid = $tid->tid;
  }
  list($view, $display, $term, $settings) = tvi_get_view_info($tid);

  // Load metatags if needed.
  if (module_exists('metatag')) {
    metatag_entity_view($term, 'taxonomy_term', 'full', NULL);
  }
  if (is_object($view) && $display) {
    $output = t('There was no content found matching this term.');
    if (isset($settings->pass_arguments) && $settings->pass_arguments == 1) {

      // Pass all arguments to views. Exclude /taxonomy/term.
      $args = array_slice(arg(), 2);
    }
    else {
      $args = array(
        $tid,
      );
      if (NULL !== $depth) {
        $args[] = $depth;
      }
    }
    if ($view->display[$display]->display_plugin == 'block') {

      // If it's a block display, views returns a block array which won't work
      // as a page callback so we need to explicitly set the page title
      // and just return the $block['content'].
      $block = $view
        ->execute_display($display, $args);
      drupal_set_title(filter_xss_admin($block['subject']), PASS_THROUGH);
      $output = $block['content'];
    }
    else {
      global $language;
      module_invoke_all('entity_view', $term, 'taxonomy_term', 'full', $language->language);
      $output = $view
        ->execute_display($display, $args);
    }
    return $output;
  }

  // Taxonomy is last resort - used if no standard views are found.
  module_load_include('inc', 'taxonomy', 'taxonomy.pages');
  return taxonomy_term_page($term);
}

/**
 * Check access for the current taxonomy page.
 *
 * We start off by checking view overrides, then take the normal permission for
 * taxonomy/term pages, if no view is found.
 *
 * @param int|object $tid
 *   The term tid or the term object.
 *
 * @return bool
 *   TRUE if the user can access the current taxonomy page.
 */
function tvi_render_view_access($tid) {
  if (is_object($tid)) {
    $tid = $tid->tid;
  }
  list($view, $display) = tvi_get_view_info($tid);
  if (is_object($view) && $display) {
    if ($view
      ->access($display)) {
      return TRUE;
    }
    return FALSE;
  }
  return user_access('access content');
}

// -----------------------------------------------------------------------------
// Internal utilities
// -----------------------------------------------------------------------------

/**
 * Return information about the arguments given to the taxonomy term callback.
 *
 * @param int $tid
 *   The term tid.
 *
 * @return array
 *   The information array including the view, the display, the term and the
 *   TVI settings object.
 */
function tvi_get_view_info($tid) {
  $info = tvi_get_term_info($tid, TVI_DATATYPE_ALL);
  $term = isset($info->term) ? $info->term : NULL;
  $view = isset($info->view) ? $info->view : NULL;
  $display = NULL;
  $settings = isset($info->settings) ? $info->settings : NULL;
  if (is_object($view) && is_object($settings) && isset($settings->status) && $settings->status) {
    $display = $settings->display;
  }

  // Important things to consider:
  //
  // * If this is a default view, then $settings will be NULL.
  // * The variable $term might be NULL if this is a multi term request.
  // * If $view or $display are NULL, then nothing was found.
  return array(
    $view,
    $display,
    $term,
    $settings,
  );
}

/**
 * Return different data sets for a specified term id.
 *
 * This function will try to get the term settings, then its ancestors settings,
 * then its vocabulary settings then the default global settings if needed.
 *
 * @param int $tid
 *   The term tid.
 * @param string $type
 *   The kind of data we want.
 *
 * @return null|object
 *   The data we asked for.
 */
function tvi_get_term_info($tid, $type = TVI_DATATYPE_VIEW) {
  static $term_info = array();
  if (!array_key_exists($tid, $term_info)) {
    $term = taxonomy_term_load($tid);

    // Return nothing when term is empty.
    if (!$term) {
      return NULL;
    }

    // Try using term and vocabulary overrides.
    tvi_include('query');
    $settings = tvi_load_settings($term->tid, TVI_TYPE_TERM, FALSE);

    // If the term has no settings, search for parent terms' ones.
    if (!$settings || empty($settings->status)) {

      // Get all the term's ancestors.
      $parents = taxonomy_get_parents_all($term->tid);

      // Remove the current term from the array.
      array_shift($parents);

      // While the settings are not set, not active or not inheritables.
      while (($current = array_shift($parents)) && (!$settings || empty($settings->status) || empty($settings->inherit))) {
        $settings = tvi_load_settings($current->tid, TVI_TYPE_TERM, FALSE);
      }

      // Avoids the case where no parent of the term are inheritables.
      if (empty($settings->inherit)) {
        $settings = FALSE;
      }
    }

    // If the term and its parents have no settings, take the vocabulary's one.
    if (!$settings || empty($settings->status)) {
      $settings = tvi_load_settings($term->vid, TVI_TYPE_VOCAB, FALSE);
    }

    // If the vocabulary have no settings, take the global settings.
    if (!$settings || empty($settings->status)) {
      $settings = tvi_load_settings('default', TVI_TYPE_ALL, FALSE);
    }
    $term_info[$tid] = array(
      'term' => $term,
      'settings' => $settings,
    );
    if (isset($settings->view)) {
      $term_info[$tid]['view'] = $settings->view;
    }
  }
  switch ($type) {
    case TVI_DATATYPE_ALL:
      return (object) $term_info[$tid];
    case TVI_DATATYPE_TERM:
      return $term_info[$tid]['term'];
    case TVI_DATATYPE_VIEW:
      return $term_info[$tid]['view'];
    case TVI_DATATYPE_SETTINGS:
      return $term_info[$tid]['settings'];
  }
  return NULL;
}

/**
 * Get the taxonomy page breadcrumb links.
 *
 * This is based off of the code for [ $views->get_breadcrumb() ].
 *
 * We needed a few modifications and the ability to use views by default, but
 * allow for theme overrides of the breadcrumb trail for this module.
 *
 * We also filter out links to the current override display page, so that we do
 * not get duplicate links, when the view path matches the current taxonomy
 * override display path.
 *
 * This also allows us to have view hierarchy in our breadcrumb, instead of the
 * typical taxonomy breadcrumb that starts with Home, then lists the terms.
 *
 * So for example, we might have something like this:
 *
 * Home >> Vocab >> Parent term >> This term
 *
 * @param object $term
 *   The term object.
 * @param object $view
 *   The views definition object.
 *
 * @return array
 *   The breadcrumb related to the given term.
 */
function tvi_get_breadcrumb($term, $view) {
  $breadcrumb = array();
  if (!empty($view->build_info['breadcrumb'])) {
    $curr_path = $view
      ->get_url(array(
      $term->tid,
    ));
    $base = TRUE;
    foreach ($view->build_info['breadcrumb'] as $path => $title) {

      // Check to see if the frontpage is in the breadcrumb trail; if it
      // is, we'll remove that from the actual breadcrumb later.
      if ($path == variable_get('site_frontpage', 'node')) {
        $base = FALSE;
        $title = t('Home');
      }
      if ($title && $path != $curr_path) {
        $breadcrumb[] = l($title, $path, array(
          'html' => TRUE,
        ));
      }
    }
    if ($base) {
      $breadcrumb = array_merge(drupal_get_breadcrumb(), $breadcrumb);
    }
  }
  return $breadcrumb;
}

/**
 * Include various application logic.
 *
 * Note, that you only have to specify the name of the include.
 * tvi_include('admin') : includes -> [ includes/tvi.admin.inc ]
 */
function tvi_include() {
  $args = func_get_args();
  foreach ($args as $name) {
    module_load_include('inc', 'tvi', 'includes/tvi.' . $name);
  }
}

Functions

Namesort descending Description
theme_tvi_breadcrumb Return the taxonomy page breadcrumb (for active view overrides).
theme_tvi_term_description Return the taxonomy description (for active view overrides).
tvi_form_alter Implements hook_form_alter().
tvi_get_breadcrumb Get the taxonomy page breadcrumb links.
tvi_get_term_info Return different data sets for a specified term id.
tvi_get_view_info Return information about the arguments given to the taxonomy term callback.
tvi_include Include various application logic.
tvi_menu Implements hook_menu().
tvi_menu_alter Implements hook_menu_alter().
tvi_modules_disabled Implements hook_modules_disabled().
tvi_modules_enabled Implements hook_modules_enabled().
tvi_permission Implements hook_permission().
tvi_render_view Replace taxonomy page callback.
tvi_render_view_access Check access for the current taxonomy page.
tvi_taxonomy_term_delete Implements hook_taxonomy_term_delete().
tvi_taxonomy_vocabulary_delete Implements hook_taxonomy_vocabulary_delete().
tvi_theme Implements hook_theme().

Constants