Skip to content
shorten.module 23.1 KiB
Newer Older
Isaac Sukin's avatar
Isaac Sukin committed
<?php

/**
 * @file
 *   Shortens URLs via external services.
 */

/**
Isaac Sukin's avatar
Isaac Sukin committed
 * Implements hook_help().
Isaac Sukin's avatar
Isaac Sukin committed
 */
function shorten_help($path, $arg) {
  $output = '';
  switch ($path) {
Isaac Sukin's avatar
Isaac Sukin committed
    case 'admin/help#shorten':
      $output = '<p>' . t('This module shortens URLs.') . '</p>';
Isaac Sukin's avatar
Isaac Sukin committed
      break;
  }
  return $output;
}

/**
Isaac Sukin's avatar
Isaac Sukin committed
 * Implements hook_menu().
Isaac Sukin's avatar
Isaac Sukin committed
 */
function shorten_menu() {
  $items = array();
  $items['admin/config/services/shorten'] = array(
    'title' => 'Shorten',
Isaac Sukin's avatar
Isaac Sukin committed
    'description' => 'Adjust certain display settings for Shorten.',
Isaac Sukin's avatar
Isaac Sukin committed
    'page callback' => 'shorten_admin_form',
    'access arguments' => array('administer site configuration'),
Isaac Sukin's avatar
Isaac Sukin committed
    'file' => 'shorten.admin.inc',
  );
  $items['admin/config/services/shorten/general'] = array(
    'title' => 'General',
    'type' => MENU_DEFAULT_LOCAL_TASK,
    'access arguments' => array('administer site configuration'),
  $items['admin/config/services/shorten/keys'] = array(
    'title' => 'Shorten API Keys',
    'description' => 'Fill in API keys to use certain services.',
Isaac Sukin's avatar
Isaac Sukin committed
    'page callback' => 'shorten_keys_form',
    'access arguments' => array('manage Shorten URLs API keys'),
    'type' => MENU_LOCAL_TASK,
    'file' => 'shorten.admin.inc',
  );
  $items['shorten'] = array(
    'title' => 'Shorten URLs',
Isaac Sukin's avatar
Isaac Sukin committed
    'page callback' => 'shorten_form_shorten_form',
    'page arguments' => array('shorten_form_shorten'),
    'access arguments' => array('use Shorten URLs page'),
  );
Isaac Sukin's avatar
Isaac Sukin committed
  return $items;
}

Isaac Sukin's avatar
Isaac Sukin committed
 * Implements hook_block_info().
Isaac Sukin's avatar
Isaac Sukin committed
function shorten_block_info() {
  $blocks = array();
  $blocks['shorten']['info'] = t('Shorten URLs');
  $blocks['shorten']['visibility'] = 0;
  $blocks['shorten']['pages'] = 'shorten';
  $blocks['shorten_short']['info'] = t('Short URL');
  $blocks['shorten_short']['cache'] = DRUPAL_CACHE_PER_PAGE;
Isaac Sukin's avatar
Isaac Sukin committed
  return $blocks;
Isaac Sukin's avatar
Isaac Sukin committed
/**
Isaac Sukin's avatar
Isaac Sukin committed
 * Implements hook_block_view().
Isaac Sukin's avatar
Isaac Sukin committed
 */
Isaac Sukin's avatar
Isaac Sukin committed
function shorten_block_view($delta = '') {
  $block = array();
  if ($delta == 'shorten') {
Isaac Sukin's avatar
Isaac Sukin committed
    $block['subject'] = t('Shorten URLs');
Isaac Sukin's avatar
Isaac Sukin committed
    $block['content'] = shorten_form_shorten_form();
Isaac Sukin's avatar
Isaac Sukin committed
    return $block;
  }
Isaac Sukin's avatar
Isaac Sukin committed
  elseif ($delta == 'shorten_short') {
    $block['subject'] = t('Short URL');
Isaac Sukin's avatar
Isaac Sukin committed
    $block['content'] = shorten_current_form();
    return $block;
  }
Isaac Sukin's avatar
Isaac Sukin committed
}

/**
 * Implements hook_block_configure().
 */
function shorten_block_configure($delta = '') {
  if ($delta == 'shorten_short') {
    drupal_set_message(t('This block displays the short URL for the page on which it is shown, which can slow down uncached pages in some instances.'), 'warning');
Isaac Sukin's avatar
Isaac Sukin committed
}

Isaac Sukin's avatar
Isaac Sukin committed
 * Implements hook_perm().
Isaac Sukin's avatar
Isaac Sukin committed
function shorten_permission() {
  return array(
    'use Shorten URLs page' => array(
      'title' => t('Use URL shortener page'),
    ),
    'manage Shorten URLs API keys' => array(
      'title' => t('Manage URL shortener API keys'),
      'description' => t('Allow viewing and editing the API keys for third-party URL shortening services.'),
    ),
  );
Isaac Sukin's avatar
Isaac Sukin committed
 * Implements hook_flush_caches().
 */
function shorten_flush_caches() {
  if (variable_get('shorten_cache_clear_all', TRUE)) {
    return array('cache_shorten');
  }
  return array();
Isaac Sukin's avatar
Isaac Sukin committed
/**
 * Retrieves and beautifies the abbreviated URL.
 * This is the main API function of this module.
 *
 * @param $original
 *   The URL of the page for which to create the abbreviated URL.  If not passed
 *   uses the current page.
 * @param $service
 *   The service to use to abbreviate the URL.
 *   For services available by default, see shorten_shorten_service().
 * @return
 *   An abbreviated URL.
 */
function shorten_url($original = '', $service = '') {
Isaac Sukin's avatar
Isaac Sukin committed
  if (!$original) {
    $original = url($_GET['q'], array('absolute' => TRUE, 'alias' => !variable_get('shorten_use_alias', 1)));
Isaac Sukin's avatar
Isaac Sukin committed
  }
  if (!$service) {
    $service = variable_get('shorten_service', 'is.gd');
  }
  $cached = cache_get($original, 'cache_shorten');
  if (!empty($cached->data) && REQUEST_TIME < $cached->expire) {
Isaac Sukin's avatar
Isaac Sukin committed
    return $cached->data;
  }
  $services = module_invoke_all('shorten_service');
  if (isset($services[$service])) {
    $url = _shorten_get_url($original, $services[$service], $service);
Isaac Sukin's avatar
Isaac Sukin committed
  // If the primary service fails, try the secondary service.
Isaac Sukin's avatar
Isaac Sukin committed
  if (!$url) {
    $service = variable_get('shorten_service_backup', 'TinyURL');
    if (isset($services[$service])) {
      $url = _shorten_get_url($original, $services[$service], $service);
Isaac Sukin's avatar
Isaac Sukin committed
    // If the secondary service fails, use the original URL.
Isaac Sukin's avatar
Isaac Sukin committed
    if (!$url) {
      $url = $original;
    }
  }
Isaac Sukin's avatar
Isaac Sukin committed
  $url = trim($url); // Redundant for most services.
  // Replace "http://" with "www." if the URL is abbreviated because it's shorter.
  if ($url != $original && variable_get('shorten_www', FALSE)) {
    if (strpos($url, 'http://') === 0) {
      $url = drupal_substr($url, 7);
      if (strpos($url, 'www.') !== 0) {
Isaac Sukin's avatar
Isaac Sukin committed
        $url = 'www.' . $url;
      }
    }
    elseif (strpos($url, 'https://') === 0) {
      $url = drupal_substr($url, 8);
      if (strpos($url, 'www.') !== 0) {
Isaac Sukin's avatar
Isaac Sukin committed
        $url = 'www.' . $url;
Isaac Sukin's avatar
Isaac Sukin committed
  }
  $cache_duration = variable_get('shorten_cache_duration', 1814400);
  // Only cache failed retrievals for a limited amount of time.
  if ($url == $original) {
    $expire = REQUEST_TIME + variable_get('shorten_cache_fail_duration', 1800);
  }
  elseif (is_numeric($cache_duration)) {
Isaac Sukin's avatar
Isaac Sukin committed
    $expire = REQUEST_TIME + $cache_duration;
  }
  else {
    $expire = CACHE_PERMANENT;
  cache_set($original, $url, 'cache_shorten', $expire);
  module_invoke_all('shorten_create', $original, $url, $service);
Isaac Sukin's avatar
Isaac Sukin committed
  return $url;
}

/**
 * Shortens URLs. Times out after three (3) seconds.
 *
 * @param $original
 *   The URL of the page for which to retrieve the abbreviated URL.
 * @param $api
 *   A string or array used to retrieve a shortened URL. If it is an array, it
 *   can have the elements 'custom,' 'url,' 'tag,' 'json,' and 'args.'
 * @param $service
 *   The service to use to abbreviate the URL.
 *   For services available by default, see shorten_shorten_service().
Isaac Sukin's avatar
Isaac Sukin committed
 * @return
 *   An abbreviated URL.
 */
function _shorten_get_url($original, $api, $service) {
  $method = drupal_strtoupper(variable_get('shorten_method', _shorten_method_default()));
Isaac Sukin's avatar
Isaac Sukin committed
  $service = t('an unknown service');
  if (is_string($api)) {
    $url = shorten_fetch($api . $original);
Isaac Sukin's avatar
Isaac Sukin committed
    $service = $api;
Isaac Sukin's avatar
Isaac Sukin committed
    // Merge in defaults.
    $api += array(
      'custom' => FALSE,
      'json' => FALSE,
    );
Isaac Sukin's avatar
Isaac Sukin committed
      // Typically $api['custom'] == 'xml' although it doesn't have to.
Isaac Sukin's avatar
Isaac Sukin committed
        $url = shorten_fetch($api['url'] . $original, $api['tag']);
      }
      elseif (!empty($api['json'])) {
        $url = shorten_fetch($api['url'] . $original, $api['json'], 'json');
      }
      elseif (!$api['custom']) {
Isaac Sukin's avatar
Isaac Sukin committed
        $url = shorten_fetch($api['url'] . $original);
      }
Isaac Sukin's avatar
Isaac Sukin committed
      $service = $api['url'];
    }
    elseif (is_string($api['custom']) && function_exists($api['custom'])) {
      $method =  t('A custom method: @method()', array('@method' => $api['custom']));
      if (!empty($api['args']) && is_array($api['args'])) {
        $args = $api['args'];
        array_unshift($args, $original);
        $url = call_user_func_array($api['custom'], $args);
      }
      else {
        $url = call_user_func($api['custom'], $original);
  if ($url) {
    if (drupal_substr($url, 0, 7) == 'http://' || drupal_substr($url, 0, 8) == 'https://') {
      return $url;
    }
Isaac Sukin's avatar
Isaac Sukin committed
  }
  watchdog('shorten', '%method failed to return an abbreviated URL from %service.', array('%method' => $method, '%service' => $service), WATCHDOG_NOTICE, $url);
  return FALSE;
}

/**
Isaac Sukin's avatar
Isaac Sukin committed
 * Implements hook_shorten_service().
Isaac Sukin's avatar
Isaac Sukin committed
 */
function shorten_shorten_service() {
  if (variable_get('shorten_budurl', '')) {
    $services['budurl'] = array(
Isaac Sukin's avatar
Isaac Sukin committed
      'url' => 'http://budurl.com/api/v1/budurls/shrink?api_key=' . variable_get('shorten_budurl', '') . '&format=txt&long_url=',
    );
  }
  if (variable_get('shorten_ez', '')) {
    $services['ez'] = array(
Isaac Sukin's avatar
Isaac Sukin committed
      'url' => 'http://ez.com/api/v1/ezlinks/shrink?api_key=' . variable_get('shorten_ez', '') . '&format=txt&long_url=',
  if (variable_get('shorten_bitly_login', '') && variable_get('shorten_bitly_key', '')) {
Isaac Sukin's avatar
Isaac Sukin committed
    $services['bit.ly'] = 'http://api.bit.ly/v3/shorten?format=txt&login=' .
      variable_get('shorten_bitly_login', '') . '&apiKey=' .
      variable_get('shorten_bitly_key', '') . '&x_login=' .
      variable_get('shorten_bitly_login', '') . '&x_apiKey=' .
      variable_get('shorten_bitly_key', '') . '&longUrl=';
    $services['j.mp'] = 'http://api.bit.ly/v3/shorten?format=txt&domain=j.mp&login=' .
      variable_get('shorten_bitly_login', '') . '&apiKey=' .
      variable_get('shorten_bitly_key', '') . '&x_login=' .
      variable_get('shorten_bitly_login', '') . '&x_apiKey=' .
      variable_get('shorten_bitly_key', '') . '&longUrl=';
  if (variable_get('shorten_googl', '')) {
    $services['goo.gl'] = array(
      'custom' => '_shorten_googl',
    );
  }
Isaac Sukin's avatar
Isaac Sukin committed
    'cli.gs' => 'http://cli.gs/api/v1/cligs/create?appid=drupal&url=',
    'is.gd' => 'http://is.gd/api.php?longurl=',
    'migre.me' => 'http://migre.me/api.txt?url=',
Isaac Sukin's avatar
Isaac Sukin committed
    'Metamark' => 'http://metamark.net/api/rest/simple?long_url=',
    'PeekURL' => 'http://peekurl.com/api.php?desturl=',
    'qr.cx' => 'http://qr.cx/api/?longurl=',
Isaac Sukin's avatar
Isaac Sukin committed
    'redir.ec' => 'http://redir.ec/_api/rest/redirec/create?appid=drupal&url=',
Isaac Sukin's avatar
Isaac Sukin committed
    'ri.ms' => 'http://ri.ms/api-create.php?url=',
    'TinyURL' => 'http://tinyurl.com/api-create.php?url=',
  );
  if (module_exists('shorturl')) {
    $services['Drupal ShortURL module'] = array(
      'custom' => '_shorten_shorturl',
  if (variable_get('shorten_cligs', '')) {
Isaac Sukin's avatar
Isaac Sukin committed
    $services['cli.gs'] = 'http://cli.gs/api/v1/cligs/create?appid=drupal&key=' . variable_get('shorten_cligs', '') . '&url=';
  if (variable_get('shorten_fwd4me', '')) {
Isaac Sukin's avatar
Isaac Sukin committed
    $services['fwd4.me'] = 'http://api.fwd4.me/?key=' . variable_get('shorten_fwd4me', '') . '&url=';
Isaac Sukin's avatar
Isaac Sukin committed
  if (variable_get('shorten_redirec', '')) {
Isaac Sukin's avatar
Isaac Sukin committed
    $services['redir.ec'] = 'http://redir.ec/_api/rest/redirec/create?appid=drupal&apikey=' . variable_get('shorten_redirec', '') . '&url=';
  if (module_exists('shurly')) {
    $services['ShURLy'] = array(
      'custom' => '_shorten_shurly',
    );
  }
Isaac Sukin's avatar
Isaac Sukin committed
  // Alphabetize. ksort() is case-sensitive.
  uksort($services, 'strcasecmp');
  return $services;
Isaac Sukin's avatar
Isaac Sukin committed
}

/**
 * Helps get a shortened URL from Goo.gl.
 */
function _shorten_googl($original) {
  $url = 'https://www.googleapis.com/urlshortener/v1/url?key='. variable_get('shorten_googl', '');
  $context = stream_context_create();
  stream_context_set_option($context, 'ssl', 'verify_host', TRUE);
  $options = array(
    'method' => 'POST',
    'data' => json_encode(array('longUrl' => $original)),
    'context' => $context,
    'headers' => array('Content-type' => 'application/json'),
  );
  $googl = shorten_fetch($url, 'id', 'json', $options);
  if ($googl) {
    return $googl;
  }
  watchdog('shorten', 'Error fetching shortened URL from goo.gl.', array(), WATCHDOG_ERROR, $url);
  return FALSE;
}

/**
 * Helps get a shortened URL via the Short URLs module.
 */
function _shorten_shorturl($original) {
  return shorturl_shorten($original, TRUE);
/**
 * Helps get a shortened URL via the ShURLy module.
 */
function _shorten_shurly($original) {
  $result = shurly_shorten($original);
  return $result['shortUrl'];
Isaac Sukin's avatar
Isaac Sukin committed
/**
 * Downloads the response of the URL abbreviation service.
 *
 * @param $url
 *   The URL which will return an abbreviated URL from any service. Includes
 *   both the service and the URL to be shortened.
 * @param $tag
 *   If the response is XML, the tag within which to look for the shortened URL.
 * @param $special
 *   A special format the service will return. Currently only supports 'json.'
 * @param $options
 *   An associative array of options to allow executing an advanced request.
 *   Valid keys include anything that would work with drupal_http_request()
 *   except that $options['context'] is only used with the PHP request method.
Isaac Sukin's avatar
Isaac Sukin committed
 * @return
 *   An abbreviated URL or FALSE if fetching the abbreviated URL fails.
Isaac Sukin's avatar
Isaac Sukin committed
 */
function shorten_fetch($url, $tag = '', $special = '', $options = array()) {
  $options += array(
    'headers' => array(),
    'method' => 'GET',
    'data' => NULL,
    'max_redirects' => 3,
    'context' => NULL, // only used with the PHP method
  );
  if (variable_get('shorten_method', _shorten_method_default()) == 'php') {
    $result = drupal_http_request($url, $options);
    $contents = isset($result->data) ? $result->data : NULL;
    if (!empty($result->error)) {
      watchdog(
        'shorten',
        '@code error shortening the URL @url using the PHP request method. Error message: %error',
        array('@code' => $result->code, '@url' => $url, '%error' => $result->error),
        WATCHDOG_ERROR,
        $url
      );
    }
Isaac Sukin's avatar
Isaac Sukin committed
  }
  elseif (variable_get('shorten_method', _shorten_method_default()) == 'curl') {
Isaac Sukin's avatar
Isaac Sukin committed
    $c = curl_init();
    curl_setopt($c, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($c, CURLOPT_CONNECTTIMEOUT, $options['timeout']);
    // https://drupal.org/node/1481634
    //if (ini_get('open_basedir') == '' && ini_get('safe_mode' == 'Off')) {
    //  curl_setopt($c, CURLOPT_FOLLOWLOCATION, TRUE);
    //}
    curl_setopt($c, CURLOPT_MAXREDIRS, $options['max_redirects']);
    // defined() checks due to https://drupal.org/node/1469400
    // Specifying the protocol is necessary to avoid malicious redirects to POP
    // in libcurl 7.26.0 to 7.28.1
    if (defined('CURLOPT_REDIR_PROTOCOLS') && defined('CURLPROTO_HTTP') && defined('CURLPROTO_HTTPS')) {
      curl_setopt($c, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
    }
Isaac Sukin's avatar
Isaac Sukin committed
    curl_setopt($c, CURLOPT_URL, $url);
    if ($options['method'] != 'GET') {
      $uri = @parse_url($url);
      if ($uri['scheme'] == 'http') {
        curl_setopt($c, CURLOPT_PORT, isset($uri['port']) ? $uri['port'] : 80);
        if (defined('CURLOPT_PROTOCOLS') && defined('CURLPROTO_HTTP')) {
          curl_setopt($c, CURLOPT_PROTOCOLS, CURLPROTO_HTTP);
        }
      }
      elseif ($uri['scheme'] == 'https') {
        curl_setopt($c, CURLOPT_PORT, isset($uri['port']) ? $uri['port'] : 443);
        if (defined('CURLOPT_PROTOCOLS') && defined('CURLPROTO_HTTPS')) {
          curl_setopt($c, CURLOPT_PROTOCOLS, CURLPROTO_HTTPS);
        }
      }
      if (!empty($options['headers'])) {
        $curl_headers = array();
        foreach ($options['headers'] as $key => $value) {
          $curl_headers[] = trim($key) . ': ' . trim($value);
        }
        curl_setopt($c, CURLOPT_HTTPHEADER, $curl_headers);
      }
      if ($options['method'] == 'POST') {
        curl_setopt($c, CURLOPT_POST, TRUE);
        curl_setopt($c, CURLOPT_POSTFIELDS, $options['data']);
      }
      else {
        curl_setopt($c, CURLOPT_CUSTOMREQUEST, $options['method']);
      }
      curl_setopt($c, CURLOPT_SSL_VERIFYPEER, FALSE);
    }
Isaac Sukin's avatar
Isaac Sukin committed
    $contents = curl_exec($c);
    curl_close($c);
  }
  else {
Isaac Sukin's avatar
Isaac Sukin committed
  }
Isaac Sukin's avatar
Isaac Sukin committed
  if ($tag) {
    if (!$special) {
      $contents = _shorten_xml($contents, $tag);
    }
    elseif ($special == 'json') {
      $contents = json_decode($contents, TRUE);
      $contents = $contents[$tag];
    }
Isaac Sukin's avatar
Isaac Sukin committed
  }
  if (!$contents || $contents == $url) {
    return FALSE;
  }
Isaac Sukin's avatar
Isaac Sukin committed
  return $contents;
}

/**
 * Parses the value between tags in an XML document.
 *
 * @param $xml
 *   The contents of the XML document.
 * @param $tag
 *   The tag to get the value from.
 * @return
 *   The value from the specified tag, typically an abbreviated URL.
 */
function _shorten_xml($xml, $tag) {
  $start = strpos($xml, $tag) + drupal_strlen($tag) + 1;
  $end = strpos($xml, $tag, $start + 1) - 2;
  $length = -(drupal_strlen($xml) - $end);
  return drupal_substr($xml, $start, $length);
}

/**
Isaac Sukin's avatar
Isaac Sukin committed
 * Returns HTML representing the shorten form.
Isaac Sukin's avatar
Isaac Sukin committed
 */
Isaac Sukin's avatar
Isaac Sukin committed
function shorten_form_shorten_form() {
  $form = drupal_get_form('shorten_form_shorten');
  return drupal_render($form);
Isaac Sukin's avatar
Isaac Sukin committed
}

/**
 * Builds a form which allows shortening of a URL via the UI.
Isaac Sukin's avatar
Isaac Sukin committed
 */
Isaac Sukin's avatar
Isaac Sukin committed
function shorten_form_shorten($form, &$form_state) {
  drupal_add_js(drupal_get_path('module', 'shorten') . '/shorten.js');
  //Form elements between ['opendiv'] and ['closediv'] will be refreshed via AHAH on form submission.
  $form['opendiv'] = array(
Isaac Sukin's avatar
Isaac Sukin committed
    '#markup' => '<div id="shorten_replace">',
Isaac Sukin's avatar
Isaac Sukin committed
  if (!isset($form_state['storage'])) {
    $form_state['storage'] = array('step' => 0);
Isaac Sukin's avatar
Isaac Sukin committed
  }
  if (isset($form_state['storage']['short_url'])) {
    // This whole "step" business keeps the form element from being cached.
    $form['shortened_url_' . $form_state['storage']['step']] = array(
      '#type' => 'textfield',
      '#title' => t('Shortened URL'),
Isaac Sukin's avatar
Isaac Sukin committed
      '#default_value' => $form_state['storage']['short_url'],
      '#size' => 25,
Isaac Sukin's avatar
Isaac Sukin committed
      '#attributes' => array('class' => array('shorten-shortened-url')),
Isaac Sukin's avatar
Isaac Sukin committed
  $form['url_' . $form_state['storage']['step']] = array(
Isaac Sukin's avatar
Isaac Sukin committed
    '#type' => 'textfield',
    '#title' => t('URL'),
    '#default_value' => '',
    '#required' => TRUE,
    '#size' => 25,
Isaac Sukin's avatar
Isaac Sukin committed
    '#attributes' => array('class' => array('shorten-long-url')),
Isaac Sukin's avatar
Isaac Sukin committed
  );
  //Form elements between ['opendiv'] and ['closediv'] will be refreshed via AHAH on form submission.
  $form['closediv'] = array(
Isaac Sukin's avatar
Isaac Sukin committed
    '#markup' => '</div>',
Isaac Sukin's avatar
Isaac Sukin committed
  $last_service = NULL;
  if (isset($form_state['storage']['service'])) {
    $last_service = $form_state['storage']['service'];
  }
  $service = _shorten_service_form($last_service);
Isaac Sukin's avatar
Isaac Sukin committed
  if (is_array($service)) {
    $form['service'] = $service;
  }
  $form['shorten'] = array(
    '#type' => 'submit',
    '#value' => t('Shorten'),
Isaac Sukin's avatar
Isaac Sukin committed
    '#ajax' => array(
      'callback' => 'shorten_save_js',
      'wrapper' => 'shorten_replace',
      'effect' => 'fade',
      'method' => 'replace',
    ),
Isaac Sukin's avatar
Isaac Sukin committed
  );
  return $form;
}

/**
 * Validate function for the Shorten form.
 * It's hard to really figure out what a valid URL is. We might reasonably
 * expect http://example.com, https://example.com, www.example.com, or even
 * just example.com to be correctly shortened by respective services. So instead
 * of dealing with this logic ourselves, we just check that there is at least a
 * period in the string, because there must be a TLD, and we check length.
Isaac Sukin's avatar
Isaac Sukin committed
 */
function shorten_form_shorten_validate($form, &$form_state) {
Isaac Sukin's avatar
Isaac Sukin committed
  $url = $form_state['values']['url_' . $form_state['storage']['step']];
  if (drupal_strlen($url) > 4) {
    if (!strpos($url, '.', 1)) {
      form_set_error('url', t('Please enter a valid URL.'));
    }
  }
  else {
Isaac Sukin's avatar
Isaac Sukin committed
    form_set_error('url', t('Please enter a valid URL.'));
  }
}

/**
 * Submit function for the Shorten form.
 */
function shorten_form_shorten_submit($form, &$form_state) {
  $service = '';
  if ($form_state['values']['service']) {
    $service = $form_state['values']['service'];
  }
Isaac Sukin's avatar
Isaac Sukin committed
  $shortened = shorten_url($form_state['values']['url_' . $form_state['storage']['step']], $service);
  if ($form_state['values']['service']) {
    $_SESSION['shorten_service'] = $form_state['values']['service'];
  }
Isaac Sukin's avatar
Isaac Sukin committed
  drupal_set_message(t('%original was shortened to %shortened', array('%original' => $form_state['values']['url_' . $form_state['storage']['step']], '%shortened' => $shortened)));
  $form_state['rebuild'] = TRUE;
Isaac Sukin's avatar
Isaac Sukin committed
  if (empty($form_state['storage'])) {
    $form_state['storage'] = array();
  }
  $form_state['storage']['short_url'] = $shortened;
  $form_state['storage']['service']   = $form_state['values']['service'];
  if (isset($form_state['storage']['step'])) {
    $form_state['storage']['step']++;
  }
  else {
    $form_state['storage']['step'] = 0;
  }
Isaac Sukin's avatar
Isaac Sukin committed
}

/**
 * JS callback for submitting the Shorten form.
 */
Isaac Sukin's avatar
Isaac Sukin committed
function shorten_save_js($form, $form_state) {
  $step = $form_state['storage']['step'];
  $new_form = array();
  $new_form['opendiv'] = $form['opendiv'];
  $new_form['shortened_url_' . $step] = $form['shortened_url_' . $step];
  $new_form['url_' . $step] = $form['url_' . $step];
  $new_form['closediv'] = $form['closediv'];
  return $new_form;
Isaac Sukin's avatar
Isaac Sukin committed
/**
 * Form which displays a list of URL shortening services.
Isaac Sukin's avatar
Isaac Sukin committed
 *
 * @param $last_service
 *   The last service used on this page, if applicable.
Isaac Sukin's avatar
Isaac Sukin committed
 */
Isaac Sukin's avatar
Isaac Sukin committed
function _shorten_service_form($last_service = NULL) {
  if (variable_get('shorten_show_service', FALSE) && _shorten_method_default() != 'none') {
    $all_services = module_invoke_all('shorten_service');
    $disallowed = variable_get('shorten_invisible_services', array());
    foreach ($all_services as $key => $value) {
      if (!$disallowed[$key]) {
        $services[$key] = $key;
      }
Isaac Sukin's avatar
Isaac Sukin committed
    }
    $default = variable_get('shorten_service', 'is.gd');
    if ($default == 'none') {
      $default = 'TinyURL';
Isaac Sukin's avatar
Isaac Sukin committed
    }
Isaac Sukin's avatar
Isaac Sukin committed
    // Remember the last service that was used.
    if (isset($_SESSION['shorten_service']) && $_SESSION['shorten_service']) {
      $default = $_SESSION['shorten_service'];
    }
Isaac Sukin's avatar
Isaac Sukin committed
    // Anonymous users don't have $_SESSION, so we use the last service used on this page, if applicable.
    if (!empty($last_service)) {
      $default = $last_service;
    }
    $count = count($services);
    if ($count > 1) {
      if (!$services[$default]) {
        unset($default);
      }
      return array(
        '#type' => 'select',
        '#title' => t('Service'),
        '#description' => t('The service to use to shorten the URL.'),
        '#required' => TRUE,
        '#default_value' => $default,
        '#options' => $services,
      );
    }
    elseif ($count) {
      return array(
        '#type' => 'value',
        '#value' => array_pop($services),
      );
    }
      '#type' => 'value',
      '#value' => $default,
Isaac Sukin's avatar
Isaac Sukin committed
  }
Isaac Sukin's avatar
Isaac Sukin committed
/**
 * Returns HTML representing the short URL form.
 */
function shorten_current_form() {
  $form = drupal_get_form('shorten_current');
  return drupal_render($form);
/**
 * Builds a textfield to show the short URL for the current page.
 */
Isaac Sukin's avatar
Isaac Sukin committed
function shorten_current($form, $form_state) {
  drupal_add_js(drupal_get_path('module', 'shorten') . '/shorten.js');
  $form['this_shortened'] = array(
    '#type' => 'textfield',
    '#size' => 25,
  );
  return $form;
}

/**
 * Determines the default method for retrieving shortened URLs.
 * cURL is the preferred method, but sometimes it's not installed.
 */
function _shorten_method_default() {
  if (function_exists('curl_exec')) {
    return 'curl';
  }
  elseif (function_exists('file_get_contents')) {
 * Implements hook_token_info().
Isaac Sukin's avatar
Isaac Sukin committed
 */
function shorten_token_info() {
  if (variable_get('shorten_generate_token', 1)) {
    $return['tokens']['node']['short-url'] = array(
      'name' => t('Short Url'),
      'description' => t('The shortened URL for the node.'),
 * Implements hook_tokens().
Isaac Sukin's avatar
Isaac Sukin committed
 */
function shorten_tokens($type, $tokens, array $data = array(), array $options = array()) {
  $replacements = array();
  if ($type == 'node'
  && !empty($data['node'])
  && isset($tokens['short-url'])
  && variable_get('shorten_generate_token', 1)) {
    $node = (object) $data['node'];
    $replacements[$tokens['short-url']] = shorten_url(url('node/' . $node->nid, array(
      'absolute' => TRUE,
      'alias' => variable_get('shorten_use_alias', 1),
    )));
  }
  return $replacements;
Isaac Sukin's avatar
Isaac Sukin committed
}