X-Git-Url: https://scripts.mit.edu/gitweb/autoinstalls/wordpress.git/blobdiff_plain/177fd6fefd2e3d5a0ea6591c71d660cabdb3c1a4..caeaf8dc94b5e3f75dc98ec92dc7b76049cdddb6:/wp-includes/shortcodes.php diff --git a/wp-includes/shortcodes.php b/wp-includes/shortcodes.php index 907f7ff3..99eec774 100644 --- a/wp-includes/shortcodes.php +++ b/wp-includes/shortcodes.php @@ -1,52 +1,91 @@ 'no foo', - 'baz' => 'default baz', - ), $atts)); - - return "foo = {$foo}"; -} -add_shortcode('bartag', 'bartag_func'); - -Example with enclosed content: - -// [baztag]content[/baztag] -function baztag_func($atts, $content='') { - return "content = $content"; -} -add_shortcode('baztag', 'baztag_func'); - -*/ - +/** + * WordPress API for creating bbcode like tags or what WordPress calls + * "shortcodes." The tag and attribute parsing or regular expression code is + * based on the Textpattern tag parser. + * + * A few examples are below: + * + * [shortcode /] + * [shortcode foo="bar" baz="bing" /] + * [shortcode foo="bar"]content[/shortcode] + * + * Shortcode tags support attributes and enclosed content, but does not entirely + * support inline shortcodes in other shortcodes. You will have to call the + * shortcode parser in your function to account for that. + * + * {@internal + * Please be aware that the above note was made during the beta of WordPress 2.6 + * and in the future may not be accurate. Please update the note when it is no + * longer the case.}} + * + * To apply shortcode tags to content: + * + * $out = do_shortcode( $content ); + * + * @link http://codex.wordpress.org/Shortcode_API + * + * @package WordPress + * @subpackage Shortcodes + * @since 2.5.0 + */ + +/** + * Container for storing shortcode tags and their hook to call for the shortcode + * + * @since 2.5.0 + * + * @name $shortcode_tags + * @var array + * @global array $shortcode_tags + */ $shortcode_tags = array(); +/** + * Add hook for shortcode tag. + * + * There can only be one hook for each shortcode. Which means that if another + * plugin has a similar shortcode, it will override yours or yours will override + * theirs depending on which order the plugins are included and/or ran. + * + * Simplest example of a shortcode tag using the API: + * + * // [footag foo="bar"] + * function footag_func( $atts ) { + * return "foo = { + * $atts[foo] + * }"; + * } + * add_shortcode( 'footag', 'footag_func' ); + * + * Example with nice attribute defaults: + * + * // [bartag foo="bar"] + * function bartag_func( $atts ) { + * $args = shortcode_atts( array( + * 'foo' => 'no foo', + * 'baz' => 'default baz', + * ), $atts ); + * + * return "foo = {$args['foo']}"; + * } + * add_shortcode( 'bartag', 'bartag_func' ); + * + * Example with enclosed content: + * + * // [baztag]content[/baztag] + * function baztag_func( $atts, $content = '' ) { + * return "content = $content"; + * } + * add_shortcode( 'baztag', 'baztag_func' ); + * + * @since 2.5.0 + * + * @uses $shortcode_tags + * + * @param string $tag Shortcode tag to be searched in post content. + * @param callable $func Hook to run when shortcode is found. + */ function add_shortcode($tag, $func) { global $shortcode_tags; @@ -54,51 +93,212 @@ function add_shortcode($tag, $func) { $shortcode_tags[$tag] = $func; } +/** + * Removes hook for shortcode. + * + * @since 2.5.0 + * + * @uses $shortcode_tags + * + * @param string $tag shortcode tag to remove hook for. + */ function remove_shortcode($tag) { global $shortcode_tags; unset($shortcode_tags[$tag]); } +/** + * Clear all shortcodes. + * + * This function is simple, it clears all of the shortcode tags by replacing the + * shortcodes global by a empty array. This is actually a very efficient method + * for removing all shortcodes. + * + * @since 2.5.0 + * + * @uses $shortcode_tags + */ function remove_all_shortcodes() { global $shortcode_tags; $shortcode_tags = array(); } +/** + * Whether a registered shortcode exists named $tag + * + * @since 3.6.0 + * + * @global array $shortcode_tags + * @param string $tag + * @return boolean + */ +function shortcode_exists( $tag ) { + global $shortcode_tags; + return array_key_exists( $tag, $shortcode_tags ); +} + +/** + * Whether the passed content contains the specified shortcode + * + * @since 3.6.0 + * + * @global array $shortcode_tags + * @param string $tag + * @return boolean + */ +function has_shortcode( $content, $tag ) { + if ( false === strpos( $content, '[' ) ) { + return false; + } + + if ( shortcode_exists( $tag ) ) { + preg_match_all( '/' . get_shortcode_regex() . '/s', $content, $matches, PREG_SET_ORDER ); + if ( empty( $matches ) ) + return false; + + foreach ( $matches as $shortcode ) { + if ( $tag === $shortcode[2] ) { + return true; + } elseif ( ! empty( $shortcode[5] ) && has_shortcode( $shortcode[5], $tag ) ) { + return true; + } + } + } + return false; +} + +/** + * Search content for shortcodes and filter shortcodes through their hooks. + * + * If there are no shortcode tags defined, then the content will be returned + * without any filtering. This might cause issues when plugins are disabled but + * the shortcode will still show up in the post or content. + * + * @since 2.5.0 + * + * @uses $shortcode_tags + * + * @param string $content Content to search for shortcodes + * @return string Content with shortcodes filtered out. + */ function do_shortcode($content) { global $shortcode_tags; + if ( false === strpos( $content, '[' ) ) { + return $content; + } + if (empty($shortcode_tags) || !is_array($shortcode_tags)) return $content; $pattern = get_shortcode_regex(); - return preg_replace_callback('/'.$pattern.'/s', 'do_shortcode_tag', $content); + return preg_replace_callback( "/$pattern/s", 'do_shortcode_tag', $content ); } +/** + * Retrieve the shortcode regular expression for searching. + * + * The regular expression combines the shortcode tags in the regular expression + * in a regex class. + * + * The regular expression contains 6 different sub matches to help with parsing. + * + * 1 - An extra [ to allow for escaping shortcodes with double [[]] + * 2 - The shortcode name + * 3 - The shortcode argument list + * 4 - The self closing / + * 5 - The content of a shortcode when it wraps some content. + * 6 - An extra ] to allow for escaping shortcodes with double [[]] + * + * @since 2.5.0 + * + * @uses $shortcode_tags + * + * @return string The shortcode search regular expression + */ function get_shortcode_regex() { global $shortcode_tags; $tagnames = array_keys($shortcode_tags); $tagregexp = join( '|', array_map('preg_quote', $tagnames) ); - return '\[('.$tagregexp.')\b(.*?)(?:(\/))?\](?:(.+?)\[\/\1\])?'; + // WARNING! Do not change this regex without changing do_shortcode_tag() and strip_shortcode_tag() + // Also, see shortcode_unautop() and shortcode.js. + return + '\\[' // Opening bracket + . '(\\[?)' // 1: Optional second opening bracket for escaping shortcodes: [[tag]] + . "($tagregexp)" // 2: Shortcode name + . '(?![\\w-])' // Not followed by word character or hyphen + . '(' // 3: Unroll the loop: Inside the opening shortcode tag + . '[^\\]\\/]*' // Not a closing bracket or forward slash + . '(?:' + . '\\/(?!\\])' // A forward slash not followed by a closing bracket + . '[^\\]\\/]*' // Not a closing bracket or forward slash + . ')*?' + . ')' + . '(?:' + . '(\\/)' // 4: Self closing tag ... + . '\\]' // ... and closing bracket + . '|' + . '\\]' // Closing bracket + . '(?:' + . '(' // 5: Unroll the loop: Optionally, anything between the opening and closing shortcode tags + . '[^\\[]*+' // Not an opening bracket + . '(?:' + . '\\[(?!\\/\\2\\])' // An opening bracket not followed by the closing shortcode tag + . '[^\\[]*+' // Not an opening bracket + . ')*+' + . ')' + . '\\[\\/\\2\\]' // Closing shortcode tag + . ')?' + . ')' + . '(\\]?)'; // 6: Optional second closing brocket for escaping shortcodes: [[tag]] } -function do_shortcode_tag($m) { +/** + * Regular Expression callable for do_shortcode() for calling shortcode hook. + * @see get_shortcode_regex for details of the match array contents. + * + * @since 2.5.0 + * @access private + * @uses $shortcode_tags + * + * @param array $m Regular expression match array + * @return mixed False on failure. + */ +function do_shortcode_tag( $m ) { global $shortcode_tags; - $tag = $m[1]; - $attr = shortcode_parse_atts($m[2]); + // allow [[foo]] syntax for escaping a tag + if ( $m[1] == '[' && $m[6] == ']' ) { + return substr($m[0], 1, -1); + } + + $tag = $m[2]; + $attr = shortcode_parse_atts( $m[3] ); - if ( isset($m[4]) ) { + if ( isset( $m[5] ) ) { // enclosing tag - extra parameter - return call_user_func($shortcode_tags[$tag], $attr, $m[4]); + return $m[1] . call_user_func( $shortcode_tags[$tag], $attr, $m[5], $tag ) . $m[6]; } else { // self-closing tag - return call_user_func($shortcode_tags[$tag], $attr); + return $m[1] . call_user_func( $shortcode_tags[$tag], $attr, null, $tag ) . $m[6]; } } +/** + * Retrieve all attributes from the shortcodes tag. + * + * The attributes list has the attribute name as the key and the value of the + * attribute as the value in the key/value pair. This allows for easier + * retrieval of the attributes, since all attributes have to be known. + * + * @since 2.5.0 + * + * @param string $text + * @return array List of attributes and their value. + */ function shortcode_parse_atts($text) { $atts = array(); $pattern = '/(\w+)\s*=\s*"([^"]*)"(?:\s|$)|(\w+)\s*=\s*\'([^\']*)\'(?:\s|$)|(\w+)\s*=\s*([^\s\'"]+)(?:\s|$)|"([^"]*)"(?:\s|$)|(\S+)(?:\s|$)/'; @@ -122,7 +322,24 @@ function shortcode_parse_atts($text) { return $atts; } -function shortcode_atts($pairs, $atts) { +/** + * Combine user attributes with known attributes and fill in defaults when needed. + * + * The pairs should be considered to be all of the attributes which are + * supported by the caller and given as a list. The returned attributes will + * only contain the attributes in the $pairs list. + * + * If the $atts list has unsupported attributes, then they will be ignored and + * removed from the final returned list. + * + * @since 2.5.0 + * + * @param array $pairs Entire list of supported attributes and their defaults. + * @param array $atts User defined attributes in shortcode tag. + * @param string $shortcode Optional. The name of the shortcode, provided for context to enable filtering + * @return array Combined and filtered attribute list. + */ +function shortcode_atts( $pairs, $atts, $shortcode = '' ) { $atts = (array)$atts; $out = array(); foreach($pairs as $name => $default) { @@ -131,9 +348,56 @@ function shortcode_atts($pairs, $atts) { else $out[$name] = $default; } + /** + * Filter a shortcode's default attributes. + * + * If the third parameter of the shortcode_atts() function is present then this filter is available. + * The third parameter, $shortcode, is the name of the shortcode. + * + * @since 3.6.0 + * + * @param array $out The output array of shortcode attributes. + * @param array $pairs The supported attributes and their defaults. + * @param array $atts The user defined shortcode attributes. + */ + if ( $shortcode ) + $out = apply_filters( "shortcode_atts_{$shortcode}", $out, $pairs, $atts ); + return $out; } -add_filter('the_content', 'do_shortcode', 11); // AFTER wpautop() +/** + * Remove all shortcode tags from the given content. + * + * @since 2.5.0 + * + * @uses $shortcode_tags + * + * @param string $content Content to remove shortcode tags. + * @return string Content without shortcode tags. + */ +function strip_shortcodes( $content ) { + global $shortcode_tags; + + if ( false === strpos( $content, '[' ) ) { + return $content; + } + + if (empty($shortcode_tags) || !is_array($shortcode_tags)) + return $content; + + $pattern = get_shortcode_regex(); + + return preg_replace_callback( "/$pattern/s", 'strip_shortcode_tag', $content ); +} + +function strip_shortcode_tag( $m ) { + // allow [[foo]] syntax for escaping a tag + if ( $m[1] == '[' && $m[6] == ']' ) { + return substr($m[0], 1, -1); + } + + return $m[1] . $m[6]; +} -?> +add_filter('the_content', 'do_shortcode', 11); // AFTER wpautop()