You are here

taxonomy_tools.module in Taxonomy Tools 8

Same filename and directory in other branches
  1. 7 taxonomy_tools.module

Drupal hooks and functions to work with taxonomy terms.

User is provided with a taxonomy term overview table that implements functionality from all Taxonomy Tools submodules.

File

taxonomy_tools.module
View source
<?php

/**
 * @file
 * Drupal hooks and functions to work with taxonomy terms.
 *
 * User is provided with a taxonomy term overview table that
 * implements functionality from all Taxonomy Tools submodules.
 */

/**
 * Implements hook_menu().
 */
function taxonomy_tools_menu() {
  $items = array();
  $items['admin/config/taxonomy-tools'] = array(
    'title' => 'Taxonomy Tools',
    'description' => 'Taxonomy Tools configuration',
    'page callback' => 'system_admin_menu_block_page',
    'access arguments' => array(
      'access administration pages',
    ),
    'file' => 'system.admin.inc',
    'file path' => drupal_get_path('module', 'system'),
  );
  $items['admin/structure/taxonomy/%taxonomy_vocabulary_machine_name/overview'] = array(
    'title' => 'Overview',
    'description' => 'Vocabulary overview table',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'taxonomy_tools_overview',
      3,
      5,
    ),
    'file' => 'taxonomy_tools.admin.inc',
    'file path' => drupal_get_path('module', 'taxonomy_tools'),
    'type' => MENU_LOCAL_TASK,
    'weight' => 50,
    'access callback' => 'taxonomy_tools_overview_access',
    'access arguments' => array(
      3,
    ),
  );
  $items['admin/structure/taxonomy/%taxonomy_vocabulary_machine_name/overview/%/add'] = array(
    'title' => 'Add term',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'taxonomy_form_term',
      array(),
      3,
    ),
    'file path' => drupal_get_path('module', 'taxonomy'),
    'file' => 'taxonomy.admin.inc',
    'access callback' => 'taxonomy_tools_overview_access',
    'access arguments' => array(
      3,
      TRUE,
    ),
  );
  $items['admin/structure/taxonomy/%taxonomy_vocabulary_machine_name/overview/add'] = array(
    'title' => 'Add term',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'taxonomy_form_term',
      array(),
      3,
    ),
    'file path' => drupal_get_path('module', 'taxonomy'),
    'file' => 'taxonomy.admin.inc',
    'access callback' => 'taxonomy_tools_overview_access',
    'access arguments' => array(
      3,
      TRUE,
    ),
  );
  $items['taxonomy/term/%taxonomy_term/overview'] = array(
    'title' => 'Subtree',
    'description' => 'Subtree overview table',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'taxonomy_tools_overview',
      NULL,
      2,
    ),
    'file' => 'taxonomy_tools.admin.inc',
    'file path' => drupal_get_path('module', 'taxonomy_tools'),
    'access arguments' => array(
      'use taxonomy tools',
    ),
    'weight' => 50,
    'type' => MENU_LOCAL_TASK,
  );
  $items['taxonomy/term/%taxonomy_term/overview/add/%taxonomy_vocabulary_machine_name'] = array(
    'title' => 'Add term',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'taxonomy_form_term',
      array(),
      5,
    ),
    'file path' => drupal_get_path('module', 'taxonomy'),
    'file' => 'taxonomy.admin.inc',
    'access callback' => 'taxonomy_tools_overview_access',
    'access arguments' => array(
      5,
      TRUE,
    ),
  );
  return $items;
}

/**
 * Implements hook_theme().
 */
function taxonomy_tools_theme() {
  return array(
    'taxonomy_tools_overview' => array(
      'render element' => 'form',
    ),
  );
}

/**
 * Implements hook_form_FORM_ID_alter().
 */
function taxonomy_tools_form_taxonomy_form_term_alter(&$form, &$form_state) {

  // Preset default parent term.
  if (arg(4) && arg(4) == 'overview' && is_numeric(arg(5))) {
    $form['relations']['parent']['#default_value'] = array(
      arg(5) => arg(5),
    );
  }
  if (arg(3) && arg(3) == 'overview' && is_numeric(arg(2))) {
    $form['relations']['parent']['#default_value'] = array(
      arg(2) => arg(2),
    );
  }
}

/**
 * Implements hook_menu_alter().
 */
function taxonomy_tools_menu_alter(&$items) {
  $use_view = FALSE;
  if (module_exists('views')) {
    $views = views_get_enabled_views();
    foreach ($views as $view) {
      foreach ($view->display as $display) {
        if (isset($display->display_options['path']) && $display->display_options['path'] == 'taxonomy/term/%') {

          // If term page path is used by a view.
          $items['taxonomy/term/%views_arg']['access callback'] = 'taxonomy_tools_taxonomy_term_access';
          $items['taxonomy/term/%views_arg']['access arguments'] = array(
            2,
          );
          $use_view = TRUE;
          break 2;
        }
      }
    }
  }
  if (!$use_view) {
    $items['taxonomy/term/%taxonomy_term']['access callback'] = 'taxonomy_tools_taxonomy_term_access';
    $items['taxonomy/term/%taxonomy_term']['access arguments'] = array(
      2,
    );
  }
}

/**
 * Controls the access to the taxonomy term.
 *
 * If the taxonomy term is from a vocabulary that uses Taxonoomy Publisher
 * functionality, it's status will checked to determine whether the user is
 * allowed to access it.
 *
 * @param stdClass $term
 *   A taxonomy term object or a string containing taxonomy term ID.
 *
 * @return bool
 *   TRUE if user is allowed to access the taxonomy term, and FALSE if isn't.
 */
function taxonomy_tools_taxonomy_term_access($term) {
  $access = user_access('access content');

  // In case only term ID is passed as parameter we must load the term object.
  if (is_numeric($term)) {
    $term = taxonomy_term_load($term);
  }
  if (is_object($term)) {
    if (module_exists('taxonomy_tools_publisher') && $access) {

      // If Taxonomy Publisher is enabled and term is available to the user.
      $config = array_filter(variable_get('taxonomy_tools_publisher_config', array()));
      if (!empty($config) && in_array($term->vocabulary_machine_name, $config)) {

        // We must check the whole hierarchy branch up to the top level.
        $result = taxonomy_tools_publisher_check_branch($term->tid);
        if (!user_access('bypass taxonomy publisher') && $result == 0) {

          // User isn't allowed to access the term,
          // because the term (or it's parent term) is not published
          // and user doesn't have permission to view unpublished terms.
          $access = FALSE;
        }
      }
    }
    if (module_exists('taxonomy_tools_role_access') && $access && !user_access('bypass taxonomy role access')) {

      // If Taxonomy Role Access is enabled and term is still available to
      // the user and user doesn't have permissions to bypass settings.
      $access = FALSE;
      global $user;
      $roles = $user->roles;
      $config = variable_get('taxonomy_tools_role_access_role_config', array());
      foreach ($roles as $rid => $role) {
        if (in_array($rid, $config)) {

          // User role is controlled by Taxonomy Role Access.
          $role_access = taxonomy_tools_role_access_get_access($term->tid, $rid);
          if ($role_access) {

            // Term is available to the user role.
            $access = TRUE;
            break;
          }
        }
        else {

          // One of the user roles is not controlled by Taxonomy Role Access
          // term is available to the user.
          $access = TRUE;
          break;
        }
      }
    }
  }
  else {

    // Something is wrong with the passed parameter - deny access.
    $access = FALSE;
  }
  return $access;
}

/**
 * Implements hook_node_grants().
 */
function taxonomy_tools_node_grants($account, $op) {
  $node_grants = array();
  $roles = $account->roles;
  $node_grants = array(
    'taxonomy_tools' => array_keys($roles),
  );
  return $node_grants;
}

/**
 * Implements hook_node_access_records().
 */
function taxonomy_tools_node_access_records($node) {
  $grants = array();
  $grants = taxonomy_tools_build_grants($node);
  return $grants;
}

/**
 * Builds node access grants for all user roles.
 *
 * @param stdClass $node
 *   A node object.
 *
 * @return array
 *   An array containing node access grants.
 */
function taxonomy_tools_build_grants($node) {
  $grants = array();

  // Get all terms associated with this node.
  $query = db_select('taxonomy_index', 'foo');
  $query
    ->addField('foo', 'tid');
  $query
    ->condition('foo.nid', $node->nid);
  $query
    ->join('taxonomy_term_data', 'bar', 'foo.tid = bar.tid');
  $query
    ->addField('bar', 'vid');
  $query
    ->join('taxonomy_vocabulary', 'baz', 'bar.vid = baz.vid');
  $query
    ->addField('baz', 'machine_name');
  $result = $query
    ->execute()
    ->fetchAll();

  // Get all user roles.
  $roles = user_roles();
  $roles = array_keys($roles);

  // Create node access grants for each user role.
  foreach ($roles as $rid) {
    $view = TRUE;
    if (!empty($result)) {

      // There are terms associated with this node.
      $permissions = taxonomy_tools_role_permissions_all($rid);
      if (module_exists('taxonomy_tools_publisher') && $view) {

        // If Taxonomy Publisher is enabled and node is visible to user role.
        $view = FALSE;
        $config = array_filter(variable_get('taxonomy_tools_publisher_config', array()));
        foreach ($result as $term_data) {
          if (!in_array($term_data->machine_name, $config) || in_array($term_data->machine_name, $config) && taxonomy_tools_publisher_check_branch($term_data->tid)) {

            // One of terms is not controlled by Taxonomy Publisher
            // or is controlled but is seen by user.
            $view = TRUE;
            break;
          }
        }
        if (!$view && in_array('bypass taxonomy publisher', $permissions)) {

          // None of associated terms is available to the user,
          // but user is allowed to see nodes from unpublished terms.
          $view = TRUE;
        }
      }
      if (module_exists('taxonomy_tools_role_access') && $view) {

        // If Taxonomy Role Access is enabled and node is visible to user role.
        $v_config = array_filter(variable_get('taxonomy_tools_role_access_vocab_config', array()));
        $r_config = variable_get('taxonomy_tools_role_access_role_config', array());
        if (in_array($rid, $r_config)) {
          $view = FALSE;

          // Only if user role is controlled by Taxonomy Role Access.
          foreach ($result as $term_data) {
            if (!in_array($term_data->machine_name, $v_config) || in_array($term_data->machine_name, $v_config) && taxonomy_tools_role_access_get_access($term_data->tid, $rid)) {

              // One of terms is not controlled by Taxonomy Role Access
              // or is controlled but is available to this user role.
              $view = TRUE;
              break;
            }
          }
          if (!$view && in_array('bypass taxonomy role access', $permissions)) {

            // None of associated terms is available to the user,
            // but user is allowed to see nodes from restricted terms.
            $view = TRUE;
          }
        }
      }
    }
    $grants[] = array(
      'realm' => 'taxonomy_tools',
      'gid' => $rid,
      'grant_view' => $view ? 1 : 0,
      'grant_update' => 0,
      'grant_delete' => 0,
      'priority' => 0,
    );
  }
  return $grants;
}

/**
 * Collects all permissions (also inherited) specific to a user role.
 *
 * @param string $rid
 *   A string containing user role ID.
 *
 * @return array
 *   An array of user permissions.
 */
function taxonomy_tools_role_permissions_all($rid) {
  $user_roles = array();
  $user_roles[$rid] = $rid;
  if ($rid != 1) {

    // All other roles than anonymous user inherit permissions from
    // authenticated user.
    $user_roles[2] = 2;
  }
  $permissions = user_role_permissions($user_roles);
  $permissions_all = array();

  // Merge all permissions.
  foreach ($permissions as $data) {
    $permissions_all = array_merge($permissions_all, array_keys($data));
  }

  // Remove duplicate values.
  $permissions_all = array_unique($permissions_all);
  return $permissions_all;
}

/**
 * Fetches ID's of all nodes associated with specific taxonomy term.
 *
 * @param string $tid
 *   A string containing taxonomy term ID.
 *
 * @return array
 *   An array containing all associated node ID's.
 */
function taxonomy_tools_associated_nodes($tid) {
  $nids = array();

  // Get all associated node ID's.
  $query = db_select('taxonomy_index', 'foo');
  $query
    ->addField('foo', 'nid');
  $query
    ->condition('foo.tid', $tid);
  $query
    ->execute();
  $result = $query
    ->execute()
    ->fetchAll();
  foreach ($result as $data) {
    $nids[] = $data->nid;
  }

  // Check if this term has any children.
  $query = db_select('taxonomy_term_hierarchy', 'foo');
  $query
    ->addField('foo', 'tid');
  $query
    ->condition('foo.parent', $tid);
  $result = $query
    ->execute()
    ->fetchAll();
  foreach ($result as $data) {

    // Nodes associated with children terms could also be affected;
    // fetch those nids also.
    $nids = array_merge($nids, taxonomy_tools_associated_nodes($data->tid));
  }
  return $nids;
}

/**
 * Builds new access grants for nodes associated with specific taxonomy term.
 *
 * @param stdClass $term
 *   A taxonomy term object
 */
function taxonomy_tools_rebuild_access_grants($term) {
  if (is_numeric($term)) {
    $term = taxonomy_term_load($term);
  }
  $rebuild = FALSE;
  if (module_exists('taxonomy_tools_publisher') && !$rebuild) {

    // Whether Taxonomy Publisher module is enabled.
    $config = array_filter(variable_get('taxonomy_tools_publisher_config', array()));

    // Only if the vocabulary uses Taxonomy Publisher.
    if (!empty($config) && in_array($term->vocabulary_machine_name, $config)) {
      $rebuild = TRUE;
    }
  }
  if (module_exists('taxonomy_tools_role_access') && !$rebuild) {

    // Whether Taxonomy Role Access module is enabled.
    $config = array_filter(variable_get('taxonomy_tools_role_access_vocab_config', array()));

    // Only if the vocabulary uses Taxonomy Role Access.
    if (!empty($config) && in_array($term->vocabulary_machine_name, $config)) {
      $rebuild = TRUE;
    }
  }
  if ($rebuild) {

    // Get all associated nodes.
    $nids = taxonomy_tools_associated_nodes($term->tid);
    foreach ($nids as $nid) {

      // Rewrite node access grants.
      $node = node_load($nid);
      node_access_acquire_grants($node);

      // Allow other modules to act on node access grants rebuild.
      module_invoke_all('taxonomy_tools_node_access_grants_rebuild', $node);
    }
  }
}

/**
 * Implements hook_taxonomy_term_update().
 */
function taxonomy_tools_taxonomy_term_update($term) {
  taxonomy_tools_rebuild_access_grants($term);
}

/**
 * Implements hook_taxonomy_term_delete().
 */
function taxonomy_tools_taxonomy_term_delete($term) {
  taxonomy_tools_rebuild_access_grants($term);
}

/**
 * Returns full parent hierarchy of taxonomy term.
 *
 * @param string $tid
 *   Taxonomy term identificator.
 *
 * @return array
 *   An array representing full hierarchy of taxonomy term.
 */
function taxonomy_tools_term_path($tid, $return = TRUE) {
  $path = array();
  $query = db_select('taxonomy_term_data', 'foo');
  $query
    ->addField('foo', 'name');
  $query
    ->condition('foo.tid', $tid);
  $query
    ->leftJoin('taxonomy_term_hierarchy', 'bar', 'foo.tid = bar.tid');
  $query
    ->addField('bar', 'parent');
  $result = $query
    ->execute()
    ->fetchObject();
  if ($return) {
    $path[] = array(
      'tid' => $tid,
      'name' => $result->name,
    );
  }
  if ($result->parent > 0) {
    $path = array_merge($path, taxonomy_tools_term_path($result->parent));
  }
  return $path;
}

/**
 * Implements hook_permission().
 */
function taxonomy_tools_permission() {
  return array(
    'use taxonomy tools' => array(
      'title' => t('Use Taxonomy Tools'),
      'description' => t('Allows the user to access Taxonomy Tools overview page.'),
    ),
  );
}

/**
 * Controls access to Taxonomy Tools overview page.
 *
 * @return bool
 *   TRUE if the vocabulary uses at least one of Taxonomy Tools
 *   sub-modules and user has permission to access
 *   Taxonomy Tools overview page;
 *   FALSE otherwise.
 */
function taxonomy_tools_overview_access($vocabulary, $add = NULL) {
  $access = FALSE;
  $access = user_access('use taxonomy tools');
  if ($access && $add && !(user_access('administer taxonomy') || user_access('edit terms in ' . $vocabulary->vid))) {
    $access = FALSE;
  }
  return $access;
}

/**
 * Implements hook_admin_paths().
 */
function taxonomy_tools_admin_paths() {
  $paths = array(
    'taxonomy-tools/*/order/*' => TRUE,
  );
  return $paths;
}

/**
 * Implements hook_taxonomy_term_presave().
 */
function taxonomy_tools_taxonomy_term_presave($term) {
  if (arg(4) == 'overview' || arg(3) == 'overview') {
    $count = array();

    // Take in account that term can have more than one parent.
    foreach ($term->parent as $tid) {
      $query = db_select('taxonomy_term_hierarchy', 'foo');
      $query
        ->addField('foo', 'tid');
      $query
        ->condition('foo.parent', $tid);
      $query
        ->join('taxonomy_term_data', 'bar', 'foo.tid = bar.tid');
      $query
        ->condition('bar.vid', $term->vid);
      $count[] = $query
        ->countQuery()
        ->execute()
        ->fetchField();
    }

    // Set term weight to siblings count + 1.
    // We use the biggest siblings count.
    $term->weight = max($count) + 1;
  }
}

/**
 * Implements hook_preprocess_HOOK().
 */
function taxonomy_tools_preprocess_page(&$variables) {
  if (arg(4) == 'overview' && arg(5) != 'add' && arg(6) != 'add') {

    // Proper breadcrumb for vocabulary overview page.
    $breadcrumb = array();
    $options = array(
      'html' => TRUE,
    );
    $step = l(t('Home'), '<front>', $options);
    array_push($breadcrumb, $step);
    $step = l(t('Administration'), 'admin', $options);
    array_push($breadcrumb, $step);
    $step = l(t('Structure'), 'admin/structure', $options);
    array_push($breadcrumb, $step);
    $step = l(t('Taxonomy'), 'admin/structure/taxonomy', $options);
    array_push($breadcrumb, $step);
    if (is_numeric(arg(5))) {
      $query = db_select('taxonomy_vocabulary', 'foo');
      $query
        ->addField('foo', 'name');
      $query
        ->condition('foo.machine_name', arg(3));
      $vocabulary_name = $query
        ->execute()
        ->fetchField();
      $step = l($vocabulary_name, 'admin/structure/taxonomy/' . arg(3) . '/overview', $options);
      array_push($breadcrumb, $step);
      $path = array_reverse(taxonomy_tools_term_path(arg(5), FALSE));
      if (!empty($path)) {
        foreach ($path as $term) {
          $step = l($term['name'], 'admin/structure/taxonomy/' . arg(3) . '/overview/' . $term['tid'], $options);
          array_push($breadcrumb, $step);
        }
      }
    }
    drupal_set_breadcrumb($breadcrumb);
  }
  if (arg(4) == 'overview' && (arg(5) == 'add' || arg(6) == 'add')) {

    // Proper breadcrumb when adding new term through vocabulary
    // overview page.
    $breadcrumb = array();
    $options = array(
      'html' => TRUE,
    );
    $step = l(t('Home'), '<front>', $options);
    array_push($breadcrumb, $step);
    $step = l(t('Administration'), 'admin', $options);
    array_push($breadcrumb, $step);
    $step = l(t('Structure'), 'admin/structure', $options);
    array_push($breadcrumb, $step);
    $step = l(t('Taxonomy'), 'admin/structure/taxonomy', $options);
    array_push($breadcrumb, $step);
    $query = db_select('taxonomy_vocabulary', 'foo');
    $query
      ->addField('foo', 'name');
    $query
      ->condition('foo.machine_name', arg(3));
    $vocabulary_name = $query
      ->execute()
      ->fetchField();
    $step = l($vocabulary_name, 'admin/structure/taxonomy/' . arg(3) . '/overview', $options);
    array_push($breadcrumb, $step);
    if (is_numeric(arg(5))) {
      $path = array_reverse(taxonomy_tools_term_path(arg(5)));
      if (!empty($path)) {
        foreach ($path as $term) {
          $step = l($term['name'], 'admin/structure/taxonomy/' . arg(3) . '/overview/' . $term['tid'], $options);
          array_push($breadcrumb, $step);
        }
      }
    }
    drupal_set_breadcrumb($breadcrumb);
  }
  if (arg(3) == 'overview' && arg(4) == 'add' && is_numeric(arg(2))) {

    // Proper breadcrumb when adding new term through vocabulary
    // overview page which is opened through term page.
    $breadcrumb = array();
    $options = array(
      'html' => TRUE,
    );
    $step = l(t('Home'), '<front>', $options);
    array_push($breadcrumb, $step);
    $path = array_reverse(taxonomy_tools_term_path(arg(2)));
    if (!empty($path)) {
      foreach ($path as $term) {
        $step = l($term['name'], 'taxonomy/term/' . $term['tid'] . '/overview', $options);
        array_push($breadcrumb, $step);
      }
    }
    drupal_set_breadcrumb($breadcrumb);
  }
}

/**
 * Returns the title of a taxonomy term.
 *
 * @param string $tid
 *   The taxonomy term title.
 */
function taxonomy_tools_term_title($tid) {
  $title = '';
  $query = db_select('taxonomy_term_data', 'foo');
  $query
    ->addField('foo', 'name');
  $query
    ->condition('foo.tid', $tid);
  $title = $query
    ->execute()
    ->fetchField();
  return $title;
}

/**
 * Implements hook_module_implements_alter().
 */
function taxonomy_tools_module_implements_alter(&$implementations, $hook) {
  if ($hook == 'menu_alter' && module_exists('views')) {

    // Implementation of hook_menu_alter() of this module is called last.
    $group = $implementations['taxonomy_tools'];
    unset($implementations['taxonomy_tools']);
    $implementations['taxonomy_tools'] = $group;
  }
}

Functions

Namesort descending Description
taxonomy_tools_admin_paths Implements hook_admin_paths().
taxonomy_tools_associated_nodes Fetches ID's of all nodes associated with specific taxonomy term.
taxonomy_tools_build_grants Builds node access grants for all user roles.
taxonomy_tools_form_taxonomy_form_term_alter Implements hook_form_FORM_ID_alter().
taxonomy_tools_menu Implements hook_menu().
taxonomy_tools_menu_alter Implements hook_menu_alter().
taxonomy_tools_module_implements_alter Implements hook_module_implements_alter().
taxonomy_tools_node_access_records Implements hook_node_access_records().
taxonomy_tools_node_grants Implements hook_node_grants().
taxonomy_tools_overview_access Controls access to Taxonomy Tools overview page.
taxonomy_tools_permission Implements hook_permission().
taxonomy_tools_preprocess_page Implements hook_preprocess_HOOK().
taxonomy_tools_rebuild_access_grants Builds new access grants for nodes associated with specific taxonomy term.
taxonomy_tools_role_permissions_all Collects all permissions (also inherited) specific to a user role.
taxonomy_tools_taxonomy_term_access Controls the access to the taxonomy term.
taxonomy_tools_taxonomy_term_delete Implements hook_taxonomy_term_delete().
taxonomy_tools_taxonomy_term_presave Implements hook_taxonomy_term_presave().
taxonomy_tools_taxonomy_term_update Implements hook_taxonomy_term_update().
taxonomy_tools_term_path Returns full parent hierarchy of taxonomy term.
taxonomy_tools_term_title Returns the title of a taxonomy term.
taxonomy_tools_theme Implements hook_theme().