/bp-core/bp-core-catchuri.php

  1. <?php 
  2. /** 
  3. * BuddyPress URI catcher. 
  4. * 
  5. * Functions for parsing the URI and determining which BuddyPress template file 
  6. * to use on-screen. 
  7. * 
  8. * @package BuddyPress 
  9. * @subpackage Core 
  10. * @since 1.0.0 
  11. */ 
  12.  
  13. // Exit if accessed directly. 
  14. defined( 'ABSPATH' ) || exit; 
  15.  
  16. /** 
  17. * Analyze the URI and break it down into BuddyPress-usable chunks. 
  18. * 
  19. * BuddyPress can use complete custom friendly URIs without the user having to 
  20. * add new rewrite rules. Custom components are able to use their own custom 
  21. * URI structures with very little work. 
  22. * 
  23. * The URIs are broken down as follows: 
  24. * - http:// example.com / members / andy / [current_component] / [current_action] / [action_variables] / [action_variables] / ... 
  25. * - OUTSIDE ROOT: http:// example.com / sites / buddypress / members / andy / [current_component] / [current_action] / [action_variables] / [action_variables] / ... 
  26. * 
  27. * Example: 
  28. * - http://example.com/members/andy/profile/edit/group/5/ 
  29. * - $bp->current_component: string 'xprofile' 
  30. * - $bp->current_action: string 'edit' 
  31. * - $bp->action_variables: array ['group', 5] 
  32. * 
  33. * @since 1.0.0 
  34. */ 
  35. function bp_core_set_uri_globals() { 
  36. global $current_blog, $wp_rewrite; 
  37.  
  38. // Don't catch URIs on non-root blogs unless multiblog mode is on. 
  39. if ( !bp_is_root_blog() && !bp_is_multiblog_mode() ) 
  40. return false; 
  41.  
  42. $bp = buddypress(); 
  43.  
  44. // Define local variables. 
  45. $root_profile = $match = false; 
  46. $key_slugs = $matches = $uri_chunks = array(); 
  47.  
  48. // Fetch all the WP page names for each component. 
  49. if ( empty( $bp->pages ) ) 
  50. $bp->pages = bp_core_get_directory_pages(); 
  51.  
  52. // Ajax or not? 
  53. if ( defined( 'DOING_AJAX' ) && DOING_AJAX || strpos( $_SERVER['REQUEST_URI'], 'wp-load.php' ) ) 
  54. $path = bp_get_referer_path(); 
  55. else 
  56. $path = esc_url( $_SERVER['REQUEST_URI'] ); 
  57.  
  58. /** 
  59. * Filters the BuddyPress global URI path. 
  60. * 
  61. * @since 1.0.0 
  62. * 
  63. * @param string $path Path to set. 
  64. */ 
  65. $path = apply_filters( 'bp_uri', $path ); 
  66.  
  67. // Take GET variables off the URL to avoid problems. 
  68. $path = strtok( $path, '?' ); 
  69.  
  70. // Fetch current URI and explode each part separated by '/' into an array. 
  71. $bp_uri = explode( '/', $path ); 
  72.  
  73. // Loop and remove empties. 
  74. foreach ( (array) $bp_uri as $key => $uri_chunk ) { 
  75. if ( empty( $bp_uri[$key] ) ) { 
  76. unset( $bp_uri[$key] ); 
  77.  
  78. /** 
  79. * If running off blog other than root, any subdirectory names must be 
  80. * removed from $bp_uri. This includes two cases: 
  81. * 
  82. * 1. when WP is installed in a subdirectory,  
  83. * 2. when BP is running on secondary blog of a subdirectory 
  84. * multisite installation. Phew! 
  85. */ 
  86. if ( is_multisite() && !is_subdomain_install() && ( bp_is_multiblog_mode() || 1 != bp_get_root_blog_id() ) ) { 
  87.  
  88. // Blow chunks. 
  89. $chunks = explode( '/', $current_blog->path ); 
  90.  
  91. // If chunks exist... 
  92. if ( !empty( $chunks ) ) { 
  93.  
  94. // ...loop through them... 
  95. foreach( $chunks as $key => $chunk ) { 
  96. $bkey = array_search( $chunk, $bp_uri ); 
  97.  
  98. // ...and unset offending keys 
  99. if ( false !== $bkey ) { 
  100. unset( $bp_uri[$bkey] ); 
  101.  
  102. $bp_uri = array_values( $bp_uri ); 
  103.  
  104. // Get site path items. 
  105. $paths = explode( '/', bp_core_get_site_path() ); 
  106.  
  107. // Take empties off the end of path. 
  108. if ( empty( $paths[count( $paths ) - 1] ) ) 
  109. array_pop( $paths ); 
  110.  
  111. // Take empties off the start of path. 
  112. if ( empty( $paths[0] ) ) 
  113. array_shift( $paths ); 
  114.  
  115. // Reset indexes. 
  116. $bp_uri = array_values( $bp_uri ); 
  117. $paths = array_values( $paths ); 
  118.  
  119. // Unset URI indices if they intersect with the paths. 
  120. foreach ( (array) $bp_uri as $key => $uri_chunk ) { 
  121. if ( isset( $paths[$key] ) && $uri_chunk == $paths[$key] ) { 
  122. unset( $bp_uri[$key] ); 
  123.  
  124. // Reset the keys by merging with an empty array. 
  125. $bp_uri = array_merge( array(), $bp_uri ); 
  126.  
  127. /** 
  128. * If a component is set to the front page, force its name into $bp_uri 
  129. * so that $current_component is populated (unless a specific WP post is being requested 
  130. * via a URL parameter, usually signifying Preview mode). 
  131. */ 
  132. if ( 'page' == get_option( 'show_on_front' ) && get_option( 'page_on_front' ) && empty( $bp_uri ) && empty( $_GET['p'] ) && empty( $_GET['page_id'] ) ) { 
  133. $post = get_post( get_option( 'page_on_front' ) ); 
  134. if ( !empty( $post ) ) { 
  135. $bp_uri[0] = $post->post_name; 
  136.  
  137. // Keep the unfiltered URI safe. 
  138. $bp->unfiltered_uri = $bp_uri; 
  139.  
  140. // Don't use $bp_unfiltered_uri, this is only for backpat with old plugins. Use $bp->unfiltered_uri. 
  141. $GLOBALS['bp_unfiltered_uri'] = &$bp->unfiltered_uri; 
  142.  
  143. // Get slugs of pages into array. 
  144. foreach ( (array) $bp->pages as $page_key => $bp_page ) 
  145. $key_slugs[$page_key] = trailingslashit( '/' . $bp_page->slug ); 
  146.  
  147. // Bail if keyslugs are empty, as BP is not setup correct. 
  148. if ( empty( $key_slugs ) ) 
  149. return; 
  150.  
  151. // Loop through page slugs and look for exact match to path. 
  152. foreach ( $key_slugs as $key => $slug ) { 
  153. if ( $slug == $path ) { 
  154. $match = $bp->pages->{$key}; 
  155. $match->key = $key; 
  156. $matches[] = 1; 
  157. break; 
  158.  
  159. // No exact match, so look for partials. 
  160. if ( empty( $match ) ) { 
  161.  
  162. // Loop through each page in the $bp->pages global. 
  163. foreach ( (array) $bp->pages as $page_key => $bp_page ) { 
  164.  
  165. // Look for a match (check members first). 
  166. if ( in_array( $bp_page->name, (array) $bp_uri ) ) { 
  167.  
  168. // Match found, now match the slug to make sure. 
  169. $uri_chunks = explode( '/', $bp_page->slug ); 
  170.  
  171. // Loop through uri_chunks. 
  172. foreach ( (array) $uri_chunks as $key => $uri_chunk ) { 
  173.  
  174. // Make sure chunk is in the correct position. 
  175. if ( !empty( $bp_uri[$key] ) && ( $bp_uri[$key] == $uri_chunk ) ) { 
  176. $matches[] = 1; 
  177.  
  178. // No match. 
  179. } else { 
  180. $matches[] = 0; 
  181.  
  182. // Have a match. 
  183. if ( !in_array( 0, (array) $matches ) ) { 
  184. $match = $bp_page; 
  185. $match->key = $page_key; 
  186. break; 
  187. }; 
  188.  
  189. // Unset matches. 
  190. unset( $matches ); 
  191.  
  192. // Unset uri chunks. 
  193. unset( $uri_chunks ); 
  194.  
  195. // URLs with BP_ENABLE_ROOT_PROFILES enabled won't be caught above. 
  196. if ( empty( $matches ) && bp_core_enable_root_profiles() && ! empty( $bp_uri[0] ) ) { 
  197.  
  198. // Switch field based on compat. 
  199. $field = bp_is_username_compatibility_mode() ? 'login' : 'slug'; 
  200.  
  201. /** 
  202. * Filter the portion of the URI that is the displayed user's slug. 
  203. * 
  204. * Eg. example.com/ADMIN (when root profiles is enabled) 
  205. * example.com/members/ADMIN (when root profiles isn't enabled) 
  206. * 
  207. * ADMIN would be the displayed user's slug. 
  208. * 
  209. * @since 2.6.0 
  210. * 
  211. * @param string $member_slug 
  212. */ 
  213. $member_slug = apply_filters( 'bp_core_set_uri_globals_member_slug', $bp_uri[0] ); 
  214.  
  215. // Make sure there's a user corresponding to $bp_uri[0]. 
  216. if ( ! empty( $bp->pages->members ) && $root_profile = get_user_by( $field, $member_slug ) ) { 
  217.  
  218. // Force BP to recognize that this is a members page. 
  219. $matches[] = 1; 
  220. $match = $bp->pages->members; 
  221. $match->key = 'members'; 
  222.  
  223. // Search doesn't have an associated page, so we check for it separately. 
  224. if ( !empty( $bp_uri[0] ) && ( bp_get_search_slug() == $bp_uri[0] ) ) { 
  225. $matches[] = 1; 
  226. $match = new stdClass; 
  227. $match->key = 'search'; 
  228. $match->slug = bp_get_search_slug(); 
  229.  
  230. // This is not a BuddyPress page, so just return. 
  231. if ( empty( $matches ) ) 
  232. return false; 
  233.  
  234. $wp_rewrite->use_verbose_page_rules = false; 
  235.  
  236. // Find the offset. With $root_profile set, we fudge the offset down so later parsing works. 
  237. $slug = !empty ( $match ) ? explode( '/', $match->slug ) : ''; 
  238. $uri_offset = empty( $root_profile ) ? 0 : -1; 
  239.  
  240. // Rejig the offset. 
  241. if ( !empty( $slug ) && ( 1 < count( $slug ) ) ) { 
  242. // Only offset if not on a root profile. Fixes issue when Members page is nested. 
  243. if ( false === $root_profile ) { 
  244. array_pop( $slug ); 
  245. $uri_offset = count( $slug ); 
  246.  
  247. // Global the unfiltered offset to use in bp_core_load_template(). 
  248. // To avoid PHP warnings in bp_core_load_template(), it must always be >= 0. 
  249. $bp->unfiltered_uri_offset = $uri_offset >= 0 ? $uri_offset : 0; 
  250.  
  251. // We have an exact match. 
  252. if ( isset( $match->key ) ) { 
  253.  
  254. // Set current component to matched key. 
  255. $bp->current_component = $match->key; 
  256.  
  257. // If members component, do more work to find the actual component. 
  258. if ( 'members' == $match->key ) { 
  259.  
  260. $after_member_slug = false; 
  261. if ( ! empty( $bp_uri[ $uri_offset + 1 ] ) ) { 
  262. $after_member_slug = $bp_uri[ $uri_offset + 1 ]; 
  263.  
  264. // Are we viewing a specific user? 
  265. if ( $after_member_slug ) { 
  266.  
  267. /** This filter is documented in bp-core/bp-core-catchuri.php */ 
  268. $after_member_slug = apply_filters( 'bp_core_set_uri_globals_member_slug', $after_member_slug ); 
  269.  
  270. // If root profile, we've already queried for the user. 
  271. if ( $root_profile instanceof WP_User ) { 
  272. $bp->displayed_user->id = $root_profile->ID; 
  273.  
  274. // Switch the displayed_user based on compatibility mode. 
  275. } elseif ( bp_is_username_compatibility_mode() ) { 
  276. $bp->displayed_user->id = (int) bp_core_get_userid( urldecode( $after_member_slug ) ); 
  277.  
  278. } else { 
  279. $bp->displayed_user->id = (int) bp_core_get_userid_from_nicename( $after_member_slug ); 
  280.  
  281. // Is this a member type directory? 
  282. if ( ! bp_displayed_user_id() && $after_member_slug === bp_get_members_member_type_base() && ! empty( $bp_uri[ $uri_offset + 2 ] ) ) { 
  283. $matched_types = bp_get_member_types( array( 
  284. 'has_directory' => true,  
  285. 'directory_slug' => $bp_uri[ $uri_offset + 2 ],  
  286. ) ); 
  287.  
  288. if ( ! empty( $matched_types ) ) { 
  289. $bp->current_member_type = reset( $matched_types ); 
  290. unset( $bp_uri[ $uri_offset + 1 ] ); 
  291.  
  292. // If the slug matches neither a member type nor a specific member, 404. 
  293. if ( ! bp_displayed_user_id() && ! bp_get_current_member_type() && $after_member_slug ) { 
  294. // Prevent components from loading their templates. 
  295. $bp->current_component = ''; 
  296. bp_do_404(); 
  297. return; 
  298.  
  299. // If the displayed user is marked as a spammer, 404 (unless logged-in user is a super admin). 
  300. if ( bp_displayed_user_id() && bp_is_user_spammer( bp_displayed_user_id() ) ) { 
  301. if ( bp_current_user_can( 'bp_moderate' ) ) { 
  302. bp_core_add_message( __( 'This user has been marked as a spammer. Only site admins can view this profile.', 'buddypress' ), 'warning' ); 
  303. } else { 
  304. bp_do_404(); 
  305. return; 
  306.  
  307. // Bump the offset. 
  308. if ( bp_displayed_user_id() ) { 
  309. if ( isset( $bp_uri[$uri_offset + 2] ) ) { 
  310. $bp_uri = array_merge( array(), array_slice( $bp_uri, $uri_offset + 2 ) ); 
  311. $bp->current_component = $bp_uri[0]; 
  312.  
  313. // No component, so default will be picked later. 
  314. } else { 
  315. $bp_uri = array_merge( array(), array_slice( $bp_uri, $uri_offset + 2 ) ); 
  316. $bp->current_component = ''; 
  317.  
  318. // Reset the offset. 
  319. $uri_offset = 0; 
  320.  
  321. // Determine the current action. 
  322. $current_action = isset( $bp_uri[ $uri_offset + 1 ] ) ? $bp_uri[ $uri_offset + 1 ] : ''; 
  323.  
  324. /** 
  325. * If a BuddyPress directory is set to the WP front page, URLs like example.com/members/?s=foo 
  326. * shouldn't interfere with blog searches. 
  327. */ 
  328. if ( empty( $current_action) && ! empty( $_GET['s'] ) && 'page' == get_option( 'show_on_front' ) && ! empty( $match->id ) ) { 
  329. $page_on_front = (int) get_option( 'page_on_front' ); 
  330. if ( (int) $match->id === $page_on_front ) { 
  331. $bp->current_component = ''; 
  332. return false; 
  333.  
  334. $bp->current_action = $current_action; 
  335.  
  336. // Slice the rest of the $bp_uri array and reset offset. 
  337. $bp_uri = array_slice( $bp_uri, $uri_offset + 2 ); 
  338. $uri_offset = 0; 
  339.  
  340. // Set the entire URI as the action variables, we will unset the current_component and action in a second. 
  341. $bp->action_variables = $bp_uri; 
  342.  
  343. // Reset the keys by merging with an empty array. 
  344. $bp->action_variables = array_merge( array(), $bp->action_variables ); 
  345.  
  346. /** 
  347. * Are root profiles enabled and allowed? 
  348. * 
  349. * @since 1.6.0 
  350. * 
  351. * @return bool True if yes, false if no. 
  352. */ 
  353. function bp_core_enable_root_profiles() { 
  354.  
  355. $retval = false; 
  356.  
  357. if ( defined( 'BP_ENABLE_ROOT_PROFILES' ) && ( true == BP_ENABLE_ROOT_PROFILES ) ) 
  358. $retval = true; 
  359.  
  360. /** 
  361. * Filters whether or not root profiles are enabled and allowed. 
  362. * 
  363. * @since 1.6.0 
  364. * 
  365. * @param bool $retval Whether or not root profiles are available. 
  366. */ 
  367. return apply_filters( 'bp_core_enable_root_profiles', $retval ); 
  368.  
  369. /** 
  370. * Load a specific template file with fallback support. 
  371. * 
  372. * Example: 
  373. * bp_core_load_template( 'members/index' ); 
  374. * Loads: 
  375. * wp-content/themes/[activated_theme]/members/index.php 
  376. * 
  377. * @since 1.0.0 
  378. * 
  379. * @param array $templates Array of templates to attempt to load. 
  380. */ 
  381. function bp_core_load_template( $templates ) { 
  382. global $wp_query; 
  383.  
  384. // Reset the post. 
  385. bp_theme_compat_reset_post( array( 
  386. 'ID' => 0,  
  387. 'is_404' => true,  
  388. 'post_status' => 'publish',  
  389. ) ); 
  390.  
  391. // Set theme compat to false since the reset post function automatically sets 
  392. // theme compat to true. 
  393. bp_set_theme_compat_active( false ); 
  394.  
  395. // Fetch each template and add the php suffix. 
  396. $filtered_templates = array(); 
  397. foreach ( (array) $templates as $template ) { 
  398. $filtered_templates[] = $template . '.php'; 
  399.  
  400. // Only perform template lookup for bp-default themes. 
  401. if ( ! bp_use_theme_compat_with_current_theme() ) { 
  402. $template = locate_template( (array) $filtered_templates, false ); 
  403.  
  404. // Theme compat doesn't require a template lookup. 
  405. } else { 
  406. $template = ''; 
  407.  
  408. /** 
  409. * Filters the template locations. 
  410. * 
  411. * Allows plugins to alter where the template files are located. 
  412. * 
  413. * @since 1.1.0 
  414. * 
  415. * @param string $template Located template path. 
  416. * @param array $filtered_templates Array of templates to attempt to load. 
  417. */ 
  418. $located_template = apply_filters( 'bp_located_template', $template, $filtered_templates ); 
  419.  
  420. /** 
  421. * If current page is an embed, wipe out bp-default template. 
  422. * 
  423. * Wiping out the bp-default template allows WordPress to use their special 
  424. * embed template, which is what we want. 
  425. */ 
  426. if ( function_exists( 'is_embed' ) && is_embed() ) { 
  427. $located_template = ''; 
  428.  
  429. if ( !empty( $located_template ) ) { 
  430. // Template was located, lets set this as a valid page and not a 404. 
  431. status_header( 200 ); 
  432. $wp_query->is_page = true; 
  433. $wp_query->is_singular = true; 
  434. $wp_query->is_404 = false; 
  435.  
  436. /** 
  437. * Fires before the loading of a located template file. 
  438. * 
  439. * @since 1.6.0 
  440. * 
  441. * @param string $located_template Template found to be loaded. 
  442. */ 
  443. do_action( 'bp_core_pre_load_template', $located_template ); 
  444.  
  445. /** 
  446. * Filters the selected template right before loading. 
  447. * 
  448. * @since 1.1.0 
  449. * 
  450. * @param string $located_template Template found to be loaded. 
  451. */ 
  452. load_template( apply_filters( 'bp_load_template', $located_template ) ); 
  453.  
  454. /** 
  455. * Fires after the loading of a located template file. 
  456. * 
  457. * @since 1.6.0 
  458. * 
  459. * @param string $located_template Template found that was loaded. 
  460. */ 
  461. do_action( 'bp_core_post_load_template', $located_template ); 
  462.  
  463. // Kill any other output after this. 
  464. exit(); 
  465.  
  466. // No template found, so setup theme compatibility. 
  467. // @todo Some other 404 handling if theme compat doesn't kick in. 
  468. } else { 
  469.  
  470. // We know where we are, so reset important $wp_query bits here early. 
  471. // The rest will be done by bp_theme_compat_reset_post() later. 
  472. if ( is_buddypress() ) { 
  473. status_header( 200 ); 
  474. $wp_query->is_page = true; 
  475. $wp_query->is_singular = true; 
  476. $wp_query->is_404 = false; 
  477.  
  478. /** 
  479. * Fires if there are no found templates to load and theme compat is needed. 
  480. * 
  481. * @since 1.7.0 
  482. */ 
  483. do_action( 'bp_setup_theme_compat' ); 
  484.  
  485. /** 
  486. * Redirect away from /profile URIs if XProfile is not enabled. 
  487. * 
  488. * @since 1.0.0 
  489. */ 
  490. function bp_core_catch_profile_uri() { 
  491. if ( !bp_is_active( 'xprofile' ) ) { 
  492.  
  493. /** 
  494. * Filters the path to redirect users to if XProfile is not enabled. 
  495. * 
  496. * @since 1.0.0 
  497. * 
  498. * @param string $value Path to redirect users to. 
  499. */ 
  500. bp_core_load_template( apply_filters( 'bp_core_template_display_profile', 'members/single/home' ) ); 
  501.  
  502. /** 
  503. * Members user shortlink redirector. 
  504. * 
  505. * Redirects x.com/members/me/** to x.com/members/{LOGGED_IN_USER_SLUG}/* 
  506. * 
  507. * @since 2.6.0 
  508. * 
  509. * @param string $member_slug The current member slug. 
  510. * @return string $member_slug The current member slug. 
  511. */ 
  512. function bp_core_members_shortlink_redirector( $member_slug ) { 
  513.  
  514. /** 
  515. * Shortlink slug to redirect to logged-in user. 
  516. * 
  517. * The x.com/members/me/** url will redirect to x.com/members/{LOGGED_IN_USER_SLUG}/* 
  518. * 
  519. * @since 2.6.0 
  520. * 
  521. * @param string $slug Defaults to 'me'. 
  522. */ 
  523. $me_slug = apply_filters( 'bp_core_members_shortlink_slug', 'me' ); 
  524.  
  525. // Check if we're on our special shortlink slug. If not, bail. 
  526. if ( $me_slug !== $member_slug ) { 
  527. return $member_slug; 
  528.  
  529. // If logged out, redirect user to login. 
  530. if ( false === is_user_logged_in() ) { 
  531. // Add our login redirector hook. 
  532. add_action( 'template_redirect', 'bp_core_no_access', 0 ); 
  533.  
  534. return $member_slug; 
  535.  
  536. $user = wp_get_current_user(); 
  537.  
  538. return bp_core_get_username( $user->ID, $user->user_nicename, $user->user_login ); 
  539. add_filter( 'bp_core_set_uri_globals_member_slug', 'bp_core_members_shortlink_redirector' ); 
  540.  
  541. /** 
  542. * Catch unauthorized access to certain BuddyPress pages and redirect accordingly. 
  543. * 
  544. * @since 1.5.0 
  545. */ 
  546. function bp_core_catch_no_access() { 
  547. global $wp_query; 
  548.  
  549. $bp = buddypress(); 
  550.  
  551. // If coming from bp_core_redirect() and $bp_no_status_set is true,  
  552. // we are redirecting to an accessible page so skip this check. 
  553. if ( !empty( $bp->no_status_set ) ) 
  554. return false; 
  555.  
  556. if ( !isset( $wp_query->queried_object ) && !bp_is_blog_page() ) { 
  557. bp_do_404(); 
  558. add_action( 'bp_template_redirect', 'bp_core_catch_no_access', 1 ); 
  559.  
  560. /** 
  561. * Redirect a user to log in for BP pages that require access control. 
  562. * 
  563. * Add an error message (if one is provided). 
  564. * 
  565. * If authenticated, redirects user back to requested content by default. 
  566. * 
  567. * @since 1.5.0 
  568. * 
  569. * @param array|string $args { 
  570. * @type int $mode Specifies the destination of the redirect. 1 will 
  571. * direct to the root domain (home page), which assumes you have a 
  572. * log-in form there; 2 directs to wp-login.php. Default: 2. 
  573. * @type string $redirect The URL the user will be redirected to after successfully 
  574. * logging in. Default: the URL originally requested. 
  575. * @type string $root The root URL of the site, used in case of error or mode 1 redirects. 
  576. * Default: the value of {@link bp_get_root_domain()}. 
  577. * @type string $message An error message to display to the user on the log-in page. 
  578. * Default: "You must log in to access the page you requested." 
  579. * } 
  580. */ 
  581. function bp_core_no_access( $args = '' ) { 
  582.  
  583. // Build the redirect URL. 
  584. $redirect_url = is_ssl() ? 'https://' : 'http://'; 
  585. $redirect_url .= $_SERVER['HTTP_HOST']; 
  586. $redirect_url .= $_SERVER['REQUEST_URI']; 
  587.  
  588. $defaults = array( 
  589. 'mode' => 2, // 1 = $root, 2 = wp-login.php. 
  590. 'redirect' => $redirect_url, // the URL you get redirected to when a user successfully logs in. 
  591. 'root' => bp_get_root_domain(), // the landing page you get redirected to when a user doesn't have access. 
  592. 'message' => __( 'You must log in to access the page you requested.', 'buddypress' ) 
  593. ); 
  594.  
  595. $r = wp_parse_args( $args, $defaults ); 
  596.  
  597. /** 
  598. * Filters the arguments used for user redirecting when visiting access controlled areas. 
  599. * 
  600. * @since 1.6.0 
  601. * 
  602. * @param array $r Array of parsed arguments for redirect determination. 
  603. */ 
  604. $r = apply_filters( 'bp_core_no_access', $r ); 
  605. extract( $r, EXTR_SKIP ); 
  606.  
  607. /** 
  608. * @ignore Ignore these filters and use 'bp_core_no_access' above. 
  609. */ 
  610. $mode = apply_filters( 'bp_no_access_mode', $mode, $root, $redirect, $message ); 
  611. $redirect = apply_filters( 'bp_no_access_redirect', $redirect, $root, $message, $mode ); 
  612. $root = apply_filters( 'bp_no_access_root', $root, $redirect, $message, $mode ); 
  613. $message = apply_filters( 'bp_no_access_message', $message, $root, $redirect, $mode ); 
  614. $root = trailingslashit( $root ); 
  615.  
  616. switch ( $mode ) { 
  617.  
  618. // Option to redirect to wp-login.php. 
  619. // Error message is displayed with bp_core_no_access_wp_login_error(). 
  620. case 2 : 
  621. if ( !empty( $redirect ) ) { 
  622. bp_core_redirect( add_query_arg( array( 'action' => 'bpnoaccess' ), wp_login_url( $redirect ) ) ); 
  623. } else { 
  624. bp_core_redirect( $root ); 
  625.  
  626. break; 
  627.  
  628. // Redirect to root with "redirect_to" parameter. 
  629. // Error message is displayed with bp_core_add_message(). 
  630. case 1 : 
  631. default : 
  632.  
  633. $url = $root; 
  634. if ( !empty( $redirect ) ) { 
  635. $url = add_query_arg( 'redirect_to', urlencode( $redirect ), $root ); 
  636.  
  637. if ( !empty( $message ) ) { 
  638. bp_core_add_message( $message, 'error' ); 
  639.  
  640. bp_core_redirect( $url ); 
  641.  
  642. break; 
  643.  
  644. /** 
  645. * Add a custom BuddyPress no access error message to wp-login.php. 
  646. * 
  647. * @since 1.5.0 
  648. * @since 2.7.0 Hook moved to 'wp_login_errors' made available since WP 3.6.0. 
  649. * 
  650. * @param WP_Error $errors Current error container. 
  651. * @return WP_Error 
  652. */ 
  653. function bp_core_no_access_wp_login_error( $errors ) { 
  654. if ( empty( $_GET['action'] ) || 'bpnoaccess' !== $_GET['action'] ) { 
  655. return $errors; 
  656.  
  657. /** 
  658. * Filters the error message for wp-login.php when needing to log in before accessing. 
  659. * 
  660. * @since 1.5.0 
  661. * 
  662. * @param string $value Error message to display. 
  663. * @param string $value URL to redirect user to after successful login. 
  664. */ 
  665. $message = apply_filters( 'bp_wp_login_error', __( 'You must log in to access the page you requested.', 'buddypress' ), $_REQUEST['redirect_to'] ); 
  666.  
  667. $errors->add( 'bp_no_access', $message ); 
  668.  
  669. return $errors; 
  670. add_filter( 'wp_login_errors', 'bp_core_no_access_wp_login_error' ); 
  671.  
  672. /** 
  673. * Add our custom error code to WP login's shake error codes. 
  674. * 
  675. * @since 2.7.0 
  676. * 
  677. * @param array $codes Array of WP error codes. 
  678. * @return array 
  679. */ 
  680. function bp_core_login_filter_shake_codes( $codes ) { 
  681. $codes[] = 'bp_no_access'; 
  682. return $codes; 
  683. add_filter( 'shake_error_codes', 'bp_core_login_filter_shake_codes' ); 
  684.  
  685. /** 
  686. * Canonicalize BuddyPress URLs. 
  687. * 
  688. * This function ensures that requests for BuddyPress content are always 
  689. * redirected to their canonical versions. Canonical versions are always 
  690. * trailingslashed, and are typically the most general possible versions of the 
  691. * URL - eg, example.com/groups/mygroup/ instead of 
  692. * example.com/groups/mygroup/home/. 
  693. * 
  694. * @since 1.6.0 
  695. * 
  696. * @see BP_Members_Component::setup_globals() where 
  697. * $bp->canonical_stack['base_url'] and ['component'] may be set. 
  698. * @see bp_core_new_nav_item() where $bp->canonical_stack['action'] may be set. 
  699. */ 
  700. function bp_redirect_canonical() { 
  701.  
  702. /** 
  703. * Filters whether or not to do canonical redirects on BuddyPress URLs. 
  704. * 
  705. * @since 1.6.0 
  706. * 
  707. * @param bool $value Whether or not to do canonical redirects. Default true. 
  708. */ 
  709. if ( !bp_is_blog_page() && apply_filters( 'bp_do_redirect_canonical', true ) ) { 
  710. // If this is a POST request, don't do a canonical redirect. 
  711. // This is for backward compatibility with plugins that submit form requests to 
  712. // non-canonical URLs. Plugin authors should do their best to use canonical URLs in 
  713. // their form actions. 
  714. if ( !empty( $_POST ) ) { 
  715. return; 
  716.  
  717. // Build the URL in the address bar. 
  718. $requested_url = bp_get_requested_url(); 
  719.  
  720. // Stash query args. 
  721. $url_stack = explode( '?', $requested_url ); 
  722. $req_url_clean = $url_stack[0]; 
  723. $query_args = isset( $url_stack[1] ) ? $url_stack[1] : ''; 
  724.  
  725. $canonical_url = bp_get_canonical_url(); 
  726.  
  727. // Only redirect if we've assembled a URL different from the request. 
  728. if ( $canonical_url !== $req_url_clean ) { 
  729.  
  730. $bp = buddypress(); 
  731.  
  732. // Template messages have been deleted from the cookie by this point, so 
  733. // they must be readded before redirecting. 
  734. if ( isset( $bp->template_message ) ) { 
  735. $message = stripslashes( $bp->template_message ); 
  736. $message_type = isset( $bp->template_message_type ) ? $bp->template_message_type : 'success'; 
  737.  
  738. bp_core_add_message( $message, $message_type ); 
  739.  
  740. if ( !empty( $query_args ) ) { 
  741. $canonical_url .= '?' . $query_args; 
  742.  
  743. bp_core_redirect( $canonical_url, 301 ); 
  744.  
  745. /** 
  746. * Output rel=canonical header tag for BuddyPress content. 
  747. * 
  748. * @since 1.6.0 
  749. */ 
  750. function bp_rel_canonical() { 
  751. $canonical_url = bp_get_canonical_url(); 
  752.  
  753. // Output rel=canonical tag. 
  754. echo "<link rel='canonical' href='" . esc_attr( $canonical_url ) . "' />\n"; 
  755.  
  756. /** 
  757. * Get the canonical URL of the current page. 
  758. * 
  759. * @since 1.6.0 
  760. * 
  761. * @param array $args { 
  762. * Optional array of arguments. 
  763. * @type bool $include_query_args Whether to include current URL arguments 
  764. * in the canonical URL returned from the function. 
  765. * } 
  766. * @return string Canonical URL for the current page. 
  767. */ 
  768. function bp_get_canonical_url( $args = array() ) { 
  769.  
  770. // For non-BP content, return the requested url, and let WP do the work. 
  771. if ( bp_is_blog_page() ) { 
  772. return bp_get_requested_url(); 
  773.  
  774. $bp = buddypress(); 
  775.  
  776. $defaults = array( 
  777. 'include_query_args' => false // Include URL arguments, eg ?foo=bar&foo2=bar2. 
  778. ); 
  779. $r = wp_parse_args( $args, $defaults ); 
  780. extract( $r ); 
  781.  
  782. // Special case: when a BuddyPress directory (eg example.com/members) 
  783. // is set to be the front page, ensure that the current canonical URL 
  784. // is the home page URL. 
  785. if ( 'page' == get_option( 'show_on_front' ) && $page_on_front = (int) get_option( 'page_on_front' ) ) { 
  786. $front_page_component = array_search( $page_on_front, bp_core_get_directory_page_ids() ); 
  787.  
  788. /** 
  789. * If requesting the front page component directory, canonical 
  790. * URL is the front page. We detect whether we're detecting a 
  791. * component *directory* by checking that bp_current_action() 
  792. * is empty - ie, this not a single item, a feed, or an item 
  793. * type directory. 
  794. */ 
  795. if ( false !== $front_page_component && bp_is_current_component( $front_page_component ) && ! bp_current_action() && ! bp_get_current_member_type() ) { 
  796. $bp->canonical_stack['canonical_url'] = trailingslashit( bp_get_root_domain() ); 
  797.  
  798. // Except when the front page is set to the registration page 
  799. // and the current user is logged in. In this case we send to 
  800. // the members directory to avoid redirect loops. 
  801. } elseif ( bp_is_register_page() && 'register' == $front_page_component && is_user_logged_in() ) { 
  802.  
  803. /** 
  804. * Filters the logged in register page redirect URL. 
  805. * 
  806. * @since 1.5.1 
  807. * 
  808. * @param string $value URL to redirect logged in members to. 
  809. */ 
  810. $bp->canonical_stack['canonical_url'] = apply_filters( 'bp_loggedin_register_page_redirect_to', bp_get_members_directory_permalink() ); 
  811.  
  812. if ( empty( $bp->canonical_stack['canonical_url'] ) ) { 
  813. // Build the URL in the address bar. 
  814. $requested_url = bp_get_requested_url(); 
  815.  
  816. // Stash query args. 
  817. $url_stack = explode( '?', $requested_url ); 
  818.  
  819. // Build the canonical URL out of the redirect stack. 
  820. if ( isset( $bp->canonical_stack['base_url'] ) ) 
  821. $url_stack[0] = $bp->canonical_stack['base_url']; 
  822.  
  823. if ( isset( $bp->canonical_stack['component'] ) ) 
  824. $url_stack[0] = trailingslashit( $url_stack[0] . $bp->canonical_stack['component'] ); 
  825.  
  826. if ( isset( $bp->canonical_stack['action'] ) ) 
  827. $url_stack[0] = trailingslashit( $url_stack[0] . $bp->canonical_stack['action'] ); 
  828.  
  829. if ( !empty( $bp->canonical_stack['action_variables'] ) ) { 
  830. foreach( (array) $bp->canonical_stack['action_variables'] as $av ) { 
  831. $url_stack[0] = trailingslashit( $url_stack[0] . $av ); 
  832.  
  833. // Add trailing slash. 
  834. $url_stack[0] = trailingslashit( $url_stack[0] ); 
  835.  
  836. // Stash in the $bp global. 
  837. $bp->canonical_stack['canonical_url'] = implode( '?', $url_stack ); 
  838.  
  839. $canonical_url = $bp->canonical_stack['canonical_url']; 
  840.  
  841. if ( !$include_query_args ) { 
  842. $canonical_url = array_reverse( explode( '?', $canonical_url ) ); 
  843. $canonical_url = array_pop( $canonical_url ); 
  844.  
  845. /** 
  846. * Filters the canonical url of the current page. 
  847. * 
  848. * @since 1.6.0 
  849. * 
  850. * @param string $canonical_url Canonical URL of the current page. 
  851. * @param array $args Array of arguments to help determine canonical URL. 
  852. */ 
  853. return apply_filters( 'bp_get_canonical_url', $canonical_url, $args ); 
  854.  
  855. /** 
  856. * Return the URL as requested on the current page load by the user agent. 
  857. * 
  858. * @since 1.6.0 
  859. * 
  860. * @return string Requested URL string. 
  861. */ 
  862. function bp_get_requested_url() { 
  863. $bp = buddypress(); 
  864.  
  865. if ( empty( $bp->canonical_stack['requested_url'] ) ) { 
  866. $bp->canonical_stack['requested_url'] = is_ssl() ? 'https://' : 'http://'; 
  867. $bp->canonical_stack['requested_url'] .= $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; 
  868.  
  869. /** 
  870. * Filters the URL as requested on the current page load by the user agent. 
  871. * 
  872. * @since 1.7.0 
  873. * 
  874. * @param string $value Requested URL string. 
  875. */ 
  876. return apply_filters( 'bp_get_requested_url', $bp->canonical_stack['requested_url'] ); 
  877.  
  878. /** 
  879. * Remove WP's canonical redirect when we are trying to load BP-specific content. 
  880. * 
  881. * Avoids issues with WordPress thinking that a BuddyPress URL might actually 
  882. * be a blog post or page. 
  883. * 
  884. * This function should be considered temporary, and may be removed without 
  885. * notice in future versions of BuddyPress. 
  886. * 
  887. * @since 1.6.0 
  888. */ 
  889. function _bp_maybe_remove_redirect_canonical() { 
  890. if ( ! bp_is_blog_page() ) 
  891. remove_action( 'template_redirect', 'redirect_canonical' ); 
  892. add_action( 'bp_init', '_bp_maybe_remove_redirect_canonical' ); 
  893.  
  894. /** 
  895. * Rehook maybe_redirect_404() to run later than the default. 
  896. * 
  897. * WordPress's maybe_redirect_404() allows admins on a multisite installation 
  898. * to define 'NOBLOGREDIRECT', a URL to which 404 requests will be redirected. 
  899. * maybe_redirect_404() is hooked to template_redirect at priority 10, which 
  900. * creates a race condition with bp_template_redirect(), our piggyback hook. 
  901. * Due to a legacy bug in BuddyPress, internal BP content (such as members and 
  902. * groups) is marked 404 in $wp_query until bp_core_load_template(), when BP 
  903. * manually overrides the automatic 404. However, the race condition with 
  904. * maybe_redirect_404() means that this manual un-404-ing doesn't happen in 
  905. * time, with the results that maybe_redirect_404() thinks that the page is 
  906. * a legitimate 404, and redirects incorrectly to NOBLOGREDIRECT. 
  907. * 
  908. * By switching maybe_redirect_404() to catch at a higher priority, we avoid 
  909. * the race condition. If bp_core_load_template() runs, it dies before reaching 
  910. * maybe_redirect_404(). If bp_core_load_template() does not run, it means that 
  911. * the 404 is legitimate, and maybe_redirect_404() can proceed as expected. 
  912. * 
  913. * This function will be removed in a later version of BuddyPress. Plugins 
  914. * (and plugin authors!) should ignore it. 
  915. * 
  916. * @since 1.6.1 
  917. * 
  918. * @link https://buddypress.trac.wordpress.org/ticket/4329 
  919. * @link https://buddypress.trac.wordpress.org/ticket/4415 
  920. */ 
  921. function _bp_rehook_maybe_redirect_404() { 
  922. if ( defined( 'NOBLOGREDIRECT' ) ) { 
  923. remove_action( 'template_redirect', 'maybe_redirect_404' ); 
  924. add_action( 'template_redirect', 'maybe_redirect_404', 100 ); 
  925. add_action( 'template_redirect', '_bp_rehook_maybe_redirect_404', 1 ); 
  926.  
  927. /** 
  928. * Remove WP's rel=canonical HTML tag if we are trying to load BP-specific content. 
  929. * 
  930. * This function should be considered temporary, and may be removed without 
  931. * notice in future versions of BuddyPress. 
  932. * 
  933. * @since 1.6.0 
  934. */ 
  935. function _bp_maybe_remove_rel_canonical() { 
  936. if ( ! bp_is_blog_page() && ! is_404() ) { 
  937. remove_action( 'wp_head', 'rel_canonical' ); 
  938. add_action( 'bp_head', 'bp_rel_canonical' ); 
  939. add_action( 'wp_head', '_bp_maybe_remove_rel_canonical', 8 ); 
  940.  
  941. /** 
  942. * Stop WordPress performing a DB query for its main loop. 
  943. * 
  944. * As of WordPress 4.6, it is possible to bypass the main WP_Query entirely. 
  945. * This saves us one unnecessary database query! :) 
  946. * 
  947. * @since 2.7.0 
  948. * 
  949. * @param null $retval Current return value for filter. 
  950. * @param WP_Query $query Current WordPress query object. 
  951. * @return null|array 
  952. */ 
  953. function bp_core_filter_wp_query( $retval, $query ) { 
  954. if ( ! $query->is_main_query() ) { 
  955. return $retval; 
  956.  
  957. /** 
  958. * If not on a BP single page, bail. 
  959. * Too early to use bp_is_single_item(), so use BP conditionals. 
  960. */ 
  961. if ( false === ( bp_is_group() || bp_is_user() || bp_is_single_activity() ) ) { 
  962. return $retval; 
  963.  
  964. // Set default properties as recommended in the 'posts_pre_query' DocBlock. 
  965. $query->found_posts = 0; 
  966. $query->max_num_pages = 0; 
  967.  
  968. // Return something other than a null value to bypass WP_Query. 
  969. return array(); 
.