|
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 * Search content for shortcodes and filter shortcodes through their hooks. |
|
132 * |
|
133 * If there are no shortcode tags defined, then the content will be returned |
|
134 * without any filtering. This might cause issues when plugins are disabled but |
|
135 * the shortcode will still show up in the post or content. |
|
136 * |
|
137 * @since 2.5 |
|
138 * @uses $shortcode_tags |
|
139 * @uses get_shortcode_regex() Gets the search pattern for searching shortcodes. |
|
140 * |
|
141 * @param string $content Content to search for shortcodes |
|
142 * @return string Content with shortcodes filtered out. |
|
143 */ |
|
144 function do_shortcode($content) { |
|
145 global $shortcode_tags; |
|
146 |
|
147 if (empty($shortcode_tags) || !is_array($shortcode_tags)) |
|
148 return $content; |
|
149 |
|
150 $pattern = get_shortcode_regex(); |
|
151 return preg_replace_callback('/'.$pattern.'/s', 'do_shortcode_tag', $content); |
|
152 } |
|
153 |
|
154 /** |
|
155 * Retrieve the shortcode regular expression for searching. |
|
156 * |
|
157 * The regular expression combines the shortcode tags in the regular expression |
|
158 * in a regex class. |
|
159 * |
|
160 * The regular expresion contains 6 different sub matches to help with parsing. |
|
161 * |
|
162 * 1/6 - An extra [ or ] to allow for escaping shortcodes with double [[]] |
|
163 * 2 - The shortcode name |
|
164 * 3 - The shortcode argument list |
|
165 * 4 - The self closing / |
|
166 * 5 - The content of a shortcode when it wraps some content. |
|
167 * |
|
168 * @since 2.5 |
|
169 * @uses $shortcode_tags |
|
170 * |
|
171 * @return string The shortcode search regular expression |
|
172 */ |
|
173 function get_shortcode_regex() { |
|
174 global $shortcode_tags; |
|
175 $tagnames = array_keys($shortcode_tags); |
|
176 $tagregexp = join( '|', array_map('preg_quote', $tagnames) ); |
|
177 |
|
178 return '(.?)\[('.$tagregexp.')\b(.*?)(?:(\/))?\](?:(.+?)\[\/\2\])?(.?)'; |
|
179 } |
|
180 |
|
181 /** |
|
182 * Regular Expression callable for do_shortcode() for calling shortcode hook. |
|
183 * @see get_shortcode_regex for details of the match array contents. |
|
184 * |
|
185 * @since 2.5 |
|
186 * @access private |
|
187 * @uses $shortcode_tags |
|
188 * |
|
189 * @param array $m Regular expression match array |
|
190 * @return mixed False on failure. |
|
191 */ |
|
192 function do_shortcode_tag($m) { |
|
193 global $shortcode_tags; |
|
194 |
|
195 // allow [[foo]] syntax for escaping a tag |
|
196 if ($m[1] == '[' && $m[6] == ']') { |
|
197 return substr($m[0], 1, -1); |
|
198 } |
|
199 |
|
200 $tag = $m[2]; |
|
201 $attr = shortcode_parse_atts($m[3]); |
|
202 |
|
203 if ( isset($m[5]) ) { |
|
204 // enclosing tag - extra parameter |
|
205 return $m[1] . call_user_func($shortcode_tags[$tag], $attr, $m[5], $m[2]) . $m[6]; |
|
206 } else { |
|
207 // self-closing tag |
|
208 return $m[1] . call_user_func($shortcode_tags[$tag], $attr, NULL, $m[2]) . $m[6]; |
|
209 } |
|
210 } |
|
211 |
|
212 /** |
|
213 * Retrieve all attributes from the shortcodes tag. |
|
214 * |
|
215 * The attributes list has the attribute name as the key and the value of the |
|
216 * attribute as the value in the key/value pair. This allows for easier |
|
217 * retrieval of the attributes, since all attributes have to be known. |
|
218 * |
|
219 * @since 2.5 |
|
220 * |
|
221 * @param string $text |
|
222 * @return array List of attributes and their value. |
|
223 */ |
|
224 function shortcode_parse_atts($text) { |
|
225 $atts = array(); |
|
226 $pattern = '/(\w+)\s*=\s*"([^"]*)"(?:\s|$)|(\w+)\s*=\s*\'([^\']*)\'(?:\s|$)|(\w+)\s*=\s*([^\s\'"]+)(?:\s|$)|"([^"]*)"(?:\s|$)|(\S+)(?:\s|$)/'; |
|
227 $text = preg_replace("/[\x{00a0}\x{200b}]+/u", " ", $text); |
|
228 if ( preg_match_all($pattern, $text, $match, PREG_SET_ORDER) ) { |
|
229 foreach ($match as $m) { |
|
230 if (!empty($m[1])) |
|
231 $atts[strtolower($m[1])] = stripcslashes($m[2]); |
|
232 elseif (!empty($m[3])) |
|
233 $atts[strtolower($m[3])] = stripcslashes($m[4]); |
|
234 elseif (!empty($m[5])) |
|
235 $atts[strtolower($m[5])] = stripcslashes($m[6]); |
|
236 elseif (isset($m[7]) and strlen($m[7])) |
|
237 $atts[] = stripcslashes($m[7]); |
|
238 elseif (isset($m[8])) |
|
239 $atts[] = stripcslashes($m[8]); |
|
240 } |
|
241 } else { |
|
242 $atts = ltrim($text); |
|
243 } |
|
244 return $atts; |
|
245 } |
|
246 |
|
247 /** |
|
248 * Combine user attributes with known attributes and fill in defaults when needed. |
|
249 * |
|
250 * The pairs should be considered to be all of the attributes which are |
|
251 * supported by the caller and given as a list. The returned attributes will |
|
252 * only contain the attributes in the $pairs list. |
|
253 * |
|
254 * If the $atts list has unsupported attributes, then they will be ignored and |
|
255 * removed from the final returned list. |
|
256 * |
|
257 * @since 2.5 |
|
258 * |
|
259 * @param array $pairs Entire list of supported attributes and their defaults. |
|
260 * @param array $atts User defined attributes in shortcode tag. |
|
261 * @return array Combined and filtered attribute list. |
|
262 */ |
|
263 function shortcode_atts($pairs, $atts) { |
|
264 $atts = (array)$atts; |
|
265 $out = array(); |
|
266 foreach($pairs as $name => $default) { |
|
267 if ( array_key_exists($name, $atts) ) |
|
268 $out[$name] = $atts[$name]; |
|
269 else |
|
270 $out[$name] = $default; |
|
271 } |
|
272 return $out; |
|
273 } |
|
274 |
|
275 /** |
|
276 * Remove all shortcode tags from the given content. |
|
277 * |
|
278 * @since 2.5 |
|
279 * @uses $shortcode_tags |
|
280 * |
|
281 * @param string $content Content to remove shortcode tags. |
|
282 * @return string Content without shortcode tags. |
|
283 */ |
|
284 function strip_shortcodes( $content ) { |
|
285 global $shortcode_tags; |
|
286 |
|
287 if (empty($shortcode_tags) || !is_array($shortcode_tags)) |
|
288 return $content; |
|
289 |
|
290 $pattern = get_shortcode_regex(); |
|
291 |
|
292 return preg_replace('/'.$pattern.'/s', '', $content); |
|
293 } |
|
294 |
|
295 add_filter('the_content', 'do_shortcode', 11); // AFTER wpautop() |
|
296 |
|
297 ?> |