You are here

taxonomy_revision.module in Taxonomy revision 7

This is the main module file for the Taxonomy revision module.

File

taxonomy_revision.module
View source
<?php

/**
 * @file
 * This is the main module file for the Taxonomy revision module.
 */

/**
 * Implements hook_views_api().
 */
function taxonomy_revision_views_api() {
  return array(
    'api' => 3,
    'path' => drupal_get_path('module', 'taxonomy_revision') . '/views',
  );
}

/**
 * Access callback for taxonomy revision.
 */
function _taxonomy_revision_access($term, $perm) {
  if (!is_array($perm)) {
    $perm = array(
      $perm,
    );
  }
  $access = FALSE;
  foreach ($perm as $permission) {
    $access = user_access($permission) || $access;
    if ($access) {
      return $access;
    }
  }
  return $access;
}

/**
 * Implements hook_permission().
 */
function taxonomy_revision_permission() {
  return array(
    'view taxonomy term revisions' => array(
      'title' => t('View revisions'),
    ),
    'revert taxonomy term revisions' => array(
      'title' => t('Revert revisions'),
    ),
    'delete taxonomy term revisions' => array(
      'title' => t('Delete revisions'),
    ),
    'choose taxonomy term revisions' => array(
      'title' => t('Choose to create revisions'),
      'description' => t('Allow users to create taxonomy term revisions'),
    ),
  );
}

/**
 * Implements hook_menu().
 */
function taxonomy_revision_menu() {
  $items['taxonomy/term/%taxonomy_term/revisions'] = array(
    'title' => 'Revisions',
    'page callback' => 'taxonomy_revision_overview',
    'page arguments' => array(
      2,
    ),
    'access callback' => '_taxonomy_revision_access',
    'access arguments' => array(
      1,
      array(
        'view taxonomy term revisions',
        'view own taxonomy term revisions',
      ),
    ),
    'weight' => 20,
    'type' => MENU_LOCAL_TASK,
    'file' => 'taxonomy_revision.pages.inc',
  );
  $items['taxonomy/term/%taxonomy_term/revisions/view/%/%'] = array(
    'title' => 'Compare revisions',
    'page callback' => 'taxonomy_revision_diffs_show',
    'page arguments' => array(
      2,
      5,
      6,
    ),
    'access arguments' => array(
      'view taxonomy term revisions',
    ),
    'file' => 'taxonomy_revision.pages.inc',
  );
  $items['taxonomy/term/%taxonomy_revision_term/revisions/%/revert'] = array(
    'title' => 'Revert to earlier revision',
    'load arguments' => array(
      4,
    ),
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'taxonomy_revision_revert_confirm',
      2,
      4,
    ),
    'access callback' => '_taxonomy_revision_access',
    'access arguments' => array(
      1,
      array(
        'revert taxonomy term revisions',
        'revert own taxonomy term revisions',
      ),
    ),
    'file' => 'taxonomy_revision.pages.inc',
  );
  $items['taxonomy/term/%taxonomy_revision_term/revisions/%/delete'] = array(
    'title' => 'Delete earlier revision',
    'load arguments' => array(
      4,
    ),
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'taxonomy_revision_delete_confirm',
      2,
      4,
    ),
    'access callback' => '_taxonomy_revision_access',
    'access arguments' => array(
      1,
      array(
        'delete taxonomy term revisions',
        'delete own taxonomy term revisions',
      ),
    ),
    'file' => 'taxonomy_revision.pages.inc',
  );
  $items['taxonomy/term/%taxonomy_revision_term/revisions/%/view'] = array(
    'title' => 'View revision',
    'load arguments' => array(
      4,
    ),
    'page callback' => 'taxonomy_revision_show',
    'page arguments' => array(
      2,
      TRUE,
    ),
    'access callback' => '_taxonomy_revision_access',
    'access arguments' => array(
      1,
      array(
        'view taxonomy term revisions',
      ),
    ),
  );
  return $items;
}

/**
 * Implements hook_theme().
 */
function taxonomy_revision_theme() {
  return array(
    'taxonomy_revision_taxonomy_revisions' => array(
      'render element' => 'form',
      'file' => 'taxonomy_revision.theme.inc',
    ),
  );
}

/**
 * Returns a list of all the existing revision numbers.
 *
 * @param $term
 *   The term entity.
 *
 * @return
 *   An associative array keyed by term revision number.
 */
function taxonomy_revision_list($term) {
  $revisions = array();
  $result = db_query('SELECT r.revision_id, r.name, r.log, r.uid, t.revision_id AS current_revision_id, r.timestamp, u.name FROM {taxonomy_term_data_revision} r LEFT JOIN {taxonomy_term_data} t ON t.revision_id = r.revision_id INNER JOIN {users} u ON u.uid = r.uid WHERE r.tid = :tid ORDER BY r.revision_id DESC', array(
    ':tid' => $term->tid,
  ));
  foreach ($result as $revision) {
    $revisions[$revision->revision_id] = $revision;
  }
  return $revisions;
}

/**
 * Implements hook_entity_diff().
 *
 * This function compares core taxonomy term properties. This is currently limited to:
 *   - name: The title of term.
 *   - description: The description of the term.
 */
function taxonomy_revision_entity_diff($old_term, $new_term, $context) {
  $result = array();
  if ($context['entity_type'] == 'taxonomy_term') {

    // The wrappers are created for each field even though there are no
    // differences for the title (name) for example.
    $taxonomy_term_properties = entity_get_property_info('taxonomy_term');
    $new_term_wrapper = entity_metadata_wrapper('taxonomy_term', $new_term);
    $old_term_wrapper = entity_metadata_wrapper('taxonomy_term', $old_term);

    // We unset the entity id property because we don't want to diff it.
    unset($taxonomy_term_properties['properties']['tid']);
    $old_term_rendered = taxonomy_term_view($old_term);
    $new_term_rendered = taxonomy_term_view($new_term);
    foreach ($taxonomy_term_properties['properties'] as $property_name => $property) {
      if (empty($property['vocabulary'])) {
      }
      if (empty($property['computed'])) {

        // Not taking into much consideration how the settings for the node are made,
        // we create our settings similarly for each of our fields.
        $result[$property_name] = array(
          '#name' => $property['label'],
          '#states' => array(),
          '#weight' => -5,
          '#settings' => array(
            'compare_format' => 0,
            'markdown' => 'drupal_html_to_text',
            'line_counter' => '',
            'compare_summary' => 0,
            'show_header' => 1,
          ),
        );
        foreach ($context['states'] as $state) {

          // We need use the raw() not the value() function (Entity Wrapper).
          // The value() includes html tags which we don't need.
          // This however doesn't recognize some values correctly, but those values are not relevant for our task.
          $old_property = $old_term_wrapper->{$property_name}
            ->raw();
          $new_property = $new_term_wrapper->{$property_name}
            ->raw();

          // The Entity Wrapper generates objects.
          if (!is_object($old_property) && !is_object($new_property)) {
            switch ($state) {
              case 'rendered':
                $result[$property_name]['#states'][$state] = array(
                  '#old' => isset($old_term_rendered[$property_name]) ? drupal_render($old_term_rendered[$property_name]) : $old_term_rendered->{$property_name}
                    ->raw(),
                  '#new' => isset($new_term_rendered[$property_name]) ? drupal_render($new_term_rendered[$property_name]) : $new_term_rendered->{$property_name}
                    ->raw(),
                );
                break;

              // In case of raw_plain (taxonomy_revision only), for the node_revision there is only raw,
              // We create our array efficiently for our diff (module) functions to identify the parameters correctly.
              case 'raw_plain':
                $result[$property_name]['#states']['raw'] = array(
                  '#old' => $old_property,
                  '#new' => $new_property,
                );
                break;

              // Anything out of the ordinary will be considered : raw.
              default:
                $result[$property_name]['#states'][$state] = array(
                  '#old' => $old_property,
                  '#new' => $new_property,
                );
                break;
            }
          }
        }
      }
    }
  }
  return $result;
}

/**
 * Implements hook_entity_info_alter().
 */
function taxonomy_revision_entity_info_alter(&$entity_info) {
  $entity_info['taxonomy_term']['revision table'] = 'taxonomy_term_data_revision';
  $entity_info['taxonomy_term']['entity keys']['revision'] = 'revision_id';
  require_once dirname(__FILE__) . '/taxonomy_revision.install';
  $schema = taxonomy_revision_schema();
  foreach ($schema['taxonomy_term_data_revision']['fields'] as $column_name => $column) {
    $entity_info['taxonomy_term']['schema_fields_sql']['revision table'][] = $column_name;
  }
}

/**
 * Implements hook_field_attach_form().
 */
function taxonomy_revision_field_attach_form($entity_type, $entity, &$form, &$form_state, $langcode) {
  if ($entity_type == 'taxonomy_term' && !empty($entity->tid)) {
    $form['revision_information'] = array(
      '#type' => 'fieldset',
      '#title' => t('Revision information'),
      '#collapsible' => TRUE,
      '#weight' => 20,
      '#access' => user_access('choose taxonomy term revisions'),
    );
    if (!_taxonomy_revision_enabled_by_default($entity->vid)) {
      $form['revision_information']['revision'] = array(
        '#type' => 'checkbox',
        '#title' => t('Create new revision'),
      );
    }
    else {
      $form['revision_information']['revision'] = array(
        '#type' => 'markup',
        '#markup' => t('New revision will be created automatically.'),
      );
    }
    $form['revision_information']['log'] = array(
      '#type' => 'textarea',
      '#title' => t('Revision log message'),
      '#rows' => 4,
      '#description' => t('Provide an explanation of the changes you are making. This will help other authors understand your motivations.'),
    );
    if (!_taxonomy_revision_enabled_by_default($entity->vid)) {
      $form['revision_information']['log']['#states'] = array(
        'invisible' => array(
          'input[name="revision"]' => array(
            'checked' => FALSE,
          ),
        ),
      );
    }
    $form['#entity_builders'][] = 'taxonomy_revision_taxonomy_form_term_submit_build_term';
  }
}

/**
 * Entity builder for the taxonomy term entity.
 *
 * @see taxonomy_revision_field_attach_form()
 */
function taxonomy_revision_taxonomy_form_term_submit_build_term($entity_type, $entity, $form, $form_state) {
  switch ($entity_type) {
    case 'taxonomy_term':
      if (!empty($form_state['values']['revision']) || _taxonomy_revision_enabled_by_default($entity->vid)) {
        $entity->revision = TRUE;
        $entity->log = check_plain($form_state['values']['log']);
      }
      break;
  }
}

/**
 * Implements hook_taxonomy_term_presave().
 */
function taxonomy_revision_taxonomy_term_presave($term) {

  // We need to force a new revision creation if the term is new.
  if (empty($term->tid)) {
    $term->revision = TRUE;
  }
}

/**
 * Saves the term.
 *
 * @param stdClass $term
 *  Term to be saved.
 */
function _taxonomy_revision_taxonomy_term_save_revision($term) {

  // Set basic revision properties on the term before saving.
  global $user;
  $term->uid = $user->uid;
  $term->timestamp = REQUEST_TIME;

  // Save over an existing revision if requested.
  if (isset($term->revision_id) && $term->revision_id && empty($term->revision)) {
    drupal_write_record('taxonomy_term_data_revision', $term);
  }
  else {
    unset($term->revision_id);
    drupal_write_record('taxonomy_term_data_revision', $term);
    drupal_write_record('taxonomy_term_data', $term, 'tid');
  }
}

/**
 * Implements hook_field_storage_pre_insert().
 */
function taxonomy_revision_field_storage_pre_insert($entity_type, $entity, &$skip_fields) {
  if ($entity_type == 'taxonomy_term') {

    // Save the term in the revision table. This needs to be done in
    // hook_field_storage_pre_insert() because it must run after
    // taxonomy_term_save() has written data to the {taxonomy_term_data} table
    // (meaning that the entity object now has a term ID attached to it) but
    // before field data has been written to the database (so we still have
    // time to attach the correct revision ID to the entity object before the
    // field API uses it).
    _taxonomy_revision_taxonomy_term_save_revision($entity);
  }
}

/**
 * Implements hook_field_storage_pre_update().
 */
function taxonomy_revision_field_storage_pre_update($entity_type, $entity, &$skip_fields) {
  if ($entity_type == 'taxonomy_term') {

    // Update the term in the revision table. This needs to be done in
    // hook_field_storage_pre_update() for similar reasons as explained in
    // taxonomy_revision_field_storage_pre_insert() for a new term.
    _taxonomy_revision_taxonomy_term_save_revision($entity);
  }
}

/**
 * Implements hook_taxonomy_term_delete().
 */
function taxonomy_revision_taxonomy_term_delete($term) {
  db_delete('taxonomy_term_data_revision')
    ->condition('tid', $term->tid)
    ->execute();
}

/**
 * Deletes a taxonomy term revision.
 *
 * @param taxonomy_term $revision
 *  The revision to delete.
 */
function taxonomy_revision_delete($revision) {
  db_delete('taxonomy_term_data_revision')
    ->condition('tid', $revision->tid)
    ->condition('revision_id', $revision->revision_id)
    ->execute();
  module_invoke_all('taxonomy_revision_delete', $revision);
  field_attach_delete_revision('taxonomy_revision', $revision);
  return TRUE;
}

/**
 * Loads a taxonomy term revision.
 *
 * @param $tid
 *  Term id of the term to be loaded.
 * @param $revision_id
 *  Revision id of the term to be loaded.
 * @return mixed
 */
function taxonomy_revision_term_load($tid, $revision_id) {
  $terms = entity_load('taxonomy_term', array(
    $tid,
  ), array(
    'revision_id' => $revision_id,
  ));
  return reset($terms);
}

/**
 * Generate an array which displays a taxonomy term detail page.
 *
 * @param $term
 *   A taxonomy term object.
 * @param $message
 *   A flag which sets a page title relevant to the revision being viewed.
 * @return
 *   A $page element suitable for use by drupal_render().
 */
function taxonomy_revision_show($term, $message = FALSE) {
  if ($message) {
    drupal_set_title(t('Revision of %name from %date', array(
      '%name' => $term->name,
      '%date' => format_date($term->timestamp),
    )), PASS_THROUGH);
  }

  // Emulates non existing taxonomy_term_view_multiple().
  // See http://drupal.org/node/708730.
  // See node_show().
  $terms = array(
    $term->tid => $term,
  );
  $view_mode = 'full';
  field_attach_prepare_view('taxonomy_term', $terms, $view_mode);
  entity_prepare_view('taxonomy_term', $terms);
  $build = array();
  $weight = 0;
  foreach ($terms as $term) {
    $build['terms'][$term->tid] = taxonomy_term_view($term, $view_mode);
    $build['terms'][$term->tid]['#weight'] = $weight;
    $weight++;
  }
  $build['terms']['#sorted'] = TRUE;
  return $build;
}

/**
 * Implements hook_form_FORM_ID_alter().
 */
function taxonomy_revision_form_taxonomy_form_vocabulary_alter(&$form, &$form_state, $form_id) {
  if (empty($form_state['confirm_delete'])) {
    $form['revision_information'] = array(
      '#type' => 'fieldset',
      '#title' => t('Revision information'),
      '#collapsible' => TRUE,
      '#weight' => 20,
      '#access' => user_access('choose taxonomy term revisions'),
    );
    if (!empty($form_state['vocabulary'])) {
      $revision_by_default = _taxonomy_revision_enabled_by_default($form_state['vocabulary']->vid);
    }
    else {
      $revision_by_default = FALSE;
    }
    $form['revision_information']['revision_by_default'] = array(
      '#type' => 'checkbox',
      '#title' => t('Create new revision by default'),
      '#default_value' => $revision_by_default,
    );
    $form['#submit'][] = 'taxonomy_revision_taxonomy_form_vocabulary_submit';
  }
}

/**
 * Submit handler for the taxonomy_form_vocabulary form.
 * @see taxonomy_revision_form_taxonomy_form_vocabulary_alter().
 */
function taxonomy_revision_taxonomy_form_vocabulary_submit($form, &$form_state) {
  if (!empty($form_state['vid'])) {
    _taxonomy_revision_enabled_by_default($form_state['vid'], !empty($form_state['values']['revision_by_default']));
  }
}

/**
 * Implements hook_form_FORM_ID_alter().
 */
function taxonomy_revision_form_taxonomy_overview_terms_alter(&$form, &$form_state) {
  $form['#submit'][] = 'taxonomy_revision_taxonomy_overview_terms_submit';
}

/**
 * Custom submit handler for the taxonomy terms overview form.
 */
function taxonomy_revision_taxonomy_overview_terms_submit($form, &$form_state) {

  // The standard taxonomy_overview_terms_submit() submit handler writes the
  // modified term weights directly to the {taxonomy_term_data} table. Sync
  // them here so they apply to all revisions also.
  $term_weights = db_query('SELECT tid, weight FROM {taxonomy_term_data} WHERE vid = :vid', array(
    ':vid' => $form['#vocabulary']->vid,
  ))
    ->fetchAllKeyed();
  foreach ($term_weights as $tid => $weight) {
    db_update('taxonomy_term_data_revision')
      ->fields(array(
      'weight' => $weight,
    ))
      ->condition('tid', $tid)
      ->execute();
  }
}

/**
 * Implements hook_field_extra_fields().
 *
 * Add support for the 'Taxonomy Revision settings' to be re-ordered by the user on the
 * 'Manage Fields' tab of vocabularies.
 */
function taxonomy_revision_field_extra_fields() {
  $info = array();
  $vocabularies = taxonomy_get_vocabularies();
  foreach ($vocabularies as $vocabulary) {
    if (!isset($info['taxonomy_term'][$vocabulary->machine_name]['form']['revision_information'])) {
      $info['taxonomy_term'][$vocabulary->machine_name]['form']['revision_information'] = array(
        'label' => t('Taxonomy Revision settings'),
        'description' => t('Taxonomy Revision module form elements'),
        'weight' => 30,
      );
    }
  }
  return $info;
}

/**
 * Gets/sets whether revisions are enabled by default for a vocabulary.
 *
 * @param $vid
 *  A vocabulary ID.
 * @param boolean $value
 *  A status value to save. Defaults to NULL to return the current status
 *  instead.
 * @return boolean
 *  The current value if no value was passed.
 */
function _taxonomy_revision_enabled_by_default($vid, $value = NULL) {
  $vocabulary = taxonomy_vocabulary_load($vid);
  if (!isset($value)) {
    return variable_get("taxonomy_revision_by_default_{$vocabulary->machine_name}", FALSE);
  }

  // Set value
  variable_set("taxonomy_revision_by_default_{$vocabulary->machine_name}", $value);
}

Functions

Namesort descending Description
taxonomy_revision_delete Deletes a taxonomy term revision.
taxonomy_revision_entity_diff Implements hook_entity_diff().
taxonomy_revision_entity_info_alter Implements hook_entity_info_alter().
taxonomy_revision_field_attach_form Implements hook_field_attach_form().
taxonomy_revision_field_extra_fields Implements hook_field_extra_fields().
taxonomy_revision_field_storage_pre_insert Implements hook_field_storage_pre_insert().
taxonomy_revision_field_storage_pre_update Implements hook_field_storage_pre_update().
taxonomy_revision_form_taxonomy_form_vocabulary_alter Implements hook_form_FORM_ID_alter().
taxonomy_revision_form_taxonomy_overview_terms_alter Implements hook_form_FORM_ID_alter().
taxonomy_revision_list Returns a list of all the existing revision numbers.
taxonomy_revision_menu Implements hook_menu().
taxonomy_revision_permission Implements hook_permission().
taxonomy_revision_show Generate an array which displays a taxonomy term detail page.
taxonomy_revision_taxonomy_form_term_submit_build_term Entity builder for the taxonomy term entity.
taxonomy_revision_taxonomy_form_vocabulary_submit Submit handler for the taxonomy_form_vocabulary form.
taxonomy_revision_taxonomy_overview_terms_submit Custom submit handler for the taxonomy terms overview form.
taxonomy_revision_taxonomy_term_delete Implements hook_taxonomy_term_delete().
taxonomy_revision_taxonomy_term_presave Implements hook_taxonomy_term_presave().
taxonomy_revision_term_load Loads a taxonomy term revision.
taxonomy_revision_theme Implements hook_theme().
taxonomy_revision_views_api Implements hook_views_api().
_taxonomy_revision_access Access callback for taxonomy revision.
_taxonomy_revision_enabled_by_default Gets/sets whether revisions are enabled by default for a vocabulary.
_taxonomy_revision_taxonomy_term_save_revision Saves the term.