PageRenderTime 45ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/wp-content/plugins/bbpress/includes/common/formatting.php

https://bitbucket.org/Thane2376/death-edge.ru
PHP | 382 lines | 240 code | 43 blank | 99 comment | 15 complexity | 7e4440adbada14e8e681d019bf0205fd MD5 | raw file
Possible License(s): LGPL-2.1, GPL-2.0, LGPL-3.0, AGPL-1.0
  1. <?php
  2. /**
  3. * bbPress Formatting
  4. *
  5. * @package bbPress
  6. * @subpackage Formatting
  7. */
  8. // Exit if accessed directly
  9. if ( !defined( 'ABSPATH' ) ) exit;
  10. /** Kses **********************************************************************/
  11. /**
  12. * Custom allowed tags for forum topics and replies
  13. *
  14. * Allows all users to post links, quotes, code, formatting, lists, and images
  15. *
  16. * @since bbPress (r4603)
  17. *
  18. * @return array Associative array of allowed tags and attributes
  19. */
  20. function bbp_kses_allowed_tags() {
  21. return apply_filters( 'bbp_kses_allowed_tags', array(
  22. // Links
  23. 'a' => array(
  24. 'href' => array(),
  25. 'title' => array(),
  26. 'rel' => array(),
  27. 'target' => array()
  28. ),
  29. // Quotes
  30. 'blockquote' => array(
  31. 'cite' => array()
  32. ),
  33. // Code
  34. 'code' => array(),
  35. 'pre' => array(),
  36. // Formatting
  37. 'em' => array(),
  38. 'strong' => array(),
  39. 'del' => array(
  40. 'datetime' => true,
  41. ),
  42. // Lists
  43. 'ul' => array(),
  44. 'ol' => array(
  45. 'start' => true,
  46. ),
  47. 'li' => array(),
  48. // Images
  49. 'img' => array(
  50. 'src' => true,
  51. 'border' => true,
  52. 'alt' => true,
  53. 'height' => true,
  54. 'width' => true,
  55. )
  56. ) );
  57. }
  58. /**
  59. * Custom kses filter for forum topics and replies, for filtering incoming data
  60. *
  61. * @since bbPress (r4603)
  62. *
  63. * @param string $data Content to filter, expected to be escaped with slashes
  64. * @return string Filtered content
  65. */
  66. function bbp_filter_kses( $data = '' ) {
  67. return addslashes( wp_kses( stripslashes( $data ), bbp_kses_allowed_tags() ) );
  68. }
  69. /**
  70. * Custom kses filter for forum topics and replies, for raw data
  71. *
  72. * @since bbPress (r4603)
  73. *
  74. * @param string $data Content to filter, expected to not be escaped
  75. * @return string Filtered content
  76. */
  77. function bbp_kses_data( $data = '' ) {
  78. return wp_kses( $data , bbp_kses_allowed_tags() );
  79. }
  80. /** Formatting ****************************************************************/
  81. /**
  82. * Filter the topic or reply content and output code and pre tags
  83. *
  84. * @since bbPress (r4641)
  85. *
  86. * @param string $content Topic and reply content
  87. * @return string Partially encodedd content
  88. */
  89. function bbp_code_trick( $content = '' ) {
  90. $content = str_replace( array( "\r\n", "\r" ), "\n", $content );
  91. $content = preg_replace_callback( "|(`)(.*?)`|", 'bbp_encode_callback', $content );
  92. $content = preg_replace_callback( "!(^|\n)`(.*?)`!s", 'bbp_encode_callback', $content );
  93. return $content;
  94. }
  95. /**
  96. * When editing a topic or reply, reverse the code trick so the textarea
  97. * contains the correct editable content.
  98. *
  99. * @since bbPress (r4641)
  100. *
  101. * @param string $content Topic and reply content
  102. * @return string Partially encodedd content
  103. */
  104. function bbp_code_trick_reverse( $content = '' ) {
  105. // Setup variables
  106. $openers = array( '<p>', '<br />' );
  107. $content = preg_replace_callback( "!(<pre><code>|<code>)(.*?)(</code></pre>|</code>)!s", 'bbp_decode_callback', $content );
  108. // Do the do
  109. $content = str_replace( $openers, '', $content );
  110. $content = str_replace( '</p>', "\n", $content );
  111. $content = str_replace( '<coded_br />', '<br />', $content );
  112. $content = str_replace( '<coded_p>', '<p>', $content );
  113. $content = str_replace( '</coded_p>', '</p>', $content );
  114. return $content;
  115. }
  116. /**
  117. * Filter the content and encode any bad HTML tags
  118. *
  119. * @since bbPress (r4641)
  120. *
  121. * @param string $content Topic and reply content
  122. * @return string Partially encodedd content
  123. */
  124. function bbp_encode_bad( $content = '' ) {
  125. // Setup variables
  126. $content = _wp_specialchars( $content, ENT_NOQUOTES );
  127. $content = preg_split( '@(`[^`]*`)@m', $content, -1, PREG_SPLIT_NO_EMPTY + PREG_SPLIT_DELIM_CAPTURE );
  128. $allowed = bbp_kses_allowed_tags();
  129. $empty = array(
  130. 'br' => true,
  131. 'hr' => true,
  132. 'img' => true,
  133. 'input' => true,
  134. 'param' => true,
  135. 'area' => true,
  136. 'col' => true,
  137. 'embed' => true
  138. );
  139. // Loop through allowed tags and compare for empty and normal tags
  140. foreach ( $allowed as $tag => $args ) {
  141. $preg = $args ? "{$tag}(?:\s.*?)?" : $tag;
  142. // Which walker to use based on the tag and arguments
  143. if ( isset( $empty[$tag] ) ) {
  144. array_walk( $content, 'bbp_encode_empty_callback', $preg );
  145. } else {
  146. array_walk( $content, 'bbp_encode_normal_callback', $preg );
  147. }
  148. }
  149. // Return the joined content array
  150. return implode( '', $content );
  151. }
  152. /** Code Callbacks ************************************************************/
  153. /**
  154. * Callback to encode the tags in topic or reply content
  155. *
  156. * @since bbPress (r4641)
  157. *
  158. * @param array $matches
  159. * @return string
  160. */
  161. function bbp_encode_callback( $matches = array() ) {
  162. // Trim inline code, not pre blocks (to prevent removing indentation)
  163. if ( "`" === $matches[1] ) {
  164. $content = trim( $matches[2] );
  165. } else {
  166. $content = $matches[2];
  167. }
  168. // Do some replacing
  169. $content = htmlspecialchars( $content, ENT_QUOTES );
  170. $content = str_replace( array( "\r\n", "\r" ), "\n", $content );
  171. $content = preg_replace( "|\n\n\n+|", "\n\n", $content );
  172. $content = str_replace( '&amp;amp;', '&amp;', $content );
  173. $content = str_replace( '&amp;lt;', '&lt;', $content );
  174. $content = str_replace( '&amp;gt;', '&gt;', $content );
  175. // Wrap in code tags
  176. $content = '<code>' . $content . '</code>';
  177. // Wrap blocks in pre tags
  178. if ( "`" !== $matches[1] ) {
  179. $content = "\n<pre>" . $content . "</pre>\n";
  180. }
  181. return $content;
  182. }
  183. /**
  184. * Callback to decode the tags in topic or reply content
  185. *
  186. * @since bbPress (r4641)
  187. *
  188. * @param array $matches
  189. * @todo Experiment with _wp_specialchars()
  190. * @return string
  191. */
  192. function bbp_decode_callback( $matches = array() ) {
  193. // Setup variables
  194. $trans_table = array_flip( get_html_translation_table( HTML_ENTITIES ) );
  195. $amps = array( '&#38;','&#038;', '&amp;' );
  196. $single = array( '&#39;','&#039;' );
  197. $content = $matches[2];
  198. $content = strtr( $content, $trans_table );
  199. // Do the do
  200. $content = str_replace( '<br />', '<coded_br />', $content );
  201. $content = str_replace( '<p>', '<coded_p>', $content );
  202. $content = str_replace( '</p>', '</coded_p>', $content );
  203. $content = str_replace( $amps, '&', $content );
  204. $content = str_replace( $single, "'", $content );
  205. // Return content wrapped in code tags
  206. return '`' . $content . '`';
  207. }
  208. /**
  209. * Callback to replace empty HTML tags in a content string
  210. *
  211. * @since bbPress (r4641)
  212. *
  213. * @internal Used by bbp_encode_bad()
  214. * @param string $content
  215. * @param string $key Not used
  216. * @param string $preg
  217. */
  218. function bbp_encode_empty_callback( &$content = '', $key = '', $preg = '' ) {
  219. if ( strpos( $content, '`' ) !== 0 ) {
  220. $content = preg_replace( "|&lt;({$preg})\s*?/*?&gt;|i", '<$1 />', $content );
  221. }
  222. }
  223. /**
  224. * Callback to replace normal HTML tags in a content string
  225. *
  226. * @since bbPress (r4641)
  227. *
  228. * @internal Used by bbp_encode_bad()
  229. * @param type $content
  230. * @param type $key
  231. * @param type $preg
  232. */
  233. function bbp_encode_normal_callback( &$content = '', $key = '', $preg = '') {
  234. if ( strpos( $content, '`' ) !== 0 ) {
  235. $content = preg_replace( "|&lt;(/?{$preg})&gt;|i", '<$1>', $content );
  236. }
  237. }
  238. /** No Follow *****************************************************************/
  239. /**
  240. * Catches links so rel=nofollow can be added (on output, not save)
  241. *
  242. * @since bbPress (r4865)
  243. * @param string $text Post text
  244. * @return string $text Text with rel=nofollow added to any links
  245. */
  246. function bbp_rel_nofollow( $text = '' ) {
  247. return preg_replace_callback( '|<a (.+?)>|i', 'bbp_rel_nofollow_callback', $text );
  248. }
  249. /**
  250. * Adds rel=nofollow to a link
  251. *
  252. * @since bbPress (r4865)
  253. * @param array $matches
  254. * @return string $text Link with rel=nofollow added
  255. */
  256. function bbp_rel_nofollow_callback( $matches = array() ) {
  257. $text = $matches[1];
  258. $text = str_replace( array( ' rel="nofollow"', " rel='nofollow'" ), '', $text );
  259. return "<a $text rel=\"nofollow\">";
  260. }
  261. /** Make Clickable ************************************************************/
  262. /**
  263. * Convert plaintext URI to HTML links.
  264. *
  265. * Converts URI, www and ftp, and email addresses. Finishes by fixing links
  266. * within links.
  267. *
  268. * This custom version of WordPress's make_clickable() skips links inside of
  269. * pre and code tags.
  270. *
  271. * @since bbPress (r4941)
  272. *
  273. * @param string $text Content to convert URIs.
  274. * @return string Content with converted URIs.
  275. */
  276. function bbp_make_clickable( $text ) {
  277. $r = '';
  278. $in_code = false;
  279. $textarr = preg_split( '/(<[^<>]+>)/', $text, -1, PREG_SPLIT_DELIM_CAPTURE ); // split out HTML tags
  280. foreach ( $textarr as $piece ) {
  281. switch ( $piece ) {
  282. case '<code>' :
  283. case '<pre>' :
  284. $in_code = true;
  285. break;
  286. case '</code>' :
  287. case '</pre>' :
  288. $in_code = false;
  289. break;
  290. }
  291. if ( $in_code || empty( $piece ) || ( $piece[0] === '<' && ! preg_match('|^<\s*[\w]{1,20}+://|', $piece) ) ) {
  292. $r .= $piece;
  293. continue;
  294. }
  295. // Long strings might contain expensive edge cases ...
  296. if ( 10000 < strlen( $piece ) ) {
  297. // ... break it up
  298. foreach ( _split_str_by_whitespace( $piece, 2100 ) as $chunk ) { // 2100: Extra room for scheme and leading and trailing paretheses
  299. if ( 2101 < strlen( $chunk ) ) {
  300. $r .= $chunk; // Too big, no whitespace: bail.
  301. } else {
  302. $r .= make_clickable( $chunk );
  303. }
  304. }
  305. } else {
  306. $ret = " $piece "; // Pad with whitespace to simplify the regexes
  307. $url_clickable = '~
  308. ([\\s(<.,;:!?]) # 1: Leading whitespace, or punctuation
  309. ( # 2: URL
  310. [\\w]{1,20}+:// # Scheme and hier-part prefix
  311. (?=\S{1,2000}\s) # Limit to URLs less than about 2000 characters long
  312. [\\w\\x80-\\xff#%\\~/@\\[\\]*(+=&$-]*+ # Non-punctuation URL character
  313. (?: # Unroll the Loop: Only allow puctuation URL character if followed by a non-punctuation URL character
  314. [\'.,;:!?)] # Punctuation URL character
  315. [\\w\\x80-\\xff#%\\~/@\\[\\]*(+=&$-]++ # Non-punctuation URL character
  316. )*
  317. )
  318. (\)?) # 3: Trailing closing parenthesis (for parethesis balancing post processing)
  319. ~xS'; // The regex is a non-anchored pattern and does not have a single fixed starting character.
  320. // Tell PCRE to spend more time optimizing since, when used on a page load, it will probably be used several times.
  321. $ret = preg_replace_callback( $url_clickable, '_make_url_clickable_cb', $ret );
  322. $ret = preg_replace_callback( '#([\s>])((www|ftp)\.[\w\\x80-\\xff\#$%&~/.\-;:=,?@\[\]+]+)#is', '_make_web_ftp_clickable_cb', $ret );
  323. $ret = preg_replace_callback( '#([\s>])([.0-9a-z_+-]+)@(([0-9a-z-]+\.)+[0-9a-z]{2,})#i', '_make_email_clickable_cb', $ret );
  324. $ret = substr( $ret, 1, -1 ); // Remove our whitespace padding.
  325. $r .= $ret;
  326. }
  327. }
  328. // Cleanup of accidental links within links
  329. $r = preg_replace( '#(<a( [^>]+?>|>))<a [^>]+?>([^>]+?)</a></a>#i', "$1$3</a>", $r );
  330. return $r;
  331. }