You are here

node_revision_delete.module in Node Revision Delete 7.3

File

node_revision_delete.module
View source
<?php

/**
 * @file
 * Node Revision Delete Module.
 */
require_once dirname(__FILE__) . '/node_revision_delete.helpers.inc';

/**
 * Implements hook_help().
 */
function node_revision_delete_help($path, $arg) {
  switch ($path) {
    case 'admin/help#node_revision_delete':
      $output = '';
      $output .= '<h3>' . t('About') . '</h3>';
      $output .= '<p>' . t('The Node Revision Delete module allows you to manage the revisions of the Node according to your choice. It helps you to keep the specific number of revisions for the node. This module provides you the flexibility for applying the revision delete for the specific content type and run it on the specific time.') . '</p>';
      $output .= '<h3>' . t('Uses') . '</h3>';
      $output .= '<dl>';
      $output .= '<dt>' . t('Configuring the module') . '</dt>';
      $output .= '<dd>' . t('You can manage the module settings from the <a href="@config-page">Node Revision Delete</a> page. For this you need the <em>Administer Node Revision Delete</em> permission.', array(
        '@config-page' => url('admin/config/content/node_revision_delete'),
      )) . '</dd>';
      $output .= '<dt>' . t('Deleting prior revisions') . '</dt>';
      $output .= '<dd>' . t('When you are deleting a revision of a node, a new checkbox will appear in a fieldset saying: "Also delete X revisions prior to this one."; if you check it, all the prior revisions will be deleted as well for the given node. If you are deleting the oldest revision, the checkbox will not appear as no prior revisions are available') . '</dd>';
      $output .= '</dl>';
      return $output;
    case 'admin/config/content/node_revision_delete':
      $output = '';
      $output .= '<p>' . t("To allow Node Revision Delete to act upon a certain content type, you should navigate to the desired content type's edit page via:") . '</p>';
      $output .= '<p><em>' . t('Administration » Structure » Content types » [Content type name]') . '</em></p>';
      $output .= '<p>' . t("Under the Publishing Options tab, enable 'Create new revision' and 'Limit the amount of revisions' checkboxes. Change the Maximum number of revisions to keep, if you need to, and finally, save your changes clicking in the Save content type button.") . '</p>';
      return $output;
  }
}

/**
 * Implements hook_permission().
 */
function node_revision_delete_permission() {
  return array(
    'administer node_revision_delete' => array(
      'title' => t('Administer Node Revision Delete'),
      'description' => t('Allow access to configure the module settings.'),
    ),
  );
}

/**
 * Implements hook_menu().
 */
function node_revision_delete_menu() {
  $items['admin/config/content/node_revision_delete'] = array(
    'title' => 'Node Revision Delete',
    'description' => 'Settings for automatically deleting node revisions.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'node_revision_delete_admin_settings_form',
    ),
    'access callback' => 'user_access',
    'access arguments' => array(
      'administer node_revision_delete',
    ),
    'type' => MENU_NORMAL_ITEM,
    'file' => 'node_revision_delete.admin.inc',
  );
  $items['admin/config/content/node_revision_delete/delete/%'] = array(
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'node_revision_delete_content_type_configuration_delete_form',
      5,
    ),
    'access arguments' => array(
      'administer node_revision_delete',
    ),
    'file' => 'node_revision_delete.admin.inc',
  );
  return $items;
}

/**
 * Implements hook_form_BASE_FORM_ID_alter().
 */
function node_revision_delete_form_node_type_form_alter(&$form, &$form_state) {

  // Getting the content types to track variable.
  $node_revision_delete_track = variable_get('node_revision_delete_track');

  // Getting the content type machine name from the actual form.
  $content_type = $form['#node_type']->type;

  // Looking if the config exists for the content type.
  if (isset($node_revision_delete_track[$content_type])) {
    $track = TRUE;
    $minimum_revisions_to_keep = $node_revision_delete_track[$content_type]['minimum_revisions_to_keep'];
    $when_to_delete = $node_revision_delete_track[$content_type]['when_to_delete'];
    $minimum_age_to_delete = $node_revision_delete_track[$content_type]['minimum_age_to_delete'];
  }
  else {
    $track = FALSE;
    $minimum_revisions_to_keep = 1;
    $when_to_delete = 0;
    $minimum_age_to_delete = 0;
  }
  $form['workflow']['section'] = array(
    '#type' => 'fieldset',
    '#title' => t("Node revision delete"),
    '#description' => t('The settings defined in this section will be applied to each node of this content type.'),
    '#attributes' => array(
      'class' => array(
        'fieldgroup',
        'form-composite',
      ),
    ),
    '#attached' => array(
      'css' => array(
        drupal_get_path('module', 'node_revision_delete') . '/css/node_revision_delete.css',
      ),
    ),
  );

  // Element to track the content type.
  $form['workflow']['section']['node_revision_delete_track'] = array(
    '#type' => 'checkbox',
    '#title' => t('Limit the amount of revisions'),
    '#default_value' => $track,
  );

  // Element for the minimum number of revisions to keep.
  $form['workflow']['section']['minimum_revisions_to_keep'] = array(
    '#type' => 'textfield',
    '#title' => t('Minimum number of revisions to keep'),
    '#description' => t('Oldest revisions will be deleted when the total amount surpases this value. Set it to 1 to remove all revisions (except the current revision).'),
    '#default_value' => $minimum_revisions_to_keep,
    '#element_validate' => array(
      'element_validate_integer_positive',
    ),
    '#states' => array(
      // Hide the settings when the cancel notify checkbox is disabled.
      'visible' => array(
        ':input[name="node_revision_delete_track"]' => array(
          'checked' => TRUE,
        ),
      ),
    ),
    '#size' => 10,
    '#maxlength' => 5,
  );

  // Getting the max number for node_revision_delete_minimum_age_to_delete_time.
  $node_revision_delete_minimum_age_to_delete_time_max_number = variable_get('node_revision_delete_minimum_age_to_delete_time')['max_number'];

  // Available options for minimum_age_to_delete.
  $options_minimum_age_to_delete[0] = t('None');
  for ($i = 1; $i <= $node_revision_delete_minimum_age_to_delete_time_max_number; $i++) {
    $options_minimum_age_to_delete[$i] = _node_revision_delete_time_string('minimum_age_to_delete', $i);
  }

  // Element to know when to delete the revisions.
  $form['workflow']['section']['minimum_age_to_delete'] = array(
    '#type' => 'select',
    '#title' => t('Minimum age of revisions to delete'),
    '#description' => t('Revisions older of this age will be deleted, but just only after the "Minimum number of revisions to keep" will be reached. If you don\'t want to take in count the age of the revisions, set to "None".'),
    '#options' => $options_minimum_age_to_delete,
    '#size' => 1,
    '#default_value' => $minimum_age_to_delete,
    '#states' => array(
      // Show the field when the checkbox is checked.
      'visible' => array(
        ':input[name="node_revision_delete_track"]' => array(
          'checked' => TRUE,
        ),
      ),
    ),
  );

  // Getting the max number for node_revision_delete_when_to_delete_time.
  $node_revision_delete_when_to_delete_time_max_number = variable_get('node_revision_delete_when_to_delete_time')['max_number'];

  // Available options for when_to_delete variable.
  $options_when_to_delete[0] = t('Always');
  for ($i = 1; $i <= $node_revision_delete_when_to_delete_time_max_number; $i++) {

    // Creating the time string.
    $options_when_to_delete[$i] = _node_revision_delete_time_string('when_to_delete', $i);
  }

  // Element to know when to delete the revisions.
  $form['workflow']['section']['when_to_delete'] = array(
    '#type' => 'select',
    '#title' => t('When to delete'),
    '#description' => t('If the current revision is not older than specified here, its older revisions will not be deleted, even if they are old enough. If set to "Always", older revisions will be deleted regardless of the age of the current revision.'),
    '#options' => $options_when_to_delete,
    '#size' => 1,
    '#default_value' => $when_to_delete,
    '#states' => array(
      // Show the field when the checkbox is checked.
      'visible' => array(
        ':input[name="node_revision_delete_track"]' => array(
          'checked' => TRUE,
        ),
      ),
    ),
  );

  // If we are creating the content type. Putting the custom submit handler
  // first to use the Entity->isNew() function, if the custom submit handler is
  // the last function called always the Entity is created first.
  array_unshift($form['#submit'], '_node_revision_delete_form_node_type_submit');
}

/**
 * Custom submit handler to save the configuration variables.
 *
 * @param array $form
 *   The form element.
 * @param array $form_state
 *   The form state.
 */
function _node_revision_delete_form_node_type_submit(array &$form, array &$form_state) {

  // Getting the form values.
  $track = $form_state['values']['node_revision_delete_track'];

  // Getting the content type machine name.
  $content_type = $form['type']['#value'];

  // If we will track the content type.
  if ($track) {

    // Getting the form values.
    $minimum_revisions_to_keep = $form_state['values']['minimum_revisions_to_keep'];
    $minimum_age_to_delete = $form_state['values']['minimum_age_to_delete'];
    $when_to_delete = $form_state['values']['when_to_delete'];

    // Saving the values in the config.
    _node_revision_delete_save_content_type_config($content_type, $minimum_revisions_to_keep, $minimum_age_to_delete, $when_to_delete);
  }
  elseif (!isset($form_state['complete form']['#node_type']->is_new) || !$form_state['complete form']['#node_type']->is_new) {

    // Deleting the value from the config.
    _node_revision_delete_delete_content_type_config($content_type);
  }

  // Cleaning the values to avoid that Drupal create variables for them.
  unset($form_state['values']['node_revision_delete_track']);
  unset($form_state['values']['minimum_revisions_to_keep']);
  unset($form_state['values']['minimum_age_to_delete']);
  unset($form_state['values']['when_to_delete']);
}

/**
 * Implements hook_node_type_delete().
 */
function node_revision_delete_node_type_delete($info) {

  // Getting the content type machine name.
  $content_type = $info->type;

  // Deleting the value from the config.
  _node_revision_delete_delete_content_type_config($content_type);
}

/**
 * Implements hook_js_alter().
 */
function node_revision_delete_js_alter(&$javascript) {

  // Deleting the library because we have our own version.
  // Maybe this cannot be longer needed once https://www.drupal.org/node/2871619
  // will be fixed.
  // Getting the core js file.
  $core_file = drupal_get_path('module', 'node') . '/content_types.js';

  // Replacing the file.
  if (isset($javascript[$core_file])) {

    // Getting the module js file.
    $module_file = drupal_get_path('module', 'node_revision_delete') . '/js/content_types.js';

    // Overwriting the file.
    $javascript[$core_file] = drupal_js_defaults($module_file);
  }
}

/**
 * Implements hook_cron().
 */
function node_revision_delete_cron() {

  // Get node revision limits for all content types.
  // If there are no tracked content types, exit this cron hook.
  $node_revision_delete_track = variable_get('node_revision_delete_track', array());
  if (empty($node_revision_delete_track)) {
    return;
  }

  // Figure out whether we should delete revisions or not on this run.
  $node_revision_delete_time = variable_get('node_revision_delete_time');
  $node_revision_delete_last_execute = variable_get('node_revision_delete_last_execute');
  $current_time = time();
  $time_difference = $current_time - $node_revision_delete_last_execute;
  if ($node_revision_delete_time >= 0 && $time_difference >= $node_revision_delete_time) {
    variable_set('node_revision_delete_last_execute', $current_time);

    // Getting the number of revisions to remove in each cron run.
    $max = variable_get('node_revision_delete_cron', 50);

    // Flag used to count how many have been deleted in this cron run.
    $total_deleted = 0;
    foreach ($node_revision_delete_track as $content_type => $content_type_info) {
      $candidates = _node_revision_delete_candidates($content_type, $content_type_info['minimum_revisions_to_keep'], $content_type_info['minimum_age_to_delete'], $content_type_info['when_to_delete']);
      $candidate_nids = array_values(array_unique(array_column($candidates, 'nid')));
      if (!empty($candidate_nids)) {
        foreach ($candidate_nids as $nid) {
          $deleted_revisions = _node_revision_delete_do_delete($nid, $node_revision_delete_track[$content_type]['minimum_revisions_to_keep']);
          $total_deleted += $deleted_revisions->count;
          if ($total_deleted >= $max) {
            break 2;
          }
        }
      }
    }
    drupal_set_message(t('Deleted @total revisions.', array(
      '@total' => $total_deleted,
    )));
  }
}

/**
 * Implements hook_form_FORM_ID_alter().
 */
function node_revision_delete_form_node_revision_delete_confirm_alter(&$form, &$form_state, $form_id) {
  $revisions = _node_revision_delete_get_previous_revisions($form['#node_revision']->nid, $form['#node_revision']->vid);
  $revisions_number = count($revisions);
  if ($revisions_number > 0) {
    $form['revision_list'] = array(
      '#type' => 'fieldset',
      '#title' => t('Delete prior revisions'),
      '#collapsible' => TRUE,
      '#collapsed' => TRUE,
    );
    $form['revision_list']['delete_prior_revisions'] = array(
      '#type' => 'checkbox',
      '#title' => t('Also delete %revs_no revisions prior to this one.', array(
        '%revs_no' => $revisions_number,
      )),
    );

    // Read more about the empty class attribute in HTML at:
    // https://stackoverflow.com/q/30748847/3653989 .
    $class = array();
    if (module_exists('responsive_tables')) {
      $class[] = RESPONSIVE_PRIORITY_MEDIUM;
    }

    // The table header.
    $header = array(
      array(
        'data' => t('Revision'),
        // Hiding the title on narrow width devices.
        'class' => $class,
      ),
      t('Revision ID'),
    );
    foreach ($revisions as $revision) {
      $rows[] = [
        t('!date by !username', [
          '!date' => l(format_date($revision->timestamp, 'short'), "node/{$form['#node_revision']->nid}/revisions/{$revision->vid}/view"),
          '!username' => theme('username', [
            'account' => $revision,
          ]),
        ]) . ($revision->log != '' ? '<p class="revision-log">' . filter_xss($revision->log) . '</p>' : ''),
        $revision->vid,
      ];
    }
    $form['revision_list']['table_markup'] = array(
      '#theme' => 'table',
      '#rows' => $rows,
      '#header' => $header,
    );
    $form['#submit'][] = '_node_revision_delete_node_revision_delete_confirm_submit';

    // Adding the donation text.
    $form['#prefix'] = _node_revision_delete_get_donation_text();
  }
}

/**
 * Custom submit handler for the revision deletion form.
 *
 * @param array $form
 *   The form element.
 * @param array $form_state
 *   The form state.
 */
function _node_revision_delete_node_revision_delete_confirm_submit(array &$form, array &$form_state) {
  if ($form_state['values']['delete_prior_revisions'] == 1) {

    // Getting the node id.
    $nid = $form['#node_revision']->nid;

    // Getting the revisions.
    $revisions_before = _node_revision_delete_get_previous_revisions($nid, $form['#node_revision']->vid);
    batch_set(_node_revision_delete_delete_batch_op($nid, array_keys($revisions_before)));
  }
}

/**
 * Batch operation definition: deleting prior revisions.
 *
 * @param int $nid
 *   The node id.
 * @param array $revisions
 *   The revisions vids.
 *
 * @return array
 *   An array with the batch definition.
 */
function _node_revision_delete_delete_batch_op($nid, array $revisions) {
  $operations = [];
  foreach ($revisions as $revision) {
    $operations[] = array(
      'node_revision_delete_batch_delete_prior_revisions',
      array(
        $nid,
        $revision,
      ),
    );
  }
  $batch = array(
    'title' => t('Deleting revisions'),
    'operations' => $operations,
    'init_message' => t('Starting to delete revisions.'),
    'file' => drupal_get_path('module', 'node_revision_delete') . '/node_revision_delete.batch.inc',
    'progress_message' => t('Deleted @current out of @total (@percentage%). Estimated time: @estimate.'),
    'error_message' => t('Error deleting revisions.'),
    'finished' => 'node_revision_delete_batch_delete_prior_revisions_finished',
  );
  return $batch;
}