Skip to:
Content

BuddyPress.org

Ticket #5865: bp-core-catchuri.php

File bp-core-catchuri.php, 25.4 KB (added by WPMUDEV, 6 years ago)

Patch added in line 60.

Line 
1<?php
2
3/**
4 * BuddyPress URI catcher.
5 *
6 * Functions for parsing the URI and determining which BuddyPress template file
7 * to use on-screen.
8 *
9 * @package BuddyPress
10 * @subpackage Core
11 */
12
13// Exit if accessed directly
14if ( !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:// domain.com / members / andy / [current_component] / [current_action] / [action_variables] / [action_variables] / ...
25 *   - OUTSIDE ROOT: http:// domain.com / sites / buddypress / members / andy / [current_component] / [current_action] / [action_variables] / [action_variables] / ...
26 *
27 *      Example:
28 *    - http://domain.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 BuddyPress (1.0.0)
34 */
35function bp_core_set_uri_globals() {
36        global $bp, $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        // Define local variables
43        $root_profile = $match   = false;
44        $key_slugs    = $matches = $uri_chunks = array();
45
46        // Fetch all the WP page names for each component
47        if ( empty( $bp->pages ) )
48                $bp->pages = bp_core_get_directory_pages();
49
50        // Ajax or not?
51        if ( defined( 'DOING_AJAX' ) && DOING_AJAX || strpos( $_SERVER['REQUEST_URI'], 'wp-load.php' ) )
52                $path = bp_core_referrer();
53        else
54                $path = esc_url( $_SERVER['REQUEST_URI'] );
55
56        // Filter the path
57        $path = apply_filters( 'bp_uri', $path );
58
59    // Force lowercase for correct match comparison when there are encoded url slugs
60    $path = strtolower($path);
61
62        // Take GET variables off the URL to avoid problems
63        $path = strtok( $path, '?' );
64
65        // Fetch current URI and explode each part separated by '/' into an array
66        $bp_uri = explode( '/', $path );
67
68        // Loop and remove empties
69        foreach ( (array) $bp_uri as $key => $uri_chunk ) {
70                if ( empty( $bp_uri[$key] ) ) {
71                        unset( $bp_uri[$key] );
72                }
73        }
74
75        // If running off blog other than root, any subdirectory names must be
76        // removed from $bp_uri. This includes two cases:
77        //
78        //    1. when WP is installed in a subdirectory,
79        //    2. when BP is running on secondary blog of a subdirectory
80        //       multisite installation. Phew!
81        if ( is_multisite() && !is_subdomain_install() && ( bp_is_multiblog_mode() || 1 != bp_get_root_blog_id() ) ) {
82
83                // Blow chunks
84                $chunks = explode( '/', $current_blog->path );
85
86                // If chunks exist...
87                if ( !empty( $chunks ) ) {
88
89                        // ...loop through them...
90                        foreach( $chunks as $key => $chunk ) {
91                                $bkey = array_search( $chunk, $bp_uri );
92
93                                // ...and unset offending keys
94                                if ( false !== $bkey ) {
95                                        unset( $bp_uri[$bkey] );
96                                }
97
98                                $bp_uri = array_values( $bp_uri );
99                        }
100                }
101        }
102
103        // Get site path items
104        $paths = explode( '/', bp_core_get_site_path() );
105
106        // Take empties off the end of path
107        if ( empty( $paths[count( $paths ) - 1] ) )
108                array_pop( $paths );
109
110        // Take empties off the start of path
111        if ( empty( $paths[0] ) )
112                array_shift( $paths );
113
114        // Reset indexes
115        $bp_uri = array_values( $bp_uri );
116        $paths  = array_values( $paths );
117
118        // Unset URI indices if they intersect with the paths
119        foreach ( (array) $bp_uri as $key => $uri_chunk ) {
120                if ( isset( $paths[$key] ) && $uri_chunk == $paths[$key] ) {
121                        unset( $bp_uri[$key] );
122                }
123        }
124
125        // Reset the keys by merging with an empty array
126        $bp_uri = array_merge( array(), $bp_uri );
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        if ( 'page' == get_option( 'show_on_front' ) && get_option( 'page_on_front' ) && empty( $bp_uri ) && empty( $_GET['p'] ) && empty( $_GET['page_id'] ) ) {
132                $post = get_post( get_option( 'page_on_front' ) );
133                if ( !empty( $post ) ) {
134                        $bp_uri[0] = $post->post_name;
135                }
136        }
137
138        // Keep the unfiltered URI safe
139        $bp->unfiltered_uri = $bp_uri;
140
141        // Don't use $bp_unfiltered_uri, this is only for backpat with old plugins. Use $bp->unfiltered_uri.
142        $GLOBALS['bp_unfiltered_uri'] = &$bp->unfiltered_uri;
143
144        // Get slugs of pages into array
145        foreach ( (array) $bp->pages as $page_key => $bp_page )
146                $key_slugs[$page_key] = trailingslashit( '/' . $bp_page->slug );
147
148        // Bail if keyslugs are empty, as BP is not setup correct
149        if ( empty( $key_slugs ) )
150                return;
151
152        // Loop through page slugs and look for exact match to path
153        foreach ( $key_slugs as $key => $slug ) {
154                if ( $slug == $path ) {
155                        $match      = $bp->pages->{$key};
156                        $match->key = $key;
157                        $matches[]  = 1;
158                        break;
159                }
160        }
161
162        // No exact match, so look for partials
163        if ( empty( $match ) ) {
164
165                // Loop through each page in the $bp->pages global
166                foreach ( (array) $bp->pages as $page_key => $bp_page ) {
167
168                        // Look for a match (check members first)
169                        if ( in_array( $bp_page->name, (array) $bp_uri ) ) {
170
171                                // Match found, now match the slug to make sure.
172                                $uri_chunks = explode( '/', $bp_page->slug );
173
174                                // Loop through uri_chunks
175                                foreach ( (array) $uri_chunks as $key => $uri_chunk ) {
176
177                                        // Make sure chunk is in the correct position
178                                        if ( !empty( $bp_uri[$key] ) && ( $bp_uri[$key] == $uri_chunk ) ) {
179                                                $matches[] = 1;
180
181                                        // No match
182                                        } else {
183                                                $matches[] = 0;
184                                        }
185                                }
186
187                                // Have a match
188                                if ( !in_array( 0, (array) $matches ) ) {
189                                        $match      = $bp_page;
190                                        $match->key = $page_key;
191                                        break;
192                                };
193
194                                // Unset matches
195                                unset( $matches );
196                        }
197
198                        // Unset uri chunks
199                        unset( $uri_chunks );
200                }
201        }
202
203        // URLs with BP_ENABLE_ROOT_PROFILES enabled won't be caught above
204        if ( empty( $matches ) && bp_core_enable_root_profiles() ) {
205
206                // Switch field based on compat
207                $field = bp_is_username_compatibility_mode() ? 'login' : 'slug';
208
209                // Make sure there's a user corresponding to $bp_uri[0]
210                if ( !empty( $bp->pages->members ) && !empty( $bp_uri[0] ) && $root_profile = get_user_by( $field, $bp_uri[0] ) ) {
211
212                        // Force BP to recognize that this is a members page
213                        $matches[]  = 1;
214                        $match      = $bp->pages->members;
215                        $match->key = 'members';
216                }
217        }
218
219        // Search doesn't have an associated page, so we check for it separately
220        if ( !empty( $bp_uri[0] ) && ( bp_get_search_slug() == $bp_uri[0] ) ) {
221                $matches[]   = 1;
222                $match       = new stdClass;
223                $match->key  = 'search';
224                $match->slug = bp_get_search_slug();
225        }
226
227        // This is not a BuddyPress page, so just return.
228        if ( empty( $matches ) )
229                return false;
230
231        $wp_rewrite->use_verbose_page_rules = false;
232
233        // Find the offset. With $root_profile set, we fudge the offset down so later parsing works
234        $slug       = !empty ( $match ) ? explode( '/', $match->slug ) : '';
235        $uri_offset = empty( $root_profile ) ? 0 : -1;
236
237        // Rejig the offset
238        if ( !empty( $slug ) && ( 1 < count( $slug ) ) ) {
239                array_pop( $slug );
240                $uri_offset = count( $slug );
241        }
242
243        // Global the unfiltered offset to use in bp_core_load_template().
244        // To avoid PHP warnings in bp_core_load_template(), it must always be >= 0
245        $bp->unfiltered_uri_offset = $uri_offset >= 0 ? $uri_offset : 0;
246
247        // We have an exact match
248        if ( isset( $match->key ) ) {
249
250                // Set current component to matched key
251                $bp->current_component = $match->key;
252
253                // If members component, do more work to find the actual component
254                if ( 'members' == $match->key ) {
255
256                        // Viewing a specific user
257                        if ( !empty( $bp_uri[$uri_offset + 1] ) ) {
258
259                                // Switch the displayed_user based on compatbility mode
260                                if ( bp_is_username_compatibility_mode() ) {
261                                        $bp->displayed_user->id = (int) bp_core_get_userid( urldecode( $bp_uri[$uri_offset + 1] ) );
262                                } else {
263                                        $bp->displayed_user->id = (int) bp_core_get_userid_from_nicename( urldecode( $bp_uri[$uri_offset + 1] ) );
264                                }
265
266                                if ( !bp_displayed_user_id() ) {
267
268                                        // Prevent components from loading their templates
269                                        $bp->current_component = '';
270
271                                        bp_do_404();
272                                        return;
273                                }
274
275                                // If the displayed user is marked as a spammer, 404 (unless logged-
276                                // in user is a super admin)
277                                if ( bp_displayed_user_id() && bp_is_user_spammer( bp_displayed_user_id() ) ) {
278                                        if ( bp_current_user_can( 'bp_moderate' ) ) {
279                                                bp_core_add_message( __( 'This user has been marked as a spammer. Only site admins can view this profile.', 'buddypress' ), 'warning' );
280                                        } else {
281                                                bp_do_404();
282                                                return;
283                                        }
284                                }
285
286                                // Bump the offset
287                                if ( isset( $bp_uri[$uri_offset + 2] ) ) {
288                                        $bp_uri                = array_merge( array(), array_slice( $bp_uri, $uri_offset + 2 ) );
289                                        $bp->current_component = $bp_uri[0];
290
291                                // No component, so default will be picked later
292                                } else {
293                                        $bp_uri                = array_merge( array(), array_slice( $bp_uri, $uri_offset + 2 ) );
294                                        $bp->current_component = '';
295                                }
296
297                                // Reset the offset
298                                $uri_offset = 0;
299                        }
300                }
301        }
302
303        // Set the current action
304        $bp->current_action = isset( $bp_uri[$uri_offset + 1] ) ? $bp_uri[$uri_offset + 1] : '';
305
306        // Slice the rest of the $bp_uri array and reset offset
307        $bp_uri      = array_slice( $bp_uri, $uri_offset + 2 );
308        $uri_offset  = 0;
309
310        // Set the entire URI as the action variables, we will unset the current_component and action in a second
311        $bp->action_variables = $bp_uri;
312
313        // Reset the keys by merging with an empty array
314        $bp->action_variables = array_merge( array(), $bp->action_variables );
315}
316
317/**
318 * Are root profiles enabled and allowed?
319 *
320 * @since BuddyPress (1.6.0)
321 *
322 * @return bool True if yes, false if no.
323 */
324function bp_core_enable_root_profiles() {
325
326        $retval = false;
327
328        if ( defined( 'BP_ENABLE_ROOT_PROFILES' ) && ( true == BP_ENABLE_ROOT_PROFILES ) )
329                $retval = true;
330
331        return apply_filters( 'bp_core_enable_root_profiles', $retval );
332}
333
334/**
335 * Load a specific template file with fallback support.
336 *
337 * Example:
338 *   bp_core_load_template( 'members/index' );
339 * Loads:
340 *   wp-content/themes/[activated_theme]/members/index.php
341 *
342 * @param array $templates Array of templates to attempt to load.
343 * @return bool|null Returns false on failure.
344 */
345function bp_core_load_template( $templates ) {
346        global $post, $bp, $wp_query, $wpdb;
347
348        // Determine if the root object WP page exists for this request
349        // note: get_page_by_path() breaks non-root pages
350        if ( !empty( $bp->unfiltered_uri_offset ) ) {
351                if ( !$page_exists = $wpdb->get_var( $wpdb->prepare( "SELECT ID FROM {$wpdb->posts} WHERE post_name = %s", $bp->unfiltered_uri[$bp->unfiltered_uri_offset] ) ) ) {
352                        return false;
353                }
354        }
355
356        // Set the root object as the current wp_query-ied item
357        $object_id = 0;
358        foreach ( (array) $bp->pages as $page ) {
359                if ( $page->name == $bp->unfiltered_uri[$bp->unfiltered_uri_offset] ) {
360                        $object_id = $page->id;
361                }
362        }
363
364        // Make the queried/post object an actual valid page
365        if ( !empty( $object_id ) ) {
366                $wp_query->queried_object    = get_post( $object_id );
367                $wp_query->queried_object_id = $object_id;
368                $post                        = $wp_query->queried_object;
369        }
370
371        // Fetch each template and add the php suffix
372        $filtered_templates = array();
373        foreach ( (array) $templates as $template ) {
374                $filtered_templates[] = $template . '.php';
375        }
376
377        // Filter the template locations so that plugins can alter where they are located
378        $located_template = apply_filters( 'bp_located_template', locate_template( (array) $filtered_templates, false ), $filtered_templates );
379        if ( !empty( $located_template ) ) {
380
381                // Template was located, lets set this as a valid page and not a 404.
382                status_header( 200 );
383                $wp_query->is_page     = true;
384                $wp_query->is_singular = true;
385                $wp_query->is_404      = false;
386
387                do_action( 'bp_core_pre_load_template', $located_template );
388
389                load_template( apply_filters( 'bp_load_template', $located_template ) );
390
391                do_action( 'bp_core_post_load_template', $located_template );
392
393                // Kill any other output after this.
394                exit();
395
396        // No template found, so setup theme compatability
397        // @todo Some other 404 handling if theme compat doesn't kick in
398        } else {
399
400                // We know where we are, so reset important $wp_query bits here early.
401                // The rest will be done by bp_theme_compat_reset_post() later.
402                if ( is_buddypress() ) {
403                        status_header( 200 );
404                        $wp_query->is_page     = true;
405                        $wp_query->is_singular = true;
406                        $wp_query->is_404      = false;
407                }
408
409                do_action( 'bp_setup_theme_compat' );
410        }
411}
412
413/**
414 * Redirect away from /profile URIs if XProfile is not enabled.
415 */
416function bp_core_catch_profile_uri() {
417        if ( !bp_is_active( 'xprofile' ) ) {
418                bp_core_load_template( apply_filters( 'bp_core_template_display_profile', 'members/single/home' ) );
419        }
420}
421
422/**
423 * Catch unauthorized access to certain BuddyPress pages and redirect accordingly.
424 *
425 * @since BuddyPress (1.5.0)
426 */
427function bp_core_catch_no_access() {
428        global $bp, $wp_query;
429
430        // If coming from bp_core_redirect() and $bp_no_status_set is true,
431        // we are redirecting to an accessible page so skip this check.
432        if ( !empty( $bp->no_status_set ) )
433                return false;
434
435        if ( !isset( $wp_query->queried_object ) && !bp_is_blog_page() ) {
436                bp_do_404();
437        }
438}
439add_action( 'bp_template_redirect', 'bp_core_catch_no_access', 1 );
440
441/**
442 * Redirect a user to log in for BP pages that require access control.
443 *
444 * Add an error message (if one is provided).
445 *
446 * If authenticated, redirects user back to requested content by default.
447 *
448 * @since BuddyPress (1.5.0)
449 *
450 * @param array $args {
451 *     @type int $mode Specifies the destintation of the redirect. 1 will
452 *           direct to the root domain (home page), which assumes you have a
453 *           log-in form there; 2 directs to wp-login.php. Default: 2.
454 *     @type string $redirect The URL the user will be redirected to after
455 *           successfully logging in. Default: the URL originally requested.
456 *     @type string $root The root URL of the site, used in case of error or
457 *           mode 1 redirects. Default: the value of {@link bp_get_root_domain()}.
458 *     @type string $message An error message to display to the user on the
459 *           log-in page. Default: "You must log in to access the page you
460 *           requested."
461 * }
462 */
463function bp_core_no_access( $args = '' ) {
464
465        // Build the redirect URL
466        $redirect_url  = is_ssl() ? 'https://' : 'http://';
467        $redirect_url .= $_SERVER['HTTP_HOST'];
468        $redirect_url .= $_SERVER['REQUEST_URI'];
469
470        $defaults = array(
471                'mode'     => 2,                    // 1 = $root, 2 = wp-login.php
472                'redirect' => $redirect_url,        // the URL you get redirected to when a user successfully logs in
473                'root'     => bp_get_root_domain(),     // the landing page you get redirected to when a user doesn't have access
474                'message'  => __( 'You must log in to access the page you requested.', 'buddypress' )
475        );
476
477        $r = wp_parse_args( $args, $defaults );
478        $r = apply_filters( 'bp_core_no_access', $r );
479        extract( $r, EXTR_SKIP );
480
481        /**
482         * @ignore Ignore these filters and use 'bp_core_no_access' above
483         */
484        $mode     = apply_filters( 'bp_no_access_mode',     $mode,     $root,     $redirect, $message );
485        $redirect = apply_filters( 'bp_no_access_redirect', $redirect, $root,     $message,  $mode    );
486        $root     = apply_filters( 'bp_no_access_root',     $root,     $redirect, $message,  $mode    );
487        $message  = apply_filters( 'bp_no_access_message',  $message,  $root,     $redirect, $mode    );
488        $root     = trailingslashit( $root );
489
490        switch ( $mode ) {
491
492                // Option to redirect to wp-login.php
493                // Error message is displayed with bp_core_no_access_wp_login_error()
494                case 2 :
495                        if ( !empty( $redirect ) ) {
496                                bp_core_redirect( add_query_arg( array( 'action' => 'bpnoaccess' ), wp_login_url( $redirect ) ) );
497                        } else {
498                                bp_core_redirect( $root );
499                        }
500
501                        break;
502
503                // Redirect to root with "redirect_to" parameter
504                // Error message is displayed with bp_core_add_message()
505                case 1 :
506                default :
507
508                        $url = $root;
509                        if ( !empty( $redirect ) )
510                                $url = add_query_arg( 'redirect_to', urlencode( $redirect ), $root );
511
512                        if ( !empty( $message ) ) {
513                                bp_core_add_message( $message, 'error' );
514                        }
515
516                        bp_core_redirect( $url );
517
518                        break;
519        }
520}
521
522/**
523 * Add an error message to wp-login.php.
524 *
525 * Hooks into the "bpnoaccess" action defined in bp_core_no_access().
526 *
527 * @since BuddyPress (1.5.0)
528 *
529 * @global $error Error message to pass to wp-login.php
530 */
531function bp_core_no_access_wp_login_error() {
532        global $error;
533
534        $error = apply_filters( 'bp_wp_login_error', __( 'You must log in to access the page you requested.', 'buddypress' ), $_REQUEST['redirect_to'] );
535
536        // shake shake shake!
537        add_action( 'login_head', 'wp_shake_js', 12 );
538}
539add_action( 'login_form_bpnoaccess', 'bp_core_no_access_wp_login_error' );
540
541/**
542 * Canonicalize BuddyPress URLs.
543 *
544 * This function ensures that requests for BuddyPress content are always
545 * redirected to their canonical versions. Canonical versions are always
546 * trailingslashed, and are typically the most general possible versions of the
547 * URL - eg, example.com/groups/mygroup/ instead of
548 * example.com/groups/mygroup/home/.
549 *
550 * @since BuddyPress (1.6.0)
551 *
552 * @see BP_Members_Component::setup_globals() where
553 *      $bp->canonical_stack['base_url'] and ['component'] may be set.
554 * @see bp_core_new_nav_item() where $bp->canonical_stack['action'] may be set.
555 * @uses bp_get_canonical_url()
556 * @uses bp_get_requested_url()
557 */
558function bp_redirect_canonical() {
559        global $bp;
560
561        if ( !bp_is_blog_page() && apply_filters( 'bp_do_redirect_canonical', true ) ) {
562                // If this is a POST request, don't do a canonical redirect.
563                // This is for backward compatibility with plugins that submit form requests to
564                // non-canonical URLs. Plugin authors should do their best to use canonical URLs in
565                // their form actions.
566                if ( !empty( $_POST ) ) {
567                        return;
568                }
569
570                // build the URL in the address bar
571                $requested_url  = bp_get_requested_url();
572
573                // Stash query args
574                $url_stack      = explode( '?', $requested_url );
575                $req_url_clean  = $url_stack[0];
576                $query_args     = isset( $url_stack[1] ) ? $url_stack[1] : '';
577
578                $canonical_url  = bp_get_canonical_url();
579
580                // Only redirect if we've assembled a URL different from the request
581                if ( $canonical_url !== $req_url_clean ) {
582
583                        // Template messages have been deleted from the cookie by this point, so
584                        // they must be readded before redirecting
585                        if ( isset( $bp->template_message ) ) {
586                                $message      = stripslashes( $bp->template_message );
587                                $message_type = isset( $bp->template_message_type ) ? $bp->template_message_type : 'success';
588
589                                bp_core_add_message( $message, $message_type );
590                        }
591
592                        if ( !empty( $query_args ) ) {
593                                $canonical_url .= '?' . $query_args;
594                        }
595
596                        bp_core_redirect( $canonical_url, 301 );
597                }
598        }
599}
600
601/**
602 * Output rel=canonical header tag for BuddyPress content.
603 *
604 * @since BuddyPress (1.6.0)
605 */
606function bp_rel_canonical() {
607        $canonical_url = bp_get_canonical_url();
608
609        // Output rel=canonical tag
610        echo "<link rel='canonical' href='" . esc_attr( $canonical_url ) . "' />\n";
611}
612
613/**
614 * Get the canonical URL of the current page.
615 *
616 * @since BuddyPress (1.6.0)
617 *
618 * @uses apply_filters() Filter bp_get_canonical_url to modify return value.
619 *
620 * @param array $args {
621 *     Optional array of arguments.
622 *     @type bool $include_query_args Whether to include current URL arguments
623 *           in the canonical URL returned from the function.
624 * }
625 * @return string Canonical URL for the current page.
626 */
627function bp_get_canonical_url( $args = array() ) {
628        global $bp;
629
630        // For non-BP content, return the requested url, and let WP do the work
631        if ( bp_is_blog_page() ) {
632                return bp_get_requested_url();
633        }
634
635        $defaults = array(
636                'include_query_args' => false // Include URL arguments, eg ?foo=bar&foo2=bar2
637        );
638        $r = wp_parse_args( $args, $defaults );
639        extract( $r );
640
641        // Special case: when a BuddyPress directory (eg example.com/members)
642        // is set to be the front page, ensure that the current canonical URL
643        // is the home page URL.
644        if ( 'page' == get_option( 'show_on_front' ) && $page_on_front = (int) get_option( 'page_on_front' ) ) {
645                $front_page_component = array_search( $page_on_front, bp_core_get_directory_page_ids() );
646
647                // If requesting the front page component directory, canonical
648                // URL is the front page. We detect whether we're detecting a
649                // component *directory* by checking that bp_current_action()
650                // is empty - ie, this not a single item or a feed
651                if ( false !== $front_page_component && bp_is_current_component( $front_page_component ) && ! bp_current_action() ) {
652                        $bp->canonical_stack['canonical_url'] = trailingslashit( bp_get_root_domain() );
653
654                // Except when the front page is set to the registration page
655                // and the current user is logged in. In this case we send to
656                // the members directory to avoid redirect loops
657                } else if ( bp_is_register_page() && 'register' == $front_page_component && is_user_logged_in() ) {
658                        $bp->canonical_stack['canonical_url'] = apply_filters( 'bp_loggedin_register_page_redirect_to', trailingslashit( bp_get_root_domain() . '/' . bp_get_members_root_slug() ) );
659                }
660        }
661
662        if ( empty( $bp->canonical_stack['canonical_url'] ) ) {
663                // Build the URL in the address bar
664                $requested_url  = bp_get_requested_url();
665
666                // Stash query args
667                $url_stack      = explode( '?', $requested_url );
668
669                // Build the canonical URL out of the redirect stack
670                if ( isset( $bp->canonical_stack['base_url'] ) )
671                        $url_stack[0] = $bp->canonical_stack['base_url'];
672
673                if ( isset( $bp->canonical_stack['component'] ) )
674                        $url_stack[0] = trailingslashit( $url_stack[0] . $bp->canonical_stack['component'] );
675
676                if ( isset( $bp->canonical_stack['action'] ) )
677                        $url_stack[0] = trailingslashit( $url_stack[0] . $bp->canonical_stack['action'] );
678
679                if ( !empty( $bp->canonical_stack['action_variables'] ) ) {
680                        foreach( (array) $bp->canonical_stack['action_variables'] as $av ) {
681                                $url_stack[0] = trailingslashit( $url_stack[0] . $av );
682                        }
683                }
684
685                // Add trailing slash
686                $url_stack[0] = trailingslashit( $url_stack[0] );
687
688                // Stash in the $bp global
689                $bp->canonical_stack['canonical_url'] = implode( '?', $url_stack );
690        }
691
692        $canonical_url = $bp->canonical_stack['canonical_url'];
693
694        if ( !$include_query_args ) {
695                $canonical_url = array_reverse( explode( '?', $canonical_url ) );
696                $canonical_url = array_pop( $canonical_url );
697        }
698
699        return apply_filters( 'bp_get_canonical_url', $canonical_url, $args );
700}
701
702/**
703 * Return the URL as requested on the current page load by the user agent.
704 *
705 * @since BuddyPress (1.6.0)
706 *
707 * @return string Requested URL string.
708 */
709function bp_get_requested_url() {
710        global $bp;
711
712        if ( empty( $bp->canonical_stack['requested_url'] ) ) {
713                $bp->canonical_stack['requested_url']  = is_ssl() ? 'https://' : 'http://';
714                $bp->canonical_stack['requested_url'] .= $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
715        }
716
717        return apply_filters( 'bp_get_requested_url', $bp->canonical_stack['requested_url'] );
718}
719
720/**
721 * Remove WP's canonical redirect when we are trying to load BP-specific content.
722 *
723 * Avoids issues with WordPress thinking that a BuddyPress URL might actually
724 * be a blog post or page.
725 *
726 * This function should be considered temporary, and may be removed without
727 * notice in future versions of BuddyPress.
728 *
729 * @since BuddyPress (1.6.0)
730 *
731 * @uses bp_is_blog_page()
732 */
733function _bp_maybe_remove_redirect_canonical() {
734        if ( ! bp_is_blog_page() )
735                remove_action( 'template_redirect', 'redirect_canonical' );
736}
737add_action( 'bp_init', '_bp_maybe_remove_redirect_canonical' );
738
739/**
740 * Rehook maybe_redirect_404() to run later than the default.
741 *
742 * WordPress's maybe_redirect_404() allows admins on a multisite installation
743 * to define 'NOBLOGREDIRECT', a URL to which 404 requests will be redirected.
744 * maybe_redirect_404() is hooked to template_redirect at priority 10, which
745 * creates a race condition with bp_template_redirect(), our piggyback hook.
746 * Due to a legacy bug in BuddyPress, internal BP content (such as members and
747 * groups) is marked 404 in $wp_query until bp_core_load_template(), when BP
748 * manually overrides the automatic 404. However, the race condition with
749 * maybe_redirect_404() means that this manual un-404-ing doesn't happen in
750 * time, with the results that maybe_redirect_404() thinks that the page is
751 * a legitimate 404, and redirects incorrectly to NOBLOGREDIRECT.
752 *
753 * By switching maybe_redirect_404() to catch at a higher priority, we avoid
754 * the race condition. If bp_core_load_template() runs, it dies before reaching
755 * maybe_redirect_404(). If bp_core_load_template() does not run, it means that
756 * the 404 is legitimate, and maybe_redirect_404() can proceed as expected.
757 *
758 * This function will be removed in a later version of BuddyPress. Plugins
759 * (and plugin authors!) should ignore it.
760 *
761 * @since BuddyPress (1.6.1)
762 *
763 * @link http://buddypress.trac.wordpress.org/ticket/4329
764 * @link http://buddypress.trac.wordpress.org/ticket/4415
765 */
766function _bp_rehook_maybe_redirect_404() {
767        if ( defined( 'NOBLOGREDIRECT' ) ) {
768                remove_action( 'template_redirect', 'maybe_redirect_404' );
769                add_action( 'template_redirect', 'maybe_redirect_404', 100 );
770        }
771}
772add_action( 'template_redirect', '_bp_rehook_maybe_redirect_404', 1 );
773
774/**
775 * Remove WP's rel=canonical HTML tag if we are trying to load BP-specific content.
776 *
777 * This function should be considered temporary, and may be removed without
778 * notice in future versions of BuddyPress.
779 *
780 * @since BuddyPress (1.6.0)
781 */
782function _bp_maybe_remove_rel_canonical() {
783        if ( ! bp_is_blog_page() && ! is_404() ) {
784                remove_action( 'wp_head', 'rel_canonical' );
785                add_action( 'bp_head', 'bp_rel_canonical' );
786        }
787}
788add_action( 'wp_head', '_bp_maybe_remove_rel_canonical', 8 );