You are here

site_status_message_external_link.module in Site Status Message 7

Site Status Message External Link.

Allows Site Status Message module to use external links.

@author: Gideon Cresswell (DrupalGideon) <https://www.drupal.org/u/drupalgideon>

File

modules/site_status_message_external_link/site_status_message_external_link.module
View source
<?php

/**
 * @file
 * Site Status Message External Link.
 *
 * Allows Site Status Message module to use external links.
 *
 * @author: Gideon Cresswell (DrupalGideon)
 *          <https://www.drupal.org/u/drupalgideon>
 */

/**
 * Implements hook_form_FORM_ID_alter().
 */
function site_status_message_external_link_form_site_status_message_settings_alter(&$form, &$form_state, $form_id) {

  // Add option to select an external link.
  $form['site_status']['site_status_message_use_external_link'] = array(
    '#type' => 'checkbox',
    '#title' => t('Use external link'),
    '#default_value' => variable_get('site_status_message_use_external_link'),
    '#description' => t('Use external rather than internal link.'),
    '#weight' => 11,
    '#states' => array(
      'visible' => array(
        ':input[name="site_status_message_showlink"]' => array(
          'checked' => TRUE,
        ),
      ),
    ),
  );

  // Provide text field for external link.
  $form['site_status']['site_status_message_external_link'] = array(
    '#type' => 'textfield',
    '#size' => 40,
    '#maxlength' => 256,
    '#title' => t('More information page'),
    '#default_value' => variable_get('site_status_message_external_link'),
    '#description' => t('An optional external link to show more information about the status message. Include http:// or https:// in the link.'),
    '#weight' => 21,
    '#element_validate' => array(
      '_site_status_message_external_link_link_validate',
    ),
    '#states' => array(
      'visible' => array(
        ':input[name="site_status_message_showlink"]' => array(
          'checked' => TRUE,
        ),
        ':input[name="site_status_message_use_external_link"]' => array(
          'checked' => TRUE,
        ),
      ),
    ),
  );

  // Update the visibility state for the internal link so it will not show.
  $form['site_status']['site_status_message_link']['#states']['visible'][':input[name="site_status_message_use_external_link"]'] = array(
    'checked' => FALSE,
  );

  // Provide external link attributes.
  $form['site_status']['site_status_message_external_link_attributes'] = array(
    '#type' => 'fieldset',
    '#title' => 'External link attributes',
    '#weight' => 22,
    '#states' => array(
      'visible' => array(
        ':input[name="site_status_message_showlink"]' => array(
          'checked' => TRUE,
        ),
        ':input[name="site_status_message_use_external_link"]' => array(
          'checked' => TRUE,
        ),
      ),
    ),
    'site_status_message_external_link_new_tab' => array(
      '#type' => 'checkbox',
      '#title' => t('Open link in a new browser tab.'),
      '#default_value' => variable_get('site_status_message_external_link_new_tab'),
      '#weight' => 25,
    ),
    'site_status_message_external_link_no_follow' => array(
      '#type' => 'checkbox',
      '#title' => t('Use %nofollow attribute.', array(
        '%nofollow' => 'rel="nofollow"',
      )),
      '#default_value' => variable_get('site_status_message_external_link_no_follow'),
      '#weight' => 28,
    ),
  );
  $form['#after_build'][] = '_site_status_message_external_link_after_build';
  if (!variable_get('site_status_message_valid_external_link') && variable_get('site_status_message_external_link')) {
    drupal_set_message(t('The external link is no longer a valid path.'), 'warning');
  }
}

/**
 * Implements hook_preprocess_HOOK().
 *
 * Preprocess function for the theme template.
 */
function site_status_message_external_link_preprocess_site_status_message(&$variables) {
  if (variable_get('site_status_message_showlink') && variable_get('site_status_message_use_external_link')) {
    $link = variable_get('site_status_message_external_link');

    // Only show the link if the external URL is valid.
    if (variable_get('site_status_message_valid_external_link')) {
      $readmore = filter_xss(variable_get('site_status_message_readmore', 'Read more'));
      $attributes = array();

      // Check if link needs to be opened in a new browser tab.
      if (variable_get('site_status_message_external_link_new_tab', '')) {
        $attributes['target'] = array(
          '_blank',
        );

        // Newly opened tab can change the window.opener.location so adding
        // these additional attributes should prevent this.
        $attributes['rel'] = array(
          'noopener',
          'noreferrer',
        );
      }

      // Check for nofollow attribute.
      if (variable_get('site_status_message_external_link_no_follow')) {
        $attributes['rel'][] = 'nofollow';
      }
      $variables['link'] = l($readmore, $link, array(
        'attributes' => $attributes,
      ));
    }
  }
}

/**
 * Implements hook_cron().
 */
function site_status_message_external_link_cron() {
  if (variable_get('site_status_message_showlink') && variable_get('site_status_message_use_external_link')) {

    // Delete the variable that contains the currently validity of the link.
    variable_del('site_status_message_valid_external_link');
    $link = variable_get('site_status_message_external_link');

    // Check if the external link is still valid every time cron runs.
    $valid_link = _site_status_message_external_link_is_url_valid($link);
    if (!$valid_link) {
      watchdog('site status message external link', '%link is no longer a valid link!', array(
        '%link' => $link,
      ), WATCHDOG_ERROR);
    }
    variable_set('site_status_message_valid_external_link', $valid_link);
  }
}

/**
 * Remove internal link validation if external link is being used.
 *
 * Callback for #after_build.
 *
 * @param array $form
 *   The form array.
 * @param array $form_state
 *   The form_state array.
 *
 * @return array
 *   The amended form array.
 */
function _site_status_message_external_link_after_build(array $form, array &$form_state) {
  $use_link = $form_state['values']['site_status_message_use_external_link'];

  // Only remove the validation if the 'use external link' checkbox is selected.
  if ($use_link) {
    $element_validate = $form['site_status']['site_status_message_link']['#element_validate'];
    $key = array_search('_site_status_message_link_validate', $element_validate);
    unset($form['site_status']['site_status_message_link']['#element_validate'][$key]);
  }
  return $form;
}

/**
 * Validation callback for the Site Status Message External Link link field.
 *
 * Callback for #element_validate.
 *
 * @param array $element
 *   The element field being validated.
 * @param array $form_state
 *   The form_state array.
 */
function _site_status_message_external_link_link_validate(array $element, array &$form_state) {
  if ($form_state['values']['site_status_message_use_external_link']) {
    $link = $form_state['values']['site_status_message_external_link'];
    $valid_link = _site_status_message_external_link_is_url_valid($link);
    if (!$valid_link) {
      form_error($element, t('You must enter a valid external path.'));
    }
    else {
      variable_set('site_status_message_valid_external_link', $valid_link);
    }
  }
}

/**
 * Check whether the external URL provided is a valid link.
 *
 * @param string $link
 *   Text string containing a possible link.
 *
 * @return bool
 *   Whether the string provided is valid or not.
 */
function _site_status_message_external_link_is_url_valid($link) {
  $valid_link = variable_get('site_status_message_valid_external_link');
  $current_link = variable_get('site_status_message_external_link');

  // We don't want to be running curl on every request so we store whether the
  // link is valid or not. The link is periodically checked with cron.
  if (!$valid_link || $link !== $current_link) {
    $schema = parse_url($link, PHP_URL_SCHEME);

    // If the schema isn't provided, return error before trying curl request.
    if (!_site_status_message_external_link_is_valid_schema($schema)) {
      return FALSE;
    }

    // Simpler than drupal_http_request().
    $ch = curl_init($link);
    curl_setopt($ch, CURLOPT_NOBODY, TRUE);
    curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.2 (KHTML, like Gecko) Chrome/22.0.1216.0 Safari/537.2');
    curl_exec($ch);
    $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);
    $valid_link = _site_status_message_external_link_is_valid_http_code($http_code);
  }
  return $valid_link;
}

/**
 * List of all valid schemas.
 *
 * @return array
 *   Array of valid schemas.
 */
function _site_status_message_external_link_valid_schemas() {
  return array(
    'http',
    'https',
  );
}

/**
 * List of all valid http response codes.
 *
 * @return array
 *   Array of valid http response codes.
 */
function _site_status_message_external_link_valid_response_codes() {

  // Allow redirects as well as the 200 response code only.
  return array(
    200,
    301,
    302,
  );
}

/**
 * Check if the provided schema is in the allowed list.
 *
 * @param string $schema
 *   The schema to test.
 *
 * @return bool
 *   Whether the schema is valid or not.
 */
function _site_status_message_external_link_is_valid_schema($schema = NULL) {
  return in_array($schema, _site_status_message_external_link_valid_schemas());
}

/**
 * Check if the provided http code is in the allowed list.
 *
 * @param string $http_code
 *   The http code to test.
 *
 * @return bool
 *   Whether the code is valid or not.
 */
function _site_status_message_external_link_is_valid_http_code($http_code = NULL) {
  return in_array((int) $http_code, _site_status_message_external_link_valid_response_codes());
}

Functions

Namesort descending Description
site_status_message_external_link_cron Implements hook_cron().
site_status_message_external_link_form_site_status_message_settings_alter Implements hook_form_FORM_ID_alter().
site_status_message_external_link_preprocess_site_status_message Implements hook_preprocess_HOOK().
_site_status_message_external_link_after_build Remove internal link validation if external link is being used.
_site_status_message_external_link_is_url_valid Check whether the external URL provided is a valid link.
_site_status_message_external_link_is_valid_http_code Check if the provided http code is in the allowed list.
_site_status_message_external_link_is_valid_schema Check if the provided schema is in the allowed list.
_site_status_message_external_link_link_validate Validation callback for the Site Status Message External Link link field.
_site_status_message_external_link_valid_response_codes List of all valid http response codes.
_site_status_message_external_link_valid_schemas List of all valid schemas.