Skip to content

Instantly share code, notes, and snippets.

@indigetal
Last active May 14, 2024 00:32
Show Gist options
  • Save indigetal/5d49e4c2f62bb2c6007595ceb11cabb4 to your computer and use it in GitHub Desktop.
Save indigetal/5d49e4c2f62bb2c6007595ceb11cabb4 to your computer and use it in GitHub Desktop.
WORK IN PROGRESS: Monetize SpacesEngine using Paid Memberships Pro and Selection of Addons. See link to accompanying tutorial in comments.
<?php
/*
Plugin Name: Paid Memberships Pro Integration with SpacesEngine for BuddyBoss
Plugin URI: https://www.paidmembershipspro.com/create-a-plugin-for-pmpro-customizations/
Description: Integrate Paid Memberships Pro With SpacesEngine for BuddyBoss
Version: 0.2.6
Author: Brandon Meyer
Author URI: indigetal.com
*/
// Check if the pmpro_get_level_ids_for_group() exists before calling it
if ( ! function_exists( 'pmpro_get_level_ids_for_group' ) ) {
// Function does not exist, handle the error or log it
error_log( 'pmpro_get_level_ids_for_group() function does not exist' );
} else {
// EDIT HERE: First, lets retrieve the level IDs in the "Individual Levels" group (here it's Group ID: 3)
$individual_levels = pmpro_get_level_ids_for_group(3);
// EDIT HERE: Retrieve level IDs in the "SpacesEngine Levels" group (here it's Group ID: 4)
$spacesengine_levels = pmpro_get_level_ids_for_group(4);
}
// (OPTIONAL) Disable the pmpro redirect to levels page when user tries to register
add_filter("pmpro_login_redirect", "__return_false");
// (OPTIONAL) EDIT HERE: ...And give all newly registered users a default membership level (here it's Level ID: 1)
function my_pmpro_default_registration_level($user_id) {
pmpro_changeMembershipLevel(1, $user_id);
}
add_action('user_register', 'my_pmpro_default_registration_level');
// Restrict access to certain pages based on level group:
/*
* NOTE: If you are using a PMPro addon that requires new users to be validated, such as the
* Email Confirmation addon, users will not have access to restricted pages until they are
* validated. See the comment in Github Gist for an alternative approach that bypasses any
* validation requirements.
*
*/
function my_pmpro_custom_redirects() {
// Check if the function has already been executed to avoid multiple executions
if ( defined( 'MY_PMPRO_CUSTOM_REDIRECTS_EXECUTED' ) ) {
return;
}
// Set the flag to indicate that the function has been executed
define( 'MY_PMPRO_CUSTOM_REDIRECTS_EXECUTED', true );
global $individual_levels, $spacesengine_levels, $pmpro_pages; // Make the variables accessible inside the function
// EDIT HERE: The pages to be restricted
$page_names = array('create-space-page', 'other-restricted-page');
// First check if PMPro is active
if ( ! function_exists( 'pmpro_hasMembershipLevel' ) ) {
return;
}
// Loop through each page name
foreach ( $page_names as $page_name ) {
// Does the page name match?
if ( strpos( $_SERVER['REQUEST_URI'], '/' . $page_name . '/' ) !== false ) {
$current_page_name = $page_name; // Set the current page name
// Check if user is an administrator
if ( current_user_can( 'administrator' ) ) {
return; // Allow administrators to access restricted pages
}
// EDIT HERE: If a visitor trying to access a restricted area doesn't have an individual membership... (EDIT 'other-restricted-page')
if ( $current_page_name == 'other-restricted-page' && ! pmpro_hasMembershipLevel( $individual_levels ) && ( ! is_page( $pmpro_pages['levels'] ) || ! is_page( $pmpro_pages['checkout'] ) ) ) {
// Redirect them to the individual plans page (EDIT '/membership/individual-plans-page/'):
wp_safe_redirect( '/membership/individual-plans-page/' );
exit;
// EDIT HERE: Redirect members that don't have level ID's associated with SpacesEngine levels to the relevant plans page (EDIT 'create-space-page'):
} elseif ( $current_page_name == 'create-space-page' && ! pmpro_hasMembershipLevel( $spacesengine_levels ) && ( ! is_page( $pmpro_pages['levels'] ) || ! is_page( $pmpro_pages['checkout'] ) ) ) {
// Redirect them to the SpacesEngine plans page (EDIT '/membership/spacesengine-plans-page/'):
wp_safe_redirect( '/membership/spacesengine-plans-page/' );
exit;
}
}
}
}
add_action( 'template_redirect', 'my_pmpro_custom_redirects' );
/*
* Adjust the cost of a level at checkout based on the selection of a custom user field for
* upselling additional options to a plan. Accounts for recurring plans, and adjusts the cost based
* on if it's a monthly or annual plan (see https://gist.github.com/indigetal/e172e17230ceb1f9516b7c147b6898d4)
*/
function my_pmpro_adjustable_level_cost($level) {
// Check if the level object exists and has an 'id' property
if (isset($level->id) && ! in_array($level->id, $GLOBALS['spacesengine_levels'])) {
return $level; // Return the unmodified level if not in SpacesEngine group
}
// Set the field name here
$field_name = 'upgrade_listing';
// Flag to track if adjustments have been made
static $adjustments_made = false;
// Check if adjustments have already been made, and proceed if not
if (!$adjustments_made) {
// Check if the selected upgrade option exists
if (!empty($_REQUEST[$field_name])) {
// EDIT HERE: Set the monthly and annual fees of the available field options
$options = array(
'promoted' => array('monthly_fee' => 10, 'annual_fee' => 100),
'featured' => array('monthly_fee' => 15, 'annual_fee' => 120)
);
/*
* There are a couple of additional housekeeping items to ensure your users are
* presented with accurate information - see comment in
* https://gist.github.com/indigetal/e172e17230ceb1f9516b7c147b6898d4?permalink_comment_id=4651793#gistcomment-4651793
*/
// YOU ARE DONE EDITING. ENJOY!
if (isset($options[$_REQUEST[$field_name]])) {
$option_values = $options[$_REQUEST[$field_name]];
// Initialize extra fee
$extra_fee = 0;
// Determine the additional fee based on the selected option and cycle period
if ($level->cycle_period === 'Month') {
$extra_fee = $option_values['monthly_fee'];
} elseif ($level->cycle_period === 'Year') {
$extra_fee = $option_values['annual_fee'];
}
// Check if there is an extra fee
if ($extra_fee > 0) {
// Add the additional fee to the level's initial payment
$level->initial_payment += $extra_fee;
// Check if the level has a recurring subscription
if (pmpro_isLevelRecurring($level)) {
// Recurring payments
$level->billing_amount += $extra_fee;
}
}
}
}
// Set the flag to true to indicate adjustments have been made
$adjustments_made = true;
}
return $level;
}
add_filter("pmpro_checkout_level", "my_pmpro_adjustable_level_cost", 10, 1);
// Update the billing_amount and initial_payment columns in pmpro_memberships_users table after checkout
function db_adjusted_cost_sync($user_id, $morder) {
global $wpdb;
// Get the specific id of the membership entry
$membership_entry_id = $wpdb->get_var(
$wpdb->prepare(
"SELECT id FROM $wpdb->pmpro_memberships_users WHERE user_id = %d AND membership_id = %d AND status = 'active'",
$user_id,
$morder->membership_id
)
);
if ($membership_entry_id) {
// Update the specific row with the adjusted prices
$wpdb->update(
$wpdb->pmpro_memberships_users,
array(
'initial_payment' => $morder->subtotal,
'billing_amount' => $morder->subtotal
),
array('id' => $membership_entry_id),
array(
'%s', // Format for initial_payment
'%s' // Format for billing_amount
),
array('%d') // Format for id
);
}
}
add_action('pmpro_after_checkout', 'db_adjusted_cost_sync', 10, 2);
/* Hook into the pmpro_get_membership_levels_for_user filter to modify the output
* of the pmpro_getMembershipLevelsForUser() function, ensuring that the adjusted
* price is reflected correctly on the membership account page.
*/
function display_membership_adjusted_price( $levels, $user_id ) {
// Iterate through each level
foreach ( $levels as $key => $level ) {
// Check if the level belongs to the SpacesEngine group
if (in_array($level->id, $GLOBALS['spacesengine_levels'])) {
// Get the selected option for the user
$upgrade_listing = get_user_meta( $user_id, 'upgrade_listing', true );
// Check if the user selected either 'featured' or 'promoted'
$selected_option = ($upgrade_listing === 'featured' || $upgrade_listing === 'promoted');
// If both conditions are true, adjust the price of the level
if ($selected_option) {
// Adjust the price of each SpacesEngine level
$levels[$key]->initial_payment = my_pmpro_adjustable_level_cost( $levels[$key]->initial_payment, $level );
$levels[$key]->billing_amount = my_pmpro_adjustable_level_cost( $levels[$key]->billing_amount, $level );
}
}
}
return $levels;
}
add_filter( 'pmpro_get_membership_levels_for_user', 'display_membership_adjusted_price', 10, 2 );
// Set the listing as promoted or featured if the user purchases that option:
function set_featured_space_meta( $post_id ) {
$post_type = get_post_type( $post_id );
if ( 'wpe_wpspace' === $post_type ) {
$current_user_id = get_current_user_id();
$upgrade_listing = get_user_meta( $current_user_id, 'upgrade_listing', true );
if ( $upgrade_listing === 'promoted' ) {
update_post_meta( $post_id, 'featured_space', 1 );
} elseif ( $upgrade_listing === 'featured' ) {
update_post_meta( $post_id, 'featured_space', 2 );
}
}
}
add_action( 'save_post', 'set_featured_space_meta' );
// Set a Member's Space to Draft and update upgrade_listing user meta to none when their SpacesEngine-based Plan is Removed
function pmpro_space_level_removed_actions( $level_id, $user_id ) {
// Get the user roles
$usermeta = get_userdata( $user_id );
$user_roles = $usermeta->roles;
// Check if the user has any of the specified roles and if their membership level is not in the SpacesEngine level group:
$allowed_roles = array(
'subscriber',
'editor',
'contributor',
'author'
);
global $spacesengine_levels; // Declare the global variable
if ( array_intersect( $allowed_roles, $user_roles ) && ! in_array( $level_id, $spacesengine_levels ) ) {
update_user_posts_to_draft( $user_id );
update_user_upgrade_listing( $user_id, 'none' ); // Update upgrade_listing to 'none'
}
}
function update_user_posts_to_draft( $user_id ) {
// Get the user's posts
$args = array(
'author' => $user_id,
'post_type' => 'wpe_wpspace',
);
$user_posts = get_posts( $args );
foreach ( $user_posts as $user_post ) {
$post = array( 'ID' => $user_post->ID, 'post_status' => 'draft' );
wp_update_post( $post );
}
}
function update_user_upgrade_listing( $user_id, $value ) {
update_user_meta( $user_id, 'upgrade_listing', $value ); // Update upgrade_listing user meta
}
add_action( 'pmpro_after_change_membership_level', 'pmpro_space_level_removed_actions', 10, 2 );
// Define $option_name as a global variable
global $option_name;
// Define an array of options and corresponding filter hooks
$options_and_filters = array(
'enable_space_creation' => 'wpe_wps_is_space_creation_enabled',
'space_creation_limit' => 'wpe_wps_get_space_creation_limit',
'verify_spaces' => 'wpe_wps_is_verification_enabled',
//'feature_spaces' => 'wpe_wps_is_featured_enabled',
//'promote_spaces' => 'wpe_wps_is_promoted_enabled',
'enable_space_admins' => 'wpe_wps_get_admins_enabled',
'enable_space_editors' => 'wpe_wps_get_editors_enabled',
'enable_profile_picture' => 'wpe_wps_can_profile_picture_upload',
'enable_cover_image' => 'wpe_wps_can_cover_image_upload',
'enable_short_description' => 'wpe_wps_is_short_description_enabled',
'display_long_description' => 'wpe_wps_can_display_long_description',
'display_website' => 'wpe_wps_can_display_website',
'display_email' => 'wpe_wps_can_display_email',
'display_address' => 'wpe_wps_can_display_address',
'display_phone_number' => 'wpe_wps_can_display_phone',
'enable_work_hours' => 'wpe_wps_is_work_hours_enabled',
'display_social_icons' => 'wpe_wps_can_display_social_icons',
'enable_categories' => 'wpe_wps_is_categories_enabled',
'enable_messaging' => 'wpe_wps_is_messaging_enabled',
'enable_engagement' => 'wpe_wps_is_engagement_enabled',
'enable_contact_form' => 'wpe_wps_is_contact_form_enabled',
'display_whatsapp_number' => 'wpe_wps_can_display_whatsapp',
'enable_activity_feed' => 'wpe_wps_is_activity_feed_enabled',
'enable_action_buttons' => 'wpe_wps_is_action_buttons_enabled',
'enable_linked_groups' => 'wpe_wps_is_groups_enabled',
'enable_services' => 'wpe_wps_is_services_enabled',
'enable_header_video' => 'wpe_wps_is_video_enabled',
'enable_reviews' => 'wpe_wps_is_reviews_enabled',
'enable_jobs' => 'wpe_wps_is_jobs_enabled',
'enable_events' => 'wpe_wps_is_events_enabled',
'display_custom_fields' => 'wpe_wps_can_display_custom_fields'
);
// Add SpacesEngine filters to PMPro level settings page:
add_action( 'pmpro_membership_level_after_other_settings', 'enable_disable_se_features', 1, 2 );
function enable_disable_se_features($level) {
/* Output the structure of the $level object for inspection
echo '<pre>';
var_dump($level);
echo '</pre>';
*/
global $spacesengine_levels, $options_and_filters; // Declare the global variables
// Retrieve the ID from the level object
$id = $level->id;
// Check if the current level ID is in the organization levels array
if (in_array($id, $spacesengine_levels)) {
?>
<h3><?php esc_html_e('SpacesEngine Features', 'pmpro'); ?></h3>
<table class="form-table">
<tbody>
<?php foreach ($options_and_filters as $option_name => $filter_hook) : ?>
<tr>
<th scope="row"><?php echo esc_html($option_name); ?></th>
<td>
<?php
// Retrieve the option value for the current membership level
$option_value = get_option('wpe_wps_' . $option_name . '_level_' . $id);
// Check if the current filter is 'space_creation_limit'
if ($option_name === 'space_creation_limit') {
?>
<input type="number" name="<?php echo esc_attr($option_name); ?>" value="<?php echo esc_attr(absint($option_value)); ?>" />
<?php
} else {
?>
<input type="checkbox" name="<?php echo esc_attr($option_name); ?>" value="1" <?php checked($option_value, 1); ?> />
<?php
}
?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php
}
}
// Define the update_wpe_wps_filters() function
function update_wpe_wps_filters($filter_hook, $value, $level_id) {
// Access $option_name defined as a global variable
global $option_name, $options_and_filters;
// Extracting option name from filter hook
$option_name = array_search($filter_hook, $options_and_filters);
// Update options specific to the membership level
update_option('wpe_wps_' . $option_name . '_level_' . $level_id, $value);
do_action($filter_hook, $value, $level_id);
}
// Hook the update function to the pmpro_save_membership_level action
add_action('pmpro_save_membership_level', 'update_wpe_wps_filters_for_membership_level', 10, 1);
// Define the function to update all options and filters
function update_wpe_wps_filters_for_membership_level($level_id) {
// Access $option_name defined as a global variable
global $option_name, $options_and_filters;
// Initialize an array to store disabled filters
$disabled_filters = array();
// Iterate over each option and filter:
foreach ($options_and_filters as $option_name => $filter_hook) {
// Check if the checkbox is checked
$value = isset($_REQUEST[$option_name]) ? true : false;
// If the value is false, it means the filter is disabled
if (!$value) {
// Add the filter hook to the disabled filters array
$disabled_filters[] = $filter_hook;
}
// Call the update function
update_wpe_wps_filters($filter_hook, $value, $level_id);
}
// Save the disabled filters for the membership level
update_option('wpe_wps_disabled_filters_level_' . $level_id, $disabled_filters);
}
// Function to disable filters based on the space author's membership level
function se_filters_for_individual_spaces() {
// Check if the post type is `wpe_wpspace`
if (get_post_type() !== 'wpe_wpspace') {
return;
}
// Get the membership level of the author
$post = get_post( $post_id );
$author_id = $post->post_author;
$author_membership_level = pmpro_getMembershipLevelForUser($author_id);
if (!$author_membership_level) {
return;
}
$membership_level_id = $author_membership_level->ID;
// Iterate over the disabled filters saved in the membership level settings
$disabled_filters = get_option('wpe_wps_disabled_filters_level_' . $membership_level_id, array());
// Check if the $disabled_filters variable is empty or not an array
if (empty($disabled_filters) || !is_array($disabled_filters)) {
// If $disabled_filters is empty or not an array, return early
return;
}
// Use the add_filter() function to dynamically apply the disabled filters
foreach ($disabled_filters as $filter_hook) {
// Disable the filter by replacing it with a callback function that does nothing
add_filter($filter_hook, 'disable_filter_callback', 10, 9999);
}
}
// Callback function to disable the filter
function disable_filter_callback() {
// return whatever value it receives as its first argument.
return func_get_arg(0);
}
// Hook the function to an appropriate action, such as 'wp' to ensure it runs early
add_action('wp', 'se_filters_for_individual_spaces');
// Disable filters for default space configuration (authored by admin)
function filters_for_orphaned_spaces( $result, $space ) {
// Define an array of filter hooks to disable
$filters_to_disable = array(
'wpe_wps_is_verification_enabled',
'wpe_wps_get_admins_enabled',
'wpe_wps_get_editors_enabled',
'wpe_wps_can_display_address',
'wpe_wps_can_display_phone',
'wpe_wps_is_work_hours_enabled',
'wpe_wps_is_messaging_enabled',
'wpe_wps_is_engagement_enabled',
'wpe_wps_is_contact_form_enabled',
'wpe_wps_can_display_whatsapp',
'wpe_wps_is_activity_feed_enabled',
'wpe_wps_is_action_buttons_enabled',
'wpe_wps_is_groups_enabled',
'wpe_wps_is_services_enabled',
'wpe_wps_is_video_enabled',
'wpe_wps_is_jobs_enabled',
'wpe_wps_is_events_enabled',
'wpe_wps_can_display_custom_fields'
);
// Iterate over the array and dynamically add filters
foreach ($filters_to_disable as $filter_hook) {
add_filter($filter_hook, 'filters_for_orphaned_spaces', 10, 2);
}
// Check if the current filter hook is in the array of filters to disable
if ( in_array( current_filter(), $filters_to_disable ) ) {
// If it is, return false to disable the filter
return false;
}
// If the filter is not in the array of filters to disable, return the original result
return $result;
}
/**
* Add a setting to the edit level settings to show or hide a membership level on the level select page.
* See https://www.paidmembershipspro.com/memberships-levels-page-order-hide-display-skip-mega-post/#h-option-2-add-a-setting-to-hide-levels-from-display-on-the-memberships-edit-level-admin
*/
//Save the pmpro_show_level_ID field
function pmpro_hide_level_from_levels_page_save( $level_id ) {
if ( $level_id <= 0 ) {
return;
}
$limit = $_REQUEST['pmpro_show_level'];
update_option( 'pmpro_show_level_'.$level_id, $limit );
}
add_action( 'pmpro_save_membership_level','pmpro_hide_level_from_levels_page_save' );
//Display the setting for the pmpro_show_level_ID field on the Edit Membership Level page
function pmpro_hide_level_from_levels_page_settings() {
?>
<h3 class='topborder'><?php esc_html_e( 'Membership Level Visibility', 'pmpro' ); ?></h3>
<table class='form-table'>
<tbody>
<tr>
<th scope='row' valign='top'><label for='pmpro_show_level'><?php esc_html_e( 'Show Level', 'pmpro' );?>:</label></th>
<td>
<?php
if ( isset( $_REQUEST['edit'] ) ) {
$edit = $_REQUEST['edit'];
$pmpro_show_level = get_option( 'pmpro_show_level_' . $edit );
if ( $pmpro_show_level === false ) {
$pmpro_show_level = 1;
}
} else {
$limit = '';
}
?>
<select id='pmpro_show_level' name='pmpro_show_level'>
<option value='1' <?php if ( $pmpro_show_level == 1 ) { ?>selected='selected'<?php } ?>><?php esc_html_e( 'Yes, show this level in the [pmpro_levels] display.', 'pmpro' );?></option>
<option value='0' <?php if ( ! $pmpro_show_level ) { ?>selected='selected'<?php } ?>><?php _e( 'No, hide this level in the [pmpro_levels] display.', 'pmpro' );?></option>
</select>
</td>
</tr>
</tbody>
</table>
<?php
}
add_action( 'pmpro_membership_level_after_other_settings', 'pmpro_hide_level_from_levels_page_settings' );
?>
@indigetal
Copy link
Author

indigetal commented Apr 8, 2024

Alternative version of custom redirect function that bypasses validation:

// Restrict access to certain pages based on level group:
function my_pmpro_custom_redirects() {
    
    // Check if the function has already been executed to avoid multiple executions
    if ( defined( 'MY_PMPRO_CUSTOM_REDIRECTS_EXECUTED' ) ) {
        return;
    }
    // Set the flag to indicate that the function has been executed
    define( 'MY_PMPRO_CUSTOM_REDIRECTS_EXECUTED', true );
    
    global $individual_levels, $spacesengine_levels, $pmpro_pages; // Make the variables accessible inside the function
    
    // Retrieve the membership levels for the current user
    $user_membership_levels = pmpro_getMembershipLevelsForUser(); // Used instead of pmpro_hasMembershipLevel() that may return false because of addons or custom code validation requests
    // Extract the IDs of the membership levels the user belongs to
    $user_level_ids = empty( $user_membership_levels ) ? array() : wp_list_pluck( $user_membership_levels, 'id' );
    // Check if the user has any individual membership level from the specified list
    $user_has_individual_level = ! empty( array_intersect( $user_level_ids, $individual_levels ) );
    // Check if the user has the SpacesEngine membership level from the specified list
    $user_has_spacesengine_level = ! empty( array_intersect( $user_level_ids, $spacesengine_levels ) );

    // EDIT HERE: The pages to be restricted
    $page_names = array('members', 'new-post', 'create-space');

    // First check if PMPro is active
    if ( ! function_exists( 'pmpro_hasMembershipLevel' ) ) {
        return;
    }

    // Loop through each page name
    foreach ( $page_names as $page_name ) {
        
        // Does the page name match?
        if ( strpos( $_SERVER['REQUEST_URI'], '/' . $page_name . '/' ) !== false ) {
            
            $current_page_name = $page_name; // Set the current page name
            
            // EDIT HERE: If a visitor trying to access the members area doesn't have any level ID...
            if ( $current_page_name == 'members' && empty($user_membership_levels) && ( ! is_page( $pmpro_pages['levels'] ) || ! is_page( $pmpro_pages['checkout'] ) ) ) {
                // Redirect them to the login page:
                wp_safe_redirect( '/wp-login.php' );
                exit;
            // EDIT HERE: If a visitor trying to create a new blog post doesn't have the relevant membership...
            } elseif ( $current_page_name == 'new-post' && !$user_has_individual_level && ( ! is_page( $pmpro_pages['levels'] ) || ! is_page( $pmpro_pages['checkout'] ) ) ) {
                // EDIT HERE: Redirect them to the individual plans page:
                wp_safe_redirect('/membership/individual-plans/');
                exit;
            // EDIT HERE: If a visitor trying to create a new Space doesn't have a SpacesEngine membership...
            } elseif ( $current_page_name == 'create-space' && !$user_has_spacesengine_level && ( ! is_page( $pmpro_pages['levels'] ) || ! is_page( $pmpro_pages['checkout'] ) ) ) {
                // EDIT HERE: Redirect them to the SpacesEngine plans page:
                wp_safe_redirect( '/membership/spacesengine-plans/' );
                exit;
            }
        }
    }
}
add_action( 'template_redirect', 'my_pmpro_custom_redirects' );

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment