将自定义过滤器添加到循环中并在您的插件中使用它们Codex Home → BuddyPress Plugin Development → Add custom filters to loops and enjoy them within your plugin
Add custom filters to loops and enjoy them within your plugin
BuddyPress uses loops to display the content of its components. In this article you will focus on how to 「rearrange」 the content displayed in four of its major components : Members, Groups, Blogs and finally Activity. You will achieve this thanks to the type or action argument of the init functions of each of these loops :
Loops
Init functions
Locations
Members
bp_has_members( array( $arguments ) )
/bp-activity/bp-activity-template.php
Groups
bp_has_groups( array( $arguments ) )
/bp-groups/bp-groups-template.php
Blogs
bp_has_blogs( array( $arguments ) )
/bp-blogs/bp-blogs-template.php
Activity
bp_has_activities( array( $arguments ) )
/bp-activity/bp-activity-template.php
To let members of a BuddyPress powered community change the way items of loops are displayed, BuddyPress uses select boxes that includes the different types to reorder the members, groups or blogs and filter the activity stream to only keep the desired action (or activity type). These select boxes are generally available from the directory page of the component, and in the specific case of the activity component : in single group and member home page.
In the following tutorial, you will first extend the available filters of the Members, Groups and Blogs loops, then you will remember how to add custom actions (or activity types) to the Activity component for your plugin, finally you will build a great feature thanks to a very interesting Activity Meta Query.
Here』s your roadmap
Add a custom filter to the Members, Groups and Blogs loops
Extend the activity types to register your plugin ones
Build a new great feature thanks to the 『favorite an activity』 functionality
Additional resources
In additional resources, you will find an example of plugin that includes all the codes that this tutorial contains. If you wish, you can activate the plugin in a local dev environment to follow the different steps of this article.
Add a custom filter to the Members, Groups and Blogs loops
To illustrate the process, you are going to use a built in filter type that is not activated by default in loops : the 「random」 order one. This screenshot shows the result of your first lines of code for the Members component.
The random filter in Members directory
If you dive into BuddyPress /bp-templates/bp-legacy/buddypress folder, you will find 3 templates that are used to display the directory pages for the 3 components you』re dealing with in this first step:
Templates
Hooks
Lines
/bp-templates/bp-legacy/buddypress/members/index.php
do_action( 'bp_members_directory_order_options' );
44
/bp-templates/bp-legacy/buddypress/groups/index.php
do_action( 'bp_groups_directory_order_options' );
44
/bp-templates/bp-legacy/buddypress/blogs/index.php
do_action( 'bp_blogs_directory_order_options' );
42
So all you need to do in order to set the 「random」 filter is to add an action to these three hooks. Let』s init your plugin』s class and write the lines that will render the above screenshot』s result.
?1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465setup_actions(); } /** * Actions * * @uses bp_is_active() * @uses is_multisite() */ private function setup_actions() { /** * Adds the random order to the select boxes of the Members, Groups and Blogs directory pages */ // Members component is core, so it will be available add_action( 'bp_members_directory_order_options', array( $this, 'random_order' ) ); // You need to check Groups component is available if( bp_is_active( 'groups' ) ) add_action( 'bp_groups_directory_order_options', array( $this, 'random_order' ) ); // You need to check WordPress config and that Blogs Component is available if( is_multisite() && bp_is_active( 'blogs' ) ) add_action( 'bp_blogs_directory_order_options', array( $this, 'random_order' ) ); } /** * Displays a new option in the Members/Groups & Blogs directories * * @return string html output */ public function random_order() { ?> prefix}bp_activity MySQL table */ 'bp_plugin', /* your component's activity type : - same value as the "type" field you will use in the {$wpdb->prefix}bp_activity MySQL table - it will be used in the value attribute of the plugin option in the activity selectbox */ 'bpplugin_action', /* your component's caption : - it will be displayed to the user in the activity selectbox */ __( 'BP Plugin Action' ) );}
Once you』ve added these lines, when opening the Activity Administration screen, you will find your plugin』s filter inside the available choices of the select box as shown in the following screen capture.
BP Plugin Action filter in Activity Administration screen
Back to front end! You can take a new swim into the BuddyPress /bp-templates/bp-legacy/buddypress folder to find the key actions to hook to in order to add your custom activity type(s).
Templates
Hooks
Lines
/bp-templates/bp-legacy/buddypress/activity/index.php
do_action( 'bp_activity_filter_options' );
110
/bp-templates/bp-legacy/buddypress/members/single/activity.php
do_action( 'bp_member_activity_filter_options' );
55
/bp-templates/bp-legacy/buddypress/groups/single/activiy.php
do_action( 'bp_group_activity_filter_options' );
20
Before rushing into the process of using these hooks, you are going to build a transition function to get the list of activity types you』ve created. As you have only one activity type so far, you may think it』s not necessary but i personaly consider it as a good way to not forget the Administration screens of the Activity component. Moreover, you will soon add a new activity type for the final step. You need to edit your plugin』s class by adding a new function to list the activity actions.
?1234567891011121314151617181920212223/** * Building an array to loop in from the display function * * Using bp_activity_get_types() will list all registered activity actions * but you need to get the ones for your plugin, and this particular function * directly returns an array of key => value. As you need to filter activity * with your component id, the global buddypress()->activity->actions will be * more helpful. * * @uses buddypress() * @return array the list of your plugin actions. */private function list_actions() { $bp_activity_actions = buddypress()->activity->actions; $bp_plugin_actions = array(); if( !empty( $bp_activity_actions->bp_plugin ) ) $bp_plugin_actions = array_values( (array) $bp_activity_actions->bp_plugin ); return $bp_plugin_actions;}
Now you can use the 3 hooks previously identified by adding some code into the setup_actions() function of your plugin』s class, just after the code that hooks 'bp_register_activity_actions'.
?12345678// Adds a new filter into the select boxes of the Activity directory page,// of group and member single items activity screensadd_action( 'bp_activity_filter_options', array( $this, 'display_activity_actions' ) );add_action( 'bp_member_activity_filter_options', array( $this, 'display_activity_actions' ) ); // You need to check Groups component is availableif( bp_is_active( 'groups' ) ) add_action( 'bp_group_activity_filter_options', array( $this, 'display_activity_actions' ) );
You will also need to create the function display_activity_actions() to build the different new options that will populate the select boxes. This function will use the transition function to get the activity types of your plugin.
?123456789101112131415161718/*** Displays new actions into the Activity select boxes* to filter activities* - Activity Directory* - Single Group and Member activity screens** @return string html output*/public function display_activity_actions() { $bp_plugin_actions = $this->list_actions(); if( empty( $bp_plugin_actions ) ) return; foreach( $bp_plugin_actions as $type ):?> <option value=""> <?php endforeach;}
Now you can check the result of your work by displaying the Activity Directory, a member』s home page and a group』s home page. Here』s a screenshot for the Activity Directory.
BP Plugin action filter in Activity Directory
As you can see, the new option is available, and you can filter the activities to only get the activities that will be generated by your plugin. You only need to build the functions that will actually write new activities as explained in the codex article 『Posting Activity from Plugins』.
Build a new great feature thanks to the favorite an activity functionality
In BuddyPress, there』s a feature to let members favorite some activities so that they can easily find the activities they are really interested in from the 「My Favorites」 tab of the Activity directory or from the one of their profile home page. And reading the above lines gave you a great idea! You think the community would really appreciate to quickly see what are the most favorited activities by all members. In a way and thanks to this new feature, members would be able to easily know what are the ones they should absolutely read. And you will see that displaying these most favorite activities in the different screens will have different new powerful meanings.
First, let』s extend the register_activity_actions() function of your class to add a new very particular activity type. It actually won』t behave like a regular one. Its goal won』t be to display a particular type of activities as any type can be favorited. Using this new filter will perform two other things : only the favorited activities will be displayed and this display will be ordered regarding the number of times an activity has been favorited. So for this reason, you will not make this filter available from the Administration screen of Activities.
?12345/* Activity Administration screen does not use bp_ajax_querystringMoreover This action type is reordering instead of filtering so you will onlyuse it on front end */if( !is_admin() ) bp_activity_set_action( 'bp_plugin', 'activity_mostfavs', __( 'Most Favorited' ) );
That』s all we have to do to add this new type to the select boxes as your function list_actions() will automatically returns it to the function display_activity_actions(). You now need to handle this new filter type by intercepting it when the init function of the Activity loop will be run. If you open the template /bp-templates/bp-legacy/buddypress/groups/single/activity-loop.php, you』ll notice that the loop is using a function as an argument of this init function :
bp_ajax_querystring() plays a key role in the way that BuddyPress gets user inputs in order to render the appropriate display of the Activity loop. Just before returning these inputs, the function offers a filter so that you can get them and eventually edit them. You are going to use this filter, but not too early to let BuddyPress actually build the loop arguments, so you will choose a priority greater than 10, let』s say 12. You need to edit the constructor of your class so that it includes a new call to the function that will contain all your filters. This is the new constructor of your plugin :
?123456789101112class BP_Loop_Filters { /** * Constructor */ public function __construct() { $this->setup_actions(); // simply add the following line to your constructor $this->setup_filters(); } }
Once the constructor edited, you will create the setup_filters() function and begin it by adding the 'bp_ajax_querystring' filter at a priority of 12. You also need to inform WordPress that your function is expecting to receive the two arguments available for this filter : the querystring and the object it relates to. That』s why there is the number 2 after the priority argument.
?123456/** * Filters */private function setup_filters() { add_filter( 'bp_ajax_querystring', array( $this, 'activity_querystring_filter' ), 12, 2 );}
The filter is in place, it』s now time you take care of building the function that will make eveything possible for your need. This is activity_querystring_filter() :
?123456789101112131415161718192021222324252627282930313233343536373839404142434445464748/** * Builds an Activity Meta Query to retrieve the favorited activities * * @param string $query_string the front end arguments for the Activity loop * @param string $object the Component object * @uses wp_parse_args() * @uses bp_displayed_user_id() * @return array()|string $query_string new arguments or same if not needed */public function activity_querystring_filter( $query_string = '', $object = '' ) { if( $object != 'activity' ) return $query_string; // You can easily manipulate the query string // by transforming it into an array and merging // arguments with these default ones $args = wp_parse_args( $query_string, array( 'action' => false, 'type' => false, 'user_id' => false, 'page' => 1 ) ); /* most favorited */ if( $args['action'] == 'activity_mostfavs' ) { unset( $args['action'], $args['type'] ); // on user's profile, shows the most favorited activities for displayed user if( bp_is_user() ) $args['user_id'] = bp_displayed_user_id(); // An activity meta query 🙂 $args['meta_query'] = array( array( /* this is the meta_key you want to filter on */ 'key' => 'favorite_count', /* You need to get all values that are >= to 1 */ 'value' => 1, 'type' => 'numeric', 'compare' => '>=' ), ); $query_string = empty( $args ) ? $query_string : $args; } return apply_filters( 'bp_plugin_activity_querystring_filter', $query_string, $object );}
Let』s take a minute to understand what happens in it. The 2 arguments are waiting for the filter values and default to an empty string. Then, the very first thing to do is to make sure the activity loop is about to be run as BuddyPress also uses this function for the Members, Groups, legacy user group forums, Blogs and private messages. In other words, if 'activity' is not the value of the object argument, then just return the current query string without editing it.
The query string you will receive is actually a query string! so the arguments will look like 'arg1=1&arg2=2'. I advise you to use the function wp_parse_args() to quicky get an array of arguments : that will be a lot more easy to manipulate. Now you can check the action or type argument (which are carrying the same values) to see if it matches your most favorited activity type. If so, you can unset this values to include all types of activities.
If you are in a user』s profile, let』s only keep the activities for the user displayed to be consistent as the most favorited activity type will also be available in the select box of member』s home page. And, this is my favorite part, you will build an Activity Meta Query to include all the activities that have a meta value over 1 for the meta key 'favorite_count'.
If you want to know more about Meta Queries, you』ll find in additional resources a link to the article dealing with Group Meta Queries and a link to the WordPress codex on the WP_Meta_Query class. Finally you simply need to replace the value of the query string with your $args array.
So far, you are getting the favorited activities by all users when the Most Favorited filter is selected by the member, you need to bring the final touch, the one that often makes the difference for your plugin. You need to edit the order of the loop so that it will display the favorited activities from the more favorited one to the less favorited one. To achieve that step, you will need to put your ninja warrior suit on! Let』s add a new filter into the setup_filters() function of your class.
This is your new setup_filters() function :
?1234567/** * Filters */private function setup_filters() { add_filter( 'bp_ajax_querystring', array( $this, 'activity_querystring_filter' ), 12, 2 ); add_filter( 'bp_activity_get_user_join_filter', array( $this, 'order_by_most_favorited' ), 10, 6 );}
This filter will get the query that is sent to the Activity loop to first edit the field it』s ordered by. Then as the bp_activity_meta table will be joined in the query thanks to your Activity Meta Query. Let』s enjoy it to the max, by also adding a new field that will alias the meta value field for the favorite_count meta key. Doing so the global $activities_template will include this value and it will be very easy to get it for each entry of the loop. The function order_by_most_favorited() will get the 6 arguments available for the 'bp_activity_get_user_join_filter' filter. The most important is the first one as it』s the sql query. Others are part of this query to help you edit the main query.
?123456789101112131415161718192021222324252627282930313233343536/** * Ninja Warrior trick to reorder the Activity Loop * regarding the activities favorite count * * @param string $sql the sql query that will be run * @param string $select_sql the select part of the query * @param string $from_sql the from part of the query * @param string $where_sql the where part of the query * @param string $sort the sort order (leaving it to DESC will be helpful!) * @param string $pag_sql the offset part of the query * @return string $sql the current or edited query */public function order_by_most_favorited( $sql = '', $select_sql = '', $from_sql = '', $where_sql = '', $sort = '', $pag_sql = '' ) { preg_match( '/'favorite_count' AND CAST((.*) AS/', $where_sql, $match ); if( !empty( $match[1] ) ) { $new_order_by = 'ORDER BY '. $match[1] .' + 0'; $new_select_sql = $select_sql . ', '. $match[1] .' AS favorite_count'; $sql = str_replace( array( $select_sql, 'ORDER BY a.date_recorded' ), array( $new_select_sql, $new_order_by ), $sql ); /** * To help you build the pattern to search for * you can use the var_dump function to see the * query that will be performed * var_dump( $sql ); */ } return $sql;}
As you can see, you need to get the table name that the Activity Meta Query will build to use it to define you new ORDER BY clause and to include your favorite_count alias field into the SELECT clause.
The very last step is to finally display the favorite count into the entry template of the Activity component. If you explore the BuddyPress /bp-templates/bp-legacy/buddypress folder, in the activity/entry.php template, you will find the 'bp_activity_entry_meta' hook which can be a nice place to add your mention about the number of times the activity has been favorited. So to insert this mention, you simply need to edit your plugin』s setup_actions() function to add an action to this hook and build the function that will be run.
?1234567891011121314151617181920212223242526272829303132private function setup_actions() { /** * previous steps code */ if( bp_is_active( 'activity' ) ) { /** * previous steps code */ // You're going to output the favorite count after action buttons add_action( 'bp_activity_entry_meta', array( $this, 'display_favorite_count' ) ); } } /** * Displays a mention to inform about the number of times the activity * was favorited. * * @global BP_Activity_Template $activities_template * @return string html output */public function display_favorite_count() { global $activities_template; $fav_count = !empty( $activities_template->activity->favorite_count ) ? $activities_template->activity->favorite_count : 0; if( !empty( $fav_count ) ):?> <a name="favorite-"> <?php endif;}
If you observe the result of a 「Most favorited」 filter into the main Activity Directory, you will see that only the favorited activities are displayed ordered by the number of times they had been favorited.
Most Favorited in Activity directory
If you do the same on the user』s profile home page, the filter will inform on his activities : the ones that has been favorited by him or other users. So it can be interested for a user to see the interest of his activities for the community.
Most favorited in member』s home page
If you display this filter into a group home page, it will list the group activities that were favorited by its own members in case of a non public group and potentially all community members for a public group.
Most favorited in a group home page
Additional resources
All the codes of this tutorial packaged in a plugin
The Members Loop in BuddyPress Codex
The Groups Loop in BuddyPress Codex
The Blogs Loop in BuddyPress Codex
The Activity Loop in BuddyPress Codex
WP_Meta_Query in WordPress codex
Group Meta Queries: Usage Example in BuddyPress Codex
Posting Activity from Plugins in BuddyPress Codex
bp_ajax_querystring in BuddyPress codex