Modifying the Toolbar
Note: Modifying the Admin Bar requires a basic understanding of WordPress Hooks,Actions and Filters. If you aren’t familiar with these items, you can find more information on the WordPress Plugin API page.
The admin bar is a collection of individual functions, each of which implements a specific top level menu item that is displayed. If you look at the code in: /bp-core/bp-core-adminbar.php you’ll see these funcitons such as bp_adminbar_blogs_menu(). That creates the ‘My Blogs’ menu item and all the items within the drop down menu.
We’re going to modify the ‘My Blogs’ menu without changing the core code, thus helping to assure that our modifications don’t break when upgrading. We are going to sort the ‘My Blogs’ menu items to have them appear in a specific order and eliminate the items that show up as ‘Subscriber’.
The admin bar is triggered by wordpress ‘actions’. These ‘actions’ are events that are triggered throughout wp and bp. The admin bar responds to the action ‘wp_footer’ which wp themes trigger at the end of each page in the theme footer. The admin bar registers it’s need to do something with the call to:
add_action( 'wp_footer', 'bp_core_admin_bar', 8 );
When the action ‘wp_footer’ is triggered it will call the function named ‘bp_core_admin_bar’. Looking at the function:
function bp_core_admin_bar() { global $bp, $wpdb, $current_blog, $doing_admin_bar; $doing_admin_bar = true; if ( (int) get_site_option( 'hide-loggedout-adminbar' ) && !is_user_logged_in() ) return false; echo '<div id="wp-admin-bar">'; // **** Do bp-adminbar-logo Actions ******** do_action( 'bp_adminbar_logo' ); echo '<ul class="main-nav">'; // **** Do bp-adminbar-menus Actions ******** do_action( 'bp_adminbar_menus' ); echo '</ul>'; echo '</div>'; }
At first glance it doesn’t seem to do much. It does though. It defines its own action and triggers that action at the end of the function:
// **** Do bp-adminbar-menus Actions ********
do_action( 'bp_adminbar_menus' );
At the bottom of bp-core-adminbar.php you’ll notice a collection of add_action() calls that respond to ‘bp_adminbar_menus’.
add_action( 'bp_adminbar_menus', 'bp_adminbar_login_menu', 2 ); add_action( 'bp_adminbar_menus', 'bp_adminbar_account_menu', 4 ); add_action( 'bp_adminbar_menus', 'bp_adminbar_blogs_menu', 6 ); add_action( 'bp_adminbar_menus', 'bp_adminbar_notifications_menu', 8 ); add_action( 'bp_adminbar_menus', 'bp_adminbar_authors_menu', 12 ); add_action( 'bp_adminbar_menus', 'bp_adminbar_random_menu', 100 );
Each of the individual menu items responds to ‘bp_adminbar_menus’. The one we are going to change is bp_adminbar_blogs_menu().
To change a function that is triggered by an action you have to unhook the current function that responds to the action and supply your own function that replaces the core function and then replace the add_action() call with one that responds to the same hook as the original function. This is how we’ll do that:
Original action hook:
add_action( 'bp_adminbar_menus', 'bp_adminbar_blogs_menu', 6 );
Remove the action hook:
remove_action('bp_adminbar_menus', 'bp_adminbar_blogs_menu', 6);
Replace the original action hook:
add_action('bp_adminbar_menus', 'my_adminbar_blogs_menu', 6);
Create a replacement function:
function my_adminbar_blogs_menu() { if ( is_user_logged_in() ) { global $bp; if ( function_exists('bp_blogs_install') ) { if ( !$blogs = wp_cache_get( 'bp_blogs_of_user_' . $bp->loggedin_user->id, 'bp' ) ) { $blogs = get_blogs_of_user( $bp->loggedin_user->id ); wp_cache_set( 'bp_blogs_of_user_' . $bp->loggedin_user->id, $blogs, 'bp' ); } $blogs = filter_blogs_by_role($blogs); [snip] the rest of the original function [snip] }
I’ve made one change to the original function.
$blogs = filter_blogs_by_role($blogs);
After the call to get_blogs_of_user() we get an array of ‘blog’ objects back from wp where the user has any role. I want to sort these blog objects to my liking and then just allow the rest of the function to run and display them.
I’ve decided to call my sort function filter_blogs_by_role() and it takes the array of blog objects, does something to them and then returns that filtered array of user blog objects. The rest of the function goes happily along to display our filtered results.
I’m not going to delve into what the filter_blogs_by_role() function does. I’ll just repeat it here. It sorts the blog objects by role with ‘Admin’ appearing before all the other roles.
// sort array of blog objs by $blog->role according to the order of $blog_roles // roles not included in $blog_roles array will not be displayed function filter_blogs_by_role($blogs){ global $bp, $blog_roles; $blog_roles[] = __( 'Admin', 'buddypress' ); $blog_roles[] = __( 'Editor', 'buddypress' ); $blog_roles[] = __( 'Author', 'buddypress' ); $blog_roles[] = __( 'Contributor', 'buddypress' ); $blog_roles[] = __( 'Subscriber', 'buddypress' ); // get roles foreach ($blogs as $blog){ $blog->role = get_blog_role_for_user( $bp->loggedin_user->id, $blog->userblog_id ); } // eliminate roles not in $blog_roles foreach ($blogs as $key => $value){ if (!in_array($value->role, $blog_roles)) unset($blogs[$key]); } // sort by $blog_roles sequence if there are any left if ($blogs){ usort($blogs, 'compare_roles'); } return $blogs; } // helper function function compare_roles($a, $b){ global $blog_roles; foreach($blog_roles as $key => $value){ if($a->role==$value){ return 0; break; } if($b->role==$value){ return 1; break; } }
Now we have a replacement function called my_adminbar_blogs_menu(). We’ve got to hook it up so that it actually does replace the orignal call to bp_adminbar_blogs_menu();
Any custom code that we create can be placed in a file called bp-custom.php. It gets loaded with all the rest of bp automatically by bp during each page load. If you don’t have one already then create one in /wp-content/plugins/. So we’ll pretend that we have done just that and that our three new functions are going to be loaded with the rest of bp.
bp-custom.php
So we have our shiny new replacement function and we now need to determine how best to hook it up to the rest of the universe and make it work. There really isn’t a ‘best’ way to do this. There are several ways to do it though.
I like to keep all my custom bp code in bp-custom.php so that’s how I’m going to implement this. Since the admin bar responds to ‘wp_footer’, I’m going to hook that event and do some things before the admin bar is displayed.
In bp-custom.php:
function my_alter_bp_adminbar(){ remove_action('bp_adminbar_menus', 'bp_adminbar_blogs_menu', 6); add_action('bp_adminbar_menus', 'my_adminbar_blogs_menu', 6); } add_action('wp_footer','my_alter_bp_adminbar',1);
I’ve told wp that when it triggers the action ‘wp_footer’ call the function my_alter_bp_adminbar() and do it with a priority of ‘1’. A priority of ‘1’ makes sure that it gets called before other functions that respond to ‘wp_footer’.
If I add that to the other three functions I just stuffed into bp-custom.php then it will replace the original menu function with my new menu function and alter the behavior of bp when it displays the ‘My Blogs’ menu.
Confusing enough for you? Welcome to event driven programming.