Skip to content

Commit

Permalink
Merge branch 'release/1.6.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
Sommerregen committed Feb 22, 2018
2 parents 1bd0c49 + 8fa5b74 commit 1526ad6
Show file tree
Hide file tree
Showing 7 changed files with 170 additions and 119 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
# v1.6.0
## 02/22/2018

1. [](#new)
* Check for page redirects [#22](https://github.com/Sommerregen/grav-plugin-external-links/pull/22) (Check for redirecting pages if `links.redirects` is enabled, thanks to [@karfau](https://github.com/karfau))
2. [](#improved)
* Set `rel="noopener noreferrer"` to all external links [#21](https://github.com/Sommerregen/grav-plugin-external-links/issues/21)
* Updated French translation strings [#19](https://github.com/Sommerregen/grav-plugin-external-links/pull/19)
* Improved `README.md` and YAML files

# v1.5.3
## 02/18/2017

Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ exclude:
# - img.domain.com/*

links:
www: true # Autom. link any hostname that starts with "www."
www: false # Autom. link any hostname that starts with "www."
redirects: false # Also mark links as external, that link to pages that redirect to an external URL

schemes: # Allowed schemes
- 'http'
Expand Down Expand Up @@ -179,7 +180,7 @@ Thanks!
## License
Copyright (c) 2017 [Benjamin Regler][github]. See also the list of [contributors] who participated in this project.
Copyright (c) 2017+ [Benjamin Regler][github]. See also the list of [contributors] who participated in this project.
[Dual-licensed](LICENSE) for use under the terms of the [MIT][mit-license] or [GPLv3][gpl-license] licenses.
Expand Down
35 changes: 23 additions & 12 deletions blueprints.yaml
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
name: "External Links"
version: "1.5.3"
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"
name: 'External Links'
version: '1.6.0'
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:
name: "Sommerregen"
email: "sommerregen@benjamin-regler.de"
homepage: "https://github.com/sommerregen/grav-plugin-external-links"
keywords: ["external", "links", "filter", "formatter", "plugin"]
docs: "https://github.com/sommerregen/grav-plugin-external-links/blob/master/README.md"
bugs: "https://github.com/sommerregen/grav-plugin-external-links/issues"
license: "MIT/GPL"
name: 'Sommerregen'
email: 'sommerregen@benjamin-regler.de'
homepage: 'https://github.com/sommerregen/grav-plugin-external-links'
keywords: ['external', 'links', 'filter', 'formatter', 'plugin']
docs: 'https://github.com/sommerregen/grav-plugin-external-links/blob/master/README.md'
bugs: 'https://github.com/sommerregen/grav-plugin-external-links/issues'
license: 'MIT/GPL'

form:
validation: strict
Expand Down Expand Up @@ -81,6 +81,17 @@ form:
validate:
type: bool

links.redirects:
type: toggle
label: PLUGINS.EXTERNAL_LINKS.LINKS.REDIRECTS
help: PLUGINS.EXTERNAL_LINKS.LINKS.REDIRECTS_HELP
default: 0
options:
1: PLUGIN_ADMIN.YES
0: PLUGIN_ADMIN.NO
validate:
type: bool

links.schemes:
type: selectize
size: large
Expand Down Expand Up @@ -108,7 +119,7 @@ form:
size: large
label: PLUGINS.EXTERNAL_LINKS.EXCLUDE.DOMAINS
help: PLUGINS.EXTERNAL_LINKS.EXCLUDE.DOMAINS_HELP
placeholder: "localhost/*, img.domain.com/*"
placeholder: 'localhost/*, img.domain.com/*'
validate:
type: commalist

Expand Down
218 changes: 117 additions & 101 deletions classes/ExternalLinks.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,101 +43,109 @@ public function render($content, $options = [], $page = null)
{
// Get all <a> tags and process them
$content = preg_replace_callback('~<a[^>]*>.*?</a>~i',
function($match) use ($options, $page) {
// Load PHP built-in DOMDocument class
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) {
return $match[0];
}

// Get the class of the <a> element
$class = $a->hasAttribute('class') ? $a->getAttribute('class') : '';
$classes = array_filter(explode(' ', $class));

// Exclude links with specific class from processing
$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', []);

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

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

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

// Add no-follow.
$nofollow = $options->get('no_follow');
if ($nofollow) {
$rel = array_filter(explode(' ', $a->getAttribute('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) {
// Add "images" class to <a> element, if it has multiple child images
$classes[] = 'images';
} elseif ($imgs->length == 1) {
$imgNode = $imgs->item(0);

// Get image size
list($width, $height) = $this->getImageSize($imgNode);

// Determine maximum dimension of image size
$size = max($width, $height);

// Depending on size determine image type
$classes[] = ((0 < $size) && ($size <= 32)) ? 'icon' : 'image';
} else {
// Add "no-image" class to <a> element, if it has no child images
$classes[] = 'no-image';
function($match) use ($options, $page) {
// Load PHP built-in DOMDocument class
if (($dom = $this->loadDOMDocument($match[0])) === null) {
return $match[0];
}

// Add title (aka alert text)
if ($options->get('title')) {
$language = Grav::instance()['language'];
$message = $language->translate(['PLUGINS.EXTERNAL_LINKS.TITLE_MESSAGE']);
$a = $dom->getElementsByTagName('a')->item(0);

// Set default title to link else, set title as data attribute
$key = $a->hasAttribute('title') ? 'data-title' : 'title';
$a->setAttribute($key, $message);
// Process links with non-empty href attribute
$href = $a->getAttribute('href');
if (strlen($href) == 0) {
return $match[0];
}
}

// Set class attribute
if (count($classes) && ($options->get('mode') === 'active')) {
$a->setAttribute('class', implode(' ', $classes));
}
// Get the class of the <a> element
$class = $a->hasAttribute('class') ? $a->getAttribute('class') : '';
$classes = array_filter(explode(' ', $class));

// Exclude links with specific class from processing
$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', []);

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

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

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

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

// Set rel="noopener noreferrer"
$rel = $a->hasAttribute('rel') ? $a->getAttribute('rel') : '';
$rel = array_filter(explode(' ', $rel));

$rel[] = 'noopener';
$rel[] = 'noreferrer';
$a->setAttribute('rel', implode(' ', array_unique($rel)));

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

// Get image size
list($width, $height) = $this->getImageSize($imgNode);

// Determine maximum dimension of image size
$size = max($width, $height);

// Depending on size determine image type
$classes[] = ((0 < $size) && ($size <= 32)) ? 'icon' : 'image';
} else {
// Add "no-image" class to <a> element, if it has no child images
$classes[] = 'no-image';
}

// Add title (aka alert text)
if ($options->get('title')) {
$language = Grav::instance()['language'];
$message = $language->translate(['PLUGINS.EXTERNAL_LINKS.TITLE_MESSAGE']);

// Set default title to link else, set title as data attribute
$key = $a->hasAttribute('title') ? 'data-title' : 'title';
$a->setAttribute($key, $message);
}
}

// Save Dom document back to HTML representation
$html = $this->saveDOMDocument($dom);
return $html;
}, $content);
// Set class attribute
if (count($classes) && ($options->get('mode') === 'active')) {
$a->setAttribute('class', implode(' ', $classes));
}

// Save Dom document back to HTML representation
$html = $this->saveDOMDocument($dom);
return $html;
}, $content);

// Write content back to page
return $content;
Expand Down Expand Up @@ -200,18 +208,26 @@ protected function isExternalUrl($url, $domains = [], $page = null)
// The protocol turns out be an allowed protocol
$external = $url;
}
} elseif ($config->get('plugins.external_links.links.www')) {
// Remove possible path duplicate
$route = Grav::instance()['base_url'] . $page->route();
$href = Utils::startsWith($url, $route)
? ltrim(mb_substr($url, mb_strlen($route)), '/')
: $url;

// We found an url without protocol, but with starting 'www' (sub-)domain
if (Utils::startsWith($url, 'www.')) {
$external = 'http://' . $url;
} elseif (Utils::startsWith($href, 'www.')) {
$external = 'http://' . $href;
} else {
if ($config->get('plugins.external_links.links.www')) {
// Remove possible path duplicate
$route = Grav::instance()['base_url'] . $page->route();
$href = Utils::startsWith($url, $route)
? ltrim(mb_substr($url, mb_strlen($route)), '/')
: $url;

// We found an url without protocol, but with starting 'www' (sub-)domain
if (Utils::startsWith($url, 'www.')) {
$external = 'http://' . $url;
} elseif (Utils::startsWith($href, 'www.')) {
$external = 'http://' . $href;
}
}
if ($config->get('plugins.external_links.links.redirects')) {
$targetPage = Grav::instance()['pages']->find($url);
if ($targetPage && $targetPage->redirect()) {
$external = $this->isExternalUrl($targetPage->redirect(), $domains, $page);
}
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions external_links.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php
/**
* External Links v1.5.3
* External Links v1.6.0
*
* 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.
Expand All @@ -9,7 +9,7 @@
* http://benjamin-regler.de/license/
*
* @package External Links
* @version 1.5.3
* @version 1.6.0
* @link <https://github.com/sommerregen/grav-plugin-external-links>
* @author Benjamin Regler <sommerregen@benjamin-regler.de>
* @copyright 2017, Benjamin Regler
Expand Down
1 change: 1 addition & 0 deletions external_links.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ exclude:

links:
www: false # Autom. link any hostname that starts with "www."
redirects: false # Also mark links as external, that link to pages that redirect to an external URL

schemes: # Allowed schemes
- 'http'
Expand Down
Loading

0 comments on commit 1526ad6

Please sign in to comment.