You are here

taxonomy_deploy.module in Deploy - Content Staging 6

Deployment API which enables modules to deploy items between servers.

This module manages deployment-related issues for taxonomy terms and vocabularies.

File

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

/**
 * @file
 * Deployment API which enables modules to deploy items between servers.
 *
 * This module manages deployment-related issues for taxonomy terms and
 * vocabularies.
 */

/**
 * Implementation of hook_menu().
 */
function taxonomy_deploy_menu() {
  $items = array();
  $items['admin/content/taxonomy/deploy'] = array(
    'title' => t('Deploy'),
    'description' => t('Add a taxonomy vocabulary to a deployment plan.'),
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'taxonomy_vocabulary_deploy_add_form',
      5,
    ),
    'access arguments' => array(
      'add items to deployment plan',
    ),
    'file' => 'taxonomy_deploy.pages.inc',
    'type' => MENU_LOCAL_TASK,
  );
  return $items;
}

/**
 * Implementation of hook_node_deploy_check().
 *
 * This is the dependency checking hook for nodes, called when
 * a deployment has been requested that includes a node.
 *
 * @param $node
 *   The node object being deployed
 */
function taxonomy_deploy_node_deploy_check($node) {
  taxonomy_deploy_check_taxonomy($node->taxonomy);
}

/**
 * Manage the dependencies for an array of taxonomy terms.
 *
 * @param $taxonomy
 *   Array of taxonomy terms, as is included with a node in node_load.
 *   You can also pass a single tid here, and the function will manually
 *   get the term and put it into an array of the appropriate format.
 */
function taxonomy_deploy_check_taxonomy($taxonomy) {

  // If the param is not an array, assume it is a tid. Get the
  // term and put it in an array such that the rest of the function
  // can work as normal.
  if (!is_array($taxonomy)) {
    $term = taxonomy_get_term($taxonomy);
    $taxonomy = array();
    $taxonomy[] = $term;
  }

  // Retrieve the remote server information.
  $url = variable_get('deploy_server_url', '');
  $pid = variable_get('deploy_pid', 0);

  // As we get vids from the remote server, cache them here for future
  // use to reduce roundtrips.
  //
  // @todo
  //   Make this static so that it will maintain through the recursive function calls?
  //   Also, should probably cache the terms as well since its quite possible to be
  //   checking a single term multiple times because of use in parents / related terms.
  $vocabularies = array();

  // Every item in a node's taxonomy array is a term array.
  foreach ($taxonomy as $term) {

    // Convert to array if its not already
    $term = (array) $term;

    // We keep a unique list of all the vids we encounter, so we can
    // handle them next. Terms must be handled before vocabularies in order
    // to make sure that vocabularies always get deployed before their terms.
    if (!in_array($term['vid'], $vocabularies)) {
      $vocabularies[] = $term['vid'];
    }

    // If this term is already in the deployment plan then either
    // a) it was added by the user and will get checked down the line or
    // b) it was added through dependency checks and its already been
    // dealt with. So we just move on in this case.
    if (!deploy_item_is_in_plan($pid, 'taxonomy_term', $term['tid'])) {

      // Does this term exist on the remote server?
      $remote_key = deploy_get_remote_key(deploy_uuid_get_term_uuid($term['tid']), 'term_data');

      // If not we're going to add it to the deployment plan, with a weight
      // of min(weight) - 1.
      if (!$remote_key) {

        // Note that when adding a taxonomy term or vocabulary to a plan, we save 'taxonomy_term'
        // or 'taxonomy_vocabulary' in the $module parameter, despite the fact that this is all
        // coming from the 'taxonomy' module. It just makes sense since they are both dealt with
        // completely differently, and it prevents me from doing some hackery like storing a
        // serialized array in the $data field.
        deploy_add_to_plan($pid, 'taxonomy_term', 'taxonomy term: ' . $term['name'], $term['tid'], deploy_get_min_weight($pid) - 1, DEPLOY_TAXONOMY_TERM_GROUP_WEIGHT);

        // Having added this term to the plan, we now need to dependency check it
        // term's parents. This will continue recursively until we get to a root term.
        taxonomy_deploy_check_taxonomy(taxonomy_get_parents($term['tid']));

        // And its related terms. Same deal.
        taxonomy_deploy_check_taxonomy(taxonomy_get_related($term['tid']));
      }
    }
  }

  // Mow we need to do the same thing for the vocabularies.
  foreach ($vocabularies as $vid) {
    if (!deploy_item_is_in_plan($pid, 'taxonomy_vocabulary', $vid)) {

      // Does this vocabulary exist on the remote server?
      $remote_key = deploy_get_remote_key(deploy_uuid_get_vocabulary_uuid($vid), 'vocabulary');

      // If not we're going to add it to the deployment plan, with a weight
      // of min(weight) - 1.
      if (!$remote_key) {

        // I hate doing this load here but i have to bite the bullet so that the
        // deployment listing makes sense.
        $vocabulary = taxonomy_vocabulary_load($vid);
        deploy_add_to_plan($pid, 'taxonomy_vocabulary', 'taxonomy vocabulary: ' . $vocabulary->name, $vid, deploy_get_min_weight($pid) - 1, DEPLOY_TAXONOMY_VOCABULARY_GROUP_WEIGHT);
      }
    }
  }
}

/**
 * Implementation of hook_deploy().
 *
 * @param $tid
 *   Unique identifier for the term we're deploying.
 * @return
 *   The results of our deployment.
 *
 * For reference here is how the term needs to be formatted.
 *
 * [name] => Test
 * [description] => test
 * [parent] => Array (
 *   [247] => 247
 * )
 * [relations] => Array (
 *   [247] => 247
 *   [249] => 249
 * )
 * [synonyms] => test test
 * [weight] => 5
 * [vid] => 3
 * [tid] => 335
 *
 */
function taxonomy_term_deploy($tid) {

  // If there is no term with this tid then bail
  $term = taxonomy_get_term($tid);
  if (!$term) {
    return FALSE;
  }

  // Fill out term object with all the data it needs that taxonomy_get_term() doesn't provide
  // because it sucks.
  //
  // Yes it really is "parent" even though a term can have multiple parents.
  $term->parent = array();
  foreach (taxonomy_get_parents($tid) as $key => $parent) {

    // if the deployment weighting has done its job, then any parent terms
    // should have been deployed prior to this one. If not, then we need to fail out
    $remote_key = deploy_get_remote_key(deploy_uuid_get_term_uuid($key), 'term_data');
    $term->parent[$remote_key['tid']] = $remote_key['tid'];
  }

  // Same thing for related terms.
  $term->relations = array();
  foreach (taxonomy_get_related($tid) as $key => $relation) {

    // If you have circular related terms (which is possible) then one of them has
    // to come up before the other one, which means the other one will not exist
    // remotely. Therefore we can't really fail out when we encounter that situation,
    // therefore when we enounter that situation we just ignore that term and move on.
    // It all works out because when the related term comes up, it will link them
    // both at save anyways.
    $remote_key = deploy_get_remote_key(deploy_uuid_get_term_uuid($key), 'term_data');
    $term->relations[$remote_key['tid']] = $remote_key['tid'];
  }

  // Synch up with the remote vocabulary, which in theory can't not exist at this point.
  $remote_key = deploy_get_remote_key(deploy_uuid_get_vocabulary_uuid($term->vid), 'vocabulary');
  $term->vid = $remote_key['vid'];

  // Synonyms are much easier thankfully.
  $term->synonyms = implode("\n", taxonomy_get_synonyms($tid));

  // Mormally, like with users and nodes, the uuid is added to the object in the 'load'
  // op of their hook. Taxonomy terms and vocabs have no load op, so we have to
  // get it by hand.
  $uuid = deploy_uuid_get_term_uuid($tid);
  $remote_key = deploy_get_remote_key($uuid, 'term_data');
  $term->tid = isset($remote_key['tid']) ? $remote_key['tid'] : NULL;
  $term->uuid = $uuid;
  return deploy_send(array(
    'taxonomy.saveTerm',
  ), array(
    $term,
  ));
}

/**
 * Implementation of hook_deploy().
 *
 * @param $vid
 *   Unique identifier for the vocabulary we're deploying.
 * @return
 *   The results of our remote call.
 * @todo
 *   At dependency checking for associated content types to make sure
 *   they exist remotely and/or are added at push time through a call to
 *   hook_taxonomy_vocabulary_deploy_check().
 *
 * For reference here is a taxonomy vocabulary object.
 *
 * [name] => test
 * [description] => test
 * [help] => test
 * [nodes] => Array (
 *   [booklist] => booklist
 *   [essay] => essay
 *   [letter] => letter
 *  )
 * [tags] => 1
 * [multiple] => 1
 * [required] => 1
 * [weight] => 4
 * [hierarchy] => 0
 * [relations] => 1
 * [vid] => 5
 *
 */
function taxonomy_vocabulary_deploy($vid) {

  // If the vocabulary isn't there then bail.
  $vocabulary = taxonomy_vocabulary_load($vid);
  if (!$vocabulary) {
    return FALSE;
  }

  // Normally, like with users and nodes, the uuid is added to the object in the 'load'
  // op of their hook. Taxonomy terms and vocabs have no load op, so we have to
  // get it by hand.
  $uuid = deploy_uuid_get_vocabulary_uuid($vid);
  $remote_key = deploy_get_remote_key($uuid, 'vocabulary');
  $vocabulary->vid = isset($remote_key['vid']) ? $remote_key['vid'] : NULL;
  $vocabulary->uuid = $uuid;
  return deploy_send(array(
    'taxonomy.saveVocabulary',
  ), array(
    $vocabulary,
  ));
}

/**
 * Implementation of hook_deploy_check_cleanup().
 *
 * After dependencies are sorted out, but before items get pushed, we need to
 * arrange the weights for our taxonomy terms such that parent terms are always
 * pushed out before child terms. This is more difficult than one might think.
 * I originally intended on having every term add itself as min(weight) - 1, but
 * this doesn't always work out. For instance, a term will add its parent at a lower
 * weight, but then a later term with the same parent might add itself at an even
 * lower weight. What we need is to get all the terms and their dependent terms
 * added, and then sort the weighting out. This is what we do here.
 */
function taxonomy_deploy_deploy_check_cleanup($pid) {
  if (db_result(db_query("SELECT DISTINCT module FROM {deploy_plan_items} WHERE pid = %d AND module = 'taxonomy_term'", $pid))) {

    // All that matters is that within a given vocabulary, all items are weighted
    // such that the parents are of a lower weight than the children. The actual
    // weights don't matter, and they don't need to be unique. This is the fastest
    // thing I could figure out to meet that need.
    // Get all the vids we are deploying terms for/
    $vocabularies = db_query("SELECT DISTINCT vid FROM {term_data} td INNER JOIN {deploy_plan_items} dpi ON td.tid = dpi.data WHERE dpi.module = 'taxonomy_term'");
    while ($vid = db_result($vocabularies)) {

      // Get the tree for this vid, which is a sorted array of taxonomy term objects.
      $tree = taxonomy_get_tree($vid);

      // In order for this to be useful to us we strip it down to just an array
      // of tids, still properly sorted. This involves re-iterating the tree but
      // I don't see what to do about that.
      foreach ($tree as &$value) {
        $value = $value->tid;
      }

      // Now get all the tids we're deploying from this vocabulary, and get them in
      // another array.
      $items = array();
      $tids = db_query("SELECT dpi.data FROM {deploy_plan_items} dpi INNER JOIN {term_data} td ON dpi.data = td.tid WHERE dpi.module = 'taxonomy_term' AND td.vid = %d", $vid);
      while ($item = db_result($tids)) {
        $items[] = $item;
      }

      // What we have now are two arrays - one sorted array of all tids in a vocabulary, and one
      // unsorted array of all tids in our plan. what we want is one sorted array of all tids in
      // the plan. Thank you array_intersect(). NOTE: the order of the parameters here matters
      // immensely.
      $items = array_intersect($tree, $items);

      // Array_intersect() maintains the original array's keys, which we use as our
      // weights. Again, I would love to know a better way to update these then running
      // an update query on them all individually.
      foreach ($items as $key => $item) {
        db_query("UPDATE {deploy_plan_items} SET weight = %d WHERE data = %d and module = 'taxonomy_term'", $key, $item);
      }
    }
  }
}

Functions

Namesort descending Description
taxonomy_deploy_check_taxonomy Manage the dependencies for an array of taxonomy terms.
taxonomy_deploy_deploy_check_cleanup Implementation of hook_deploy_check_cleanup().
taxonomy_deploy_menu Implementation of hook_menu().
taxonomy_deploy_node_deploy_check Implementation of hook_node_deploy_check().
taxonomy_term_deploy Implementation of hook_deploy().
taxonomy_vocabulary_deploy Implementation of hook_deploy().