Skip to content

Commit

Permalink
Merge branch 'release/1.2.2'
Browse files Browse the repository at this point in the history
  • Loading branch information
Sommerregen committed May 10, 2015
2 parents dd5322b + e6e1a34 commit 2751b7a
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 78 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
# v1.2.2
## 05/10/2015

2. [](#improved)
* PSR fixes

# v1.2.1
## 03/24/2015

Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
## About

`External Links` is a plugin for [GetGrav.org](http://getgrav.org) used to differentiate between internal and external links. It also includes the ability of adding "rel=nofollow" to links as well and how external links shall be opened via "target" attribute. [Wikipedia](https://www.wikipedia.org/) is a well-known example.
`External Links` is a plugin for [GetGrav.org](http://getgrav.org) used to differentiate between internal and external links. It also includes the ability of adding "rel=nofollow" to links as and determines how external links shall be opened via the "target" attribute. [Wikipedia](https://www.wikipedia.org/) is a well-known example.

Furthermore this plugin enables you to specify multiple domains, each of them on a new line to prevent them from being seen as external sites.
Furthermore it enables you to specify multiple domains, each of them on a new line to prevent them from being seen as external sites.

If you are interested in seeing this plugin in action, here is a screenshot:

Expand Down Expand Up @@ -98,7 +98,7 @@ Sometimes you maybe wish to explicitly set a link to be "external". Although thi

Something you might want to do is to override the look and feel of the external links, and with Grav it is super easy.

Copy the stylesheet [css/external_links.css](css/external_links.css) into the `css` folder of your custom theme, and add it to the list of CSS files.
Copy the stylesheet [assets/css/external_links.css](assets/css/external_links.css) into the `css` folder of your custom theme, and add it to the list of CSS files.

```
/your/site/grav/user/themes/custom-theme/css/external_links.css
Expand Down
2 changes: 1 addition & 1 deletion blueprints.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: External Links
version: 1.2.1
version: 1.2.2
description: "This plugin adds small icons to external and mailto links, informing users the link will take them to a new site or open their email client."
icon: external-link
author:
Expand Down
119 changes: 63 additions & 56 deletions classes/ExternalLinks.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
* Helper class to add small icons to external and mailto links, informing
* users the link will take them to a new site or open their email client.
*/
class ExternalLinks {
class ExternalLinks
{
/**
* @var ExternalLinks
*/
Expand All @@ -39,20 +40,21 @@ class ExternalLinks {
*
* @return string The processed content
*/
public function process($content, $options = array()) {
public function process($content, $options = [])
{
// Get all <a> tags and process them
$content = preg_replace_callback('~<a[^>]*>.*?</a>~i',
function($match) use ($options) {
// Load PHP built-in DOMDocument class
if ( ($dom = $this->loadDOMDocument($match[0])) === NULL ) {
if (($dom = $this->loadDOMDocument($match[0])) === null) {
return $match[0];
}

$a = $dom->getElementsByTagName('a')->item(0);

// Process links with non-empty href attribute
$href = $a->getAttribute('href');
if ( strlen($href) == 0 ) {
if (strlen($href) == 0) {
return $match[0];
}

Expand All @@ -61,46 +63,46 @@ function($match) use ($options) {
$classes = array_filter(explode(' ', $class));

// Exclude links with specific class from processing
$exclude = $options->get('exclude.classes', NULL);
if ( $exclude AND !!array_intersect($exclude, $classes) ) {
$exclude = $options->get('exclude.classes', null);
if ($exclude && !!array_intersect($exclude, $classes)) {
return $match[0];
}

// Get domains to be seen as internal
$domains = $options->get('exclude.domains', array());
$domains = $options->get('exclude.domains', []);

// This is a mailto link.
if ( strpos($href, 'mailto:') === 0 ) {
if (strpos($href, 'mailto:') === 0) {
$classes[] = 'mailto';
}

// The link is external
elseif ( $this->isExternalUrl($href, $domains) ) {
elseif ($this->isExternalUrl($href, $domains)) {
// Add external class
$classes[] = 'external-link';

// Add target="_blank"
$target = $options->get('target');
if ( $target ) {
if ($target) {
$a->setAttribute('target', $target);
}

// Add no-follow.
$nofollow = $options->get('no_follow');
if ( $nofollow ) {
if ($nofollow) {
$rel = array_filter(explode(' ', $a->getAttribute('rel')));
if ( !in_array('nofollow', $rel) ) {
if (!in_array('nofollow', $rel)) {
$rel[] = 'nofollow';
$a->setAttribute('rel', implode(' ', $rel));
}
}

// Add image class to <a> if it has at least one <img> child element
$imgs = $a->getElementsByTagName('img');
if ( $imgs->length > 1 ) {
if ($imgs->length > 1) {
// Add "images" class to <a> element, if it has multiple child images
$classes[] = 'images';
} elseif ( $imgs->length == 1 ) {
} elseif ($imgs->length == 1) {
$imgNode = $imgs->item(0);

// Get image size
Expand All @@ -110,7 +112,7 @@ function($match) use ($options) {
$size = max($width, $height);

// Depending on size determine image type
$classes[] = ( (0 < $size) AND ($size <= 32) ) ? 'icon' : 'image';
$classes[] = ((0 < $size) && ($size <= 32)) ? 'icon' : 'image';
} else {
// Add "no-image" class to <a> element, if it has no child images
$classes[] = 'no-image';
Expand All @@ -124,7 +126,7 @@ function($match) use ($options) {
}

// Set class attribute
if ( count($classes) AND ($options->get('mode') === 'active') ) {
if (count($classes) && ($options->get('mode') === 'active')) {
$a->setAttribute('class', implode(' ', $classes));
}

Expand All @@ -148,23 +150,24 @@ function($match) use ($options) {
* @param string $url The URL to test.
* @param array $domains An array of domains to be seen as internal.
*
* @return boolean Returns TRUE, if the URL is external,
* FALSE otherwise.
* @return boolean Returns true, if the URL is external,
* false otherwise.
*/
protected function isExternalUrl($url, $domains = array()) {
protected function isExternalUrl($url, $domains = [])
{
static $allowed_protocols;
static $pattern;

// Statically store allowed protocols
if ( !isset($allowed_protocols) ) {
if (!isset($allowed_protocols)) {
$allowed_protocols = array_flip(array(
'ftp', 'http', 'https', 'irc', 'mailto', 'news', 'nntp',
'rtsp', 'sftp', 'ssh', 'tel', 'telnet', 'webcal')
);
}

// Statically store internal domains as a PCRE pattern.
if ( !isset($pattern) OR (count($domains) > 0)) {
if (!isset($pattern) || (count($domains) > 0)) {
$domains = array_merge($domains,
array(self::getGrav()['base_url_absolute']));

Expand All @@ -175,26 +178,26 @@ protected function isExternalUrl($url, $domains = array()) {
implode('|', $domains)) . ')#i';
}

$external = FALSE;
$external = false;
if ( !preg_match($pattern, $url) ) {
// Check if URL is external by extracting colon position
$colonpos = strpos($url, ':');
if ( $colonpos > 0 ) {
if ($colonpos > 0) {
// We found a colon, possibly a protocol. Verify.
$protocol = strtolower(substr($url, 0, $colonpos));
if ( isset($allowed_protocols[$protocol]) ) {
// The protocol turns out be an allowed protocol
$external = TRUE;
$external = true;
}
} elseif ( Utils::startsWith($url, 'www.') ) {
} elseif (Utils::startsWith($url, 'www.')) {
// We found an url without protocol, but with starting
// 'www' (sub-)domain
$external = TRUE;
$external = true;
}
}

// Only if a colon and a valid protocol was found return TRUE
return ($colonpos !== FALSE) AND $external;
// Only if a colon and a valid protocol was found return true
return ($colonpos !== false) && $external;
}

/**
Expand All @@ -206,7 +209,8 @@ protected function isExternalUrl($url, $domains = array()) {
* @return array Return the dimension of the image of the
* format array(width, height)
*/
protected function getImageSize($imgNode, $limit = 32) {
protected function getImageSize($imgNode, $limit = 32)
{
// Hold units (assume standard font with 16px base pixel size)
// Calculations based on pixels
$units = array(
Expand All @@ -231,12 +235,12 @@ protected function getImageSize($imgNode, $limit = 32) {
$height = 0;

// Determine image dimensions based on "src" atrribute
if ( $imgNode->hasAttribute('src') ) {
if ($imgNode->hasAttribute('src')) {
$src = $imgNode->getAttribute('src');

// Simple check if the URL is internal i.e. check if path exists
$path = $_SERVER['DOCUMENT_ROOT'] . $src;
if ( realpath($path) AND is_file($path) ) {
if (realpath($path) && is_file($path)) {
$size = @getimagesize($path);
} else {
// The URL is external; try to load it (default: 32 KB)
Expand All @@ -255,7 +259,7 @@ protected function getImageSize($imgNode, $limit = 32) {
$style = $imgNode->getAttribute('style');

// Width
if ( preg_match('~width:\s*(\d+)([a-z]+)~i', $style, $matches) ) {
if (preg_match('~width:\s*(\d+)([a-z]+)~i', $style, $matches)) {
$width = $matches[1];
// Convert unit to pixel
if ( isset($units[$matches[2]]) ) {
Expand All @@ -264,10 +268,10 @@ protected function getImageSize($imgNode, $limit = 32) {
}

// Height
if ( preg_match('~height:\s*(\d+)([a-z]+)~i', $style, $matches) ) {
if (preg_match('~height:\s*(\d+)([a-z]+)~i', $style, $matches)) {
$height = $matches[1];
// Convert unit to pixel
if ( isset($units[$matches[2]]) ) {
if (isset($units[$matches[2]])) {
$height *= $units[$matches[2]];
}
}
Expand All @@ -289,23 +293,24 @@ protected function getImageSize($imgNode, $limit = 32) {
*
* @return mixed Returns an array with up to 7 elements
*/
protected function getRemoteImageSize($uri, $limit = -1) {
protected function getRemoteImageSize($uri, $limit = -1)
{
// Create temporary file to store data from $uri
$tmp_name = tempnam(sys_get_temp_dir(), uniqid('ris'));
if ( $tmp_name === FALSE ) {
return FALSE;
if ($tmp_name === false) {
return false;
}

// Open temporary file
$tmp = fopen($tmp_name, 'rb');

// Check which method we should use to get remote image sizes
$allow_url_fopen = ini_get('allow_url_fopen') ? TRUE : FALSE;
$allow_url_fopen = ini_get('allow_url_fopen') ? true : false;
$use_curl = function_exists('curl_version');

// Use stream copy
if ( $allow_url_fopen ) {
$options = array();
if ($allow_url_fopen) {
$options = [];
if ( $limit > 0 ) {
// Loading number of $limit bytes
$options['http']['header'] = array('Range: bytes=0-' . $limit);
Expand All @@ -316,17 +321,17 @@ protected function getRemoteImageSize($uri, $limit = -1) {
@copy($uri, $tmp_name, $context);

// Use Curl
} elseif ( $use_curl ) {
} elseif ($use_curl) {
// Initialize Curl
$options = array(
CURLOPT_HEADER => FALSE, // Don't return headers
CURLOPT_FOLLOWLOCATION => TRUE, // Follow redirects
CURLOPT_AUTOREFERER => TRUE, // Set referrer on redirect
CURLOPT_HEADER => false, // Don't return headers
CURLOPT_FOLLOWLOCATION => true, // Follow redirects
CURLOPT_AUT||EFERER => true, // Set referrer on redirect
CURLOPT_CONNECTTIMEOUT => 120, // Timeout on connect
CURLOPT_TIMEOUT => 120, // Timeout on response
CURLOPT_MAXREDIRS => 10, // Stop after 10 redirects
CURLOPT_ENCODING => '', // Handle all encodings
CURLOPT_BINARYTRANSFER => TRUE, // Transfer as binary file
CURLOPT_BINARYTRANSFER => true, // Transfer as binary file
CURLOPT_FILE => $tmp, // Curl file
CURLOPT_URL => $uri, // URI
);
Expand All @@ -342,7 +347,7 @@ protected function getRemoteImageSize($uri, $limit = -1) {

// Abort request when more data is received
curl_setopt($curl, CURLOPT_BUFFERSIZE, 512); // More progress info
curl_setopt($curl, CURLOPT_NOPROGRESS, FALSE); // Monitor progress
curl_setopt($curl, CURLOPT_NOPROGRESS, false); // Monitor progress
curl_setopt($curl, CURLOPT_PROGRESSFUNCTION,
function($download_size, $downloaded, $upload_size, $uploaded) use ($limit) {
// If $downloaded exceeds $limit, returning non-zero breaks
Expand All @@ -361,7 +366,7 @@ function($download_size, $downloaded, $upload_size, $uploaded) use ($limit) {

// Retrieve image information
$info = array(0, 0, 'width="0" height="0"');
if ( filesize($tmp_name) > 0 ) {
if (filesize($tmp_name) > 0) {
$info = @getimagesize($tmp_name);
}

Expand All @@ -385,9 +390,10 @@ function($download_size, $downloaded, $upload_size, $uploaded) use ($limit) {
*
* @return DOMDocument DOMDocument object of content
*/
protected function loadDOMDocument($content) {
protected function loadDOMDocument($content)
{
// Clear previous errors
if ( libxml_use_internal_errors(TRUE) === TRUE ) {
if (libxml_use_internal_errors(true) === true) {
libxml_clear_errors();
}

Expand All @@ -405,8 +411,8 @@ protected function loadDOMDocument($content) {
@$document->loadHTML($content);

// Do nothing, if DOM is empty
if ( is_null($document->documentElement) ) {
return NULL;
if (is_null($document->documentElement)) {
return null;
}

return $document;
Expand All @@ -420,17 +426,18 @@ protected function loadDOMDocument($content) {
* @return string The outputted DOM document as HTML(5)
* compliant string
*/
protected function saveDOMDocument($document) {
protected function saveDOMDocument($document)
{
// Pretty print output
$document->preserveWhiteSpace = FALSE;
$document->formatOutput = TRUE;
$document->preserveWhiteSpace = false;
$document->formatOutput = true;

// Transform DOM document to valid HTML(5)
$content = '';
$body = $document->getElementsByTagName('body')->item(0);
foreach ( $body->childNodes as $node ) {
foreach ($body->childNodes as $node) {
// Expand empty tags (e.g. <br/> to <br></br>)
if ( ($html = $document->saveXML($node, LIBXML_NOEMPTYTAG)) !== FALSE ) {
if (($html = $document->saveXML($node, LIBXML_NOEMPTYTAG)) !== false) {
$content .= $html;
}
}
Expand Down
Loading

0 comments on commit 2751b7a

Please sign in to comment.