| 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] ); |
| | 112 | // 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 | ); |
| | 125 | return false; |
| | 126 | } |
| | 127 | |
| | 128 | // Convert the URI passed by the web server into a walk array |
| | 129 | // =============================================================== |
| | 130 | |
| | 131 | $this->walk = self::buildWalk(&$walk_error); |
| | 132 | |
| | 133 | if($walk_error){ |
| | 134 | |
| | 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 | } |
| | 143 | |
| | 144 | // Intersect the walk array with the site's page tree |
| | 145 | // =============================================================== |
| | 146 | |
| | 147 | $this->intersect = self::pageIntersect($this->walk, &$intersect_error); |
| | 148 | |
| | 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; |
| | 159 | } |
| | 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; |
| 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; |
| | 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 | */ |
| | 191 | |
| | 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; |
| | 201 | } |
| | 202 | // Otherwise, try to match on the $_SERVER['http_referer'] variable |
| | 203 | elseif( !empty($this->http_referer) ){ |
| | 204 | |
| | 205 | $ref = $this->http_referer; |
| | 206 | } |
| | 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; |
| | 224 | } |
| | 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 | |
| | 234 | } |
| | 235 | else { |
| | 236 | // The $request_uri variable has the structure "/foo/bar/baz" |
| | 237 | $raw_uri = esc_url($this->request_uri); |
| | 238 | } |
| | 239 | |
| | 240 | |
| | 241 | // Parse the URI into an array of tokens |
| | 242 | // ================================================= |
| | 243 | |
| | 244 | $raw_uri = apply_filters('bp_uri', $raw_uri); |
| | 245 | $parsed_uri = parse_url($raw_uri); |
| | 246 | |
| | 247 | if(!$parsed_uri){ |
| | 248 | |
| | 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; |
| | 257 | } |
| | 258 | |
| | 259 | // Strip any surplus "/" characters from the URI string, and |
| | 260 | // explode it into a walk array |
| | 261 | $walk = explode('/', trim($parsed_uri["path"], '/') ); |
| | 262 | |
| | 263 | // Any subdirectory names must be removed from $bp_uri. This includes two cases: |
| | 264 | // a) when WP is installed in a subdirectory, |
| | 265 | // b) when BP is running on secondary blog of a subdirectory multisite install |
| | 266 | |
| | 267 | $base_walk = explode( '/', trim($this->current_blog->path, '/') ); |
| | 268 | $base_count = count($base_walk); |
| | 269 | |
| | 270 | if($base_count > 0){ |
| | 271 | |
| | 272 | // Remove the base tokens from the walk array while |
| | 273 | // simultaneously re-basing the array |
| | 274 | |
| | 275 | $temp_walk = array(); |
| | 276 | $intersect_count = 0; |
| | 277 | foreach($walk as $index => $token){ |
| | 278 | |
| | 279 | if( isset( $base_walk[$index] ) && $token == $base_walk[$index] ){ |
| | 280 | |
| | 281 | $intersect_count++; |
| | 282 | } |
| | 283 | else { |
| | 284 | $temp_walk[] = $token; |
| | 285 | } |
| | 286 | } |
| | 287 | unset($index, $token); |
| | 288 | |
| | 289 | // If any tokens in the base array fail to intersect with |
| | 290 | // walk array, this is not a valid URI |
| | 291 | |
| | 292 | if($base_count != $intersect_count){ |
| | 293 | |
| | 294 | $error = array( |
| | 295 | 'numeric'=>3, |
| | 296 | 'text'=>"Malformed base URI", |
| | 297 | 'data'=>array( |
| | 298 | "walk"=>$walk, |
| | 299 | "base_tokens"=>$base_walk, |
| | 300 | "result"=>$temp_walk |
| | 301 | ), |
| | 302 | 'file'=>__FILE__, 'line'=>__LINE__, 'method'=>__METHOD__, |
| | 303 | 'child'=>null |
| | 304 | ); |
| | 305 | return false; |
| | 306 | } |
| | 307 | |
| | 308 | $walk = $temp_walk; |
| | 309 | |
| 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; |
| | 331 | |
| | 332 | // Fetch the site's pages and loft them into a hierarchical tree |
| | 333 | // ============================================================== |
| | 334 | |
| | 335 | $this->flat_pages = self::getPageHierarchy(&$pages_error); |
| | 336 | |
| | 337 | if($pages_error){ |
| | 338 | |
| | 339 | $error = array( |
| | 340 | 'numeric'=>1, |
| | 341 | 'text'=>"Error fetching site pages", |
| | 342 | 'file'=>__FILE__, 'line'=>__LINE__, 'method'=>__METHOD__, |
| | 343 | 'child'=>$pages_error |
| | 344 | ); |
| | 345 | return false; |
| | 346 | } |
| | 347 | |
| | 348 | $this->lofted_pages = self::loftHierarchy($this->flat_pages, &$loft_error); |
| | 349 | |
| | 350 | if($loft_error){ |
| | 351 | |
| | 352 | $error = array( |
| | 353 | 'numeric'=>2, |
| | 354 | 'text'=>"Error lofting pages array", |
| | 355 | 'data'=>array('flat_pages'=>$this->flat_pages), |
| | 356 | 'file'=>__FILE__, 'line'=>__LINE__, 'method'=>__METHOD__, |
| | 357 | 'child'=>$loft_error |
| | 358 | ); |
| | 359 | return false; |
| | 360 | } |
| | 361 | |
| | 362 | |
| | 363 | // Intersect the walk array with the pages tree |
| | 364 | // ============================================================== |
| | 365 | |
| | 366 | $intersect = self::walkIntersectTree($walk, $this->lofted_pages, &$intersect_error); |
| | 367 | |
| | 368 | if($intersect_error){ |
| | 369 | |
| | 370 | $error = array( |
| | 371 | 'numeric'=>3, |
| | 372 | 'text'=>"Error intersecting walk with pages tree", |
| | 373 | 'data'=>array("walk"=>$walk, "lofted_pages"=>$this->lofted_pages), |
| | 374 | 'file'=>__FILE__, 'line'=>__LINE__, 'method'=>__METHOD__, |
| | 375 | 'child'=>$intersect_error |
| | 376 | ); |
| | 377 | return false; |
| 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 { |
| | 575 | $user_id = (int) bp_core_get_userid( urldecode($user_name) ); |
| | 576 | } |
| | 577 | else { |
| | 578 | $user_id = (int) bp_core_get_userid_from_nicename( urldecode($user_name) ); |
| | 579 | } |
| | 580 | |
| | 581 | // CASE 1: Token in first transect position isn't a valid user_id |
| | 582 | // --------------------------------------------------------------------------------------- |
| | 583 | if( empty($user_id) ){ |
| | 584 | |
| | 585 | $this->bp->current_component = null; // Prevent components from loading their templates |
| | 586 | bp_do_404(); |
| | 587 | |
| | 588 | $status = array( |
| | 589 | 'numeric'=>7, |
| | 590 | 'text'=>"Match on members component, but user_id is not valid.", |
| | 591 | 'file'=>__FILE__, 'line'=>__LINE__, 'method'=>__METHOD__ |
| | 592 | ); |
| | 593 | return false; |
| | 594 | |
| | 595 | } |
| | 596 | |
| | 597 | elseif( !empty($user_id) ){ |
| | 598 | |
| | 599 | $this->bp->displayed_user->id = $user_id; |
| | 600 | |
| | 601 | // CASE 2: Token in first transect position matches a user_id that |
| | 602 | // has been marked as a spammer |
| | 603 | // --------------------------------------------------------------------------------------- |
| | 604 | if( bp_is_user_spammer($user_id) ){ |
| | 605 | |
| | 606 | if( is_super_admin() ){ |
| | 607 | |
| | 608 | bp_core_add_message( __( 'This user has been marked as a spammer. Only site admins can view this profile.', 'buddypress' ), 'error' ); |
| | 609 | } |
| | 610 | else { |
| | 611 | // If the user viewing the profile is not a super-admin, hide the page |
| | 623 | // CASE 3: There are one or more tokens left in the transect after the user_name has |
| | 624 | // been shifted-out. This means we have a secondary component nested inside the members |
| | 625 | // component. The secondary component's *slug* will be the first token in the transect. We |
| | 626 | // have to set $this->bp->current_component to the *name* of the secondary component so |
| | 627 | // BP loads the correct template chain. |
| | 628 | // --------------------------------------------------------------------------------------- |
| | 629 | elseif( count($transect) > 0) { |
| | 630 | |
| | 631 | $current_component_slug = array_shift($transect); |
| | 632 | |
| | 633 | // CASE 3A: Match against the "primary" components that can exist both as a top-level |
| | 634 | // page and a secondary page nested beneath the "members" component. External plugins |
| | 635 | // following the "BuddyPress Example Component" pattern will appear in this array. |
| | 636 | // |
| | 637 | // TODO: This creates a cardinality problem. Primary components will appear at |
| | 638 | // both "example.com/members/membername/slug_name" and "example.com/slug_name". This |
| | 639 | // is further complicated by the fact that some components use the alias location as a |
| | 640 | // *context*, for example, "activity" at the root node shows activity for all users on |
| | 641 | // the site, but "activity" nested in the "members" component shows activity for a user. |
| | 642 | // There needs to be a set of configuration options on the admin back-end to specify |
| | 643 | // which location to use for a given component. Note that this is a legacy problem with |
| | 644 | // the original BP router design and we have emulated it for compatibility. |
| | 645 | // --------------------------------------------------------------------------------------- |
| | 646 | |
| | 647 | $this->bp->current_component = self::getPrimaryComponentName($current_component_slug, &$primary_component_error); |
| | 648 | |
| | 649 | if($primary_component_error){ |
| | 650 | |
| | 651 | $error = array( |
| | 652 | 'numeric'=>3, |
| | 653 | 'text'=>"Error fetching primary component name", |
| | 654 | 'data'=>array("current_component_slug"=>$current_component_slug), |
| | 655 | 'file'=>__FILE__, 'line'=>__LINE__, 'method'=>__METHOD__, |
| | 656 | 'child'=>$primary_component_error |
| | 657 | ); |
| | 658 | return false; |
| | 659 | } |
| | 660 | |
| | 661 | if($this->bp->current_component != null){ |
| | 662 | |
| | 663 | $status = array( |
| | 664 | 'numeric'=>9, |
| | 665 | 'text'=>"Match on members component with primary nested component", |
| | 666 | 'data'=>array( 'bp_pages'=>$this->bp->pages, |
| | 667 | 'active_components'=>$this->bp->active_components, |
| | 668 | 'current_component_slug'=>$current_component_slug, |
| | 669 | "component"=>$this->bp->current_component), |
| | 670 | 'file'=>__FILE__, 'line'=>__LINE__, 'method'=>__METHOD__ |
| | 671 | ); |
| | 672 | } |
| | 673 | else { |
| | 674 | |
| | 675 | // CASE 3B: Match against the "secondary" components that can only exist as a secondary |
| | 676 | // page nested beneath the "members" component. Matching is determined by the component's |
| | 677 | // action functions, which hook on the 'bp_init' action. Action functions are located |
| | 678 | // in "/component_name/bp-component_name-actions.php". |
| | 679 | // --------------------------------------------------------------------------------------- |
| | 680 | |
| | 681 | $this->bp->current_component = $current_component_slug; |
| | 682 | |
| | 683 | $status = array( |
| | 684 | 'numeric'=>10, |
| | 685 | 'text'=>"Match on members component, with possible match on secondary nested component", |
| | 686 | 'data'=>array( 'bp_pages'=>$this->bp->pages, |
| | 687 | 'active_components'=>$this->bp->active_components, |
| | 688 | 'current_component_slug'=>$current_component_slug), |
| | 689 | 'file'=>__FILE__, 'line'=>__LINE__, 'method'=>__METHOD__ |
| | 690 | ); |
| 335 | | /** |
| 336 | | * bp_core_load_template() |
| 337 | | * |
| 338 | | * Load a specific template file with fallback support. |
| 339 | | * |
| 340 | | * Example: |
| 341 | | * bp_core_load_template( 'members/index' ); |
| 342 | | * Loads: |
| 343 | | * wp-content/themes/[activated_theme]/members/index.php |
| 344 | | * |
| 345 | | * @package BuddyPress Core |
| 346 | | * @param $username str Username to check. |
| 347 | | * @return false|int The user ID of the matched user, or false. |
| 348 | | */ |
| 349 | | function bp_core_load_template( $templates ) { |
| 350 | | global $post, $bp, $wp_query, $wpdb; |
| | 778 | $sql = "SELECT ID, post_name, post_parent, post_title FROM {$posts_table_name} WHERE post_type = 'page' AND post_status != 'auto-draft'"; |
| | 779 | $pages = $wpdb->get_results($sql); |
| | 780 | |
| | 781 | // Trap any database errors |
| | 782 | $sql_error = mysql_error($wpdb->dbh); |
| | 783 | |
| | 784 | if($sql_error){ |
| | 785 | |
| | 786 | $error = array( |
| | 787 | 'numeric'=>1, |
| | 788 | 'text'=>"Database error", |
| | 789 | 'data'=>array($sql, $sql_error), |
| | 790 | 'file'=>__FILE__, 'line'=>__LINE__, 'method'=>__METHOD__, |
| | 791 | 'child'=>null |
| | 792 | ); |
| | 796 | |
| | 797 | // Spin the SQL server's output into a useful format |
| | 798 | $result = array(); |
| | 799 | |
| | 800 | foreach($pages as $page){ |
| | 801 | |
| | 802 | $result[$page->ID] = array( "parent"=>$page->post_parent, |
| | 803 | "slug"=>$page->post_name, |
| | 804 | "title"=>$page->post_title |
| | 805 | ); |
| | 806 | } |
| | 807 | unset($page); |
| | 808 | |
| | 809 | return $result; |
| | 810 | |
| | 811 | } |
| | 812 | |
| | 813 | |
| | 814 | /** |
| | 815 | * Lofts a flat array of nodes into a rooted directed tree in O(n) time |
| | 816 | * with only O(n) extra memory. This is also known as the "in-place quick |
| | 817 | * union" algorithm. |
| | 818 | * |
| | 819 | * @link http://en.wikipedia.org/wiki/Tree_(graph_theory) |
| | 820 | * @link http://en.wikipedia.org/wiki/Glossary_of_graph_theory#Walks |
| | 821 | * @link http://en.wikipedia.org/wiki/Quicksort (in-place version) |
| | 822 | * |
| | 823 | * @version 1.6 |
| | 824 | * @since 1.6 |
| | 825 | * @param array $nodes | Flat array of nodes |
| | 826 | * @return array $result | Hierarchical array of nodes |
| | 827 | */ |
| | 828 | |
| | 829 | public function loftHierarchy($nodes) { |
| | 830 | |
| | 831 | $tree = array(); |
| | 832 | |
| | 833 | foreach( $nodes as $node_id => $data){ |
| | 834 | |
| | 835 | // Note: we can operate directly on the passed parameter, because unless |
| | 836 | // explicitly told not to by using the "&$" sigil, PHP passes copies |
| | 837 | // of variables into a function. |
| | 838 | |
| | 839 | $nodes[$node_id]["node_id"] = $node_id; // Insert the node_id into each node to make the data |
| | 840 | // structure easier to use. Note the unit tests are very |
| | 841 | // picky about the order this gets done in because it |
| | 842 | // affects its position in the output array. |
| | 843 | if( empty($data["parent"]) ){ |
| | 844 | |
| | 845 | $tree["children"][$node_id] =& $nodes[$node_id]; |
| | 846 | } |
| | 847 | else { |
| | 848 | $nodes[$data["parent"]]["children"][$node_id] =& $nodes[$node_id]; |
| | 849 | } |
| | 850 | } |
| | 851 | |
| | 852 | return $tree; |
| 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; |
| | 855 | |
| | 856 | /** |
| | 857 | * Finds the longest intersect between a walk and a tree. |
| | 858 | * |
| | 859 | * @link http://en.wikipedia.org/wiki/Glossary_of_graph_theory#Walks |
| | 860 | * @link http://en.wikipedia.org/wiki/Breadth-first_search |
| | 861 | * |
| | 862 | * @version 1.6 |
| | 863 | * @since 1.6 |
| | 864 | * @param array $walk | Walk array |
| | 865 | * @param array $tree | Tree array |
| | 866 | * @return array $result | Walk key and matching node id |
| | 867 | */ |
| | 868 | |
| | 869 | public function walkIntersectTree($walk, $tree, &$error=null) { |
| | 870 | |
| | 871 | |
| | 872 | if( !is_array($walk) ){ |
| | 873 | |
| | 874 | $error = array( |
| | 875 | 'numeric'=>1, |
| | 876 | 'text'=>"Walk is not a valid array", |
| | 877 | 'data'=>array( "walk"=>$walk, "tree"=>$tree), |
| | 878 | 'file'=>__FILE__, 'line'=>__LINE__, 'method'=>__METHOD__, |
| | 879 | 'child'=>null |
| | 880 | ); |
| | 881 | return false; |
| | 882 | } |
| | 883 | |
| | 884 | if( !is_array($tree) ){ |
| | 885 | |
| | 886 | $error = array( |
| | 887 | 'numeric'=>2, |
| | 888 | 'text'=>"Tree is not a valid array", |
| | 889 | 'data'=>array( "walk"=>$walk, "tree"=>$tree), |
| | 890 | 'file'=>__FILE__, 'line'=>__LINE__, 'method'=>__METHOD__, |
| | 891 | 'child'=>null |
| | 892 | ); |
| | 893 | return false; |
| | 894 | } |
| | 895 | |
| | 896 | |
| | 897 | // Loop through each child node, searching for the |
| | 898 | // child node with the longest walk |
| | 899 | // ================================================ |
| | 900 | |
| | 901 | $min_offset = null; |
| | 902 | $min_node = null; |
| | 903 | |
| | 904 | foreach( $tree["children"] as $node_id => $data){ |
| | 905 | |
| | 906 | if( isset( $walk[0] ) && $data["slug"] == $walk[0] ){ |
| | 907 | |
| | 908 | $reduced_walk = array_slice($walk, 1); |
| | 909 | $intersect = self::walkIntersectTree_iterator($reduced_walk, $data); |
| | 910 | |
| | 911 | if( ($min_offset === null) || ($intersect["walk_offset"] < $min_offset) ){ |
| | 912 | |
| | 913 | $min_offset = $intersect["walk_offset"]; |
| | 914 | $min_node = $intersect["node_id"]; |
| | 915 | } |
| | 916 | } |
| | 917 | |
| | 918 | } |
| | 919 | |
| | 920 | // Return the child node with the longest walk, or if |
| | 921 | // there was no matching child node, return this node |
| | 922 | // ================================================ |
| | 923 | |
| | 924 | if($min_offset === null){ |
| | 925 | |
| | 926 | $result = array( |
| | 927 | "endpoint_id"=>null, |
| | 928 | "endpoint_name"=>null, |
| | 929 | "walk_key"=>null, |
| | 930 | "transect"=>array() |
| | 931 | ); |
| | 932 | } |
| | 933 | else { |
| | 934 | |
| | 935 | // Convert offset to array key number so functions further down |
| | 936 | // the chain can use array_slice() to find the tokens after the |
| | 937 | // endpoint that correspond to actions/arguements (if they exist) |
| | 938 | |
| | 939 | $walk_key = (count($walk) - $min_offset) - 1; |
| | 940 | |
| | 941 | $result = array( |
| | 942 | "endpoint_id" => $min_node, |
| | 943 | "endpoint_name"=>$walk[$walk_key], |
| | 944 | "walk_key" => $walk_key, |
| | 945 | "transect"=>array_slice($walk, ($walk_key +1) ) |
| | 946 | ); |
| 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; |
| | 953 | |
| | 954 | /** |
| | 955 | * Finds the longest intersect between the walk and the tree. |
| | 956 | * |
| | 957 | * @link http://en.wikipedia.org/wiki/Glossary_of_graph_theory#Walks |
| | 958 | * @link http://en.wikipedia.org/wiki/Breadth-first_search |
| | 959 | * |
| | 960 | * @version 1.6 |
| | 961 | * @since 1.6 |
| | 962 | * @param array $walk | Walk array |
| | 963 | * @param array $tree | Tree array |
| | 964 | * @return array $result | Walk offset and matching node id |
| | 965 | */ |
| | 966 | |
| | 967 | public function walkIntersectTree_iterator($walk, $tree) { |
| | 968 | |
| | 969 | |
| | 970 | // Calculate offsets |
| | 971 | // ================================================ |
| | 972 | |
| | 973 | $walk_offset = count($walk); |
| | 974 | |
| | 975 | if( isset( $tree['children'] ) && is_array($tree["children"]) ){ |
| | 976 | |
| | 977 | $children_count = count($tree["children"]); |
| | 978 | } |
| | 979 | else { |
| | 980 | $children_count = 0; |
| | 981 | } |
| | 982 | |
| | 983 | // If either termination condition is met, return |
| | 984 | // ================================================ |
| | 985 | |
| | 986 | if( ($walk_offset == 0) || ($children_count == 0) ){ |
| | 987 | |
| | 988 | $result = array( "node_id"=>$tree["node_id"], |
| | 989 | "walk_offset"=>$walk_offset |
| | 990 | ); |
| | 991 | |
| | 992 | return $result; |
| | 993 | } |
| | 994 | |
| | 995 | // Loop through each child node, searching for the |
| | 996 | // child node with the longest walk |
| | 997 | // ================================================ |
| | 998 | |
| | 999 | $min_offset = null; |
| | 1000 | $min_node = null; |
| | 1001 | |
| | 1002 | foreach( $tree["children"] as $node_id => $data){ |
| | 1003 | |
| | 1004 | if($data["slug"] == $walk[0]){ |
| | 1005 | |
| | 1006 | $reduced_walk = array_slice($walk, 1); |
| | 1007 | $intersect = self::walkIntersectTree_iterator($reduced_walk, $data); |
| | 1008 | |
| | 1009 | if( ($min_offset === null) || ($intersect["walk_offset"] < $min_offset) ){ |
| | 1010 | |
| | 1011 | $min_offset = $intersect["walk_offset"]; |
| | 1012 | $min_node = $intersect["node_id"]; |
| | 1013 | } |
| | 1014 | } |
| | 1015 | |
| | 1016 | } |
| | 1017 | |
| | 1018 | // Return the child node with the longest walk, or if |
| | 1019 | // there was no matching child node, return this node |
| | 1020 | // ================================================ |
| | 1021 | |
| | 1022 | if($min_offset === null){ |
| | 1023 | |
| | 1024 | $result = array( |
| | 1025 | "node_id"=>$tree["node_id"], |
| | 1026 | "walk_offset"=>$walk_offset |
| | 1027 | ); |
| | 1028 | } |
| | 1029 | else { |
| | 1030 | $result = array( |
| | 1031 | "node_id"=>$min_node, |
| | 1032 | "walk_offset"=>$min_offset |
| | 1033 | ); |
| | 1034 | } |
| | 1035 | |
| | 1036 | return $result; |
| | 1037 | |
| 379 | | // Fetch each template and add the php suffix |
| 380 | | foreach ( (array) $templates as $template ) |
| 381 | | $filtered_templates[] = $template . '.php'; |
| | 1041 | /** |
| | 1042 | * Checks if a slug matches an active "primary" BuddyPress component. Primary components |
| | 1043 | * are components which can exist as a top-level page on the site, and in some cases |
| | 1044 | * a secondary page nested below the "members" component. Third-party components following |
| | 1045 | * the "BuddyPress Example Component" pattern will appear in the results. |
| | 1046 | * |
| | 1047 | * @version 1.6 |
| | 1048 | * @since 1.6 |
| | 1049 | * @param string $slug | Name of slug to check |
| | 1050 | * @return bool/string $result | False on failure. Null on nonexistent. Name of component on success. |
| | 1051 | */ |
| | 1052 | |
| | 1053 | public function getPrimaryComponentName($slug, &$error=null) { |
| | 1054 | |
| | 1055 | |
| | 1056 | if( empty($slug) ){ |
| | 1057 | |
| | 1058 | $error = array( |
| | 1059 | 'numeric'=>1, |
| | 1060 | 'text'=>"Called with empty slug", |
| | 1061 | 'file'=>__FILE__, 'line'=>__LINE__, 'method'=>__METHOD__, |
| | 1062 | 'child'=>null |
| | 1063 | ); |
| | 1064 | return false; |
| | 1065 | } |
| | 1066 | |
| | 1067 | // If the BP Pages object hasn't been loaded yet, try to load it |
| | 1068 | if( empty($this->bp->pages) ){ |
| | 1069 | |
| | 1070 | $this->bp->pages = self::buildDirectoryPages($this->flat_pages); |
| | 1071 | } |
| | 1072 | |
| | 1073 | if( empty($this->bp->pages) ){ |
| | 1074 | |
| | 1075 | $error = array( |
| | 1076 | 'numeric'=>2, |
| | 1077 | 'text'=>"Failed to load BP pages object", |
| | 1078 | 'data'=>array("bp_pages"=>$this->bp->pages), |
| | 1079 | 'file'=>__FILE__, 'line'=>__LINE__, 'method'=>__METHOD__, |
| | 1080 | 'child'=>null |
| | 1081 | ); |
| | 1082 | return false; |
| | 1083 | } |
| | 1084 | |
| | 1085 | |
| | 1086 | foreach($this->bp->pages as $component_name => $data){ |
| | 1087 | |
| | 1088 | // NOTE: We cannot use an algorithm that checks against $this->bp->active_components, |
| | 1089 | // because its impossible for 3rd-party components to add themselves to this array |
| | 1090 | // using the 'bp_active_components' filter. The filter is placed so early in the call |
| | 1091 | // stack it runs before 3rd-party components can load any of their plugin files. |
| 399 | | // Kill any other output after this. |
| 400 | | die; |
| | 1113 | /** |
| | 1114 | * Generates the BP component pages array |
| | 1115 | * |
| | 1116 | * @version 1.6 |
| | 1117 | * @since 1.6 |
| | 1118 | * @param array $flat_pages | Flat array of all WordPress pages on the site |
| | 1119 | * @return obj $pages | Structured object containing page ID's, Names, and Slugs |
| | 1120 | */ |
| | 1121 | function buildDirectoryPages($flat_pages, &$error=null) { |
| | 1122 | |
| | 1123 | |
| | 1124 | if( empty($flat_pages) ){ |
| | 1125 | |
| | 1126 | $error = array( |
| | 1127 | 'numeric'=>1, |
| | 1128 | 'text'=>"Called with empty flat_pages array", |
| | 1129 | 'file'=>__FILE__, 'line'=>__LINE__, 'method'=>__METHOD__, |
| | 1130 | 'child'=>null |
| | 1131 | ); |
| | 1132 | return false; |
| | 1133 | } |
| | 1134 | |
| | 1135 | |
| | 1136 | $page_ids = (array)bp_core_get_directory_page_ids(); |
| | 1137 | |
| | 1138 | if( empty($page_ids) ){ |
| | 1139 | |
| | 1140 | $error = array( |
| | 1141 | 'numeric'=>2, |
| | 1142 | 'text'=>"BP core directory page ids option is empty", |
| | 1143 | 'file'=>__FILE__, 'line'=>__LINE__, 'method'=>__METHOD__, |
| | 1144 | 'child'=>null |
| | 1145 | ); |
| | 1146 | return false; |
| | 1147 | } |
| | 1148 | |
| | 1149 | |
| | 1150 | $pages = new stdClass; |
| | 1151 | |
| | 1152 | // Iterate through each entry in the BP pages config option |
| | 1153 | foreach( $page_ids as $component_id => $bp_page_id ) { |
| | 1154 | |
| | 1155 | // Iterate through each WP site page in the flat pages array |
| | 1156 | foreach( $flat_pages as $wp_page_id => $data ) { |
| | 1157 | |
| | 1158 | // If the page ids match, add this page to the components array |
| | 1159 | if( $wp_page_id == $bp_page_id ) { |
| | 1160 | |
| | 1161 | $pages->{$component_id}->name = $data['slug']; |
| | 1162 | $pages->{$component_id}->id = $wp_page_id; |
| | 1163 | $pages->{$component_id}->title = $data['title']; |
| | 1164 | |
| | 1165 | $stem = array(); |
| | 1166 | $stem[] = $data['slug']; |
| | 1167 | |
| | 1168 | $parent = $data['parent']; |
| | 1169 | |
| | 1170 | // If the page is not attached to the root node, traverse the page tree backwards to the |
| | 1171 | // root node generating the reverse walk, then flip it and implode it to a string. |
| | 1172 | |
| | 1173 | while( $parent != 0 ){ |
| | 1174 | |
| | 1175 | $stem[] = $flat_pages[$parent]['slug']; |
| | 1176 | $parent = $flat_pages[$parent]['parent']; |
| | 1177 | } |
| | 1178 | |
| | 1179 | // TODO: BuddyPress incorrectly calls this a "slug", which is confusing. The correct term |
| | 1180 | // is a "stem" (in string form) and a "walk" (in array form). |
| | 1181 | |
| | 1182 | $pages->{$component_id}->slug = implode( '/', array_reverse( (array)$stem ) ); |
| | 1183 | } |
| | 1184 | |
| | 1185 | unset($slug); |
| | 1186 | } |
| | 1187 | unset($wp_page_id, $data); |
| | 1188 | |
| | 1189 | } |
| | 1190 | unset($component_id, $bp_page_id); |
| | 1191 | |
| | 1192 | return apply_filters( 'bp_core_get_directory_pages', $pages ); |
| | 1193 | |
| | 1194 | } |
| | 1195 | |
| | 1196 | |
| | 1197 | /** |
| | 1198 | * Load a specific template file, with fallback support. |
| | 1199 | * |
| | 1200 | * Example: bp_core_load_template( 'members/index' ); |
| | 1201 | * Loads: wp-content/themes/[activated_theme]/members/index.php |
| | 1202 | * |
| | 1203 | * @version 1.6 |
| | 1204 | * @since 1.6 |
| | 1205 | * @param string/array $templates | Single template name as string. Multiple template names as array of string. |
| | 1206 | * @return bool/die $result | False on failure. Loads template and terminates thread on success. |
| | 1207 | */ |
| | 1208 | function loadTemplate($templates, &$error=null) { |
| | 1209 | |
| | 1210 | /* |
| | 1211 | if( !$this->intersect["endpoint_id"] ){ |
| | 1212 | |
| | 1213 | $error = array( |
| | 1214 | 'numeric'=>1, |
| | 1215 | 'text'=>"Cannot load template because router was unable to intersect the current |
| | 1216 | request URI with any pages in the site's page tree.", |
| | 1217 | 'data'=>array("intersect"=>$this->intersect, |
| | 1218 | "walk"=>$this->walk, |
| | 1219 | "templates"=>$templates), |
| | 1220 | 'file'=>__FILE__, 'line'=>__LINE__, 'method'=>__METHOD__, |
| | 1221 | 'child'=>null |
| | 1222 | ); |
| | 1223 | return false; |
| | 1224 | } |
| | 1225 | */ |
| | 1226 | // Add a ".php" suffix to each template file in the $templates array |
| | 1227 | foreach( (array)$templates as $template ){ |
| | 1228 | |
| | 1229 | $filtered_templates[] = $template . '.php'; |
| | 1230 | } |
| | 1231 | |
| | 1232 | // Filter the template locations so that plugins can alter where they are located |
| | 1233 | $located_template = apply_filters( 'bp_located_template', locate_template( (array) $filtered_templates, false ), $filtered_templates ); |
| | 1234 | |
| | 1235 | if($located_template){ |
| | 1236 | |
| | 1237 | // Explicitly set WP's internal query variables to the correct state (because the |
| | 1238 | // default is to 404 the page) |
| | 1239 | |
| | 1240 | $this->wp_query->is_page = true; |
| | 1241 | $this->wp_query->is_404 = false; |
| | 1242 | $this->wp_query->is_singular = true; |
| | 1243 | |
| | 1244 | // Explicitly set the HTTP headers. Note that this only sets the headers for the web |
| | 1245 | // page. The web server generates its own headers for individual items such as images |
| | 1246 | // and CSS stylesheets loaded by the page. |
| | 1247 | |
| | 1248 | $protocol = $_SERVER["SERVER_PROTOCOL"]; |
| | 1249 | $code = 200; |
| | 1250 | $text = "OK"; |
| | 1251 | |
| | 1252 | if( ($protocol != 'HTTP/1.1') && ($protocol != 'HTTP/1.0') ){ |
| | 1253 | |
| | 1254 | $protocol = 'HTTP/1.0'; |
| | 1255 | } |
| | 1256 | |
| | 1257 | $status_header = "$protocol $code $text"; |
| | 1258 | |
| | 1259 | header($status_header, true, $code); |
| | 1260 | |
| | 1261 | load_template( apply_filters( 'bp_load_template', $located_template ) ); |
| | 1262 | |
| | 1263 | } |
| | 1264 | |
| | 1265 | if(!$this->unit_test){ |
| | 1266 | |
| | 1267 | die; |
| | 1268 | |
| | 1269 | // TODO: It's bad practice to place silent die() calls all over an application's code because it |
| | 1270 | // makes it very difficult to unit-test. |
| | 1271 | // |
| | 1272 | // The die() call above prevents WordPress from loading the template for the WP page we hijacked. If |
| | 1273 | // it was removed, the system would display the BP version of the page, followed by the WP version of |
| | 1274 | // the page. A better solution might be to set WP's internal variables so that it thinks it's successfully |
| | 1275 | // loaded the page, resulting in no further output and correctly running all the WP shutdown processes. |
| | 1276 | |
| | 1277 | } |
| | 1278 | |
| | 1279 | } |
| | 1280 | |
| | 1281 | |
| | 1282 | |
| | 1283 | } // End class BP_router |
| | 1284 | |
| | 1285 | |
| | 1286 | |
| | 1287 | // BRIDGE FUNCTIONS |
| | 1288 | // ======================================================================================================== |
| | 1289 | // These functions allow legacy code to access the new router class |
| | 1290 | |
| | 1291 | |
| | 1292 | function bp_core_set_uri_globals(){ |
| | 1293 | |
| | 1294 | global $bp; |
| | 1295 | $bp->router = new BP_router(); |
| | 1296 | |
| | 1297 | $result = $bp->router->route(&$status, &$error); |
| | 1298 | return $result; |
| | 1299 | } |
| | 1300 | |
| | 1301 | function bp_core_load_template($templates){ |
| | 1302 | |
| | 1303 | global $bp; |
| | 1304 | $result = $bp->router->loadTemplate($templates, &$error); |
| | 1305 | return $result; |
| | 1306 | } |
| | 1307 | |
| | 1308 | // ======================================================================================================== |
| | 1309 | |
| | 1310 | |
| | 1311 | /** |
| | 1312 | * Are root profiles enabled and allowed |
| | 1313 | * |
| | 1314 | * @since BuddyPress (1.6) |
| | 1315 | * @return bool True if yes, false if no |
| | 1316 | */ |
| | 1317 | function bp_core_enable_root_profiles() { |
| | 1318 | |
| | 1319 | $retval = false; |
| | 1320 | |
| | 1321 | if ( defined( 'BP_ENABLE_ROOT_PROFILES' ) && ( true == BP_ENABLE_ROOT_PROFILES ) ) |
| | 1322 | $retval = true; |
| | 1323 | |
| | 1324 | return apply_filters( 'bp_core_enable_root_profiles', $retval ); |