View source
<?php
use Drupal\Core\Cache\Cache;
use GuzzleHttp\Exception\RequestException;
use Drupal\Core\Url;
function shorten_help($path, $arg) {
$output = '';
switch ($path) {
case 'admin/help#shorten':
$output = '<p>' . t('This module shortens URLs.') . '</p>';
break;
}
return $output;
}
function shorten_permission() {
return [
'use Shorten URLs page' => [
'title' => t('Use URL shortener page'),
],
'manage Shorten URLs API keys' => [
'title' => t('Manage URL shortener API keys'),
'description' => t('Allow viewing and editing the API keys for third-party URL shortening services.'),
],
];
}
function shorten_flush_caches() {
return [];
}
function shorten_url($original = '', $service = '') {
if (!$original) {
$original = Url::fromRoute('<current>', [], [
'absolute' => 'true',
])
->toString();
}
$original_for_caching = urlencode($original);
if (!$service) {
$service = \Drupal::config('shorten.settings')
->get('shorten_service');
}
$services = \Drupal::moduleHandler()
->invokeAll('shorten_service');
if (isset($services[$service])) {
$url = _shorten_get_url($original, $services[$service], $service);
}
if (empty($url)) {
$service = \Drupal::config('shorten.settings')
->get('shorten_service_backup');
if (isset($services[$service])) {
$url = _shorten_get_url($original, $services[$service], $service);
}
if (empty($url)) {
$url = $original;
}
}
$url = trim($url);
if ($url != $original && \Drupal::config('shorten.settings')
->get('shorten_www')) {
if (strpos($url, 'http://') === 0) {
$url = mb_substr($url, 7);
if (strpos($url, 'www.') !== 0) {
$url = 'www.' . $url;
}
}
elseif (strpos($url, 'https://') === 0) {
$url = mb_substr($url, 8);
if (strpos($url, 'www.') !== 0) {
$url = 'www.' . $url;
}
}
}
$cache_duration = \Drupal::config('shorten.settings')
->get('shorten_cache_duration');
if ($url == $original) {
$expire = \Drupal::time()
->getRequestTime() + \Drupal::config('shorten.settings')
->get('shorten_cache_fail_duration');
}
elseif (is_numeric($cache_duration)) {
$expire = \Drupal::time()
->getRequestTime() + $cache_duration;
}
else {
$expire = Cache::PERMANENT;
}
\Drupal::moduleHandler()
->invokeAll('shorten_create', [
$original,
$url,
$service,
]);
return $url;
}
function _shorten_get_url($original, $api, $service) {
$method = mb_strtoupper(\Drupal::config('shorten.settings')
->get('shorten_method'));
$service = t('an unknown service');
if (is_string($api)) {
$url = shorten_fetch($api . $original);
$service = $api;
}
elseif (is_array($api)) {
$api += [
'custom' => FALSE,
'json' => FALSE,
];
if (!empty($api['url'])) {
$original = urlencode($original);
if (!empty($api['tag'])) {
$url = shorten_fetch($api['url'] . $original, $api['tag']);
}
elseif (!empty($api['json'])) {
$url = shorten_fetch($api['url'] . $original, $api['json'], 'json');
}
elseif (!$api['custom']) {
$url = shorten_fetch($api['url'] . $original);
}
$service = $api['url'];
}
elseif (is_string($api['custom']) && function_exists($api['custom'])) {
$method = t('A custom method: @method()', [
'@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 (isset($url)) {
if (mb_substr($url, 0, 7) == 'http://' || mb_substr($url, 0, 8) == 'https://') {
return $url;
}
}
\Drupal::logger('shorten')
->notice('%method failed to return an abbreviated URL from %service.', [
'%method' => $method,
'%service' => $service,
]);
return FALSE;
}
function shorten_shorten_service() {
$services = [];
if (\Drupal::config('shorten.settings')
->get('shorten_budurl')) {
$services['budurl'] = [
'url' => 'http://budurl.com/api/v1/budurls/shrink?api_key=' . \Drupal::config('shorten.settings')
->get('shorten_budurl') . '&format=txt&long_url=',
];
}
if (\Drupal::config('shorten.settings')
->get('shorten_ez')) {
$services['ez'] = [
'url' => 'http://ez.com/api/v1/ezlinks/shrink?api_key=' . \Drupal::config('shorten.settings')
->get('shorten_ez') . '&format=txt&long_url=',
];
}
if (\Drupal::config('shorten.settings')
->get('shorten_bitly_login') && \Drupal::config('shorten.settings')
->get('shorten_bitly_key')) {
$services['bit.ly'] = 'https://api-ssl.bitly.com/v3/shorten?format=txt&login=' . \Drupal::config('shorten.settings')
->get('shorten_bitly_login') . '&apiKey=' . \Drupal::config('shorten.settings')
->get('shorten_bitly_key') . '&x_login=' . \Drupal::config('shorten.settings')
->get('shorten_bitly_login') . '&x_apiKey=' . \Drupal::config('shorten.settings')
->get('shorten_bitly_key') . '&longUrl=';
$services['j.mp'] = 'https://api-ssl.bitly.com/v3/shorten?format=txt&domain=j.mp&login=' . \Drupal::config('shorten.settings')
->get('shorten_bitly_login') . '&apiKey=' . \Drupal::config('shorten.settings')
->get('shorten_bitly_key') . '&x_login=' . \Drupal::config('shorten.settings')
->get('shorten_bitly_login') . '&x_apiKey=' . \Drupal::config('shorten.settings')
->get('shorten_bitly_key') . '&longUrl=';
}
if (\Drupal::config('shorten.settings')
->get('shorten_googl')) {
$services['goo.gl'] = [
'custom' => '_shorten_googl',
];
}
$services += [
'is.gd' => 'https://is.gd/create.php?format=simple&url=',
'migre.me' => 'http://migre.me/api.txt?url=',
'Metamark' => 'http://metamark.net/api/rest/simple?long_url=',
'PeekURL' => 'http://peekurl.com/api.php?desturl=',
'qr.cx' => 'http://qr.cx/api/?longurl=',
'ri.ms' => 'http://ri.ms/api-create.php?url=',
'TinyURL' => 'http://tinyurl.com/api-create.php?url=',
];
if (\Drupal::config('shorten.settings')
->get('shorten_fwd4me')) {
$services['fwd4.me'] = 'http://api.fwd4.me/?key=' . \Drupal::config('shorten.settings')
->get('shorten_fwd4me') . '&url=';
}
if (\Drupal::moduleHandler()
->moduleExists('shurly')) {
$services['ShURLy'] = [
'custom' => '_shorten_shurly',
];
}
uksort($services, 'strcasecmp');
return $services;
}
function _shorten_googl($original) {
$url = 'https://www.googleapis.com/urlshortener/v1/url?key=' . \Drupal::config('shorten.settings')
->get('shorten_googl');
$context = stream_context_create();
stream_context_set_option($context, 'ssl', 'verify_host', TRUE);
$options = [
'method' => 'POST',
'data' => json_encode([
'longUrl' => $original,
]),
'context' => $context,
'headers' => [
'Content-type' => 'application/json',
],
];
$googl = shorten_fetch($url, 'id', 'json', $options);
if ($googl) {
return $googl;
}
\Drupal::logger('shorten')
->error('Error fetching shortened URL from goo.gl.', []);
return FALSE;
}
function _shorten_shurly($original) {
$result = shurly_shorten($original);
return $result['shortUrl'];
}
function shorten_fetch($url, $tag = '', $special = '', $options = []) {
$options += [
'headers' => [],
'method' => 'GET',
'data' => NULL,
'max_redirects' => 3,
'timeout' => \Drupal::config('shorten.settings')
->get('shorten_timeout'),
'context' => NULL,
];
if (\Drupal::config('shorten.settings')
->get('shorten_method') == 'php') {
try {
$response = \Drupal::httpClient()
->get($url, $options);
$result = stripslashes((string) $response
->getBody());
} catch (RequestException $e) {
\Drupal::logger('shorten')
->error('@code error shortening the URL @url using the PHP request method. Error message: %error', [
'@code' => $result->code,
'@url' => $url,
'%error' => $e,
]);
}
$contents = empty($result) ? NULL : $result;
}
elseif (\Drupal::config('shorten.settings')
->get('shorten_method') == 'curl') {
$c = curl_init();
curl_setopt($c, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($c, CURLOPT_CONNECTTIMEOUT, $options['timeout']);
curl_setopt($c, CURLOPT_MAXREDIRS, $options['max_redirects']);
if (defined('CURLOPT_REDIR_PROTOCOLS') && defined('CURLPROTO_HTTP') && defined('CURLPROTO_HTTPS')) {
curl_setopt($c, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
}
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 = [];
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);
}
$contents = curl_exec($c);
curl_close($c);
}
else {
return FALSE;
}
if (!$contents) {
return FALSE;
}
if ($tag) {
if (!$special) {
$contents = _shorten_xml($contents, $tag);
}
elseif ($special == 'json') {
$contents = json_decode($contents, TRUE);
$contents = shorten_get_value_from_json($contents, $tag);
}
}
if (!$contents || $contents == $url) {
return FALSE;
}
return $contents;
}
function shorten_get_value_from_json($contents, $expression) {
$exploded_expression = str_getcsv($expression, '.', '"', '\\');
if (count($exploded_expression) === 1) {
if (!isset($contents[$expression])) {
\Drupal::logger('shorten')
->error('json response does not contain specified member name or path expression: @expression', [
'@expression' => $expression,
]);
return NULL;
}
return $contents[$expression];
}
return shorten_get_path_in_array($contents, $exploded_expression);
}
function shorten_get_path_in_array($data, $path) {
if (isset($path[0])) {
$element = $path[0];
array_shift($path);
if (isset($data[$element])) {
$data = shorten_get_path_in_array($data[$element], $path);
}
}
return $data;
}
function _shorten_xml($xml, $tag) {
$start = strpos($xml, $tag) + mb_strlen($tag) + 1;
$end = strpos($xml, $tag, $start + 1) - 2;
$length = -(mb_strlen($xml) - $end);
return mb_substr($xml, $start, $length);
}
function shorten_save_js(array &$form, $form_state) {
$storage =& $form_state
->getStorage();
$step = $storage['step'];
$new_form = [];
$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;
}
function _shorten_service_form($last_service = NULL) {
if (\Drupal::config('shorten.settings')
->get('shorten_show_service') && _shorten_method_default() != 'none') {
$all_services = \Drupal::moduleHandler()
->invokeAll('shorten_service');
$services = [];
$disallowed = unserialize(\Drupal::config('shorten.settings')
->get('shorten_invisible_services'));
foreach ($all_services as $key => $value) {
if (!$disallowed[$key]) {
$services[$key] = $key;
}
}
$default = \Drupal::config('shorten.settings')
->get('shorten_service');
if ($default == 'none') {
$default = 'TinyURL';
}
if (isset($_SESSION['shorten_service']) && $_SESSION['shorten_service']) {
$default = $_SESSION['shorten_service'];
}
if (!empty($last_service)) {
$default = $last_service;
}
$count = count($services);
if ($count > 1) {
if (isset($services[$default])) {
unset($default);
$default = NULL;
}
return [
'#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 [
'#type' => 'value',
'#value' => array_pop($services),
];
}
return [
'#type' => 'value',
'#value' => $default,
];
}
}
function _shorten_method_default() {
if (function_exists('curl_exec')) {
return 'curl';
}
elseif (function_exists('file_get_contents')) {
return 'php';
}
return 'none';
}
function shorten_token_info() {
$info = [];
$info['tokens']['node']['short-url'] = [
'name' => t('Short Url'),
'description' => t('The shortened URL for the node. <strong>Deprecated:</strong> use [node:url:shorten] or [node:url:unaliased:shorten] instead.'),
];
$info['tokens']['url']['shorten'] = [
'name' => t('Shorten'),
'description' => t("Shorten URL using the default service."),
];
return $info;
}
function shorten_tokens($type, $tokens, array $data = [], array $options = []) {
$replacements = [];
if ($type == 'node' && !empty($data['node']) && isset($tokens['short-url'])) {
$node = (object) $data['node'];
$url = Url::fromUri('internal:/node' . $node->nid, [
'absolute' => TRUE,
'alias' => \Drupal::config('shorten.settings')
->get('shorten_use_alias'),
])
->toString();
$replacements[$tokens['short-url']] = shorten_url($url);
}
$url_options = [
'absolute' => TRUE,
];
if (isset($options['language'])) {
$url_options['language'] = $options['language'];
$language_code = $options['language']->language;
}
else {
$language_code = NULL;
}
$sanitize = !empty($options['sanitize']);
if ($type == 'url' && !empty($data['path'])) {
$path = $data['path'];
if (isset($data['options'])) {
$url_options = $data['options'] + $url_options;
}
foreach ($tokens as $name => $original) {
switch ($name) {
case 'shorten':
$value = Url::fromUri('internal:/' . $path, $url_options)
->toString();
$replacements[$original] = shorten_url($value);
break;
}
}
}
return $replacements;
}