You are here

smartqueue.module in Nodequeue 7.2

File

modules/smartqueue/smartqueue.module
View source
<?php

/**
 * Implements hook_nodequeue_info().
 */
function smartqueue_nodequeue_info() {
  return array(
    'smartqueue_taxonomy' => array(
      'title' => t('Taxonomy queue'),
      'description' => t('Each combination of taxonomy terms from the selected vocabularies will have their own unique subqueue. You can place nodes into any of these subqueues based on which terms that node has been tagged with. Using subqueues with very large, or too many taxonomies may degrade performance.'),
    ),
  );
}

/**
 * Implements hook_menu().
 */
function smartqueue_menu() {

  // Term paths.
  $items['taxonomy/term/%taxonomy_term/queue'] = array(
    'title' => 'Queues',
    'page callback' => 'smartqueue_term_tab',
    'page arguments' => array(
      2,
    ),
    'access callback' => 'smartqueue_term_tab_access',
    'access arguments' => array(
      2,
    ),
    'file' => 'smartqueue.admin.inc',
    'weight' => 15,
    'type' => MENU_LOCAL_TASK,
  );
  return $items;
}

/**
 * Implements hook_admin_paths().
 */
function smartqueue_admin_paths() {
  $paths = array(
    'taxonomy/term/*/queue' => TRUE,
  );
  return $paths;
}

/**
 * Access callback for smartqueue_term_tab().
 *
 * @param $term
 *   The fully loaded taxonomy term that will be loaded by the hook_menu.
 *
 * @return boolean
 */
function smartqueue_term_tab_access($term) {
  if (!variable_get('smartqueue_use_tab', TRUE) || !user_access('manipulate queues')) {

    // For performance reasons: If the menu tab is disabled or the user can't
    // manipulate queues, there is no reason to run the rest of these queries.
    return FALSE;
  }

  // Check that user has access.
  if (_nodequeue_access_admin_or_manipulate()) {

    // Check that there are queues.
    $subqueues = smartqueue_get_subqueues_by_term($term);
    if (empty($subqueues)) {
      return FALSE;
    }
    return TRUE;
  }
  return FALSE;
}

/**
 * Fetches info about all subqueues for a term.
 *
 * @param $term
 *   The fully loaded taxonomy term that will be loaded by the hook_menu.
 *
 * @return array
 *   Array of all subqueues for this term keyed by Queue ID.
 */
function smartqueue_get_subqueues_by_term($term) {

  // SELECT nq.reference AS reference, sq.reference AS sqref, sq.sqid as id FROM nodequeue_queue nq INNER JOIN nodequeue_subqueue sq ON nq.qid = sq.qid WHERE nq.owner = 'smartqueue_taxonomy' AND sq.reference = 717;
  $result = db_query("SELECT\n      nq.title as title,\n      nq.qid as queue_id,\n      nq.reference AS field_name,\n      sq.reference AS subqueue_ref,\n      sq.sqid as subqueue_id\n    FROM {nodequeue_queue} nq\n    INNER JOIN {nodequeue_subqueue} sq ON nq.qid = sq.qid\n    WHERE nq.owner = 'smartqueue_taxonomy'\n    AND (sq.reference = :tid\n      OR sq.reference LIKE :like2\n      OR sq.reference LIKE :like3\n      OR sq.reference LIKE :like4)", array(
    ':tid' => $term->tid,
    ':like2' => '%-' . $term->tid,
    ':like3' => $term->tid . '-%',
    ':like4' => '%-' . $term->tid . '-%',
  ))
    ->fetchAll();
  $subqueues = array();
  foreach ($result as $row) {
    $subqueues[$row->queue_id] = array(
      'title' => $row->title,
      'qid' => $row->queue_id,
      'subqueue_id' => $row->subqueue_id,
      'path' => 'admin/structure/nodequeue/' . $row->queue_id . '/view/' . $row->subqueue_id,
    );
  }
  return $subqueues;
}

/**
 * Implements hook_nodequeue_form().
 */
function smartqueue_taxonomy_nodequeue_form($queue, &$form) {

  // Load data about taxonomy_term_reference fields.
  $options = array();
  $labels = array();
  $fields = field_info_fields();
  foreach ($fields as $field_name => $field_info) {
    if ($field_info['type'] == 'taxonomy_term_reference') {
      $vocabulary_name = $field_info['settings']['allowed_values'][0]['vocabulary'];
      $vocab = taxonomy_vocabulary_machine_name_load($vocabulary_name);
      $vocabulary = $vocab->name;
      foreach ($field_info['bundles'] as $entity_type => $bundles) {
        foreach ($bundles as $bundle) {
          $instance = field_info_instance($entity_type, $field_name, $bundle);
          if (isset($labels[$instance['label']])) {
            $count = $labels[$instance['label']];
            $count++;
          }
          else {
            $count = 1;
          }
          $labels[$field_name][$instance['label']] = $count;
        }
      }
      asort($labels[$field_name]);
      $labels_copy = $labels[$field_name];
      $labels_inverted = array_flip($labels_copy);
      $label = array_pop($labels_inverted);
      $options[$field_name] = t('@label (%field-name), selecting terms from vocabulary: @vocabulary.', array(
        '@label' => $label,
        '%field-name' => $field_name,
        '@vocabulary' => $vocabulary,
      ));
    }
  }
  $form['placeholder']['taxonomy_fields'] = array(
    '#type' => 'checkboxes',
    '#title' => t('Taxonomy term reference fields'),
    '#description' => t('Each unique combination of terms from all of these fields will have a subqueue.'),
    '#options' => $options,
  );
  $form['placeholder']['use_parents'] = array(
    '#type' => 'checkbox',
    '#title' => t('Assume parent term for hierarchical vocabs'),
    '#description' => t("Instead of using the node's terms for nodequeue assignment use the top-most parents of a term. This has no effect if there in non-hierarchical vocabularies."),
    '#default_value' => isset($queue->use_parents) ? $queue->use_parents : 0,
  );
  $form['placeholder']['use_parents_all'] = array(
    '#type' => 'checkbox',
    '#title' => t('Assume all ancestor terms for hierarchical vocabs'),
    '#description' => t("Instead of using just the node's terms for nodequeue assignment use all the ancestors of a term in addition. This has no effect if there in non hierarchical vocabularies."),
    '#default_value' => isset($queue->use_parents_all) ? $queue->use_parents_all : 0,
  );
  $form['subqueue_title'] = array(
    '#type' => 'textfield',
    '#title' => t('Subqueue title'),
    '#default_value' => $queue->subqueue_title,
    '#size' => 50,
    '#maxlength' => 64,
    '#description' => t('Use <em>%subqueue</em> to embed the subqueue title. This is used to distinguish multiple nodequeues with subqueues from each other.'),
  );

  // Change reference field into our saved format.
  if (!empty($queue->qid)) {
    $form['placeholder']['taxonomy_fields']['#disabled'] = TRUE;
    $form['placeholder']['taxonomy_fields']['#default_value'] = explode('-', $queue->reference);
  }
}

/**
 * Implements hook_nodequeue_form_validate().
 */
function smartqueue_taxonomy_nodequeue_form_validate($queue, &$form_state, &$form) {
  $field_names = array_keys(array_filter($form_state['values']['taxonomy_fields']));
  if (empty($field_names)) {
    form_error($form['placeholder']['taxonomy_fields'], t('You must select at least one field.'));
  }

  // Convert checkboxes to our reference data format.
  form_set_value($form['reference'], implode('-', $field_names), $form_state);
}

/**
 * Implements hook_nodequeue_form_submit_finish().
 */
function smartqueue_taxonomy_nodequeue_form_submit_finish($queue, $form_state) {

  // Check if queue already exists.
  $qid = db_select('smartqueue', 's')
    ->fields('s', array(
    'qid',
  ))
    ->condition('qid', $queue->qid)
    ->execute()
    ->fetchField();
  if ($qid) {

    // Update existing queue.
    db_update('smartqueue')
      ->fields(array(
      'use_parents' => $form_state['values']['use_parents'],
      'use_parents_all' => $form_state['values']['use_parents_all'],
    ))
      ->condition('qid', $queue->qid)
      ->execute();
  }
  else {

    // Insert new queue.
    db_insert('smartqueue')
      ->fields(array(
      'qid' => $queue->qid,
      'use_parents' => $form_state['values']['use_parents'],
      'use_parents_all' => $form_state['values']['use_parents_all'],
    ))
      ->execute();
  }
}

/**
 * Implements hook_nodequeue_subqueues().
 *
 * Returns list of references for subqueues that can host a given node.
 */
function smartqueue_taxonomy_nodequeue_subqueues(&$queue, $node) {
  $field_names = array();

  // Check if at least one supported field exists in node and load
  // selected tids.
  foreach (explode('-', $queue->reference) as $field_name) {

    // Save tids.
    if ($field_values = field_get_items('node', $node, $field_name)) {
      $field_names[$field_name] = array();
      foreach ($field_values as $field_value) {
        $field_names[$field_name][] = $field_value['tid'];
      }
    }
  }
  if (!empty($field_names) && !empty($queue->use_parents)) {

    // Replace taxonomy IDs with their parents'.
    foreach ($field_names as $field_name => &$tids) {
      $tids = smartqueue_taxonomy_get_parents($tids);
    }
  }
  if (!empty($field_names) && !empty($queue->use_parents_all)) {

    // Replace taxonomy IDs with all their parents'.
    foreach ($field_names as $field_name => &$tids) {
      $tids = smartqueue_taxonomy_get_children($tids);
    }
  }

  // Forbid NO terms being set, but allow various non-terms to be set.
  $empty = TRUE;
  foreach ($field_names as $field_name => $tids) {
    if (!empty($tids)) {
      $empty = FALSE;
    }
    if (!count($field_names[$field_name])) {
      $field_names[$field_name][] = 0;
    }
  }
  if ($empty) {
    return;
  }

  // Build reference strings for all subqueues.
  $references = smartqueue_build_string(array_filter($field_names));

  // We're returning an array of references for efficiency, but we also have
  // to check to see if the references we've generated exist. If they don't,
  // we have to create them.
  $exists = array();
  $subqueues = nodequeue_load_subqueues_by_reference(array(
    $queue->qid => $references,
  ));
  foreach ($subqueues as $subqueue) {
    $exists[$subqueue->reference] = TRUE;
  }

  // Create subqueues if needed.
  foreach ($references as $reference) {
    if (empty($exists[$reference])) {
      nodequeue_add_subqueue($queue, smartqueue_taxonomy_nodequeue_subqueue_title($queue, $reference), $reference);
    }
  }
  return $references;
}

/**
 * Implements hook_nodequeue_alter().
 */
function smartqueue_nodequeue_alter(&$data, $type) {
  switch ($type) {
    case 'load_queues':
      if (!empty($data)) {
        $qids = array_keys($data);
        $result = db_query("SELECT * FROM {smartqueue} WHERE qid IN (:qids)", array(
          ':qids' => $qids,
        ));
        foreach ($result as $queue) {
          $data[$queue->qid]->use_parents = $queue->use_parents;
          $data[$queue->qid]->use_parents_all = $queue->use_parents_all;
        }
      }
      break;
  }
}

/**
 * Implements hook_nodequeue_delete().
 */
function smartqueue_nodequeue_delete($qid) {

  // Remove data from the smartqueue table.
  db_delete('smartqueue')
    ->condition('qid', $qid)
    ->execute();
}

/**
 * Build an array of strings that represents all of the possible term
 * combinations.
 */
function smartqueue_build_string($arrays) {
  $array = array_shift($arrays);
  $term = '';
  if (empty($arrays)) {
    return $array;
  }
  $substrings = smartqueue_build_string($arrays);
  $strings = array();
  foreach ($array as $term) {
    foreach ($substrings as $string) {
      $strings[] = "{$term}-{$string}";
    }
  }
  return $strings;
}

/**
 * Form title for a new taxonomy subqueue.
 *
 * @param $queue Queue object.
 * @param $reference Subqueue reference string (tids imploded with '-').
 *
 * @return string
 */
function smartqueue_taxonomy_nodequeue_subqueue_title($queue, $reference) {
  $tids = explode('-', $reference);
  $titles = array();
  foreach ($tids as $tid) {

    // $tid can be 0, specifically meaning this term is unset.
    if ($tid) {
      $titles[$tid] = taxonomy_term_load($tid)->name;
    }
  }

  // Create name using names of all term names. This could be
  // done better, but is OK for now.
  return implode('-', $titles);
}

/**
 * Implements hook_taxonomy_term_update().
 *
 * Updates subqueue title if term name changes.
 */
function smartqueue_taxonomy_term_update($term) {

  // Find subqueues that contain this term.
  $result = db_query("SELECT nq.reference AS reference, sq.reference AS sqref, sq.sqid\n    FROM {nodequeue_queue} nq\n    INNER JOIN {nodequeue_subqueue} sq ON nq.qid = sq.qid\n    WHERE nq.owner = 'smartqueue_taxonomy'\n    AND (sq.reference = ?\n      OR sq.reference LIKE ?\n      OR sq.reference LIKE ?\n      OR sq.reference LIKE ?)", array(
    $term->tid,
    '%-' . $term->tid,
    $term->tid . '-%',
    '%-' . $term->tid . '-%',
  ))
    ->fetchAll();
  foreach ($result as $row) {

    // Note that $row already contains the needed $row->reference.
    $title = smartqueue_taxonomy_nodequeue_subqueue_title($row, $row->sqref);

    // Change the title of the subqueue in the db.
    nodequeue_subqueue_update_title($row->sqid, $title);
  }
}

/**
 * Implements hook_taxonomy_term_delete().
 *
 * Deletes subqueue if term is removed
 */
function smartqueue_taxonomy_term_delete($term) {

  // Find subqueues that contain this term.
  $result = db_query("SELECT sq.sqid FROM {nodequeue_subqueue} sq\n    INNER JOIN {nodequeue_queue} nq ON sq.qid = nq.qid\n    WHERE nq.owner = 'smartqueue_taxonomy'\n    AND (sq.reference = ?\n      OR sq.reference LIKE ?\n      OR sq.reference LIKE ?\n      OR sq.reference LIKE ?)", array(
    $term->tid,
    '%-' . $term->tid,
    $term->tid . '-%',
    '%-' . $term->tid . '-%',
  ))
    ->fetchAll();
  foreach ($result as $row) {
    nodequeue_remove_subqueue($row->sqid);
  }
}

/**
 * Get the top-level parents of the given taxonomy terms.
 *
 * @param $tids, an array of taxonomy IDs
 *
 * @return an array of taxonomy IDs
 */
function smartqueue_taxonomy_get_parents($tids) {
  if ($tids) {
    $top_level_tids = array();
    foreach ($tids as $tid) {
      $parents = taxonomy_get_parents_all($tid);
      $parent = array_pop($parents);
      $top_level_tids[] = $parent->tid;
    }
    return array_unique($top_level_tids);
  }
  else {
    return array();
  }
}

/**
 * Implements hook_nodequeue_autocomplete().
 */
function smartqueue_taxonomy_nodequeue_autocomplete($queue, $subqueue, $string, &$query) {
  $matches = array();

  // Filtering by the reference
  $query
    ->join('taxonomy_index', 'ti', 'n.nid = ti.nid');
  $query
    ->condition('ti.tid', $subqueue->reference, '=');
  $query
    ->addTag('i18n_select');
  $result = $query
    ->execute();
  foreach ($result as $node) {
    $id = nodequeue_get_content_id($queue, $node);
    $matches[$node->nid] = check_plain($node->title) . " [nid: {$id}]";
  }
  return $matches;
}
function smartqueue_taxonomy_get_children($tids) {
  if ($tids) {
    $parent_tids = array();
    foreach ($tids as $tid) {
      $parent_terms = taxonomy_get_parents_all($tid);
      foreach ($parent_terms as $term) {
        $parent_tids[] = $term->tid;
      }
    }
    return $parent_tids;
  }
  else {
    return array();
  }
}