Skip to:
Content

BuddyPress.org


Ignore:
Timestamp:
05/03/2023 06:18:23 AM (13 months ago)
Author:
imath
Message:

Stop using BP Legacy URL parser in favor of the new BP Rewrites API

  • Deprecate bp_core_set_uri_globals(). This function is moved inside the BP Classic compatibility plugin.
  • Introduce the new bp_register_nav action to hook to when globalizing Members single item navigations from the BP_Component class.
  • Improve bp_get_component_navigations() so that Avatar/Cover images navigation items are moved inside the Profile sub nav if the Extended profile component is active.
  • Register Avatar/Cover images Ajax actions so that these actions trigger our new URL Parser inside Ajax context.
  • Improve the BP_Core_Nav::add_nav() method so that any BP action variable slugs can be customized.
  • Improve Members & Groups component canonical redirections.
  • Handle slugs customization persistency using directory pages post metas.
  • Introduce a new repair tool to reset all slugs to BuddyPress default one.
  • Adapt some PHPUnit tests to better handle our new URL parser.

Props Props r-a-y, johnjamesjacoby, boonebgorges

Closes https://github.com/buddypress/buddypress/pull/94
See #4954

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/bp-core/bp-core-catchuri.php

    r13461 r13468  
    1313// Exit if accessed directly.
    1414defined( '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 }
    38915
    39016/**
Note: See TracChangeset for help on using the changeset viewer.