/includes/common/functions.php

  1. <?php 
  2.  
  3. /** 
  4. * bbPress Common Functions 
  5. * 
  6. * Common functions are ones that are used by more than one component, like 
  7. * forums, topics, replies, users, topic tags, etc... 
  8. * 
  9. * @package bbPress 
  10. * @subpackage Functions 
  11. */ 
  12.  
  13. // Exit if accessed directly 
  14. if ( !defined( 'ABSPATH' ) ) exit; 
  15.  
  16. /** Formatting ****************************************************************/ 
  17.  
  18. /** 
  19. * A bbPress specific method of formatting numeric values 
  20. * 
  21. * @since bbPress (r2486) 
  22. * 
  23. * @param string $number Number to format 
  24. * @param string $decimals Optional. Display decimals 
  25. * @uses apply_filters() Calls 'bbp_number_format' with the formatted values,  
  26. * number and display decimals bool 
  27. * @return string Formatted string 
  28. */ 
  29. function bbp_number_format( $number = 0, $decimals = false, $dec_point = '.', $thousands_sep = ', ' ) { 
  30.  
  31. // If empty, set $number to (int) 0 
  32. if ( ! is_numeric( $number ) ) 
  33. $number = 0; 
  34.  
  35. return apply_filters( 'bbp_number_format', number_format( $number, $decimals, $dec_point, $thousands_sep ), $number, $decimals, $dec_point, $thousands_sep ); 
  36.  
  37. /** 
  38. * A bbPress specific method of formatting numeric values 
  39. * 
  40. * @since bbPress (r3857) 
  41. * 
  42. * @param string $number Number to format 
  43. * @param string $decimals Optional. Display decimals 
  44. * @uses apply_filters() Calls 'bbp_number_format' with the formatted values,  
  45. * number and display decimals bool 
  46. * @return string Formatted string 
  47. */ 
  48. function bbp_number_format_i18n( $number = 0, $decimals = false ) { 
  49.  
  50. // If empty, set $number to (int) 0 
  51. if ( ! is_numeric( $number ) ) 
  52. $number = 0; 
  53.  
  54. return apply_filters( 'bbp_number_format_i18n', number_format_i18n( $number, $decimals ), $number, $decimals ); 
  55.  
  56. /** 
  57. * Convert time supplied from database query into specified date format. 
  58. * 
  59. * @since bbPress (r2455) 
  60. * 
  61. * @param int|object $post Optional. Default is global post object. A post_id or 
  62. * post object 
  63. * @param string $d Optional. Default is 'U'. Either 'G', 'U', or php date 
  64. * format 
  65. * @param bool $translate Optional. Default is false. Whether to translate the 
  66. * result 
  67. * @uses mysql2date() To convert the format 
  68. * @uses apply_filters() Calls 'bbp_convert_date' with the time, date format 
  69. * and translate bool 
  70. * @return string Returns timestamp 
  71. */ 
  72. function bbp_convert_date( $time, $d = 'U', $translate = false ) { 
  73. $time = mysql2date( $d, $time, $translate ); 
  74.  
  75. return apply_filters( 'bbp_convert_date', $time, $d, $translate ); 
  76.  
  77. /** 
  78. * Output formatted time to display human readable time difference. 
  79. * 
  80. * @since bbPress (r2544) 
  81. * 
  82. * @param string $older_date Unix timestamp from which the difference begins. 
  83. * @param string $newer_date Optional. Unix timestamp from which the 
  84. * difference ends. False for current time. 
  85. * @param int $gmt Optional. Whether to use GMT timezone. Default is false. 
  86. * @uses bbp_get_time_since() To get the formatted time 
  87. */ 
  88. function bbp_time_since( $older_date, $newer_date = false, $gmt = false ) { 
  89. echo bbp_get_time_since( $older_date, $newer_date, $gmt ); 
  90. /** 
  91. * Return formatted time to display human readable time difference. 
  92. * 
  93. * @since bbPress (r2544) 
  94. * 
  95. * @param string $older_date Unix timestamp from which the difference begins. 
  96. * @param string $newer_date Optional. Unix timestamp from which the 
  97. * difference ends. False for current time. 
  98. * @param int $gmt Optional. Whether to use GMT timezone. Default is false. 
  99. * @uses current_time() To get the current time in mysql format 
  100. * @uses human_time_diff() To get the time differene in since format 
  101. * @uses apply_filters() Calls 'bbp_get_time_since' with the time 
  102. * difference and time 
  103. * @return string Formatted time 
  104. */ 
  105. function bbp_get_time_since( $older_date, $newer_date = false, $gmt = false ) { 
  106.  
  107. // Setup the strings 
  108. $unknown_text = apply_filters( 'bbp_core_time_since_unknown_text', __( 'sometime', 'bbpress' ) ); 
  109. $right_now_text = apply_filters( 'bbp_core_time_since_right_now_text', __( 'right now', 'bbpress' ) ); 
  110. $ago_text = apply_filters( 'bbp_core_time_since_ago_text', __( '%s ago', 'bbpress' ) ); 
  111.  
  112. // array of time period chunks 
  113. $chunks = array( 
  114. array( 60 * 60 * 24 * 365 , __( 'year', 'bbpress' ), __( 'years', 'bbpress' ) ),  
  115. array( 60 * 60 * 24 * 30 , __( 'month', 'bbpress' ), __( 'months', 'bbpress' ) ),  
  116. array( 60 * 60 * 24 * 7, __( 'week', 'bbpress' ), __( 'weeks', 'bbpress' ) ),  
  117. array( 60 * 60 * 24 , __( 'day', 'bbpress' ), __( 'days', 'bbpress' ) ),  
  118. array( 60 * 60 , __( 'hour', 'bbpress' ), __( 'hours', 'bbpress' ) ),  
  119. array( 60 , __( 'minute', 'bbpress' ), __( 'minutes', 'bbpress' ) ),  
  120. array( 1, __( 'second', 'bbpress' ), __( 'seconds', 'bbpress' ) ) 
  121. ); 
  122.  
  123. if ( !empty( $older_date ) && !is_numeric( $older_date ) ) { 
  124. $time_chunks = explode( ':', str_replace( ' ', ':', $older_date ) ); 
  125. $date_chunks = explode( '-', str_replace( ' ', '-', $older_date ) ); 
  126. $older_date = gmmktime( (int) $time_chunks[1], (int) $time_chunks[2], (int) $time_chunks[3], (int) $date_chunks[1], (int) $date_chunks[2], (int) $date_chunks[0] ); 
  127.  
  128. // $newer_date will equal false if we want to know the time elapsed 
  129. // between a date and the current time. $newer_date will have a value if 
  130. // we want to work out time elapsed between two known dates. 
  131. $newer_date = ( !$newer_date ) ? strtotime( current_time( 'mysql', $gmt ) ) : $newer_date; 
  132.  
  133. // Difference in seconds 
  134. $since = $newer_date - $older_date; 
  135.  
  136. // Something went wrong with date calculation and we ended up with a negative date. 
  137. if ( 0 > $since ) { 
  138. $output = $unknown_text; 
  139.  
  140. // We only want to output two chunks of time here, eg: 
  141. // x years, xx months 
  142. // x days, xx hours 
  143. // so there's only two bits of calculation below: 
  144. } else { 
  145.  
  146. // Step one: the first chunk 
  147. for ( $i = 0, $j = count( $chunks ); $i < $j; ++$i ) { 
  148. $seconds = $chunks[$i][0]; 
  149.  
  150. // Finding the biggest chunk (if the chunk fits, break) 
  151. $count = floor( $since / $seconds ); 
  152. if ( 0 != $count ) { 
  153. break; 
  154.  
  155. // If $i iterates all the way to $j, then the event happened 0 seconds ago 
  156. if ( !isset( $chunks[$i] ) ) { 
  157. $output = $right_now_text; 
  158.  
  159. } else { 
  160.  
  161. // Set output var 
  162. $output = ( 1 == $count ) ? '1 '. $chunks[$i][1] : $count . ' ' . $chunks[$i][2]; 
  163.  
  164. // Step two: the second chunk 
  165. if ( $i + 2 < $j ) { 
  166. $seconds2 = $chunks[$i + 1][0]; 
  167. $name2 = $chunks[$i + 1][1]; 
  168. $count2 = floor( ( $since - ( $seconds * $count ) ) / $seconds2 ); 
  169.  
  170. // Add to output var 
  171. if ( 0 != $count2 ) { 
  172. $output .= ( 1 == $count2 ) ? _x( ', ', 'Separator in time since', 'bbpress' ) . ' 1 '. $name2 : _x( ', ', 'Separator in time since', 'bbpress' ) . ' ' . $count2 . ' ' . $chunks[$i + 1][2]; 
  173.  
  174. // No output, so happened right now 
  175. if ( ! (int) trim( $output ) ) { 
  176. $output = $right_now_text; 
  177.  
  178. // Append 'ago' to the end of time-since if not 'right now' 
  179. if ( $output != $right_now_text ) { 
  180. $output = sprintf( $ago_text, $output ); 
  181.  
  182. return apply_filters( 'bbp_get_time_since', $output, $older_date, $newer_date ); 
  183.  
  184. /** 
  185. * Formats the reason for editing the topic/reply. 
  186. * 
  187. * Does these things: 
  188. * - Trimming 
  189. * - Removing periods from the end of the string 
  190. * - Trimming again 
  191. * 
  192. * @since bbPress (r2782) 
  193. * 
  194. * @param int $topic_id Optional. Topic id 
  195. * @return string Status of topic 
  196. */ 
  197. function bbp_format_revision_reason( $reason = '' ) { 
  198. $reason = (string) $reason; 
  199.  
  200. // Format reason for proper display 
  201. if ( empty( $reason ) ) 
  202. return $reason; 
  203.  
  204. // Trimming 
  205. $reason = trim( $reason ); 
  206.  
  207. // We add our own full stop. 
  208. while ( substr( $reason, -1 ) === '.' ) 
  209. $reason = substr( $reason, 0, -1 ); 
  210.  
  211. // Trim again 
  212. $reason = trim( $reason ); 
  213.  
  214. return $reason; 
  215.  
  216. /** Misc **********************************************************************/ 
  217.  
  218. /** 
  219. * Return the unescaped redirect_to request value 
  220. * 
  221. * @bbPress (r4655) 
  222. * 
  223. * @return string The URL to redirect to, if set 
  224. */ 
  225. function bbp_get_redirect_to() { 
  226. $retval = !empty( $_REQUEST['redirect_to'] ) ? $_REQUEST['redirect_to'] : ''; 
  227.  
  228. return apply_filters( 'bbp_get_redirect_to', $retval ); 
  229.  
  230. /** 
  231. * Append 'view=all' to query string if it's already there from referer 
  232. * 
  233. * @since bbPress (r3325) 
  234. * 
  235. * @param string $original_link Original Link to be modified 
  236. * @param bool $force Override bbp_get_view_all() check 
  237. * @uses current_user_can() To check if the current user can moderate 
  238. * @uses add_query_arg() To add args to the url 
  239. * @uses apply_filters() Calls 'bbp_add_view_all' with the link and original link 
  240. * @return string The link with 'view=all' appended if necessary 
  241. */ 
  242. function bbp_add_view_all( $original_link = '', $force = false ) { 
  243.  
  244. // Are we appending the view=all vars? 
  245. if ( bbp_get_view_all() || !empty( $force ) ) { 
  246. $link = add_query_arg( array( 'view' => 'all' ), $original_link ); 
  247. } else { 
  248. $link = $original_link; 
  249.  
  250. return apply_filters( 'bbp_add_view_all', $link, $original_link ); 
  251.  
  252. /** 
  253. * Remove 'view=all' from query string 
  254. * 
  255. * @since bbPress (r3325) 
  256. * 
  257. * @param string $original_link Original Link to be modified 
  258. * @uses current_user_can() To check if the current user can moderate 
  259. * @uses add_query_arg() To add args to the url 
  260. * @uses apply_filters() Calls 'bbp_add_view_all' with the link and original link 
  261. * @return string The link with 'view=all' appended if necessary 
  262. */ 
  263. function bbp_remove_view_all( $original_link = '' ) { 
  264. return apply_filters( 'bbp_add_view_all', remove_query_arg( 'view', $original_link ), $original_link ); 
  265.  
  266. /** 
  267. * If current user can and is vewing all topics/replies 
  268. * 
  269. * @since bbPress (r3325) 
  270. * 
  271. * @uses current_user_can() To check if the current user can moderate 
  272. * @uses apply_filters() Calls 'bbp_get_view_all' with the link and original link 
  273. * @return bool Whether current user can and is viewing all 
  274. */ 
  275. function bbp_get_view_all( $cap = 'moderate' ) { 
  276. $retval = ( ( !empty( $_GET['view'] ) && ( 'all' === $_GET['view'] ) && current_user_can( $cap ) ) ); 
  277. return apply_filters( 'bbp_get_view_all', (bool) $retval ); 
  278.  
  279. /** 
  280. * Assist pagination by returning correct page number 
  281. * 
  282. * @since bbPress (r2628) 
  283. * 
  284. * @uses get_query_var() To get the 'paged' value 
  285. * @return int Current page number 
  286. */ 
  287. function bbp_get_paged() { 
  288. global $wp_query; 
  289.  
  290. // Check the query var 
  291. if ( get_query_var( 'paged' ) ) { 
  292. $paged = get_query_var( 'paged' ); 
  293.  
  294. // Check query paged 
  295. } elseif ( !empty( $wp_query->query['paged'] ) ) { 
  296. $paged = $wp_query->query['paged']; 
  297.  
  298. // Paged found 
  299. if ( !empty( $paged ) ) 
  300. return (int) $paged; 
  301.  
  302. // Default to first page 
  303. return 1; 
  304.  
  305. /** 
  306. * Fix post author id on post save 
  307. * 
  308. * When a logged in user changes the status of an anonymous reply or topic, or 
  309. * edits it, the post_author field is set to the logged in user's id. This 
  310. * function fixes that. 
  311. * 
  312. * @since bbPress (r2734) 
  313. * 
  314. * @param array $data Post data 
  315. * @param array $postarr Original post array (includes post id) 
  316. * @uses bbp_get_topic_post_type() To get the topic post type 
  317. * @uses bbp_get_reply_post_type() To get the reply post type 
  318. * @uses bbp_is_topic_anonymous() To check if the topic is by an anonymous user 
  319. * @uses bbp_is_reply_anonymous() To check if the reply is by an anonymous user 
  320. * @return array Data 
  321. */ 
  322. function bbp_fix_post_author( $data = array(), $postarr = array() ) { 
  323.  
  324. // Post is not being updated or the post_author is already 0, return 
  325. if ( empty( $postarr['ID'] ) || empty( $data['post_author'] ) ) 
  326. return $data; 
  327.  
  328. // Post is not a topic or reply, return 
  329. if ( !in_array( $data['post_type'], array( bbp_get_topic_post_type(), bbp_get_reply_post_type() ) ) ) 
  330. return $data; 
  331.  
  332. // Is the post by an anonymous user? 
  333. if ( ( bbp_get_topic_post_type() === $data['post_type'] && !bbp_is_topic_anonymous( $postarr['ID'] ) ) || 
  334. ( bbp_get_reply_post_type() === $data['post_type'] && !bbp_is_reply_anonymous( $postarr['ID'] ) ) ) 
  335. return $data; 
  336.  
  337. // The post is being updated. It is a topic or a reply and is written by an anonymous user. 
  338. // Set the post_author back to 0 
  339. $data['post_author'] = 0; 
  340.  
  341. return $data; 
  342.  
  343. /** 
  344. * Check the date against the _bbp_edit_lock setting. 
  345. * 
  346. * @since bbPress (r3133) 
  347. * 
  348. * @param string $post_date_gmt 
  349. * 
  350. * @uses get_option() Get the edit lock time 
  351. * @uses current_time() Get the current time 
  352. * @uses strtotime() Convert strings to time 
  353. * @uses apply_filters() Allow output to be manipulated 
  354. * 
  355. * @return bool 
  356. */ 
  357. function bbp_past_edit_lock( $post_date_gmt ) { 
  358.  
  359. // Assume editing is allowed 
  360. $retval = false; 
  361.  
  362. // Bail if empty date 
  363. if ( ! empty( $post_date_gmt ) ) { 
  364.  
  365. // Period of time 
  366. $lockable = '+' . get_option( '_bbp_edit_lock', '5' ) . ' minutes'; 
  367.  
  368. // Now 
  369. $cur_time = current_time( 'timestamp', true ); 
  370.  
  371. // Add lockable time to post time 
  372. $lock_time = strtotime( $lockable, strtotime( $post_date_gmt ) ); 
  373.  
  374. // Compare 
  375. if ( $cur_time >= $lock_time ) { 
  376. $retval = true; 
  377.  
  378. return apply_filters( 'bbp_past_edit_lock', (bool) $retval, $cur_time, $lock_time, $post_date_gmt ); 
  379.  
  380. /** Statistics ****************************************************************/ 
  381.  
  382. /** 
  383. * Get the forum statistics 
  384. * 
  385. * @since bbPress (r2769) 
  386. * 
  387. * @param mixed $args Optional. The function supports these arguments (all 
  388. * default to true): 
  389. * - count_users: Count users? 
  390. * - count_forums: Count forums? 
  391. * - count_topics: Count topics? If set to false, private, spammed and trashed 
  392. * topics are also not counted. 
  393. * - count_private_topics: Count private topics? (only counted if the current 
  394. * user has read_private_topics cap) 
  395. * - count_spammed_topics: Count spammed topics? (only counted if the current 
  396. * user has edit_others_topics cap) 
  397. * - count_trashed_topics: Count trashed topics? (only counted if the current 
  398. * user has view_trash cap) 
  399. * - count_replies: Count replies? If set to false, private, spammed and 
  400. * trashed replies are also not counted. 
  401. * - count_private_replies: Count private replies? (only counted if the current 
  402. * user has read_private_replies cap) 
  403. * - count_spammed_replies: Count spammed replies? (only counted if the current 
  404. * user has edit_others_replies cap) 
  405. * - count_trashed_replies: Count trashed replies? (only counted if the current 
  406. * user has view_trash cap) 
  407. * - count_tags: Count tags? If set to false, empty tags are also not counted 
  408. * - count_empty_tags: Count empty tags? 
  409. * @uses bbp_count_users() To count the number of registered users 
  410. * @uses bbp_get_forum_post_type() To get the forum post type 
  411. * @uses bbp_get_topic_post_type() To get the topic post type 
  412. * @uses bbp_get_reply_post_type() To get the reply post type 
  413. * @uses wp_count_posts() To count the number of forums, topics and replies 
  414. * @uses wp_count_terms() To count the number of topic tags 
  415. * @uses current_user_can() To check if the user is capable of doing things 
  416. * @uses number_format_i18n() To format the number 
  417. * @uses apply_filters() Calls 'bbp_get_statistics' with the statistics and args 
  418. * @return object Walked forum tree 
  419. */ 
  420. function bbp_get_statistics( $args = '' ) { 
  421.  
  422. // Parse arguments against default values 
  423. $r = bbp_parse_args( $args, array( 
  424. 'count_users' => true,  
  425. 'count_forums' => true,  
  426. 'count_topics' => true,  
  427. 'count_private_topics' => true,  
  428. 'count_spammed_topics' => true,  
  429. 'count_trashed_topics' => true,  
  430. 'count_replies' => true,  
  431. 'count_private_replies' => true,  
  432. 'count_spammed_replies' => true,  
  433. 'count_trashed_replies' => true,  
  434. 'count_tags' => true,  
  435. 'count_empty_tags' => true 
  436. ), 'get_statistics' ); 
  437.  
  438. // Defaults 
  439. $user_count = 0; 
  440. $forum_count = 0; 
  441. $topic_count = 0; 
  442. $topic_count_hidden = 0; 
  443. $reply_count = 0; 
  444. $reply_count_hidden = 0; 
  445. $topic_tag_count = 0; 
  446. $empty_topic_tag_count = 0; 
  447.  
  448. // Users 
  449. if ( !empty( $r['count_users'] ) ) { 
  450. $user_count = bbp_get_total_users(); 
  451.  
  452. // Forums 
  453. if ( !empty( $r['count_forums'] ) ) { 
  454. $forum_count = wp_count_posts( bbp_get_forum_post_type() )->publish; 
  455.  
  456. // Post statuses 
  457. $private = bbp_get_private_status_id(); 
  458. $spam = bbp_get_spam_status_id(); 
  459. $trash = bbp_get_trash_status_id(); 
  460. $closed = bbp_get_closed_status_id(); 
  461.  
  462. // Topics 
  463. if ( !empty( $r['count_topics'] ) ) { 
  464. $all_topics = wp_count_posts( bbp_get_topic_post_type() ); 
  465.  
  466. // Published (publish + closed) 
  467. $topic_count = $all_topics->publish + $all_topics->{$closed}; 
  468.  
  469. if ( current_user_can( 'read_private_topics' ) || current_user_can( 'edit_others_topics' ) || current_user_can( 'view_trash' ) ) { 
  470.  
  471. // Private 
  472. $topics['private'] = ( !empty( $r['count_private_topics'] ) && current_user_can( 'read_private_topics' ) ) ? (int) $all_topics->{$private} : 0; 
  473.  
  474. // Spam 
  475. $topics['spammed'] = ( !empty( $r['count_spammed_topics'] ) && current_user_can( 'edit_others_topics' ) ) ? (int) $all_topics->{$spam} : 0; 
  476.  
  477. // Trash 
  478. $topics['trashed'] = ( !empty( $r['count_trashed_topics'] ) && current_user_can( 'view_trash' ) ) ? (int) $all_topics->{$trash} : 0; 
  479.  
  480. // Total hidden (private + spam + trash) 
  481. $topic_count_hidden = $topics['private'] + $topics['spammed'] + $topics['trashed']; 
  482.  
  483. // Generate the hidden topic count's title attribute 
  484. $topic_titles[] = !empty( $topics['private'] ) ? sprintf( __( 'Private: %s', 'bbpress' ), number_format_i18n( $topics['private'] ) ) : ''; 
  485. $topic_titles[] = !empty( $topics['spammed'] ) ? sprintf( __( 'Spammed: %s', 'bbpress' ), number_format_i18n( $topics['spammed'] ) ) : ''; 
  486. $topic_titles[] = !empty( $topics['trashed'] ) ? sprintf( __( 'Trashed: %s', 'bbpress' ), number_format_i18n( $topics['trashed'] ) ) : ''; 
  487.  
  488. // Compile the hidden topic title 
  489. $hidden_topic_title = implode( ' | ', array_filter( $topic_titles ) ); 
  490.  
  491. // Replies 
  492. if ( !empty( $r['count_replies'] ) ) { 
  493.  
  494. $all_replies = wp_count_posts( bbp_get_reply_post_type() ); 
  495.  
  496. // Published 
  497. $reply_count = $all_replies->publish; 
  498.  
  499. if ( current_user_can( 'read_private_replies' ) || current_user_can( 'edit_others_replies' ) || current_user_can( 'view_trash' ) ) { 
  500.  
  501. // Private 
  502. $replies['private'] = ( !empty( $r['count_private_replies'] ) && current_user_can( 'read_private_replies' ) ) ? (int) $all_replies->{$private} : 0; 
  503.  
  504. // Spam 
  505. $replies['spammed'] = ( !empty( $r['count_spammed_replies'] ) && current_user_can( 'edit_others_replies' ) ) ? (int) $all_replies->{$spam} : 0; 
  506.  
  507. // Trash 
  508. $replies['trashed'] = ( !empty( $r['count_trashed_replies'] ) && current_user_can( 'view_trash' ) ) ? (int) $all_replies->{$trash} : 0; 
  509.  
  510. // Total hidden (private + spam + trash) 
  511. $reply_count_hidden = $replies['private'] + $replies['spammed'] + $replies['trashed']; 
  512.  
  513. // Generate the hidden topic count's title attribute 
  514. $reply_titles[] = !empty( $replies['private'] ) ? sprintf( __( 'Private: %s', 'bbpress' ), number_format_i18n( $replies['private'] ) ) : ''; 
  515. $reply_titles[] = !empty( $replies['spammed'] ) ? sprintf( __( 'Spammed: %s', 'bbpress' ), number_format_i18n( $replies['spammed'] ) ) : ''; 
  516. $reply_titles[] = !empty( $replies['trashed'] ) ? sprintf( __( 'Trashed: %s', 'bbpress' ), number_format_i18n( $replies['trashed'] ) ) : ''; 
  517.  
  518. // Compile the hidden replies title 
  519. $hidden_reply_title = implode( ' | ', array_filter( $reply_titles ) ); 
  520.  
  521.  
  522. // Topic Tags 
  523. if ( !empty( $r['count_tags'] ) && bbp_allow_topic_tags() ) { 
  524.  
  525. // Get the count 
  526. $topic_tag_count = wp_count_terms( bbp_get_topic_tag_tax_id(), array( 'hide_empty' => true ) ); 
  527.  
  528. // Empty tags 
  529. if ( !empty( $r['count_empty_tags'] ) && current_user_can( 'edit_topic_tags' ) ) { 
  530. $empty_topic_tag_count = wp_count_terms( bbp_get_topic_tag_tax_id() ) - $topic_tag_count; 
  531.  
  532. // Tally the tallies 
  533. $statistics = array_map( 'number_format_i18n', array_map( 'absint', compact( 
  534. 'user_count',  
  535. 'forum_count',  
  536. 'topic_count',  
  537. 'topic_count_hidden',  
  538. 'reply_count',  
  539. 'reply_count_hidden',  
  540. 'topic_tag_count',  
  541. 'empty_topic_tag_count' 
  542. ) ) ); 
  543.  
  544. // Add the hidden (topic/reply) count title attribute strings because we 
  545. // don't need to run the math functions on these (see above) 
  546. $statistics['hidden_topic_title'] = isset( $hidden_topic_title ) ? $hidden_topic_title : ''; 
  547. $statistics['hidden_reply_title'] = isset( $hidden_reply_title ) ? $hidden_reply_title : ''; 
  548.  
  549. return apply_filters( 'bbp_get_statistics', $statistics, $r ); 
  550.  
  551. /** New/edit topic/reply helpers **********************************************/ 
  552.  
  553. /** 
  554. * Filter anonymous post data 
  555. * 
  556. * We use REMOTE_ADDR here directly. If you are behind a proxy, you should 
  557. * ensure that it is properly set, such as in wp-config.php, for your 
  558. * environment. See {@link http://core.trac.wordpress.org/ticket/9235} 
  559. * 
  560. * Note that bbp_pre_anonymous_filters() is responsible for sanitizing each 
  561. * of the filtered core anonymous values here. 
  562. * 
  563. * If there are any errors, those are directly added to {@link bbPress:errors} 
  564. * 
  565. * @since bbPress (r2734) 
  566. * 
  567. * @param mixed $args Optional. If no args are there, then $_POST values are 
  568. * used. 
  569. * @uses apply_filters() Calls 'bbp_pre_anonymous_post_author_name' with the 
  570. * anonymous user name 
  571. * @uses apply_filters() Calls 'bbp_pre_anonymous_post_author_email' with the 
  572. * anonymous user email 
  573. * @uses apply_filters() Calls 'bbp_pre_anonymous_post_author_website' with the 
  574. * anonymous user website 
  575. * @return bool|array False on errors, values in an array on success 
  576. */ 
  577. function bbp_filter_anonymous_post_data( $args = '' ) { 
  578.  
  579. // Parse arguments against default values 
  580. $r = bbp_parse_args( $args, array ( 
  581. 'bbp_anonymous_name' => !empty( $_POST['bbp_anonymous_name'] ) ? $_POST['bbp_anonymous_name'] : false,  
  582. 'bbp_anonymous_email' => !empty( $_POST['bbp_anonymous_email'] ) ? $_POST['bbp_anonymous_email'] : false,  
  583. 'bbp_anonymous_website' => !empty( $_POST['bbp_anonymous_website'] ) ? $_POST['bbp_anonymous_website'] : false,  
  584. ), 'filter_anonymous_post_data' ); 
  585.  
  586. // Filter variables and add errors if necessary 
  587. $r['bbp_anonymous_name'] = apply_filters( 'bbp_pre_anonymous_post_author_name', $r['bbp_anonymous_name'] ); 
  588. if ( empty( $r['bbp_anonymous_name'] ) ) 
  589. bbp_add_error( 'bbp_anonymous_name', __( '<strong>ERROR</strong>: Invalid author name submitted!', 'bbpress' ) ); 
  590.  
  591. $r['bbp_anonymous_email'] = apply_filters( 'bbp_pre_anonymous_post_author_email', $r['bbp_anonymous_email'] ); 
  592. if ( empty( $r['bbp_anonymous_email'] ) ) 
  593. bbp_add_error( 'bbp_anonymous_email', __( '<strong>ERROR</strong>: Invalid email address submitted!', 'bbpress' ) ); 
  594.  
  595. // Website is optional 
  596. $r['bbp_anonymous_website'] = apply_filters( 'bbp_pre_anonymous_post_author_website', $r['bbp_anonymous_website'] ); 
  597.  
  598. // Return false if we have any errors 
  599. $retval = bbp_has_errors() ? false : $r; 
  600.  
  601. // Finally, return sanitized data or false 
  602. return apply_filters( 'bbp_filter_anonymous_post_data', $retval, $r ); 
  603.  
  604. /** 
  605. * Check for duplicate topics/replies 
  606. * 
  607. * Check to make sure that a user is not making a duplicate post 
  608. * 
  609. * @since bbPress (r2763) 
  610. * 
  611. * @param array $post_data Contains information about the comment 
  612. * @uses current_user_can() To check if the current user can throttle 
  613. * @uses get_meta_sql() To generate the meta sql for checking anonymous email 
  614. * @uses apply_filters() Calls 'bbp_check_for_duplicate_query' with the 
  615. * duplicate check query and post data 
  616. * @uses wpdb::get_var() To execute our query and get the var back 
  617. * @uses get_post_meta() To get the anonymous user email post meta 
  618. * @uses do_action() Calls 'bbp_post_duplicate_trigger' with the post data when 
  619. * it is found that it is a duplicate 
  620. * @return bool True if it is not a duplicate, false if it is 
  621. */ 
  622. function bbp_check_for_duplicate( $post_data = array() ) { 
  623.  
  624. // No duplicate checks for those who can throttle 
  625. if ( current_user_can( 'throttle' ) ) 
  626. return true; 
  627.  
  628. // Define global to use get_meta_sql() and get_var() methods 
  629. global $wpdb; 
  630.  
  631. // Parse arguments against default values 
  632. $r = bbp_parse_args( $post_data, array( 
  633. 'post_author' => 0,  
  634. 'post_type' => array( bbp_get_topic_post_type(), bbp_get_reply_post_type() ),  
  635. 'post_parent' => 0,  
  636. 'post_content' => '',  
  637. 'post_status' => bbp_get_trash_status_id(),  
  638. 'anonymous_data' => false 
  639. ), 'check_for_duplicate' ); 
  640.  
  641. // Check for anonymous post 
  642. if ( empty( $r['post_author'] ) && ( !empty( $r['anonymous_data'] ) && !empty( $r['anonymous_data']['bbp_anonymous_email'] ) ) ) { 
  643. $clauses = get_meta_sql( array( array( 
  644. 'key' => '_bbp_anonymous_email',  
  645. 'value' => $r['anonymous_data']['bbp_anonymous_email'] 
  646. ) ), 'post', $wpdb->posts, 'ID' ); 
  647.  
  648. $join = $clauses['join']; 
  649. $where = $clauses['where']; 
  650. } else { 
  651. $join = $where = ''; 
  652.  
  653. // Unslash $r to pass through $wpdb->prepare() 
  654. // 
  655. // @see: http://bbpress.trac.wordpress.org/ticket/2185/ 
  656. // @see: http://core.trac.wordpress.org/changeset/23973/ 
  657. $r = function_exists( 'wp_unslash' ) ? wp_unslash( $r ) : stripslashes_deep( $r ); 
  658.  
  659. // Prepare duplicate check query 
  660. $query = $wpdb->prepare( "SELECT ID FROM {$wpdb->posts} {$join} WHERE post_type = %s AND post_status != %s AND post_author = %d AND post_content = %s {$where}", $r['post_type'], $r['post_status'], $r['post_author'], $r['post_content'] ); 
  661. $query .= !empty( $r['post_parent'] ) ? $wpdb->prepare( " AND post_parent = %d", $r['post_parent'] ) : ''; 
  662. $query .= " LIMIT 1"; 
  663. $dupe = apply_filters( 'bbp_check_for_duplicate_query', $query, $r ); 
  664.  
  665. if ( $wpdb->get_var( $dupe ) ) { 
  666. do_action( 'bbp_check_for_duplicate_trigger', $post_data ); 
  667. return false; 
  668.  
  669. return true; 
  670.  
  671. /** 
  672. * Check for flooding 
  673. * 
  674. * Check to make sure that a user is not making too many posts in a short amount 
  675. * of time. 
  676. * 
  677. * @since bbPress (r2734) 
  678. * 
  679. * @param false|array $anonymous_data Optional - if it's an anonymous post. Do 
  680. * not supply if supplying $author_id. 
  681. * Should have key 'bbp_author_ip'. 
  682. * Should be sanitized (see 
  683. * {@link bbp_filter_anonymous_post_data()} 
  684. * for sanitization) 
  685. * @param int $author_id Optional. Supply if it's a post by a logged in user. 
  686. * Do not supply if supplying $anonymous_data. 
  687. * @uses get_option() To get the throttle time 
  688. * @uses get_transient() To get the last posted transient of the ip 
  689. * @uses bbp_get_user_last_posted() To get the last posted time of the user 
  690. * @uses current_user_can() To check if the current user can throttle 
  691. * @return bool True if there is no flooding, false if there is 
  692. */ 
  693. function bbp_check_for_flood( $anonymous_data = false, $author_id = 0 ) { 
  694.  
  695. // Option disabled. No flood checks. 
  696. $throttle_time = get_option( '_bbp_throttle_time' ); 
  697. if ( empty( $throttle_time ) ) 
  698. return true; 
  699.  
  700. // User is anonymous, so check a transient based on the IP 
  701. if ( !empty( $anonymous_data ) && is_array( $anonymous_data ) ) { 
  702. $last_posted = get_transient( '_bbp_' . bbp_current_author_ip() . '_last_posted' ); 
  703.  
  704. if ( !empty( $last_posted ) && time() < $last_posted + $throttle_time ) { 
  705. return false; 
  706.  
  707. // User is logged in, so check their last posted time 
  708. } elseif ( !empty( $author_id ) ) { 
  709. $author_id = (int) $author_id; 
  710. $last_posted = bbp_get_user_last_posted( $author_id ); 
  711.  
  712. if ( isset( $last_posted ) && time() < $last_posted + $throttle_time && !current_user_can( 'throttle' ) ) { 
  713. return false; 
  714. } else { 
  715. return false; 
  716.  
  717. return true; 
  718.  
  719. /** 
  720. * Checks topics and replies against the discussion moderation of blocked keys 
  721. * 
  722. * @since bbPress (r3581) 
  723. * 
  724. * @param array $anonymous_data Anonymous user data 
  725. * @param int $author_id Topic or reply author ID 
  726. * @param string $title The title of the content 
  727. * @param string $content The content being posted 
  728. * @uses bbp_is_user_keymaster() Allow keymasters to bypass blacklist 
  729. * @uses bbp_current_author_ip() To get current user IP address 
  730. * @uses bbp_current_author_ua() To get current user agent 
  731. * @return bool True if test is passed, false if fail 
  732. */ 
  733. function bbp_check_for_moderation( $anonymous_data = false, $author_id = 0, $title = '', $content = '' ) { 
  734.  
  735. // Allow for moderation check to be skipped 
  736. if ( apply_filters( 'bbp_bypass_check_for_moderation', false, $anonymous_data, $author_id, $title, $content ) ) 
  737. return true; 
  738.  
  739. // Bail if keymaster is author 
  740. if ( !empty( $author_id ) && bbp_is_user_keymaster( $author_id ) ) 
  741. return true; 
  742.  
  743. // Define local variable(s) 
  744. $_post = array(); 
  745. $match_out = ''; 
  746.  
  747. /** Blacklist *************************************************************/ 
  748.  
  749. // Get the moderation keys 
  750. $blacklist = trim( get_option( 'moderation_keys' ) ); 
  751.  
  752. // Bail if blacklist is empty 
  753. if ( empty( $blacklist ) ) 
  754. return true; 
  755.  
  756. /** User Data *************************************************************/ 
  757.  
  758. // Map anonymous user data 
  759. if ( !empty( $anonymous_data ) ) { 
  760. $_post['author'] = $anonymous_data['bbp_anonymous_name']; 
  761. $_post['email'] = $anonymous_data['bbp_anonymous_email']; 
  762. $_post['url'] = $anonymous_data['bbp_anonymous_website']; 
  763.  
  764. // Map current user data 
  765. } elseif ( !empty( $author_id ) ) { 
  766.  
  767. // Get author data 
  768. $user = get_userdata( $author_id ); 
  769.  
  770. // If data exists, map it 
  771. if ( !empty( $user ) ) { 
  772. $_post['author'] = $user->display_name; 
  773. $_post['email'] = $user->user_email; 
  774. $_post['url'] = $user->user_url; 
  775.  
  776. // Current user IP and user agent 
  777. $_post['user_ip'] = bbp_current_author_ip(); 
  778. $_post['user_ua'] = bbp_current_author_ua(); 
  779.  
  780. // Post title and content 
  781. $_post['title'] = $title; 
  782. $_post['content'] = $content; 
  783.  
  784. /** Max Links *************************************************************/ 
  785.  
  786. $max_links = get_option( 'comment_max_links' ); 
  787. if ( !empty( $max_links ) ) { 
  788.  
  789. // How many links? 
  790. $num_links = preg_match_all( '/<a [^>]*href/i', $content, $match_out ); 
  791.  
  792. // Allow for bumping the max to include the user's URL 
  793. $num_links = apply_filters( 'comment_max_links_url', $num_links, $_post['url'] ); 
  794.  
  795. // Das ist zu viele links! 
  796. if ( $num_links >= $max_links ) { 
  797. return false; 
  798.  
  799. /** Words *****************************************************************/ 
  800.  
  801. // Get words separated by new lines 
  802. $words = explode( "\n", $blacklist ); 
  803.  
  804. // Loop through words 
  805. foreach ( (array) $words as $word ) { 
  806.  
  807. // Trim the whitespace from the word 
  808. $word = trim( $word ); 
  809.  
  810. // Skip empty lines 
  811. if ( empty( $word ) ) { continue; } 
  812.  
  813. // Do some escaping magic so that '#' chars in the 
  814. // spam words don't break things: 
  815. $word = preg_quote( $word, '#' ); 
  816. $pattern = "#$word#i"; 
  817.  
  818. // Loop through post data 
  819. foreach ( $_post as $post_data ) { 
  820.  
  821. // Check each user data for current word 
  822. if ( preg_match( $pattern, $post_data ) ) { 
  823.  
  824. // Post does not pass 
  825. return false; 
  826.  
  827. // Check passed successfully 
  828. return true; 
  829.  
  830. /** 
  831. * Checks topics and replies against the discussion blacklist of blocked keys 
  832. * 
  833. * @since bbPress (r3446) 
  834. * 
  835. * @param array $anonymous_data Anonymous user data 
  836. * @param int $author_id Topic or reply author ID 
  837. * @param string $title The title of the content 
  838. * @param string $content The content being posted 
  839. * @uses bbp_is_user_keymaster() Allow keymasters to bypass blacklist 
  840. * @uses bbp_current_author_ip() To get current user IP address 
  841. * @uses bbp_current_author_ua() To get current user agent 
  842. * @return bool True if test is passed, false if fail 
  843. */ 
  844. function bbp_check_for_blacklist( $anonymous_data = false, $author_id = 0, $title = '', $content = '' ) { 
  845.  
  846. // Allow for blacklist check to be skipped 
  847. if ( apply_filters( 'bbp_bypass_check_for_blacklist', false, $anonymous_data, $author_id, $title, $content ) ) 
  848. return true; 
  849.  
  850. // Bail if keymaster is author 
  851. if ( !empty( $author_id ) && bbp_is_user_keymaster( $author_id ) ) 
  852. return true; 
  853.  
  854. // Define local variable 
  855. $_post = array(); 
  856.  
  857. /** Blacklist *************************************************************/ 
  858.  
  859. // Get the moderation keys 
  860. $blacklist = trim( get_option( 'blacklist_keys' ) ); 
  861.  
  862. // Bail if blacklist is empty 
  863. if ( empty( $blacklist ) ) 
  864. return true; 
  865.  
  866. /** User Data *************************************************************/ 
  867.  
  868. // Map anonymous user data 
  869. if ( !empty( $anonymous_data ) ) { 
  870. $_post['author'] = $anonymous_data['bbp_anonymous_name']; 
  871. $_post['email'] = $anonymous_data['bbp_anonymous_email']; 
  872. $_post['url'] = $anonymous_data['bbp_anonymous_website']; 
  873.  
  874. // Map current user data 
  875. } elseif ( !empty( $author_id ) ) { 
  876.  
  877. // Get author data 
  878. $user = get_userdata( $author_id ); 
  879.  
  880. // If data exists, map it 
  881. if ( !empty( $user ) ) { 
  882. $_post['author'] = $user->display_name; 
  883. $_post['email'] = $user->user_email; 
  884. $_post['url'] = $user->user_url; 
  885.  
  886. // Current user IP and user agent 
  887. $_post['user_ip'] = bbp_current_author_ip(); 
  888. $_post['user_ua'] = bbp_current_author_ua(); 
  889.  
  890. // Post title and content 
  891. $_post['title'] = $title; 
  892. $_post['content'] = $content; 
  893.  
  894. /** Words *****************************************************************/ 
  895.  
  896. // Get words separated by new lines 
  897. $words = explode( "\n", $blacklist ); 
  898.  
  899. // Loop through words 
  900. foreach ( (array) $words as $word ) { 
  901.  
  902. // Trim the whitespace from the word 
  903. $word = trim( $word ); 
  904.  
  905. // Skip empty lines 
  906. if ( empty( $word ) ) { continue; } 
  907.  
  908. // Do some escaping magic so that '#' chars in the 
  909. // spam words don't break things: 
  910. $word = preg_quote( $word, '#' ); 
  911. $pattern = "#$word#i"; 
  912.  
  913. // Loop through post data 
  914. foreach ( $_post as $post_data ) { 
  915.  
  916. // Check each user data for current word 
  917. if ( preg_match( $pattern, $post_data ) ) { 
  918.  
  919. // Post does not pass 
  920. return false; 
  921.  
  922. // Check passed successfully 
  923. return true; 
  924.  
  925. /** Subscriptions *************************************************************/ 
  926.  
  927. /** 
  928. * Sends notification emails for new replies to subscribed topics 
  929. * 
  930. * Gets new post's ID and check if there are subscribed users to that topic, and 
  931. * if there are, send notifications 
  932. * 
  933. * Note: in bbPress 2.6, we've moved away from 1 email per subscriber to 1 email 
  934. * with everyone BCC'd. This may have negative repercussions for email services 
  935. * that limit the number of addresses in a BCC field (often to around 500.) In 
  936. * those cases, we recommend unhooking this function and creating your own 
  937. * custom emailer script. 
  938. *  
  939. * @since bbPress (r2668) 
  940. * 
  941. * @param int $reply_id ID of the newly made reply 
  942. * @uses bbp_is_subscriptions_active() To check if the subscriptions are active 
  943. * @uses bbp_get_reply_id() To validate the reply ID 
  944. * @uses bbp_get_topic_id() To validate the topic ID 
  945. * @uses bbp_get_forum_id() To validate the forum ID 
  946. * @uses bbp_get_reply() To get the reply 
  947. * @uses bbp_is_reply_published() To make sure the reply is published 
  948. * @uses bbp_get_topic_id() To validate the topic ID 
  949. * @uses bbp_get_topic() To get the reply's topic 
  950. * @uses bbp_is_topic_published() To make sure the topic is published 
  951. * @uses bbp_get_reply_author_display_name() To get the reply author's display name 
  952. * @uses do_action() Calls 'bbp_pre_notify_subscribers' with the reply id,  
  953. * topic id and user id 
  954. * @uses bbp_get_topic_subscribers() To get the topic subscribers 
  955. * @uses apply_filters() Calls 'bbp_subscription_mail_message' with the 
  956. * message, reply id, topic id and user id 
  957. * @uses apply_filters() Calls 'bbp_subscription_mail_title' with the 
  958. * topic title, reply id, topic id and user id 
  959. * @uses apply_filters() Calls 'bbp_subscription_mail_headers' 
  960. * @uses get_userdata() To get the user data 
  961. * @uses wp_mail() To send the mail 
  962. * @uses do_action() Calls 'bbp_post_notify_subscribers' with the reply id,  
  963. * topic id and user id 
  964. * @return bool True on success, false on failure 
  965. */ 
  966. function bbp_notify_subscribers( $reply_id = 0, $topic_id = 0, $forum_id = 0, $anonymous_data = false, $reply_author = 0 ) { 
  967.  
  968. // Bail if subscriptions are turned off 
  969. if ( !bbp_is_subscriptions_active() ) { 
  970. return false; 
  971.  
  972. /** Validation ************************************************************/ 
  973.  
  974. $reply_id = bbp_get_reply_id( $reply_id ); 
  975. $topic_id = bbp_get_topic_id( $topic_id ); 
  976. $forum_id = bbp_get_forum_id( $forum_id ); 
  977.  
  978. /** Topic *****************************************************************/ 
  979.  
  980. // Bail if topic is not published 
  981. if ( !bbp_is_topic_published( $topic_id ) ) { 
  982. return false; 
  983.  
  984. /** Reply *****************************************************************/ 
  985.  
  986. // Bail if reply is not published 
  987. if ( !bbp_is_reply_published( $reply_id ) ) { 
  988. return false; 
  989.  
  990. // Poster name 
  991. $reply_author_name = bbp_get_reply_author_display_name( $reply_id ); 
  992.  
  993. /** Mail ******************************************************************/ 
  994.  
  995. // Remove filters from reply content and topic title to prevent content 
  996. // from being encoded with HTML entities, wrapped in paragraph tags, etc... 
  997. remove_all_filters( 'bbp_get_reply_content' ); 
  998. remove_all_filters( 'bbp_get_topic_title' ); 
  999.  
  1000. // Strip tags from text and setup mail data 
  1001. $topic_title = strip_tags( bbp_get_topic_title( $topic_id ) ); 
  1002. $reply_content = strip_tags( bbp_get_reply_content( $reply_id ) ); 
  1003. $reply_url = bbp_get_reply_url( $reply_id ); 
  1004. $blog_name = wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES ); 
  1005. $do_not_reply = '<noreply@' . ltrim( get_home_url(), '^(http|https)://' ) . '>'; 
  1006.  
  1007. // For plugins to filter messages per reply/topic/user 
  1008. $message = sprintf( __( '%1$s wrote: 
  1009.  
  1010. %2$s 
  1011.  
  1012. Post Link: %3$s 
  1013.  
  1014. ----------- 
  1015.  
  1016. You are receiving this email because you subscribed to a forum topic. 
  1017.  
  1018. Login and visit the topic to unsubscribe from these emails.', 'bbpress' ),  
  1019.  
  1020. $reply_author_name,  
  1021. $reply_content,  
  1022. $reply_url 
  1023. ); 
  1024.  
  1025. $message = apply_filters( 'bbp_subscription_mail_message', $message, $reply_id, $topic_id ); 
  1026. if ( empty( $message ) ) { 
  1027. return; 
  1028.  
  1029. // For plugins to filter titles per reply/topic/user 
  1030. $subject = apply_filters( 'bbp_subscription_mail_title', '[' . $blog_name . '] ' . $topic_title, $reply_id, $topic_id ); 
  1031. if ( empty( $subject ) ) { 
  1032. return; 
  1033.  
  1034. /** Users *****************************************************************/ 
  1035.  
  1036. // Array to hold BCC's 
  1037. $headers = array(); 
  1038.  
  1039. // Setup the From header 
  1040. $headers[] = 'From: ' . get_bloginfo( 'name' ) . ' ' . $do_not_reply; 
  1041.  
  1042. // Get topic subscribers and bail if empty 
  1043. $user_ids = bbp_get_topic_subscribers( $topic_id, true ); 
  1044. if ( empty( $user_ids ) ) { 
  1045. return false; 
  1046.  
  1047. // Loop through users 
  1048. foreach ( (array) $user_ids as $user_id ) { 
  1049.  
  1050. // Don't send notifications to the person who made the post 
  1051. if ( !empty( $reply_author ) && (int) $user_id === (int) $reply_author ) { 
  1052. continue; 
  1053.  
  1054. // Get email address of subscribed user 
  1055. $headers[] = 'Bcc: ' . get_userdata( $user_id )->user_email; 
  1056.  
  1057. /** Send it ***************************************************************/ 
  1058.  
  1059. // Custom headers 
  1060. $headers = apply_filters( 'bbp_subscription_mail_headers', $headers ); 
  1061.  
  1062. do_action( 'bbp_pre_notify_subscribers', $reply_id, $topic_id, $user_ids ); 
  1063.  
  1064. // Send notification email 
  1065. wp_mail( $do_not_reply, $subject, $message, $headers ); 
  1066.  
  1067. do_action( 'bbp_post_notify_subscribers', $reply_id, $topic_id, $user_ids ); 
  1068.  
  1069. return true; 
  1070.  
  1071. /** 
  1072. * Sends notification emails for new topics to subscribed forums 
  1073. * 
  1074. * Gets new post's ID and check if there are subscribed users to that forum, and 
  1075. * if there are, send notifications 
  1076. * 
  1077. * Note: in bbPress 2.6, we've moved away from 1 email per subscriber to 1 email 
  1078. * with everyone BCC'd. This may have negative repercussions for email services 
  1079. * that limit the number of addresses in a BCC field (often to around 500.) In 
  1080. * those cases, we recommend unhooking this function and creating your own 
  1081. * custom emailer script. 
  1082. *  
  1083. * @since bbPress (r5156) 
  1084. * 
  1085. * @param int $topic_id ID of the newly made reply 
  1086. * @uses bbp_is_subscriptions_active() To check if the subscriptions are active 
  1087. * @uses bbp_get_topic_id() To validate the topic ID 
  1088. * @uses bbp_get_forum_id() To validate the forum ID 
  1089. * @uses bbp_is_topic_published() To make sure the topic is published 
  1090. * @uses bbp_get_forum_subscribers() To get the forum subscribers 
  1091. * @uses bbp_get_topic_author_display_name() To get the topic author's display name 
  1092. * @uses do_action() Calls 'bbp_pre_notify_forum_subscribers' with the topic id,  
  1093. * forum id and user id 
  1094. * @uses apply_filters() Calls 'bbp_forum_subscription_mail_message' with the 
  1095. * message, topic id, forum id and user id 
  1096. * @uses apply_filters() Calls 'bbp_forum_subscription_mail_title' with the 
  1097. * topic title, topic id, forum id and user id 
  1098. * @uses apply_filters() Calls 'bbp_forum_subscription_mail_headers' 
  1099. * @uses get_userdata() To get the user data 
  1100. * @uses wp_mail() To send the mail 
  1101. * @uses do_action() Calls 'bbp_post_notify_forum_subscribers' with the topic,  
  1102. * id, forum id and user id 
  1103. * @return bool True on success, false on failure 
  1104. */ 
  1105. function bbp_notify_forum_subscribers( $topic_id = 0, $forum_id = 0, $anonymous_data = false, $topic_author = 0 ) { 
  1106.  
  1107. // Bail if subscriptions are turned off 
  1108. if ( !bbp_is_subscriptions_active() ) { 
  1109. return false; 
  1110.  
  1111. /** Validation ************************************************************/ 
  1112.  
  1113. $topic_id = bbp_get_topic_id( $topic_id ); 
  1114. $forum_id = bbp_get_forum_id( $forum_id ); 
  1115.  
  1116. /** Topic *****************************************************************/ 
  1117.  
  1118. // Bail if topic is not published 
  1119. if ( ! bbp_is_topic_published( $topic_id ) ) { 
  1120. return false; 
  1121.  
  1122. // Poster name 
  1123. $topic_author_name = bbp_get_topic_author_display_name( $topic_id ); 
  1124.  
  1125. /** Mail ******************************************************************/ 
  1126.  
  1127. // Remove filters from reply content and topic title to prevent content 
  1128. // from being encoded with HTML entities, wrapped in paragraph tags, etc... 
  1129. remove_all_filters( 'bbp_get_topic_content' ); 
  1130. remove_all_filters( 'bbp_get_topic_title' ); 
  1131.  
  1132. // Strip tags from text and setup mail data 
  1133. $topic_title = strip_tags( bbp_get_topic_title( $topic_id ) ); 
  1134. $topic_content = strip_tags( bbp_get_topic_content( $topic_id ) ); 
  1135. $topic_url = get_permalink( $topic_id ); 
  1136. $blog_name = wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES ); 
  1137. $do_not_reply = '<noreply@' . ltrim( get_home_url(), '^(http|https)://' ) . '>'; 
  1138.  
  1139. // For plugins to filter messages per reply/topic/user 
  1140. $message = sprintf( __( '%1$s wrote: 
  1141.  
  1142. %2$s 
  1143.  
  1144. Topic Link: %3$s 
  1145.  
  1146. ----------- 
  1147.  
  1148. You are receiving this email because you subscribed to a forum. 
  1149.  
  1150. Login and visit the topic to unsubscribe from these emails.', 'bbpress' ),  
  1151.  
  1152. $topic_author_name,  
  1153. $topic_content,  
  1154. $topic_url 
  1155. ); 
  1156.  
  1157. $message = apply_filters( 'bbp_forum_subscription_mail_message', $message, $topic_id, $forum_id, $user_id ); 
  1158. if ( empty( $message ) ) { 
  1159. return; 
  1160.  
  1161. // For plugins to filter titles per reply/topic/user 
  1162. $subject = apply_filters( 'bbp_forum_subscription_mail_title', '[' . $blog_name . '] ' . $topic_title, $topic_id, $forum_id, $user_id ); 
  1163. if ( empty( $subject ) ) { 
  1164. return; 
  1165.  
  1166. /** User ******************************************************************/ 
  1167.  
  1168. // Array to hold BCC's 
  1169. $headers = array(); 
  1170.  
  1171. // Setup the From header 
  1172. $headers[] = 'From: ' . get_bloginfo( 'name' ) . ' ' . $do_not_reply; 
  1173.  
  1174. // Get topic subscribers and bail if empty 
  1175. $user_ids = bbp_get_forum_subscribers( $forum_id, true ); 
  1176. if ( empty( $user_ids ) ) { 
  1177. return false; 
  1178.  
  1179. // Loop through users 
  1180. foreach ( (array) $user_ids as $user_id ) { 
  1181.  
  1182. // Don't send notifications to the person who made the post 
  1183. if ( !empty( $topic_author ) && (int) $user_id === (int) $topic_author ) { 
  1184. continue; 
  1185.  
  1186. // Get email address of subscribed user 
  1187. $headers[] = 'Bcc: ' . get_userdata( $user_id )->user_email; 
  1188.  
  1189. /** Send it ***************************************************************/ 
  1190.  
  1191. // Custom headers 
  1192. $headers = apply_filters( 'bbp_subscription_mail_headers', $headers ); 
  1193.  
  1194. do_action( 'bbp_pre_notify_forum_subscribers', $topic_id, $forum_id, $user_ids ); 
  1195.  
  1196. // Send notification email 
  1197. wp_mail( $do_not_reply, $subject, $message, $headers ); 
  1198.  
  1199. do_action( 'bbp_post_notify_forum_subscribers', $topic_id, $forum_id, $user_ids ); 
  1200.  
  1201. return true; 
  1202.  
  1203. /** Login *********************************************************************/ 
  1204.  
  1205. /** 
  1206. * Return a clean and reliable logout URL 
  1207. * 
  1208. * @param string $url URL 
  1209. * @param string $redirect_to Where to redirect to? 
  1210. * @uses add_query_arg() To add args to the url 
  1211. * @uses apply_filters() Calls 'bbp_logout_url' with the url and redirect to 
  1212. * @return string The url 
  1213. */ 
  1214. function bbp_logout_url( $url = '', $redirect_to = '' ) { 
  1215.  
  1216. // Make sure we are directing somewhere 
  1217. if ( empty( $redirect_to ) && !strstr( $url, 'redirect_to' ) ) { 
  1218.  
  1219. // Rejig the $redirect_to 
  1220. if ( !isset( $_SERVER['REDIRECT_URL'] ) || ( $redirect_to !== home_url( $_SERVER['REDIRECT_URL'] ) ) ) { 
  1221. $redirect_to = isset( $_SERVER['HTTP_REFERER'] ) ? $_SERVER['HTTP_REFERER'] : ''; 
  1222.  
  1223. $redirect_to = ( is_ssl() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; 
  1224.  
  1225. // Sanitize $redirect_to and add it to full $url 
  1226. $redirect_to = add_query_arg( array( 'loggedout' => 'true' ), esc_url( $redirect_to ) ); 
  1227. $url = add_query_arg( array( 'redirect_to' => urlencode( $redirect_to ) ), $url ); 
  1228.  
  1229. // Filter and return 
  1230. return apply_filters( 'bbp_logout_url', $url, $redirect_to ); 
  1231.  
  1232. /** Queries *******************************************************************/ 
  1233.  
  1234. /** 
  1235. * Merge user defined arguments into defaults array. 
  1236. * 
  1237. * This function is used throughout bbPress to allow for either a string or array 
  1238. * to be merged into another array. It is identical to wp_parse_args() except 
  1239. * it allows for arguments to be passively or aggressively filtered using the 
  1240. * optional $filter_key parameter. 
  1241. * 
  1242. * @since bbPress (r3839) 
  1243. * 
  1244. * @param string|array $args Value to merge with $defaults 
  1245. * @param array $defaults Array that serves as the defaults. 
  1246. * @param string $filter_key String to key the filters from 
  1247. * @return array Merged user defined values with defaults. 
  1248. */ 
  1249. function bbp_parse_args( $args, $defaults = array(), $filter_key = '' ) { 
  1250.  
  1251. // Setup a temporary array from $args 
  1252. if ( is_object( $args ) ) { 
  1253. $r = get_object_vars( $args ); 
  1254. } elseif ( is_array( $args ) ) { 
  1255. $r =& $args; 
  1256. } else { 
  1257. wp_parse_str( $args, $r ); 
  1258.  
  1259. // Passively filter the args before the parse 
  1260. if ( !empty( $filter_key ) ) { 
  1261. $r = apply_filters( 'bbp_before_' . $filter_key . '_parse_args', $r ); 
  1262.  
  1263. // Parse 
  1264. if ( is_array( $defaults ) && !empty( $defaults ) ) { 
  1265. $r = array_merge( $defaults, $r ); 
  1266.  
  1267. // Aggressively filter the args after the parse 
  1268. if ( !empty( $filter_key ) ) { 
  1269. $r = apply_filters( 'bbp_after_' . $filter_key . '_parse_args', $r ); 
  1270.  
  1271. // Return the parsed results 
  1272. return $r; 
  1273.  
  1274. /** 
  1275. * Adds ability to include or exclude specific post_parent ID's 
  1276. * 
  1277. * @since bbPress (r2996) 
  1278. * 
  1279. * @global DB $wpdb 
  1280. * @global WP $wp 
  1281. * @param string $where 
  1282. * @param WP_Query $object 
  1283. * @return string 
  1284. */ 
  1285. function bbp_query_post_parent__in( $where, $object = '' ) { 
  1286. global $wpdb, $wp; 
  1287.  
  1288. // Noop if WP core supports this already 
  1289. if ( in_array( 'post_parent__in', $wp->private_query_vars ) ) 
  1290. return $where; 
  1291.  
  1292. // Bail if no object passed 
  1293. if ( empty( $object ) ) 
  1294. return $where; 
  1295.  
  1296. // Only 1 post_parent so return $where 
  1297. if ( is_numeric( $object->query_vars['post_parent'] ) ) 
  1298. return $where; 
  1299.  
  1300. // Including specific post_parent's 
  1301. if ( ! empty( $object->query_vars['post_parent__in'] ) ) { 
  1302. $ids = implode( ', ', wp_parse_id_list( $object->query_vars['post_parent__in'] ) ); 
  1303. $where .= " AND {$wpdb->posts}.post_parent IN ($ids)"; 
  1304.  
  1305. // Excluding specific post_parent's 
  1306. } elseif ( ! empty( $object->query_vars['post_parent__not_in'] ) ) { 
  1307. $ids = implode( ', ', wp_parse_id_list( $object->query_vars['post_parent__not_in'] ) ); 
  1308. $where .= " AND {$wpdb->posts}.post_parent NOT IN ($ids)"; 
  1309.  
  1310. // Return possibly modified $where 
  1311. return $where; 
  1312.  
  1313. /** 
  1314. * Query the DB and get the last public post_id that has parent_id as post_parent 
  1315. * 
  1316. * @param int $parent_id Parent id 
  1317. * @param string $post_type Post type. Defaults to 'post' 
  1318. * @uses bbp_get_topic_post_type() To get the topic post type 
  1319. * @uses wp_cache_get() To check if there is a cache of the last child id 
  1320. * @uses wpdb::prepare() To prepare the query 
  1321. * @uses wpdb::get_var() To get the result of the query in a variable 
  1322. * @uses wp_cache_set() To set the cache for future use 
  1323. * @uses apply_filters() Calls 'bbp_get_public_child_last_id' with the child 
  1324. * id, parent id and post type 
  1325. * @return int The last active post_id 
  1326. */ 
  1327. function bbp_get_public_child_last_id( $parent_id = 0, $post_type = 'post' ) { 
  1328. global $wpdb; 
  1329.  
  1330. // Bail if nothing passed 
  1331. if ( empty( $parent_id ) ) 
  1332. return false; 
  1333.  
  1334. // The ID of the cached query 
  1335. $cache_id = 'bbp_parent_' . $parent_id . '_type_' . $post_type . '_child_last_id'; 
  1336.  
  1337. // Check for cache and set if needed 
  1338. $child_id = wp_cache_get( $cache_id, 'bbpress_posts' ); 
  1339. if ( false === $child_id ) { 
  1340. $post_status = array( bbp_get_public_status_id() ); 
  1341.  
  1342. // Add closed status if topic post type 
  1343. if ( $post_type === bbp_get_topic_post_type() ) { 
  1344. $post_status[] = bbp_get_closed_status_id(); 
  1345.  
  1346. // Join post statuses together 
  1347. $post_status = "'" . implode( "', '", $post_status ) . "'"; 
  1348.  
  1349. $child_id = $wpdb->get_var( $wpdb->prepare( "SELECT ID FROM {$wpdb->posts} WHERE post_parent = %d AND post_status IN ( {$post_status} ) AND post_type = '%s' ORDER BY ID DESC LIMIT 1;", $parent_id, $post_type ) ); 
  1350. wp_cache_set( $cache_id, $child_id, 'bbpress_posts' ); 
  1351.  
  1352. // Filter and return 
  1353. return apply_filters( 'bbp_get_public_child_last_id', (int) $child_id, $parent_id, $post_type ); 
  1354.  
  1355. /** 
  1356. * Query the DB and get a count of public children 
  1357. * 
  1358. * @param int $parent_id Parent id 
  1359. * @param string $post_type Post type. Defaults to 'post' 
  1360. * @uses bbp_get_topic_post_type() To get the topic post type 
  1361. * @uses wp_cache_get() To check if there is a cache of the children count 
  1362. * @uses wpdb::prepare() To prepare the query 
  1363. * @uses wpdb::get_var() To get the result of the query in a variable 
  1364. * @uses wp_cache_set() To set the cache for future use 
  1365. * @uses apply_filters() Calls 'bbp_get_public_child_count' with the child 
  1366. * count, parent id and post type 
  1367. * @return int The number of children 
  1368. */ 
  1369. function bbp_get_public_child_count( $parent_id = 0, $post_type = 'post' ) { 
  1370. global $wpdb; 
  1371.  
  1372. // Bail if nothing passed 
  1373. if ( empty( $parent_id ) ) 
  1374. return false; 
  1375.  
  1376. // The ID of the cached query 
  1377. $cache_id = 'bbp_parent_' . $parent_id . '_type_' . $post_type . '_child_count'; 
  1378.  
  1379. // Check for cache and set if needed 
  1380. $child_count = wp_cache_get( $cache_id, 'bbpress_posts' ); 
  1381. if ( false === $child_count ) { 
  1382. $post_status = array( bbp_get_public_status_id() ); 
  1383.  
  1384. // Add closed status if topic post type 
  1385. if ( $post_type === bbp_get_topic_post_type() ) { 
  1386. $post_status[] = bbp_get_closed_status_id(); 
  1387.  
  1388. // Join post statuses together 
  1389. $post_status = "'" . implode( "', '", $post_status ) . "'"; 
  1390.  
  1391. $child_count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(ID) FROM {$wpdb->posts} WHERE post_parent = %d AND post_status IN ( {$post_status} ) AND post_type = '%s';", $parent_id, $post_type ) ); 
  1392. wp_cache_set( $cache_id, $child_count, 'bbpress_posts' ); 
  1393.  
  1394. // Filter and return 
  1395. return apply_filters( 'bbp_get_public_child_count', (int) $child_count, $parent_id, $post_type ); 
  1396.  
  1397. /** 
  1398. * Query the DB and get a the child id's of public children 
  1399. * 
  1400. * @param int $parent_id Parent id 
  1401. * @param string $post_type Post type. Defaults to 'post' 
  1402. * @uses bbp_get_topic_post_type() To get the topic post type 
  1403. * @uses wp_cache_get() To check if there is a cache of the children 
  1404. * @uses wpdb::prepare() To prepare the query 
  1405. * @uses wpdb::get_col() To get the result of the query in an array 
  1406. * @uses wp_cache_set() To set the cache for future use 
  1407. * @uses apply_filters() Calls 'bbp_get_public_child_ids' with the child ids,  
  1408. * parent id and post type 
  1409. * @return array The array of children 
  1410. */ 
  1411. function bbp_get_public_child_ids( $parent_id = 0, $post_type = 'post' ) { 
  1412. global $wpdb; 
  1413.  
  1414. // Bail if nothing passed 
  1415. if ( empty( $parent_id ) ) 
  1416. return false; 
  1417.  
  1418. // The ID of the cached query 
  1419. $cache_id = 'bbp_parent_public_' . $parent_id . '_type_' . $post_type . '_child_ids'; 
  1420.  
  1421. // Check for cache and set if needed 
  1422. $child_ids = wp_cache_get( $cache_id, 'bbpress_posts' ); 
  1423. if ( false === $child_ids ) { 
  1424. $post_status = array( bbp_get_public_status_id() ); 
  1425.  
  1426. // Add closed status if topic post type 
  1427. if ( $post_type === bbp_get_topic_post_type() ) { 
  1428. $post_status[] = bbp_get_closed_status_id(); 
  1429.  
  1430. // Join post statuses together 
  1431. $post_status = "'" . implode( "', '", $post_status ) . "'"; 
  1432.  
  1433. $child_ids = $wpdb->get_col( $wpdb->prepare( "SELECT ID FROM {$wpdb->posts} WHERE post_parent = %d AND post_status IN ( {$post_status} ) AND post_type = '%s' ORDER BY ID DESC;", $parent_id, $post_type ) ); 
  1434. wp_cache_set( $cache_id, $child_ids, 'bbpress_posts' ); 
  1435.  
  1436. // Filter and return 
  1437. return apply_filters( 'bbp_get_public_child_ids', $child_ids, $parent_id, $post_type ); 
  1438.  
  1439. /** 
  1440. * Query the DB and get a the child id's of all children 
  1441. * 
  1442. * @param int $parent_id Parent id 
  1443. * @param string $post_type Post type. Defaults to 'post' 
  1444. * @uses bbp_get_topic_post_type() To get the topic post type 
  1445. * @uses wp_cache_get() To check if there is a cache of the children 
  1446. * @uses wpdb::prepare() To prepare the query 
  1447. * @uses wpdb::get_col() To get the result of the query in an array 
  1448. * @uses wp_cache_set() To set the cache for future use 
  1449. * @uses apply_filters() Calls 'bbp_get_public_child_ids' with the child ids,  
  1450. * parent id and post type 
  1451. * @return array The array of children 
  1452. */ 
  1453. function bbp_get_all_child_ids( $parent_id = 0, $post_type = 'post' ) { 
  1454. global $wpdb; 
  1455.  
  1456. // Bail if nothing passed 
  1457. if ( empty( $parent_id ) ) 
  1458. return false; 
  1459.  
  1460. // The ID of the cached query 
  1461. $cache_id = 'bbp_parent_all_' . $parent_id . '_type_' . $post_type . '_child_ids'; 
  1462.  
  1463. // Check for cache and set if needed 
  1464. $child_ids = wp_cache_get( $cache_id, 'bbpress_posts' ); 
  1465. if ( false === $child_ids ) { 
  1466. $post_status = array( bbp_get_public_status_id() ); 
  1467.  
  1468. // Extra post statuses based on post type 
  1469. switch ( $post_type ) { 
  1470.  
  1471. // Forum 
  1472. case bbp_get_forum_post_type() : 
  1473. $post_status[] = bbp_get_private_status_id(); 
  1474. $post_status[] = bbp_get_hidden_status_id(); 
  1475. break; 
  1476.  
  1477. // Topic 
  1478. case bbp_get_topic_post_type() : 
  1479. $post_status[] = bbp_get_closed_status_id(); 
  1480. $post_status[] = bbp_get_trash_status_id(); 
  1481. $post_status[] = bbp_get_spam_status_id(); 
  1482. break; 
  1483.  
  1484. // Reply 
  1485. case bbp_get_reply_post_type() : 
  1486. $post_status[] = bbp_get_trash_status_id(); 
  1487. $post_status[] = bbp_get_spam_status_id(); 
  1488. break; 
  1489.  
  1490. // Join post statuses together 
  1491. $post_status = "'" . implode( "', '", $post_status ) . "'"; 
  1492.  
  1493. $child_ids = $wpdb->get_col( $wpdb->prepare( "SELECT ID FROM {$wpdb->posts} WHERE post_parent = %d AND post_status IN ( {$post_status} ) AND post_type = '%s' ORDER BY ID DESC;", $parent_id, $post_type ) ); 
  1494. wp_cache_set( $cache_id, $child_ids, 'bbpress_posts' ); 
  1495.  
  1496. // Filter and return 
  1497. return apply_filters( 'bbp_get_all_child_ids', $child_ids, (int) $parent_id, $post_type ); 
  1498.  
  1499. /** Globals *******************************************************************/ 
  1500.  
  1501. /** 
  1502. * Get the unfiltered value of a global $post's key 
  1503. * 
  1504. * Used most frequently when editing a forum/topic/reply 
  1505. * 
  1506. * @since bbPress (r3694) 
  1507. * 
  1508. * @global WP_Query $post 
  1509. * @param string $field Name of the key 
  1510. * @param string $context How to sanitize - raw|edit|db|display|attribute|js 
  1511. * @return string Field value 
  1512. */ 
  1513. function bbp_get_global_post_field( $field = 'ID', $context = 'edit' ) { 
  1514. global $post; 
  1515.  
  1516. $retval = isset( $post->$field ) ? $post->$field : ''; 
  1517. $retval = sanitize_post_field( $field, $retval, $post->ID, $context ); 
  1518.  
  1519. return apply_filters( 'bbp_get_global_post_field', $retval, $post ); 
  1520.  
  1521. /** Nonces ********************************************************************/ 
  1522.  
  1523. /** 
  1524. * Makes sure the user requested an action from another page on this site. 
  1525. * 
  1526. * To avoid security exploits within the theme. 
  1527. * 
  1528. * @since bbPress (r4022) 
  1529. * 
  1530. * @uses do_action() Calls 'bbp_check_referer' on $action. 
  1531. * @param string $action Action nonce 
  1532. * @param string $query_arg where to look for nonce in $_REQUEST 
  1533. */ 
  1534. function bbp_verify_nonce_request( $action = '', $query_arg = '_wpnonce' ) { 
  1535.  
  1536. /** Home URL **************************************************************/ 
  1537.  
  1538. // Parse home_url() into pieces to remove query-strings, strange characters,  
  1539. // and other funny things that plugins might to do to it. 
  1540. $parsed_home = parse_url( home_url( '/', ( is_ssl() ? 'https' : 'http' ) ) ); 
  1541.  
  1542. // Maybe include the port, if it's included 
  1543. if ( isset( $parsed_home['port'] ) ) { 
  1544. $parsed_host = $parsed_home['host'] . ':' . $parsed_home['port']; 
  1545. } else { 
  1546. $parsed_host = $parsed_home['host']; 
  1547.  
  1548. // Set the home URL for use in comparisons 
  1549. $home_url = trim( strtolower( $parsed_home['scheme'] . '://' . $parsed_host . $parsed_home['path'] ), '/' ); 
  1550.  
  1551. /** Requested URL *********************************************************/ 
  1552.  
  1553. // Maybe include the port, if it's included in home_url() 
  1554. if ( isset( $parsed_home['port'] ) ) { 
  1555. $request_host = $_SERVER['HTTP_HOST'] . ':' . $_SERVER['SERVER_PORT']; 
  1556. } else { 
  1557. $request_host = $_SERVER['HTTP_HOST']; 
  1558.  
  1559. // Build the currently requested URL 
  1560. $scheme = is_ssl() ? 'https://' : 'http://'; 
  1561. $requested_url = strtolower( $scheme . $request_host . $_SERVER['REQUEST_URI'] ); 
  1562.  
  1563. /** Look for match ********************************************************/ 
  1564.  
  1565. // Filter the requested URL, for configurations like reverse proxying 
  1566. $matched_url = apply_filters( 'bbp_verify_nonce_request_url', $requested_url ); 
  1567.  
  1568. // Check the nonce 
  1569. $result = isset( $_REQUEST[$query_arg] ) ? wp_verify_nonce( $_REQUEST[$query_arg], $action ) : false; 
  1570.  
  1571. // Nonce check failed 
  1572. if ( empty( $result ) || empty( $action ) || ( strpos( $matched_url, $home_url ) !== 0 ) ) { 
  1573. $result = false; 
  1574.  
  1575. // Do extra things 
  1576. do_action( 'bbp_verify_nonce_request', $action, $result ); 
  1577.  
  1578. return $result; 
  1579.  
  1580. /** Feeds *********************************************************************/ 
  1581.  
  1582. /** 
  1583. * This function is hooked into the WordPress 'request' action and is 
  1584. * responsible for sniffing out the query vars and serving up RSS2 feeds if 
  1585. * the stars align and the user has requested a feed of any bbPress type. 
  1586. * 
  1587. * @since bbPress (r3171) 
  1588. * 
  1589. * @param array $query_vars 
  1590. * @return array 
  1591. */ 
  1592. function bbp_request_feed_trap( $query_vars = array() ) { 
  1593.  
  1594. // Looking at a feed 
  1595. if ( isset( $query_vars['feed'] ) ) { 
  1596.  
  1597. // Forum/Topic/Reply Feed 
  1598. if ( isset( $query_vars['post_type'] ) ) { 
  1599.  
  1600. // Matched post type 
  1601. $post_type = false; 
  1602.  
  1603. // Post types to check 
  1604. $post_types = array( 
  1605. bbp_get_forum_post_type(),  
  1606. bbp_get_topic_post_type(),  
  1607. bbp_get_reply_post_type() 
  1608. ); 
  1609.  
  1610. // Cast query vars as array outside of foreach loop 
  1611. $qv_array = (array) $query_vars['post_type']; 
  1612.  
  1613. // Check if this query is for a bbPress post type 
  1614. foreach ( $post_types as $bbp_pt ) { 
  1615. if ( in_array( $bbp_pt, $qv_array, true ) ) { 
  1616. $post_type = $bbp_pt; 
  1617. break; 
  1618.  
  1619. // Looking at a bbPress post type 
  1620. if ( ! empty( $post_type ) ) { 
  1621.  
  1622. // Supported select query vars 
  1623. $select_query_vars = array( 
  1624. 'p' => false,  
  1625. 'name' => false,  
  1626. $post_type => false,  
  1627. ); 
  1628.  
  1629. // Setup matched variables to select 
  1630. foreach ( $query_vars as $key => $value ) { 
  1631. if ( isset( $select_query_vars[$key] ) ) { 
  1632. $select_query_vars[$key] = $value; 
  1633.  
  1634. // Remove any empties 
  1635. $select_query_vars = array_filter( $select_query_vars ); 
  1636.  
  1637. // What bbPress post type are we looking for feeds on? 
  1638. switch ( $post_type ) { 
  1639.  
  1640. // Forum 
  1641. case bbp_get_forum_post_type() : 
  1642.  
  1643. // Define local variable(s) 
  1644. $meta_query = array(); 
  1645.  
  1646. // Single forum 
  1647. if ( !empty( $select_query_vars ) ) { 
  1648.  
  1649. // Load up our own query 
  1650. query_posts( array_merge( array( 
  1651. 'post_type' => bbp_get_forum_post_type(),  
  1652. 'feed' => true 
  1653. ), $select_query_vars ) ); 
  1654.  
  1655. // Restrict to specific forum ID 
  1656. $meta_query = array( array( 
  1657. 'key' => '_bbp_forum_id',  
  1658. 'value' => bbp_get_forum_id(),  
  1659. 'type' => 'numeric',  
  1660. 'compare' => '=' 
  1661. ) ); 
  1662.  
  1663. // Only forum replies 
  1664. if ( !empty( $_GET['type'] ) && ( bbp_get_reply_post_type() === $_GET['type'] ) ) { 
  1665.  
  1666. // The query 
  1667. $the_query = array( 
  1668. 'author' => 0,  
  1669. 'feed' => true,  
  1670. 'post_type' => bbp_get_reply_post_type(),  
  1671. 'post_parent' => 'any',  
  1672. 'post_status' => array( bbp_get_public_status_id(), bbp_get_closed_status_id() ),  
  1673. 'posts_per_page' => bbp_get_replies_per_rss_page(),  
  1674. 'order' => 'DESC',  
  1675. 'meta_query' => $meta_query 
  1676. ); 
  1677.  
  1678. // Output the feed 
  1679. bbp_display_replies_feed_rss2( $the_query ); 
  1680.  
  1681. // Only forum topics 
  1682. } elseif ( !empty( $_GET['type'] ) && ( bbp_get_topic_post_type() === $_GET['type'] ) ) { 
  1683.  
  1684. // The query 
  1685. $the_query = array( 
  1686. 'author' => 0,  
  1687. 'feed' => true,  
  1688. 'post_type' => bbp_get_topic_post_type(),  
  1689. 'post_parent' => bbp_get_forum_id(),  
  1690. 'post_status' => array( bbp_get_public_status_id(), bbp_get_closed_status_id() ),  
  1691. 'posts_per_page' => bbp_get_topics_per_rss_page(),  
  1692. 'order' => 'DESC' 
  1693. ); 
  1694.  
  1695. // Output the feed 
  1696. bbp_display_topics_feed_rss2( $the_query ); 
  1697.  
  1698. // All forum topics and replies 
  1699. } else { 
  1700.  
  1701. // Exclude private/hidden forums if not looking at single 
  1702. if ( empty( $select_query_vars ) ) 
  1703. $meta_query = array( bbp_exclude_forum_ids( 'meta_query' ) ); 
  1704.  
  1705. // The query 
  1706. $the_query = array( 
  1707. 'author' => 0,  
  1708. 'feed' => true,  
  1709. 'post_type' => array( bbp_get_reply_post_type(), bbp_get_topic_post_type() ),  
  1710. 'post_parent' => 'any',  
  1711. 'post_status' => array( bbp_get_public_status_id(), bbp_get_closed_status_id() ),  
  1712. 'posts_per_page' => bbp_get_replies_per_rss_page(),  
  1713. 'order' => 'DESC',  
  1714. 'meta_query' => $meta_query 
  1715. ); 
  1716.  
  1717. // Output the feed 
  1718. bbp_display_replies_feed_rss2( $the_query ); 
  1719.  
  1720. break; 
  1721.  
  1722. // Topic feed - Show replies 
  1723. case bbp_get_topic_post_type() : 
  1724.  
  1725. // Single topic 
  1726. if ( !empty( $select_query_vars ) ) { 
  1727.  
  1728. // Load up our own query 
  1729. query_posts( array_merge( array( 
  1730. 'post_type' => bbp_get_topic_post_type(),  
  1731. 'feed' => true 
  1732. ), $select_query_vars ) ); 
  1733.  
  1734. // Output the feed 
  1735. bbp_display_replies_feed_rss2( array( 'feed' => true ) ); 
  1736.  
  1737. // All topics 
  1738. } else { 
  1739.  
  1740. // The query 
  1741. $the_query = array( 
  1742. 'author' => 0,  
  1743. 'feed' => true,  
  1744. 'post_parent' => 'any',  
  1745. 'posts_per_page' => bbp_get_topics_per_rss_page(),  
  1746. 'show_stickies' => false 
  1747. ); 
  1748.  
  1749. // Output the feed 
  1750. bbp_display_topics_feed_rss2( $the_query ); 
  1751.  
  1752. break; 
  1753.  
  1754. // Replies 
  1755. case bbp_get_reply_post_type() : 
  1756.  
  1757. // The query 
  1758. $the_query = array( 
  1759. 'posts_per_page' => bbp_get_replies_per_rss_page(),  
  1760. 'meta_query' => array( array( ) ),  
  1761. 'feed' => true 
  1762. ); 
  1763.  
  1764. // All replies 
  1765. if ( empty( $select_query_vars ) ) { 
  1766. bbp_display_replies_feed_rss2( $the_query ); 
  1767.  
  1768. break; 
  1769.  
  1770. // Single Topic Vview 
  1771. } elseif ( isset( $query_vars[ bbp_get_view_rewrite_id() ] ) ) { 
  1772.  
  1773. // Get the view 
  1774. $view = $query_vars[ bbp_get_view_rewrite_id() ]; 
  1775.  
  1776. // We have a view to display a feed 
  1777. if ( !empty( $view ) ) { 
  1778.  
  1779. // Get the view query 
  1780. $the_query = bbp_get_view_query_args( $view ); 
  1781.  
  1782. // Output the feed 
  1783. bbp_display_topics_feed_rss2( $the_query ); 
  1784.  
  1785. // @todo User profile feeds 
  1786.  
  1787. // No feed so continue on 
  1788. return $query_vars; 
  1789.  
  1790. /** Templates ******************************************************************/ 
  1791.  
  1792. /** 
  1793. * Used to guess if page exists at requested path 
  1794. * 
  1795. * @since bbPress (r3304) 
  1796. * 
  1797. * @uses get_option() To see if pretty permalinks are enabled 
  1798. * @uses get_page_by_path() To see if page exists at path 
  1799. * 
  1800. * @param string $path 
  1801. * @return mixed False if no page, Page object if true 
  1802. */ 
  1803. function bbp_get_page_by_path( $path = '' ) { 
  1804.  
  1805. // Default to false 
  1806. $retval = false; 
  1807.  
  1808. // Path is not empty 
  1809. if ( !empty( $path ) ) { 
  1810.  
  1811. // Pretty permalinks are on so path might exist 
  1812. if ( get_option( 'permalink_structure' ) ) { 
  1813. $retval = get_page_by_path( $path ); 
  1814.  
  1815. return apply_filters( 'bbp_get_page_by_path', $retval, $path ); 
  1816.  
  1817. /** 
  1818. * Sets the 404 status. 
  1819. * 
  1820. * Used primarily with topics/replies inside hidden forums. 
  1821. * 
  1822. * @since bbPress (r3051) 
  1823. * 
  1824. * @global WP_Query $wp_query 
  1825. * @uses WP_Query::set_404() 
  1826. */ 
  1827. function bbp_set_404() { 
  1828. global $wp_query; 
  1829.  
  1830. if ( ! isset( $wp_query ) ) { 
  1831. _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.', 'bbpress' ), '3.1' ); 
  1832. return false; 
  1833.  
  1834. $wp_query->set_404(); 
.