Grappling with BP theme compatibility

Theme compatibility was introduced in BuddyPress 1.7 and allows site operators to use BP with any available WordPress theme. I have been way behind at updating BP Group Hierarchy to support theme compatibility, and as a result the Group Tree page looks like garbage when not using a theme that was written for BuddyPress. This post is about the effort to fix that.

If you are writing a normal BuddyPress plugin — one that adds pages rather than replacing them — see this excellent article (and this one too) at the BuddyPress Codex for info on how to implement BP theme compatibility.

First idea: Replace the content from directory_content with the group tree.

BP_Groups_Theme_Compat::directory_content() is the native BP function that displays the group list page. It’s called as a hook to a filter called bp_replace_the_content, so the content should be available for further filtering after it runs.

 
/**
 * (BP 1.7+) Filter the theme-compatibility content for a group tree page
 */
function bp_group_hierarchy_inject_group_tree() {
        add_filter( 'bp_replace_the_content', 'bp_group_hierarchy_buffer_group_tree', 100 );
}
function bp_group_hierarchy_buffer_group_tree( $content ) {
 
        ob_start();
 
        $loop_template = apply_filters( 'bp_located_template', locate_template( array( "tree/index.php" ), false ), "tree/index.php" );
        load_template( $loop_template );
 
        $output = ob_get_clean();
        return $output;
}
 
// If admin says only show the group tree (and we are in theme compat), do it:
add_action( 'groups_directory_groups_setup', 'bp_group_hierarchy_inject_group_tree' );

Result: No dice. Directory content is echoed to the screen, not returned to the filter chain. The group tree appears below the flat group list.

Workaround / second idea: Unhook the directory_content function so it doesn’t echo.

Here’s a snippet that I threw together testing this out (warning: closure => PHP 5.3+ !):

/**
 * (BP 1.7+) Filter the theme-compatibility content for a group tree page
 */
function bp_group_hierarchy_inject_group_tree() {
 
        add_filter( 'bp_replace_the_content', function() {
                /**
                 * Filter is hooked by a method of an anonymous instance...
                 * Search all registered listeners and pick off the BP_Groups_Theme_Compat one.
                 */
 
                global $wp_filter;
 
                foreach( $wp_filter['bp_replace_the_content'][10] as $key => $filter ) {
                        if( is_array( $filter['function'] ) && is_a( $filter['function'][0], 'BP_Groups_Theme_Compat' ) ) {
                                // Found and removing the native content
                                unset( $wp_filter['bp_replace_the_content'][10][$key] );
                        }
                }
 
        }, 1 );
 
        add_filter( 'bp_replace_the_content', 'bp_group_hierarchy_buffer_group_tree', 100 );
}
function bp_group_hierarchy_buffer_group_tree( $content ) {
 
        /* Same as before */
        return $output;
}
 
// If admin says only show the group tree (and we are in theme compat), do it:
add_action( 'groups_directory_groups_setup', 'bp_group_hierarchy_inject_group_tree' );

Result: Works well but is pretty awkward and brittle. It looks like the kind of solution I’d really like to avoid.

Let’s try something completely different. :)

Third idea: Use BP Theme Compat functions to replace the loop template file.

BuddyPress theme compatibility adds a lot of flexibility in how content is gathered for the site. I actually find the theme compatibility process much more intuitive to work with than the traditional template loading process.

Here are some highlights:

  1. bp_register_template_stack() lets you add a source of template files without filtering bp_located_template. Nice!
  2. bp_get_template_part() offers an easy way to switch out template files on the fly that is just not possible with load_template.

Is it really this easy?

SPOILER ALERT: This does not work for BP Group Hierarchy. But for most applications, I think this is the best way to conditionally replace a BP page with content from a plugin.

 
/**
 * (BP 1.7+) Add plugin templates folder to list of available template paths
 */
function bp_group_hierarchy_register_template_location() {
	return dirname( __FILE__ ) . '/templates/';
}
 
/**
 * (BP 1.7+) Replace groups/groups-loop with tree/tree-loop depending on user settings
 */
function bp_group_hierarchy_maybe_replace_group_loop_template( $templates, $slug, $name ) {
 
	if( 'groups/groups-loop' != $slug )
		return $templates;
 
	return array( 'tree/tree-loop.php' );
}
 
if( function_exists( 'bp_register_template_stack' ) )
	bp_register_template_stack( 'bp_group_hierarchy_register_template_location' );
 
// If admin says only show the group tree (and we are in theme compat), do it:
add_filter( 'bp_get_template_part', 'bp_group_hierarchy_maybe_replace_group_loop_template', 10, 3 );

Result: Works(!)
But when you trigger an AJAX request, the unaltered ID on the “All Groups” tab leads BP to load the flat list.
We also still need to hook the bp_located_template filter to find stylesheets, since we are enqueue-ing them instead of loading the contents inline.
It also also removes the ability to customize the “Group Tree” label, a feature added by popular demand.

Fourth idea: Use BP Theme Compat functions to replace the whole Groups list page.

This is really just a twist on how things are currently handled, and it looks like the current way will be around as long as BP-compatible themes bypass bp_get_template_part.

 
/**
 * (BP 1.7+) Add plugin templates folder to list of available template paths
 */
function bp_group_hierarchy_register_template_location() {
	return dirname( __FILE__ ) . '/templates/';
}
 
/**
 * (BP 1.7+) Replace groups/index with tree/index-compat depending on user settings
 */
function bp_group_hierarchy_maybe_replace_group_index_template( $templates, $slug, $name ) {
 
	if( 'groups/index' != $slug )
		return $templates;
 
	return array( 'tree/index-compat.php' );
}
 
if( function_exists( 'bp_register_template_stack' ) )
	bp_register_template_stack( 'bp_group_hierarchy_register_template_location' );
 
// If admin says only show the group tree (and we are in theme compat), do it:
add_filter( 'bp_get_template_part', 'bp_group_hierarchy_maybe_replace_group_index_template', 10, 3 );

Result: Works seamlessly. It even allows BP to load templates from the stylesheet and template directories before falling back to those included with the plugin.

The only drawback is that it requires a new template file, the group tree version of the bp-templates/bp-legacy/buddypress/groups/index.php file from BP. Which is to say that it adds another layer of template hijinks and possible theme compatibility issues rather than removing an existing one.

Wrap up

I don’t know if this is the strategy that will ultimately make it into the plugin, but it is straightforward and has performed well in early tests. In the meantime, I hope this post will be useful to anyone else trying to do something crazy and ill-advised with BuddyPress.

If you use any of the techniques on this page, please drop me a line in the comments and let me know how they worked for you. And if you have any suggestions on techniques that work even better than these, please send those along too. 😉

1 Response

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>