On This Page...
Last updated September 22nd, 2023 at 01:00 pm
This post is inspired by a question that was posed in Facebook MemberPress Unofficial User Group:
Is there a way to see all of the users’ course progress in one place? I know I can access each user and see their progress that way. I was just hoping that there might be an easier way to see all of the members at once. Does anyone have any ideas on how to do this? Thanks in advance!
This is exactly what I implemented — a custom memberpress course progress page — in one of my WordPress/MemberPress websites that has three courses using the MemberPress Courses addon. In my case, I wanted to offer two views of course progress: one grouped by User; the other grouped by Course.
Overview
Screen Shots of the Final Product
Here are two screen shots displaying small sections of the course progress reports. The names and usernames of actual members are blurred out.
Course Progress Grouped by User:

Course Progress Grouped by Course:

Components of the Solution:
1. Custom Function
A custom function in the /wp-content/mu-plugins/
folder which generates the code using a WordPress shortcode: [course_progress]
. The shortcode accepts one parameter: group_by
, with the possible values of user
and course
. The full script is provided below.
2. Protected Page
I created a protected page which only site Admins can access, using a rule based on role.
Aside from the page title and a short, 2-line intro paragraph, the page consists of a single containing block — the Kadence Tabs block, with two tabs: one for Course Progress Grouped by User; the other for Course Progress Grouped by Course. I inserted the shortcode [course_progress group_by="user"]
in the first tab and [course_progress group_by="course"]
in the second tab.
3. Protected Nav Menu Items
We already had a top-level navigation menu item called “Resources”, so I added a link to the protected page there. Using the Nav Menu Roles WordPress plugin, I hid this nav menu item from anyone who is not a logged-in Admin. (I also assigned the class name of “admin-only” to this nav menu link and used CSS to render it with red text over a yellow background to highlight it and remind Admins that no one else can see it.)
The Code:
Here is the complete script. I disabled wrapping of lines because I find that hurts more than it helps. If you want to inspect the code more closely, it’s probably best to click on the “view source” button, and then copy/paste it into your script editor. Comments within the script may be enough to explain what’s going on here. If you have questions or comments, please write them in the comments section below. (Big hat tip to MemberPress support staff who have shared code snippets with me over the years, especially TylerG.)
function shortcode_nwb_course_progress($atts) { $atts = shortcode_atts( array( 'group_by' => 'user', ), $atts, 'course_progress' ); $group_by = $atts['group_by']; $users = get_users( array( 'role__in' => array( 'customer_free', 'customer_premium'), 'orderby' => 'name', 'order' => 'ASC', ) ); // Grab all courses and see if the sub account has access. $course_posts = get_posts(array( 'post_type' => 'mpcs-course', 'post_status' => 'publish', 'numberposts' => -1, 'orderby' => 'title', 'order' => 'ASC'), ); $output = ''; /* Init $output */ switch ( $group_by ) { case 'user': if(!empty($users)) { /* Start a div */ $output .= '<div class="course-progress-wrap">'; $output .= '<h2>Course Progress Grouped by User</h2>'; $output .= '<table class="course-progress__table" style="width:auto;">'; foreach($users as $user) { $courses = array(); // I moved down $user_id = $user->ID; $mepr_user = new MeprUser($user_id); $registration_timestamp = strtotime($user->user_registered); // 2023-03-22 14:02:55 $registration_date = date('m-d-Y', $registration_timestamp); $elapsed_days = round((time() - $registration_timestamp) / (24*60*60)); /* Now get memberships */ $this_user = MeprUtils::get_user_by('id', $user_id); $his_products = array(); // Init $his_product_ids = array(); // Init $active_products = $mepr_user->active_product_subscriptions('products'); if(!empty($active_products)) { foreach($active_products as $membership_id) { $his_products[] = $membership_id->post_title; $his_product_ids[] = $membership_id->ID; // for class name } $his_products_cos = implode(', ' , $his_products); } /* Product Class 2023-03-22 14:35:00 */ $product_class = ( in_array(203, $his_product_ids) ) ? 'master' : 'intro'; // Now loop through courses. if(!empty($course_posts)) { $output .= '<tr>'; /* Add user_login 2022-11-02 17:32:01 */ $output .= '<td colspan="2" class="' . $product_class . '"><h3>' . $user->display_name . ' <span class="user_login">(' . $user->user_login . ')</span> - <span class="program_name ' . $product_class . '">' . $his_products_cos . '</span></h3>'; /* 2023-03-22 14:15:30, Add reg info */ $output .= 'Registered: <span class="registration_date">' . $registration_date . '</span>'; $output .= ' (<span class="elapsed-days">' . $elapsed_days . ' days ago</span>)'; $output .= '</td>'; foreach($course_posts as $course_post) { if(!MeprRule::is_locked_for_user($mepr_user, $course_post)) { $courses[] = $course_post; } } foreach($courses as $course) { $course = new memberpress\courses\models\Course($course->ID); $progress = $course->user_progress($user_id); $prog_pct = $progress / 100; $output .= '<tr>'; $output .= '<td class="col-1">' . $course->post_title .'</td>'; $output .= '<td><span class="course-progress">' . '<span class="user-progress" data-value="'. $progress . '" style="width: ' . $progress . '%;">' . $progress . '</span> </span> <span>%</span></td>'; } $output .= '</tr>'; } } } $output .= '</table>'; $output .= '</div>'; break; case 'course': if ( !empty($course_posts) ) { $output .= '<div class="course-progress-wrap">'; $output .= '<h2>Course Progress Grouped by Course</h2>'; $output .= '<table class="course-progress__table" style="width:auto;">'; foreach($course_posts as $course_post) { $courses = array(); $courses[] = $course_post; foreach ( $courses as $course ) { $course = new memberpress\courses\models\Course($course->ID); $output .= '<tr>'; $output .= '<td colspan="2"><h3>' . $course->post_title . '</h3></td>'; $output .= '</tr>'; foreach($users as $user) { $user_id = $user->ID; $mepr_user = new MeprUser($user_id); $output .= '<tr>'; $progress = $course->user_progress($user_id); /* Add user_login 2022-11-02 17:33:58 */ $output .= '<td class="col-1">' . $user->display_name . ' (' . $user->user_login . ')</td>'; $output .= '<td><span class="course-progress">' . '<span class="user-progress" data-value="'. $progress . '" style="width: ' . $progress . '%;">' . $progress . '</span> </span> <span>%</span> </td>'; $output .= '</tr>'; } } } $output .= '</table>'; $output .= '</div><!-- end wrap -->'; } break; } return $output; } add_shortcode('course_progress', 'shortcode_nwb_course_progress');
In Closing
I hope that the solution I’ve offered here might be of help to some of you. If so, let me know in the comments.
If you build membership websites with MemberPress, you might want to see other MemberPress-related posts I’ve written.
Leave a Reply