Codex Home → BuddyPress Plugin Development → Using BP Theme Compat in Plugins
Using BP Theme Compat in Plugins
A BuddyPress plugin can need to display content in several areas of a BuddyPress powered community :
In its own directory
In the member』s profile pages
In groups
The goal of this tutorial is to show you a way to take benefit of the magic that will be introduced in the next major release of BuddyPress (version 1.7) to maximize theme compatibility for BuddyPress content and for the content generated by your plugin.
In Plugin』s main directory.
When using the BP_Component class, we can specify that our plugin will have its own directory area. Here』s a quick reminder :
class BP_Plugin_Component extends BP_Component {
function __construct() {}
function includes() {
// Files to include
$includes = array(
/* some files */
'includes/bp-plugin-screen.php',
/*some other files */
);
parent::includes( $includes );
}
function setup_globals() {
$globals = array(
'slug' => 'some-slug',
'root_slug' => 'some-root-slug',
/* has_directory is set to true in order to setup plugin's main directory */
'has_directory' => true,
'notification_callback' => 'function_to_format_notification',
/* etc... */
);
parent::setup_globals( $globals );
}
function setup_nav() {}
function setup_admin_bar() {}
}
function bp_plugin_load_core_component() {
global $bp;
$bp->bp_plugin = new BP_Plugin_Component;
}
add_action( 'bp_loaded', 'bp_plugin_load_core_component' );
In this example, i』m using the file bp-plugin-screen.php in order to hold all the functions that will allow us to set the needed template for our plugin』s main directory.
So far, in order to indicate to BuddyPress the path to my plugin』s template, i used to filter the bp_located_template hook :
function bp_plugin_load_template_filter( $found_template, $templates ) {
//Only filter the template location when we're on the bp-plugin component pages.
if ( !bp_is_current_component( 'bp-plugin' ) )
return $found_template;
foreach ( (array) $templates as $template ) {
if ( file_exists( STYLESHEETPATH . '/' . $template ) )
$filtered_templates[] = STYLESHEETPATH . '/' . $template;
elseif( file_exists( TEMPLATEPATH . '/' . $template ) )
$filtered_templates[] = TEMPLATEPATH . '/' . $template;
else
$filtered_templates[] = BP_PLUGIN_DIR . '/templates/' . $template;
}
$found_template = $filtered_templates[0];
return apply_filters( 'bp_plugin_load_template_filter', $found_template );
}
add_filter( 'bp_located_template', 'bp_plugin_load_template_filter', 10, 2 );
We first check for our template file in the active theme or in the parent theme, and if nothing was found, we add to the templates array the path to our plugin template folder. Doing so, we allow themes to 「override」 and adapt the template to their design.
This done, in order to tell to BuddyPress to load the required template for my root plugin directory i filter the hook 『bp_screens『 :
function bp_plugin_screen_index() {
// i first check i'm on my plugin directory area...
if ( !bp_displayed_user_id() && bp_is_current_component( 'bp-plugin' ) && !bp_current_action() ) {
bp_update_is_directory( true, 'bp-plugin' );
//... before using bp_core_load_template to ask BuddyPress
// to load the template bp-plugin (which is located in
// BP_PLUGIN_DIR . '/templates/bp-plugin.php)
bp_core_load_template( apply_filters( 'bp_plugin_screen_index', 'bp-plugin' ) );
}
}
add_action( 'bp_screens', 'bp_plugin_screen_index' );
As you can see, when BuddyPress will run the function bp_core_load_template, which is located at line 346 of the /bp-core/bp-core-catchuri.php file (in BuddyPress 1.7-beta1), as soon as it meets the 『bp_located_template『 filter, my function 『bp_plugin_load_template_filter』 will step in to add my plugin template path. This is made possible thanks to line 379 of this file :
// Filter the template locations so that plugins can alter where they are located
$located_template = apply_filters( 'bp_located_template', locate_template( (array) $filtered_templates, false ), $filtered_templates );
In BuddyPress 1.7, this method is still working fine as soon as the community administrator is using the bundled BP Default theme or a child of it. But, if he decides to enjoy the 「BP Theme Compat」 great new feature in order to use any WordPress theme, then the content generated by our plugin will not display the best way.
To maximize this, we need to use the new hook introduced in the 1.7 version : do_action(『bp_setup_theme_compat』) which is located at line 410 of the bp_core_load_template function.
Actually, if BuddyPress haven』t found the required template then this hook is finally fired. Unlike what we』ve done just above, the funny part of this tutorial is that we first won』t tell BuddyPress what template to load if the active theme is not BP Default or a child of it.
In order to check if BP Default, or a child of it, or an eventual BuddyPress theme that is not relying on BP Default is active on the blog, i use this function :
function bp_plugin_is_bp_default() {
// if active theme is BP Default or a child theme, then we return true
// If the Buddypress version is < 1.7, then return true too
if(current_theme_supports('buddypress') || in_array( 'bp-default', array( get_stylesheet(), get_template() ) ) || ( defined( 'BP_VERSION' ) && version_compare( BP_VERSION, '1.7', ' 0,
'post_title' => 'BP Plugin Directory',
'post_author' => 0,
'post_date' => 0,
'post_content' => '',
'post_type' => 'bp_plugin',
'post_status' => 'publish',
'is_archive' => true,
'comment_status' => 'closed'
) );
}
/**
* Filter the_content with bp-plugin index template part
*/
public function directory_content() {
bp_buffer_template_part( 'bp-plugin' );
}
}
new BP_Plugin_Theme_Compat ();
Thanks to this step, we just made sure that BuddyPress will look for the bp-plugin template in the WordPress Theme or in the BP Legacy templates before trying to load it.
However, unless the active theme thought about adding this template, it won』t find it as it won』t check our plugin』s templates folder. We need to reference our plugin template folder first.
To do so, we can add a filter on the 「bp_get_template_stack」 hook in order to store our plugin path into the BuddyPress template paths :
function bp_plugin_add_template_stack( $templates ) {
// if we're on a page of our plugin and the theme is not BP Default, then we
// add our path to the template path array
if ( bp_is_current_component( 'bp-plugin' ) && !bp_plugin_is_bp_default() ) {
$templates[] = BP_PLUGIN_DIR . '/templates/';
}
return $templates;
}
add_filter( 'bp_get_template_stack', 'bp_plugin_add_template_stack', 10, 1 );
Doing so we make sure that if our template is not found in the WordPress active theme or in bp-legacy, it will be found in our plugin』s template folder. Finally we just need to adapt our plugin template in BP_PLUGIN_DIR . 『/templates/』 in order to strip the get_header, get_sidebar, and get_footer calls and to simply put our output into the new container tag 『
function bp_plugin_locate_template( $template = false ) {
if( empty( $template ) )
return false;
if( bp_plugin_is_bp_default() ) {
locate_template( array( $template . '.php' ), true );
} else {
bp_get_template_part( $template );
}
}
In the member』s profile pages
We have two choices to load our plugin template in this area. First, we can take benefit of the different hooks bundled in the members/single/plugins.php BuddyPress template to inject our plugin』s content in its member area. Three hooks can be used :
do_action( 'bp_template_content_header' );
do_action( 'bp_template_title' );
do_action( 'bp_template_content' );
Back on our component class, when setting our plugin user navigation, we specify the screen function to play :
class BP_Plugin_Component extends BP_Component {
function __construct() {}
function includes() {}
function setup_globals() {}
function setup_nav() {
$main_nav = array(
'name' => __( 'BP Plugin' ),
'slug' => BP_PLUGIN_SLUG,
'position' => 80,
/* main nav screen function callback */
'screen_function' => 'bp_plugin_main_screen_function',
'default_subnav_slug' => 'bp-plugin-subnav'
);
// Add a few subnav items under the main tab
$sub_nav[] = array(
'name' => __( 'BP Plugin' ),
'slug' => 'bp-plugin-subnav',
'parent_url' => 'link to the parent url',
'parent_slug' => 'parent slug',
/* sub nav screen function callback */
'screen_function' => 'bp_plugin_main_screen_function',
'position' => 10,
);
parent::setup_nav( $main_nav, $sub_nav );
}
function setup_admin_bar() {}
}
Now in our bp-plugin-screen.php file, we need to write some code for our screen function in order to add actions when the three different hooks will be fired to display our content, for example :
function bp_plugin_main_screen_function {
add_action( 'bp_template_content_header', 'bp_plugin_menu_header' );
add_action( 'bp_template_title', 'bp_plugin_title' );
add_action( 'bp_template_content', 'bp_plugin_content' );
bp_core_load_template( apply_filters( 'bp_core_template_plugin', 'members/single/plugins' ) );
}
function bp_plugin_menu_header() {
_e( 'Menu Header' );
}
function bp_plugin_title() {
_e( 'Plugin title' );
}
function bp_plugin_content() {
_e( 'Plugin content');
}
We can use another approach by using a new template from our plugin』s template directory. However, unlike what we』ve accomplished earlier when we hooked 『bp_setup_theme_compat『 to display our root plugin directory content, we』ll use another method to modify the template to load as whatever happens, BP_Members_Theme_Compat will buffer the members/single/plugins template. So in our screen function, we first use the bp_core_load_template as it will be used in the BP Default case and as it will finally fire the bp_setup_theme_compat hook for any WordPress theme. Then we use our BP Default check in order to add a filter (if it returns false) to the bp_get_template_part function.
function bp_plugin_main_screen_function(){
bp_core_load_template( apply_filters( 'bp_plugin_main_screen_function', 'bp-plugin-main-user-template' ) );
//if BP Default is not used, we filter bp_get_template_part
if( !bp_plugin_is_bp_default() )
add_filter('bp_get_template_part','bp_plugin_user_template_part', 10, 3 );
}
function bp_plugin_user_template_part( $templates, $slug, $name ) {
if( $slug != 'members/single/plugins' )
return $templates;
return array( 'bp-plugin-main-user-template.php' );
}
Our bp_plugin_user_template_part will override the members/single/plugins.php BuddyPress template by our bp-plugin-main-user-template.php plugin template.
Finally, don』t forget to create the template BP_PLUGIN_DIR . 『/templates/bp-plugin-main-user-template.php』
In Group pages
here we just need to rely on the display function of the BuddyPress Group Api. In order to make sure our content will load the best way in BP Default or a child of it and any WordPress theme we can use our custom bp_plugin_locate_template function.
class BP_Plugin_Group extends BP_Group_Extension {
/** other function of group API **/
function display() {
bp_plugin_locate_template( 'bp-plugin-group-template' );
}
/** other function of group API **/
}
Finally, don』t forget to create the template BP_PLUGIN_DIR . 『/templates/bp-plugin-group-template.php』