You are here

edit_limit.module in Edit Limit 7

Same filename and directory in other branches
  1. 6 edit_limit.module

edit_limit.module Primary module file for Edit Limit. This contains all the hooks needed to run the module.

File

edit_limit.module
View source
<?php

/**
 * @file edit_limit.module
 * Primary module file for Edit Limit. This contains all the hooks needed to
 * run the module.
 */
define('EDIT_LIMIT_NODE_COUNT_DEFAULT', 3);
define('EDIT_LIMIT_COMMENT_COUNT', 3);
define('EDIT_LIMIT_NODE_TIME_DEFAULT', 86400);

// in seconds, 1 day
define('EDIT_LIMIT_COMMENT_TIME', 300);

// in seconds, 5 minutes
define('EDIT_LIMIT_TIME_UNITS', t('@seconds,@minutes,@hours,@days', array(
  "@seconds" => "seconds",
  "@minutes" => "minutes",
  "@hours" => "hours",
  "@days" => "days",
)));
define('EDIT_LIMIT_TIME_UNIT_DEFAULT', t('seconds'));

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

  // This is the minimum information you can provide for a menu item.
  $items['admin/config/content/edit_limit'] = array(
    'title' => 'Edit Limit',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'edit_limit_settings',
    ),
    'access arguments' => array(
      'administer edit limit',
    ),
    'file' => 'edit_limit.admin.inc',
    'description' => 'Configure how often or how soon nodes and comments can be edited before they are locked.',
  );
  return $items;
}

/**
 * Implements hook_permissions().
 */
function edit_limit_permission() {
  return array(
    'bypass edit limits' => array(
      'title' => t('Bypass edit limits'),
      'description' => t('Bypass the edit limits set in place for normal users.'),
    ),
    'administer edit limit' => array(
      'title' => t('Administer edit limits'),
      'description' => t('Bypass the edit limits set in place for normal users.'),
    ),
  );
}

/**
 * Implements hook_menu_alter().
 *
 * This allows additional checks to be made for granting access checks. For instance,
 * the node menu items use node_access() to grant permissions to edit nodes, but we
 * want to not only check that, but also check to see if the user is allowed to continue
 * making edits.
 */
function edit_limit_menu_alter(&$items) {

  // node_access() overrides
  $items['node/%node/edit']['access callback'] = 'edit_limit_node_access';

  // Override comment edit access checks
  $items['comment/%comment/edit']['access callback'] = 'edit_limit_comment_access';
}

/**
 * Helper function to determine if the user has permissions to use whatever grants node_access()
 * grants to this user, as determined by the additional site membership checks.
 */
function edit_limit_node_access($op, $node, $account = NULL) {
  module_load_include('inc', 'node', 'node.pages');
  if (node_access($op, $node, $account)) {
    switch ($op) {
      case 'update':
        if (user_access('bypass edit limits')) {
          return TRUE;
        }
        return _edit_limit_user_acccess($node);
    }
  }
  return FALSE;
}

/**
 * Helper function to perform additional access checks after node_access() has been checked.
 */
function _edit_limit_user_acccess($node) {

  // Be liberal here. The user has already passed node_access() by this point.
  $return = TRUE;

  // If this is a new node, let it go.
  if (empty($node->nid)) {
    return $return;
  }

  // Only limit edits if the content type is in the list.
  $allowed_types = variable_get('edit_limit_node_types', array());
  if (empty($allowed_types[$node->type])) {
    return $return;
  }

  // Get the time limit settings
  $edit_limit_node_time_enabled = variable_get('edit_limit_node_time_enabled', FALSE);
  $time_limits['default'] = intval(variable_get('edit_limit_node_time_default', 10)) * 24 * 60 * 60;
  $time_limits['node'][$node->type] = intval(variable_get('edit_limit_node_time_' . $node->type, $time_limits['default']));

  // Get the edit count limit settings
  $edit_limit_node_count_enabled = variable_get('edit_limit_node_count_enabled', FALSE);
  $count_limits['default'] = intval(variable_get('edit_limit_node_count_default', EDIT_LIMIT_NODE_COUNT_DEFAULT));
  $count_limits['node'][$node->type] = intval(variable_get('edit_limit_node_count_' . $node->type, $count_limits['default']));

  // If no options are enabled, we're done.
  if (!$edit_limit_node_time_enabled && !$edit_limit_node_count_enabled) {
    return $return;
  }

  // Test that the node hasn't been edited more than the allowed number of times.
  $count_limit_reached = FALSE;
  if ($edit_limit_node_count_enabled && $return) {
    $edit_count = edit_limit_node_count($node->nid);
    $count_limit = $count_limits['node'][$node->type] ? $count_limits['node'][$node->type] : $count_limits['node']['default'];
    if ($count_limit <= $edit_count) {
      $return = FALSE;
      $count_limit_reached = TRUE;
    }
  }

  // Test that the node was not created longer ago than the allowed time limit.
  if ($edit_limit_node_time_enabled && $return) {
    $time_unit = variable_get('edit_limit_node_time_unit', EDIT_LIMIT_TIME_UNIT_DEFAULT);
    $timeleft = intval($node->created) + intval(_unit_to_seconds(variable_get('edit_limit_node_time_default', EDIT_LIMIT_NODE_TIME_DEFAULT), $time_unit)) - REQUEST_TIME;
    drupal_add_js(drupal_get_path('module', 'edit_limit') . '/edit_limit.js');
    if ($timeleft <= 0) {
      $time_limit_reached = TRUE;
      $return = FALSE;
    }
    elseif (!$count_limit_reached && arg(0) == 'node' && arg(2) == 'edit') {
      static $type;
      if (empty($type)) {
        $type = 'status';
        $message = '<span class="edit-limit-time-unit-' . $time_unit . '">' . t('You have %seconds %time_unit left to edit this content.', array(
          '%seconds' => $timeleft,
          '%time_unit' => t('seconds'),
        )) . '</span>';
        drupal_set_message(filter_xss($message, array(
          'em',
          'span',
        )), $type, FALSE);
      }
    }
  }
  return $return;
}

/**
 * Helper function to convert a time unit to seconds
 */
function _unit_to_seconds($time = 0, $unit = 'seconds') {
  switch ($unit) {
    case 'minutes':
      $time_in_seconds = $time * 60;
      break;
    case 'hours':
      $time_in_seconds = $time * 60 * 60;
      break;
    case 'days':
      $time_in_seconds = $time * 60 * 60 * 24;
      break;
    default:
      $time_in_seconds = $time;
      break;
  }
  return $time_in_seconds;
}

/**
 * Access check function to see if the user can still edit the comment or not.
 * It must, at minimum, perform the comment module's access checks first.
 */
function edit_limit_comment_access($edit = NULL, $comment = NULL) {
  if (user_access('post comments')) {
    if (user_access('bypass edit limits')) {
      return TRUE;
    }
    $return = comment_access($edit, $comment);

    // Only limit edits if content type is in the list.
    $node = node_load($comment->nid);
    if (!in_array($node->type, variable_get('edit_limit_comments', array()))) {
      return $return;
    }
    if (variable_get('edit_limit_comment_time_enabled', FALSE)) {
      $time_unit = variable_get('edit_limit_comment_time_unit', EDIT_LIMIT_TIME_UNIT_DEFAULT);

      // By this point, we want to return false if the time has passed.
      if ($comment->created + intval(_unit_to_seconds(variable_get('edit_limit_comment_time', EDIT_LIMIT_COMMENT_TIME), $time_unit)) < REQUEST_TIME) {
        $return = FALSE;
      }
      else {
        static $time_left;
        if (empty($time_left)) {
          drupal_add_js(drupal_get_path('module', 'edit_limit') . '/edit_limit.js');
          $time_left = $comment->created + intval(_unit_to_seconds(variable_get('edit_limit_comment_time', EDIT_LIMIT_COMMENT_TIME), $time_unit)) - REQUEST_TIME;
        }
        if (user_access('edit own comments')) {
          $type = 'status';
          $message = '<span class="edit-limit-time-unit-' . check_plain($time_unit) . '">' . t('You have %time_left %time_unit left to edit this comment.', array(
            '%time_left' => $time_left,
            '%time_unit' => 'seconds',
          )) . '</span>';
          drupal_set_message(filter_xss($message, array(
            'em',
            'span',
          )), $type, FALSE);
        }
      }
    }
    return $return;
  }
  return FALSE;
}

/**
 * Implements hook_node_update().
 */
function edit_limit_node_update($node) {
  if (variable_get('edit_limit_node_count_enabled', FALSE)) {

    // Update the count of edit limits.
    $count_data = db_select('edit_limit_node_count', 'e')
      ->fields('e')
      ->condition('nid', $node->nid, '=')
      ->execute()
      ->fetchAssoc();
    $edit_count = 0;
    $update = array();
    if (!empty($count_data)) {
      $edit_count = $count_data['count'];
      $update = array(
        'nid',
      );
    }
    $edit_count++;
    $data = array(
      'nid' => $node->nid,
      'count' => $edit_count,
    );
    drupal_write_record('edit_limit_node_count', $data, $update);
  }
}

/**
 * Implements hook_node_prepare().
 */
function edit_limit_node_prepare($node) {
  if (!empty($node->nid)) {
    if (variable_get('edit_limit_node_count_enabled', FALSE)) {
      $limit = variable_get('edit_limit_node_count_default', EDIT_LIMIT_NODE_COUNT_DEFAULT);
      $count = edit_limit_node_count($node->nid);

      // If the edit count is getting close to the limit, make this a warning instead of a simple status
      $type = $count + 2 > $limit ? 'warning' : 'status';
      $message = t("This node has been edit !count out of !limit times, after which it will be locked.", array(
        '!count' => $count,
        '!limit' => $limit,
      ));
      drupal_set_message(filter_xss($message), $type);
    }
  }
}

/**
 * Helper function to get the number of times a particular node
 * has been edited.
 */
function edit_limit_node_count($nid) {
  $count = db_select('edit_limit_node_count', 'e')
    ->fields('e', array(
    'count',
  ))
    ->condition('nid', $nid, '=')
    ->execute()
    ->fetchField();
  $count = empty($count) ? 0 : $count;
  return $count;
}

/**
 * Implements hook_link_alter().
 * This removes comment edit links if the comment is no longer editable.
 */

//function edit_limit_link_alter(&$links, $node, $comment = NULL) {
function edit_limit_node_view_alter(&$build) {

  // Don't bother if the user can't edit their own comments anyway
  if (!user_access('edit own comments')) {
    return FALSE;
  }
  if (!empty($build['links']['comment']['#links']['comment-add']) && !empty($build['comments']['comments'])) {

    // Allow edit links if the edit count hasn't been reached yet.
    // TODO: Complete this functionality.
    if (0) {
      $results = db_query("SELECT * FROM {edit_limit_comment_time} WHERE cid = :cid", array(
        ':cid' => $comment->cid,
      ));
    }

    // Allow edit links if there is enough time left based on the system variable edit_limit_comment_time.
    foreach ($build['comments']['comments'] as $key => $comment) {
      if (!is_numeric($key)) {
        continue;
      }

      // We don't have to do much user access checking, since this link will only exist if the user passed
      // the comment_access() check for this given comment.
      if (!user_access('bypass edit limits') && variable_get('edit_limit_comment_time_enabled', FALSE) && !empty($build['comments']['comments'][$key]['links']['comment']['#links']['comment-edit'])) {
        $time_unit = variable_get('edit_limit_comment_time_unit', EDIT_LIMIT_TIME_UNIT_DEFAULT);
        $time_limit = $comment['#comment']->created + intval(_unit_to_seconds(variable_get('edit_limit_comment_time', EDIT_LIMIT_COMMENT_TIME), $time_unit));

        // If too much time has passed, destroy the link.
        if ($time_limit < REQUEST_TIME) {
          unset($build['comments']['comments'][$key]['links']['comment']['#links']['comment-edit']);
        }
        else {

          // Add the javascript that will handle the countdown.
          drupal_add_js(drupal_get_path('module', 'edit_limit') . '/edit_limit.js');

          // Modify the link to add the remaining seconds (only users that can't pass 'bypass edit limits' get this far).
          $build['comments']['comments'][$key]['links']['comment']['#links']['comment-edit']['title'] .= ' <span class="edit-limit-time-unit-' . $time_unit . '">' . t('(%time_limit %time_unit left)', array(
            '%time_limit' => $time_limit - REQUEST_TIME,
            '%time_unit' => 'seconds',
          )) . '</span>';
        }
      }
    }
  }
}

Functions

Namesort descending Description
edit_limit_comment_access Access check function to see if the user can still edit the comment or not. It must, at minimum, perform the comment module's access checks first.
edit_limit_menu Implements hook_menu().
edit_limit_menu_alter Implements hook_menu_alter().
edit_limit_node_access Helper function to determine if the user has permissions to use whatever grants node_access() grants to this user, as determined by the additional site membership checks.
edit_limit_node_count Helper function to get the number of times a particular node has been edited.
edit_limit_node_prepare Implements hook_node_prepare().
edit_limit_node_update Implements hook_node_update().
edit_limit_node_view_alter
edit_limit_permission Implements hook_permissions().
_edit_limit_user_acccess Helper function to perform additional access checks after node_access() has been checked.
_unit_to_seconds Helper function to convert a time unit to seconds

Constants

Namesort descending Description
EDIT_LIMIT_COMMENT_COUNT
EDIT_LIMIT_COMMENT_TIME
EDIT_LIMIT_NODE_COUNT_DEFAULT @file edit_limit.module Primary module file for Edit Limit. This contains all the hooks needed to run the module.
EDIT_LIMIT_NODE_TIME_DEFAULT
EDIT_LIMIT_TIME_UNITS
EDIT_LIMIT_TIME_UNIT_DEFAULT