|
1 <?php |
|
2 /** |
|
3 * WordPress API for creating bbcode like tags or what WordPress calls |
|
4 * "shortcodes." The tag and attribute parsing or regular expression code is |
|
5 * based on the Textpattern tag parser. |
|
6 * |
|
7 * A few examples are below: |
|
8 * |
|
9 * [shortcode /] |
|
10 * [shortcode foo="bar" baz="bing" /] |
|
11 * [shortcode foo="bar"]content[/shortcode] |
|
12 * |
|
13 * Shortcode tags support attributes and enclosed content, but does not entirely |
|
14 * support inline shortcodes in other shortcodes. You will have to call the |
|
15 * shortcode parser in your function to account for that. |
|
16 * |
|
17 * {@internal |
|
18 * Please be aware that the above note was made during the beta of WordPress 2.6 |
|
19 * and in the future may not be accurate. Please update the note when it is no |
|
20 * longer the case.}} |
|
21 * |
|
22 * To apply shortcode tags to content: |
|
23 * |
|
24 * <code> |
|
25 * $out = do_shortcode($content); |
|
26 * </code> |
|
27 * |
|
28 * @link http://codex.wordpress.org/Shortcode_API |
|
29 * |
|
30 * @package WordPress |
|
31 * @subpackage Shortcodes |
|
32 * @since 2.5 |
|
33 */ |
|
34 |
|
35 /** |
|
36 * Container for storing shortcode tags and their hook to call for the shortcode |
|
37 * |
|
38 * @since 2.5 |
|
39 * @name $shortcode_tags |
|
40 * @var array |
|
41 * @global array $shortcode_tags |
|
42 */ |
|
43 $shortcode_tags = array(); |
|
44 |
|
45 /** |
|
46 * Add hook for shortcode tag. |
|
47 * |
|
48 * There can only be one hook for each shortcode. Which means that if another |
|
49 * plugin has a similar shortcode, it will override yours or yours will override |
|
50 * theirs depending on which order the plugins are included and/or ran. |
|
51 * |
|
52 * Simplest example of a shortcode tag using the API: |
|
53 * |
|
54 * <code> |
|
55 * // [footag foo="bar"] |
|
56 * function footag_func($atts) { |
|
57 * return "foo = {$atts[foo]}"; |
|
58 * } |
|
59 * add_shortcode('footag', 'footag_func'); |
|
60 * </code> |
|
61 * |
|
62 * Example with nice attribute defaults: |
|
63 * |
|
64 * <code> |
|
65 * // [bartag foo="bar"] |
|
66 * function bartag_func($atts) { |
|
67 * extract(shortcode_atts(array( |
|
68 * 'foo' => 'no foo', |
|
69 * 'baz' => 'default baz', |
|
70 * ), $atts)); |
|
71 * |
|
72 * return "foo = {$foo}"; |
|
73 * } |
|
74 * add_shortcode('bartag', 'bartag_func'); |
|
75 * </code> |
|
76 * |
|
77 * Example with enclosed content: |
|
78 * |
|
79 * <code> |
|
80 * // [baztag]content[/baztag] |
|
81 * function baztag_func($atts, $content='') { |
|
82 * return "content = $content"; |
|
83 * } |
|
84 * add_shortcode('baztag', 'baztag_func'); |
|
85 * </code> |
|
86 * |
|
87 * @since 2.5 |
|
88 * @uses $shortcode_tags |
|
89 * |
|
90 * @param string $tag Shortcode tag to be searched in post content. |
|
91 * @param callable $func Hook to run when shortcode is found. |
|
92 */ |
|
93 function add_shortcode($tag, $func) { |
|
94 global $shortcode_tags; |
|
95 |
|
96 if ( is_callable($func) ) |
|
97 $shortcode_tags[$tag] = $func; |
|
98 } |
|
99 |
|
100 /** |
|
101 * Removes hook for shortcode. |
|
102 * |
|
103 * @since 2.5 |
|
104 * @uses $shortcode_tags |
|
105 * |
|
106 * @param string $tag shortcode tag to remove hook for. |
|
107 */ |
|
108 function remove_shortcode($tag) { |
|
109 global $shortcode_tags; |
|
110 |
|
111 unset($shortcode_tags[$tag]); |
|
112 } |
|
113 |
|
114 /** |
|
115 * Clear all shortcodes. |
|
116 * |
|
117 * This function is simple, it clears all of the shortcode tags by replacing the |
|
118 * shortcodes global by a empty array. This is actually a very efficient method |
|
119 * for removing all shortcodes. |
|
120 * |
|
121 * @since 2.5 |
|
122 * @uses $shortcode_tags |
|
123 */ |
|
124 function remove_all_shortcodes() { |
|
125 global $shortcode_tags; |
|
126 |
|
127 $shortcode_tags = array(); |
|
128 } |
|
129 |
|
130 /** |
|
131 * Whether a registered shortcode exists named $tag |
|
132 * |
|
133 * @since 3.6.0 |
|
134 * |
|
135 * @global array $shortcode_tags |
|
136 * @param string $tag |
|
137 * @return boolean |
|
138 */ |
|
139 function shortcode_exists( $tag ) { |
|
140 global $shortcode_tags; |
|
141 return array_key_exists( $tag, $shortcode_tags ); |
|
142 } |
|
143 |
|
144 /** |
|
145 * Whether the passed content contains the specified shortcode |
|
146 * |
|
147 * @since 3.6.0 |
|
148 * |
|
149 * @global array $shortcode_tags |
|
150 * @param string $tag |
|
151 * @return boolean |
|
152 */ |
|
153 function has_shortcode( $content, $tag ) { |
|
154 if ( shortcode_exists( $tag ) ) { |
|
155 preg_match_all( '/' . get_shortcode_regex() . '/s', $content, $matches, PREG_SET_ORDER ); |
|
156 if ( empty( $matches ) ) |
|
157 return false; |
|
158 |
|
159 foreach ( $matches as $shortcode ) { |
|
160 if ( $tag === $shortcode[2] ) |
|
161 return true; |
|
162 } |
|
163 } |
|
164 return false; |
|
165 } |
|
166 |
|
167 /** |
|
168 * Search content for shortcodes and filter shortcodes through their hooks. |
|
169 * |
|
170 * If there are no shortcode tags defined, then the content will be returned |
|
171 * without any filtering. This might cause issues when plugins are disabled but |
|
172 * the shortcode will still show up in the post or content. |
|
173 * |
|
174 * @since 2.5 |
|
175 * @uses $shortcode_tags |
|
176 * @uses get_shortcode_regex() Gets the search pattern for searching shortcodes. |
|
177 * |
|
178 * @param string $content Content to search for shortcodes |
|
179 * @return string Content with shortcodes filtered out. |
|
180 */ |
|
181 function do_shortcode($content) { |
|
182 global $shortcode_tags; |
|
183 |
|
184 if (empty($shortcode_tags) || !is_array($shortcode_tags)) |
|
185 return $content; |
|
186 |
|
187 $pattern = get_shortcode_regex(); |
|
188 return preg_replace_callback( "/$pattern/s", 'do_shortcode_tag', $content ); |
|
189 } |
|
190 |
|
191 /** |
|
192 * Retrieve the shortcode regular expression for searching. |
|
193 * |
|
194 * The regular expression combines the shortcode tags in the regular expression |
|
195 * in a regex class. |
|
196 * |
|
197 * The regular expression contains 6 different sub matches to help with parsing. |
|
198 * |
|
199 * 1 - An extra [ to allow for escaping shortcodes with double [[]] |
|
200 * 2 - The shortcode name |
|
201 * 3 - The shortcode argument list |
|
202 * 4 - The self closing / |
|
203 * 5 - The content of a shortcode when it wraps some content. |
|
204 * 6 - An extra ] to allow for escaping shortcodes with double [[]] |
|
205 * |
|
206 * @since 2.5 |
|
207 * @uses $shortcode_tags |
|
208 * |
|
209 * @return string The shortcode search regular expression |
|
210 */ |
|
211 function get_shortcode_regex() { |
|
212 global $shortcode_tags; |
|
213 $tagnames = array_keys($shortcode_tags); |
|
214 $tagregexp = join( '|', array_map('preg_quote', $tagnames) ); |
|
215 |
|
216 // WARNING! Do not change this regex without changing do_shortcode_tag() and strip_shortcode_tag() |
|
217 // Also, see shortcode_unautop() and shortcode.js. |
|
218 return |
|
219 '\\[' // Opening bracket |
|
220 . '(\\[?)' // 1: Optional second opening bracket for escaping shortcodes: [[tag]] |
|
221 . "($tagregexp)" // 2: Shortcode name |
|
222 . '(?![\\w-])' // Not followed by word character or hyphen |
|
223 . '(' // 3: Unroll the loop: Inside the opening shortcode tag |
|
224 . '[^\\]\\/]*' // Not a closing bracket or forward slash |
|
225 . '(?:' |
|
226 . '\\/(?!\\])' // A forward slash not followed by a closing bracket |
|
227 . '[^\\]\\/]*' // Not a closing bracket or forward slash |
|
228 . ')*?' |
|
229 . ')' |
|
230 . '(?:' |
|
231 . '(\\/)' // 4: Self closing tag ... |
|
232 . '\\]' // ... and closing bracket |
|
233 . '|' |
|
234 . '\\]' // Closing bracket |
|
235 . '(?:' |
|
236 . '(' // 5: Unroll the loop: Optionally, anything between the opening and closing shortcode tags |
|
237 . '[^\\[]*+' // Not an opening bracket |
|
238 . '(?:' |
|
239 . '\\[(?!\\/\\2\\])' // An opening bracket not followed by the closing shortcode tag |
|
240 . '[^\\[]*+' // Not an opening bracket |
|
241 . ')*+' |
|
242 . ')' |
|
243 . '\\[\\/\\2\\]' // Closing shortcode tag |
|
244 . ')?' |
|
245 . ')' |
|
246 . '(\\]?)'; // 6: Optional second closing brocket for escaping shortcodes: [[tag]] |
|
247 } |
|
248 |
|
249 /** |
|
250 * Regular Expression callable for do_shortcode() for calling shortcode hook. |
|
251 * @see get_shortcode_regex for details of the match array contents. |
|
252 * |
|
253 * @since 2.5 |
|
254 * @access private |
|
255 * @uses $shortcode_tags |
|
256 * |
|
257 * @param array $m Regular expression match array |
|
258 * @return mixed False on failure. |
|
259 */ |
|
260 function do_shortcode_tag( $m ) { |
|
261 global $shortcode_tags; |
|
262 |
|
263 // allow [[foo]] syntax for escaping a tag |
|
264 if ( $m[1] == '[' && $m[6] == ']' ) { |
|
265 return substr($m[0], 1, -1); |
|
266 } |
|
267 |
|
268 $tag = $m[2]; |
|
269 $attr = shortcode_parse_atts( $m[3] ); |
|
270 |
|
271 if ( isset( $m[5] ) ) { |
|
272 // enclosing tag - extra parameter |
|
273 return $m[1] . call_user_func( $shortcode_tags[$tag], $attr, $m[5], $tag ) . $m[6]; |
|
274 } else { |
|
275 // self-closing tag |
|
276 return $m[1] . call_user_func( $shortcode_tags[$tag], $attr, null, $tag ) . $m[6]; |
|
277 } |
|
278 } |
|
279 |
|
280 /** |
|
281 * Retrieve all attributes from the shortcodes tag. |
|
282 * |
|
283 * The attributes list has the attribute name as the key and the value of the |
|
284 * attribute as the value in the key/value pair. This allows for easier |
|
285 * retrieval of the attributes, since all attributes have to be known. |
|
286 * |
|
287 * @since 2.5 |
|
288 * |
|
289 * @param string $text |
|
290 * @return array List of attributes and their value. |
|
291 */ |
|
292 function shortcode_parse_atts($text) { |
|
293 $atts = array(); |
|
294 $pattern = '/(\w+)\s*=\s*"([^"]*)"(?:\s|$)|(\w+)\s*=\s*\'([^\']*)\'(?:\s|$)|(\w+)\s*=\s*([^\s\'"]+)(?:\s|$)|"([^"]*)"(?:\s|$)|(\S+)(?:\s|$)/'; |
|
295 $text = preg_replace("/[\x{00a0}\x{200b}]+/u", " ", $text); |
|
296 if ( preg_match_all($pattern, $text, $match, PREG_SET_ORDER) ) { |
|
297 foreach ($match as $m) { |
|
298 if (!empty($m[1])) |
|
299 $atts[strtolower($m[1])] = stripcslashes($m[2]); |
|
300 elseif (!empty($m[3])) |
|
301 $atts[strtolower($m[3])] = stripcslashes($m[4]); |
|
302 elseif (!empty($m[5])) |
|
303 $atts[strtolower($m[5])] = stripcslashes($m[6]); |
|
304 elseif (isset($m[7]) and strlen($m[7])) |
|
305 $atts[] = stripcslashes($m[7]); |
|
306 elseif (isset($m[8])) |
|
307 $atts[] = stripcslashes($m[8]); |
|
308 } |
|
309 } else { |
|
310 $atts = ltrim($text); |
|
311 } |
|
312 return $atts; |
|
313 } |
|
314 |
|
315 /** |
|
316 * Combine user attributes with known attributes and fill in defaults when needed. |
|
317 * |
|
318 * The pairs should be considered to be all of the attributes which are |
|
319 * supported by the caller and given as a list. The returned attributes will |
|
320 * only contain the attributes in the $pairs list. |
|
321 * |
|
322 * If the $atts list has unsupported attributes, then they will be ignored and |
|
323 * removed from the final returned list. |
|
324 * |
|
325 * @since 2.5 |
|
326 * |
|
327 * @param array $pairs Entire list of supported attributes and their defaults. |
|
328 * @param array $atts User defined attributes in shortcode tag. |
|
329 * @param string $shortcode Optional. The name of the shortcode, provided for context to enable filtering |
|
330 * @return array Combined and filtered attribute list. |
|
331 */ |
|
332 function shortcode_atts( $pairs, $atts, $shortcode = '' ) { |
|
333 $atts = (array)$atts; |
|
334 $out = array(); |
|
335 foreach($pairs as $name => $default) { |
|
336 if ( array_key_exists($name, $atts) ) |
|
337 $out[$name] = $atts[$name]; |
|
338 else |
|
339 $out[$name] = $default; |
|
340 } |
|
341 /** |
|
342 * Filter a shortcode's default attributes. |
|
343 * |
|
344 * If the third parameter of the shortcode_atts() function is present then this filter is available. |
|
345 * The third parameter, $shortcode, is the name of the shortcode. |
|
346 * |
|
347 * @since 3.6.0 |
|
348 * |
|
349 * @param array $out The output array of shortcode attributes. |
|
350 * @param array $pairs The supported attributes and their defaults. |
|
351 * @param array $atts The user defined shortcode attributes. |
|
352 */ |
|
353 if ( $shortcode ) |
|
354 $out = apply_filters( "shortcode_atts_{$shortcode}", $out, $pairs, $atts ); |
|
355 |
|
356 return $out; |
|
357 } |
|
358 |
|
359 /** |
|
360 * Remove all shortcode tags from the given content. |
|
361 * |
|
362 * @since 2.5 |
|
363 * @uses $shortcode_tags |
|
364 * |
|
365 * @param string $content Content to remove shortcode tags. |
|
366 * @return string Content without shortcode tags. |
|
367 */ |
|
368 function strip_shortcodes( $content ) { |
|
369 global $shortcode_tags; |
|
370 |
|
371 if (empty($shortcode_tags) || !is_array($shortcode_tags)) |
|
372 return $content; |
|
373 |
|
374 $pattern = get_shortcode_regex(); |
|
375 |
|
376 return preg_replace_callback( "/$pattern/s", 'strip_shortcode_tag', $content ); |
|
377 } |
|
378 |
|
379 function strip_shortcode_tag( $m ) { |
|
380 // allow [[foo]] syntax for escaping a tag |
|
381 if ( $m[1] == '[' && $m[6] == ']' ) { |
|
382 return substr($m[0], 1, -1); |
|
383 } |
|
384 |
|
385 return $m[1] . $m[6]; |
|
386 } |
|
387 |
|
388 add_filter('the_content', 'do_shortcode', 11); // AFTER wpautop() |