Last month, we looked at the basics of BuddyPress and how you can use it to increase social networking in your projects. The response was great and many requested theming tutorials. So, in this three part series, we'll explain some core elements of the BuddyPress API and unpack how to create a custom child theme that will survive both BuddyPress and WordPress updates.
With more and more clients looking for social networking, we went over the basics of BuddyPress and the impact that it can bring to your projects. Now, we want to dive into developing child themes that are customized to your needs and that will weather updates to both BuddyPress and WordPress. Recently, I searched in the theme repository for BuddyPress compatible themes and was met with a whopping 19 returned results! Needless to say, this is a market that could use some developing. While there are plugins out there that can make your themes compatible with BuddyPress, I am always a supporter of understanding how to do something yourself and not rely entirely on plugins. Besides this, creating a basic child theme is a straight-forward process, and one worth learning if you plan to develop with BuddyPress.
What We Will Do
In this tutorial, I will walk you through some core elements of the BuddyPress API and through the basic steps to create a customized BuddyPress child theme. By the end of this tutorial, you should be able to understand:
- How to use BuddyPress template tags
- How to recognize and customize BuddyPress loops
- How to use BuddyPress conditionals in your themes
- How to create and edit a basic BuddyPress child theme
This tutorial will lay the groundwork for the next two parts, which will contain more in-depth techniques when working with specific elements within BuddyPress like groups and forums.
Starting with the BuddyPress API
If you've been working with WordPress, then you know the importance of understanding how to work with it's specific functions, actions, hooks, and filters. You've also realized that when you understand these things better, your projects go faster and you can dive more fully into customizing themes and plugins. There is nothing different with BuddyPress.
I want to first walk you through some key elements of the BuddyPress API : template tags, loops, and conditionals. It is my hope that as we explore these elements and then how to create a child theme that you'll be able to take what is written here and apply it to make your own custom BuddyPress child themes.
Working with BuddyPress Template Tags
To start with, let's take a look at BuddyPress functions that are core to theme development - template tags. The BuddyPress codex has a list of these here as well for your reference. These template tags are vital to really customizing your themes, and just like Dan Davies suggested in his recent wp.tutsplus article, you should have some of these on speed dial if you plan on doing much theme development in BuddyPress. Some of them are exactly the same as the template tags in Wordpress, which should also save you some time if you have experience designing templates in WordPress.
Here's a quick list of some template tags from header to footer to use in your theme development:
- get_header() - the same template tag as WordPress. This includes your theme's header.php file.
- bp_get_loggedin_user_nav() - displays site navigation links for logged in users. It also adds a "current selected" class to the nav item being viewed so that you can create some custom CSS to chnage the display of that list item.
- the_content()
- the_excerpt
- the_tags()
- the_category()
- bp_loggedin_user_avatar() - displays the currently logged in user's avatar.
- bp_loggedin_user_domain()- echoes the current logged in user's URL.
- bp_member_permalink - echoes the URL of a member's profile page.
- bp_member_avatar - displays a member's avatar. This can be used in the member loop to create a global list of users.
- bp_get_user_firstname()
- bp_group_member_joined_since()
- bp_group_permalink() - echoes a link to a particular group's home page.
- bp_group_avatar() - displays a group's avatar.
- bp_group_description_excerpt() - displays the group description as specified by the group admin.
- bp_get_group_name() -
- bp_sitewide_activity_feed_link() - echoes the link for the site's RSS activity feed.
- bp_directory_groups_search_form() - displays a search form that will search through all public groups and their activity.
- bp_directory_forums_search_form() - displays a search form that allows for searches based on through forum content.
- bp_directory_members_search_form() - displays a search form that allows for searches based on member content and info.
-
locate_template() - this is used to include templates like the sidebar and search forms. To include the sidebar it looks like:
<?php locate_template( array( 'sidebar.php' ), true ) ;?>
You can also use this template tag to display search form templates. For this in action, look no further than bp-default's index.php file.
- bp_groups_pagination_count()
- bp_groups_pagination_links()
- bp_members_pagination_count()
- bp_members_pagination_links()
- bp_activity_pagination_count()
- bp_activity_pagination_links()
- get_footer()
Header and Navigation
Post and Content Formatting
These template tags are the same tags used to format and display content in WordPress. They operate in the same WordPress loop as well. To see an example of these in action, open up the bp-default theme and open index.php. Lines 15-61 give a great example of the WordPress loop at work in BuddyPress.
User and Member Info
To see these tags in action open up members/member-loop.php and members/single.php within bp-default.
Groups
To see these tags in action open up groups/groups-loop.php and groups/single.php within bp-default.
Activity
Search Forms
Includes
Pagination
In addition to these, other pagination template tags exist for different content types. Simply replace the word "groups" in the above template tags with the words : forum, blogs, group_members, or messages, to include the links or count for whichever loop you are using. These loops are examined more in-depth below.
Footer
Working with BuddyPress Loops
As I already mentioned above, you can use the default WordPress loop in your themes to display posts and post meta content. BuddyPress also contains its own loops that each are responsible for displaying BuddyPress-specific content types. BuddyPress includes loops for: groups, members, activity stream, forum topics, blogs, group members, private messages, and profile data. While each one of these looks very similar to the standard WordPress loop, each one has its own specific variables which also allow for greater customization.
Be sure to dive into the bp-default theme files to get a better understanding of loops and their specific uses. Each of the content types will have a folder where the specific loop file is located.
Let's look at an overview of each one, and some specific ways that you can customize their display.
Groups Loop
The groups loop is used to display a list of site groups. The loop looks like this:
<?php if ( bp_has_groups() ) : ?> <div class="pagination"> <div class="pag-count" id="group-dir-count"> <?php bp_groups_pagination_count() ?> </div> <div class="pagination-links" id="group-dir-pag"> <?php bp_groups_pagination_links() ?> </div> </div> <ul id="groups-list" class="item-list"> <?php while ( bp_groups() ) : bp_the_group(); ?> <li> <div class="item-avatar"> <a href="<?php bp_group_permalink() ?>"><?php bp_group_avatar( 'type=thumb&width=50&height=50' ) ?></a> </div> <div class="item"> <div class="item-title"><a href="<?php bp_group_permalink() ?>"><?php bp_group_name() ?></a></div> <div class="item-meta"><span class="activity"><?php printf( __( 'active %s ago', 'buddypress' ), bp_get_group_last_active() ) ?></span></div> <div class="item-desc"><?php bp_group_description_excerpt() ?></div> <?php do_action( 'bp_directory_groups_item' ) ?> </div> <div class="action"> <?php bp_group_join_button() ?> <div class="meta"> <?php bp_group_type() ?> / <?php bp_group_member_count() ?> </div> <?php do_action( 'bp_directory_groups_actions' ) ?> </div> <div class="clear"></div> </li> <?php endwhile; ?> </ul> <?php do_action( 'bp_after_groups_loop' ) ?> <?php else: ?> <div id="message" class="info"> <p><?php _e( 'There were no groups found.', 'buddypress' ) ?></p> </div> <?php endif; ?>
The following parameters are the most useful when customizing the groups loop:
- type - by default this is set to active, but is can also accept the following: newest, popular, random, alphabetical, most-forum-topics, most-forum-posts.
- per_page - by default, the BuddyPress loop displays 10 groups per page, but by entering a new number, you can alter that.
- user_id - by specifying a user id, only the groups that the user is a part of will be shown. For example, to display only the groups that the current logged in user is a part of, the code would be:
Let's take a look at an example loop, customized using the above parameters. Right after the opening bracket enter the following:
$args = array( 'type' => 'popular', 'max' => 5, 'user_id' => $user_id ); if ( bp_has_groups ( $args ) ) { ...
Since we are using multiple arguments, I set up a variable named args that can handle an array. I then set the type to popular, the max per page to 5, and the user_id to the current logged in user's id. This will then display the top 5 most popular groups that the current member is a part of and then paginate the rest.
Members Loop
The members loop is used to display a list of current members. The standard members loop looks like this:
<?php if ( bp_has_members() ) : ?> <div class="pagination"> <div class="pag-count" id="member-dir-count"> <?php bp_members_pagination_count() ?> </div> <div class="pagination-links" id="member-dir-pag"> <?php bp_members_pagination_links() ?> </div> </div> <?php do_action( 'bp_before_directory_members_list' ) ?> <ul id="members-list" class="item-list"> <?php while ( bp_members() ) : bp_the_member(); ?> <li> <div class="item-avatar"> <a href="<?php bp_member_permalink() ?>"><?php bp_member_avatar() ?></a> </div> <div class="item"> <div class="item-title"> <a href="<?php bp_member_permalink() ?>"><?php bp_member_name() ?></a> <?php if ( bp_get_member_latest_update() ) : ?> <span class="update"> - <?php bp_member_latest_update( 'length=10' ) ?></span> <?php endif; ?> </div> <div class="item-meta"><span class="activity"><?php bp_member_last_active() ?></span></div> <?php do_action( 'bp_directory_members_item' ) ?> <?php /*** * If you want to show specific profile fields here you can, * but it'll add an extra query for each member in the loop * (only one regadless of the number of fields you show): * * bp_member_profile_data( 'field=the field name' ); */ ?> </div> <div class="action"> <?php do_action( 'bp_directory_members_actions' ) ?> </div> <div class="clear"></div> </li> <?php endwhile; ?> </ul> <?php do_action( 'bp_after_directory_members_list' ) ?> <?php bp_member_hidden_fields() ?> <?php else: ?> <div id="message" class="info"> <p><?php _e( "Sorry, no members were found.", 'buddypress' ) ?></p> </div> <?php endif; ?>
To go deeper, let's break this code down a little. The first and last lines are vital, and tell BuddyPress to only display the rest of our code if there are members present to show.
<?php if ( bp_has_members() ) : ?> ... <?php endif; ?>
Next, we have our member pagination template tags. There are also template tags for other content type pagination like groups and blogs. These are written above for your reference.
<div class="pagination"> <div class="pag-count" id="member-dir-count"> <?php bp_members_pagination_count() ?> </div> <div class="pagination-links" id="member-dir-pag"> <?php bp_members_pagination_links() ?> </div> </div>
As you can see, each loop can be broken down into pieces revealing more about template tags and their uses. In this loop alone, we see the template tags: bp_member_profile_data, bp_member_permalink, bp_member_avatar, and bp_member_latest_update.
The bp_has_members() argument has multiple parameters that can be customized to alter the member list displayed. The most useful are:
- type - returns active users by default. Other arguments are: newest, popular, online, alphabetical, random.
- per_page
- max
- user_id - when this is specified, it returns only the users that are friends of the specified user number.
Activity Stream Loop
<?php if ( bp_has_activities() ) : ?> <div class="pagination"> <div class="pag-count"><?php bp_activity_pagination_count() ?></div> <div class="pagination-links"><?php bp_activity_pagination_links() ?></div> </div> <ul id="activity-stream" class="activity-list item-list"> <?php while ( bp_activities() ) : bp_the_activity(); ?> <li class="<?php bp_activity_css_class() ?>" id="activity-<?php bp_activity_id() ?>"> <div class="activity-avatar"> <a href="<?php bp_activity_user_link() ?>"> <?php bp_activity_avatar( 'type=full&width=100&height=100' ) ?> </a> </div> <div class="activity-content"> <div class="activity-header"> <?php bp_activity_action() ?> </div> <?php if ( bp_get_activity_content_body() ) : ?> <div class="activity-inner"> <?php bp_activity_content_body() ?> </div> <?php endif; ?> <?php do_action( 'bp_activity_entry_content' ) ?> </div> </li> <?php endwhile; ?> </ul> <?php else : ?> <div id="message" class="info"> <p><?php _e( 'Sorry, there was no activity found. Please try a different filter.', 'buddypress' ) ?></p> </div> <?php endif; ?>
Helpful parameters for the activity stream loop are:
- display_comments - when set to "true", this will display comments on activity.
- per_page - again ,this sets how many activities to show before BuddyPress pagination.
There are other helpful tips on filtering sitewide activity and adding commenting support at the activity stream loop page in the BuddyPress codex.
Forum Topics Loop
The forum topics loop is used to display a list of forums topics, who posted last on the topic, and how many total posts there are in the topic, among other things. The loop looks like this:
<?php if ( bp_has_forum_topics() ) : ?> <div class="pagination"> <div id="post-count" class="pag-count"> <?php bp_forum_pagination_count() ?> </div> <div class="pagination-links" id="topic-pag"> <?php bp_forum_pagination() ?> </div> </div> <table class="forum"> <tr> <th id="th-title"><?php _e( 'Topic Title', 'buddypress' ) ?></th> <th id="th-poster"><?php _e( 'Latest Poster', 'buddypress' ) ?></th> <?php if ( !bp_is_group_forum() ) : ?> <th id="th-group"><?php _e( 'Posted In Group', 'buddypress' ) ?></th> <?php endif; ?> <th id="th-postcount"><?php _e( 'Posts', 'buddypress' ) ?></th> <th id="th-freshness"><?php _e( 'Freshness', 'buddypress' ) ?></th> </tr> <?php while ( bp_forum_topics() ) : bp_the_forum_topic(); ?> <tr class="<?php bp_the_topic_css_class() ?>"> <td class="td-title"> <a class="topic-title" href="<?php bp_the_topic_permalink() ?>" title="<?php bp_the_topic_title() ?> - <?php _e( 'Permalink', 'buddypress' ) ?>"> <?php bp_the_topic_title() ?> </a> </td> <td class="td-poster"> <a href="<?php bp_the_topic_permalink() ?>"><?php bp_the_topic_last_poster_avatar( 'type=thumb&width=20&height=20' ) ?></a> <div class="poster-name"><?php bp_the_topic_last_poster_name() ?></div> </td> <?php if ( !bp_is_group_forum() ) : ?> <td class="td-group"> <a href="<?php bp_the_topic_object_permalink() ?>"><?php bp_the_topic_object_avatar( 'type=thumb&width=20&height=20' ) ?></a> <div class="object-name"><a href="<?php bp_the_topic_object_permalink() ?>" title="<?php bp_the_topic_object_name() ?>"><?php bp_the_topic_object_name() ?></a></div> </td> <?php endif; ?> <td class="td-postcount"> <?php bp_the_topic_total_posts() ?> </td> <td class="td-freshness"> <?php bp_the_topic_time_since_last_post() ?> </td> </tr> <?php endwhile; ?> </table> <?php else: ?> <div id="message" class="info"> <p><?php _e( 'Sorry, there were no forum topics found.', 'buddypress' ) ?></p> </div> <?php endif;?>
Helpful parameters for the forum topics loop are:
- type - by default, the loop pulls the latest, or newest, topics, but you can use the following arguments: popular,unreplied,tags. If you choose "tags", then you must also speficy search terms.
- forum_id - by specifying this, you pull forum topics only from the specified forum id.
- search_terms - used in conjunction with the "tags" type to return only specific topics by keyword.
Blogs Loop
The blogs loop is used to display a list of user blogs. The loop looks like this:
<?php if ( bp_has_blogs() ) : ?> <div class="pagination"> <div class="pag-count" id="blog-dir-count"> <?php bp_blogs_pagination_count() ?> </div> <div class="pagination-links" id="blog-dir-pag"> <?php bp_blogs_pagination_links() ?> </div> </div> <ul id="blogs-list" class="item-list"> <?php while ( bp_blogs() ) : bp_the_blog(); ?> <li> <div class="item-avatar"> <a href="<?php bp_blog_permalink() ?>"><?php bp_blog_avatar('type=thumb') ?></a> </div> <div class="item"> <div class="item-title"><a href="<?php bp_blog_permalink() ?>"><?php bp_blog_name() ?></a></div> <div class="item-meta"><span class="activity"><?php bp_blog_last_active() ?></span></div> <?php do_action( 'bp_directory_blogs_item' ) ?> </div> <div class="action"> <div class="generic-button blog-button visit"> <a href="<?php bp_blog_permalink() ?>" class="visit" title="<?php _e( 'Visit Blog', 'buddypress' ) ?>"><?php _e( 'Visit Blog', 'buddypress' ) ?></a> </div> <div class="meta"> <?php bp_blog_latest_post() ?> </div> <?php do_action( 'bp_directory_blogs_actions' ) ?> </div> <div class="clear"></div> </li> <?php endwhile; ?> </ul> <?php do_action( 'bp_after_directory_blogs_list' ) ?> <?php bp_blog_hidden_fields() ?> <?php else: ?> <div id="message" class="info"> <p><?php _e( 'Sorry, there were no blogs found.', 'buddypress' ) ?></p> </div> <?php endif; ?>
Helpful parameters for the blogs loop are:
- type - by default this returns active blogs, but you can also use the arguments : newest and random.
- per_page
Group Members Loop
This loop is used to display members of a specific group.
<?php if ( bp_group_has_members() ) : ?> <div id="member-count" class="pag-count"> <?php bp_group_member_pagination_count() ?> </div> <div id="member-pagination" class="pagination-links"> <?php bp_group_member_pagination() ?> </div> <ul id="member-list" class="item-list"> <?php while ( bp_group_members() ) : bp_group_the_member(); ?> <li> <!-- Example template tags you can use --> <?php bp_group_member_avatar() ?> <?php bp_group_member_link() ?> <?php bp_group_member_joined_since() ?> </li> <?php endwhile; ?> </ul> <?php else: ?> <div id="message" class="info"> <p>This group has no members.</p> </div> <?php endif;?>
Private Messages Loop
The private messages loop is used to display private messages to a user. It also includes a nice list of template tags to customize its display.
<div class="pagination-count"> <?php bp_messages_pagination_count() ?> </div> <div class="pagination-links"> <?php bp_messages_pagination() ?> </div> <ul id="message-threads"> <?php while ( bp_message_threads() ) : bp_message_thread(); ?> <li> <!-- Example template tags you can use --> <?php bp_message_thread_id() ?> <?php bp_message_thread_has_unread() ?> <?php bp_message_thread_unread_count() ?> <?php bp_message_thread_avatar() ?> <?php bp_message_thread_from() ?> <?php bp_message_thread_last_post_date() ?> <?php bp_message_thread_view_link() ?> <?php bp_message_thread_subject() ?> <?php bp_message_thread_excerpt() ?> <?php bp_message_thread_delete_link() ?> </li> <?php endwhile; ?> </ul> <?php else: ?> <div id="message" class="info"> <p>There are no messages to display.</p> </div> <?php endif;?>
Profile Data Loop
The profile data loop is used to display user's profile data.
<?php if ( bp_has_profile() ) : ?> <?php while ( bp_profile_groups() ) : bp_the_profile_group(); ?> <ul id="profile-groups"> <?php if ( bp_profile_group_has_fields() ) : ?> <li> <?php bp_the_profile_group_name() ?> <ul id="profile-group-fields"> <?php while ( bp_profile_fields() ) : bp_the_profile_field(); ?> <?php if ( bp_field_has_data() ) : ?> <li> <?php bp_the_profile_field_name() ?> <?php bp_the_profile_field_value() ?> </li> <?php endif; ?> <?php endwhile; ?> </ul> <li> <?php endif; ?> </ul> <?php endwhile; ?> <?php else: ?> <div id="message" class="info"> <p>This user does not have a profile.</p> </div> <?php endif;?>
While BuddyPress contains a lot of loops, they are easy to understand and customize. By studying them, you should also gain a better understanding of how BuddyPress uses template tags and conditionals in its default theme. Now, let's take a look at how BuddyPress uses conditional template tags.
Using Conditional Template Tags
Conditionals are a great way to easily and quickly develop custom themes that change based on certain conditions. In BuddyPress, these conditionals will start with : bp_is_ and they are used to display specific content on specific pages. For example, if I wanted to display something specific on group home pages, then I would use the following conditional template tag:
<?php if ( bp_is_group_home() ) : ?> Show this on group home pages <? endif; ?>
My conditional template tag here is bp_is_group_home(). To display something only on user home pages, I would do the same as above, but with the conditional template tag: bp_is_home or bp_is_my_profile.
For a comprehensive list of these, click here.
Creating a BuddyPress Child Theme
Now, let's move out of API talk - you can get up and stretch for a minute if you need to - and move to some practical application. Again, we are trying to lay the groundwork for our custom theme to come in parts 2 and 3, so let's look now at creating a child theme for BuddyPress. I can't stress the importance of this enough. Let me share some personal experience about how important this point really is. Recently, I made a rookie mistake, and directly edited the core files of BuddyPress without thinking about upgrades and new releases. I bet you can guess what happened next. That's right. I forgot to back it up and the new release overwrote all of the work that I had put into it. I was pretty bummed and had to spend unneccessary hours fixing the mess. So, to help you avoid that, here's how to create a BuddyPress child theme that we will edit and work on in the next two parts of this tutorial series.
Lay a Solid Foundation
First, create a new folder in your wp-content/themes/ folder named cool-bp-theme. This will be our BuddyPress child theme. If you are confused about what a "child theme" is, just think of it as a theme that will still perform and function like the bp-default theme located in wp-content/plugins/buddypress/bp-themes/bp-default/, but will allow for editing and customization and will not break when Wordpress or BuddyPress is upgraded. It does this by only changing the template files located in our child theme folder. It will even use the bp-default stylesheet, but allow for new stylesheets to be imported or original CSS to be overwritten in style.css.
After creating the new folder, I create a new blank functions.php file (because I expect to edit some functionality), and a new style.css file with the following code:
/* Theme Name: Cool New BuddyPress Theme Description: Cool new theme for BuddyPress. Version: 1.0 Author: Adam Murray Author URI: http://twodoorscreative.com/ Template: bp-default Tags: buddypress, two-column, light */
The core element here that is different is the template line, where I tie this to the bp-default theme thus making it a child theme. I now want to go a step further and import the standard bp-default stylesheets. To do that, underneath this code I enter:
/* Inherit the default theme styles */ @import url( ../../plugins/buddypress/bp-themes/bp-default/_inc/css/default.css ); /* Inherit the default theme adminbar styles */ @import url( ../../plugins/buddypress/bp-themes/bp-default/_inc/css/adminbar.css );
Now, you have the basics of a BuddyPress child theme. If you browse to Appearance --> Themes in your Wordpress dashboard, you will now see the child theme, and, when you activate it, you should see exactly what you would see if you activated bp-default.
Duplicate, Move, and Edit
Since the child theme is now up and active, to edit a template, we simply do the following:
- Duplicate the file we want to edit from the bp-default theme.
- Move the duplicated file - keeping the same file name - to our new child theme folder. You could also copy the old file to the new theme folder.
- Edit the file. As you edit, you will immediately notice the changes in your theme's appearance.
For a quick example of how to do this, let's look at adding the custom header menu that I taught in last month's BuddyPress tutorial.
- Copy header.php to your new theme folder.
- Open the blank functions.php file that you created eariler and add the following:
<php //Register Custom Menu function register_bp_default_menus() { register_nav_menus( array( 'header-menu' => ( 'Header Menu' )) ); } add_action( 'init', 'register_bp_default_menus' ); ?>
- Now open header.php, and replace line 79 with the following:
<?php wp_nav_menu( array( 'theme_location' => 'header-menu', 'container' => false, ) ); ?>
- Now, open up your site and look at how the old list of pages has been removed. Go to Appearance --> Menus and create a new header menu!
The best part of all is that this is a permanent change and won't be removed due to an upgrade!
Conclusion
In conclusion, as I hope that I've said repeatedly, this is a foundation for you to build upon. I hope that this tutorial has given you a deeper understanding of the BuddyPress API, and that it has helped you become more familiar with the elements that make up a BuddyPress theme. In the upcoming sections, we'll elaborate on our current child theme to create a theme that looks and feels like we want it to by using template tags, loops, and conditionals. By doing that together, I hope to take to you a place where you can comfortably create new themes with BuddyPress.
Thanks for reading and I hope I've helped you learn something new about themeing with BuddyPress and its API!
Comments