You are here

readonlymode.module in Read only mode 7

Same filename and directory in other branches
  1. 8 readonlymode.module
  2. 6 readonlymode.module
  3. 2.0.x readonlymode.module

The Read Ony Mode main module file.

Read Only Mode provides an alternate to the built in 'Maintenance Mode' in Drupal. Instead of displaying a static text file to users while the site is in maintenance mode, Read Only Mode will allow access (reading) of new content while preventing the addition of new content (posting / submitting forms / etc).

This allows the site to remain functional while maintenance is performed. This module also provides messaging to users and administrators to indicate that the site is in maintenance mode.

File

readonlymode.module
View source
<?php

/**
 * @file
 * The Read Ony Mode main module file.
 *
 * Read Only Mode provides an alternate to the built in 'Maintenance Mode' in
 * Drupal. Instead of displaying a static text file to users while the site is
 * in maintenance mode, Read Only Mode will allow access (reading) of new
 * content while preventing the addition of new content (posting / submitting
 * forms / etc).
 *
 * This allows the site to remain functional while maintenance is performed.
 * This module also provides messaging to users and administrators to indicate
 * that the site is in maintenance mode.
 */

/**
 * Indicate the form has been blocked by readonlymode.
 */
define('READONLYMODE_FORM_DENIED', 0);

/**
 * Indicate the form has been made view only by readonlymode.
 */
define('READONLYMODE_FORM_VIEWONLY', 1);

/**
 * Indicate the form has allowed by readonlymode.
 */
define('READONLYMODE_FORM_ALLOWED', 2);

/**
 * Implements hook_module_implements_alter().
 *
 * Make sure our form alter runs last.
 */
function readonlymode_module_implements_alter(&$implementations, $hook) {
  if ($hook == 'form_alter') {
    $group = $implementations['readonlymode'];
    unset($implementations['readonlymode']);
    $implementations['readonlymode'] = $group;
  }
}

/**
 * Implements hook_permission().
 *
 * Create permission to allow access to forms while in read only mode
 */
function readonlymode_permission() {
  return array(
    'readonlymode access forms' => array(
      'title' => t('Access all forms while in <em>Read Only Mode</em>'),
    ),
  );
}

/**
 * Implements hook_form_alter().
 *
 * Permit posting of content.
 */
function readonlymode_form_alter(&$form, $form_state, $form_id) {
  $mode = _readonlymode_form_check($form_id);
  if ($mode != READONLYMODE_FORM_ALLOWED) {

    // Check whether the form is a fresh form being served to the user.
    if (!isset($form_state['input']['form_token'])) {

      // If a redirect URL is set, then we redirect to it.
      if ($url = variable_get('site_readonly_url', '')) {
        drupal_goto($url);
      }
      else {

        // Replace FAPI #after_build handlers with our block form.
        $form['#after_build'] = array(
          'readonlymode_block_form',
        );
      }
    }
  }
  $path = current_path();
  if (user_access('readonlymode access forms') && variable_get('site_readonly', FALSE) && $path != 'admin/config/development/maintenance') {
    drupal_set_message(t('The site is currently set to read-only, content moderation is disabled for all users without the "Access all forms while in maintenance" permission. <a href="@link">Go online</a>.', array(
      '@link' => url('admin/config/development/maintenance'),
    )), 'warning', FALSE);
  }
  $form['#validate'][] = 'readonlymode_check_form_validate';
}

/**
 * After build callback to block the form.
 *
 * @see readonlymode_form_alter()
 */
function readonlymode_block_form($form) {

  // Remove all elements of the form.
  foreach (element_children($form) as $key) {
    if (!in_array($key, array(
      'form_id',
      'form_token',
      'form_build_id',
    ))) {
      unset($form[$key]);
    }
  }
  $form['readonly_notice'] = array(
    '#markup' => _readonlymode_notice(),
    '#prefix' => '<div class="messages warning">',
    '#suffix' => '</div>',
  );
  return $form;
}

/**
 * After build callback to render read only forms.
 */
function readonlymode_readonly_form($element) {
  if (isset($element['#type'])) {
    if (in_array($element['#type'], array(
      'button',
      'image_button',
      'submit',
    ))) {
      $element['#access'] = FALSE;
    }
    else {
      $element['#attributes']['readonly'] = 'readonly';
      $element['#attributes']['disabled'] = 'disabled';
    }
  }

  // Recurse for any child elements.
  foreach (element_children($element) as $key) {
    $element[$key] = readonlymode_readonly_form($element[$key]);
  }
  return $element;
}

/**
 * Implements hook_form_FORM_ID_alter().
 *
 * Alter the Site Maintenance form
 */
function readonlymode_form_system_site_maintenance_mode_alter(&$form, $form_state) {
  $form['read_only'] = array(
    '#title' => t('Read Only Mode'),
    '#type' => 'fieldset',
    '#weight' => 0,
    '#collapsible' => TRUE,
    '#collapsed' => variable_get('site_readonly', FALSE) ? FALSE : TRUE,
  );
  $form['read_only']['site_readonly'] = array(
    '#type' => 'checkbox',
    '#title' => t('Read only mode'),
    '#description' => t('Put the site in read-only mode. When set to "Read-only", all content moderation (add/edit) will be impossible.'),
    '#weight' => 0,
    '#default_value' => variable_get('site_readonly', FALSE),
  );

  // Message configuration is in a collapsed fieldset
  // so that it doesn't clutter the display.
  $form['read_only']['messages'] = array(
    '#title' => t('Messages and redirects'),
    '#type' => 'fieldset',
    '#description' => t('Configure the redirect URL and messages to display to users while the site is in Read Only Mode.'),
    '#collapsible' => TRUE,
    '#collapsed' => TRUE,
  );
  $form['read_only']['messages']['site_readonly_message'] = array(
    '#type' => 'textarea',
    '#title' => t('Read Only Mode warning'),
    '#description' => t('This warning will be displayed when viewing a page that has a blocked form while in Read Only Mode.'),
    '#default_value' => _readonlymode_notice(),
    '#rows' => 3,
    '#required' => TRUE,
  );
  $form['read_only']['messages']['site_readonly_message_form_not_saved'] = array(
    '#type' => 'textarea',
    '#title' => t('Form submission error'),
    '#description' => t('This error will be displayed when a blocked form is submitted while in Read Only Mode. This scenario occurs when a user starts filling out a form during normal site operation and then attempts to submit the form after Read Only Mode has been enabled.'),
    '#default_value' => _readonlymode_notice_form_not_saved(),
    '#rows' => 3,
    '#required' => TRUE,
  );
  $form['read_only']['messages']['site_readonly_url'] = array(
    '#type' => 'textfield',
    '#title' => t('Redirect path'),
    '#description' => t('When given, Drupal will redirect the user to this URL when a user tries to add/edit content instead of displaying the message above.'),
    '#default_value' => variable_get('site_readonly_url', ''),
  );

  // Allowed forms configuration is in a collapsed fieldset
  // so that it doesn't clutter the display.
  $form['read_only']['forms'] = array(
    '#title' => t('Allowed forms'),
    '#type' => 'fieldset',
    '#description' => t('Configure which forms will be exempt from restriction when in read-only mode.'),
    '#collapsible' => TRUE,
    '#collapsed' => TRUE,
  );
  $form['read_only']['forms']['site_readonly_forms_allowed'] = array(
    '#type' => 'textarea',
    '#title' => t('Forms that can be submitted'),
    '#description' => t("These forms are not restricted when in read only mode. Enter one form id per line. You may use the wildcard character '*' to use loose matches. For example: webform* will match all webforms. Note that the following forms will always be allowed: %allowed_forms.", array(
      '%allowed_forms' => implode(', ', _readonlymode_default_forms_allowed()),
    )),
    '#default_value' => variable_get('site_readonly_forms_allowed', ''),
  );
  $form['read_only']['forms']['site_readonly_forms_viewonly'] = array(
    '#type' => 'textarea',
    '#title' => t('Forms that can be viewed'),
    '#description' => t("These forms are allowed to be viewed but will not accept form submissions. Enter one form id per line. You may use the wildcard character '*' to use loose matches. For example: webform* will match all webforms. Note that the following forms will always be allowed: %allowed_forms.", array(
      '%allowed_forms' => implode(', ', _readonlymode_default_forms_viewonly()),
    )),
    '#default_value' => variable_get('site_readonly_forms_viewonly', ''),
  );
  $form['#validate'][] = 'readonlymode_form_validate_url';
}

/**
 * Implements hook_form_validate().
 */
function readonlymode_form_validate_url(&$form, $form_state) {
  if ($path = $form_state['values']['site_readonly_url']) {
    $item = menu_get_item($path);
    if (!$item || !$item['access']) {
      form_set_error('site_readonly_url', t('The path %link_path is either invalid or you do not have access to it.', array(
        '%link_path' => $path,
      )));
    }
  }
}

/**
 * Returns the custom readonlymode warning.
 *
 * Used when a page with a blocked form is viewed.
 */
function _readonlymode_notice() {
  return t(variable_get('site_readonly_message', variable_get('site_name', 'drupal') . ' is currently in maintenance. During this maintenance it is not possible to change site content (like comments, pages and users).'));
}

/**
 * Return the custom readonlymode error.
 *
 * Used when a user attempts to submit a blocked form.
 */
function _readonlymode_notice_form_not_saved() {
  return t(variable_get('site_readonly_message_form_not_saved', 'Data not saved. ' . variable_get('site_name', 'drupal') . ' is currently in maintenance. During maintenance it is not possible to change content. Please make a note of your changes and try again later.'));
}

/**
 * Internal handler to check whether this form is to be restricted.
 *
 * @param int $form_id
 *   The ID of the form we want to check.
 *
 * @return int
 *   - READONLYMODE_FORM_DENIED: The form has been blocked.
 *   - READONLYMODE_FORM_VIEWONLY: The form should be view only.
 *   - READONLYMODE_FORM_ALLOWED: The form has been allowed.
 */
function _readonlymode_form_check($form_id) {

  // Get hold of our cache.
  $cache =& drupal_static(__FUNCTION__, NULL);

  // If NULL, we need to check the non-form specific options.
  if (!isset($cache)) {

    // If not in read-only mode, allow the form.
    if (!variable_get('site_readonly', FALSE)) {
      $cache = READONLYMODE_FORM_ALLOWED;
    }
    elseif (user_access('readonlymode access forms')) {
      $cache = READONLYMODE_FORM_ALLOWED;
    }
    else {
      $cache = array();
    }
  }

  // If we have a scalar value, we don't need to check specific forms.
  if (!is_array($cache)) {
    return $cache;
  }

  // Look at form specific level if required.
  if (!isset($cache[$form_id])) {

    // Is the form in the list of default forms? Then allow access.
    if (in_array($form_id, _readonlymode_default_forms_allowed())) {
      $cache[$form_id] = READONLYMODE_FORM_ALLOWED;
    }
    elseif (in_array($form_id, _readonlymode_default_forms_viewonly())) {
      $cache[$form_id] = READONLYMODE_FORM_VIEWONLY;
    }
    elseif (_readonlymode_form_list_check($form_id, variable_get('site_readonly_forms_allowed', ''))) {
      $cache[$form_id] = READONLYMODE_FORM_ALLOWED;
    }
    elseif (_readonlymode_form_list_check($form_id, variable_get('site_readonly_forms_viewonly', ''))) {
      $cache[$form_id] = READONLYMODE_FORM_VIEWONLY;
    }
    else {
      $cache[$form_id] = READONLYMODE_FORM_DENIED;
    }
  }
  return $cache[$form_id];
}

/**
 * Check for form_id is a given list.
 *
 * $list is a string of form ids separated by newlines.
 * Returns TRUE is matched, FALSE otherwise.
 */
function _readonlymode_form_list_check($form_id, $list) {
  $l = preg_split('/(\\r\\n|\\n|\\r)/', $list);
  foreach ($l as $word) {

    // Skip empty words.
    if (empty($word)) {
      continue;
    }
    $word = str_replace('*', '.*', $word);
    if (preg_match('/^' . $word . '$/', $form_id) === 1) {
      return TRUE;
    }
  }
  return FALSE;
}

/**
 * Our base forms allowed during read-only mode.
 */
function _readonlymode_default_forms_allowed() {
  return array(
    'read_only',
    'user_pass',
    'user_login',
    'user_login_block',
    'search_form',
    'search_block_form',
    'system_modules',
    'system_site_maintenance_mode',
    'views_exposed_form',
  );
}

/**
 * Our base forms allowed for view during read-only mode.
 */
function _readonlymode_default_forms_viewonly() {
  return array(
    'node_admin_content',
    'comment_admin_overview',
  );
}

/**
 * Validate handler for checking whether the form submission is occurring.
 *
 * Sets an form error when read-only mode is enabled.
 */
function readonlymode_check_form_validate($form, &$form_state) {

  // Check for read-only mode, whether we are allowed this form,
  if (_readonlymode_form_check($form['form_id']['#value']) != READONLYMODE_FORM_ALLOWED) {
    form_set_error('', _readonlymode_notice_form_not_saved());
  }
}

/**
 *  Implements hook_block_info().
 */
function readonlymode_block_info() {
  $blocks = array(
    'readonlymode_block' => array(
      'info' => t('Read only mode block'),
    ),
  );
  return $blocks;
}

/**
 *  Implements hook_block_view().
 */
function readonlymode_block_view($delta = '') {

  // block will be displayed only site is in readonly mode
  if ($delta == 'readonlymode_block' && variable_get('site_readonly', FALSE) == TRUE) {
    $block = array();
    $block['subject'] = t('Read only mode block');
    $block['content'] = _readonlymode_notice();
    return $block;
  }
}

Functions

Namesort descending Description
readonlymode_block_form After build callback to block the form.
readonlymode_block_info Implements hook_block_info().
readonlymode_block_view Implements hook_block_view().
readonlymode_check_form_validate Validate handler for checking whether the form submission is occurring.
readonlymode_form_alter Implements hook_form_alter().
readonlymode_form_system_site_maintenance_mode_alter Implements hook_form_FORM_ID_alter().
readonlymode_form_validate_url Implements hook_form_validate().
readonlymode_module_implements_alter Implements hook_module_implements_alter().
readonlymode_permission Implements hook_permission().
readonlymode_readonly_form After build callback to render read only forms.
_readonlymode_default_forms_allowed Our base forms allowed during read-only mode.
_readonlymode_default_forms_viewonly Our base forms allowed for view during read-only mode.
_readonlymode_form_check Internal handler to check whether this form is to be restricted.
_readonlymode_form_list_check Check for form_id is a given list.
_readonlymode_notice Returns the custom readonlymode warning.
_readonlymode_notice_form_not_saved Return the custom readonlymode error.

Constants

Namesort descending Description
READONLYMODE_FORM_ALLOWED Indicate the form has allowed by readonlymode.
READONLYMODE_FORM_DENIED Indicate the form has been blocked by readonlymode.
READONLYMODE_FORM_VIEWONLY Indicate the form has been made view only by readonlymode.