Skip to:
Content

BuddyPress.org

Ticket #4140: bp-core-catchuri.patch

File bp-core-catchuri.patch, 52.0 KB (added by foxly, 13 years ago)

bp-core-catchuri.php patch

  • C:\xampp\htdocs\wp-content\plugins\

    # This patch file was generated by NetBeans IDE
    # This patch can be applied using context Tools: Apply Diff Patch action on respective folder.
    # It uses platform neutral UTF-8 encoding.
    # Above lines and this line are ignored by the patching process.
    old new  
    11<?php
    22
    33/**
    4  * BUDDYPRESS ROUTER
    5  * Analyzes a URI passed from the web server and determines the correct page module to send the request to.
     4 * BuddyPress URI catcher
    65 *
    7  * @version 1.6
    8  * @since 1.6
    9  * @package Core
    10  * @subpackage Router
    11  * @license GPL v2.0
     6 * Functions for parsing the URI and determining which BuddyPress template file
     7 * to use on-screen.
    128 *
    13  * ========================================================================================================
     9 * Based on contributions from: Chris Taylor - http://www.stillbreathing.co.uk/
     10 * Modified for BuddyPress by: Andy Peatling - http://apeatling.wordpress.com/
     11 *
     12 * @package BuddyPress
     13 * @subpackage Core
    1414 */
    1515
    16 class BP_router {
     16// Exit if accessed directly
     17if ( !defined( 'ABSPATH' ) ) exit;
    1718
    18 
    19         var $http_referer;                  // $_SERVER['HTTP_REFERER'] sent in from the web server
    20         var $request_uri;                   // $_SERVER['REQUEST_URI'] sent in from the web server
    21         var $wp_http_referer;               // $_REQUEST['_wp_http_referer'] sent in from the web server
    22 
    23         var $bp;                            // Local copy of $bp singleton
    24 
    25         var $wpdb;                          // Local copy of $wpdb singleton
    26         var $wp_query;                      // Local copy of $wp_query singleton
    27         var $current_blog;                  // Local copy of WordPress $current_blog global
    28         var $current_site;                  // Local copy of WordPress $current_site global
    29 
    30         var $walk;                          // Walk array for current URI
    31         var $flat_pages;                    // The page tree for the root blog or current blog as a flat array
    32         var $lofted_pages;                  // The page tree for the root blog or current blog as a hierarchical array
    33         var $intersect;                     // Intersect object
    34 
    35         var $unit_test = false;             // Set true to disable die() calls in template loader methods
    36 
    37 
    38         // ================================================================================================================
    39 
    40 
    41         function BP_router($args=null) {
    42 
    43                 $this->__construct($args);
    44         }
    45 
    46         function __construct($args=null) {
    47 
    48                 // Handle dependency-injection for unit tests
    49                 if($args){
    50 
    51                         $this->http_referer = &$args['http_referer'];
    52                         $this->request_uri = &$args['request_uri'];
    53                         $this->wp_http_referer = &$args['wp_http_referer'];
    54 
    55                         $this->bp = &$args['bp'];
    56 
    57                         $this->wpdb = &$args['wpdb'];
    58                         $this->wp_query = &$args['wp_query'];
    59                         $this->current_blog = &$args['current_blog'];
    60                         $this->current_site = &$args['current_site'];
    61 
    62                         $this->walk = &$args['walk'];
    63                         $this->flat_pages = &$args['flat_pages'];
    64                         $this->lofted_pages = &$args['lofted_pages'];
    65                         $this->intersect = &$args['intersect'];
    66 
    67                 }
    68                 else {
    69 
    70                         global $bp;
    71                         global $wpdb, $wp_query, $current_blog, $current_site;
    72 
    73                         $this->http_referer = $_SERVER['HTTP_REFERER'];
    74                         $this->request_uri = $_SERVER['REQUEST_URI'];
    75                         $this->wp_http_referer = $_REQUEST['_wp_http_referer'];
    76 
    77                         $this->bp = &$bp;
    78 
    79                         $this->wpdb = &$wpdb;
    80                         $this->wp_query = &$wp_query;
    81                         $this->current_blog = &$current_blog;
    82                         $this->current_site = &$current_site;
    83 
    84                         $this->walk = null;
    85                         $this->flat_pages = null;
    86                         $this->lofted_pages = null;
    87                         $this->intersect = null;
    88                 }
    89 
    90         }
    91 
    92 
    9319        /**
    94          * Given a URI owned by BuddyPress, load the correct templates
     20 * Analyzes the URI structure and breaks it down into parts for use in code.
     21 * BuddyPress can use complete custom friendly URI's without the user having to
     22 * add new re-write rules. Custom components are able to use their own custom
     23 * URI structures with very little work.
    9524         *
    96          * @version 1.6
    97          * @since 1.6
     25 * @package BuddyPress Core
     26 * @since BuddyPress (r100)
     27 *
     28 * The URI's are broken down as follows:
     29 *   - http:// domain.com / members / andy / [current_component] / [current_action] / [action_variables] / [action_variables] / ...
     30 *   - OUTSIDE ROOT: http:// domain.com / sites / buddypress / members / andy / [current_component] / [current_action] / [action_variables] / [action_variables] / ...
     31 *
     32 *      Example:
     33 *    - http://domain.com/members/andy/profile/edit/group/5/
     34 *    - $bp->current_component: string 'xprofile'
     35 *    - $bp->current_action: string 'edit'
     36 *    - $bp->action_variables: array ['group', 5]
     37 *
    9838         */
     39function bp_core_set_uri_globals() {
     40        global $bp, $current_blog, $wp_rewrite;
    9941
    100         public function route(&$status=null, &$error=null) {
    101 
    102 
    103                 // Reset the global component, action, and item variables
    104                 // ===============================================================
    105 
    106                 $this->bp->current_component = "";
    107                 $this->bp->current_action = "";
    108                 $this->bp->current_item = "";
    109                 $this->bp->action_variables = array();
    110                 $this->bp->displayed_user->id = null;
    111 
    11242                // Don't catch URIs on non-root blogs unless multiblog mode is on
    113                 // ===============================================================
    114 
    115                 if( !bp_is_root_blog() && !bp_is_multiblog_mode() ){
    116 
    117                         $status = array(
    118                                 'numeric'=>0,   // We normally start numbering at 1 but this is a
    119                                                 // special case due to the &$status reference from
    120                                                 // the matchComponent() method
    121 
    122                                 'text'=>"Multiblog mode is off and URI was on non-root blog",
    123                                 'file'=>__FILE__, 'line'=>__LINE__, 'method'=>__METHOD__
    124                         );
     43        if ( !bp_is_root_blog() && !bp_is_multiblog_mode() )
    12544                        return false;
    126                 }
    12745
    128                 // Convert the URI passed by the web server into a walk array
    129                 // ===============================================================
     46        // Define local variables
     47        $root_profile = $match   = false;
     48        $key_slugs    = $matches = $uri_chunks = array();
    13049
    131                 $this->walk = self::buildWalk(&$walk_error);
     50        // Fetch all the WP page names for each component
     51        if ( empty( $bp->pages ) )
     52                $bp->pages = bp_core_get_directory_pages();
    13253
    133                 if($walk_error){
     54        // Ajax or not?
     55        if ( strpos( $_SERVER['REQUEST_URI'], 'wp-load.php' ) )
     56                $path = bp_core_referrer();
     57        else
     58                $path = esc_url( $_SERVER['REQUEST_URI'] );
    13459
    135                         $error = array(
    136                                 'numeric'=>1,
    137                                 'text'=>"Walk error",
    138                                 'file'=>__FILE__, 'line'=>__LINE__, 'method'=>__METHOD__,
    139                                 'child'=>$walk_error
    140                         );
    141                         return false;
    142                 }
     60        // Filter the path
     61        $path = apply_filters( 'bp_uri', $path );
    14362
    144                 // Intersect the walk array with the site's page tree
    145                 // ===============================================================
     63        // Take GET variables off the URL to avoid problems
     64        $path = strtok( $path, '?' );
    14665
    147                 $this->intersect = self::pageIntersect($this->walk, &$intersect_error);
     66        // Fetch current URI and explode each part separated by '/' into an array
     67        $bp_uri = explode( '/', $path );
    14868
    149                 if($intersect_error){
    150 
    151                         $error = array(
    152                                 'numeric'=>2,
    153                                 'text'=>"Intersect error",
    154                                 'data'=>array('walk'=>$this->walk),
    155                                 'file'=>__FILE__, 'line'=>__LINE__, 'method'=>__METHOD__,
    156                                 'child'=>$intersect_error
    157                         );
    158                         return false;
     69        // Loop and remove empties
     70        foreach ( (array) $bp_uri as $key => $uri_chunk ) {
     71                if ( empty( $bp_uri[$key] ) ) {
     72                        unset( $bp_uri[$key] );
    15973                }
    160 
    161                 // Match the intersect to a BuddyPress component, and set the global BP variables
    162                 // ===============================================================
    163 
    164                 $result = self::matchComponent($this->intersect, &$status, &$match_error);
    165 
    166                 if($match_error){
    167 
    168                         $error = array(
    169                                 'numeric'=>3,
    170                                 'text'=>"Match error",
    171                                 'data'=>array('intersect'=>$this->intersect),
    172                                 'file'=>__FILE__, 'line'=>__LINE__, 'method'=>__METHOD__,
    173                                 'child'=>$match_error
    174                         );
    175                         return false;           
    17674                }
    17775
    178                 return $result;
     76        // If running off blog other than root, any subdirectory names must be
     77        // removed from $bp_uri. This includes two cases:
     78        //
     79        //    1. when WP is installed in a subdirectory,
     80        //    2. when BP is running on secondary blog of a subdirectory
     81        //       multisite installation. Phew!
     82        if ( is_multisite() && !is_subdomain_install() && ( bp_is_multiblog_mode() || 1 != bp_get_root_blog_id() ) ) {
    17983
    180         }
     84                // Blow chunks
     85                $chunks = explode( '/', $current_blog->path );
    18186
     87                // If chunks exist...
     88                if ( !empty( $chunks ) ) {
    18289
    183         /**
    184          * Given a URI from the web server, create a walk array
    185          *
    186          * @link http://en.wikipedia.org/wiki/Glossary_of_graph_theory#Walks
    187          * @version 1.6
    188          * @since 1.6
    189          * @return array $walk | Walk array
    190          */
     90                        // ...loop through them...
     91                        foreach( $chunks as $key => $chunk ) {
     92                                $bkey = array_search( $chunk, $bp_uri );
    19193
    192         public function buildWalk(&$error=null) {
    193 
    194 
    195                 if ( strpos($this->request_uri, 'wp-load.php') ){
    196 
    197                         // Try to match on the $_REQUEST['_wp_http_referer'] variable
    198                         if( !empty($this->_wp_http_referer) ){
    199 
    200                                 $ref = $this->_wp_http_referer;
     94                                // ...and unset offending keys
     95                                if ( false !== $bkey ) {
     96                                        unset( $bp_uri[$bkey] );
    20197                        }
    202                         // Otherwise, try to match on the $_SERVER['http_referer'] variable
    203                         elseif( !empty($this->http_referer) ){
    20498
    205                                 $ref = $this->http_referer;
     99                                $bp_uri = array_values( $bp_uri );
    206100                        }
    207 
    208                         // If the $_SERVER['request_uri'] variable is NULL, or if the referer
    209                         // is pointing to itself, this is not a valid request
    210 
    211                         if($ref == $this->request_uri){
    212 
    213                                 $error = array(
    214                                         'numeric'=>1,
    215                                         'text'=>"Invalid AJAX referer",
    216                                         'data'=>array(  "_wp_http_referer"=>$this->_wp_http_referer,
    217                                                         "http_referer"=>$this->http_referer,
    218                                                         "request_uri"=>$this->request_uri
    219                                                      ),
    220                                         'file'=>__FILE__, 'line'=>__LINE__, 'method'=>__METHOD__,
    221                                         'child'=>null
    222                                 );
    223                                 return false;
    224101                        }
    225 
    226                         // The $_wp_http_referer and $http_referer variables have the structure
    227                         // "http://site.com/foo/bar/baz" so we remove the "http://site.com/" from
    228                         // the string to make it have the same structure as $request_uri
    229 
    230                         $referer = explode('/', $ref);
    231                         unset($referer[0], $referer[1], $referer[2]);
    232                         $raw_uri = implode('/', $referer);
    233 
    234102                }
    235                 else {
    236                         // The $request_uri variable has the structure "/foo/bar/baz"
    237                         $raw_uri = esc_url($this->request_uri);
    238                 }
    239103
     104        // Get site path items
     105        $paths = explode( '/', bp_core_get_site_path() );
    240106
    241                 // Parse the URI into an array of tokens
    242                 // =================================================
     107        // Take empties off the end of path
     108        if ( empty( $paths[count( $paths ) - 1] ) )
     109                array_pop( $paths );
    243110
    244                 $raw_uri = apply_filters('bp_uri', $raw_uri);
    245                 $parsed_uri = parse_url($raw_uri);
     111        // Take empties off the start of path
     112        if ( empty( $paths[0] ) )
     113                array_shift( $paths );
    246114
    247                 if(!$parsed_uri){
     115        // Reset indexes
     116        $bp_uri = array_values( $bp_uri );
     117        $paths  = array_values( $paths );
    248118
    249                         $error = array(
    250                                 'numeric'=>2,
    251                                 'text'=>"Couldn't parse supplied URI string",
    252                                 'data'=>$raw_uri,
    253                                 'file'=>__FILE__, 'line'=>__LINE__, 'method'=>__METHOD__,
    254                                 'child'=>null
    255                         );
    256                         return false;
     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] );
    257123                }
    258 
    259                 // Strip any surplus "/" characters from the URI string, and
    260                 // explode it into a walk array
    261 
    262                 $walk = explode('/', trim($parsed_uri["path"], '/') );
    263 
    264 
    265                 // If BP is running off a non-root blog, remove the
    266                 // the blog's base path from the beginning of the walk
    267                 // =================================================
    268 
    269                 if( is_multisite() && !is_subdomain_install() && ( bp_is_multiblog_mode() || bp_get_root_blog_id() != 1 ) ){
    270 
    271                         // Any subdirectory names must be removed from $bp_uri. This includes two cases:
    272                         // a) when WP is installed in a subdirectory,
    273                         // b) when BP is running on secondary blog of a subdirectory multisite install
    274 
    275                         $base_walk = explode( '/', trim($this->current_blog->path, '/') );
    276                         $base_count = count($base_walk);
    277 
    278                         if($base_count > 0){
    279 
    280                                 // Remove the base tokens from the walk array while
    281                                 // simultaneously re-basing the array
    282 
    283                                 $temp_walk = array();
    284 
    285                                 foreach($walk as $index => $token){
    286 
    287                                         if($token == $base_walk[$index]){
    288 
    289                                                 $intersect_count++;
    290124                                        }
    291                                         else {
    292                                                 $temp_walk[] = $token;
    293                                         }
    294                                 }
    295                                 unset($index, $token);
    296125
    297                                 // If any tokens in the base array fail to intersect with
    298                                 // walk array, this is not a valid URI
     126        // Reset the keys by merging with an empty array
     127        $bp_uri = array_merge( array(), $bp_uri );
    299128
    300                                 if($base_count != $intersect_count){
    301 
    302                                         $error = array(
    303                                                 'numeric'=>3,
    304                                                 'text'=>"Malformed base URI",
    305                                                 'data'=>array(
    306                                                                 "walk"=>$walk,
    307                                                                 "base_tokens"=>$base_walk,
    308                                                                 "result"=>$temp_walk
    309                                                  ),
    310                                                 'file'=>__FILE__, 'line'=>__LINE__, 'method'=>__METHOD__,
    311                                                 'child'=>null
    312                                         );
    313                                         return false;
     129        // If a component is set to the front page, force its name into $bp_uri
     130        // so that $current_component is populated (unless a specific WP post is being requested
     131        // via a URL parameter, usually signifying Preview mode)
     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;
    314136                                }
    315 
    316                                 $walk = $temp_walk;
    317 
    318137                        }
    319138
    320                 }
     139        // Keep the unfiltered URI safe
     140        $bp->unfiltered_uri = $bp_uri;
    321141
    322                 return $walk;
     142        // Get slugs of pages into array
     143        foreach ( (array) $bp->pages as $page_key => $bp_page )
     144                $key_slugs[$page_key] = trailingslashit( '/' . $bp_page->slug );
    323145
    324         }
     146        // Bail if keyslugs are empty, as BP is not setup correct
     147        if ( empty( $key_slugs ) )
     148                return;
    325149
    326 
    327         /**
    328          * Intersect a walk with the site's pages tree, returning the endpoint id,
    329          * endpoint slug, and transect array
    330          *
    331          * @link http://en.wikipedia.org/wiki/Tree_(graph_theory)
    332          * @link http://en.wikipedia.org/wiki/Union_(set_theory)
    333          * @version 1.6
    334          * @since 1.6
    335          * @param array $walk | Walk array
    336          * @return array $result | Result array
    337          */
    338 
    339         public function pageIntersect($walk, &$error=null) {
    340 
    341 
    342                 // Fetch the site's pages and loft them into a hierarchical tree
    343                 // ==============================================================
    344 
    345                 $this->flat_pages = self::getPageHierarchy(&$pages_error);
    346 
    347                 if($pages_error){
    348 
    349                         $error = array(
    350                                 'numeric'=>1,
    351                                 'text'=>"Error fetching site pages",
    352                                 'file'=>__FILE__, 'line'=>__LINE__, 'method'=>__METHOD__,
    353                                 'child'=>$pages_error
    354                         );
    355                         return false;
     150        // Loop through page slugs and look for exact match to path
     151        foreach ( $key_slugs as $key => $slug ) {
     152                if ( $slug == $path ) {
     153                        $match      = $bp->pages->{$key};
     154                        $match->key = $key;
     155                        $matches[]  = 1;
     156                        break;
    356157                }
    357 
    358                 $this->lofted_pages = self::loftHierarchy($this->flat_pages, &$loft_error);
    359 
    360                 if($loft_error){
    361 
    362                         $error = array(
    363                                 'numeric'=>2,
    364                                 'text'=>"Error lofting pages array",
    365                                 'data'=>array('flat_pages'=>$this->flat_pages),
    366                                 'file'=>__FILE__, 'line'=>__LINE__, 'method'=>__METHOD__,
    367                                 'child'=>$loft_error
    368                         );
    369                         return false;
    370158                }
    371159
     160        // No exact match, so look for partials
     161        if ( empty( $match ) ) {
    372162
    373                 // Intersect the walk array with the pages tree
    374                 // ==============================================================
     163                // Loop through each page in the $bp->pages global
     164                foreach ( (array) $bp->pages as $page_key => $bp_page ) {
    375165
    376                 $intersect = self::walkIntersectTree($walk, $this->lofted_pages, &$intersect_error);
     166                        // Look for a match (check members first)
     167                        if ( in_array( $bp_page->name, (array) $bp_uri ) ) {
    377168
    378                 if($intersect_error){
     169                                // Match found, now match the slug to make sure.
     170                                $uri_chunks = explode( '/', $bp_page->slug );
    379171
    380                         $error = array(
    381                                 'numeric'=>3,
    382                                 'text'=>"Error intersecting walk with pages tree",
    383                                 'data'=>array("walk"=>$walk, "lofted_pages"=>$this->lofted_pages),
    384                                 'file'=>__FILE__, 'line'=>__LINE__, 'method'=>__METHOD__,
    385                                 'child'=>$intersect_error
    386                         );
    387                         return false;
    388                 }
     172                                // Loop through uri_chunks
     173                                foreach ( (array) $uri_chunks as $key => $uri_chunk ) {
    389174
    390                 return $intersect;
     175                                        // Make sure chunk is in the correct position
     176                                        if ( !empty( $bp_uri[$key] ) && ( $bp_uri[$key] == $uri_chunk ) ) {
     177                                                $matches[] = 1;
    391178
     179                                        // No match
     180                                        } else {
     181                                                $matches[] = 0;
    392182        }
    393 
    394 
    395         /**
    396          * Determine which BP component (if any) matches a given transect
    397          *
    398          * @link http://en.wikipedia.org/wiki/Cycle_(graph_theory)
    399          * @link http://en.wikipedia.org/wiki/Cycle_detection
    400          * @version 1.6
    401          * @since 1.6
    402          * @param array $intersect | Intersect array
    403          * @param array $status | Reason no match was found
    404          * @return bool $result | True on match. False on no match.
    405          */
    406 
    407         public function matchComponent($intersect, &$status, &$error=null) {
    408 
    409            
    410                 $transect = $intersect["transect"];
    411 
    412                 // CASE 1: Front-page component
    413                 // ====================================================================
    414                 if( $intersect["endpoint_id"] === null ){
    415 
    416                         // If a component is set to the front page, and the user is not requesting
    417                         // a specific post via a URL parameter, we have a match
    418 
    419                         $not_preview_mode = ( empty($_GET['p']) && empty($_GET['page_id']) );
    420 
    421                         if($not_preview_mode){
    422 
    423                                 $show_page_on_front = (get_option('show_on_front') == 'page'); // Note comparison operator
    424                                 $post_id = get_option('page_on_front');
    425 
    426                                 if($show_page_on_front && $post_id){
    427 
    428                                         $post = get_post($post_id);
    429 
    430                                         if( !empty($post) ){
    431 
    432                                                 $this->bp->current_component = (string)$post->post_name;
    433 
    434                                                 $status = array(
    435                                                         'numeric'=>1,
    436                                                         'text'=>"Successful match on front-page component.",
    437                                                         'data'=>array('current_component'=>$this->bp->current_component,
    438                                                                       'post_id'=>$post_id,
    439                                                                       'post'=>$post ),
    440                                                         'file'=>__FILE__, 'line'=>__LINE__, 'method'=>__METHOD__
    441                                                 );
    442183                                        }
    443                                         else {
    444184
    445                                                 $error = array(
    446                                                         'numeric'=>1,
    447                                                         'text'=>"Site front page set to component, but component's post was empty",
    448                                                         'data'=>array("post_id"=>$post_id),
    449                                                         'file'=>__FILE__, 'line'=>__LINE__, 'method'=>__METHOD__,
    450                                                         'child'=>null
    451                                                 );
    452                                                 return false;
    453                                         }
    454                                 }
    455                         }
     185                                // Have a match
     186                                if ( !in_array( 0, (array) $matches ) ) {
     187                                        $match      = $bp_page;
     188                                        $match->key = $page_key;
     189                                        break;
     190                                };
    456191
    457                         if(!$route_found){
    458 
    459                                 $status = array(
    460                                         'numeric'=>2,
    461                                         'text'=>"Site front page with no components active on front page.",
    462                                         'file'=>__FILE__, 'line'=>__LINE__, 'method'=>__METHOD__
    463                                 );
    464                                 return false;
     192                                // Unset matches
     193                                unset( $matches );
    465194                        }
    466195
     196                        // Unset uri chunks
     197                        unset( $uri_chunks );
    467198                }
    468 
    469                 // CASE 2: Any non-nested component
    470                 // ====================================================================
    471 
    472                 if(!$this->bp->current_component){
    473 
    474                         $this->bp->current_component = self::getPrimaryComponentName($intersect["endpoint_name"], &$primary_component_error);
    475 
    476                         if($primary_component_error){
    477 
    478                                 $error = array(
    479                                         'numeric'=>2,
    480                                         'text'=>"Error fetching primary component name",
    481                                         'data'=>array("endpoint_name"=>$intersect["endpoint_name"]),
    482                                         'file'=>__FILE__, 'line'=>__LINE__, 'method'=>__METHOD__,
    483                                         'child'=>$primary_component_error
    484                                 );
    485                                 return false;
    486199                        }
    487200
    488                         if($this->bp->current_component){
     201        // URLs with BP_ENABLE_ROOT_PROFILES enabled won't be caught above
     202        if ( empty( $matches ) && bp_core_enable_root_profiles() ) {
    489203
    490                                 $status = array(
    491                                         'numeric'=>3,
    492                                         'text'=>"Successful match on primary component",
    493                                         'data'=>array('current_component'=>$this->bp->current_component),
    494                                         'file'=>__FILE__, 'line'=>__LINE__, 'method'=>__METHOD__
    495                                 );
     204                // Switch field based on compat
     205                $field = bp_is_username_compatibility_mode() ? 'login' : 'slug';
    496206
    497                         }
    498                 }
     207                // Make sure there's a user corresponding to $bp_uri[0]
     208                if ( !empty( $bp->pages->members ) && !empty( $bp_uri[0] ) && $root_profile = get_user_by( $field, $bp_uri[0] ) ) {
    499209
     210                        // Force BP to recognize that this is a members page
     211                        $matches[]  = 1;
     212                        $match      = $bp->pages->members;
     213                        $match->key = 'members';
    500214
    501                 // CASE 3: Root profile
    502                 // ====================================================================
    503 
    504                 if (    !$this->bp->current_component                                           // 1) Has not matched a component in an earlier stage
    505                         && !empty($transect)                                                    // 2) There are tokens in the transect
    506                         && !empty($this->bp->pages->members)                                    // 3) Members component is active
    507                         && defined( 'BP_ENABLE_ROOT_PROFILES' ) && BP_ENABLE_ROOT_PROFILES )    // 4) Root profiles constant is defined and true
    508                 {
    509 
    510                         // Shift the user name off the transect
    511                         $user_name = array_shift($transect);
    512 
    513                         // Switch the user_id based on compatibility mode
    514                         if( bp_is_username_compatibility_mode() ){
    515 
    516                                 $user_id = (int) bp_core_get_userid( urldecode($user_name) );
     215                        // Without the 'members' URL chunk, WordPress won't know which page to load
     216                        // This filter intercepts the WP query and tells it to load the members page
     217                        add_filter( 'request', create_function( '$query_args', '$query_args["pagename"] = "' . $match->name . '"; return $query_args;' ) );
    517218                        }
    518                         else {
    519                                 $user_id = (int) bp_core_get_userid_from_nicename( urldecode($user_name) );
    520219                        }
    521220
    522                         if($user_id){
    523 
    524                                 $this->bp->current_component = "members";
    525                                 $this->bp->displayed_user->id = $user_id;
    526 
    527                                 $status = array(
    528                                         'numeric'=>4,
    529                                         'text'=>"Successful match on root profile",
    530                                         'data'=>array('current_component'=>$this->bp->current_component),
    531                                         'file'=>__FILE__, 'line'=>__LINE__, 'method'=>__METHOD__
    532                                 );
    533 
    534                                 // Without the 'members' URL chunk, WordPress won't know which page to load,
    535                                 // so this filter intercepts the WP query and tells it to load the members page
    536 
    537                                 $function_string  = '$query_args["pagename"] = "';
    538                                 $function_string .= $this->bp->pages->members->name;
    539                                 $function_string .= '"; return $query_args;';
    540 
    541                                 add_filter( 'request', create_function('$query_args', $function_string) );
    542 
     221        // Search doesn't have an associated page, so we check for it separately
     222        if ( !empty( $bp_uri[0] ) && ( bp_get_search_slug() == $bp_uri[0] ) ) {
     223                $matches[]   = 1;
     224                $match       = new stdClass;
     225                $match->key  = 'search';
     226                $match->slug = bp_get_search_slug();
    543227                        }
    544                         else {
    545228
    546                                 $status = array(
    547                                         'numeric'=>5,
    548                                         'text'=>"Root profiles enabled. No matching user.",
    549                                         'file'=>__FILE__, 'line'=>__LINE__, 'method'=>__METHOD__
    550                                 );
     229        // This is not a BuddyPress page, so just return.
     230        if ( empty( $matches ) )
    551231                                return false;
    552                         }
    553232
    554                 }
     233        $wp_rewrite->use_verbose_page_rules = false;
    555234
    556                 // CASE 4: No match
    557                 // ====================================================================
     235        // Find the offset. With $root_profile set, we fudge the offset down so later parsing works
     236        $slug       = !empty ( $match ) ? explode( '/', $match->slug ) : '';
     237        $uri_offset = empty( $root_profile ) ? 0 : -1;
    558238
    559                 if(!$this->bp->current_component){
    560 
    561                         $status = array(
    562                                 'numeric'=>6,
    563                                 'text'=>"No matching components",
    564                                 'data'=>array('intersect'=>$this->intersect, 'walk'=>$this->walk),
    565                                 'file'=>__FILE__, 'line'=>__LINE__, 'method'=>__METHOD__
    566                         );
    567                         return false;
     239        // Rejig the offset
     240        if ( !empty( $slug ) && ( 1 < count( $slug ) ) ) {
     241                array_pop( $slug );
     242                $uri_offset = count( $slug );
    568243                }
    569244
    570                 // Members Component secondary processing
    571                 // ====================================================================
     245        // Global the unfiltered offset to use in bp_core_load_template().
     246        // To avoid PHP warnings in bp_core_load_template(), it must always be >= 0
     247        $bp->unfiltered_uri_offset = $uri_offset >= 0 ? $uri_offset : 0;
    572248
    573                 if( ($this->bp->current_component == "members") && !empty($transect) ){
     249        // We have an exact match
     250        if ( isset( $match->key ) ) {
    574251
    575                         // If the component is "members", the transect must either contain no tokens (show all users on site),
    576                         // or the first token in the transect must be a valid user name (show single user)
     252                // Set current component to matched key
     253                $bp->current_component = $match->key;
    577254
    578                         $user_name = array_shift($transect);
     255                // If members component, do more work to find the actual component
     256                if ( 'members' == $match->key ) {
    579257
    580                         // Switch the user_id based on compatibility mode
    581                         if( bp_is_username_compatibility_mode() ){
     258                        // Viewing a specific user
     259                        if ( !empty( $bp_uri[$uri_offset + 1] ) ) {
    582260
    583                                 $user_id = (int) bp_core_get_userid( urldecode($user_name) );
     261                                // Switch the displayed_user based on compatbility mode
     262                                if ( bp_is_username_compatibility_mode() ) {
     263                                        $bp->displayed_user->id = (int) bp_core_get_userid( urldecode( $bp_uri[$uri_offset + 1] ) );
     264                                } else {
     265                                        $bp->displayed_user->id = (int) bp_core_get_userid_from_nicename( urldecode( $bp_uri[$uri_offset + 1] ) );
    584266                        }
    585                         else {
    586                                 $user_id = (int) bp_core_get_userid_from_nicename( urldecode($user_name) );
    587                         }
    588267
    589                         // CASE 1: Token in first transect position isn't a valid user_id
    590                         // ---------------------------------------------------------------------------------------
    591                         if( empty($user_id) ){
     268                                if ( !bp_displayed_user_id() ) {
    592269
    593                                 $this->bp->current_component = null;    // Prevent components from loading their templates
    594                                 bp_do_404();
     270                                        // Prevent components from loading their templates
     271                                        $bp->current_component = '';
    595272
    596                                 $status = array(
    597                                         'numeric'=>7,
    598                                         'text'=>"Match on members component, but user_id is not valid.",
    599                                         'file'=>__FILE__, 'line'=>__LINE__, 'method'=>__METHOD__
    600                                 );
    601                                 return false;
    602 
     273                                        bp_do_404();
     274                                        return;
    603275                        }
    604276
    605                         elseif( !empty($user_id) ){
    606 
    607                                 $this->bp->displayed_user->id = $user_id;
    608 
    609                                 // CASE 2: Token in first transect position matches a user_id that
    610                                 // has been marked as a spammer
    611                                 // ---------------------------------------------------------------------------------------
    612                                 if( bp_core_is_user_spammer($user_id) ){                               
    613 
    614                                         if( is_super_admin() ){
    615 
    616                                                 bp_core_add_message( __( 'This user has been marked as a spammer. Only site admins can view this profile.', 'buddypress' ), 'error' );
    617                                         }
    618                                         else {
    619                                                 // If the user viewing the profile is not a super-admin, hide the page
     277                                // If the displayed user is marked as a spammer, 404 (unless logged-
     278                                // in user is a super admin)
     279                                if ( bp_displayed_user_id() && bp_is_user_spammer( bp_displayed_user_id() ) ) {
     280                                        if ( bp_current_user_can( 'bp_moderate' ) ) {
     281                                                bp_core_add_message( __( 'This user has been marked as a spammer. Only site admins can view this profile.', 'buddypress' ), 'warning' );
     282                                        } else {
    620283                                                bp_do_404();
    621 
    622                                                 $status = array(
    623                                                         'numeric'=>8,
    624                                                         'text'=>"Match on members component, but user_id is marked as a spammer and viewer is not a super-admin.",
    625                                                         'file'=>__FILE__, 'line'=>__LINE__, 'method'=>__METHOD__
    626                                                 );
    627                                                 return false;
     284                                                return;
    628285                                        }
    629 
    630286                                }
    631                                 // CASE 3: There are one or more tokens left in the transect after the user_name has
    632                                 // been shifted-out. This means we have a secondary component nested inside the members
    633                                 // component. The secondary component's *slug* will be the first token in the transect. We
    634                                 // have to set $this->bp->current_component to the *name* of the secondary component so
    635                                 // BP loads the correct template chain.
    636                                 // ---------------------------------------------------------------------------------------
    637                                 elseif( count($transect) > 0) {
    638287
    639                                         $current_component_slug = array_shift($transect);
     288                                // Bump the offset
     289                                if ( isset( $bp_uri[$uri_offset + 2] ) ) {
     290                                        $bp_uri                = array_merge( array(), array_slice( $bp_uri, $uri_offset + 2 ) );
     291                                        $bp->current_component = $bp_uri[0];
    640292
    641                                         // CASE 3A: Match against the "primary" components that can exist both as a top-level
    642                                         // page and a secondary page nested beneath the "members" component. External plugins
    643                                         // following the "BuddyPress Example Component" pattern will appear in this array.
    644                                         //
    645                                         // TODO: This creates a cardinality problem. Primary components will appear at
    646                                         // both "example.com/members/membername/slug_name" and "example.com/slug_name". This
    647                                         // is further complicated by the fact that some components use the alias location as a
    648                                         // *context*, for example, "activity" at the root node shows activity for all users on
    649                                         // the site, but "activity" nested in the "members" component shows activity for a user.
    650                                         // There needs to be a set of configuration options on the admin back-end to specify
    651                                         // which location to use for a given component. Note that this is a legacy problem with
    652                                         // the original BP router design and we have emulated it for compatibility.
    653                                         // ---------------------------------------------------------------------------------------
    654 
    655                                         $this->bp->current_component = self::getPrimaryComponentName($current_component_slug, &$primary_component_error);
    656 
    657                                         if($primary_component_error){
    658 
    659                                                 $error = array(
    660                                                         'numeric'=>3,
    661                                                         'text'=>"Error fetching primary component name",
    662                                                         'data'=>array("current_component_slug"=>$current_component_slug),
    663                                                         'file'=>__FILE__, 'line'=>__LINE__, 'method'=>__METHOD__,
    664                                                         'child'=>$primary_component_error
    665                                                 );
    666                                                 return false;
     293                                // No component, so default will be picked later
     294                                } else {
     295                                        $bp_uri                = array_merge( array(), array_slice( $bp_uri, $uri_offset + 2 ) );
     296                                        $bp->current_component = '';
    667297                                        }
    668298
    669                                         if($this->bp->current_component != null){
    670 
    671                                                 $status = array(
    672                                                         'numeric'=>9,
    673                                                         'text'=>"Match on members component with primary nested component",
    674                                                         'data'=>array(  'bp_pages'=>$this->bp->pages,
    675                                                                         'active_components'=>$this->bp->active_components,
    676                                                                         'current_component_slug'=>$current_component_slug,
    677                                                                         "component"=>$this->bp->current_component),
    678                                                         'file'=>__FILE__, 'line'=>__LINE__, 'method'=>__METHOD__
    679                                                 );
     299                                // Reset the offset
     300                                $uri_offset = 0;
    680301                                        }
    681                                         else {
    682                                        
    683                                                 // CASE 3B: Match against the "secondary" components that can only exist as a secondary
    684                                                 // page nested beneath the "members" component. Matching is determined by the component's
    685                                                 // action functions, which hook on the 'bp_init' action. Action functions are located
    686                                                 // in "/component_name/bp-component_name-actions.php".
    687                                                 // ---------------------------------------------------------------------------------------
    688 
    689                                                 $this->bp->current_component = $current_component_slug;
    690                                                
    691                                                 $status = array(
    692                                                         'numeric'=>10,
    693                                                         'text'=>"Match on members component, with possible match on secondary nested component",
    694                                                         'data'=>array(  'bp_pages'=>$this->bp->pages,
    695                                                                         'active_components'=>$this->bp->active_components,
    696                                                                         'current_component_slug'=>$current_component_slug),
    697                                                         'file'=>__FILE__, 'line'=>__LINE__, 'method'=>__METHOD__
    698                                                 );
    699                                                
    700 
    701302                                        }
    702                                        
    703303                                }
    704                                 // CASE 4: There are no tokens left in the transect, so we're at the default screen
    705                                 // in the members component. Set $this->bp->current_component to the default profile
    706                                 // component (defined in bp-members-loader.php line 113)
    707                                 // ---------------------------------------------------------------------------------------
    708                                 else {
    709                                         $this->bp->current_component = $this->bp->default_component;
    710304                                       
    711                                         $status = array(
    712                                                 'numeric'=>11,
    713                                                 'text'=>"Match on members component with no nested component",
    714                                                 'data'=>array("component"=>$this->bp->current_component),
    715                                                 'file'=>__FILE__, 'line'=>__LINE__, 'method'=>__METHOD__
    716                                         );
    717                                 }
     305        // Set the current action
     306        $bp->current_action = isset( $bp_uri[$uri_offset + 1] ) ? $bp_uri[$uri_offset + 1] : '';
    718307                               
     308        // Slice the rest of the $bp_uri array and reset offset
     309        $bp_uri      = array_slice( $bp_uri, $uri_offset + 2 );
     310        $uri_offset  = 0;
    719311
    720                         }
     312        // Set the entire URI as the action variables, we will unset the current_component and action in a second
     313        $bp->action_variables = $bp_uri;
    721314
    722 
     315        // Reset the keys by merging with an empty array
     316        $bp->action_variables = array_merge( array(), $bp->action_variables );
    723317                }
    724318
    725 
    726                 // Set BP's global variables
    727                 // ====================================================================
    728 
    729                 if( isset($transect[0]) ){
    730 
    731                         $this->bp->current_action = array_shift($transect);
    732 
    733                             if( count($transect) > 0 ){
    734 
    735                                     $this->bp->action_variables = $transect;
    736                             }
    737                        
    738                 }
    739 
    740                 // Set WP global variables
    741                 // ====================================================================
    742                 if( !empty($object_id) ){
    743 
    744                         // Set WP's internal query variables to the same state they would be in if
    745                         // WP had loaded the page itself instead of BP intercepting the page load
    746                         // and replacing it with our own content
    747 
    748                         // TODO: We've emulated this for compatibility. BP should try to avoid
    749                         // doing this unless actually necessary, because it costs an extra query on
    750                         // each page load.
    751 
    752                         $this->wp_query->queried_object_id = $this->intersect["endpoint_id"];
    753                         $this->wp_query->queried_object    = &get_post($this->intersect["endpoint_id"]);
    754 
    755                 }
    756 
    757         }
    758 
    759 
    760319        /**
    761          * Returns a flat array of the site's page hierarchy
     320 * Are root profiles enabled and allowed
    762321         *
    763          * @version 1.6
    764          * @since 1.6
    765          * @return array $result | Page hierarchy as flat array
     322 * @since BuddyPress (1.6)
     323 * @return bool True if yes, false if no
    766324         */
     325function bp_core_enable_root_profiles() {
    767326
    768         public function getPageHierarchy(&$error=null) {
     327        $retval = false;
    769328
    770                 // TODO: Add caching capabilities
     329        if ( defined( 'BP_ENABLE_ROOT_PROFILES' ) && ( true == BP_ENABLE_ROOT_PROFILES ) )
     330                $retval = true;
    771331           
    772                 global $wpdb;
    773 
    774                 // Always get page data from the root blog, except on multiblog mode, when it comes
    775                 // from the current blog
    776 
    777                 if( bp_is_multiblog_mode() ){
    778 
    779                         $posts_table_name = $wpdb->posts;
     332        return apply_filters( 'bp_core_enable_root_profiles', $retval );
    780333                }
    781                 else {
    782                         $posts_table_name = $wpdb->get_blog_prefix( bp_get_root_blog_id() ) . 'posts';
    783                 }
    784334               
    785                 $sql = "SELECT ID, post_name, post_parent, post_title FROM {$posts_table_name} WHERE post_type = 'page' AND post_status != 'auto-draft'";
    786                 $pages = $wpdb->get_results($sql);
    787 
    788                 // Trap any database errors
    789                 $sql_error = mysql_error($wpdb->dbh);
    790 
    791                 if($sql_error){
    792 
    793                         $error = array(
    794                                 'numeric'=>1,
    795                                 'text'=>"Database error",
    796                                 'data'=>array($sql, $sql_error),
    797                                 'file'=>__FILE__, 'line'=>__LINE__, 'method'=>__METHOD__,
    798                                 'child'=>null
    799                         );
    800 
    801                         return false;
    802                 }
    803 
    804                 // Spin the SQL server's output into a useful format
    805                 $result = array();
    806 
    807                 foreach($pages as $page){
    808 
    809                         $result[$page->ID] = array( "parent"=>$page->post_parent,
    810                                                     "slug"=>$page->post_name,
    811                                                     "title"=>$page->post_title
    812                                              );
    813                 }
    814                 unset($page);
    815 
    816                 return $result;
    817 
    818         }
    819 
    820        
    821335        /**
    822          * Lofts a flat array of nodes into a rooted directed tree in O(n) time
    823          * with only O(n) extra memory. This is also known as the "in-place quick
    824          * union" algorithm.
     336 * bp_core_load_template()
    825337         *
    826          * @link http://en.wikipedia.org/wiki/Tree_(graph_theory)
    827          * @link http://en.wikipedia.org/wiki/Glossary_of_graph_theory#Walks
    828          * @link http://en.wikipedia.org/wiki/Quicksort (in-place version)
     338 * Load a specific template file with fallback support.
    829339         *
    830          * @version 1.6
    831          * @since 1.6
    832          * @param array $nodes | Flat array of nodes
    833          * @return array $result | Hierarchical array of nodes
    834          */
    835 
    836         public function loftHierarchy($nodes) {
    837 
    838                 $tree = array();
    839 
    840                 foreach( $nodes as $node_id => $data){
    841 
    842                         // Note: we can operate directly on the passed parameter, because unless
    843                         // explicitly told not to by using the "&$" sigil, PHP passes copies
    844                         // of variables into a function.
    845 
    846                         $nodes[$node_id]["node_id"] = $node_id;     // Insert the node_id into each node to make the data
    847                                                                     // structure easier to use. Note the unit tests are very
    848                                                                     // picky about the order this gets done in because it
    849                                                                     // affects its position in the output array.
    850                         if( empty($data["parent"]) ){
    851 
    852                                 $tree["children"][$node_id] =& $nodes[$node_id];
    853                         }
    854                         else {
    855                                 $nodes[$data["parent"]]["children"][$node_id] =& $nodes[$node_id];
    856                         }
    857                 }
    858 
    859                 return $tree;
    860         }
    861 
    862 
    863         /**
    864          * Finds the longest intersect between a walk and a tree.
     340 * Example:
     341 *   bp_core_load_template( 'members/index' );
     342 * Loads:
     343 *   wp-content/themes/[activated_theme]/members/index.php
    865344         *
    866          * @link http://en.wikipedia.org/wiki/Glossary_of_graph_theory#Walks
    867          * @link http://en.wikipedia.org/wiki/Breadth-first_search
    868          *
    869          * @version 1.6
    870          * @since 1.6
    871          * @param array $walk | Walk array
    872          * @param array $tree | Tree array
    873          * @return array $result | Walk key and matching node id
     345 * @package BuddyPress Core
     346 * @param $username str Username to check.
     347 * @return false|int The user ID of the matched user, or false.
    874348         */
     349function bp_core_load_template( $templates ) {
     350        global $post, $bp, $wp_query, $wpdb;
    875351
    876         public function walkIntersectTree($walk, $tree, &$error=null) {
    877 
    878 
    879                 if( !is_array($walk) ){
    880 
    881                         $error = array(
    882                                 'numeric'=>1,
    883                                 'text'=>"Walk is not a valid array",
    884                                 'data'=>array( "walk"=>$walk, "tree"=>$tree),
    885                                 'file'=>__FILE__, 'line'=>__LINE__, 'method'=>__METHOD__,
    886                                 'child'=>null
    887                         );
     352        // Determine if the root object WP page exists for this request
     353        // note: get_page_by_path() breaks non-root pages
     354        if ( !empty( $bp->unfiltered_uri_offset ) ) {
     355                if ( !$page_exists = $wpdb->get_var( $wpdb->prepare( "SELECT ID FROM {$wpdb->posts} WHERE post_name = %s", $bp->unfiltered_uri[$bp->unfiltered_uri_offset] ) ) ) {
    888356                        return false;
    889357                }
    890 
    891                 if( !is_array($tree) ){
    892 
    893                         $error = array(
    894                                 'numeric'=>2,
    895                                 'text'=>"Tree is not a valid array",
    896                                 'data'=>array( "walk"=>$walk, "tree"=>$tree),
    897                                 'file'=>__FILE__, 'line'=>__LINE__, 'method'=>__METHOD__,
    898                                 'child'=>null
    899                         );
    900                         return false;
    901358                }
    902359               
    903 
    904                 // Loop through each child node, searching for the
    905                 // child node with the longest walk
    906                 // ================================================
    907 
    908                 $min_offset = null;
    909                 $min_node = null;
    910 
    911                 foreach( $tree["children"] as $node_id => $data){
    912 
    913                         if($data["slug"] == $walk[0]){
    914 
    915                                 $reduced_walk = array_slice($walk, 1);
    916                                 $intersect = self::walkIntersectTree_iterator($reduced_walk, $data);
    917 
    918                                 if( ($min_offset === null) || ($intersect["walk_offset"] < $min_offset) ){
    919 
    920                                         $min_offset = $intersect["walk_offset"];
    921                                         $min_node = $intersect["node_id"];
     360        // Set the root object as the current wp_query-ied item
     361        $object_id = 0;
     362        foreach ( (array) $bp->pages as $page ) {
     363                if ( $page->name == $bp->unfiltered_uri[$bp->unfiltered_uri_offset] ) {
     364                        $object_id = $page->id;
    922365                                }
    923366                        }
    924367
     368        // Make the queried/post object an actual valid page
     369        if ( !empty( $object_id ) ) {
     370                $wp_query->queried_object    = &get_post( $object_id );
     371                $wp_query->queried_object_id = $object_id;
     372                $post                        = $wp_query->queried_object;
    925373                }
    926374
    927                 // Return the child node with the longest walk, or if
    928                 // there was no matching child node, return this node
    929                 // ================================================
     375        // Define local variables
     376        $located_template   = false;
     377        $filtered_templates = array();
    930378
    931                 if($min_offset === null){
    932 
    933                         $result = array(
    934                                             "endpoint_id"=>null,
    935                                             "endpoint_name"=>null,
    936                                             "walk_key"=>null,
    937                                             "transect"=>array()
    938                         );
    939                 }
    940                 else {
    941 
    942                         // Convert offset to array key number so functions further down
    943                         // the chain can use array_slice() to find the tokens after the
    944                         // endpoint that correspond to actions/arguements (if they exist)
    945 
    946                         $walk_key = (count($walk) - $min_offset) - 1;
    947 
    948                         $result = array(
    949                                             "endpoint_id" => $min_node,
    950                                             "endpoint_name"=>$walk[$walk_key],
    951                                             "walk_key" => $walk_key,
    952                                             "transect"=>array_slice($walk, ($walk_key +1) )
    953                         );
    954                 }
    955 
    956                 return $result;
    957 
    958         }
    959 
    960 
    961         /**
    962          * Finds the longest intersect between the walk and the tree.
    963          *
    964          * @link http://en.wikipedia.org/wiki/Glossary_of_graph_theory#Walks
    965          * @link http://en.wikipedia.org/wiki/Breadth-first_search
    966          *
    967          * @version 1.6
    968          * @since 1.6
    969          * @param array $walk | Walk array
    970          * @param array $tree | Tree array
    971          * @return array $result | Walk offset and matching node id
    972          */
    973 
    974         public function walkIntersectTree_iterator($walk, $tree) {
    975 
    976 
    977                 // Calculate offsets
    978                 // ================================================
    979 
    980                 $walk_offset = count($walk);
    981 
    982                 if( is_array($tree["children"]) ){
    983 
    984                         $children_count = count($tree["children"]);
    985                 }
    986                 else {
    987                         $children_count = 0;
    988                 }
    989 
    990                 // If either termination condition is met, return
    991                 // ================================================
    992 
    993                 if( ($walk_offset == 0) || ($children_count == 0) ){
    994 
    995                         $result = array(    "node_id"=>$tree["node_id"],
    996                                             "walk_offset"=>$walk_offset
    997                         );
    998 
    999                         return $result;
    1000                 }
    1001 
    1002                 // Loop through each child node, searching for the
    1003                 // child node with the longest walk
    1004                 // ================================================
    1005 
    1006                 $min_offset = null;
    1007                 $min_node = null;
    1008 
    1009                 foreach( $tree["children"] as $node_id => $data){
    1010 
    1011                         if($data["slug"] == $walk[0]){
    1012 
    1013                                 $reduced_walk = array_slice($walk, 1);
    1014                                 $intersect = self::walkIntersectTree_iterator($reduced_walk, $data);
    1015 
    1016                                 if( ($min_offset === null) || ($intersect["walk_offset"] < $min_offset) ){
    1017 
    1018                                         $min_offset = $intersect["walk_offset"];
    1019                                         $min_node = $intersect["node_id"];
    1020                                 }
    1021                         }
    1022 
    1023                 }
    1024 
    1025                 // Return the child node with the longest walk, or if
    1026                 // there was no matching child node, return this node
    1027                 // ================================================
    1028 
    1029                 if($min_offset === null){
    1030 
    1031                         $result = array(
    1032                                             "node_id"=>$tree["node_id"],
    1033                                             "walk_offset"=>$walk_offset
    1034                         );
    1035                 }
    1036                 else {
    1037                         $result = array(
    1038                                             "node_id"=>$min_node,
    1039                                             "walk_offset"=>$min_offset
    1040                         );
    1041                 }
    1042 
    1043                 return $result;
    1044 
    1045         }
    1046 
    1047 
    1048         /**
    1049          * Checks if a slug matches an active "primary" BuddyPress component. Primary components
    1050          * are components which can exist as a top-level page on the site, and in some cases
    1051          * a secondary page nested below the "members" component. Third-party components following
    1052          * the "BuddyPress Example Component" pattern will appear in the results.
    1053          *
    1054          * @version 1.6
    1055          * @since 1.6
    1056          * @param string $slug | Name of slug to check
    1057          * @return bool/string $result | False on failure. Null on nonexistent. Name of component on success.
    1058          */
    1059 
    1060         public function getPrimaryComponentName($slug, &$error=null) {
    1061 
    1062            
    1063                 if( empty($slug) ){
    1064 
    1065                         $error = array(
    1066                                 'numeric'=>1,
    1067                                 'text'=>"Called with empty slug",
    1068                                 'file'=>__FILE__, 'line'=>__LINE__, 'method'=>__METHOD__,
    1069                                 'child'=>null
    1070                         );
    1071                         return false;
    1072                 }
    1073 
    1074                 // If the BP Pages object hasn't been loaded yet, try to load it
    1075                 if( empty($this->bp->pages) ){
    1076 
    1077                         $this->bp->pages = self::buildDirectoryPages($this->flat_pages);
    1078                 }
    1079 
    1080                 if( empty($this->bp->pages) ){
    1081 
    1082                         $error = array(
    1083                                 'numeric'=>2,
    1084                                 'text'=>"Failed to load BP pages object",
    1085                                 'data'=>array("bp_pages"=>$this->bp->pages),
    1086                                 'file'=>__FILE__, 'line'=>__LINE__, 'method'=>__METHOD__,
    1087                                 'child'=>null
    1088                         );
    1089                         return false;
    1090                 }
    1091 
    1092 
    1093                 foreach($this->bp->pages as $component_name => $data){
    1094 
    1095                         // NOTE: We cannot use an algorithm that checks against $this->bp->active_components,
    1096                         // because its impossible for 3rd-party components to add themselves to this array
    1097                         // using the 'bp_active_components' filter. The filter is placed so early in the call
    1098                         // stack it runs before 3rd-party components can load any of their plugin files.
    1099 
    1100                         if( !array_key_exists($component_name, $this->bp->deactivated_components)   // 1) Component is active
    1101                             && $data->name == $slug )                                               // 2) Slug matches
    1102                         {
    1103                                 return $component_name;
    1104                         }
    1105                 }
    1106                 unset($component_name, $data);
    1107 
    1108                 // Separate check for search component (because its not a real BP component,
    1109                 // and its not included in the $bp->active_components array)
    1110 
    1111                 if($slug == bp_get_search_slug()){
    1112 
    1113                         return "search";
    1114                 }
    1115 
    1116                 return null;
    1117                
    1118         }
    1119 
    1120         /**
    1121          * Generates the BP component pages array
    1122          *
    1123          * @version 1.6
    1124          * @since 1.6
    1125          * @param array $flat_pages | Flat array of all WordPress pages on the site
    1126          * @return obj $pages | Structured object containing page ID's, Names, and Slugs
    1127          */
    1128         function buildDirectoryPages($flat_pages, &$error=null) {
    1129 
    1130 
    1131                 if( empty($flat_pages) ){
    1132 
    1133                         $error = array(
    1134                                 'numeric'=>1,
    1135                                 'text'=>"Called with empty flat_pages array",
    1136                                 'file'=>__FILE__, 'line'=>__LINE__, 'method'=>__METHOD__,
    1137                                 'child'=>null
    1138                         );
    1139                         return false;
    1140                 }
    1141 
    1142 
    1143                 $page_ids = (array)bp_core_get_directory_page_ids();
    1144 
    1145                 if( empty($page_ids) ){
    1146 
    1147                         $error = array(
    1148                                 'numeric'=>2,
    1149                                 'text'=>"BP core directory page ids option is empty",
    1150                                 'file'=>__FILE__, 'line'=>__LINE__, 'method'=>__METHOD__,
    1151                                 'child'=>null
    1152                         );
    1153                         return false;
    1154                 }
    1155        
    1156 
    1157                 $pages = new stdClass;
    1158 
    1159                 // Iterate through each entry in the BP pages config option
    1160                 foreach( $page_ids as $component_id => $bp_page_id ) {
    1161 
    1162                         // Iterate through each WP site page in the flat pages array
    1163                         foreach( $flat_pages as $wp_page_id => $data ) {
    1164 
    1165                                 // If the page ids match, add this page to the components array
    1166                                 if( $wp_page_id == $bp_page_id ) {
    1167 
    1168                                         $pages->{$component_id}->name  = $data['slug'];
    1169                                         $pages->{$component_id}->id    = $wp_page_id;
    1170                                         $pages->{$component_id}->title = $data['title'];
    1171 
    1172                                         $stem = array();
    1173                                         $stem[] = $data['slug'];
    1174 
    1175                                         $parent = $data['parent'];
    1176 
    1177                                         // If the page is not attached to the root node, traverse the page tree backwards to the
    1178                                         // root node generating the reverse walk, then flip it and implode it to a string.
    1179 
    1180                                         while( $parent != 0 ){
    1181 
    1182                                                 $stem[] = $flat_pages[$parent]['slug'];
    1183                                                 $parent = $flat_pages[$parent]['parent'];
    1184                                         }
    1185 
    1186                                         // TODO: BuddyPress incorrectly calls this a "slug", which is confusing. The correct term
    1187                                         // is a "stem" (in string form) and a "walk" (in array form).
    1188 
    1189                                         $pages->{$component_id}->slug = implode( '/', array_reverse( (array)$stem ) );
    1190                                 }
    1191 
    1192                                 unset($slug);
    1193                         }
    1194                         unset($wp_page_id, $data);
    1195 
    1196                 }
    1197                 unset($component_id, $bp_page_id);
    1198        
    1199                 return apply_filters( 'bp_core_get_directory_pages', $pages );
    1200                
    1201         }
    1202 
    1203 
    1204         /**
    1205          * Load a specific template file, with fallback support.
    1206          *
    1207          * Example: bp_core_load_template( 'members/index' );
    1208          * Loads: wp-content/themes/[activated_theme]/members/index.php
    1209          *
    1210          * @version 1.6
    1211          * @since 1.6
    1212          * @param string/array $templates | Single template name as string. Multiple template names as array of string.
    1213          * @return bool/die $result | False on failure. Loads template and terminates thread on success.
    1214          */
    1215         function loadTemplate($templates, &$error=null) {
    1216 
    1217 
    1218                 if( !$this->intersect["endpoint_id"] ){
    1219 
    1220                         $error = array(
    1221                                 'numeric'=>1,
    1222                                 'text'=>"Cannot load template because router was unable to intersect the current
    1223                                          request URI with any pages in the site's page tree.",
    1224                                 'data'=>array("intersect"=>$this->intersect,
    1225                                               "walk"=>$this->walk,
    1226                                               "templates"=>$templates),
    1227                                 'file'=>__FILE__, 'line'=>__LINE__, 'method'=>__METHOD__,
    1228                                 'child'=>null
    1229                         );
    1230                         return false;
    1231                 }
    1232 
    1233                 // Add a ".php" suffix to each template file in the $templates array
    1234                 foreach( (array)$templates as $template ){
    1235                    
     379        // Fetch each template and add the php suffix
     380        foreach ( (array) $templates as $template )
    1236381                        $filtered_templates[] = $template . '.php';
    1237                 }
    1238382
    1239383                // Filter the template locations so that plugins can alter where they are located
    1240384                $located_template = apply_filters( 'bp_located_template', locate_template( (array) $filtered_templates, false ), $filtered_templates );
     385        if ( !empty( $located_template ) ) {
    1241386
    1242                 if($located_template){
     387                // Template was located, lets set this as a valid page and not a 404.
     388                status_header( 200 );
     389                $wp_query->is_page = $wp_query->is_singular = true;
     390                $wp_query->is_404  = false;
    1243391
    1244                         // Explicitly set WP's internal query variables to the correct state (because the
    1245                         // default is to 404 the page)
     392                do_action( 'bp_core_pre_load_template', $located_template );
    1246393
    1247                         $this->wp_query->is_page = true;
    1248                         $this->wp_query->is_404 = false;
    1249 
    1250                         // Explicitly set the HTTP headers. Note that this only sets the headers for the web
    1251                         // page. The web server generates its own headers for individual items such as images
    1252                         // and CSS stylesheets loaded by the page.
    1253 
    1254                         $protocol = $_SERVER["SERVER_PROTOCOL"];
    1255                         $code = 200;
    1256                         $text = "OK";
    1257 
    1258                         if( ($protocol != 'HTTP/1.1') && ($protocol != 'HTTP/1.0') ){
    1259 
    1260                                 $protocol = 'HTTP/1.0';
    1261                         }
    1262 
    1263                         $status_header = "$protocol $code $text";
    1264 
    1265                         header($status_header, true, $code);
    1266 
    1267394                        load_template( apply_filters( 'bp_load_template', $located_template ) );
    1268395
     396                do_action( 'bp_core_post_load_template', $located_template );
    1269397                }
    1270398
    1271                 if(!$this->unit_test){
    1272 
    1273                         // TODO: It's bad practice to place silent die() calls all over an application's code because it
    1274                         // makes it very difficult to unit-test. There should only be ONE silent die() in an app, used on
    1275                         // successful termination in the controller's core. Beyond that, die() should ONLY be used in
    1276                         // a "kernel panic" situation, and should ALWAYS include debugging info like line numbers and
    1277                         // a variable dump.
    1278 
     399        // Kill any other output after this.
    1279400                        die;
    1280401                }
    1281402
    1282         }
    1283 
    1284 
    1285        
    1286 } // End class BP_router
    1287 
    1288 
    1289 
    1290 // BRIDGE FUNCTIONS
    1291 // ========================================================================================================
    1292 // These functions allow legacy code to access the new router class
    1293 
    1294 
    1295 function bp_core_set_uri_globals(){
    1296 
    1297         global $bp;
    1298         $bp->router = new BP_router();
    1299 
    1300         $result = $bp->router->route(&$status, &$error);
    1301         return $result;
    1302 }
    1303 
    1304 function bp_core_load_template($templates){
    1305 
    1306         global $bp;
    1307         $result = $bp->router->loadTemplate($templates, &$error);
    1308         return $result;
    1309 }
    1310 
    1311 // ========================================================================================================
    1312 
    1313 
    1314403/**
    1315  * Are root profiles enabled and allowed
    1316  *
    1317  * @since BuddyPress (1.6)
    1318  * @return bool True if yes, false if no
    1319  */
    1320 function bp_core_enable_root_profiles() {
    1321 
    1322         $retval = false;
    1323 
    1324         if ( defined( 'BP_ENABLE_ROOT_PROFILES' ) && ( true == BP_ENABLE_ROOT_PROFILES ) )
    1325                 $retval = true;
    1326 
    1327         return apply_filters( 'bp_core_enable_root_profiles', $retval );
    1328 }
    1329 
    1330 /**
    1331404 * bp_core_catch_profile_uri()
    1332405 *
    1333406 * If the extended profiles component is not installed we still need
     
    1587660        }
    1588661}
    1589662add_action( 'wp_head', '_bp_maybe_remove_rel_canonical', 8 );
    1590 
    1591 
    1592  No newline at end of file
    1593663?>