External Links - Version 5.4.1

Version Description

  • Troubleshooting release. Adds a few html comments in the page source to ensure hooks are being called.
Download this release

Release Info

Developer Mike_Koepke
Plugin Icon wp plugin External Links
Version 5.4.1
Comparing to
See all releases

Code changes from version 5.5 to 5.4.1

external-links-anchor-utils.php DELETED
@@ -1,412 +0,0 @@
1
- <?php
2
- /*
3
- * External Links Anchor Utils
4
- * Author: Denis de Bernardy & Mike Koepke <http://www.semiologic.com>
5
- * Version: 1.6.1
6
- *
7
- * Forked from Anchor-Utils
8
- */
9
-
10
- if ( @ini_get('pcre.backtrack_limit') <= 1000000 )
11
- @ini_set('pcre.backtrack_limit', 1000000);
12
- if ( @ini_get('pcre.recursion_limit') <= 250000 )
13
- @ini_set('pcre.recursion_limit', 250000);
14
-
15
- /**
16
- * external_links_anchor_utils
17
- *
18
- * @packageExternal Links Anchor Utils
19
- **/
20
-
21
- class external_links_anchor_utils {
22
-
23
- private $external_links = null;
24
-
25
- /**
26
- * constructor
27
- */
28
- public function __construct( external_links $external_links, $apply_globally = true, $inc_text_widgets = true ) {
29
-
30
- $this->external_links = $external_links;
31
-
32
- if ( $apply_globally ) {
33
- add_action('wp_head', array($this, 'ob_start'), 100000);
34
- }
35
- else {
36
- add_filter('the_content', array($this, 'filter'), 100000);
37
- add_filter('the_excerpt', array($this, 'filter'), 100000);
38
- add_filter('comment_text', array($this, 'filter'), 100000);
39
- if ( $inc_text_widgets )
40
- add_filter('widget_text', array($this, 'filter'), 100000);
41
- }
42
-
43
- } #external_links_anchor_utils
44
-
45
-
46
- /**
47
- * ob_start()
48
- *
49
- * @return void
50
- **/
51
-
52
- function ob_start() {
53
- echo '<!-- external-links ' . 'ob_start' . ' -->' . "\n";
54
-
55
- if ( has_filter('ob_filter_anchor') ) {
56
- ob_start(array($this, 'ob_filter'));
57
- add_action('wp_print_footer_scripts', array($this, 'ob_flush'), 100000);
58
- }
59
- } # ob_start()
60
-
61
- /**
62
- * ob_filter()
63
- *
64
- * @param string $text
65
- * @return string $text
66
- **/
67
-
68
- function ob_filter($text) {
69
- global $escape_anchor_filter;
70
- $escape_anchor_filter = array();
71
-
72
- $text = '<!-- external-links ' . 'ob_filter' . ' -->' . "\n" . $text;
73
-
74
- $text = $this->escape($text);
75
-
76
- $text = preg_replace_callback("/
77
- <\s*a\s+
78
- ([^<>]+)
79
- >
80
- (.*?)
81
- <\s*\/\s*a\s*>
82
- /isx", array($this, 'ob_filter_callback'), $text);
83
-
84
- $text = $this->unescape($text);
85
-
86
- return $text;
87
- } # ob_filter()
88
-
89
-
90
- /**
91
- * ob_flush()
92
- *
93
- * @return void
94
- **/
95
-
96
- static function ob_flush() {
97
- echo '<!-- external-links ' . 'ob_end_flush' . ' -->' . "\n";
98
-
99
- ob_end_flush();
100
- } # ob_flush()
101
-
102
-
103
- /**
104
- * ob_filter_callback()
105
- *
106
- * @param array $match
107
- * @return string $str
108
- **/
109
-
110
- function ob_filter_callback($match) {
111
- # skip empty anchors
112
- if ( !trim($match[2]) )
113
- return $match[0];
114
-
115
- # parse anchor
116
- $anchor = $this->parse_anchor($match);
117
-
118
- if ( !$anchor )
119
- return $match[0];
120
-
121
- # filter anchor
122
- // $anchor = apply_filters( 'ob_filter_anchor', $anchor );
123
- $anchor = $this->external_links->filter( $anchor );
124
-
125
- # return anchor
126
- return $this->build_anchor($anchor);
127
- } # ob_filter_callback()
128
-
129
-
130
- /**
131
- * filter()
132
- *
133
- * @param string $text
134
- * @return string $text
135
- **/
136
-
137
- function filter($text) {
138
- $text = '<!-- external-links ' . current_filter() . ' -->' . "\n" . $text;
139
- if ( !has_filter('filter_anchor') )
140
- return $text;
141
-
142
- global $escape_anchor_filter;
143
- $escape_anchor_filter = array();
144
-
145
- $text = $this->escape($text);
146
-
147
- $text = preg_replace_callback("/
148
- <\s*a\s+
149
- ([^<>]+)
150
- >
151
- (.*?)
152
- <\s*\/\s*a\s*>
153
- /isx", array($this, 'filter_callback'), $text);
154
-
155
- $text = $this->unescape($text);
156
-
157
- return $text;
158
- } # filter()
159
-
160
-
161
- /**
162
- * filter_callback()
163
- *
164
- * @param array $match
165
- * @return string $str
166
- **/
167
-
168
- function filter_callback($match) {
169
- # skip empty anchors
170
- if ( !trim($match[2]) )
171
- return $match[0];
172
-
173
- # parse anchor
174
- $anchor = $this->parse_anchor($match);
175
-
176
- if ( !$anchor )
177
- return $match[0];
178
-
179
- # filter anchor
180
- // $anchor = apply_filters( 'filter_anchor', $anchor );
181
- $anchor = $this->external_links->filter( $anchor );
182
-
183
- # return anchor
184
- return $this->build_anchor($anchor);
185
- } # filter_callback()
186
-
187
-
188
- /**
189
- * parse_anchor()
190
- *
191
- * @param array $match
192
- * @return array $anchor
193
- **/
194
-
195
- function parse_anchor($match) {
196
- $anchor = array();
197
- $anchor['attr'] = $this->parse_attrs($match[1]);
198
-
199
- if ( !is_array($anchor['attr']) || empty($anchor['attr']['href']) # parser error or no link
200
- || trim($anchor['attr']['href']) != esc_url($anchor['attr']['href'], null, 'db') ) # likely a script
201
- return false;
202
-
203
- foreach ( array('class', 'rel') as $attr ) {
204
- if ( !isset($anchor['attr'][$attr]) ) {
205
- $anchor['attr'][$attr] = array();
206
- } else {
207
- $anchor['attr'][$attr] = explode(' ', $anchor['attr'][$attr]);
208
- $anchor['attr'][$attr] = array_map('trim', $anchor['attr'][$attr]);
209
- }
210
- }
211
-
212
- $anchor['body'] = $match[2];
213
-
214
- $anchor['attr']['href'] = @html_entity_decode($anchor['attr']['href'], ENT_COMPAT, get_option('blog_charset'));
215
-
216
- return $anchor;
217
- } # parse_anchor()
218
-
219
-
220
- /**
221
- * build_anchor()
222
- *
223
- * @param array $anchor
224
- * @return string $anchor
225
- **/
226
-
227
- function build_anchor($anchor) {
228
- $anchor['attr']['href'] = esc_url($anchor['attr']['href']);
229
-
230
- $str = '<a';
231
- foreach ( $anchor['attr'] as $k => $v ) {
232
- if ( is_array($v) ) {
233
- $v = array_unique($v);
234
- if ( $v )
235
- $str .= ' ' . $k . '="' . implode(' ', $v) . '"';
236
- } else {
237
- if ($k)
238
- $str .= ' ' . $k . '="' . $v . '"';
239
- else
240
- $str .= ' ' . $v;
241
- }
242
- }
243
- $str .= '>' . $anchor['body'] . '</a>';
244
-
245
- return $str;
246
- } # build_anchor()
247
-
248
-
249
- /**
250
- * escape()
251
- *
252
- * @param string $text
253
- * @return string $text
254
- **/
255
-
256
- function escape($text) {
257
- global $escape_anchor_filter;
258
-
259
- if ( !isset($escape_anchor_filter) )
260
- $escape_anchor_filter = array();
261
-
262
- foreach ( array(
263
- 'head' => "/
264
- .*?
265
- <\s*\/\s*head\s*>
266
- /isx",
267
- 'blocks' => "/
268
- <\s*(script|style|object|textarea)(?:\s.*?)?>
269
- .*?
270
- <\s*\/\s*\\1\s*>
271
- /isx",
272
- ) as $regex ) {
273
- $text = preg_replace_callback($regex, array($this, 'escape_callback'), $text);
274
- }
275
-
276
- return $text;
277
- } # escape()
278
-
279
-
280
- /**
281
- * escape_callback()
282
- *
283
- * @param array $match
284
- * @return string $text
285
- **/
286
-
287
- function escape_callback($match) {
288
- global $escape_anchor_filter;
289
-
290
- $tag_id = "----escape_external_links_anchor_utils:" . md5($match[0]) . "----";
291
- $escape_anchor_filter[$tag_id] = $match[0];
292
-
293
- return $tag_id;
294
- } # escape_callback()
295
-
296
-
297
- /**
298
- * unescape()
299
- *
300
- * @param string $text
301
- * @return string $text
302
- **/
303
-
304
- function unescape($text) {
305
- global $escape_anchor_filter;
306
-
307
- if ( !$escape_anchor_filter )
308
- return $text;
309
-
310
- $unescape = array_reverse($escape_anchor_filter);
311
-
312
- return str_replace(array_keys($unescape), array_values($unescape), $text);
313
- } # unescape()
314
-
315
- /**
316
- * Parse an attributes string into an array. If the string starts with a tag,
317
- * then the attributes on the first tag are parsed. This parses via a manual
318
- * loop and is designed to be safer than using DOMDocument.
319
- *
320
- * @param string|* $attrs
321
- * @return array
322
- *
323
- * @example parse_attrs( 'src="example.jpg" alt="example"' )
324
- * @example parse_attrs( '<img src="example.jpg" alt="example">' )
325
- * @example parse_attrs( '<a href="example"></a>' )
326
- * @example parse_attrs( '<a href="example">' )
327
- */
328
- function parse_attrs($attrs) {
329
-
330
- if ( !is_scalar($attrs) )
331
- return (array) $attrs;
332
-
333
- $attrs = str_split( trim($attrs) );
334
-
335
- if ( '<' === $attrs[0] ) # looks like a tag so strip the tagname
336
- while ( $attrs && ! ctype_space($attrs[0]) && $attrs[0] !== '>' )
337
- array_shift($attrs);
338
-
339
- $arr = array(); # output
340
- $name = ''; # for the current attr being parsed
341
- $value = ''; # for the current attr being parsed
342
- $mode = 0; # whether current char is part of the name (-), the value (+), or neither (0)
343
- $stop = false; # delimiter for the current $value being parsed
344
- $space = ' '; # a single space
345
- $paren = 0; # in parenthesis for js attrs
346
-
347
- foreach ( $attrs as $j => $curr ) {
348
-
349
- if ( $mode < 0 ) {# name
350
- if ( '=' === $curr ) {
351
- $mode = 1;
352
- $stop = false;
353
- } elseif ( '>' === $curr ) {
354
- '' === $name or $arr[ $name ] = $value;
355
- break;
356
- } elseif ( !ctype_space($curr) ) {
357
- if ( ctype_space( $attrs[ $j - 1 ] ) ) { # previous char
358
- '' === $name or $arr[ $name ] = ''; # previous name
359
- $name = $curr; # initiate new
360
- } else {
361
- $name .= $curr;
362
- }
363
- }
364
- } elseif ( $mode > 0 ) {# value
365
- if ( $paren ) {
366
- $value .= $curr;
367
- if ( $curr === "(")
368
- $paren += 1;
369
- elseif ( $curr === ")")
370
- $paren -= 1;
371
- }
372
- else {
373
- if ( $stop === false ) {
374
- if ( !ctype_space($curr) ) {
375
- if ( '"' === $curr || "'" === $curr ) {
376
- $value = '';
377
- $stop = $curr;
378
- } else {
379
- $value = $curr;
380
- $stop = $space;
381
- }
382
- }
383
- } elseif ( $stop === $space ? ctype_space($curr) : $curr === $stop ) {
384
- $arr[ $name ] = $value;
385
- $mode = 0;
386
- $name = $value = '';
387
- } else {
388
- $value .= $curr;
389
- if ( $curr === "(")
390
- $paren += 1;
391
- elseif ( $curr === ")")
392
- $paren -= 1;
393
- }
394
- }
395
- } else {# neither
396
-
397
- if ( '>' === $curr )
398
- break;
399
- if ( !ctype_space( $curr ) ) {
400
- # initiate
401
- $name = $curr;
402
- $mode = -1;
403
- }
404
- }
405
- }
406
-
407
- # incl the final pair if it was quoteless
408
- '' === $name or $arr[ $name ] = $value;
409
-
410
- return $arr;
411
- }
412
- } # external_links_anchor_utils
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
readme.txt CHANGED
@@ -36,12 +36,6 @@ If you require more dedicated assistance, consider using [Semiologic Pro](http:/
36
 
37
  == Change Log ==
38
 
39
- = 5.5 =
40
-
41
- - Use wp_print_footer_scripts hook instead of wp_footer as some themes fail to call wp_footer();
42
- - Use own custom version of the anchor_utils class
43
- - Content, excerpt and comment filters no longer called when Apply Globally is selected. Improves performance.
44
-
45
  = 5.4.1 =
46
 
47
  - Troubleshooting release. Adds a few html comments in the page source to ensure hooks are being called.
36
 
37
  == Change Log ==
38
 
 
 
 
 
 
 
39
  = 5.4.1 =
40
 
41
  - Troubleshooting release. Adds a few html comments in the page source to ensure hooks are being called.
sem-external-links.php CHANGED
@@ -3,7 +3,7 @@
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: 5.5
7
  Author: Denis de Bernardy & Mike Koepke
8
  Author URI: http://www.getsemiologic.com
9
  Text Domain: external-links
@@ -108,22 +108,22 @@ class external_links {
108
  function init() {
109
  // more stuff: register actions and filters
110
  if ( !is_admin() ) {
111
- if ( !class_exists('external_links_anchor_utils') )
112
- include $this->plugin_path . '/external-links-anchor-utils.php';
113
 
114
  $o = external_links::get_options();
115
 
116
- // add_filter(($o['global'] ? 'ob_' : '' ) . 'filter_anchor', array($this, 'filter'));
117
-
118
  $inc_text_widgets = false;
119
  if ( isset( $o['text_widgets'] ) && $o['text_widgets'] )
120
  $inc_text_widgets = true;
121
 
122
- $this->anchor_utils = new external_links_anchor_utils( $this, $o['global'], $inc_text_widgets );
123
 
124
  if ( $o['icon'] )
125
  add_action('wp_enqueue_scripts', array($this, 'styles'), 5);
126
 
 
 
127
  unset($o);
128
  }
129
  else {
@@ -172,8 +172,14 @@ class external_links {
172
  # no icons for images
173
  $is_image = (bool) preg_match("/^\s*<\s*img\s.+?>\s*$/is", $anchor['body']);
174
 
 
 
175
  $o = external_links::get_options();
176
 
 
 
 
 
177
  if ( !in_array('external', $anchor['attr']['class']) )
178
  $anchor['attr']['class'][] = 'external';
179
 
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: 5.4.1
7
  Author: Denis de Bernardy & Mike Koepke
8
  Author URI: http://www.getsemiologic.com
9
  Text Domain: external-links
108
  function init() {
109
  // more stuff: register actions and filters
110
  if ( !is_admin() ) {
111
+ if ( !class_exists('anchor_utils') )
112
+ include $this->plugin_path . '/anchor-utils/anchor-utils.php';
113
 
114
  $o = external_links::get_options();
115
 
 
 
116
  $inc_text_widgets = false;
117
  if ( isset( $o['text_widgets'] ) && $o['text_widgets'] )
118
  $inc_text_widgets = true;
119
 
120
+ $this->anchor_utils = new anchor_utils( $inc_text_widgets );
121
 
122
  if ( $o['icon'] )
123
  add_action('wp_enqueue_scripts', array($this, 'styles'), 5);
124
 
125
+ add_filter(($o['global'] ? 'ob_' : '' ) . 'filter_anchor', array($this, 'filter'));
126
+
127
  unset($o);
128
  }
129
  else {
172
  # no icons for images
173
  $is_image = (bool) preg_match("/^\s*<\s*img\s.+?>\s*$/is", $anchor['body']);
174
 
175
+ $current_filter = $anchor['current_filter'];
176
+
177
  $o = external_links::get_options();
178
 
179
+ # is this is a widget callback and the option is off return;
180
+ if ( 'widget_text' == $current_filter && (isset( $o['text_widgets'] ) && !$o['text_widgets']) )
181
+ return $anchor;
182
+
183
  if ( !in_array('external', $anchor['attr']['class']) )
184
  $anchor['attr']['class'][] = 'external';
185