/bp-core/bp-core-filters.php

  1. <?php 
  2. /** 
  3. * BuddyPress Filters. 
  4. * 
  5. * This file contains the filters that are used throughout BuddyPress. They are 
  6. * consolidated here to make searching for them easier, and to help developers 
  7. * understand at a glance the order in which things occur. 
  8. * 
  9. * There are a few common places that additional filters can currently be found. 
  10. * 
  11. * - BuddyPress: In {@link BuddyPress::setup_actions()} in buddypress.php 
  12. * - Component: In {@link BP_Component::setup_actions()} in 
  13. * bp-core/bp-core-component.php 
  14. * - Admin: More in {@link BP_Admin::setup_actions()} in 
  15. * bp-core/bp-core-admin.php 
  16. * 
  17. * @package BuddyPress 
  18. * @subpackage Core 
  19. * @since 1.5.0 
  20. * 
  21. * @see bp-core-actions.php 
  22. */ 
  23.  
  24. // Exit if accessed directly. 
  25. defined( 'ABSPATH' ) || exit; 
  26.  
  27. /** 
  28. * Attach BuddyPress to WordPress. 
  29. * 
  30. * BuddyPress uses its own internal actions to help aid in third-party plugin 
  31. * development, and to limit the amount of potential future code changes when 
  32. * updates to WordPress core occur. 
  33. * 
  34. * These actions exist to create the concept of 'plugin dependencies'. They 
  35. * provide a safe way for plugins to execute code *only* when BuddyPress is 
  36. * installed and activated, without needing to do complicated guesswork. 
  37. * 
  38. * For more information on how this works, see the 'Plugin Dependency' section 
  39. * near the bottom of this file. 
  40. * 
  41. * v--WordPress Actions v--BuddyPress Sub-actions 
  42. */ 
  43. add_filter( 'request', 'bp_request', 10 ); 
  44. add_filter( 'template_include', 'bp_template_include', 10 ); 
  45. add_filter( 'login_redirect', 'bp_login_redirect', 10, 3 ); 
  46. add_filter( 'map_meta_cap', 'bp_map_meta_caps', 10, 4 ); 
  47.  
  48. // Add some filters to feedback messages. 
  49. add_filter( 'bp_core_render_message_content', 'wptexturize' ); 
  50. add_filter( 'bp_core_render_message_content', 'convert_smilies' ); 
  51. add_filter( 'bp_core_render_message_content', 'convert_chars' ); 
  52. add_filter( 'bp_core_render_message_content', 'wpautop' ); 
  53. add_filter( 'bp_core_render_message_content', 'shortcode_unautop' ); 
  54. add_filter( 'bp_core_render_message_content', 'wp_kses_data', 5 ); 
  55.  
  56. // Emails. 
  57. add_filter( 'bp_email_set_content_html', 'wp_filter_post_kses', 6 ); 
  58. add_filter( 'bp_email_set_content_html', 'stripslashes', 8 ); 
  59. add_filter( 'bp_email_set_content_plaintext', 'wp_strip_all_tags', 6 ); 
  60. add_filter( 'bp_email_set_subject', 'sanitize_text_field', 6 ); 
  61.  
  62. /** 
  63. * Template Compatibility. 
  64. * 
  65. * If you want to completely bypass this and manage your own custom BuddyPress 
  66. * template hierarchy, start here by removing this filter, then look at how 
  67. * bp_template_include() works and do something similar. :) 
  68. */ 
  69. add_filter( 'bp_template_include', 'bp_template_include_theme_supports', 2, 1 ); 
  70. add_filter( 'bp_template_include', 'bp_template_include_theme_compat', 4, 2 ); 
  71.  
  72. // Filter BuddyPress template locations. 
  73. add_filter( 'bp_get_template_stack', 'bp_add_template_stack_locations' ); 
  74.  
  75. // Turn comments off for BuddyPress pages. 
  76. add_filter( 'comments_open', 'bp_comments_open', 10, 2 ); 
  77.  
  78. // Prevent DB query for WP's main loop. 
  79. add_filter( 'posts_pre_query', 'bp_core_filter_wp_query', 10, 2 ); 
  80.  
  81. /** 
  82. * Prevent specific pages (eg 'Activate') from showing on page listings. 
  83. * 
  84. * @since 1.5.0 
  85. * 
  86. * @param array $pages List of excluded page IDs, as passed to the 
  87. * 'wp_list_pages_excludes' filter. 
  88. * @return array The exclude list, with BP's pages added. 
  89. */ 
  90. function bp_core_exclude_pages( $pages = array() ) { 
  91.  
  92. // Bail if not the root blog. 
  93. if ( ! bp_is_root_blog() ) 
  94. return $pages; 
  95.  
  96. $bp = buddypress(); 
  97.  
  98. if ( !empty( $bp->pages->activate ) ) 
  99. $pages[] = $bp->pages->activate->id; 
  100.  
  101. if ( !empty( $bp->pages->register ) ) 
  102. $pages[] = $bp->pages->register->id; 
  103.  
  104. if ( !empty( $bp->pages->forums ) && ( !bp_is_active( 'forums' ) || ( bp_is_active( 'forums' ) && bp_forums_has_directory() && !bp_forums_is_installed_correctly() ) ) ) 
  105. $pages[] = $bp->pages->forums->id; 
  106.  
  107. /** 
  108. * Filters specific pages that shouldn't show up on page listings. 
  109. * 
  110. * @since 1.5.0 
  111. * 
  112. * @param array $pages Array of pages to exclude. 
  113. */ 
  114. return apply_filters( 'bp_core_exclude_pages', $pages ); 
  115. add_filter( 'wp_list_pages_excludes', 'bp_core_exclude_pages' ); 
  116.  
  117. /** 
  118. * Prevent specific pages (eg 'Activate') from showing in the Pages meta box of the Menu Administration screen. 
  119. * 
  120. * @since 2.0.0 
  121. * 
  122. * @param object|null $object The post type object used in the meta box. 
  123. * @return object|null The $object, with a query argument to remove register and activate pages id. 
  124. */ 
  125. function bp_core_exclude_pages_from_nav_menu_admin( $object = null ) { 
  126.  
  127. // Bail if not the root blog. 
  128. if ( ! bp_is_root_blog() ) { 
  129. return $object; 
  130.  
  131. if ( 'page' != $object->name ) { 
  132. return $object; 
  133.  
  134. $bp = buddypress(); 
  135. $pages = array(); 
  136.  
  137. if ( ! empty( $bp->pages->activate ) ) { 
  138. $pages[] = $bp->pages->activate->id; 
  139.  
  140. if ( ! empty( $bp->pages->register ) ) { 
  141. $pages[] = $bp->pages->register->id; 
  142.  
  143. if ( ! empty( $pages ) ) { 
  144. $object->_default_query['post__not_in'] = $pages; 
  145.  
  146. return $object; 
  147. add_filter( 'nav_menu_meta_box_object', 'bp_core_exclude_pages_from_nav_menu_admin', 11, 1 ); 
  148.  
  149. /** 
  150. * Adds current page CSS classes to the parent BP page in a WP Page Menu. 
  151. * 
  152. * Because BuddyPress primarily uses virtual pages, we need a way to highlight 
  153. * the BP parent page during WP menu generation. This function checks the 
  154. * current BP component against the current page in the WP menu to see if we 
  155. * should highlight the WP page. 
  156. * 
  157. * @since 2.2.0 
  158. * 
  159. * @param array $retval CSS classes for the current menu page in the menu. 
  160. * @param WP_Post $page The page properties for the current menu item. 
  161. * @return array 
  162. */ 
  163. function bp_core_menu_highlight_parent_page( $retval, $page ) { 
  164. if ( ! is_buddypress() ) { 
  165. return $retval; 
  166.  
  167. $page_id = false; 
  168.  
  169. // Loop against all BP component pages. 
  170. foreach ( (array) buddypress()->pages as $component => $bp_page ) { 
  171. // Handles the majority of components. 
  172. if ( bp_is_current_component( $component ) ) { 
  173. $page_id = (int) $bp_page->id; 
  174.  
  175. // Stop if not on a user page. 
  176. if ( ! bp_is_user() && ! empty( $page_id ) ) { 
  177. break; 
  178.  
  179. // Members component requires an explicit check due to overlapping components. 
  180. if ( bp_is_user() && 'members' === $component ) { 
  181. $page_id = (int) $bp_page->id; 
  182. break; 
  183.  
  184. // Duplicate some logic from Walker_Page::start_el() to highlight menu items. 
  185. if ( ! empty( $page_id ) ) { 
  186. $_bp_page = get_post( $page_id ); 
  187. if ( in_array( $page->ID, $_bp_page->ancestors, true ) ) { 
  188. $retval[] = 'current_page_ancestor'; 
  189. if ( $page->ID === $page_id ) { 
  190. $retval[] = 'current_page_item'; 
  191. } elseif ( $_bp_page && $page->ID === $_bp_page->post_parent ) { 
  192. $retval[] = 'current_page_parent'; 
  193.  
  194. $retval = array_unique( $retval ); 
  195.  
  196. return $retval; 
  197. add_filter( 'page_css_class', 'bp_core_menu_highlight_parent_page', 10, 2 ); 
  198.  
  199. /** 
  200. * Adds current page CSS classes to the parent BP page in a WP Nav Menu. 
  201. * 
  202. * When {@link wp_nav_menu()} is used, this function helps to highlight the 
  203. * current BP parent page during nav menu generation. 
  204. * 
  205. * @since 2.2.0 
  206. * 
  207. * @param array $retval CSS classes for the current nav menu item in the menu. 
  208. * @param WP_Post $item The properties for the current nav menu item. 
  209. * @return array 
  210. */ 
  211. function bp_core_menu_highlight_nav_menu_item( $retval, $item ) { 
  212. // If we're not on a BP page or if the current nav item is not a page, stop! 
  213. if ( ! is_buddypress() || 'page' !== $item->object ) { 
  214. return $retval; 
  215.  
  216. // Get the WP page. 
  217. $page = get_post( $item->object_id ); 
  218.  
  219. // See if we should add our highlight CSS classes for the page. 
  220. $retval = bp_core_menu_highlight_parent_page( $retval, $page ); 
  221.  
  222. return $retval; 
  223. add_filter( 'nav_menu_css_class', 'bp_core_menu_highlight_nav_menu_item', 10, 2 ); 
  224.  
  225. /** 
  226. * Filter the blog post comments array and insert BuddyPress URLs for users. 
  227. * 
  228. * @since 1.2.0 
  229. * 
  230. * @param array $comments The array of comments supplied to the comments template. 
  231. * @param int $post_id The post ID. 
  232. * @return array $comments The modified comment array. 
  233. */ 
  234. function bp_core_filter_comments( $comments, $post_id ) { 
  235. global $wpdb; 
  236.  
  237. foreach( (array) $comments as $comment ) { 
  238. if ( $comment->user_id ) 
  239. $user_ids[] = $comment->user_id; 
  240.  
  241. if ( empty( $user_ids ) ) 
  242. return $comments; 
  243.  
  244. $user_ids = implode( ', ', wp_parse_id_list( $user_ids ) ); 
  245.  
  246. if ( !$userdata = $wpdb->get_results( "SELECT ID as user_id, user_login, user_nicename FROM {$wpdb->users} WHERE ID IN ({$user_ids})" ) ) 
  247. return $comments; 
  248.  
  249. foreach( (array) $userdata as $user ) 
  250. $users[$user->user_id] = bp_core_get_user_domain( $user->user_id, $user->user_nicename, $user->user_login ); 
  251.  
  252. foreach( (array) $comments as $i => $comment ) { 
  253. if ( !empty( $comment->user_id ) ) { 
  254. if ( !empty( $users[$comment->user_id] ) ) 
  255. $comments[$i]->comment_author_url = $users[$comment->user_id]; 
  256.  
  257. return $comments; 
  258. add_filter( 'comments_array', 'bp_core_filter_comments', 10, 2 ); 
  259.  
  260. /** 
  261. * When a user logs in, redirect him in a logical way. 
  262. * 
  263. * @since 1.2.0 
  264. * 
  265. * are redirected to on login. 
  266. * 
  267. * @param string $redirect_to The URL to be redirected to, sanitized in wp-login.php. 
  268. * @param string $redirect_to_raw The unsanitized redirect_to URL ($_REQUEST['redirect_to']). 
  269. * @param WP_User $user The WP_User object corresponding to a successfully 
  270. * logged-in user. Otherwise a WP_Error object. 
  271. * @return string The redirect URL. 
  272. */ 
  273. function bp_core_login_redirect( $redirect_to, $redirect_to_raw, $user ) { 
  274.  
  275. // Only modify the redirect if we're on the main BP blog. 
  276. if ( !bp_is_root_blog() ) { 
  277. return $redirect_to; 
  278.  
  279. // Only modify the redirect once the user is logged in. 
  280. if ( !is_a( $user, 'WP_User' ) ) { 
  281. return $redirect_to; 
  282.  
  283. /** 
  284. * Filters whether or not to redirect. 
  285. * 
  286. * Allows plugins to have finer grained control of redirect upon login. 
  287. * 
  288. * @since 1.6.0 
  289. * 
  290. * @param bool $value Whether or not to redirect. 
  291. * @param string $redirect_to Sanitized URL to be redirected to. 
  292. * @param string $redirect_to_raw Unsanitized URL to be redirected to. 
  293. * @param WP_User $user The WP_User object corresponding to a 
  294. * successfully logged in user. 
  295. */ 
  296. $maybe_redirect = apply_filters( 'bp_core_login_redirect', false, $redirect_to, $redirect_to_raw, $user ); 
  297. if ( false !== $maybe_redirect ) { 
  298. return $maybe_redirect; 
  299.  
  300. // If a 'redirect_to' parameter has been passed that contains 'wp-admin', verify that the 
  301. // logged-in user has any business to conduct in the Dashboard before allowing the 
  302. // redirect to go through. 
  303. if ( !empty( $redirect_to ) && ( false === strpos( $redirect_to, 'wp-admin' ) || user_can( $user, 'edit_posts' ) ) ) { 
  304. return $redirect_to; 
  305.  
  306. if ( false === strpos( wp_get_referer(), 'wp-login.php' ) && false === strpos( wp_get_referer(), 'activate' ) && empty( $_REQUEST['nr'] ) ) { 
  307. return wp_get_referer(); 
  308.  
  309. /** 
  310. * Filters the URL to redirect users to upon successful login. 
  311. * 
  312. * @since 1.9.0 
  313. * 
  314. * @param string $value URL to redirect to. 
  315. */ 
  316. return apply_filters( 'bp_core_login_redirect_to', bp_get_root_domain() ); 
  317. add_filter( 'bp_login_redirect', 'bp_core_login_redirect', 10, 3 ); 
  318.  
  319. /** 
  320. * Decode HTML entities for plain-text emails. 
  321. * 
  322. * @since 2.5.0 
  323. * 
  324. * @param string $retval Current email content. 
  325. * @param string $prop Email property to check against. 
  326. * @param string $transform Either 'raw' or 'replace-tokens'. 
  327. * @return string $retval Modified email content. 
  328. */ 
  329. function bp_email_plaintext_entity_decode( $retval, $prop, $transform ) { 
  330. switch ( $prop ) { 
  331. case 'content_plaintext' : 
  332. case 'subject' : 
  333. // Only decode if 'replace-tokens' is the current type. 
  334. if ( 'replace-tokens' === $transform ) { 
  335. return html_entity_decode( $retval, ENT_QUOTES ); 
  336. } else { 
  337. return $retval; 
  338. break; 
  339.  
  340. default : 
  341. return $retval; 
  342. break; 
  343. add_filter( 'bp_email_get_property', 'bp_email_plaintext_entity_decode', 10, 3 ); 
  344.  
  345. /** 
  346. * Replace the generated password in the welcome email with '[User Set]'. 
  347. * 
  348. * On a standard BP installation, users who register themselves also set their 
  349. * own passwords. Therefore there is no need for the insecure practice of 
  350. * emailing the plaintext password to the user in the welcome email. 
  351. * 
  352. * This filter will not fire when a user is registered by the site admin. 
  353. * 
  354. * @since 1.2.1 
  355. * 
  356. * @param string $welcome_email Complete email passed through WordPress. 
  357. * @return string Filtered $welcome_email with the password replaced 
  358. * by '[User Set]'. 
  359. */ 
  360. function bp_core_filter_user_welcome_email( $welcome_email ) { 
  361.  
  362. // Don't touch the email when a user is registered by the site admin. 
  363. if ( ( is_admin() || is_network_admin() ) && buddypress()->members->admin->signups_page != get_current_screen()->id ) { 
  364. return $welcome_email; 
  365.  
  366. if ( strpos( bp_get_requested_url(), 'wp-activate.php' ) !== false ) { 
  367. return $welcome_email; 
  368.  
  369. // Don't touch the email if we don't have a custom registration template. 
  370. if ( ! bp_has_custom_signup_page() ) { 
  371. return $welcome_email; 
  372.  
  373. // [User Set] Replaces 'PASSWORD' in welcome email; Represents value set by user 
  374. return str_replace( 'PASSWORD', __( '[User Set]', 'buddypress' ), $welcome_email ); 
  375. add_filter( 'update_welcome_user_email', 'bp_core_filter_user_welcome_email' ); 
  376.  
  377. /** 
  378. * Replace the generated password in the welcome email with '[User Set]'. 
  379. * 
  380. * On a standard BP installation, users who register themselves also set their 
  381. * own passwords. Therefore there is no need for the insecure practice of 
  382. * emailing the plaintext password to the user in the welcome email. 
  383. * 
  384. * This filter will not fire when a user is registered by the site admin. 
  385. * 
  386. * @since 1.2.1 
  387. * 
  388. * @param string $welcome_email Complete email passed through WordPress. 
  389. * @param int $blog_id ID of the blog user is joining. 
  390. * @param int $user_id ID of the user joining. 
  391. * @param string $password Password of user. 
  392. * @return string Filtered $welcome_email with $password replaced by '[User Set]'. 
  393. */ 
  394. function bp_core_filter_blog_welcome_email( $welcome_email, $blog_id, $user_id, $password ) { 
  395.  
  396. // Don't touch the email when a user is registered by the site admin. 
  397. if ( ( is_admin() || is_network_admin() ) && buddypress()->members->admin->signups_page != get_current_screen()->id ) { 
  398. return $welcome_email; 
  399.  
  400. // Don't touch the email if we don't have a custom registration template. 
  401. if ( ! bp_has_custom_signup_page() ) 
  402. return $welcome_email; 
  403.  
  404. // [User Set] Replaces $password in welcome email; Represents value set by user 
  405. return str_replace( $password, __( '[User Set]', 'buddypress' ), $welcome_email ); 
  406. add_filter( 'update_welcome_email', 'bp_core_filter_blog_welcome_email', 10, 4 ); 
  407.  
  408. /** 
  409. * Notify new users of a successful registration (with blog). 
  410. * 
  411. * This function filter's WP's 'wpmu_signup_blog_notification', and replaces 
  412. * WP's default welcome email with a BuddyPress-specific message. 
  413. * 
  414. * @since 1.0.0 
  415. * 
  416. * @see wpmu_signup_blog_notification() for a description of parameters. 
  417. * 
  418. * @param string $domain The new blog domain. 
  419. * @param string $path The new blog path. 
  420. * @param string $title The site title. 
  421. * @param string $user The user's login name. 
  422. * @param string $user_email The user's email address. 
  423. * @param string $key The activation key created in wpmu_signup_blog(). 
  424. * @return bool Returns false to stop original WPMU function from continuing. 
  425. */ 
  426. function bp_core_activation_signup_blog_notification( $domain, $path, $title, $user, $user_email, $key ) { 
  427. $args = array( 
  428. 'tokens' => array( 
  429. 'activate-site.url' => esc_url( bp_get_activation_page() . '?key=' . urlencode( $key ) ),  
  430. 'domain' => $domain,  
  431. 'key_blog' => $key,  
  432. 'path' => $path,  
  433. 'user-site.url' => esc_url( "http://{$domain}{$path}" ),  
  434. 'title' => $title,  
  435. 'user.email' => $user_email,  
  436. ),  
  437. ); 
  438. bp_send_email( 'core-user-registration-with-blog', array( array( $user_email => $user ) ), $args ); 
  439.  
  440. // Return false to stop the original WPMU function from continuing. 
  441. return false; 
  442. add_filter( 'wpmu_signup_blog_notification', 'bp_core_activation_signup_blog_notification', 1, 6 ); 
  443.  
  444. /** 
  445. * Notify new users of a successful registration (without blog). 
  446. * 
  447. * @since 1.0.0 
  448. * 
  449. * @see wpmu_signup_user_notification() for a full description of params. 
  450. * 
  451. * @param string $user The user's login name. 
  452. * @param string $user_email The user's email address. 
  453. * @param string $key The activation key created in wpmu_signup_user(). 
  454. * @param array $meta By default, an empty array. 
  455. * @return bool|string Returns false to stop original WPMU function from continuing. 
  456. */ 
  457. function bp_core_activation_signup_user_notification( $user, $user_email, $key, $meta ) { 
  458. if ( is_admin() ) { 
  459.  
  460. // If the user is created from the WordPress Add User screen, don't send BuddyPress signup notifications. 
  461. if( in_array( get_current_screen()->id, array( 'user', 'user-network' ) ) ) { 
  462. // If the Super Admin want to skip confirmation email. 
  463. if ( isset( $_POST[ 'noconfirmation' ] ) && is_super_admin() ) { 
  464. return false; 
  465.  
  466. // WordPress will manage the signup process. 
  467. } else { 
  468. return $user; 
  469.  
  470. /** 
  471. * There can be a case where the user was created without the skip confirmation 
  472. * And the super admin goes in pending accounts to resend it. In this case, as the 
  473. * meta['password'] is not set, the activation url must be WordPress one. 
  474. */ 
  475. } elseif ( buddypress()->members->admin->signups_page == get_current_screen()->id ) { 
  476. $is_hashpass_in_meta = maybe_unserialize( $meta ); 
  477.  
  478. if ( empty( $is_hashpass_in_meta['password'] ) ) { 
  479. return $user; 
  480.  
  481. $user_id = 0; 
  482. $user_object = get_user_by( 'login', $user ); 
  483. if ( $user_object ) { 
  484. $user_id = $user_object->ID; 
  485.  
  486. $args = array( 
  487. 'tokens' => array( 
  488. 'activate.url' => esc_url( trailingslashit( bp_get_activation_page() ) . "{$key}/" ),  
  489. 'key' => $key,  
  490. 'user.email' => $user_email,  
  491. 'user.id' => $user_id,  
  492. ),  
  493. ); 
  494. bp_send_email( 'core-user-registration', array( array( $user_email => $user ) ), $args ); 
  495.  
  496. // Return false to stop the original WPMU function from continuing. 
  497. return false; 
  498. add_filter( 'wpmu_signup_user_notification', 'bp_core_activation_signup_user_notification', 1, 4 ); 
  499.  
  500. /** 
  501. * Filter the page title for BuddyPress pages. 
  502. * 
  503. * @since 1.5.0 
  504. * 
  505. * @see wp_title() 
  506. * @global object $bp BuddyPress global settings. 
  507. * 
  508. * @param string $title Original page title. 
  509. * @param string $sep How to separate the various items within the page title. 
  510. * @param string $seplocation Direction to display title. 
  511. * @return string New page title. 
  512. */ 
  513. function bp_modify_page_title( $title = '', $sep = '»', $seplocation = 'right' ) { 
  514. global $paged, $page, $_wp_theme_features; 
  515.  
  516. // Get the BuddyPress title parts. 
  517. $bp_title_parts = bp_get_title_parts( $seplocation ); 
  518.  
  519. // If not set, simply return the original title. 
  520. if ( ! $bp_title_parts ) { 
  521. return $title; 
  522.  
  523. // Get the blog name, so we can check if the original $title included it. 
  524. $blogname = get_bloginfo( 'name', 'display' ); 
  525.  
  526. /** 
  527. * Are we going to fake 'title-tag' theme functionality? 
  528. * 
  529. * @link https://buddypress.trac.wordpress.org/ticket/6107 
  530. * @see wp_title() 
  531. */ 
  532. $title_tag_compatibility = (bool) ( ! empty( $_wp_theme_features['title-tag'] ) || ( $blogname && strstr( $title, $blogname ) ) ); 
  533.  
  534. // Append the site title to title parts if theme supports title tag. 
  535. if ( true === $title_tag_compatibility ) { 
  536. $bp_title_parts['site'] = $blogname; 
  537.  
  538. if ( ( $paged >= 2 || $page >= 2 ) && ! is_404() && ! bp_is_single_activity() ) { 
  539. $bp_title_parts['page'] = sprintf( __( 'Page %s', 'buddypress' ), max( $paged, $page ) ); 
  540.  
  541. // Pad the separator with 1 space on each side. 
  542. $prefix = str_pad( $sep, strlen( $sep ) + 2, ' ', STR_PAD_BOTH ); 
  543.  
  544. // Join the parts together. 
  545. $new_title = join( $prefix, array_filter( $bp_title_parts ) ); 
  546.  
  547. // Append the prefix for pre `title-tag` compatibility. 
  548. if ( false === $title_tag_compatibility ) { 
  549. $new_title = $new_title . $prefix; 
  550.  
  551. /** 
  552. * Filters the older 'wp_title' page title for BuddyPress pages. 
  553. * 
  554. * @since 1.5.0 
  555. * 
  556. * @param string $new_title The BuddyPress page title. 
  557. * @param string $title The original WordPress page title. 
  558. * @param string $sep The title parts separator. 
  559. * @param string $seplocation Location of the separator (left or right). 
  560. */ 
  561. return apply_filters( 'bp_modify_page_title', $new_title, $title, $sep, $seplocation ); 
  562. add_filter( 'wp_title', 'bp_modify_page_title', 20, 3 ); 
  563. add_filter( 'bp_modify_page_title', 'wptexturize' ); 
  564. add_filter( 'bp_modify_page_title', 'convert_chars' ); 
  565. add_filter( 'bp_modify_page_title', 'esc_html' ); 
  566.  
  567. /** 
  568. * Filter the document title for BuddyPress pages. 
  569. * 
  570. * @since 2.4.3 
  571. * 
  572. * @param array $title The WordPress document title parts. 
  573. * @return array the unchanged title parts or the BuddyPress ones 
  574. */ 
  575. function bp_modify_document_title_parts( $title = array() ) { 
  576. // Get the BuddyPress title parts. 
  577. $bp_title_parts = bp_get_title_parts(); 
  578.  
  579. // If not set, simply return the original title. 
  580. if ( ! $bp_title_parts ) { 
  581. return $title; 
  582.  
  583. // Get the separator used by wp_get_document_title(). 
  584. $sep = apply_filters( 'document_title_separator', '-' ); 
  585.  
  586. // Build the BuddyPress portion of the title. 
  587. // We don't need to sanitize this as WordPress will take care of it. 
  588. $bp_title = array( 
  589. 'title' => join( " $sep ", $bp_title_parts ) 
  590. ); 
  591.  
  592. // Add the pagination number if needed (not sure if this is necessary). 
  593. if ( isset( $title['page'] ) && ! bp_is_single_activity() ) { 
  594. $bp_title['page'] = $title['page']; 
  595.  
  596. // Add the sitename if needed. 
  597. if ( isset( $title['site'] ) ) { 
  598. $bp_title['site'] = $title['site']; 
  599.  
  600. /** 
  601. * Filters BuddyPress title parts that will be used into the document title. 
  602. * 
  603. * @since 2.4.3 
  604. * 
  605. * @param array $bp_title The BuddyPress page title parts. 
  606. * @param array $title The original WordPress title parts. 
  607. */ 
  608. return apply_filters( 'bp_modify_document_title_parts', $bp_title, $title ); 
  609. add_filter( 'document_title_parts', 'bp_modify_document_title_parts', 20, 1 ); 
  610.  
  611. /** 
  612. * Add BuddyPress-specific items to the wp_nav_menu. 
  613. * 
  614. * @since 1.9.0 
  615. * 
  616. * @param WP_Post $menu_item The menu item. 
  617. * @return WP_Post The modified WP_Post object. 
  618. */ 
  619. function bp_setup_nav_menu_item( $menu_item ) { 
  620. if ( is_admin() ) { 
  621. return $menu_item; 
  622.  
  623. // Prevent a notice error when using the customizer. 
  624. $menu_classes = $menu_item->classes; 
  625.  
  626. if ( is_array( $menu_classes ) ) { 
  627. $menu_classes = implode( ' ', $menu_item->classes); 
  628.  
  629. // We use information stored in the CSS class to determine what kind of 
  630. // menu item this is, and how it should be treated. 
  631. preg_match( '/\sbp-(.*)-nav/', $menu_classes, $matches ); 
  632.  
  633. // If this isn't a BP menu item, we can stop here. 
  634. if ( empty( $matches[1] ) ) { 
  635. return $menu_item; 
  636.  
  637. switch ( $matches[1] ) { 
  638. case 'login' : 
  639. if ( is_user_logged_in() ) { 
  640. $menu_item->_invalid = true; 
  641. } else { 
  642. $menu_item->url = wp_login_url( bp_get_requested_url() ); 
  643.  
  644. break; 
  645.  
  646. case 'logout' : 
  647. if ( ! is_user_logged_in() ) { 
  648. $menu_item->_invalid = true; 
  649. } else { 
  650. $menu_item->url = wp_logout_url( bp_get_requested_url() ); 
  651.  
  652. break; 
  653.  
  654. // Don't show the Register link to logged-in users. 
  655. case 'register' : 
  656. if ( is_user_logged_in() ) { 
  657. $menu_item->_invalid = true; 
  658.  
  659. break; 
  660.  
  661. // All other BP nav items are specific to the logged-in user,  
  662. // and so are not relevant to logged-out users. 
  663. default: 
  664. if ( is_user_logged_in() ) { 
  665. $menu_item->url = bp_nav_menu_get_item_url( $matches[1] ); 
  666. } else { 
  667. $menu_item->_invalid = true; 
  668.  
  669. break; 
  670.  
  671. // If component is deactivated, make sure menu item doesn't render. 
  672. if ( empty( $menu_item->url ) ) { 
  673. $menu_item->_invalid = true; 
  674.  
  675. // Highlight the current page. 
  676. } else { 
  677. $current = bp_get_requested_url(); 
  678. if ( strpos( $current, $menu_item->url ) !== false ) { 
  679. if ( is_array( $menu_item->classes ) ) { 
  680. $menu_item->classes[] = 'current_page_item'; 
  681. $menu_item->classes[] = 'current-menu-item'; 
  682. } else { 
  683. $menu_item->classes = array( 'current_page_item', 'current-menu-item' ); 
  684.  
  685. return $menu_item; 
  686. add_filter( 'wp_setup_nav_menu_item', 'bp_setup_nav_menu_item', 10, 1 ); 
  687.  
  688. /** 
  689. * Populate BuddyPress user nav items for the customizer. 
  690. * 
  691. * @since 2.3.3 
  692. * 
  693. * @param array $items The array of menu items. 
  694. * @param string $type The requested type. 
  695. * @param string $object The requested object name. 
  696. * @param integer $page The page num being requested. 
  697. * @return array The paginated BuddyPress user nav items. 
  698. */ 
  699. function bp_customizer_nav_menus_get_items( $items = array(), $type = '', $object = '', $page = 0 ) { 
  700. if ( 'bp_loggedin_nav' === $object ) { 
  701. $bp_items = bp_nav_menu_get_loggedin_pages(); 
  702. } elseif ( 'bp_loggedout_nav' === $object ) { 
  703. $bp_items = bp_nav_menu_get_loggedout_pages(); 
  704. } else { 
  705. return $items; 
  706.  
  707. foreach ( $bp_items as $bp_item ) { 
  708. $items[] = array( 
  709. 'id' => "bp-{$bp_item->post_excerpt}",  
  710. 'title' => html_entity_decode( $bp_item->post_title, ENT_QUOTES, get_bloginfo( 'charset' ) ),  
  711. 'type' => $type,  
  712. 'url' => esc_url_raw( $bp_item->guid ),  
  713. 'classes' => "bp-menu bp-{$bp_item->post_excerpt}-nav",  
  714. 'type_label' => _x( 'Custom Link', 'customizer menu type label', 'buddypress' ),  
  715. 'object' => $object,  
  716. 'object_id' => -1,  
  717. ); 
  718.  
  719. return array_slice( $items, 10 * $page, 10 ); 
  720. add_filter( 'customize_nav_menu_available_items', 'bp_customizer_nav_menus_get_items', 10, 4 ); 
  721.  
  722. /** 
  723. * Set BuddyPress item navs for the customizer. 
  724. * 
  725. * @since 2.3.3 
  726. * 
  727. * @param array $item_types An associative array structured for the customizer. 
  728. * @return array $item_types An associative array structured for the customizer. 
  729. */ 
  730. function bp_customizer_nav_menus_set_item_types( $item_types = array() ) { 
  731. $item_types = array_merge( $item_types, array( 
  732. 'bp_loggedin_nav' => array( 
  733. 'title' => _x( 'BuddyPress (logged-in)', 'customizer menu section title', 'buddypress' ),  
  734. 'type' => 'bp_nav',  
  735. 'object' => 'bp_loggedin_nav',  
  736. ),  
  737. 'bp_loggedout_nav' => array( 
  738. 'title' => _x( 'BuddyPress (logged-out)', 'customizer menu section title', 'buddypress' ),  
  739. 'type' => 'bp_nav',  
  740. 'object' => 'bp_loggedout_nav',  
  741. ),  
  742. ) ); 
  743.  
  744. return $item_types; 
  745. add_filter( 'customize_nav_menu_available_item_types', 'bp_customizer_nav_menus_set_item_types', 10, 1 ); 
  746.  
  747. /** 
  748. * Filter SQL query strings to swap out the 'meta_id' column. 
  749. * 
  750. * WordPress uses the meta_id column for commentmeta and postmeta, and so 
  751. * hardcodes the column name into its *_metadata() functions. BuddyPress, on 
  752. * the other hand, uses 'id' for the primary column. To make WP's functions 
  753. * usable for BuddyPress, we use this just-in-time filter on 'query' to swap 
  754. * 'meta_id' with 'id. 
  755. * 
  756. * @since 2.0.0 
  757. * 
  758. * @access private Do not use. 
  759. * 
  760. * @param string $q SQL query. 
  761. * @return string 
  762. */ 
  763. function bp_filter_metaid_column_name( $q ) { 
  764. /** 
  765. * Replace quoted content with __QUOTE__ to avoid false positives. 
  766. * This regular expression will match nested quotes. 
  767. */ 
  768. $quoted_regex = "/'[^'\\\\]*(?:\\\\.[^'\\\\]*)*'/s"; 
  769. preg_match_all( $quoted_regex, $q, $quoted_matches ); 
  770. $q = preg_replace( $quoted_regex, '__QUOTE__', $q ); 
  771.  
  772. $q = str_replace( 'meta_id', 'id', $q ); 
  773.  
  774. // Put quoted content back into the string. 
  775. if ( ! empty( $quoted_matches[0] ) ) { 
  776. for ( $i = 0; $i < count( $quoted_matches[0] ); $i++ ) { 
  777. $quote_pos = strpos( $q, '__QUOTE__' ); 
  778. $q = substr_replace( $q, $quoted_matches[0][ $i ], $quote_pos, 9 ); 
  779.  
  780. return $q; 
  781.  
  782. /** 
  783. * Filter the edit post link to avoid its display in BuddyPress pages. 
  784. * 
  785. * @since 2.1.0 
  786. * 
  787. * @param string $edit_link The edit link. 
  788. * @param int $post_id Post ID. 
  789. * @return bool|string Will be a boolean (false) if $post_id is 0. Will be a string (the unchanged edit link) 
  790. * otherwise 
  791. */ 
  792. function bp_core_filter_edit_post_link( $edit_link = '', $post_id = 0 ) { 
  793. if ( 0 === $post_id ) { 
  794. $edit_link = false; 
  795.  
  796. return $edit_link; 
  797.  
  798. /** 
  799. * Should BuddyPress load the mentions scripts and related assets, including results to prime the 
  800. * mentions suggestions? 
  801. * 
  802. * @since 2.2.0 
  803. * 
  804. * @param bool $load_mentions True to load mentions assets, false otherwise. 
  805. * @param bool $mentions_enabled True if mentions are enabled. 
  806. * @return bool True if mentions scripts should be loaded. 
  807. */ 
  808. function bp_maybe_load_mentions_scripts_for_blog_content( $load_mentions, $mentions_enabled ) { 
  809. if ( ! $mentions_enabled ) { 
  810. return $load_mentions; 
  811.  
  812. if ( $load_mentions || ( bp_is_blog_page() && is_singular() && comments_open() ) ) { 
  813. return true; 
  814.  
  815. return $load_mentions; 
  816. add_filter( 'bp_activity_maybe_load_mentions_scripts', 'bp_maybe_load_mentions_scripts_for_blog_content', 10, 2 ); 
  817.  
  818. /** 
  819. * Injects specific BuddyPress CSS classes into a widget sidebar. 
  820. * 
  821. * Helps to standardize styling of BuddyPress widgets within a theme that 
  822. * does not use dynamic CSS classes in their widget sidebar's 'before_widget' 
  823. * call. 
  824. * 
  825. * @since 2.4.0 
  826. * @access private 
  827. * 
  828. * @global array $wp_registered_widgets Current registered widgets. 
  829. * 
  830. * @param array $params Current sidebar params. 
  831. * @return array 
  832. */ 
  833. function _bp_core_inject_bp_widget_css_class( $params ) { 
  834. global $wp_registered_widgets; 
  835.  
  836. $widget_id = $params[0]['widget_id']; 
  837.  
  838. // If callback isn't an array, bail. 
  839. if ( false === is_array( $wp_registered_widgets[ $widget_id ]['callback'] ) ) { 
  840. return $params; 
  841.  
  842. // If the current widget isn't a BuddyPress one, stop! 
  843. // We determine if a widget is a BuddyPress widget, if the widget class 
  844. // begins with 'bp_'. 
  845. if ( 0 !== strpos( $wp_registered_widgets[ $widget_id ]['callback'][0]->id_base, 'bp_' ) ) { 
  846. return $params; 
  847.  
  848. // Dynamically add our widget CSS classes for BP widgets if not already there. 
  849. $classes = array(); 
  850.  
  851. // Try to find 'widget' CSS class. 
  852. if ( false === strpos( $params[0]['before_widget'], 'widget ' ) ) { 
  853. $classes[] = 'widget'; 
  854.  
  855. // Try to find 'buddypress' CSS class. 
  856. if ( false === strpos( $params[0]['before_widget'], ' buddypress' ) ) { 
  857. $classes[] = 'buddypress'; 
  858.  
  859. // Stop if widget already has our CSS classes. 
  860. if ( empty( $classes ) ) { 
  861. return $params; 
  862.  
  863. // CSS injection time! 
  864. $params[0]['before_widget'] = str_replace( 'class="', 'class="' . implode( ' ', $classes ) . ' ', $params[0]['before_widget'] ); 
  865.  
  866. return $params; 
  867. add_filter( 'dynamic_sidebar_params', '_bp_core_inject_bp_widget_css_class' ); 
  868.  
  869. /** 
  870. * Add email link styles to rendered email template. 
  871. * 
  872. * This is only used when the email content has been merged into the email template. 
  873. * 
  874. * @since 2.5.0 
  875. * 
  876. * @param string $value Property value. 
  877. * @param string $property_name Email template property name. 
  878. * @param string $transform How the return value was transformed. 
  879. * @return string Updated value. 
  880. */ 
  881. function bp_email_add_link_color_to_template( $value, $property_name, $transform ) { 
  882. if ( $property_name !== 'template' || $transform !== 'add-content' ) { 
  883. return $value; 
  884.  
  885. $settings = bp_email_get_appearance_settings(); 
  886. $replacement = 'style="color: ' . esc_attr( $settings['highlight_color'] ) . ';'; 
  887.  
  888. // Find all links. 
  889. preg_match_all( '#<a[^>]+>#i', $value, $links, PREG_SET_ORDER ); 
  890. foreach ( $links as $link ) { 
  891. $new_link = $link = array_shift( $link ); 
  892.  
  893. // Add/modify style property. 
  894. if ( strpos( $link, 'style="' ) !== false ) { 
  895. $new_link = str_replace( 'style="', $replacement, $link ); 
  896. } else { 
  897. $new_link = str_replace( '<a ', "<a {$replacement}\" ", $link ); 
  898.  
  899. if ( $new_link !== $link ) { 
  900. $value = str_replace( $link, $new_link, $value ); 
  901.  
  902. return $value; 
  903. add_filter( 'bp_email_get_property', 'bp_email_add_link_color_to_template', 6, 3 ); 
  904.  
  905. /** 
  906. * Add custom headers to outgoing emails. 
  907. * 
  908. * @since 2.5.0 
  909. * 
  910. * @param array $headers Array of email headers. 
  911. * @param string $property Name of property. Unused. 
  912. * @param string $transform Return value transformation. Unused. 
  913. * @param BP_Email $email Email object reference. 
  914. * @return array 
  915. */ 
  916. function bp_email_set_default_headers( $headers, $property, $transform, $email ) { 
  917. $headers['X-BuddyPress'] = bp_get_version(); 
  918. $headers['X-BuddyPress-Type'] = $email->get( 'type' ); 
  919.  
  920. $tokens = $email->get_tokens(); 
  921.  
  922. // Add 'List-Unsubscribe' header if applicable. 
  923. if ( ! empty( $tokens['unsubscribe'] ) && $tokens['unsubscribe'] !== site_url( 'wp-login.php' ) ) { 
  924. $user = get_user_by( 'email', $tokens['recipient.email'] ); 
  925.  
  926. $headers['List-Unsubscribe'] = sprintf( 
  927. '<%s>',  
  928. esc_url_raw( bp_email_get_unsubscribe_link( array( 
  929. 'user_id' => $user->ID,  
  930. 'notification_type' => $email->get( 'type' ),  
  931. ) ) ) 
  932. ); 
  933.  
  934. return $headers; 
  935. add_filter( 'bp_email_get_headers', 'bp_email_set_default_headers', 6, 4 ); 
  936.  
  937. /** 
  938. * Add default email tokens. 
  939. * 
  940. * @since 2.5.0 
  941. * 
  942. * @param array $tokens Email tokens. 
  943. * @param string $property_name Unused. 
  944. * @param string $transform Unused. 
  945. * @param BP_Email $email Email being sent. 
  946. * @return array 
  947. */ 
  948. function bp_email_set_default_tokens( $tokens, $property_name, $transform, $email ) { 
  949. $tokens['site.admin-email'] = bp_get_option( 'admin_email' ); 
  950. $tokens['site.url'] = home_url(); 
  951.  
  952. // These options are escaped with esc_html on the way into the database in sanitize_option(). 
  953. $tokens['site.description'] = wp_specialchars_decode( bp_get_option( 'blogdescription' ), ENT_QUOTES ); 
  954. $tokens['site.name'] = wp_specialchars_decode( bp_get_option( 'blogname' ), ENT_QUOTES ); 
  955.  
  956. // Default values for tokens set conditionally below. 
  957. $tokens['email.preheader'] = ''; 
  958. $tokens['recipient.email'] = ''; 
  959. $tokens['recipient.name'] = ''; 
  960. $tokens['recipient.username'] = ''; 
  961.  
  962.  
  963. // Who is the email going to? 
  964. $recipient = $email->get( 'to' ); 
  965. if ( $recipient ) { 
  966. $recipient = array_shift( $recipient ); 
  967. $user_obj = $recipient->get_user( 'search-email' ); 
  968.  
  969. $tokens['recipient.email'] = $recipient->get_address(); 
  970. $tokens['recipient.name'] = $recipient->get_name(); 
  971.  
  972. if ( ! $user_obj && $tokens['recipient.email'] ) { 
  973. $user_obj = get_user_by( 'email', $tokens['recipient.email'] ); 
  974.  
  975. if ( $user_obj ) { 
  976. $tokens['recipient.username'] = $user_obj->user_login; 
  977. if ( bp_is_active( 'settings' ) && empty( $tokens['unsubscribe'] ) ) { 
  978. $tokens['unsubscribe'] = esc_url( sprintf( 
  979. '%s%s/notifications/',  
  980. bp_core_get_user_domain( $user_obj->ID ),  
  981. bp_get_settings_slug() 
  982. ) ); 
  983.  
  984. // Set default unsubscribe link if not passed. 
  985. if ( empty( $tokens['unsubscribe'] ) ) { 
  986. $tokens['unsubscribe'] = site_url( 'wp-login.php' ); 
  987.  
  988. // Email preheader. 
  989. $post = $email->get_post_object(); 
  990. if ( $post ) { 
  991. $tokens['email.preheader'] = sanitize_text_field( get_post_meta( $post->ID, 'bp_email_preheader', true ) ); 
  992.  
  993. return $tokens; 
  994. add_filter( 'bp_email_get_tokens', 'bp_email_set_default_tokens', 6, 4 ); 
  995.  
  996. /** 
  997. * Find and render the template for Email posts (the Customizer and admin previews). 
  998. * 
  999. * Misuses the `template_include` filter which expects a string, but as we need to replace 
  1000. * the `{{{content}}}` token with the post's content, we use object buffering to load the 
  1001. * template, replace the token, and render it. 
  1002. * 
  1003. * The function returns an empty string to prevent WordPress rendering another template. 
  1004. * 
  1005. * @since 2.5.0 
  1006. * 
  1007. * @param string $template Path to template (probably single.php). 
  1008. * @return string 
  1009. */ 
  1010. function bp_core_render_email_template( $template ) { 
  1011. if ( get_post_type() !== bp_get_email_post_type() || ! is_single() ) { 
  1012. return $template; 
  1013.  
  1014. /** 
  1015. * Filter template used to display Email posts. 
  1016. * 
  1017. * @since 2.5.0 
  1018. * 
  1019. * @param string $template Path to current template (probably single.php). 
  1020. */ 
  1021. $email_template = apply_filters( 'bp_core_render_email_template',  
  1022. bp_locate_template( bp_email_get_template( get_queried_object() ), false ),  
  1023. $template 
  1024. ); 
  1025.  
  1026. if ( ! $email_template ) { 
  1027. return $template; 
  1028.  
  1029. ob_start(); 
  1030. include( $email_template ); 
  1031. $template = ob_get_contents(); 
  1032. ob_end_clean(); 
  1033.  
  1034. // Make sure we add a <title> tag so WP Customizer picks it up. 
  1035. $template = str_replace( '<head>', '<head><title>' . esc_html_x( 'BuddyPress Emails', 'screen heading', 'buddypress' ) . '</title>', $template ); 
  1036. echo str_replace( '{{{content}}}', nl2br( get_post()->post_content ), $template ); 
  1037.  
  1038. /** 
  1039. * Link colours are applied directly in the email template before sending, so we 
  1040. * need to add an extra style here to set the colour for the Customizer or preview. 
  1041. */ 
  1042. $settings = bp_email_get_appearance_settings(); 
  1043. printf( 
  1044. '<style>a { color: %s; }</style>',  
  1045. esc_attr( $settings['highlight_color'] ) 
  1046. ); 
  1047.  
  1048. return ''; 
  1049. add_action( 'bp_template_include', 'bp_core_render_email_template', 12 ); 
.