Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Branch: master
Fetching contributors…

Cannot retrieve contributors at this time

947 lines (847 sloc) 31.851 kB
<?php
// $Id$
/**
* @file description http://www.youtube.com/watch?v=Qo7qoonzTCE
*
* @todo
* - click to copy link as a Views field
* - add some watchdog logging
* - add hook for other modules to create additional/substitute long URL validation
* - add option/permission to reactivate URLs
*/
/**
* Implementation of hook_help().
*/
function shurly_help($path, $arg) {
$output = '';
switch ($path) {
case "admin/help#shurly":
$output = '<div style="white-space:pre-wrap">' . htmlentities(file_get_contents('README.markdown', FILE_USE_INCLUDE_PATH)) . '</div>';
break;
}
return $output;
}
/**
* Implementation of hook_menu()
*/
function shurly_menu() {
// callback for creation of URLs
$items = array();
$items['shurly'] = array(
'title' => 'Create URL',
'description' => 'Create a short URL',
'page callback' => 'drupal_get_form',
'page arguments' => array('shurly_create_form'),
'access arguments' => array('Create short URLs'),
);
$items['shurly/delete/%'] = array(
'title' => 'Delete URL',
'page callback' => 'drupal_get_form',
'page arguments' => array('shurly_confirm_delete_form', 2),
'access callback' => 'shurly_delete_access',
'access arguments' => array(2),
'type' => MENU_CALLBACK,
);
$items['admin/build/shurly/settings'] = array(
'title' => 'Settings',
'page callback' => 'drupal_get_form',
'page arguments' => array('shurly_settings_form'),
'access arguments' => array('Administer short URLs'),
'type' => MENU_LOCAL_TASK,
);
return $items;
}
/**
* Implementation of hook_perm()
*/
function shurly_perm() {
return array('Create short URLs', 'Enter custom URLs', 'View own URL stats', 'Delete own URLs', 'Administer short URLs');
}
function shurly_block($op = 'list', $delta = 0, $edit = array()) {
// create a block to add URL
switch ($op) {
case 'list':
$blocks['form'] = array('info' => t('Short URL form'));
$blocks['bookmarklet'] = array('info' => t('ShURLy bookmarklet'));
return $blocks;
case 'view':
// don't show the block when user is on the callback page
if ($delta == 'form' && user_access('Create short URLs') && arg(0) != 'shurly') {
$block = array('subject' => t('Create a short URL'),
'content' => drupal_get_form('shurly_create_form'));
return $block;
}
if ($delta == 'bookmarklet' && user_access('Create short URLs')) {
drupal_add_css(drupal_get_path('module', 'shurly') . '/shurly.css');
$block = array('subject' => t('Bookmarklet'),
'content' => t("<p>Drag this link to your bookmark bar to quickly create a short URL from any page: <a class=\"shurly-bookmarklet\" href=\"!jsurl\">!sitename</a><br />
Or install the <a href=\"http://github.com/downloads/Lullabot/shurly/shurly.safariextz\">Safari browser extension</a>.</p>", array('!jsurl' => "javascript:void(location.href='". _surl('shurly', array('absolute' => TRUE)) ."?url='+encodeURIComponent(location.href))", '!sitename' => variable_get('site_name', 'Drupal'))),
);
return $block;
}
break;
}
}
/**
* Implementation of hook_boot()
*/
function shurly_boot() {
// if the path has any unallowed characters in it (such as slashes),
// it's not a short URL, so we can bail out and save ourselves a database call
if ($_GET && $_GET['q'] && shurly_validate_custom($_GET['q'])) {
$row = db_fetch_object(db_query("SELECT rid, destination FROM {shurly} WHERE BINARY source = '%s' AND active = 1", $_GET['q']));
if ($row) {
shurly_goto($row);
}
}
}
/**
* Implementation of hook_theme()
*/
function shurly_theme($existing, $type, $theme, $path) {
return array(
'shurly_create_form' => array(
'arguments' => array('form' => NULL),
//'file' => 'shurly_form.inc',
),
);
}
/**
* Implementation of hook_views_api.
* Notifies the Views module that we're compatible with a particular API revision.
*/
function shurly_views_api() {
return array(
'api' => 2,
'path' => drupal_get_path('module', 'shurly') . '/views',
);
}
/**
* Access callback for deleting (deactivating) a URL
*/
function shurly_delete_access($rid) {
if (is_numeric($rid)) {
global $user;
if (!$user->uid) {
// anonymous users can't delete URLs
return FALSE;
}
// see if there's a row
$row = db_fetch_object(db_query('SELECT uid, source, destination FROM {shurly} WHERE rid = %d', $rid));
// if there's a row, and either the user is an admin, or they've got permission to create and they own this URL, then let them access
if ($row && (user_access('Administer short URLs') || (user_access('Delete own URLs') && $row->uid == $user->uid))) {
return TRUE;
}
}
return FALSE;
}
/**
* Confirmation form to delete a link
*/
function shurly_confirm_delete_form(&$form_state, $rid) {
$row = db_fetch_object(db_query('SELECT destination FROM {shurly} WHERE rid = %d', $rid));
$form['rid'] = array(
'#type' => 'value',
'#value' => $rid,
);
// the 'destination' argument here is a bit of a hack...
return confirm_form($form, t('Are you sure you want to delete and deactivate this URL?'), rawurldecode($_REQUEST['destination']), t('You are about to deactivate the link which redirects to %url. Once this item is deleted, you will not be able to create another link with the same short URL.', array('%url' => $row->destination)));
}
/**
* Submit handler for above form
*/
function shurly_confirm_delete_form_submit($form, &$form_state) {
drupal_set_message(t('URL has been deactivated'));
shurly_set_link_active($form_state['values']['rid'], 0);
}
/**
* The main form
*/
function shurly_create_form($form_state) {
// form to create new short URLs
$form['long_url'] = array(
'#title' => t('Enter a long URL to make short'),
'#type' => 'textfield',
'#maxlength' => 255,
'#default_value' => isset($form_state['storage']['shurly']['long_url']) ? $form_state['storage']['shurly']['long_url'] : (isset($_GET['url']) ? $_GET['url'] : 'http://'),
'#attributes' => array('tabindex' => 1),
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Shrink it!'),
'#attributes' => array('tabindex' => 3),
);
$short_default = user_access('Enter custom URLs') ? (isset($form_state['storage']['shurly']['short_url']) ? $form_state['storage']['shurly']['short_url'] : '') : '';
$form['short_url'] = array(
'#type' => 'textfield',
'#size' => 6,
'#field_prefix' => $GLOBALS['base_url'] .'/',
'#field_suffix' => ' <span class="shurly-choose">&lt;--- ' . t('create custom URL') . '</span>',
'#default_value' => $short_default,
'#access' => user_access('Enter custom URLs'),
'#attributes' => array('tabindex' => 2),
);
if (isset($form_state['storage']['shurly']['final_url'])) {
$form['result'] = array(
'#type' => 'textfield',
'#size' => 30,
'#value' => $form_state['storage']['shurly']['final_url'],
'#prefix' => '<div class="shurly-result">',
'#suffix' => '</div>',
'#field_prefix' => t('Your short URL: '),
'#field_suffix' => ' <div id="shurly-copy-container" style="position:relative;"><div id="shurly-copy">' . t('copy') . '</div></div>
<div><a href="http://twitter.com?status='. urlencode($form_state['storage']['shurly']['final_url']) .'">' . t('Create a Twitter message with this URL') . '</a></div>',
);
}
unset($form_state['storage']['shurly']);
return $form;
}
function theme_shurly_create_form($form) {
$path = drupal_get_path('module', 'shurly');
drupal_add_css($path . '/shurly.css');
drupal_add_js($path . '/zeroclipboard/ZeroClipboard.js');
drupal_add_js($path . '/shurly.js');
drupal_add_js("ZeroClipboard.setMoviePath( '". base_path() . $path .'/zeroclipboard/ZeroClipboard.swf' ."' );", 'inline');
$output = '';
$output .= '<div class="container-inline">';
$output .= drupal_render($form['long_url']);
$output .= drupal_render($form['submit']);
$output .= '</div>';
$output .= drupal_render($form['short_url']);
$output .= drupal_render($form);
return $output;
}
function shurly_create_form_validate(&$form, &$form_state) {
if (!user_access('Create short URLs')) {
form_set_error('', t('You do not have permission to create short URLs on this site'));
return;
}
$rate_limit = shurly_rate_limit_allowed();
if (!$rate_limit['allowed']) {
form_set_error('', t('Rate limit exceeded. You are limited to @rate requests per @time minute period.', array('@rate' => $rate_limit['rate'], '@time' => $rate_limit['time'])));
return;
}
$form_state['values']['long_url'] = trim($form_state['values']['long_url']);
$form_state['values']['short_url'] = trim($form_state['values']['short_url']);
$vals = $form_state['values'];
// check that they've entered a URL
if ($vals['long_url'] == '' || $vals['long_url'] == 'http://' || $vals['long_url'] == 'https://') {
form_set_error('long_url', t('Please enter a web URL'));
}
elseif (!shurly_validate_long($form_state['values']['long_url'], $form_state['values']['short_url'])) {
shurly_errors_to_form();
}
if ($vals['short_url'] != '') {
// a custom short URL has been entered
$form_state['custom'] = TRUE;
if (!shurly_validate_custom($vals['short_url'])) {
form_set_error('short_url', t('Short URL contains unallowed characters'));
}
elseif ($exists = shurly_url_exists($vals['short_url'], $vals['long_url'])) {
form_set_error('short_url', t('This short URL has already been used'));
}
elseif (!shurly_path_available($vals['short_url'])) {
form_set_error('short_url', t('This custom URL is reserved. Please choose another.'));
}
}
else {
// custom short URL field is empty
$form_state['custom'] = FALSE;
if ($exist = shurly_get_latest_short($vals['long_url'], $GLOBALS['user']->uid)) {
$short = $exist;
// we flag this as URL Exists so that it displays but doesn't get saved to the db
$form_state['url_exists'] = TRUE;
}
else {
$short = shurly_next_url();
}
$form_state['values']['short_url'] = $short;
$form_state['storage']['shurly']['short_url'] = $short;
}
}
function shurly_create_form_submit($form, &$form_state) {
// submit the short URL form
$long_url = $form_state['storage']['shurly']['long_url'] = $form_state['values']['long_url'];
$short_url = $form_state['storage']['shurly']['short_url'] = $form_state['values']['short_url'];
$final_url = $form_state['storage']['shurly']['final_url'] = rawurldecode(_surl($short_url, array('absolute' => TRUE)));
$custom = $form_state['custom'];
if (empty($form_state['url_exists'])) {
shurly_save_url($long_url, $short_url, NULL, $custom);
}
}
function shurly_settings_form($form_state) {
$form['shurly_throttle'] = array(
'#type' => 'fieldset',
'#title' => t('Rate limiting'),
'#tree' => TRUE,
'#description' => t('Limit requests by IP address. Leave blank for no rate limiting.<br /><strong>Note:</strong> Only roles with the \'Create short URLs\' permission are listed here. Change that permission <a href="!url">here</a>.', array('!url' => url('admin/user/permissions', array('fragment' => 'module-shurly')))),
);
$saved = variable_get('shurly_throttle', array());
foreach (user_roles(FALSE, 'Create short URLs') as $rid => $name) {
$form['shurly_throttle'][$rid] = array(
'#type' => 'fieldset',
'#title' => $name,
'#tree' => TRUE,
);
$form['shurly_throttle'][$rid]['rate'] = array(
'#type' => 'textfield',
'#size' => '3',
'#prefix' => '<div class="container-inline">',
'#field_suffix' => ' ' . t('requests'),
'#default_value' => $saved[$rid]['rate'],
);
$form['shurly_throttle'][$rid]['time'] = array(
'#type' => 'textfield',
'#size' => '3',
'#field_prefix' => t('within'),
'#field_suffix' => ' ' . t('minutes'),
'#default_value' => $saved[$rid]['time'],
'#suffix' => '</div>',
);
$form['shurly_throttle'][$rid]['weight'] = array(
'#type' => 'weight',
'#title' => t('Weight'),
'#default_value' => isset($saved[$rid]['weight']) ? $saved[$rid]['weight'] : 0,
'#description' => t('Order of this role when considering a user with multiple roles. A user\'s lightest role will take precedence.'),
);
}
return system_settings_form($form);
}
/**
* From http://www.php.net/manual/en/function.base-convert.php#52450
*
* Parameters:
* $num - your decimal integer
* $base - base to which you wish to convert $num (leave it 0 if you are providing $index or omit if you're using default (62))
* $index - if you wish to use the default list of digits (0-1a-zA-Z), omit this option, otherwise provide a string (ex.: "zyxwvu")
*/
function shurly_dec2any($num, $base=62, $index=FALSE) {
if (! $base ) {
$base = strlen( $index );
}
elseif (! $index ) {
// note: we could rearrange this string to get more random looking URLs
// another note, to create printable URLs, omit the following characters: 01lIO
$index = substr("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", 0, $base );
}
$out = "";
for ( $t = floor( log10( $num ) / log10( $base ) ); $t >= 0; $t-- ) {
$a = floor( $num / pow( $base, $t ) );
$out = $out . substr( $index, $a, 1 );
$num = $num - ( $a * pow( $base, $t ) );
}
return $out;
}
/**************************************************************
* Backport of the flood controls from Drupal 7
* these functions won't be needed in the D7 version of ShURLy
**************************************************************/
function shurly_cron() {
// Cleanup the flood.
db_query('DELETE FROM {shurly_flood} WHERE expiration < %d', time());
}
function shurly_flood_register_event($name, $window = 3600, $identifier = NULL) {
if (!isset($identifier)) {
$identifier = ip_address();
}
db_query("INSERT INTO {shurly_flood} (event, identifier, timestamp, expiration) VALUES ('%s', '%s', %d, %d)", $name, ip_address(), time(), time() + $window);
}
function shurly_flood_is_allowed($name, $threshold, $window = 3600, $identifier = NULL) {
if (!isset($identifier)) {
$identifier = ip_address();
}
$number = db_result(db_query("SELECT COUNT(*) FROM {shurly_flood} WHERE event = '%s' AND identifier = '%s' AND timestamp > %d", $name, $identifier, time() - $window));
return ($number < $threshold);
}
/******************************************************
* API functions
******************************************************
*/
/**
* API function to shorten a URL
* @arg $long_url - the long URL to shorten
* @arg $custom - optional custom short URL,
* user_access('enter custom URLs') should be checked prior to calling shurly_shorten()
*
* @return an array with the following keys
* 'success' => TRUE or FALSE
* 'error' => reason for for failure
* 'long_url' => the long url
* 'short_url' => the short url
*/
function shurly_shorten($long_url, $custom = NULL, $account = NULL) {
$success = FALSE;
$account = ($account) ? $account : $GLOBALS['user'];
$error = '';
$no_save = FALSE;
$rate_limit = shurly_rate_limit_allowed($account);
if (!$rate_limit['allowed']) {
$error = t('Rate limit exceeded. You are limited to @rate requests per @time minute period.', array('@rate' => $rate_limit['rate'], '@time' => $rate_limit['time']));
}
elseif (!shurly_validate_long($long_url)) {
$error = implode(' ', shurly_get_errors());
}
elseif (is_null($custom)) {
$latest = shurly_get_latest_short($long_url, $account->uid);
if ($latest) {
$no_save = TRUE;
$success = TRUE;
$short = $latest;
}
else {
$short = shurly_next_url();
}
}
else {
$short = $custom;
if (!shurly_validate_custom($short) || !shurly_path_available($short)) {
$error .= $error ? ' ' : '';
$error .= t('Invalid short URL.');
}
elseif (shurly_url_exists($short)) {
$error .= $error ? ' ' : '';
$error .= t('Existing short URL.');
}
}
if (!$error && !$no_save) {
if (shurly_save_url($long_url, $short, $account, $custom)) {
$success = TRUE;
}
else {
$error = t('Unknown database error.');
}
}
return array(
'success' => $success,
'error' => $error,
'longUrl' => $long_url,
'shortUrl' => isset($short) ? _surl($short, array('absolute' => TRUE)) : '',
'user' => (int)$account->uid,
);
}
function shurly_expand($short, $account = NULL) {
$error = '';
$success = FALSE;
$account = ($account) ? $account : $GLOBALS['user'];
$rate_limit = shurly_rate_limit_allowed($account);
if (!$rate_limit['allowed']) {
$error = t('Rate limit exceeded. You are limited to @rate requests per @time minute period.', array('@rate' => $rate_limit['rate'], '@time' => $rate_limit['time']));
}
elseif ($redirect = shurly_get_redirect($short, TRUE)) {
$success = TRUE;
$long_url = $redirect->destination;
}
else {
$error = t('Not found');
}
return array(
'success' => $success,
'error' => $error,
'longUrl' => $long_url,
'shortUrl' => _surl($short, array('absolute' => TRUE)),
'user' => (int)$account->uid,
);
}
/**
* Check rate limit for this user
* return an array in the following format
* array(
* 'allowed' => TRUE/FALSE
* 'rate' => number of requests allowed
* 'time' => period of time in minutes
* )
*/
function shurly_rate_limit_allowed($account = NULL) {
if (!isset($account)) {
global $user;
$account = $user;
}
$settings = variable_get('shurly_throttle', array());
if (!empty($settings) && is_array($account->roles)) {
$use_rid = array_shift(array_keys($account->roles));
// get list of roles with permission to create short URLs
$creating_roles = user_roles(FALSE, 'Create short URLs');
foreach($account->roles as $rid => $name) {
// check that this role has permission to create URLs, otherwise discard it
if (array_key_exists($rid, $creating_roles)) {
// find the lightest role... if roles are the same weight, use the next role
$settings[$rid]['weight'] = isset($settings[$rid]['weight']) ? $settings[$rid]['weight'] : 0;
$use_rid = $settings[$use_rid]['weight'] < $settings[$rid]['weight'] ? $use_rid : $rid;
}
}
}
if (!empty($settings) && is_numeric($settings[$use_rid]['rate']) && is_numeric($settings[$use_rid]['time'])) {
// see if it's allowed
$allowed = shurly_flood_is_allowed('shurly', $settings[$use_rid]['rate'], $settings[$use_rid]['time'] * 60);
// increment the counter
shurly_flood_register_event('shurly', $settings[$use_rid]['time'] * 60);
$return = array(
'allowed' => $allowed,
'rate' => $settings[$use_rid]['rate'],
'time' => $settings[$use_rid]['time'],
);
}
else {
// not set... don't do a flood check
$return = array(
'allowed' => TRUE,
);
}
return $return;
}
/**
* API function to save a URL
* @arg $custom is a TRUE/FALSE
*/
function shurly_save_url($long_url, $short_path, $account = NULL, $custom = NULL) {
if (is_null($account)) {
$account = $GLOBALS['user'];
}
$record = array();
$record['destination'] = $long_url;
$record['custom'] = $custom ? 1 : 0;
$record['created'] = time();
$record['source'] = $short_path;
$record['uid'] = $account->uid;
$record['count'] = $record['last_used'] = 0;
$record['active'] = 1;
return drupal_write_record('shurly', $record);
}
/**
* Activate or deactivate a link
* @arg $rid (int) the redirect id
* @arg $active (boolean) TRUE to make redirect active, FALSE to make it inactive
*/
function shurly_set_link_active($rid, $active) {
$record = db_fetch_array(db_query('SELECT * FROM {shurly} WHERE rid = %d', $rid));
if ($record) {
$record['rid'] = $rid;
$record['active'] = $active ? 1 : 0;
return drupal_write_record('shurly', $record, 'rid');
}
else {
return FALSE;
}
}
/**
* General function to validate long and short URLs
* calls hook_shurly_validate(&$long, &$custom)
*/
function shurly_validate(&$long, &$custom = NULL) {
$modules = module_implements('shurly_validate');
$return = TRUE;
foreach($modules as $module) {
$function = $module . '_shurly_validate';
if (!$function($long, $custom)) {
$return = FALSE;
break;
}
}
return $return;
}
/**
* Validate custom short URL string
*
* @return TRUE if valid, FALSE if invalid
*/
function shurly_validate_custom($custom) {
// check the length of the string
if (strlen($custom) == 0) {
return FALSE;
}
// disallow: #%&@*{}\:;<>?/+.,'"$|`^[] and space character
return preg_match('/[\/#%&\@\*\{\}\\:\;<>\?\+ \.\,\'\"\$\|`^\[\]]/u', $custom) ? FALSE : TRUE;
}
/**
* Validate a long URL
*
* Checks for:
* - a valid URL
* - it's not a link to an existing short URL
* - it's not a link to itself
*
* @param
* $long url - the long URL entered by user
* $custom - custom short URL entered by user
*
* @return
* BOOLEAN - TRUE if valid, FALSE if invalid
*/
function shurly_validate_long(&$long_url, $custom = NULL) {
$return = TRUE;
$match = FALSE;
// if the person didn't remove the original http:// from the field, pull it out
$long_url = preg_replace('!^http\://(http\://|https\://)!i', '\\1', $long_url);
$long_parse = parse_url($long_url);
$base_parse = parse_url($GLOBALS['base_url']);
if ($long_parse === FALSE || !$long_parse['host']) {
// malformed URL
// or no host in the URL
shurly_set_error(t('Invalid URL.'), 'long_url');
$return = FALSE;
}
elseif ($long_parse['scheme'] != 'http' && $long_parse['scheme'] != 'https') {
shurly_set_error(t('Invalid URL. Only http:// and https:// URLs are allowed.'), 'long_url');
$return = FALSE;
}
else {
$long_domain_parts = explode('.', $long_parse['host']);
$base_domain_parts = explode('.', $base_parse['host']);
// if last domain part of entered URL matches last part of this domain
if (isset($base_domain_parts[count($long_domain_parts) - 1]) && $long_domain_parts[count($long_domain_parts) - 1] == $base_domain_parts[count($long_domain_parts) - 1]) {
// and (if there's a 2nd to last)
if (count($long_domain_parts) >= 2) {
// check that 2nd to last matches
if (isset($base_domain_parts[count($long_domain_parts) - 2]) && $long_domain_parts[count($long_domain_parts) - 2] == $base_domain_parts[count($long_domain_parts) - 2]) {
// last 2 parts link to this domain
$match = TRUE;
}
}
else {
// there's only one part, and it links here
$match = TRUE;
}
// We only get down here if the long URL links to this domain
// by the way, we're ignoring any subdomain...
// so http://lbt.me/something and http://www.lbt.me/something are assumed to be the same
if ($match) {
// let's see if there's a $_GET['q'] in the long URL
$query = $long_parse['query'];
$query = html_entity_decode($query);
$query_array = explode('&', $query);
$queries = array();
foreach ($query_array as $val) {
$x = explode('=', $val);
$queries[$x[0]] = $x[1];
}
if ($queries['q']) {
// if there's a 'q' query, Drupal uses this instead of anything in the path
$path = $queries['q'];
}
else {
$path = $long_parse['path'];
}
// see if this is a link to an existing shortURL
// remove the leading "/" from path, if it exists
$path = array_pop(explode('/', $path, 2));
if ($path) {
// get the base path of this Drupal install
$base = array_pop(explode('/', base_path(), 2));
// remove the base from the path
if ($base) {
$path = preg_replace('!'. preg_quote($base, '!') .'!i', '', $path);
}
if (shurly_url_exists($path)) {
// link to existing short URL
shurly_set_error(t('Illegal URL'), 'long_url');
$return = FALSE;
}
if ($custom && $custom == $path) {
// they've created a link to itself
shurly_set_error(t('Recursive URL'), 'short_url');
$return = FALSE;
}
}
}
}
}
return $return;
}
/**
* Generate a random short URL
* Pretty much unused at this point
* this method could take a LOOOONG time on a site with lots of URLs
*/
function shurly_generate_random($len = NULL) {
if ($len == NULL) {
$len = variable_get('shurly_length', 4);
}
$charset = "abcdefghijklmnopqrstuvwxyz123456789";
$charlen = strlen($charset) - 1;
do {
$str = '';
for ($i=0; $i<$len; $i++) {
$str .= $charset[mt_rand(0, $charlen)];
}
// check that this string hasn't been used already
// check that the string is a valid (available) path
} while (shurly_url_exists($str) || !shurly_path_available($str));
return $str;
}
/**
* Return next available short URL
*/
function shurly_next_url() {
$count = variable_get('shurly_counter', 3249); // starts the URLs with 3 characters
do {
$count++;
// counter is stored as base 10
// $index is a-z, A-Z, 0-9, sorted randomly, with confusing characters (01lIO) removed - 57 characters
// a custom index can be created as a variable override in settings.php
$index = variable_get('shurly_index', 'kZ4oJ3Uwi5STqcpGNxfYgMQAdPWmsenh78XB26uLbEaRDzKrHVj9CyFtv');
$str = shurly_dec2any($count, NULL, $index);
// check that this string hasn't been used already
// check that the string is a valid (available) path
} while (shurly_url_exists($str) || !shurly_path_available($str));
variable_set('shurly_counter', $count);
return $str;
}
/**
* Checks to see if there's a menu handler, path alias, or language prefix for a given path
*
* @return TRUE if there are no conflicts
*/
function shurly_path_available($path) {
// check to see if path represents an enabled language
$languages = language_list();
if (array_key_exists($path, $languages)) {
return FALSE;
}
$return = TRUE;
// see if $path is an alias
$source = drupal_lookup_path('source', $path);
if ($source) {
// if so, set alias source to $path
$path = $source;
}
// check to see if $path has a menu callback
if (menu_get_item($path)) {
$return = FALSE;
}
return $return;
}
/**
* Check to see if this short URL already exists
*/
function shurly_url_exists($short, $long = NULL) {
$redirect = shurly_get_redirect($short);
$return = FALSE;
if ($redirect) {
$return = 'found';
}
if ($long && $redirect->destination == $long) {
$return = 'match';
}
return $return;
}
/**
* Given the short URL, return the long one
* NOTE: Always check $redirect->active before using the result
*/
function shurly_get_redirect($short_url, $check_active = FALSE) {
// we add 'BINARY' to ensure that we're doing a CASE SENSITIVE query
$query = "SELECT * FROM {shurly} WHERE BINARY source = '%s'";
if ($check_active) {
$query .= ' AND active = 1';
}
$redirect = db_fetch_object(db_query($query, $short_url));
return $redirect;
}
/**
* Get the latest generated short URL by a given user for a given long URL
*/
function shurly_get_latest_short($long, $uid) {
return db_result(db_query("SELECT source FROM {shurly} WHERE destination = '%s' AND uid = %d AND custom = 0 AND active = 1 ORDER BY rid DESC", $long, $uid));
}
/**
* A heavily modified version of drupal_goto() (which hasn't been bootstrapped during hook_boot()
*/
function shurly_goto($row) {
if (!$row || (isset($_REQUEST['redirect']) && $_REQUEST['redirect'] == 'false')) {
return;
}
// Allow other modules to implement hook_shurly_redirect_before()
// to add additional logging information to the database or perform other tasks
// _before() is probably best to use for altering the $row->destination
// Remember this is running during hook_boot(). Many Drupal functions are unavailable.
module_invoke_all('shurly_redirect_before', $row);
$url = $row->destination;
// Remove newlines from the URL to avoid header injection attacks.
$url = str_replace(array("\n", "\r"), '', $url);
// We do not want this while running update.php.
if (!defined('MAINTENANCE_MODE') || MAINTENANCE_MODE != 'update') {
// Allow modules to react to the end of the page request before redirecting.
module_invoke_all('exit', $url);
}
// Even though session_write_close() is registered as a shutdown function, we
// need all session data written to the database before redirecting.
session_write_close();
header('Location: '. $url, TRUE, 301);
// header has been sent... browser has been redirected
// now we can do any expensive operations
// update access information on this row
db_query('UPDATE {shurly} SET count = count + 1, last_used = %d WHERE rid = %d', time(), $row->rid);
// note: If possible, other modules should probably insert more information
// in the database by using hook_db_rewrite_sql() on the above query
// rather than creating a new db call
// Allow other modules to implement hook_shurly_redirect_after()
// _after() happens after the redirect has already been sent to browser.
// It's probably best for slower operations like additional database logging
// Remember this is running during hook_boot(). Many Drupal functions are unavailable.
module_invoke_all('shurly_redirect_after', $row);
// The "Location" header sends a redirect status code to the HTTP daemon. In
// some cases this can be wrong, so we make sure none of the code below the
// drupal_goto() call gets executed upon redirection.
exit();
}
/**
* Set an validation error
* We don't call form_set_error() directly because we don't want to be
* calling drupal_set_message() during API calls
*/
function shurly_set_error($message = NULL, $field = NULL) {
static $errors = array();
if ($message) {
$errors[] = array('message' => $message, 'field' => $field);
}
return $errors;
}
/**
* Get an array of errors (for delivering errors through web services)
*/
function shurly_get_errors() {
$errors = shurly_set_error();
$return = array();
foreach ($errors as $error) {
$return[] = $error['message'];
}
return $return;
}
/**
* Set errors on form items (for use with form API)
*/
function shurly_errors_to_form() {
$errors = shurly_set_error();
foreach ($errors as $error) {
form_set_error($error['field'], $error['message']);
}
}
/**
* Internal function to call url() without language prefixing or subdomain rewrites
*/
function _surl($path = NULL, $options = array()) {
$options['language'] = _shurly_language_stub();
return url($path, $options);
}
/**
* Internal function to call l() without language prefixing or subdomain rewrites
*/
function _sl($text, $path, $options = array()) {
$options['language'] = _shurly_language_stub();
return l($text, $path, $options);
}
/**
* Return default language object which will avoid redirects and subdomains
*
* This is necessary because we always want our short URLs to be
* the first item in the path, even if we've got another language enabled
*/
function _shurly_language_stub() {
static $language;
if (!isset($language)) {
$language = language_default();
$language->prefix = '';
$language->domain = '';
}
return $language;
}
Jump to Line
Something went wrong with that request. Please try again.