css
12 years ago
js
11 years ago
capabilities.php
13 years ago
contact-form-template.php
11 years ago
contact-form.php
11 years ago
controller.php
11 years ago
formatting.php
12 years ago
functions.php
11 years ago
mail.php
11 years ago
pipe.php
12 years ago
shortcodes.php
12 years ago
submission.php
11 years ago
upgrade.php
11 years ago
shortcodes.php
420 lines
| 1 | <?php |
| 2 | |
| 3 | class WPCF7_ShortcodeManager { |
| 4 | |
| 5 | private static $instance; |
| 6 | |
| 7 | private $shortcode_tags = array(); |
| 8 | |
| 9 | // Taggs scanned at the last time of do_shortcode() |
| 10 | private $scanned_tags = null; |
| 11 | |
| 12 | // Executing shortcodes (true) or just scanning (false) |
| 13 | private $exec = true; |
| 14 | |
| 15 | private function __construct() {} |
| 16 | |
| 17 | public static function get_instance() { |
| 18 | if ( empty( self::$instance ) ) { |
| 19 | self::$instance = new self; |
| 20 | } |
| 21 | |
| 22 | return self::$instance; |
| 23 | } |
| 24 | |
| 25 | public function get_scanned_tags() { |
| 26 | return $this->scanned_tags; |
| 27 | } |
| 28 | |
| 29 | public function add_shortcode( $tag, $func, $has_name = false ) { |
| 30 | if ( ! is_callable( $func ) ) |
| 31 | return; |
| 32 | |
| 33 | $tags = array_filter( array_unique( (array) $tag ) ); |
| 34 | |
| 35 | foreach ( $tags as $tag ) { |
| 36 | $this->shortcode_tags[$tag] = array( |
| 37 | 'function' => $func, |
| 38 | 'has_name' => (boolean) $has_name ); |
| 39 | } |
| 40 | } |
| 41 | |
| 42 | public function remove_shortcode( $tag ) { |
| 43 | unset( $this->shortcode_tags[$tag] ); |
| 44 | } |
| 45 | |
| 46 | public function normalize_shortcode( $content ) { |
| 47 | if ( empty( $this->shortcode_tags ) || ! is_array( $this->shortcode_tags ) ) |
| 48 | return $content; |
| 49 | |
| 50 | $pattern = $this->get_shortcode_regex(); |
| 51 | return preg_replace_callback( '/' . $pattern . '/s', |
| 52 | array( $this, 'normalize_space_cb' ), $content ); |
| 53 | } |
| 54 | |
| 55 | private function normalize_space_cb( $m ) { |
| 56 | // allow [[foo]] syntax for escaping a tag |
| 57 | if ( $m[1] == '[' && $m[6] == ']' ) |
| 58 | return $m[0]; |
| 59 | |
| 60 | $tag = $m[2]; |
| 61 | $attr = trim( preg_replace( '/[\r\n\t ]+/', ' ', $m[3] ) ); |
| 62 | $content = trim( $m[5] ); |
| 63 | |
| 64 | $content = str_replace( "\n", '<WPPreserveNewline />', $content ); |
| 65 | |
| 66 | $result = $m[1] . '[' . $tag |
| 67 | . ( $attr ? ' ' . $attr : '' ) |
| 68 | . ( $m[4] ? ' ' . $m[4] : '' ) |
| 69 | . ']' |
| 70 | . ( $content ? $content . '[/' . $tag . ']' : '' ) |
| 71 | . $m[6]; |
| 72 | |
| 73 | return $result; |
| 74 | } |
| 75 | |
| 76 | public function do_shortcode( $content, $exec = true ) { |
| 77 | $this->exec = (bool) $exec; |
| 78 | $this->scanned_tags = array(); |
| 79 | |
| 80 | if ( empty( $this->shortcode_tags ) || ! is_array( $this->shortcode_tags ) ) |
| 81 | return $content; |
| 82 | |
| 83 | $pattern = $this->get_shortcode_regex(); |
| 84 | return preg_replace_callback( '/' . $pattern . '/s', |
| 85 | array( $this, 'do_shortcode_tag' ), $content ); |
| 86 | } |
| 87 | |
| 88 | public function scan_shortcode( $content ) { |
| 89 | $this->do_shortcode( $content, false ); |
| 90 | return $this->scanned_tags; |
| 91 | } |
| 92 | |
| 93 | private function get_shortcode_regex() { |
| 94 | $tagnames = array_keys( $this->shortcode_tags ); |
| 95 | $tagregexp = join( '|', array_map( 'preg_quote', $tagnames ) ); |
| 96 | |
| 97 | return '(\[?)' |
| 98 | . '\[(' . $tagregexp . ')(?:[\r\n\t ](.*?))?(?:[\r\n\t ](\/))?\]' |
| 99 | . '(?:([^[]*?)\[\/\2\])?' |
| 100 | . '(\]?)'; |
| 101 | } |
| 102 | |
| 103 | private function do_shortcode_tag( $m ) { |
| 104 | // allow [[foo]] syntax for escaping a tag |
| 105 | if ( $m[1] == '[' && $m[6] == ']' ) { |
| 106 | return substr( $m[0], 1, -1 ); |
| 107 | } |
| 108 | |
| 109 | $tag = $m[2]; |
| 110 | $attr = $this->shortcode_parse_atts( $m[3] ); |
| 111 | |
| 112 | $scanned_tag = array( |
| 113 | 'type' => $tag, |
| 114 | 'basetype' => trim( $tag, '*' ), |
| 115 | 'name' => '', |
| 116 | 'options' => array(), |
| 117 | 'raw_values' => array(), |
| 118 | 'values' => array(), |
| 119 | 'pipes' => null, |
| 120 | 'labels' => array(), |
| 121 | 'attr' => '', |
| 122 | 'content' => '' ); |
| 123 | |
| 124 | if ( is_array( $attr ) ) { |
| 125 | if ( is_array( $attr['options'] ) ) { |
| 126 | if ( $this->shortcode_tags[$tag]['has_name'] && ! empty( $attr['options'] ) ) { |
| 127 | $scanned_tag['name'] = array_shift( $attr['options'] ); |
| 128 | |
| 129 | if ( ! wpcf7_is_name( $scanned_tag['name'] ) ) |
| 130 | return $m[0]; // Invalid name is used. Ignore this tag. |
| 131 | } |
| 132 | |
| 133 | $scanned_tag['options'] = (array) $attr['options']; |
| 134 | } |
| 135 | |
| 136 | $scanned_tag['raw_values'] = (array) $attr['values']; |
| 137 | |
| 138 | if ( WPCF7_USE_PIPE ) { |
| 139 | $pipes = new WPCF7_Pipes( $scanned_tag['raw_values'] ); |
| 140 | $scanned_tag['values'] = $pipes->collect_befores(); |
| 141 | $scanned_tag['pipes'] = $pipes; |
| 142 | } else { |
| 143 | $scanned_tag['values'] = $scanned_tag['raw_values']; |
| 144 | } |
| 145 | |
| 146 | $scanned_tag['labels'] = $scanned_tag['values']; |
| 147 | |
| 148 | } else { |
| 149 | $scanned_tag['attr'] = $attr; |
| 150 | } |
| 151 | |
| 152 | $scanned_tag['values'] = array_map( 'trim', $scanned_tag['values'] ); |
| 153 | $scanned_tag['labels'] = array_map( 'trim', $scanned_tag['labels'] ); |
| 154 | |
| 155 | $content = trim( $m[5] ); |
| 156 | $content = preg_replace( "/<br[\r\n\t ]*\/?>$/m", '', $content ); |
| 157 | $scanned_tag['content'] = $content; |
| 158 | |
| 159 | $scanned_tag = apply_filters( 'wpcf7_form_tag', $scanned_tag, $this->exec ); |
| 160 | |
| 161 | $this->scanned_tags[] = $scanned_tag; |
| 162 | |
| 163 | if ( $this->exec ) { |
| 164 | $func = $this->shortcode_tags[$tag]['function']; |
| 165 | return $m[1] . call_user_func( $func, $scanned_tag ) . $m[6]; |
| 166 | } else { |
| 167 | return $m[0]; |
| 168 | } |
| 169 | } |
| 170 | |
| 171 | private function shortcode_parse_atts( $text ) { |
| 172 | $atts = array( 'options' => array(), 'values' => array() ); |
| 173 | $text = preg_replace( "/[\x{00a0}\x{200b}]+/u", " ", $text ); |
| 174 | $text = stripcslashes( trim( $text ) ); |
| 175 | |
| 176 | $pattern = '%^([-+*=0-9a-zA-Z:.!?#$&@_/|\%\r\n\t ]*?)((?:[\r\n\t ]*"[^"]*"|[\r\n\t ]*\'[^\']*\')*)$%'; |
| 177 | |
| 178 | if ( preg_match( $pattern, $text, $match ) ) { |
| 179 | if ( ! empty( $match[1] ) ) { |
| 180 | $atts['options'] = preg_split( '/[\r\n\t ]+/', trim( $match[1] ) ); |
| 181 | } |
| 182 | if ( ! empty( $match[2] ) ) { |
| 183 | preg_match_all( '/"[^"]*"|\'[^\']*\'/', $match[2], $matched_values ); |
| 184 | $atts['values'] = wpcf7_strip_quote_deep( $matched_values[0] ); |
| 185 | } |
| 186 | } else { |
| 187 | $atts = $text; |
| 188 | } |
| 189 | |
| 190 | return $atts; |
| 191 | } |
| 192 | |
| 193 | } |
| 194 | |
| 195 | function wpcf7_add_shortcode( $tag, $func, $has_name = false ) { |
| 196 | $manager = WPCF7_ShortcodeManager::get_instance(); |
| 197 | |
| 198 | return $manager->add_shortcode( $tag, $func, $has_name ); |
| 199 | } |
| 200 | |
| 201 | function wpcf7_remove_shortcode( $tag ) { |
| 202 | $manager = WPCF7_ShortcodeManager::get_instance(); |
| 203 | |
| 204 | return $manager->remove_shortcode( $tag ); |
| 205 | } |
| 206 | |
| 207 | function wpcf7_do_shortcode( $content ) { |
| 208 | $manager = WPCF7_ShortcodeManager::get_instance(); |
| 209 | |
| 210 | return $manager->do_shortcode( $content ); |
| 211 | } |
| 212 | |
| 213 | class WPCF7_Shortcode { |
| 214 | |
| 215 | public $type; |
| 216 | public $basetype; |
| 217 | public $name = ''; |
| 218 | public $options = array(); |
| 219 | public $raw_values = array(); |
| 220 | public $values = array(); |
| 221 | public $pipes; |
| 222 | public $labels = array(); |
| 223 | public $attr = ''; |
| 224 | public $content = ''; |
| 225 | |
| 226 | public function __construct( $tag ) { |
| 227 | foreach ( $tag as $key => $value ) { |
| 228 | if ( property_exists( __CLASS__, $key ) ) |
| 229 | $this->{$key} = $value; |
| 230 | } |
| 231 | } |
| 232 | |
| 233 | public function is_required() { |
| 234 | return ( '*' == substr( $this->type, -1 ) ); |
| 235 | } |
| 236 | |
| 237 | public function has_option( $opt ) { |
| 238 | $pattern = sprintf( '/^%s(:.+)?$/i', preg_quote( $opt, '/' ) ); |
| 239 | return (bool) preg_grep( $pattern, $this->options ); |
| 240 | } |
| 241 | |
| 242 | public function get_option( $opt, $pattern = '', $single = false ) { |
| 243 | $preset_patterns = array( |
| 244 | 'date' => '([0-9]{4}-[0-9]{2}-[0-9]{2}|today(.*))', |
| 245 | 'int' => '[0-9]+', |
| 246 | 'signed_int' => '-?[0-9]+', |
| 247 | 'class' => '[-0-9a-zA-Z_]+', |
| 248 | 'id' => '[-0-9a-zA-Z_]+' ); |
| 249 | |
| 250 | if ( isset( $preset_patterns[$pattern] ) ) |
| 251 | $pattern = $preset_patterns[$pattern]; |
| 252 | |
| 253 | if ( '' == $pattern ) |
| 254 | $pattern = '.+'; |
| 255 | |
| 256 | $pattern = sprintf( '/^%s:%s$/i', preg_quote( $opt, '/' ), $pattern ); |
| 257 | |
| 258 | if ( $single ) { |
| 259 | $matches = $this->get_first_match_option( $pattern ); |
| 260 | |
| 261 | if ( ! $matches ) |
| 262 | return false; |
| 263 | |
| 264 | return substr( $matches[0], strlen( $opt ) + 1 ); |
| 265 | } else { |
| 266 | $matches_a = $this->get_all_match_options( $pattern ); |
| 267 | |
| 268 | if ( ! $matches_a ) |
| 269 | return false; |
| 270 | |
| 271 | $results = array(); |
| 272 | |
| 273 | foreach ( $matches_a as $matches ) |
| 274 | $results[] = substr( $matches[0], strlen( $opt ) + 1 ); |
| 275 | |
| 276 | return $results; |
| 277 | } |
| 278 | } |
| 279 | |
| 280 | public function get_id_option() { |
| 281 | return $this->get_option( 'id', 'id', true ); |
| 282 | } |
| 283 | |
| 284 | public function get_class_option( $default = '' ) { |
| 285 | if ( is_string( $default ) ) |
| 286 | $default = explode( ' ', $default ); |
| 287 | |
| 288 | $options = array_merge( |
| 289 | (array) $default, |
| 290 | (array) $this->get_option( 'class', 'class' ) ); |
| 291 | |
| 292 | $options = array_filter( array_unique( $options ) ); |
| 293 | |
| 294 | return implode( ' ', $options ); |
| 295 | } |
| 296 | |
| 297 | public function get_size_option( $default = '' ) { |
| 298 | $matches_a = $this->get_all_match_options( '%^([0-9]*)/[0-9]*$%' ); |
| 299 | |
| 300 | foreach ( (array) $matches_a as $matches ) { |
| 301 | if ( isset( $matches[1] ) && '' !== $matches[1] ) |
| 302 | return $matches[1]; |
| 303 | } |
| 304 | |
| 305 | return $default; |
| 306 | } |
| 307 | |
| 308 | public function get_maxlength_option( $default = '' ) { |
| 309 | $matches_a = $this->get_all_match_options( |
| 310 | '%^(?:[0-9]*x?[0-9]*)?/([0-9]+)$%' ); |
| 311 | |
| 312 | foreach ( (array) $matches_a as $matches ) { |
| 313 | if ( isset( $matches[1] ) && '' !== $matches[1] ) |
| 314 | return $matches[1]; |
| 315 | } |
| 316 | |
| 317 | return $default; |
| 318 | } |
| 319 | |
| 320 | public function get_cols_option( $default = '' ) { |
| 321 | $matches_a = $this->get_all_match_options( |
| 322 | '%^([0-9]*)x([0-9]*)(?:/[0-9]+)?$%' ); |
| 323 | |
| 324 | foreach ( (array) $matches_a as $matches ) { |
| 325 | if ( isset( $matches[1] ) && '' !== $matches[1] ) |
| 326 | return $matches[1]; |
| 327 | } |
| 328 | |
| 329 | return $default; |
| 330 | } |
| 331 | |
| 332 | public function get_rows_option( $default = '' ) { |
| 333 | $matches_a = $this->get_all_match_options( |
| 334 | '%^([0-9]*)x([0-9]*)(?:/[0-9]+)?$%' ); |
| 335 | |
| 336 | foreach ( (array) $matches_a as $matches ) { |
| 337 | if ( isset( $matches[2] ) && '' !== $matches[2] ) |
| 338 | return $matches[2]; |
| 339 | } |
| 340 | |
| 341 | return $default; |
| 342 | } |
| 343 | |
| 344 | public function get_date_option( $opt ) { |
| 345 | $option = $this->get_option( $opt, 'date', true ); |
| 346 | |
| 347 | if ( preg_match( '/^[0-9]{4}-[0-9]{2}-[0-9]{2}$/', $option ) ) { |
| 348 | return $option; |
| 349 | } |
| 350 | |
| 351 | if ( preg_match( '/^today(?:([+-][0-9]+)([a-z]*))?/', $option, $matches ) ) { |
| 352 | $number = isset( $matches[1] ) ? (int) $matches[1] : 0; |
| 353 | $unit = isset( $matches[2] ) ? $matches[2] : ''; |
| 354 | |
| 355 | if ( ! preg_match( '/^(day|month|year|week)s?$/', $unit ) ) { |
| 356 | $unit = 'days'; |
| 357 | } |
| 358 | |
| 359 | $date = gmdate( 'Y-m-d', |
| 360 | strtotime( sprintf( 'today %1$s %2$s', $number, $unit ) ) ); |
| 361 | return $date; |
| 362 | } |
| 363 | |
| 364 | return false; |
| 365 | } |
| 366 | |
| 367 | public function get_default_option() { |
| 368 | $options = (array) $this->get_option( 'default' ); |
| 369 | |
| 370 | if ( empty( $options ) ) { |
| 371 | return false; |
| 372 | } |
| 373 | |
| 374 | foreach ( $options as $opt ) { |
| 375 | $opt = sanitize_key( $opt ); |
| 376 | |
| 377 | if ( 'user_' == substr( $opt, 0, 5 ) && is_user_logged_in() ) { |
| 378 | $primary_props = array( 'user_login', 'user_email', 'user_url' ); |
| 379 | $opt = in_array( $opt, $primary_props ) ? $opt : substr( $opt, 5 ); |
| 380 | |
| 381 | $user = wp_get_current_user(); |
| 382 | $user_prop = $user->get( $opt ); |
| 383 | |
| 384 | if ( ! empty( $user_prop ) ) { |
| 385 | return $user_prop; |
| 386 | } |
| 387 | } |
| 388 | } |
| 389 | |
| 390 | return false; |
| 391 | } |
| 392 | |
| 393 | public function get_data_option( $args = '' ) { |
| 394 | $options = (array) $this->get_option( 'data' ); |
| 395 | |
| 396 | return apply_filters( 'wpcf7_form_tag_data_option', null, $options, $args ); |
| 397 | } |
| 398 | |
| 399 | public function get_first_match_option( $pattern ) { |
| 400 | foreach( (array) $this->options as $option ) { |
| 401 | if ( preg_match( $pattern, $option, $matches ) ) |
| 402 | return $matches; |
| 403 | } |
| 404 | |
| 405 | return false; |
| 406 | } |
| 407 | |
| 408 | public function get_all_match_options( $pattern ) { |
| 409 | $result = array(); |
| 410 | |
| 411 | foreach( (array) $this->options as $option ) { |
| 412 | if ( preg_match( $pattern, $option, $matches ) ) |
| 413 | $result[] = $matches; |
| 414 | } |
| 415 | |
| 416 | return $result; |
| 417 | } |
| 418 | } |
| 419 | |
| 420 | ?> |