You are here

gotwo.module in Go - url redirects 7

Same filename and directory in other branches
  1. 5 gotwo.module
  2. 6 gotwo.module

Module that provides easy to use redirection links. A redirection link would be like: http://examples.org/go/a_label http://examples.org/go/123546 http://examples.org/go/or/like/this

It's much like url aliases except these are redirects

File

gotwo.module
View source
<?php

/**
 * @file
 * Module that provides easy to use redirection links. A redirection link
 * would be like:
 *  http://examples.org/go/a_label
 *  http://examples.org/go/123546
 *  http://examples.org/go/or/like/this
 *
 * It's much like url aliases except these are redirects
 */
define('GOTWO_CREATE', 1);

/**
 * Implements hook_permission().
 */
function gotwo_permission() {
  return array(
    'administer gotwo' => array(
      'title' => t('Administer gotwo'),
      'description' => t('Perform maintenance tasks for Gotwo module.'),
    ),
    'edit gotwo redirects' => array(
      'title' => t('Edit gotwo redirects'),
      'description' => t("Allow users to add or edit 'go' URLs."),
    ),
    'view gotwo redirects' => array(
      'title' => t('View gotwo redirects'),
      'description' => t("Allow users to view the existing 'go' URLs."),
    ),
  );
}

/**
 * Implements hook_menu().
 */
function gotwo_menu() {
  $items['go'] = array(
    'page callback' => '_gotwo_do_redir',
    'access callback' => TRUE,
    'type' => MENU_CALLBACK,
  );
  $items['admin/structure/gotwo'] = array(
    'title' => 'Go redirects',
    'description' => 'You can use the &lt;go&gt; tags just like the &lt;a&gt; for nicer urls.',
    'page callback' => '_gotwo_list',
    'access arguments' => array(
      'view gotwo redirects',
    ),
    'file' => 'gotwo.admin.inc',
  );
  $items['admin/structure/gotwo/list'] = array(
    'title' => 'List',
    'access arguments' => array(
      'view gotwo redirects',
    ),
    'file' => 'gotwo.admin.inc',
    'type' => MENU_DEFAULT_LOCAL_TASK,
    'weight' => -10,
  );
  $items['admin/structure/gotwo/add'] = array(
    'title' => 'Add redirect',
    'description' => 'Add a new Go redirect',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'gotwo_add_form',
    ),
    'access arguments' => array(
      'edit gotwo redirects',
    ),
    'file' => 'gotwo.admin.inc',
    'type' => MENU_LOCAL_TASK,
  );
  $items['admin/structure/gotwo/delete/%gotwo'] = array(
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'gotwo_delete_form',
      4,
    ),
    'access arguments' => array(
      'edit gotwo redirects',
    ),
    'file' => 'gotwo.admin.inc',
    'type' => MENU_CALLBACK,
  );
  $items['admin/structure/gotwo/reset/%gotwo'] = array(
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'gotwo_reset_form',
      4,
    ),
    'access arguments' => array(
      'edit gotwo redirects',
    ),
    'file' => 'gotwo.admin.inc',
    'type' => MENU_CALLBACK,
  );
  $items['admin/config/search/gotwo'] = array(
    'title' => 'Go settings',
    'description' => 'Configure URL parameters and disclaimer options.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'gotwo_admin_settings_form',
    ),
    'access arguments' => array(
      'administer gotwo',
    ),
    'file' => 'gotwo.admin.inc',
  );
  return $items;
}

/**
 * Menu helper function to verify if gotwo id exists.
 */
function gotwo_load($gid) {
  return db_query("SELECT * FROM {gotwo} WHERE gid = :gid", array(
    ':gid' => $gid,
  ))
    ->fetchObject();
}

/**
 * Redirect the user to the given location.
 */
function _gotwo_do_redir() {
  $args = func_get_args();
  $src = implode('/', $args);

  // $src may be an GID or a source URL.
  $result = db_query("SELECT * FROM {gotwo} WHERE src = :src OR gid = :gid", array(
    ':src' => $src,
    ':gid' => intval($src),
  ))
    ->fetchObject();
  if (!$result) {
    drupal_not_found();
  }
  else {

    // Count up the current click counter +1.
    db_update('gotwo')
      ->fields(array(
      'cnt' => NULL,
    ))
      ->expression('cnt', 'cnt + 1')
      ->condition('gid', $result->gid)
      ->execute();
    if (variable_get('gotwo_disclaimer_boolean', FALSE)) {

      // Display the Disclaimer
      $disclaimer_title = variable_get('gotwo_disclaimer_title', 'Disclaimer');
      $disclaimer_text = variable_get('gotwo_disclaimer_text', '');
      $disclaimer_time = variable_get('gotwo_disclaimer_time', 0);
      drupal_set_title($disclaimer_title);
      $tokens = array(
        '%url',
        '%seconds',
      );
      $token_values = array(
        check_url($result->dst),
        $disclaimer_time,
      );
      $page_content = filter_xss_admin(str_replace($tokens, $token_values, $disclaimer_text));

      // Should we refresh?
      if ($disclaimer_time > 0) {
        $disclaimer_meta = array(
          '#tag' => 'meta',
          '#attributes' => array(
            'http-equiv' => 'refresh',
            'content' => $disclaimer_time . ';url=' . check_url($result->dst),
          ),
        );
        drupal_add_html_head($disclaimer_meta, 'gotwo_disclaimer_refresh');
      }
      return $page_content;
    }
    else {

      // Parse the URL.
      $uri = drupal_parse_url($result->dst);
      drupal_goto($uri['path'], array(
        'query' => $uri['query'],
        'fragment' => $uri['fragment'],
      ));
    }
  }
}

/**
 * Implements hook_filter_info().
 */
function gotwo_filter_info() {
  $filters['gotwo_link'] = array(
    'title' => t('"Go" redirection filter'),
    'description' => t('Automatically creates redirection urls. &lt;go href=""&gt;&lt;/go&gt; tags are nicely translated to &lt;a href=""&gt;&lt;/a&gt; tags.'),
    'process callback' => '_gotwo_link',
    /* 'settings callback' => '_gotwo_link_settings',
       'default settings' => array(
         'gotwo_numeric' => variable_get('gotwo_numeric', FALSE),
         'gotwo_transliteration' => variable_get('gotwo_transliteration', TRUE),
         'gotwo_separator' => variable_get('gotwo_separator', '-'),
         'gotwo_max_length' => variable_get('gotwo_max_length', 128),
       ), */
    'tips callback' => '_gotwo_link_tips',
  );
  return $filters;
}

/**
 * Process callback for the GO filter.
 *
 * This function will load the text into a DOM object, search for <go> elements,
 * saves non-existent href's and title's to the {gowto} table and replace the
 * <go> tags with it's real <a> links. If defined in the <go> link the href is
 * altered as requested. The full text with all links replaced is returned.
 *
 * @param $text
 *   A raw text with all <go> links to be processed.
 * @return
 *   A raw text with all <go> links replaced with real <a> links.
 */
function _gotwo_link($text, $filter) {
  $html_dom = filter_dom_load($text);
  $goNodesToRemove = array();
  foreach ($html_dom
    ->getElementsByTagName('go') as $goNode) {

    // Clone <go> link to <a> link and keep all attributes intact.
    $linkNode = $goNode->ownerDocument
      ->createElement('a');
    if ($goNode->attributes->length) {
      foreach ($goNode->attributes as $attribute) {
        $linkNode
          ->setAttribute($attribute->nodeName, $attribute->nodeValue);
      }
    }

    // Clone all <go> child nodes inside the new <a> tag.
    while ($goNode
      ->hasChildNodes()) {
      $childNodes = $goNode->childNodes;
      $linkNode
        ->appendChild($childNodes
        ->item(0));
    }

    // Verify if the url exists in the {gotwo} table. If the url is missing,
    // add the url with link title to the {gotwo} table.
    $href = $goNode
      ->getAttribute('href');
    $title = $goNode
      ->getAttribute('title');
    if (!empty($href)) {
      $linkNode
        ->setAttribute('href', _gotwo_get_url($href, empty($title) ? NULL : $title));
    }

    // Insert the new $linkNode before the previous $goNode.
    $goNode->parentNode
      ->insertBefore($linkNode, $goNode);

    // Save $goNode to remove array. We cannot remove the child here or the DOM
    // index will be cluttered and every second <go> link is not replacement.
    $goNodesToRemove[] = $goNode;
  }

  // Now we are able to remove the child's without loosing the index.
  foreach ($goNodesToRemove as $goNode) {
    $goNode->parentNode
      ->removeChild($goNode);
  }
  $text = filter_dom_serialize($html_dom);
  return trim($text);
}

/**
 * Settings callback for the GO filter.
 */

/*
function _gotwo_link_settings($form, &$form_state, $filter, $format, $defaults) {
  $filter->settings += $defaults;

  $settings['gotwo_numeric'] = array(
    '#type' => 'checkbox',
    '#title' => t('Create numerical URLs'),
    '#description' => t('Use numbers instead of a human readable URL. Use "go/1234" instead of "go/some/location".'),
    '#default_value' => $filter->settings['gotwo_numeric'],
  );

  $transliterate_dependencies = '<div class="admin-dependencies">';
  $transliterate_dependencies .= t('Depends on: !dependencies', array('!dependencies' => (module_exists('transliteration') ? t('@module (<span class="admin-enabled">enabled</span>)', array('@module' => 'Transliteration')) : t('@module (<span class="admin-disabled">disabled</span>)', array('@module' => 'Transliteration')))));
  $transliterate_dependencies .= '</div>';

  $settings['gotwo_transliteration'] = array(
    '#type' => 'checkbox',
    '#title' => t('Transliterate URLs'),
    '#description' => t('Enables <a href="!url">transliteration</a> of URLs. Generally spoken, it takes Unicode text and tries to represent it in US-ASCII characters (universally displayable, unaccented characters) by attempting to transliterate the pronunciation expressed by the text in some other writing system to Roman letters.', array('!url' => 'http://drupal.org/project/transliteration')) . $transliterate_dependencies,
    '#default_value' => $filter->settings['gotwo_transliteration'],
    '#disabled' => (module_exists('transliteration') ? FALSE : TRUE),
  );
  $settings['gotwo_separator'] = array(
    '#type' => 'select',
    '#title' => t('URL separator'),
    '#description' => t('All spaces in URLs can be replaced with an underscore or hyphen. If set to "-", an URL of "Lorem ipsum dolor" becomes "Lorem-ipsum-dolor".'),
    '#default_value' => $filter->settings['gotwo_separator'],
    '#options' => array(
      '-' => t('Hyphen (-)'),
      '_' => t('Underscore (_)'),
    ),
  );
  $settings['gotwo_max_length'] = array(
    '#type' => 'textfield',
    '#title' => t('Maximum length of target labels'),
    '#description' => t('Target labels are the parts after the "go/" part of the shown url. The absolute maximum is 128.'),
    '#default_value' => $filter->settings['gotwo_max_length'],
    '#element_validate' => array('_gotwo_element_max_length_validate'),
    '#size' => 10,
  );

  return $settings;
}
*/

/**
 * Filter tips callback for GO filter.
 */
function _gotwo_link_tips($filter, $format, $long = FALSE) {
  return t('You can use the &lt;go&gt; tags just like the &lt;a&gt; for nicer urls.');
}

/**
 * Element settings validation.
 */
function _gotwo_element_max_length_validate(&$element, &$form_state) {
  $value = isset($element['#value']) ? $element['#value'] : $element['#default_value'];
  if ($value < 10 || $value > 128) {
    form_error($element, t('The value for %element-title cannot be less than 10 and not more than 128.', array(
      '%element-title' => $element['#title'],
    )));
  }
  return $element;
}

/**
 * Return the GO url for a given link.
 */
function _gotwo_get_url($url, $src = NULL, $flags = GOTWO_CREATE) {
  $res = _gotwo_get($url, $src, $flags);
  if (!$res) {
    return url($url);
  }
  if (variable_get('gotwo_numeric', FALSE)) {
    return url('go/' . $res->gid);
  }
  return url('go/' . $res->src);
}

/**
 * Return the GO object url for a given link.
 */
function _gotwo_get($url, $src = NULL, $flags = GOTWO_CREATE) {

  // Only add valid URLs to the database. Otherwise the disclaimer reload may fail.
  if (!valid_url($url)) {
    return FALSE;
  }

  // If there is no title to mangle, use the url without schema instead.
  if (!$src) {
    $src = preg_replace('#^(http(s)?://)#', '', $url);
  }
  $src = _gotwo_cleanstring($src);
  $maxlength = variable_get('gotwo_max_length', 128);
  $res = db_query("SELECT * FROM {gotwo} WHERE src = :src AND dst = :dst", array(
    ':src' => $src,
    ':dst' => $url,
  ))
    ->fetchObject();
  if ($res === FALSE) {
    $res = db_query("SELECT * FROM {gotwo} WHERE src = gid+:src AND dst = :dst", array(
      ':src' => '/' . $src,
      ':dst' => $url,
    ))
      ->fetchObject();
    if (!empty($res)) {
      $src_old = substr($res->gid . '/' . $src, 0, $maxlength);
      if ($src_old != $res->src) {
        $res == FALSE;
      }
    }
  }
  if ($res === FALSE) {
    $res = new stdClass();
    if ($flags & GOTWO_CREATE) {

      // Force unique src.
      $res = db_query("SELECT * FROM {gotwo} WHERE src = :src", array(
        ':src' => $src,
      ))
        ->fetchObject();
      if (!empty($res)) {

        // TODO: find a better solution.
        // Insert a dummy first with an uniqe src value to get the 'gid' value.
        $gid = db_insert('gotwo')
          ->fields(array(
          'src' => uniqid(),
          'dst' => $url,
        ))
          ->execute();
        $src = substr($gid . '/' . $src, 0, $maxlength);
        db_update('gotwo')
          ->fields(array(
          'src' => $src,
        ))
          ->condition('gid', $gid)
          ->execute();
        $res->gid = $gid;
        $res->src = $src;
        $res->dst = $url;
      }
      else {
        $gid = db_insert('gotwo')
          ->fields(array(
          'src' => $src,
          'dst' => $url,
        ))
          ->execute();
        $res->gid = $gid;
        $res->src = $src;
        $res->dst = $url;
      }
    }
    else {
      return FALSE;
    }
  }
  return $res;
}

/**
 * Mangle the input for url friendlyness. Based on pathauto_cleanstring from pathauto.module
 */
function _gotwo_cleanstring($string) {

  // Transliterate the URL with transliteration module.
  if (module_exists('transliteration') && variable_get('gotwo_transliteration', TRUE)) {
    $string = transliteration_get($string, '?', language_default('language'));
  }
  $output = str_replace("'", "", $string);
  $pattern = '#[^a-zA-Z0-9./]+# ';
  $separator = variable_get('gotwo_separator', '-');
  $output = preg_replace($pattern, $separator, $output);
  if ($separator) {
    if (ctype_alnum($separator)) {
      $seppattern = $separator;
    }
    else {
      $seppattern = '\\' . $separator;
    }
    $output = preg_replace("/^{$seppattern}+|{$seppattern}+\$/", "", $output);
  }
  $maxlength = variable_get('gotwo_max_length', 128);
  $output = substr($output, 0, $maxlength);
  return $output;
}

Functions

Namesort descending Description
gotwo_filter_info Implements hook_filter_info().
gotwo_load Menu helper function to verify if gotwo id exists.
gotwo_menu Implements hook_menu().
gotwo_permission Implements hook_permission().
_gotwo_cleanstring Mangle the input for url friendlyness. Based on pathauto_cleanstring from pathauto.module
_gotwo_do_redir Redirect the user to the given location.
_gotwo_element_max_length_validate Element settings validation.
_gotwo_get Return the GO object url for a given link.
_gotwo_get_url Return the GO url for a given link.
_gotwo_link Process callback for the GO filter.
_gotwo_link_tips Filter tips callback for GO filter.

Constants

Namesort descending Description
GOTWO_CREATE @file Module that provides easy to use redirection links. A redirection link would be like: http://examples.org/go/a_label http://examples.org/go/123546 http://examples.org/go/or/like/this