Version Description
- Additional performance changes for long post/page when Apply Globally is off.
- Links with no anchor text will have nofollow and/or target set, but no external icon
- Improved detection and handling of anchor links as internal.
- Initial support for AddThis placeholder links
Download this release
Release Info
Developer | Mike_Koepke |
Plugin | External Links |
Version | 6.2 |
Comparing to | |
See all releases |
Code changes from version 6.1 to 6.2
- readme.txt +8 -0
- sem-external-links.php +176 -165
readme.txt
CHANGED
@@ -60,6 +60,14 @@ The plugin supports a non-started rel="follow" attribute on links to override th
|
|
60 |
|
61 |
== Change Log ==
|
62 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
63 |
= 6.1 =
|
64 |
|
65 |
- The nofollow attribute was not being set if a certain combination of the global, follow comments, set nofollow settings were set.
|
60 |
|
61 |
== Change Log ==
|
62 |
|
63 |
+
= 6.2 =
|
64 |
+
|
65 |
+
- Additional performance changes for long post/page when Apply Globally is off.
|
66 |
+
- Links with no anchor text will have nofollow and/or target set, but no external icon
|
67 |
+
- Improved detection and handling of anchor links as internal.
|
68 |
+
- Initial support for AddThis placeholder links
|
69 |
+
|
70 |
+
|
71 |
= 6.1 =
|
72 |
|
73 |
- The nofollow attribute was not being set if a certain combination of the global, follow comments, set nofollow settings were set.
|
sem-external-links.php
CHANGED
@@ -3,9 +3,9 @@
|
|
3 |
Plugin Name: External Links
|
4 |
Plugin URI: http://www.semiologic.com/software/external-links/
|
5 |
Description: Marks outbound links as such, with various effects that are configurable under <a href="options-general.php?page=external-links">Settings / External Links</a>.
|
6 |
-
Version: 6.
|
7 |
Author: Denis de Bernardy & Mike Koepke
|
8 |
-
Author URI:
|
9 |
Text Domain: external-links
|
10 |
Domain Path: /lang
|
11 |
License: Dual licensed under the MIT and GPLv2 licenses
|
@@ -19,7 +19,7 @@ This software is copyright Denis de Bernardy & Mike Koepke, and is distributed u
|
|
19 |
|
20 |
**/
|
21 |
|
22 |
-
define('sem_external_links_version', '6.
|
23 |
|
24 |
/**
|
25 |
* external_links
|
@@ -131,11 +131,11 @@ class sem_external_links {
|
|
131 |
$this->anchor_utils = new external_links_anchor_utils( $this );
|
132 |
}
|
133 |
else {
|
134 |
-
add_filter('the_content', array($this, '
|
135 |
-
add_filter('the_excerpt', array($this, '
|
136 |
-
add_filter('comment_text', array($this, '
|
137 |
if ( $inc_text_widgets )
|
138 |
-
add_filter('widget_text', array($this, '
|
139 |
}
|
140 |
}
|
141 |
else {
|
@@ -169,19 +169,23 @@ class sem_external_links {
|
|
169 |
* process_content()
|
170 |
*
|
171 |
* @param string $text
|
|
|
172 |
* @return string $text
|
173 |
**/
|
174 |
|
175 |
-
function process_content($text) {
|
176 |
|
177 |
// short circuit if there's no anchors at all in the text
|
178 |
if ( false === stripos($text, '<a ') )
|
179 |
return($text);
|
180 |
|
181 |
-
global
|
182 |
-
$
|
|
|
|
|
183 |
|
184 |
-
|
|
|
185 |
|
186 |
// find all occurrences of anchors and fill matches with links
|
187 |
preg_match_all("/
|
@@ -207,36 +211,87 @@ class sem_external_links {
|
|
207 |
if ( !empty($raw_links) && !empty($processed_links) )
|
208 |
$text = str_replace($raw_links, $processed_links, $text);
|
209 |
|
210 |
-
|
|
|
|
|
211 |
|
212 |
return $text;
|
213 |
} # process_content()
|
214 |
|
215 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
216 |
/**
|
217 |
* escape()
|
218 |
*
|
219 |
* @param string $text
|
|
|
220 |
* @return string $text
|
221 |
**/
|
222 |
|
223 |
-
function escape($text) {
|
224 |
global $escape_anchor_filter;
|
225 |
|
226 |
if ( !isset($escape_anchor_filter) )
|
227 |
$escape_anchor_filter = array();
|
228 |
|
229 |
-
|
230 |
-
|
231 |
-
|
232 |
-
|
233 |
-
|
234 |
-
|
235 |
-
|
236 |
-
|
237 |
-
|
238 |
-
|
239 |
-
|
|
|
|
|
|
|
|
|
240 |
$text = preg_replace_callback($regex, array($this, 'escape_callback'), $text);
|
241 |
}
|
242 |
|
@@ -288,21 +343,17 @@ class sem_external_links {
|
|
288 |
**/
|
289 |
|
290 |
function process_link($match) {
|
291 |
-
# skip empty anchors
|
292 |
-
if ( !trim($match[2]) )
|
293 |
-
return $match[0];
|
294 |
-
|
295 |
# parse anchor
|
296 |
$anchor = $this->parse_anchor($match);
|
297 |
|
298 |
if ( !$anchor )
|
299 |
-
return
|
300 |
|
301 |
# filter anchor
|
302 |
$anchor = $this->filter_anchor( $anchor );
|
303 |
|
304 |
if ( $anchor )
|
305 |
-
$anchor = $this->build_anchor($anchor);
|
306 |
|
307 |
return $anchor;
|
308 |
} # process_link()
|
@@ -317,7 +368,8 @@ class sem_external_links {
|
|
317 |
|
318 |
function parse_anchor($match) {
|
319 |
$anchor = array();
|
320 |
-
$anchor['attr'] = $this->parse_attrs( $match[1] );
|
|
|
321 |
|
322 |
if ( !is_array($anchor['attr']) || empty($anchor['attr']['href']) # parser error or no link
|
323 |
|| trim($anchor['attr']['href']) != esc_url($anchor['attr']['href'], null, 'db') ) # likely a script
|
@@ -343,128 +395,35 @@ class sem_external_links {
|
|
343 |
/**
|
344 |
* build_anchor()
|
345 |
*
|
|
|
346 |
* @param array $anchor
|
347 |
* @return string $anchor
|
348 |
*/
|
349 |
|
350 |
-
function build_anchor($anchor) {
|
351 |
-
$anchor['attr']['href'] = esc_url($anchor['attr']['href']);
|
352 |
|
353 |
-
$
|
354 |
-
|
355 |
-
|
356 |
-
|
357 |
-
|
358 |
-
|
359 |
-
|
360 |
-
|
361 |
-
|
362 |
-
|
363 |
-
|
|
|
|
|
|
|
|
|
|
|
364 |
}
|
365 |
}
|
366 |
-
$str .= '>' . $anchor['body'] . '</a>';
|
367 |
|
368 |
-
return $
|
369 |
} # build_anchor()
|
370 |
|
371 |
-
/**
|
372 |
-
* Parse an attributes string into an array. If the string starts with a tag,
|
373 |
-
* then the attributes on the first tag are parsed. This parses via a manual
|
374 |
-
* loop and is designed to be safer than using DOMDocument.
|
375 |
-
*
|
376 |
-
* @param string|* $attrs
|
377 |
-
* @return array
|
378 |
-
*
|
379 |
-
* @example parse_attrs( 'src="example.jpg" alt="example"' )
|
380 |
-
* @example parse_attrs( '<img src="example.jpg" alt="example">' )
|
381 |
-
* @example parse_attrs( '<a href="example"></a>' )
|
382 |
-
* @example parse_attrs( '<a href="example">' )
|
383 |
-
*/
|
384 |
-
function parse_attrs($attrs) {
|
385 |
-
|
386 |
-
if ( !is_scalar($attrs) )
|
387 |
-
return (array) $attrs;
|
388 |
-
|
389 |
-
$attrs = str_split( trim($attrs) );
|
390 |
-
|
391 |
-
if ( '<' === $attrs[0] ) # looks like a tag so strip the tagname
|
392 |
-
while ( $attrs && ! ctype_space($attrs[0]) && $attrs[0] !== '>' )
|
393 |
-
array_shift($attrs);
|
394 |
-
|
395 |
-
$arr = array(); # output
|
396 |
-
$name = ''; # for the current attr being parsed
|
397 |
-
$value = ''; # for the current attr being parsed
|
398 |
-
$mode = 0; # whether current char is part of the name (-), the value (+), or neither (0)
|
399 |
-
$stop = false; # delimiter for the current $value being parsed
|
400 |
-
$space = ' '; # a single space
|
401 |
-
$paren = 0; # in parenthesis for js attrs
|
402 |
-
|
403 |
-
foreach ( $attrs as $j => $curr ) {
|
404 |
-
|
405 |
-
if ( $mode < 0 ) {# name
|
406 |
-
if ( '=' === $curr ) {
|
407 |
-
$mode = 1;
|
408 |
-
$stop = false;
|
409 |
-
} elseif ( '>' === $curr ) {
|
410 |
-
'' === $name or $arr[ $name ] = $value;
|
411 |
-
break;
|
412 |
-
} elseif ( !ctype_space($curr) ) {
|
413 |
-
if ( ctype_space( $attrs[ $j - 1 ] ) ) { # previous char
|
414 |
-
'' === $name or $arr[ $name ] = ''; # previous name
|
415 |
-
$name = $curr; # initiate new
|
416 |
-
} else {
|
417 |
-
$name .= $curr;
|
418 |
-
}
|
419 |
-
}
|
420 |
-
} elseif ( $mode > 0 ) {# value
|
421 |
-
if ( $paren ) {
|
422 |
-
$value .= $curr;
|
423 |
-
if ( $curr === "(")
|
424 |
-
$paren += 1;
|
425 |
-
elseif ( $curr === ")")
|
426 |
-
$paren -= 1;
|
427 |
-
}
|
428 |
-
else {
|
429 |
-
if ( $stop === false ) {
|
430 |
-
if ( !ctype_space($curr) ) {
|
431 |
-
if ( '"' === $curr || "'" === $curr ) {
|
432 |
-
$value = '';
|
433 |
-
$stop = $curr;
|
434 |
-
} else {
|
435 |
-
$value = $curr;
|
436 |
-
$stop = $space;
|
437 |
-
}
|
438 |
-
}
|
439 |
-
} elseif ( $stop === $space ? ctype_space($curr) : $curr === $stop ) {
|
440 |
-
$arr[ $name ] = $value;
|
441 |
-
$mode = 0;
|
442 |
-
$name = $value = '';
|
443 |
-
} else {
|
444 |
-
$value .= $curr;
|
445 |
-
if ( $curr === "(")
|
446 |
-
$paren += 1;
|
447 |
-
elseif ( $curr === ")")
|
448 |
-
$paren -= 1;
|
449 |
-
}
|
450 |
-
}
|
451 |
-
} else {# neither
|
452 |
-
|
453 |
-
if ( '>' === $curr )
|
454 |
-
break;
|
455 |
-
if ( !ctype_space( $curr ) ) {
|
456 |
-
# initiate
|
457 |
-
$name = $curr;
|
458 |
-
$mode = -1;
|
459 |
-
}
|
460 |
-
}
|
461 |
-
}
|
462 |
-
|
463 |
-
# incl the final pair if it was quoteless
|
464 |
-
'' === $name or $arr[ $name ] = $value;
|
465 |
-
|
466 |
-
return $arr;
|
467 |
-
}
|
468 |
|
469 |
/**
|
470 |
* Updates attribute of an HTML tag.
|
@@ -479,8 +438,8 @@ class sem_external_links {
|
|
479 |
$attr_value = false;
|
480 |
$quote = false; // quotes to wrap attribute values
|
481 |
|
482 |
-
if (preg_match('
|
483 |
-
|| preg_match('
|
484 |
) {
|
485 |
// two possible ways to get existing attributes
|
486 |
$attr_value = $matches[1];
|
@@ -491,15 +450,19 @@ class sem_external_links {
|
|
491 |
if ($attr_value)
|
492 |
{
|
493 |
//replace current attribute
|
494 |
-
|
495 |
$attr_name . '="' . esc_attr($new_attr_value) . '"', $html);
|
496 |
}
|
497 |
else {
|
498 |
// attribute does not currently exist, add it
|
499 |
-
|
|
|
|
|
|
|
500 |
}
|
501 |
-
} # update_attribute()
|
502 |
|
|
|
|
|
503 |
|
504 |
/**
|
505 |
* filter_anchor()
|
@@ -513,12 +476,37 @@ class sem_external_links {
|
|
513 |
if ( is_feed() )
|
514 |
return null;
|
515 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
516 |
# ignore local urls
|
517 |
-
|
518 |
return null;
|
519 |
|
520 |
# no icons for images
|
521 |
-
$is_image = (
|
522 |
|
523 |
$updated = false;
|
524 |
if ( !in_array('external', $anchor['attr']['class']) ) {
|
@@ -529,8 +517,11 @@ class sem_external_links {
|
|
529 |
if ( !$is_image && $this->opts['icon'] && !in_array('external_icon', $anchor['attr']['class'])
|
530 |
&& !in_array('no_icon', $anchor['attr']['class'])
|
531 |
&& !in_array('noicon', $anchor['attr']['class']) ) {
|
532 |
-
|
533 |
-
$
|
|
|
|
|
|
|
534 |
}
|
535 |
|
536 |
if ( $this->opts['nofollow'] && !in_array('nofollow', $anchor['attr']['rel'])
|
@@ -551,6 +542,28 @@ class sem_external_links {
|
|
551 |
} # filter_anchor()
|
552 |
|
553 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
554 |
/**
|
555 |
* is_local_url()
|
556 |
*
|
@@ -559,17 +572,15 @@ class sem_external_links {
|
|
559 |
**/
|
560 |
|
561 |
function is_local_url($url) {
|
562 |
-
if (
|
563 |
-
return true;
|
564 |
-
elseif ( (substr($url, 0, 2) != '//') && (strpos($url, 'http://') === false) && (strpos($url, 'https://') === false) )
|
565 |
return true;
|
566 |
elseif ( $url == 'http://' || $url == 'https://' )
|
567 |
return true;
|
568 |
elseif ( preg_match("~/go(/|\.)~i", $url) )
|
569 |
return false;
|
570 |
-
|
571 |
static $site_domain;
|
572 |
-
|
573 |
if ( !isset($site_domain) ) {
|
574 |
$site_domain = home_url();
|
575 |
$site_domain = parse_url($site_domain);
|
@@ -583,7 +594,7 @@ class sem_external_links {
|
|
583 |
return false;
|
584 |
}
|
585 |
$site_domain = preg_replace("/^www\./i", '', $site_domain);
|
586 |
-
|
587 |
# The following is not bullet proof, but it's good enough for a WP site
|
588 |
if ( $site_domain != 'localhost' && !preg_match("/\d+(\.\d+){3}/", $site_domain) ) {
|
589 |
if ( preg_match("/\.([^.]+)$/", $site_domain, $tld) ) {
|
@@ -592,9 +603,9 @@ class sem_external_links {
|
|
592 |
$site_domain = false;
|
593 |
return false;
|
594 |
}
|
595 |
-
|
596 |
$site_domain = substr($site_domain, 0, strlen($site_domain) - 1 - strlen($tld));
|
597 |
-
|
598 |
if ( preg_match("/\.([^.]+)$/", $site_domain, $subtld) ) {
|
599 |
$subtld = end($subtld);
|
600 |
if ( strlen($subtld) <= 4 ) {
|
@@ -606,16 +617,16 @@ class sem_external_links {
|
|
606 |
$site_domain = $subtld;
|
607 |
}
|
608 |
}
|
609 |
-
|
610 |
$site_domain .= ".$tld";
|
611 |
}
|
612 |
-
|
613 |
$site_domain = strtolower($site_domain);
|
614 |
}
|
615 |
-
|
616 |
if ( !$site_domain )
|
617 |
return false;
|
618 |
-
|
619 |
$link_domain = @parse_url($url);
|
620 |
if ($link_domain === false)
|
621 |
return true;
|
@@ -633,7 +644,7 @@ class sem_external_links {
|
|
633 |
if ( $subdomains != '')
|
634 |
$link_domain = str_replace($subdomains . '.', '', $link_domain);
|
635 |
}
|
636 |
-
|
637 |
if ( $site_domain == $link_domain ) {
|
638 |
return true;
|
639 |
} elseif ( function_exists('is_multisite') && is_multisite() ) {
|
3 |
Plugin Name: External Links
|
4 |
Plugin URI: http://www.semiologic.com/software/external-links/
|
5 |
Description: Marks outbound links as such, with various effects that are configurable under <a href="options-general.php?page=external-links">Settings / External Links</a>.
|
6 |
+
Version: 6.2
|
7 |
Author: Denis de Bernardy & Mike Koepke
|
8 |
+
Author URI: https://www.semiologic.com
|
9 |
Text Domain: external-links
|
10 |
Domain Path: /lang
|
11 |
License: Dual licensed under the MIT and GPLv2 licenses
|
19 |
|
20 |
**/
|
21 |
|
22 |
+
define('sem_external_links_version', '6.2');
|
23 |
|
24 |
/**
|
25 |
* external_links
|
131 |
$this->anchor_utils = new external_links_anchor_utils( $this );
|
132 |
}
|
133 |
else {
|
134 |
+
add_filter('the_content', array($this, 'process_content_content'), 100000);
|
135 |
+
// add_filter('the_excerpt', array($this, 'process_content_excerpt'), 100000);
|
136 |
+
add_filter('comment_text', array($this, 'process_content_comment'), 100000);
|
137 |
if ( $inc_text_widgets )
|
138 |
+
add_filter('widget_text', array($this, 'process_content_widget'), 100000);
|
139 |
}
|
140 |
}
|
141 |
else {
|
169 |
* process_content()
|
170 |
*
|
171 |
* @param string $text
|
172 |
+
* @param string $context
|
173 |
* @return string $text
|
174 |
**/
|
175 |
|
176 |
+
function process_content($text, $context = "global") {
|
177 |
|
178 |
// short circuit if there's no anchors at all in the text
|
179 |
if ( false === stripos($text, '<a ') )
|
180 |
return($text);
|
181 |
|
182 |
+
$escape_needed = array( 'global', 'content', 'widgets' );
|
183 |
+
if ( in_array($context, $escape_needed ) ) {
|
184 |
+
global $escape_anchor_filter;
|
185 |
+
$escape_anchor_filter = array();
|
186 |
|
187 |
+
$text = $this->escape($text, $context);
|
188 |
+
}
|
189 |
|
190 |
// find all occurrences of anchors and fill matches with links
|
191 |
preg_match_all("/
|
211 |
if ( !empty($raw_links) && !empty($processed_links) )
|
212 |
$text = str_replace($raw_links, $processed_links, $text);
|
213 |
|
214 |
+
if ( in_array($context, $escape_needed ) ) {
|
215 |
+
$text = $this->unescape($text);
|
216 |
+
}
|
217 |
|
218 |
return $text;
|
219 |
} # process_content()
|
220 |
|
221 |
|
222 |
+
/**
|
223 |
+
* process_content_comment()
|
224 |
+
*
|
225 |
+
* @param string $text
|
226 |
+
* @return string $text
|
227 |
+
**/
|
228 |
+
|
229 |
+
function process_content_comment($text) {
|
230 |
+
return $this->process_content( $text, 'comment' );
|
231 |
+
}
|
232 |
+
|
233 |
+
/**
|
234 |
+
* process_content_content()
|
235 |
+
*
|
236 |
+
* @param string $text
|
237 |
+
* @return string $text
|
238 |
+
**/
|
239 |
+
|
240 |
+
function process_content_content($text) {
|
241 |
+
return $this->process_content( $text, 'content' );
|
242 |
+
}
|
243 |
+
|
244 |
+
/**
|
245 |
+
* process_content_excerpt()
|
246 |
+
*
|
247 |
+
* @param string $text
|
248 |
+
* @return string $text
|
249 |
+
**/
|
250 |
+
|
251 |
+
function process_content_excerpt($text) {
|
252 |
+
return $this->process_content( $text, 'excerpt' );
|
253 |
+
}
|
254 |
+
|
255 |
+
/**
|
256 |
+
* process_content_widget()
|
257 |
+
*
|
258 |
+
* @param string $text
|
259 |
+
* @return string $text
|
260 |
+
**/
|
261 |
+
|
262 |
+
function process_content_widget($text) {
|
263 |
+
return $this->process_content( $text, 'widget' );
|
264 |
+
}
|
265 |
+
|
266 |
/**
|
267 |
* escape()
|
268 |
*
|
269 |
* @param string $text
|
270 |
+
* @param string $context
|
271 |
* @return string $text
|
272 |
**/
|
273 |
|
274 |
+
function escape($text, $context) {
|
275 |
global $escape_anchor_filter;
|
276 |
|
277 |
if ( !isset($escape_anchor_filter) )
|
278 |
$escape_anchor_filter = array();
|
279 |
|
280 |
+
$exclusions = array();
|
281 |
+
|
282 |
+
if ( $context == 'global' )
|
283 |
+
$exclusions['head'] = "/
|
284 |
+
.*?
|
285 |
+
<\s*\/\s*head\s*>
|
286 |
+
/isx";
|
287 |
+
|
288 |
+
$exclusions['blocks'] = "/
|
289 |
+
<\s*(script|style|object|textarea)(?:\s.*?)?>
|
290 |
+
.*?
|
291 |
+
<\s*\/\s*\\1\s*>
|
292 |
+
/isx";
|
293 |
+
|
294 |
+
foreach ( $exclusions as $regex ) {
|
295 |
$text = preg_replace_callback($regex, array($this, 'escape_callback'), $text);
|
296 |
}
|
297 |
|
343 |
**/
|
344 |
|
345 |
function process_link($match) {
|
|
|
|
|
|
|
|
|
346 |
# parse anchor
|
347 |
$anchor = $this->parse_anchor($match);
|
348 |
|
349 |
if ( !$anchor )
|
350 |
+
return false;
|
351 |
|
352 |
# filter anchor
|
353 |
$anchor = $this->filter_anchor( $anchor );
|
354 |
|
355 |
if ( $anchor )
|
356 |
+
$anchor = $this->build_anchor($match[0], $anchor);
|
357 |
|
358 |
return $anchor;
|
359 |
} # process_link()
|
368 |
|
369 |
function parse_anchor($match) {
|
370 |
$anchor = array();
|
371 |
+
// $anchor['attr'] = $this->parse_attrs( $match[1] );
|
372 |
+
$anchor['attr'] = $this->parseAttributes( $match[1] );
|
373 |
|
374 |
if ( !is_array($anchor['attr']) || empty($anchor['attr']['href']) # parser error or no link
|
375 |
|| trim($anchor['attr']['href']) != esc_url($anchor['attr']['href'], null, 'db') ) # likely a script
|
395 |
/**
|
396 |
* build_anchor()
|
397 |
*
|
398 |
+
* @param $link
|
399 |
* @param array $anchor
|
400 |
* @return string $anchor
|
401 |
*/
|
402 |
|
403 |
+
function build_anchor($link, $anchor) {
|
|
|
404 |
|
405 |
+
$attrs = array( 'class', 'rel', 'target');
|
406 |
+
|
407 |
+
foreach ( $attrs as $attr ) {
|
408 |
+
if ( isset($anchor['attr'][$attr]) ) {
|
409 |
+
$new_attr_value = null;
|
410 |
+
$values = $anchor['attr'][$attr];
|
411 |
+
if ( is_array($values) ) {
|
412 |
+
$values = array_unique($values);
|
413 |
+
if ( $values )
|
414 |
+
$new_attr_value = implode(' ', $values );
|
415 |
+
} else {
|
416 |
+
$new_attr_value = $values;
|
417 |
+
}
|
418 |
+
|
419 |
+
if ( $new_attr_value )
|
420 |
+
$link = $this->update_attribute($link, $attr, $new_attr_value);
|
421 |
}
|
422 |
}
|
|
|
423 |
|
424 |
+
return $link;
|
425 |
} # build_anchor()
|
426 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
427 |
|
428 |
/**
|
429 |
* Updates attribute of an HTML tag.
|
438 |
$attr_value = false;
|
439 |
$quote = false; // quotes to wrap attribute values
|
440 |
|
441 |
+
if (preg_match('/<\s*a\s+' . $attr_name . '="([^"]*)\s*>"/iu', $html, $matches)
|
442 |
+
|| preg_match('/<\s*a\s+' . $attr_name . "='([^']*)\s*>'/iu", $html, $matches)
|
443 |
) {
|
444 |
// two possible ways to get existing attributes
|
445 |
$attr_value = $matches[1];
|
450 |
if ($attr_value)
|
451 |
{
|
452 |
//replace current attribute
|
453 |
+
$html = str_ireplace("$attr_name=" . $quote . "$attr_value" . $quote,
|
454 |
$attr_name . '="' . esc_attr($new_attr_value) . '"', $html);
|
455 |
}
|
456 |
else {
|
457 |
// attribute does not currently exist, add it
|
458 |
+
$pos = strpos( $html, '>' );
|
459 |
+
if ($pos !== false) {
|
460 |
+
$html = substr_replace( $html, " $attr_name=\"" . esc_attr($new_attr_value) . '">', $pos, strlen('>') );
|
461 |
+
}
|
462 |
}
|
|
|
463 |
|
464 |
+
return $html;
|
465 |
+
} # update_attribute()
|
466 |
|
467 |
/**
|
468 |
* filter_anchor()
|
476 |
if ( is_feed() )
|
477 |
return null;
|
478 |
|
479 |
+
// if we don't have a href or find a ? only obviously this some illformed or temp link
|
480 |
+
if ( empty( $anchor['attr']['href'] ) || (substr($anchor['attr']['href'], 0, 1) == '?' ) )
|
481 |
+
return null;
|
482 |
+
|
483 |
+
// we have a placeholder link. Normally treat as a local but there are exceptions like add this
|
484 |
+
if ( substr($anchor['attr']['href'], 0, 1) == '#' ) {
|
485 |
+
// now is this a full blown anchor?
|
486 |
+
if ( strlen( $anchor['attr']['href'] ) > 1 ) {
|
487 |
+
// yep, bail
|
488 |
+
return null;
|
489 |
+
}
|
490 |
+
|
491 |
+
// can we find addthis_button class? if so then don't bail and process external
|
492 |
+
$addthis = false;
|
493 |
+
foreach( $anchor['attr']['class'] as $c => $class) {
|
494 |
+
$pos = strpos( $class, "addthis_button" );
|
495 |
+
if ( null !== $pos ) {
|
496 |
+
$addthis = true;
|
497 |
+
break;
|
498 |
+
}
|
499 |
+
}
|
500 |
+
// no addthis. bail as we got just a an anchor only
|
501 |
+
if ( !$addthis )
|
502 |
+
return null;
|
503 |
+
}
|
504 |
# ignore local urls
|
505 |
+
elseif ( sem_external_links::is_local_url($anchor['attr']['href']) )
|
506 |
return null;
|
507 |
|
508 |
# no icons for images
|
509 |
+
$is_image = ( false !== strpos($anchor['body'], "<img ") );
|
510 |
|
511 |
$updated = false;
|
512 |
if ( !in_array('external', $anchor['attr']['class']) ) {
|
517 |
if ( !$is_image && $this->opts['icon'] && !in_array('external_icon', $anchor['attr']['class'])
|
518 |
&& !in_array('no_icon', $anchor['attr']['class'])
|
519 |
&& !in_array('noicon', $anchor['attr']['class']) ) {
|
520 |
+
// don't add an icon if there is no text in the link
|
521 |
+
if ($anchor['body'] != null) {
|
522 |
+
$anchor['attr']['class'][] = 'external_icon';
|
523 |
+
$updated = true;
|
524 |
+
}
|
525 |
}
|
526 |
|
527 |
if ( $this->opts['nofollow'] && !in_array('nofollow', $anchor['attr']['rel'])
|
542 |
} # filter_anchor()
|
543 |
|
544 |
|
545 |
+
function parseAttributes($text) {
|
546 |
+
$attributes = array();
|
547 |
+
$pattern = '#(?(DEFINE)
|
548 |
+
(?<name>[a-zA-Z][a-zA-Z0-9-:]*)
|
549 |
+
(?<value_double>"[^"]+")
|
550 |
+
(?<value_single>\'[^\']+\')
|
551 |
+
(?<value_none>[^\s>]+)
|
552 |
+
(?<value>((?&value_double)|(?&value_single)|(?&value_none)))
|
553 |
+
)
|
554 |
+
(?<n>(?&name))(=(?<v>(?&value)))?#xs';
|
555 |
+
|
556 |
+
if (preg_match_all($pattern, $text, $matches, PREG_SET_ORDER)) {
|
557 |
+
foreach ($matches as $match) {
|
558 |
+
$attributes[$match['n']] = isset($match['v'])
|
559 |
+
? trim($match['v'], '\'"')
|
560 |
+
: null;
|
561 |
+
}
|
562 |
+
}
|
563 |
+
|
564 |
+
return $attributes;
|
565 |
+
}
|
566 |
+
|
567 |
/**
|
568 |
* is_local_url()
|
569 |
*
|
572 |
**/
|
573 |
|
574 |
function is_local_url($url) {
|
575 |
+
if ( (substr($url, 0, 2) != '//') && (strpos($url, 'http://') === false) && (strpos($url, 'https://') === false) )
|
|
|
|
|
576 |
return true;
|
577 |
elseif ( $url == 'http://' || $url == 'https://' )
|
578 |
return true;
|
579 |
elseif ( preg_match("~/go(/|\.)~i", $url) )
|
580 |
return false;
|
581 |
+
|
582 |
static $site_domain;
|
583 |
+
|
584 |
if ( !isset($site_domain) ) {
|
585 |
$site_domain = home_url();
|
586 |
$site_domain = parse_url($site_domain);
|
594 |
return false;
|
595 |
}
|
596 |
$site_domain = preg_replace("/^www\./i", '', $site_domain);
|
597 |
+
|
598 |
# The following is not bullet proof, but it's good enough for a WP site
|
599 |
if ( $site_domain != 'localhost' && !preg_match("/\d+(\.\d+){3}/", $site_domain) ) {
|
600 |
if ( preg_match("/\.([^.]+)$/", $site_domain, $tld) ) {
|
603 |
$site_domain = false;
|
604 |
return false;
|
605 |
}
|
606 |
+
|
607 |
$site_domain = substr($site_domain, 0, strlen($site_domain) - 1 - strlen($tld));
|
608 |
+
|
609 |
if ( preg_match("/\.([^.]+)$/", $site_domain, $subtld) ) {
|
610 |
$subtld = end($subtld);
|
611 |
if ( strlen($subtld) <= 4 ) {
|
617 |
$site_domain = $subtld;
|
618 |
}
|
619 |
}
|
620 |
+
|
621 |
$site_domain .= ".$tld";
|
622 |
}
|
623 |
+
|
624 |
$site_domain = strtolower($site_domain);
|
625 |
}
|
626 |
+
|
627 |
if ( !$site_domain )
|
628 |
return false;
|
629 |
+
|
630 |
$link_domain = @parse_url($url);
|
631 |
if ($link_domain === false)
|
632 |
return true;
|
644 |
if ( $subdomains != '')
|
645 |
$link_domain = str_replace($subdomains . '.', '', $link_domain);
|
646 |
}
|
647 |
+
|
648 |
if ( $site_domain == $link_domain ) {
|
649 |
return true;
|
650 |
} elseif ( function_exists('is_multisite') && is_multisite() ) {
|