PageRenderTime 21ms CodeModel.GetById 7ms RepoModel.GetById 1ms app.codeStats 0ms

/data/plugins/contact-form/includes/shortcodes.php

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