You are here

enforce_revlog.module in Enforce revision log message 6

Same filename and directory in other branches
  1. 8 enforce_revlog.module
  2. 7 enforce_revlog.module

Allows enforcing unpriviledged users to enter a log message every time a node revision is created or reverted

File

enforce_revlog.module
View source
<?php

/**
 * @file
 * Allows enforcing unpriviledged users to enter a log message every time
 * a node revision is created or reverted
 */

/**
 * TODO:
 * - Make comments follow more closely the Doxygen format
 */

/**
* Implementation of hook_perm().
* 
* Adding permission for skipping the check.
*/
function enforce_revlog_perm() {
  return array(
    'skip revision log message',
  );
}

/**
 * Implementation of hook_menu().
 * 
 * Administration page and JS callback
 */
function enforce_revlog_menu() {
  $items = array();
  $items['admin/settings/enforce_revlog'] = array(
    'title' => 'Enforce revlog',
    'description' => 'Enforce users to enter a log message for every revision.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'enforce_revlog_settings',
    ),
    'access callback' => 'user_access',
    'access arguments' => array(
      'administer site configuration',
    ),
  );
  $items['enforce_revlog/js'] = array(
    'page callback' => 'enforce_revlog_js',
    'access arguments' => array(
      'access content',
    ),
    'type' => MENU_CALLBACK,
  );
  return $items;
}

/**
 * Settings page
 */
function enforce_revlog_settings() {
  $form['info_msg'] = array(
    '#type' => 'markup',
    // not necessary (default value), added for exhaustivity
    '#value' => t('Do not forget to <a href="@setperms">set permissions</a> for roles that will be able to skip entering a log message', array(
      '@setperms' => url('admin/user/permissions', array(
        'fragment' => 'module-enforce_revlog',
      )),
    )),
    // use placeholder and url() as it's best practice for these translatable strings
    '#prefix' => '<p><em>',
    '#suffix' => '</em></p>',
  );
  $form['enforce_revlog_revision_revert'] = array(
    '#type' => 'checkbox',
    '#title' => t('Enforce users to enter a log message when reverting a revision'),
    '#default_value' => variable_get('enforce_revlog_revision_revert', 0),
  );
  $form['types'] = array(
    '#type' => 'fieldset',
    '#title' => t('Enforce users to enter a log message for every revision of these content types'),
    '#description' => t('Each selected content type must have revisions enabled for this module to work'),
  );

  // Generate per content-type settings
  foreach (node_get_types() as $type => $name) {
    $form['types']['enforce_revlog_node_type_' . $type] = array(
      '#type' => 'checkbox',
      '#title' => $type,
      '#default_value' => variable_get('enforce_revlog_node_type_' . $type, 0),
    );
  }
  return system_settings_form($form);
}

/**
* Implementation of hook_nodeapi()
*
* Displays a message when the revision log is empty.
*/
function enforce_revlog_nodeapi(&$node, $op, $arg = 0) {
  switch ($op) {
    case 'prepare':

      // We should enable enforce_revlog on this form if:
      if ($node->nid && variable_get('enforce_revlog_node_type_' . $node->type, 0) && !user_access('skip revision log message')) {
        $node->enforce_revlog = TRUE;
      }
      break;
    case 'validate':

      // A log message will only be required if:
      if ($node->nid && $node->revision && $node->op == $node->submit && variable_get('enforce_revlog_node_type_' . $node->type, 0) && !user_access('skip revision log message') && empty($node->log)) {

        // different message if user is a node administrator
        $message = user_access('administer nodes') ? t('Please enter a revision log message or uncheck the revision checkbox.') : t('Please enter a revision log message.');
        form_set_error('log', $message);
      }
      break;
    case 'presave':

      // Trigger only if we're reverting a revision
      // Determined thanks to a custom property in the node object
      if (!empty($node->enforce_revlog_revision_revert)) {

        // Adding the custom log message to the standard one
        $node->log = $node->enforce_revlog_log_message . ' (' . $node->log . ')';

        // Deleting custom object properties, there aren't needed anymore
        unset($node->enforce_revlog_log_message, $node->enforce_revlog_revision_revert);
      }
      break;
  }
}

/**
* Implementation of hook_help()
*/
function enforce_revlog_help($path, $arg) {
  switch ($path) {
    case 'admin/help#enforce_revlog':

      // Help page
      return '<p>' . t('Enforce users to enter a log message for every revision.') . '</p>';
    case 'admin/settings/enforce_revlog':

      // Settings page header
      return '<p>' . t('Settings for the Enforce revlog module.') . '</p>';
  }
}

/**
* Implementation of hook_form_alter().
* 
* Trying to identify all node forms to enable log message textarea as required
*/
function enforce_revlog_form_alter(&$form, $form_state, $form_id) {
  switch ($form['#id']) {
    case 'node-form':
      if ($form['#node']->enforce_revlog) {

        // Triggering AHAH handler on enabling / disabling creation of a new revision
        $form['revision_information']['revision']['#ahah'] = array(
          'event' => 'change',
          // not necessary (default value), added for exhaustivity
          'path' => 'enforce_revlog/js',
          'wrapper' => 'edit-log-wrapper',
        );

        // Mark the log message as required if creation of a new revision is enabled
        if (!empty($form['#node']->revision)) {

          // Using a custom theme function here instead of setting the #required property
          // to prevent validation issues we would encounter otherwise
          $form['revision_information']['log']['#theme'] = 'enforce_revlog_log_message';
        }
      }
      break;
    case 'node-revision-revert-confirm':

      // Add a log message textarea to the revision revert form - adapt the one from node.module
      $form['log'] = array(
        '#type' => 'textarea',
        '#title' => t('Log message'),
        '#default_value' => '',
        '#rows' => 2,
        '#description' => t('An explanation of your reasons for reverting the revision to help other authors understand your motivations.'),
        '#required' => variable_get('enforce_revlog_revision_revert', 0) && !user_access('skip revision log message') ? TRUE : FALSE,
      );

      // Adding our own properties to the node object for future use
      $form['#node_revision']->enforce_revlog_revision_revert = 1;
      $form['#node_revision']->enforce_revlog_log_message = '';

      // Custom validation function
      $form['#validate'][] = 'enforce_revlog_revision_revert_validate';
      break;
    case 'node-type-form':

      // Adding enforce_revlog setting on the content type editing form.
      // Value will be automatically saved in the variable table.
      // Content type name changes are handled automatically too.
      $form['workflow']['enforce_revlog_node_type'] = array(
        '#type' => 'radios',
        '#title' => t('Enforce users to enter a log message for every revision'),
        '#description' => t('Revisions must be enabled for this module to work'),
        '#default_value' => variable_get('enforce_revlog_node_type_' . $form['#node_type']->type, 0),
        '#options' => array(
          t('Disabled'),
          t('Enabled'),
        ),
      );
      break;
  }
}

/**
 * Validation function for the revision revert form
 * Passing $form by reference to store changes to the object
 */
function enforce_revlog_revision_revert_validate(&$form, &$form_state) {

  // Storing the custom log message in the node object
  $form['#node_revision']->enforce_revlog_log_message = check_plain($form_state['values']['log']);
}

/**
 * Helper AHAH function to change the required state of the log message textarea.
 * Allows visual feedback by the user.
 * This function is only triggered when the right conditions are met.
 */
function enforce_revlog_js() {
  $form_state = array(
    'submitted' => FALSE,
  );
  $form_build_id = $_POST['form_build_id'];

  // Add the new element to the stored form. Without adding the element to the
  // form, Drupal is not aware of this new element's existence and will not
  // process it. We retreive the cached form, add the element, and resave.
  $form = form_get_cache($form_build_id, $form_state);

  // Switching theme function used to render the log message textarea. Will switch the "required" state.
  // We cannot use the #required property due to validation issues otherwise
  $form['revision_information']['log']['#theme'] = empty($form['revision_information']['log']['#theme']) ? 'enforce_revlog_log_message' : NULL;
  form_set_cache($form_build_id, $form, $form_state);
  $form += array(
    '#post' => $_POST,
    '#programmed' => FALSE,
  );

  // Rebuild the form.
  $form = form_builder($_POST['form_id'], $form, $form_state);

  // Render the new output.
  $new_form = $form['revision_information']['log'];
  return drupal_json(array(
    'status' => TRUE,
    'data' => drupal_render($new_form),
  ));
}

/**
 * Implementation of hook_node_type().
 * 
 * Deleting associated variable when content type is deleted
 * Avoiding issues with unused variables and hook_uninstall()
 * Update is handled automatically
 * @see enforce_revlog_uninstall().
 */
function enforce_revlog_node_type($op, $info) {
  switch ($op) {
    case 'delete':
      variable_del('enforce_revlog_node_type_' . $info->type);
      break;
  }
}

/**
 * Implementation of hook_theme()
 * Enabling custom theme implementation
 */
function enforce_revlog_theme() {
  return array(
    'enforce_revlog_log_message' => array(
      'arguments' => array(
        'element' => NULL,
      ),
    ),
  );
}

/**
 * Custom theme function for the log message element
 * Faking the #required property without triggering the automatic validation
 */
function theme_enforce_revlog_log_message($element) {

  // Setting the #required property here as it won't affect the form - only the display
  $element['#required'] = TRUE;
  return theme('textarea', $element);
}

Functions

Namesort descending Description
enforce_revlog_form_alter Implementation of hook_form_alter().
enforce_revlog_help Implementation of hook_help()
enforce_revlog_js Helper AHAH function to change the required state of the log message textarea. Allows visual feedback by the user. This function is only triggered when the right conditions are met.
enforce_revlog_menu Implementation of hook_menu().
enforce_revlog_nodeapi Implementation of hook_nodeapi()
enforce_revlog_node_type Implementation of hook_node_type().
enforce_revlog_perm Implementation of hook_perm().
enforce_revlog_revision_revert_validate Validation function for the revision revert form Passing $form by reference to store changes to the object
enforce_revlog_settings Settings page
enforce_revlog_theme Implementation of hook_theme() Enabling custom theme implementation
theme_enforce_revlog_log_message Custom theme function for the log message element Faking the #required property without triggering the automatic validation