Version Description
Download this release
Release Info
Developer | Mike_Koepke |
Plugin | External Links |
Version | 6.0 |
Comparing to | |
See all releases |
Code changes from version 4.2 to 6.0
- anchor-utils/anchor-utils.php +2 -305
- external-links-anchor-utils.php +400 -0
- readme.txt +68 -4
- sem-external-links-admin.php +23 -11
- sem-external-links.php +154 -69
anchor-utils/anchor-utils.php
CHANGED
@@ -2,311 +2,8 @@
|
|
2 |
/*
|
3 |
* Anchor Utils
|
4 |
* Author: Denis de Bernardy & Mike Koepke <http://www.semiologic.com>
|
5 |
-
* Version: 1.2
|
6 |
*/
|
7 |
|
8 |
-
|
9 |
-
@ini_set('pcre.backtrack_limit', 750000);
|
10 |
-
if ( @ini_get('pcre.recursion_limit') <= 250000 )
|
11 |
-
@ini_set('pcre.recursion_limit', 250000);
|
12 |
|
13 |
-
/**
|
14 |
-
* anchor_utils
|
15 |
-
*
|
16 |
-
* @package Anchor Utils
|
17 |
-
**/
|
18 |
-
|
19 |
-
class anchor_utils {
|
20 |
-
/**
|
21 |
-
* anchor_utils
|
22 |
-
*/
|
23 |
-
function anchor_utils() {
|
24 |
-
add_filter('the_content', array($this, 'filter'), 100);
|
25 |
-
add_filter('the_excerpt', array($this, 'filter'), 100);
|
26 |
-
add_filter('widget_text', array($this, 'filter'), 100);
|
27 |
-
add_filter('comment_text', array($this, 'filter'), 100);
|
28 |
-
|
29 |
-
add_action('wp_head', array($this, 'ob_start'), 10000);
|
30 |
-
} #anchor_utils
|
31 |
-
|
32 |
-
/**
|
33 |
-
* ob_start()
|
34 |
-
*
|
35 |
-
* @return void
|
36 |
-
**/
|
37 |
-
|
38 |
-
function ob_start() {
|
39 |
-
static $done = false;
|
40 |
-
|
41 |
-
if ( $done )
|
42 |
-
return;
|
43 |
-
|
44 |
-
if ( has_filter('ob_filter_anchor') ) {
|
45 |
-
ob_start(array($this, 'ob_filter'));
|
46 |
-
add_action('wp_footer', array($this, 'ob_flush'), 10000);
|
47 |
-
$done = true;
|
48 |
-
}
|
49 |
-
} # ob_start()
|
50 |
-
|
51 |
-
|
52 |
-
/**
|
53 |
-
* ob_filter()
|
54 |
-
*
|
55 |
-
* @param string $text
|
56 |
-
* @return string $text
|
57 |
-
**/
|
58 |
-
|
59 |
-
function ob_filter($text) {
|
60 |
-
global $escape_anchor_filter;
|
61 |
-
$escape_anchor_filter = array();
|
62 |
-
|
63 |
-
$text = anchor_utils::escape($text);
|
64 |
-
|
65 |
-
$text = preg_replace_callback("/
|
66 |
-
<\s*a\s+
|
67 |
-
([^<>]+)
|
68 |
-
>
|
69 |
-
(.+?)
|
70 |
-
<\s*\/\s*a\s*>
|
71 |
-
/isx", array($this, 'ob_filter_callback'), $text);
|
72 |
-
|
73 |
-
$text = anchor_utils::unescape($text);
|
74 |
-
|
75 |
-
return $text;
|
76 |
-
} # ob_filter()
|
77 |
-
|
78 |
-
|
79 |
-
/**
|
80 |
-
* ob_flush()
|
81 |
-
*
|
82 |
-
* @return void
|
83 |
-
**/
|
84 |
-
|
85 |
-
static function ob_flush() {
|
86 |
-
static $done = true;
|
87 |
-
|
88 |
-
if ( $done )
|
89 |
-
return;
|
90 |
-
|
91 |
-
ob_end_flush();
|
92 |
-
$done = true;
|
93 |
-
} # ob_flush()
|
94 |
-
|
95 |
-
|
96 |
-
/**
|
97 |
-
* ob_filter_callback()
|
98 |
-
*
|
99 |
-
* @param array $match
|
100 |
-
* @return string $str
|
101 |
-
**/
|
102 |
-
|
103 |
-
function ob_filter_callback($match) {
|
104 |
-
# skip empty anchors
|
105 |
-
if ( !trim($match[2]) )
|
106 |
-
return $match[0];
|
107 |
-
|
108 |
-
# parse anchor
|
109 |
-
$anchor = anchor_utils::parse_anchor($match);
|
110 |
-
|
111 |
-
if ( !$anchor )
|
112 |
-
return $match[0];
|
113 |
-
|
114 |
-
# filter anchor
|
115 |
-
$anchor = apply_filters('ob_filter_anchor', $anchor);
|
116 |
-
|
117 |
-
# return anchor
|
118 |
-
return anchor_utils::build_anchor($anchor);
|
119 |
-
} # ob_filter_callback()
|
120 |
-
|
121 |
-
|
122 |
-
/**
|
123 |
-
* filter()
|
124 |
-
*
|
125 |
-
* @param string $text
|
126 |
-
* @return string $text
|
127 |
-
**/
|
128 |
-
|
129 |
-
function filter($text) {
|
130 |
-
if ( !has_filter('filter_anchor') )
|
131 |
-
return $text;
|
132 |
-
|
133 |
-
global $escape_anchor_filter;
|
134 |
-
$escape_anchor_filter = array();
|
135 |
-
|
136 |
-
$text = anchor_utils::escape($text);
|
137 |
-
|
138 |
-
$text = preg_replace_callback("/
|
139 |
-
<\s*a\s+
|
140 |
-
([^<>]+)
|
141 |
-
>
|
142 |
-
(.+?)
|
143 |
-
<\s*\/\s*a\s*>
|
144 |
-
/isx", array($this, 'filter_callback'), $text);
|
145 |
-
|
146 |
-
$text = anchor_utils::unescape($text);
|
147 |
-
|
148 |
-
return $text;
|
149 |
-
} # filter()
|
150 |
-
|
151 |
-
|
152 |
-
/**
|
153 |
-
* filter_callback()
|
154 |
-
*
|
155 |
-
* @param array $match
|
156 |
-
* @return string $str
|
157 |
-
**/
|
158 |
-
|
159 |
-
function filter_callback($match) {
|
160 |
-
# skip empty anchors
|
161 |
-
if ( !trim($match[2]) )
|
162 |
-
return $match[0];
|
163 |
-
|
164 |
-
# parse anchor
|
165 |
-
$anchor = anchor_utils::parse_anchor($match);
|
166 |
-
|
167 |
-
if ( !$anchor )
|
168 |
-
return $match[0];
|
169 |
-
|
170 |
-
# filter anchor
|
171 |
-
$anchor = apply_filters('filter_anchor', $anchor);
|
172 |
-
|
173 |
-
# return anchor
|
174 |
-
return anchor_utils::build_anchor($anchor);
|
175 |
-
} # filter_callback()
|
176 |
-
|
177 |
-
|
178 |
-
/**
|
179 |
-
* parse_anchor()
|
180 |
-
*
|
181 |
-
* @param array $match
|
182 |
-
* @return array $anchor
|
183 |
-
**/
|
184 |
-
|
185 |
-
function parse_anchor($match) {
|
186 |
-
|
187 |
-
// Fix links that have javascript onClick or similar code but uses =" " rather than =' ". Messes up attribute extraction especially for threaded comments
|
188 |
-
$on_patterns = '/(onClick|onMouseOver|onMouseOut|onMouseDown|onMouseUp|onDblClick|onContextMenu|onLoad|onAbort|onError)="(.+?)"/iU';
|
189 |
-
$match[1] = preg_replace($on_patterns, "$1='$2'", $match[1]);
|
190 |
-
|
191 |
-
$anchor = array();
|
192 |
-
$anchor['attr'] = shortcode_parse_atts($match[1]);
|
193 |
-
|
194 |
-
if ( !is_array($anchor['attr']) || empty($anchor['attr']['href']) # parser error or no link
|
195 |
-
|| trim($anchor['attr']['href']) != esc_url($anchor['attr']['href'], null, 'db') ) # likely a script
|
196 |
-
return false;
|
197 |
-
|
198 |
-
foreach ( array('class', 'rel') as $attr ) {
|
199 |
-
if ( !isset($anchor['attr'][$attr]) ) {
|
200 |
-
$anchor['attr'][$attr] = array();
|
201 |
-
} else {
|
202 |
-
$anchor['attr'][$attr] = explode(' ', $anchor['attr'][$attr]);
|
203 |
-
$anchor['attr'][$attr] = array_map('trim', $anchor['attr'][$attr]);
|
204 |
-
}
|
205 |
-
}
|
206 |
-
|
207 |
-
$anchor['body'] = $match[2];
|
208 |
-
|
209 |
-
$anchor['attr']['href'] = @html_entity_decode($anchor['attr']['href'], ENT_COMPAT, get_option('blog_charset'));
|
210 |
-
|
211 |
-
return $anchor;
|
212 |
-
} # parse_anchor()
|
213 |
-
|
214 |
-
|
215 |
-
/**
|
216 |
-
* build_anchor()
|
217 |
-
*
|
218 |
-
* @param array $anchor
|
219 |
-
* @return string $anchor
|
220 |
-
**/
|
221 |
-
|
222 |
-
function build_anchor($anchor) {
|
223 |
-
$anchor['attr']['href'] = esc_url($anchor['attr']['href']);
|
224 |
-
|
225 |
-
$str = '<a';
|
226 |
-
foreach ( $anchor['attr'] as $k => $v ) {
|
227 |
-
if ( is_array($v) ) {
|
228 |
-
$v = array_unique($v);
|
229 |
-
if ( $v )
|
230 |
-
$str .= ' ' . $k . '="' . implode(' ', $v) . '"';
|
231 |
-
} else {
|
232 |
-
if ($k)
|
233 |
-
$str .= ' ' . $k . '="' . $v . '"';
|
234 |
-
else
|
235 |
-
$str .= ' ' . $v;
|
236 |
-
}
|
237 |
-
}
|
238 |
-
$str .= '>' . $anchor['body'] . '</a>';
|
239 |
-
|
240 |
-
return $str;
|
241 |
-
} # build_anchor()
|
242 |
-
|
243 |
-
|
244 |
-
/**
|
245 |
-
* escape()
|
246 |
-
*
|
247 |
-
* @param string $text
|
248 |
-
* @return string $text
|
249 |
-
**/
|
250 |
-
|
251 |
-
function escape($text) {
|
252 |
-
global $escape_anchor_filter;
|
253 |
-
|
254 |
-
if ( !isset($escape_anchor_filter) )
|
255 |
-
$escape_anchor_filter = array();
|
256 |
-
|
257 |
-
foreach ( array(
|
258 |
-
'head' => "/
|
259 |
-
.*?
|
260 |
-
<\s*\/\s*head\s*>
|
261 |
-
/isx",
|
262 |
-
'blocks' => "/
|
263 |
-
<\s*(script|style|object|textarea)(?:\s.*?)?>
|
264 |
-
.*?
|
265 |
-
<\s*\/\s*\\1\s*>
|
266 |
-
/isx",
|
267 |
-
) as $regex ) {
|
268 |
-
$text = preg_replace_callback($regex, array($this, 'escape_callback'), $text);
|
269 |
-
}
|
270 |
-
|
271 |
-
return $text;
|
272 |
-
} # escape()
|
273 |
-
|
274 |
-
|
275 |
-
/**
|
276 |
-
* escape_callback()
|
277 |
-
*
|
278 |
-
* @param array $match
|
279 |
-
* @return string $text
|
280 |
-
**/
|
281 |
-
|
282 |
-
function escape_callback($match) {
|
283 |
-
global $escape_anchor_filter;
|
284 |
-
|
285 |
-
$tag_id = "----escape_anchor_utils:" . md5($match[0]) . "----";
|
286 |
-
$escape_anchor_filter[$tag_id] = $match[0];
|
287 |
-
|
288 |
-
return $tag_id;
|
289 |
-
} # escape_callback()
|
290 |
-
|
291 |
-
|
292 |
-
/**
|
293 |
-
* unescape()
|
294 |
-
*
|
295 |
-
* @param string $text
|
296 |
-
* @return string $text
|
297 |
-
**/
|
298 |
-
|
299 |
-
function unescape($text) {
|
300 |
-
global $escape_anchor_filter;
|
301 |
-
|
302 |
-
if ( !$escape_anchor_filter )
|
303 |
-
return $text;
|
304 |
-
|
305 |
-
$unescape = array_reverse($escape_anchor_filter);
|
306 |
-
|
307 |
-
return str_replace(array_keys($unescape), array_values($unescape), $text);
|
308 |
-
} # unescape()
|
309 |
-
} # anchor_utils
|
310 |
-
|
311 |
-
$anchor_utils = new anchor_utils();
|
312 |
-
?>
|
2 |
/*
|
3 |
* Anchor Utils
|
4 |
* Author: Denis de Bernardy & Mike Koepke <http://www.semiologic.com>
|
5 |
+
* Version: 1.6.2
|
6 |
*/
|
7 |
|
8 |
+
// no longer used
|
|
|
|
|
|
|
9 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
external-links-anchor-utils.php
ADDED
@@ -0,0 +1,400 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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('template_redirect', array($this, 'ob_start'), 100);
|
34 |
+
}
|
35 |
+
else {
|
36 |
+
add_filter('the_content', array($this, 'filter'), 1000000);
|
37 |
+
add_filter('the_excerpt', array($this, 'filter'), 1000000);
|
38 |
+
add_filter('comment_text', array($this, 'filter'), 1000000);
|
39 |
+
if ( $inc_text_widgets )
|
40 |
+
add_filter('widget_text', array($this, 'filter'), 1000000);
|
41 |
+
}
|
42 |
+
|
43 |
+
} #external_links_anchor_utils
|
44 |
+
|
45 |
+
|
46 |
+
/**
|
47 |
+
* ob_start()
|
48 |
+
*
|
49 |
+
* @return void
|
50 |
+
**/
|
51 |
+
|
52 |
+
function ob_start() {
|
53 |
+
ob_start(array($this, 'ob_filter'));
|
54 |
+
add_action('wp_footer', array($this, 'ob_flush'), 1000000);
|
55 |
+
} # ob_start()
|
56 |
+
|
57 |
+
/**
|
58 |
+
* ob_filter()
|
59 |
+
*
|
60 |
+
* @param string $text
|
61 |
+
* @return string $text
|
62 |
+
**/
|
63 |
+
|
64 |
+
function ob_filter($text) {
|
65 |
+
global $escape_anchor_filter;
|
66 |
+
$escape_anchor_filter = array();
|
67 |
+
|
68 |
+
$text = $this->escape($text);
|
69 |
+
|
70 |
+
$text = preg_replace_callback("/
|
71 |
+
<\s*a\s+
|
72 |
+
([^<>]+)
|
73 |
+
>
|
74 |
+
(.*?)
|
75 |
+
<\s*\/\s*a\s*>
|
76 |
+
/isx", array($this, 'ob_filter_callback'), $text);
|
77 |
+
|
78 |
+
$text = $this->unescape($text);
|
79 |
+
|
80 |
+
return $text;
|
81 |
+
} # ob_filter()
|
82 |
+
|
83 |
+
|
84 |
+
/**
|
85 |
+
* ob_flush()
|
86 |
+
*
|
87 |
+
* @return void
|
88 |
+
**/
|
89 |
+
|
90 |
+
static function ob_flush() {
|
91 |
+
ob_end_flush();
|
92 |
+
} # ob_flush()
|
93 |
+
|
94 |
+
|
95 |
+
/**
|
96 |
+
* ob_filter_callback()
|
97 |
+
*
|
98 |
+
* @param array $match
|
99 |
+
* @return string $str
|
100 |
+
**/
|
101 |
+
|
102 |
+
function ob_filter_callback($match) {
|
103 |
+
# skip empty anchors
|
104 |
+
if ( !trim($match[2]) )
|
105 |
+
return $match[0];
|
106 |
+
|
107 |
+
# parse anchor
|
108 |
+
$anchor = $this->parse_anchor($match);
|
109 |
+
|
110 |
+
if ( !$anchor )
|
111 |
+
return $match[0];
|
112 |
+
|
113 |
+
# filter anchor
|
114 |
+
// $anchor = apply_filters( 'ob_filter_anchor', $anchor );
|
115 |
+
$anchor = $this->external_links->filter( $anchor );
|
116 |
+
|
117 |
+
# return anchor
|
118 |
+
return $this->build_anchor($anchor);
|
119 |
+
} # ob_filter_callback()
|
120 |
+
|
121 |
+
|
122 |
+
/**
|
123 |
+
* filter()
|
124 |
+
*
|
125 |
+
* @param string $text
|
126 |
+
* @return string $text
|
127 |
+
**/
|
128 |
+
|
129 |
+
function filter($text) {
|
130 |
+
global $escape_anchor_filter;
|
131 |
+
$escape_anchor_filter = array();
|
132 |
+
|
133 |
+
$text = $this->escape($text);
|
134 |
+
|
135 |
+
$text = preg_replace_callback("/
|
136 |
+
<\s*a\s+
|
137 |
+
([^<>]+)
|
138 |
+
>
|
139 |
+
(.*?)
|
140 |
+
<\s*\/\s*a\s*>
|
141 |
+
/isx", array($this, 'filter_callback'), $text);
|
142 |
+
|
143 |
+
$text = $this->unescape($text);
|
144 |
+
|
145 |
+
return $text;
|
146 |
+
} # filter()
|
147 |
+
|
148 |
+
|
149 |
+
/**
|
150 |
+
* filter_callback()
|
151 |
+
*
|
152 |
+
* @param array $match
|
153 |
+
* @return string $str
|
154 |
+
**/
|
155 |
+
|
156 |
+
function filter_callback($match) {
|
157 |
+
# skip empty anchors
|
158 |
+
if ( !trim($match[2]) )
|
159 |
+
return $match[0];
|
160 |
+
|
161 |
+
# parse anchor
|
162 |
+
$anchor = $this->parse_anchor($match);
|
163 |
+
|
164 |
+
if ( !$anchor )
|
165 |
+
return $match[0];
|
166 |
+
|
167 |
+
# filter anchor
|
168 |
+
// $anchor = apply_filters( 'filter_anchor', $anchor );
|
169 |
+
$anchor = $this->external_links->filter( $anchor );
|
170 |
+
|
171 |
+
# return anchor
|
172 |
+
return $this->build_anchor($anchor);
|
173 |
+
} # filter_callback()
|
174 |
+
|
175 |
+
|
176 |
+
/**
|
177 |
+
* parse_anchor()
|
178 |
+
*
|
179 |
+
* @param array $match
|
180 |
+
* @return array $anchor
|
181 |
+
**/
|
182 |
+
|
183 |
+
function parse_anchor($match) {
|
184 |
+
$anchor = array();
|
185 |
+
$anchor['attr'] = $this->parse_attrs($match[1]);
|
186 |
+
|
187 |
+
if ( !is_array($anchor['attr']) || empty($anchor['attr']['href']) # parser error or no link
|
188 |
+
|| trim($anchor['attr']['href']) != esc_url($anchor['attr']['href'], null, 'db') ) # likely a script
|
189 |
+
return false;
|
190 |
+
|
191 |
+
foreach ( array('class', 'rel') as $attr ) {
|
192 |
+
if ( !isset($anchor['attr'][$attr]) ) {
|
193 |
+
$anchor['attr'][$attr] = array();
|
194 |
+
} else {
|
195 |
+
$anchor['attr'][$attr] = explode(' ', $anchor['attr'][$attr]);
|
196 |
+
$anchor['attr'][$attr] = array_map('trim', $anchor['attr'][$attr]);
|
197 |
+
}
|
198 |
+
}
|
199 |
+
|
200 |
+
$anchor['body'] = $match[2];
|
201 |
+
|
202 |
+
$anchor['attr']['href'] = @html_entity_decode($anchor['attr']['href'], ENT_COMPAT, get_option('blog_charset'));
|
203 |
+
|
204 |
+
return $anchor;
|
205 |
+
} # parse_anchor()
|
206 |
+
|
207 |
+
|
208 |
+
/**
|
209 |
+
* build_anchor()
|
210 |
+
*
|
211 |
+
* @param array $anchor
|
212 |
+
* @return string $anchor
|
213 |
+
**/
|
214 |
+
|
215 |
+
function build_anchor($anchor) {
|
216 |
+
$anchor['attr']['href'] = esc_url($anchor['attr']['href']);
|
217 |
+
|
218 |
+
$str = '<a';
|
219 |
+
foreach ( $anchor['attr'] as $k => $v ) {
|
220 |
+
if ( is_array($v) ) {
|
221 |
+
$v = array_unique($v);
|
222 |
+
if ( $v )
|
223 |
+
$str .= ' ' . $k . '="' . implode(' ', $v) . '"';
|
224 |
+
} else {
|
225 |
+
if ($k)
|
226 |
+
$str .= ' ' . $k . '="' . $v . '"';
|
227 |
+
else
|
228 |
+
$str .= ' ' . $v;
|
229 |
+
}
|
230 |
+
}
|
231 |
+
$str .= '>' . $anchor['body'] . '</a>';
|
232 |
+
|
233 |
+
return $str;
|
234 |
+
} # build_anchor()
|
235 |
+
|
236 |
+
|
237 |
+
/**
|
238 |
+
* escape()
|
239 |
+
*
|
240 |
+
* @param string $text
|
241 |
+
* @return string $text
|
242 |
+
**/
|
243 |
+
|
244 |
+
function escape($text) {
|
245 |
+
global $escape_anchor_filter;
|
246 |
+
|
247 |
+
if ( !isset($escape_anchor_filter) )
|
248 |
+
$escape_anchor_filter = array();
|
249 |
+
|
250 |
+
foreach ( array(
|
251 |
+
'head' => "/
|
252 |
+
.*?
|
253 |
+
<\s*\/\s*head\s*>
|
254 |
+
/isx",
|
255 |
+
'blocks' => "/
|
256 |
+
<\s*(script|style|object|textarea)(?:\s.*?)?>
|
257 |
+
.*?
|
258 |
+
<\s*\/\s*\\1\s*>
|
259 |
+
/isx",
|
260 |
+
) as $regex ) {
|
261 |
+
$text = preg_replace_callback($regex, array($this, 'escape_callback'), $text);
|
262 |
+
}
|
263 |
+
|
264 |
+
return $text;
|
265 |
+
} # escape()
|
266 |
+
|
267 |
+
|
268 |
+
/**
|
269 |
+
* escape_callback()
|
270 |
+
*
|
271 |
+
* @param array $match
|
272 |
+
* @return string $text
|
273 |
+
**/
|
274 |
+
|
275 |
+
function escape_callback($match) {
|
276 |
+
global $escape_anchor_filter;
|
277 |
+
|
278 |
+
$tag_id = "----escape_external_links_anchor_utils:" . md5($match[0]) . "----";
|
279 |
+
$escape_anchor_filter[$tag_id] = $match[0];
|
280 |
+
|
281 |
+
return $tag_id;
|
282 |
+
} # escape_callback()
|
283 |
+
|
284 |
+
|
285 |
+
/**
|
286 |
+
* unescape()
|
287 |
+
*
|
288 |
+
* @param string $text
|
289 |
+
* @return string $text
|
290 |
+
**/
|
291 |
+
|
292 |
+
function unescape($text) {
|
293 |
+
global $escape_anchor_filter;
|
294 |
+
|
295 |
+
if ( !$escape_anchor_filter )
|
296 |
+
return $text;
|
297 |
+
|
298 |
+
$unescape = array_reverse($escape_anchor_filter);
|
299 |
+
|
300 |
+
return str_replace(array_keys($unescape), array_values($unescape), $text);
|
301 |
+
} # unescape()
|
302 |
+
|
303 |
+
/**
|
304 |
+
* Parse an attributes string into an array. If the string starts with a tag,
|
305 |
+
* then the attributes on the first tag are parsed. This parses via a manual
|
306 |
+
* loop and is designed to be safer than using DOMDocument.
|
307 |
+
*
|
308 |
+
* @param string|* $attrs
|
309 |
+
* @return array
|
310 |
+
*
|
311 |
+
* @example parse_attrs( 'src="example.jpg" alt="example"' )
|
312 |
+
* @example parse_attrs( '<img src="example.jpg" alt="example">' )
|
313 |
+
* @example parse_attrs( '<a href="example"></a>' )
|
314 |
+
* @example parse_attrs( '<a href="example">' )
|
315 |
+
*/
|
316 |
+
function parse_attrs($attrs) {
|
317 |
+
|
318 |
+
if ( !is_scalar($attrs) )
|
319 |
+
return (array) $attrs;
|
320 |
+
|
321 |
+
$attrs = str_split( trim($attrs) );
|
322 |
+
|
323 |
+
if ( '<' === $attrs[0] ) # looks like a tag so strip the tagname
|
324 |
+
while ( $attrs && ! ctype_space($attrs[0]) && $attrs[0] !== '>' )
|
325 |
+
array_shift($attrs);
|
326 |
+
|
327 |
+
$arr = array(); # output
|
328 |
+
$name = ''; # for the current attr being parsed
|
329 |
+
$value = ''; # for the current attr being parsed
|
330 |
+
$mode = 0; # whether current char is part of the name (-), the value (+), or neither (0)
|
331 |
+
$stop = false; # delimiter for the current $value being parsed
|
332 |
+
$space = ' '; # a single space
|
333 |
+
$paren = 0; # in parenthesis for js attrs
|
334 |
+
|
335 |
+
foreach ( $attrs as $j => $curr ) {
|
336 |
+
|
337 |
+
if ( $mode < 0 ) {# name
|
338 |
+
if ( '=' === $curr ) {
|
339 |
+
$mode = 1;
|
340 |
+
$stop = false;
|
341 |
+
} elseif ( '>' === $curr ) {
|
342 |
+
'' === $name or $arr[ $name ] = $value;
|
343 |
+
break;
|
344 |
+
} elseif ( !ctype_space($curr) ) {
|
345 |
+
if ( ctype_space( $attrs[ $j - 1 ] ) ) { # previous char
|
346 |
+
'' === $name or $arr[ $name ] = ''; # previous name
|
347 |
+
$name = $curr; # initiate new
|
348 |
+
} else {
|
349 |
+
$name .= $curr;
|
350 |
+
}
|
351 |
+
}
|
352 |
+
} elseif ( $mode > 0 ) {# value
|
353 |
+
if ( $paren ) {
|
354 |
+
$value .= $curr;
|
355 |
+
if ( $curr === "(")
|
356 |
+
$paren += 1;
|
357 |
+
elseif ( $curr === ")")
|
358 |
+
$paren -= 1;
|
359 |
+
}
|
360 |
+
else {
|
361 |
+
if ( $stop === false ) {
|
362 |
+
if ( !ctype_space($curr) ) {
|
363 |
+
if ( '"' === $curr || "'" === $curr ) {
|
364 |
+
$value = '';
|
365 |
+
$stop = $curr;
|
366 |
+
} else {
|
367 |
+
$value = $curr;
|
368 |
+
$stop = $space;
|
369 |
+
}
|
370 |
+
}
|
371 |
+
} elseif ( $stop === $space ? ctype_space($curr) : $curr === $stop ) {
|
372 |
+
$arr[ $name ] = $value;
|
373 |
+
$mode = 0;
|
374 |
+
$name = $value = '';
|
375 |
+
} else {
|
376 |
+
$value .= $curr;
|
377 |
+
if ( $curr === "(")
|
378 |
+
$paren += 1;
|
379 |
+
elseif ( $curr === ")")
|
380 |
+
$paren -= 1;
|
381 |
+
}
|
382 |
+
}
|
383 |
+
} else {# neither
|
384 |
+
|
385 |
+
if ( '>' === $curr )
|
386 |
+
break;
|
387 |
+
if ( !ctype_space( $curr ) ) {
|
388 |
+
# initiate
|
389 |
+
$name = $curr;
|
390 |
+
$mode = -1;
|
391 |
+
}
|
392 |
+
}
|
393 |
+
}
|
394 |
+
|
395 |
+
# incl the final pair if it was quoteless
|
396 |
+
'' === $name or $arr[ $name ] = $value;
|
397 |
+
|
398 |
+
return $arr;
|
399 |
+
}
|
400 |
+
} # external_links_anchor_utils
|
readme.txt
CHANGED
@@ -3,7 +3,7 @@ Contributors: Denis-de-Bernardy, Mike_Koepke
|
|
3 |
Donate link: http://www.semiologic.com/partners/
|
4 |
Tags: external-links, nofollow, link-target, link-icon, semiologic
|
5 |
Requires at least: 2.8
|
6 |
-
Tested up to: 3.
|
7 |
License: GPLv2 or later
|
8 |
License URI: http://www.gnu.org/licenses/gpl-2.0.html
|
9 |
|
@@ -16,16 +16,16 @@ The external links plugin for WordPress lets you process outgoing links differen
|
|
16 |
|
17 |
Under Settings / External Links, you can configure the plugin to:
|
18 |
|
19 |
-
- Process all outgoing links, rather than only those within your entries' content.
|
20 |
- Add an external link icon to outgoing links. You can use a class="no_icon" attribute on links to override this.
|
|
|
21 |
- Open outgoing links in new windows. Note that this can damage your visitor's trust towards your site in that they can think your site used a pop-under.
|
22 |
-
- Add rel=nofollow to the links. You can use a rel="nofollow" attribute on links to override this.
|
23 |
|
24 |
= Help Me! =
|
25 |
|
26 |
The [Semiologic forum](http://forum.semiologic.com) is the best place to report issues. Please note, however, that while community members and I do our best to answer all queries, we're assisting you on a voluntary basis.
|
27 |
|
28 |
-
If you require more dedicated assistance, consider using [Semiologic Pro](http://www.
|
29 |
|
30 |
|
31 |
== Installation ==
|
@@ -36,6 +36,70 @@ If you require more dedicated assistance, consider using [Semiologic Pro](http:/
|
|
36 |
|
37 |
== Change Log ==
|
38 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
39 |
= 4.2 =
|
40 |
|
41 |
- WP 3.6 compat
|
3 |
Donate link: http://www.semiologic.com/partners/
|
4 |
Tags: external-links, nofollow, link-target, link-icon, semiologic
|
5 |
Requires at least: 2.8
|
6 |
+
Tested up to: 3.9
|
7 |
License: GPLv2 or later
|
8 |
License URI: http://www.gnu.org/licenses/gpl-2.0.html
|
9 |
|
16 |
|
17 |
Under Settings / External Links, you can configure the plugin to:
|
18 |
|
19 |
+
- Process all outgoing links, rather than only those within your entries' content and text widgets.
|
20 |
- Add an external link icon to outgoing links. You can use a class="no_icon" attribute on links to override this.
|
21 |
+
- Add rel=nofollow to the links. You can use a rel="follow" attribute on links to override this.
|
22 |
- Open outgoing links in new windows. Note that this can damage your visitor's trust towards your site in that they can think your site used a pop-under.
|
|
|
23 |
|
24 |
= Help Me! =
|
25 |
|
26 |
The [Semiologic forum](http://forum.semiologic.com) is the best place to report issues. Please note, however, that while community members and I do our best to answer all queries, we're assisting you on a voluntary basis.
|
27 |
|
28 |
+
If you require more dedicated assistance, consider using [Semiologic Pro](http://www.semiologic.com).
|
29 |
|
30 |
|
31 |
== Installation ==
|
36 |
|
37 |
== Change Log ==
|
38 |
|
39 |
+
= 5.5.4 =
|
40 |
+
|
41 |
+
- Remove HTML comments added in 5.4.1 to assist in troubleshooting some select site issues
|
42 |
+
|
43 |
+
= 5.5.3 =
|
44 |
+
|
45 |
+
- Use template_redirect hook and put ourselves after default hooks that most 404/redirect plugins want to use and Yoast's at priority 99999
|
46 |
+
|
47 |
+
= 5.5.2 =
|
48 |
+
|
49 |
+
- Fix compatibility with Yoast WP SEO plugin when Force Title Rewrite option is on and using the Apply Globally setting of this plugin.
|
50 |
+
|
51 |
+
= 5.5.1 =
|
52 |
+
|
53 |
+
- Additional tweak to global callback processing
|
54 |
+
|
55 |
+
= 5.5 =
|
56 |
+
|
57 |
+
- Use wp_print_footer_scripts hook instead of wp_footer as some themes fail to call wp_footer();
|
58 |
+
- Use own custom version of the anchor_utils class
|
59 |
+
- Content, excerpt and comment filters no longer called when Apply Globally is selected. Improves performance.
|
60 |
+
|
61 |
+
= 5.4.1 =
|
62 |
+
|
63 |
+
- Troubleshooting release. Adds a few html comments in the page source to ensure hooks are being called.
|
64 |
+
|
65 |
+
= 5.4 =
|
66 |
+
|
67 |
+
- Handle nested parenthesis in javascript event attributes on links and images
|
68 |
+
|
69 |
+
= 5.3.2 =
|
70 |
+
|
71 |
+
- Temporarily placeholders links - http:// and https:// (no other url components) are no longer processed.
|
72 |
+
|
73 |
+
= 5.3.1 =
|
74 |
+
|
75 |
+
- Fix localization
|
76 |
+
|
77 |
+
= 5.3 =
|
78 |
+
|
79 |
+
- Fix: Conflict with Auto Thickbox plugin that would result in text widgets still being filtered even though option was turned off
|
80 |
+
- Fix: Ensure this plugin filter is executed way back in the change to prevent other plugins/themes from reversing our changes
|
81 |
+
- Code refactoring
|
82 |
+
- WP 3.9 compat
|
83 |
+
|
84 |
+
= 5.2.1 =
|
85 |
+
|
86 |
+
- Checks for new sem_dofollow class to determine if Do Follow plugin is active
|
87 |
+
- WP 3.8 compat
|
88 |
+
|
89 |
+
= 5.2 =
|
90 |
+
|
91 |
+
- Further updates to the link attribute parsing code
|
92 |
+
- Fixed bug where external link was not processed if it was preceded by an empty text anchor link.
|
93 |
+
|
94 |
+
= 5.1 =
|
95 |
+
|
96 |
+
- Take two! With issues now with breaking google adsense code reverted back to 4.2 parsing code but added more advanced dom attribute parsing code to handle various link configurations.
|
97 |
+
|
98 |
+
= 5.0 =
|
99 |
+
|
100 |
+
- Completely replaced the mechanism for parsing links to resolve the various errors that have been occurring with different external services' link attributes
|
101 |
+
- Tested with WP 3.7
|
102 |
+
|
103 |
= 4.2 =
|
104 |
|
105 |
- WP 3.6 compat
|
sem-external-links-admin.php
CHANGED
@@ -9,7 +9,7 @@ class external_links_admin {
|
|
9 |
/**
|
10 |
* external_links_admin()
|
11 |
*/
|
12 |
-
|
13 |
add_action('settings_page_external-links', array($this, 'save_options'), 0);
|
14 |
}
|
15 |
|
@@ -25,10 +25,10 @@ class external_links_admin {
|
|
25 |
|
26 |
check_admin_referer('external_links');
|
27 |
|
28 |
-
foreach ( array('global', 'icon', 'target', 'nofollow') as $var )
|
29 |
$$var = isset($_POST[$var]);
|
30 |
|
31 |
-
update_option('external_links', compact('global', 'icon', 'target', 'nofollow'));
|
32 |
|
33 |
echo "<div class=\"updated fade\">\n"
|
34 |
. "<p>"
|
@@ -54,7 +54,7 @@ class external_links_admin {
|
|
54 |
|
55 |
$options = external_links::get_options();
|
56 |
|
57 |
-
if ( $options['nofollow'] && function_exists('strip_nofollow') ) {
|
58 |
echo "<div class=\"error\">\n"
|
59 |
. "<p>"
|
60 |
. __('Note: Your rel=nofollow preferences is being ignored because the dofollow plugin is enabled on your site.', 'external-links')
|
@@ -62,12 +62,10 @@ class external_links_admin {
|
|
62 |
. "</div>\n";
|
63 |
}
|
64 |
|
65 |
-
screen_icon();
|
66 |
-
|
67 |
echo '<h2>' . __('External Links Settings', 'external-links') . '</h2>' . "\n";
|
68 |
|
69 |
echo '<table class="form-table">' . "\n";
|
70 |
-
|
71 |
echo '<tr>' . "\n"
|
72 |
. '<th scope="row">'
|
73 |
. __('Apply Globally', 'external-links')
|
@@ -78,11 +76,26 @@ class external_links_admin {
|
|
78 |
. checked($options['global'], true, false)
|
79 |
. ' />'
|
80 |
. ' '
|
81 |
-
. __('Apply these settings to all outbound links
|
82 |
. '</label>'
|
83 |
. '</td>' . "\n"
|
84 |
. '</tr>' . "\n";
|
85 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
86 |
echo '<tr>' . "\n"
|
87 |
. '<th scope="row">'
|
88 |
. __('Add Icons', 'external-links')
|
@@ -113,7 +126,7 @@ class external_links_admin {
|
|
113 |
. __('Add a rel=nofollow attribute to outbound links.', 'external-links')
|
114 |
. '</label>'
|
115 |
. '<br />' . "\n"
|
116 |
-
. __('Note: You can override this behavior by adding
|
117 |
. '</td>' . "\n"
|
118 |
. '</tr>' . "\n";
|
119 |
|
@@ -148,4 +161,3 @@ class external_links_admin {
|
|
148 |
} # external_links_admin
|
149 |
|
150 |
$external_links_admin = new external_links_admin();
|
151 |
-
?>
|
9 |
/**
|
10 |
* external_links_admin()
|
11 |
*/
|
12 |
+
public function __construct() {
|
13 |
add_action('settings_page_external-links', array($this, 'save_options'), 0);
|
14 |
}
|
15 |
|
25 |
|
26 |
check_admin_referer('external_links');
|
27 |
|
28 |
+
foreach ( array('global', 'icon', 'target', 'nofollow', 'text_widgets') as $var )
|
29 |
$$var = isset($_POST[$var]);
|
30 |
|
31 |
+
update_option('external_links', compact('global', 'icon', 'target', 'nofollow', 'text_widgets'));
|
32 |
|
33 |
echo "<div class=\"updated fade\">\n"
|
34 |
. "<p>"
|
54 |
|
55 |
$options = external_links::get_options();
|
56 |
|
57 |
+
if ( $options['nofollow'] && ( function_exists('strip_nofollow') || class_exists('sem_dofollow') ) ) {
|
58 |
echo "<div class=\"error\">\n"
|
59 |
. "<p>"
|
60 |
. __('Note: Your rel=nofollow preferences is being ignored because the dofollow plugin is enabled on your site.', 'external-links')
|
62 |
. "</div>\n";
|
63 |
}
|
64 |
|
|
|
|
|
65 |
echo '<h2>' . __('External Links Settings', 'external-links') . '</h2>' . "\n";
|
66 |
|
67 |
echo '<table class="form-table">' . "\n";
|
68 |
+
|
69 |
echo '<tr>' . "\n"
|
70 |
. '<th scope="row">'
|
71 |
. __('Apply Globally', 'external-links')
|
76 |
. checked($options['global'], true, false)
|
77 |
. ' />'
|
78 |
. ' '
|
79 |
+
. __('Apply these settings to all outbound links on the site except those in scripts, styles and the html head section.', 'external-links')
|
80 |
. '</label>'
|
81 |
. '</td>' . "\n"
|
82 |
. '</tr>' . "\n";
|
83 |
+
|
84 |
+
echo '<tr>' . "\n"
|
85 |
+
. '<th scope="row">'
|
86 |
+
. __('Apply to Text Widgets', 'external-links')
|
87 |
+
. '</th>' . "\n"
|
88 |
+
. '<td>'
|
89 |
+
. '<label>'
|
90 |
+
. '<input type="checkbox" name="text_widgets"'
|
91 |
+
. checked($options['text_widgets'], true, false)
|
92 |
+
. ' />'
|
93 |
+
. ' '
|
94 |
+
. __('Apply these settings to all text widgets in addition to those in posts and comments.', 'external-links')
|
95 |
+
. '</label>'
|
96 |
+
. '</td>' . "\n"
|
97 |
+
. '</tr>' . "\n";
|
98 |
+
|
99 |
echo '<tr>' . "\n"
|
100 |
. '<th scope="row">'
|
101 |
. __('Add Icons', 'external-links')
|
126 |
. __('Add a rel=nofollow attribute to outbound links.', 'external-links')
|
127 |
. '</label>'
|
128 |
. '<br />' . "\n"
|
129 |
+
. __('Note: You can override this behavior by adding the attribute rel="follow" to individual links.', 'external-links')
|
130 |
. '</td>' . "\n"
|
131 |
. '</tr>' . "\n";
|
132 |
|
161 |
} # external_links_admin
|
162 |
|
163 |
$external_links_admin = new external_links_admin();
|
|
sem-external-links.php
CHANGED
@@ -3,26 +3,22 @@
|
|
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: 4
|
7 |
Author: Denis de Bernardy & Mike Koepke
|
8 |
Author URI: http://www.getsemiologic.com
|
9 |
Text Domain: external-links
|
10 |
Domain Path: /lang
|
|
|
11 |
*/
|
12 |
|
13 |
/*
|
14 |
Terms of use
|
15 |
------------
|
16 |
|
17 |
-
This software is copyright
|
18 |
|
19 |
-
http://www.opensource.org/licenses/gpl-2.0.php
|
20 |
**/
|
21 |
|
22 |
-
|
23 |
-
load_plugin_textdomain('external-links', false, dirname(plugin_basename(__FILE__)) . '/lang');
|
24 |
-
|
25 |
-
|
26 |
/**
|
27 |
* external_links
|
28 |
*
|
@@ -30,24 +26,121 @@ load_plugin_textdomain('external-links', false, dirname(plugin_basename(__FILE__
|
|
30 |
**/
|
31 |
|
32 |
class external_links {
|
33 |
-
/**
|
34 |
-
* external_links()
|
35 |
-
*/
|
36 |
-
function external_links() {
|
37 |
-
if ( !is_admin() ) {
|
38 |
-
$o = external_links::get_options();
|
39 |
|
40 |
-
|
41 |
-
add_action('wp_print_styles', array($this, 'styles'), 5);
|
42 |
|
43 |
-
|
44 |
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
49 |
}
|
50 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
51 |
/**
|
52 |
* styles()
|
53 |
*
|
@@ -58,49 +151,48 @@ class external_links {
|
|
58 |
$folder = plugin_dir_url(__FILE__);
|
59 |
wp_enqueue_style('external-links', $folder . 'sem-external-links.css', null, '20090903');
|
60 |
} # styles()
|
61 |
-
|
62 |
-
|
63 |
/**
|
64 |
* filter()
|
65 |
*
|
66 |
-
* @param
|
67 |
-
* @return
|
68 |
-
|
69 |
|
70 |
function filter($anchor) {
|
71 |
# disable in feeds
|
72 |
if ( is_feed() )
|
73 |
return $anchor;
|
74 |
-
|
75 |
# ignore local urls
|
76 |
if ( external_links::is_local_url($anchor['attr']['href']) )
|
77 |
return $anchor;
|
78 |
-
|
79 |
# no icons for images
|
80 |
$is_image = (bool) preg_match("/^\s*<\s*img\s.+?>\s*$/is", $anchor['body']);
|
81 |
-
|
82 |
$o = external_links::get_options();
|
83 |
-
|
84 |
if ( !in_array('external', $anchor['attr']['class']) )
|
85 |
$anchor['attr']['class'][] = 'external';
|
86 |
-
|
87 |
if ( !$is_image && $o['icon'] && !in_array('external_icon', $anchor['attr']['class'])
|
88 |
&& !in_array('no_icon', $anchor['attr']['class'])
|
89 |
&& !in_array('noicon', $anchor['attr']['class']) )
|
90 |
$anchor['attr']['class'][] = 'external_icon';
|
91 |
-
|
92 |
if ( $o['nofollow'] && !function_exists('strip_nofollow')
|
93 |
&& !in_array('nofollow', $anchor['attr']['rel'])
|
94 |
&& !in_array('follow', $anchor['attr']['rel']) )
|
95 |
$anchor['attr']['rel'][] = 'nofollow';
|
96 |
-
|
97 |
if ( $o['target'] && empty($anchor['attr']['target']) )
|
98 |
$anchor['attr']['target'] = '_blank';
|
99 |
-
|
100 |
return $anchor;
|
101 |
} # filter()
|
102 |
|
103 |
-
|
104 |
/**
|
105 |
* is_local_url()
|
106 |
*
|
@@ -111,6 +203,8 @@ class external_links {
|
|
111 |
function is_local_url($url) {
|
112 |
if ( in_array(substr($url, 0, 1), array('?', '#')) || strpos($url, '://') === false )
|
113 |
return true;
|
|
|
|
|
114 |
elseif ( preg_match("~/go(/|\.)~i", $url) )
|
115 |
return false;
|
116 |
|
@@ -162,9 +256,9 @@ class external_links {
|
|
162 |
if ( !$site_domain )
|
163 |
return false;
|
164 |
|
165 |
-
$link_domain = parse_url($url);
|
166 |
-
if ($link_domain
|
167 |
-
return
|
168 |
elseif (is_array($link_domain)) {
|
169 |
if (isset($link_domain['host']))
|
170 |
$link_domain = $link_domain['host'];
|
@@ -206,13 +300,13 @@ class external_links {
|
|
206 |
|
207 |
$o = get_option('external_links');
|
208 |
|
209 |
-
if ( $o === false )
|
210 |
$o = external_links::init_options();
|
211 |
-
|
212 |
return $o;
|
213 |
} # get_options()
|
214 |
-
|
215 |
-
|
216 |
/**
|
217 |
* init_options()
|
218 |
*
|
@@ -220,23 +314,27 @@ class external_links {
|
|
220 |
**/
|
221 |
|
222 |
function init_options() {
|
223 |
-
$o = get_option('
|
224 |
-
|
225 |
-
|
226 |
-
|
227 |
-
|
228 |
-
|
229 |
-
|
230 |
-
|
231 |
-
|
232 |
-
|
233 |
-
|
234 |
-
|
|
|
|
|
|
|
235 |
update_option('external_links', $o);
|
236 |
-
|
237 |
return $o;
|
238 |
} # init_options()
|
239 |
-
|
|
|
240 |
|
241 |
/**
|
242 |
* admin_menu()
|
@@ -255,17 +353,4 @@ class external_links {
|
|
255 |
} # admin_menu()
|
256 |
} # external_links
|
257 |
|
258 |
-
|
259 |
-
function external_links_admin() {
|
260 |
-
include_once dirname(__FILE__) . '/sem-external-links-admin.php';
|
261 |
-
}
|
262 |
-
|
263 |
-
add_action('load-settings_page_external-links', 'external_links_admin');
|
264 |
-
|
265 |
-
if ( !is_admin() ) {
|
266 |
-
if ( !class_exists('anchor_utils') )
|
267 |
-
include dirname(__FILE__) . '/anchor-utils/anchor-utils.php';
|
268 |
-
}
|
269 |
-
|
270 |
-
$external_links = new external_links();
|
271 |
-
?>
|
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.4
|
7 |
Author: Denis de Bernardy & Mike Koepke
|
8 |
Author URI: http://www.getsemiologic.com
|
9 |
Text Domain: external-links
|
10 |
Domain Path: /lang
|
11 |
+
License: Dual licensed under the MIT and GPLv2 licenses
|
12 |
*/
|
13 |
|
14 |
/*
|
15 |
Terms of use
|
16 |
------------
|
17 |
|
18 |
+
This software is copyright Denis de Bernardy & Mike Koepke, and is distributed under the terms of the MIT and GPLv2 licenses.
|
19 |
|
|
|
20 |
**/
|
21 |
|
|
|
|
|
|
|
|
|
22 |
/**
|
23 |
* external_links
|
24 |
*
|
26 |
**/
|
27 |
|
28 |
class external_links {
|
|
|
|
|
|
|
|
|
|
|
|
|
29 |
|
30 |
+
protected $opts;
|
|
|
31 |
|
32 |
+
protected $anchor_utils;
|
33 |
|
34 |
+
/**
|
35 |
+
* Plugin instance.
|
36 |
+
*
|
37 |
+
* @see get_instance()
|
38 |
+
* @type object
|
39 |
+
*/
|
40 |
+
protected static $instance = NULL;
|
41 |
+
|
42 |
+
/**
|
43 |
+
* URL to this plugin's directory.
|
44 |
+
*
|
45 |
+
* @type string
|
46 |
+
*/
|
47 |
+
public $plugin_url = '';
|
48 |
+
|
49 |
+
/**
|
50 |
+
* Path to this plugin's directory.
|
51 |
+
*
|
52 |
+
* @type string
|
53 |
+
*/
|
54 |
+
public $plugin_path = '';
|
55 |
+
|
56 |
+
/**
|
57 |
+
* Access this plugin’s working instance
|
58 |
+
*
|
59 |
+
* @wp-hook plugins_loaded
|
60 |
+
* @return object of this class
|
61 |
+
*/
|
62 |
+
public static function get_instance()
|
63 |
+
{
|
64 |
+
NULL === self::$instance and self::$instance = new self;
|
65 |
+
|
66 |
+
return self::$instance;
|
67 |
+
}
|
68 |
+
|
69 |
+
/**
|
70 |
+
* Loads translation file.
|
71 |
+
*
|
72 |
+
* Accessible to other classes to load different language files (admin and
|
73 |
+
* front-end for example).
|
74 |
+
*
|
75 |
+
* @wp-hook init
|
76 |
+
* @param string $domain
|
77 |
+
* @return void
|
78 |
+
*/
|
79 |
+
public function load_language( $domain )
|
80 |
+
{
|
81 |
+
load_plugin_textdomain(
|
82 |
+
$domain,
|
83 |
+
FALSE,
|
84 |
+
dirname(plugin_basename(__FILE__)) . '/lang'
|
85 |
+
);
|
86 |
+
}
|
87 |
+
|
88 |
+
/**
|
89 |
+
* Constructor.
|
90 |
+
*
|
91 |
+
*
|
92 |
+
*/
|
93 |
+
public function __construct() {
|
94 |
+
$this->plugin_url = plugins_url( '/', __FILE__ );
|
95 |
+
$this->plugin_path = plugin_dir_path( __FILE__ );
|
96 |
+
$this->load_language( 'external-links' );
|
97 |
+
|
98 |
+
add_action( 'plugins_loaded', array ( $this, 'init' ) );
|
99 |
}
|
100 |
|
101 |
+
|
102 |
+
/**
|
103 |
+
* init()
|
104 |
+
*
|
105 |
+
* @return void
|
106 |
+
**/
|
107 |
+
|
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 {
|
130 |
+
add_action('admin_menu', array($this, 'admin_menu'));
|
131 |
+
add_action('load-settings_page_external-links', array($this, 'external_links_admin'));
|
132 |
+
}
|
133 |
+
}
|
134 |
+
|
135 |
+
/**
|
136 |
+
* external_links_admin()
|
137 |
+
*
|
138 |
+
* @return void
|
139 |
+
**/
|
140 |
+
function external_links_admin() {
|
141 |
+
include_once $this->plugin_path . '/sem-external-links-admin.php';
|
142 |
+
}
|
143 |
+
|
144 |
/**
|
145 |
* styles()
|
146 |
*
|
151 |
$folder = plugin_dir_url(__FILE__);
|
152 |
wp_enqueue_style('external-links', $folder . 'sem-external-links.css', null, '20090903');
|
153 |
} # styles()
|
154 |
+
|
155 |
+
|
156 |
/**
|
157 |
* filter()
|
158 |
*
|
159 |
+
* @param $anchor
|
160 |
+
* @return string
|
161 |
+
*/
|
162 |
|
163 |
function filter($anchor) {
|
164 |
# disable in feeds
|
165 |
if ( is_feed() )
|
166 |
return $anchor;
|
167 |
+
|
168 |
# ignore local urls
|
169 |
if ( external_links::is_local_url($anchor['attr']['href']) )
|
170 |
return $anchor;
|
171 |
+
|
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 |
+
|
180 |
if ( !$is_image && $o['icon'] && !in_array('external_icon', $anchor['attr']['class'])
|
181 |
&& !in_array('no_icon', $anchor['attr']['class'])
|
182 |
&& !in_array('noicon', $anchor['attr']['class']) )
|
183 |
$anchor['attr']['class'][] = 'external_icon';
|
184 |
+
|
185 |
if ( $o['nofollow'] && !function_exists('strip_nofollow')
|
186 |
&& !in_array('nofollow', $anchor['attr']['rel'])
|
187 |
&& !in_array('follow', $anchor['attr']['rel']) )
|
188 |
$anchor['attr']['rel'][] = 'nofollow';
|
189 |
+
|
190 |
if ( $o['target'] && empty($anchor['attr']['target']) )
|
191 |
$anchor['attr']['target'] = '_blank';
|
192 |
+
|
193 |
return $anchor;
|
194 |
} # filter()
|
195 |
|
|
|
196 |
/**
|
197 |
* is_local_url()
|
198 |
*
|
203 |
function is_local_url($url) {
|
204 |
if ( in_array(substr($url, 0, 1), array('?', '#')) || strpos($url, '://') === false )
|
205 |
return true;
|
206 |
+
elseif ( $url == 'http://' || $url == 'https://' )
|
207 |
+
return true;
|
208 |
elseif ( preg_match("~/go(/|\.)~i", $url) )
|
209 |
return false;
|
210 |
|
256 |
if ( !$site_domain )
|
257 |
return false;
|
258 |
|
259 |
+
$link_domain = @parse_url($url);
|
260 |
+
if ($link_domain === false)
|
261 |
+
return true;
|
262 |
elseif (is_array($link_domain)) {
|
263 |
if (isset($link_domain['host']))
|
264 |
$link_domain = $link_domain['host'];
|
300 |
|
301 |
$o = get_option('external_links');
|
302 |
|
303 |
+
if ( $o === false || !isset($o['text_widgets']) )
|
304 |
$o = external_links::init_options();
|
305 |
+
|
306 |
return $o;
|
307 |
} # get_options()
|
308 |
+
|
309 |
+
|
310 |
/**
|
311 |
* init_options()
|
312 |
*
|
314 |
**/
|
315 |
|
316 |
function init_options() {
|
317 |
+
$o = get_option('external_links');
|
318 |
+
|
319 |
+
$defaults = array(
|
320 |
+
'global' => false,
|
321 |
+
'icon' => true,
|
322 |
+
'target' => false,
|
323 |
+
'nofollow' => true,
|
324 |
+
'text_widgets' => true,
|
325 |
+
);
|
326 |
+
|
327 |
+
if ( !$o )
|
328 |
+
$o = $defaults;
|
329 |
+
else
|
330 |
+
$o = wp_parse_args($o, $defaults);
|
331 |
+
|
332 |
update_option('external_links', $o);
|
333 |
+
|
334 |
return $o;
|
335 |
} # init_options()
|
336 |
+
|
337 |
+
|
338 |
|
339 |
/**
|
340 |
* admin_menu()
|
353 |
} # admin_menu()
|
354 |
} # external_links
|
355 |
|
356 |
+
$external_links = external_links::get_instance();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|