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