You are here

elf.module in External Links Filter 7.3

Adds an icon to external and mailto links.

File

elf.module
View source
<?php

/**
 * @file
 *   Adds an icon to external and mailto links.
 */

/**
 * Implement hook_init().
 */
function elf_init() {
  require_once 'includes/common.inc';
  $path = drupal_get_path('module', 'elf');
  drupal_add_css("{$path}/css/elf.css");
  if (variable_get('elf_window', FALSE)) {
    drupal_add_js("{$path}/js/elf.js");
  }
}

/**
 * Implement hook_menu().
 */
function elf_menu() {
  $items['admin/config/content/elf'] = array(
    'title' => 'External Links Filter',
    'description' => 'Configure the external links filter.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'elf_form_settings',
    ),
    'access arguments' => array(
      'administer site configuration',
    ),
    'type' => MENU_NORMAL_ITEM,
  );
  $items['elf/go'] = array(
    'page callback' => 'elf_redirect',
    'access callback' => TRUE,
    'type' => MENU_CALLBACK,
  );
  return $items;
}

/**
 * Implement hook_filter().
 */
function elf_filter_info() {
  $filters['elf'] = array(
    'title' => t('Add an icon to external and mailto links'),
    'settings callback' => 'elf_filter_settings',
    'process callback' => 'elf_replace',
    'default settings' => array(
      'elf_nofollow' => FALSE,
    ),
  );
  return $filters;
}

/**
 * Hook_filter_info() settings callback.
 *
 * @see elf_filter_info()
 */
function elf_filter_settings(array $form, array &$form_state, stdClass $filter, stdClass $format, array $defaults) {
  $settings['elf_nofollow'] = array(
    '#type' => 'checkbox',
    '#title' => t('Add rel="nofollow" to all external links'),
    '#default_value' => isset($filter->settings['elf_nofollow']) ? $filter->settings['elf_nofollow'] : $defaults['elf_nofollow'],
  );
  return $settings;
}

/**
 * Admin settings form.
 *
 * @see elf_form_settings_submit()
 */
function elf_form_settings(array $form, array &$form_state) {
  $domains = variable_get('elf_domains', array());
  $form['elf_domains'] = array(
    '#type' => 'textarea',
    '#default_value' => implode("\n", $domains),
    '#title' => t('Internal domains'),
    '#description' => t('If your site spans multiple domains, specify each of them on a new line to prevent them from being seen as external sites. Make sure to include the right protocol; !example_right, and not !example_wrong, for instance. Asterisks are wildcards.', array(
      '!example_right' => '<code>http://example.com</code>',
      '!example_wrong' => '<code>example.com</code>',
    )),
  );
  $form['elf_window'] = array(
    '#type' => 'checkbox',
    '#default_value' => variable_get('elf_window', FALSE),
    '#title' => t('Use JavaScript to open external links in a new window'),
  );
  $form['elf_redirect'] = array(
    '#type' => 'checkbox',
    '#default_value' => variable_get('elf_redirect', FALSE),
    '#title' => t('Redirect users to external websites via !url_path.', array(
      '!url_path' => '<code>/elf/go</code>',
    )),
  );
  return system_settings_form($form, FALSE);
}

/**
 * Form validation handler for elf_form_settings().
 *
 * @see elf_form_settings()
 */
function elf_form_settings_validate(array $form, array &$form_state) {
  $values = preg_split('#\\s#', $form_state['values']['elf_domains']);
  $domains = array();
  $errors = array();
  foreach ($values as $value) {

    // Remove trailing slashes, because not all users will use those for their links.
    $value = trim($value, '/');
    if (strlen($value)) {
      if (!url_is_external($value)) {
        $errors[] = $value;
      }
      $domains[] = $value;
    }
  }
  if ($errors) {
    form_set_error('elf_domains', format_plural(count($errors), '%domain is not a valid external domain.', '%domain are no valid external domains', array(
      '%domain' => implode(', ', $errors),
    )));
  }
  else {
    form_set_value($form['elf_domains'], array_unique($domains), $form_state);
  }
}

/**
 * Hook_filter_info() process callback.
 *
 * @see elf_filter_info()
 */
function elf_replace($text, stdClass $filter) {
  $document = filter_dom_load($text);
  $links = $document
    ->getElementsByTagName('a');
  foreach ($links as $a) {
    if ($href = $a
      ->getAttribute('href')) {

      // This is a mailto link.
      if (strpos($href, 'mailto:') === 0) {
        $a
          ->setAttribute('class', $a
          ->getAttribute('class') ? $a
          ->getAttribute('class') . ' elf-mailto elf-icon' : 'elf-mailto elf-icon');
      }
      elseif (elf_url_external($href)) {

        // Add external class.
        $a
          ->setAttribute('class', $a
          ->getAttribute('class') ? $a
          ->getAttribute('class') . ' elf-external elf-icon' : 'elf-external elf-icon');
        if ($a
          ->getElementsByTagName('img')->length > 0) {
          $a
            ->setAttribute('class', $a
            ->getAttribute('class') ? $a
            ->getAttribute('class') . ' elf-img' : 'elf-img');
        }

        // Add nofollow.
        $nofollow = $filter->settings['elf_nofollow'];
        if ($nofollow) {
          $rel = array_filter(explode(' ', $a
            ->getAttribute('rel')));
          if (!in_array('nofollow', $rel)) {
            $rel[] = 'nofollow';
            $a
              ->setAttribute('rel', implode(' ', $rel));
          }
        }

        // Add redirect.
        if (variable_get('elf_redirect', FALSE)) {
          $redirect_url = elf_get_redirect_url($a
            ->getAttribute('href'));
          $a
            ->setAttribute('href', $redirect_url);
        }
      }
    }
  }
  return filter_dom_serialize($document);
}

/**
 * Test if a URL is external
 *
 * @param $url string
 */
function elf_url_external($url) {
  global $base_url;
  static $pattern = NULL;

  // Statically store internal domains as a PCRE pattern.
  if (!$pattern) {
    $domains = array();
    foreach (array_merge(variable_get('elf_domains', array()), array(
      $base_url,
    )) as $domain) {
      $domains[] = preg_quote($domain, '#');
    }
    $pattern = '#^(' . str_replace('\\*', '.*', implode('|', $domains)) . ')#';
  }
  return preg_match($pattern, $url) ? FALSE : url_is_external($url);
}

/**
 * Redirect the browser to the external URL from $_GET['url'].
 */
function elf_redirect() {
  $url = !empty($_GET['url']) ? $_GET['url'] : '';
  $key = !empty($_GET['key']) ? $_GET['key'] : '';
  if (!$url) {
    elf_redirect_exit();
  }

  // Works as a broken link if no valid arguments found.
  $parsed_url = drupal_parse_url(elf_get_redirect_url($url));
  if ($key != $parsed_url['query']['key']) {
    elf_redirect_exit();
  }

  // Perform the redirection to the external URL.
  drupal_goto($url);
}

/**
 * Deliver a "page not found" error to the browser and performs end-of-request tasks.
 */
function elf_redirect_exit() {
  drupal_not_found();
  drupal_exit();
}

/**
 * Creates a safe internal URL that redirects to the an external one.
 *
 * @param string $external_url
 *   The target URL.
 *
 * @return string
 *   An internal URL that points to the ELF redirection route.
 */
function elf_get_redirect_url($external_url) {
  $key = drupal_hmac_base64($external_url, drupal_get_private_key());
  return url('elf/go', array(
    'query' => array(
      'url' => $external_url,
      'key' => $key,
    ),
  ));
}

Functions

Namesort descending Description
elf_filter_info Implement hook_filter().
elf_filter_settings Hook_filter_info() settings callback.
elf_form_settings Admin settings form.
elf_form_settings_validate Form validation handler for elf_form_settings().
elf_get_redirect_url Creates a safe internal URL that redirects to the an external one.
elf_init Implement hook_init().
elf_menu Implement hook_menu().
elf_redirect Redirect the browser to the external URL from $_GET['url'].
elf_redirect_exit Deliver a "page not found" error to the browser and performs end-of-request tasks.
elf_replace Hook_filter_info() process callback.
elf_url_external Test if a URL is external