Protecting categories-as-such, not just posts that have a category

I have a fiddly need for a particular site build, which I'll try to articulate clearly and briefly. I have a custom post type ('prompts'), with many prompts belonging to multiple categories. Categories are the organizing principle around which I've built the who-can-see-what protection rules of the Membership aspect.

In some cases, one prompt will have two categories, each of which gives members of two different memberships access to that prompt.

The problem comes when I try to hook up an outside app, via xmlrpc, and using a purpose-built function. I want the app to return only those prompts the logged-in member has access to, organized by category. But Membership Pro seems to lock out users at the content/prompt level – it makes no prohibition against the user 'seeing' the prohibited category itself.

I need my custom query to say, in essence:
1. "First build a list of all categories to which this person has access"
2. "NOW build a list of all posts, within each category, to which this person has access"

2 is trivially easy, 1 seems unsupported. Is there a way to filter my query to achieve this?

Current functions appended below, for reference:

add_filter( 'xmlrpc_methods', 'add_dm_xmlrpc_methods' );

function add_dm_xmlrpc_methods( $methods ) {
    $methods['dm.getPosts'] = 'dm_xmlrpc_get_posts';
    $methods['dm.getPrompts'] = 'dm_xmlrpc_get_prompts';
    return $methods;
}

function dm_xmlrpc_get_posts( $args ) {
    global $wp_xmlrpc_server;
    $wp_xmlrpc_server->escape( $args );

    $blog_ID     = (int) $args[0];
    $username  = $args[1];
    $password   = $args[2];
    $post_type  = $args[3];
    $category = $args[4];
    $numberposts = $args[5];
    $extra = $args[6];

    if ( !$user = $wp_xmlrpc_server->login($username, $password) ) {
        return $wp_xmlrpc_server->error;
    }

    $category_int = (int) $category;

    if( !empty( $category_int ) ) {
        $category_call = 'cat';
    } else {
        $category_call = 'category_name';
    }

    $post_args = array(
        'numberposts' => $numberposts,
        'posts_per_page' => $numberposts,
        $category_call => $category,
        'post_type' => $post_type,
        'post_status' => 'any'
    );
    if( is_array( $extra ) )
        $post_args = array_merge( $post_args, $extra );

    $posts_list = get_posts( $post_args );

    if ( !$posts_list ) {
        $wp_xmlrpc_server->error = new IXR_Error(500, __('Either there are no posts, or something went wrong.'));
        return $wp_xmlrpc_server->error;
    } else {
        return $posts_list;
    }
}

============================

function dm_xmlrpc_get_prompts( $args ) {
    global $wp_xmlrpc_server;
    $wp_xmlrpc_server->escape( $args );

    $blog_ID     = (int) $args[0];
    $username  = $args[1];
    $password   = $args[2];
    $storyteller   = $args[3];

    if ( !$user = $wp_xmlrpc_server->login($username, $password) ) {
        return $wp_xmlrpc_server->error;
    }

    $prompt_query_args = array(
        'posts_per_page' => -1,
        'post_type' => 'prompts',
        'post_status' => 'publish',
		'tax_query' => array(
		array(
			'taxonomy' => 'storytellers',
			'field' => 'slug',
			'terms' => $storyteller
			)
		)
    );

	$prompt_query = new WP_Query( $prompt_query_args );
    $prompt_list = array();

    while ( $prompt_query->have_posts() ) { 

        $prompt_query->the_post();
        $prompt_title = get_the_title();
        $prompt_ID = get_the_ID();
        //$prompt_tax = get_the_taxonomies();
        //$prompt_tax = get_the_terms($prompt_ID,'storytellers');

        $prompt_categories = get_the_category();

 	foreach ( $prompt_categories as $key=>$category ) {

            $prompt_category_name = $category->name;
			$prompt_category_id = $category->cat_ID;
			$prompt_category_array[] = array(
				'category_name' => $prompt_category_name,
				'category_id' => $prompt_category_id
			);
        }

        //$prompt_list[$prompt_category][] = $prompt_title; // Create an array with the category names and post titles
        $prompt_list[] = array(
			'categories' => $prompt_category_array,
			'prompt_title' => html_entity_decode($prompt_title),
			'prompt_id' => $prompt_ID
			);
	//clear the categories for the next prompt
	unset($prompt_category_array);
	$prompt_category_array = array();
    }

    if ( !$prompt_list ) {
        //$wp_xmlrpc_server->error = new IXR_Error(500, __('Either there are no posts, or something went wrong.'));
        //return $wp_xmlrpc_server->error;
        return dm_xmlrpc_get_all_prompts( $blog_ID,$username,$password );
    } else {
        //return $prompt_categories;
        return $prompt_list;
    }
}

function dm_xmlrpc_get_all_prompts( $blog_ID,$username,$password ) {

$default_categories = array(51,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,147,148,185);
// removed howto cat #167

    $prompt_query_args = array(
        'posts_per_page' => -1,
        'post_type' => 'prompts',
        'post_status' => 'publish',
        'category__in' => $default_categories
    );

	$prompt_query = new WP_Query( $prompt_query_args );
    $prompt_list = array();

    while ( $prompt_query->have_posts() ) { 

        $prompt_query->the_post();
        $prompt_title = get_the_title();
        $prompt_ID = get_the_ID();

        $prompt_categories = get_the_category();

 	foreach ( $prompt_categories as $key=>$category ) {

		if (in_array( $category->cat_ID, $default_categories )) {
			$prompt_category_name = $category->name;
			$prompt_category_id = $category->cat_ID;
			$prompt_category_array[] = array(
				'category_name' => $prompt_category_name,
				'category_id' => $prompt_category_id
			);
		}
	}

        $prompt_list[] = array(
			'categories' => $prompt_category_array,
			'prompt_title' => html_entity_decode($prompt_title),
			'prompt_id' => $prompt_ID
			);
	unset($prompt_category_array);
	$prompt_category_array = array();
    }

    if ( !$prompt_list ) {
        $wp_xmlrpc_server->error = new IXR_Error(500, __('Either there are no posts, or something went wrong.'));
        return $wp_xmlrpc_server->error;
    } else {
        //return $prompt_categories;
        return $prompt_list;
    }
}
  • benjamin_barnett

    Not necessarily needed info, but here is why I need that additional category-as-such level of filtering.

    Suppose two memberships: banana-lover and apple-lover. Banana-lovers can see all prompts in banana category, Apple-lovers can see all apple-categoy prompts.

    I have a prompt – 'Tutti frutti', let's say – with both categories: banana and apple.

    My outside (tablet) app hits the site, logged in as Bob Banana-lover, and starts building Bob's list of prompts by category. Checks the Banana category:
    - Yep, there's a prompt Bob has access to, that has this category
    - Cool, list this category and its prompt

    Now app checks the Apple category:
    - Yep, there's a prompt Bob has access to, that has this category (although Bob is forbidden access to this category)
    - Cool, list this category and its prompt – which is logical, but horribly wrong for my purposes.

    ...see?

  • Jude

    HI @benjamin_barnett

    Extremely sorry about the delay on this one. We've been swamped with tickets and this took a while to get to. What you want can easily be done by passing the terms and taxonomies to the has_access function and then run your query on only the list of allowed categories

    terms_array() = array( 'banana-lover' , 'apple-lover' , 'fruit-lover' ) ;
    tax_array() = array( 'category' ) ;
    
    $objRule = new MS_Rule_Category_Model() ; 
    
    $valid_categories = $objRule->protect_categories( $terms_array , $tax_array ) ; 
    
    // $valid_categories has ONLY the ones with access for this user

    Then in your own code use $valid_categories as shown below

    foreach ( $prompt_categories as $key=>$category ) {
    
    		if (in_array( $category->cat_ID, $valid_categories )) {
    			$prompt_category_name = $category->name;
    			$prompt_category_id = $category->cat_ID;
    			$prompt_category_array[] = array(
    				'category_name' => $prompt_category_name,
    				'category_id' => $prompt_category_id
    			);
    		}
           }

    Hope that made sense, I'll be happy to look in further if needed

    Cheers
    Jude

  • benjamin_barnett

    Jude, I could use some additional clarification on this item. We incorporated your code into our function (which, to repeat and re-emphasize, is being called via XML-RPC by an outside iPad/iPhone app).

    The issue we're now running into is that when content protection is switched on, the Membership module is blocking access via XML-RPC entirely -- our revised code isn't getting a chance to run. We'd assumed (wrongly, I guess) that passing user credentials to the XMLRPC object would provide recognition/validation to the Membership object as well, but that's clearly not the case.

    I understand that the info is probably there, somewhere, in your API documents, but I wonder if you might provide a snippet of example code that passes XMLRPC-provided user credentials through to the Membership API so as to permit access to the needed category and prompt XMLRPC endpoints.

    Our current code, which works but in a really gimped way (it pulls out ONLY membership packages of a user, and works even when content protection is OFF) is pasted below my sign off for your perusal. It's strictly a hack-y, temporary work-around -- we build allowed category lists by hand based on membership level(s). We're hoping you can point us to the better, fully-API-integrated solution.

    Alternatively (maybe first/quickly?), as a strictly inferior and again, temporary, fall-back option ... is there a way to turn content protection OFF but leave the Membership signup form active? That would let us use our gimped hack until we can work through full XML-RPC / Membership API user validation.

    :Benjamin Barnett

    ===================================================
    Our current code:

    add_filter( 'xmlrpc_methods', 'add_dm_xmlrpc_methods' );
    
    function add_dm_xmlrpc_methods( $methods ) {
        ini_set('memory_limit', '128M');
        $methods['dm.getPosts'] = 'dm_xmlrpc_get_posts';
        $methods['dm.getPrompts'] = 'dm_xmlrpc_get_prompts';
        $methods['dm.getCategories'] = 'dm_xmlrpc_get_categories';
        return $methods;
    }
    
    function dm_xmlrpc_get_posts( $args ) {
        global $wp_xmlrpc_server;
        $wp_xmlrpc_server->escape( $args );
    
        $blog_ID     = (int) $args[0];
        $username  = $args[1];
        $password   = $args[2];
        $post_type  = $args[3];
        $category = $args[4];
        $numberposts = $args[5];
        $extra = $args[6];
    
        if ( !$user = $wp_xmlrpc_server->login($username, $password) ) {
            return $wp_xmlrpc_server->error;
        }
    
        $category_int = (int) $category;
    
        if( !empty( $category_int ) ) {
            $category_call = 'cat';
        } else {
            $category_call = 'category_name';
        }
    
        $post_args = array(
            'numberposts' => $numberposts,
            'posts_per_page' => $numberposts,
            $category_call => $category,
            'post_type' => $post_type,
            'post_status' => 'any'
        );
        if( is_array( $extra ) )
            $post_args = array_merge( $post_args, $extra );
    
        $posts_list = get_posts( $post_args );
    
        if ( !$posts_list ) {
            $wp_xmlrpc_server->error = new IXR_Error(500, __('Either there are no posts, or something went wrong.'));
            return $wp_xmlrpc_server->error;
        } else {
            return $posts_list;
        }
    }
    
    function dm_xmlrpc_get_prompts( $args ) {
        global $wp_xmlrpc_server;
        $wp_xmlrpc_server->escape( $args );
    
        $blog_ID     = (int) $args[0];
        $username  = $args[1];
        $password   = $args[2];
        $storyteller   = $args[3];
    
        if ( !$user = $wp_xmlrpc_server->login($username, $password) ) {
            return $wp_xmlrpc_server->error;
        }
    
        $prompt_query_args = array(
            'posts_per_page' => -1,
            'post_type' => 'prompts',
            'post_status' => 'publish',
    		'tax_query' => array(
    		array(
    			'taxonomy' => 'storytellers',
    			'field' => 'slug',
    			'terms' => $storyteller
    			)
    		)
        );
    
    	$prompt_query = new WP_Query( $prompt_query_args );
        $prompt_list = array();
    
        while ( $prompt_query->have_posts() ) { 
    
            $prompt_query->the_post();
            $prompt_title = get_the_title();
            $prompt_ID = get_the_ID();
            //$prompt_tax = get_the_taxonomies();
            //$prompt_tax = get_the_terms($prompt_ID,'storytellers');
    
            $prompt_categories = get_the_category();
    
     	foreach ( $prompt_categories as $key=>$category ) {
    
                $prompt_category_name = $category->name;
    			$prompt_category_id = $category->cat_ID;
    			$prompt_category_array[] = array(
    				'category_name' => $prompt_category_name,
    				'category_id' => $prompt_category_id
    			);
            }
    
            //$prompt_list[$prompt_category][] = $prompt_title; // Create an array with the category names and post titles
            $prompt_list[] = array(
    			'categories' => $prompt_category_array,
    			'prompt_title' => html_entity_decode($prompt_title),
    			'prompt_id' => $prompt_ID
    			);
    	//clear the categories for the next prompt
    	unset($prompt_category_array);
    	$prompt_category_array = array();
        }
    
        if ( !$prompt_list ) {
            //$wp_xmlrpc_server->error = new IXR_Error(500, __('Either there are no prompts, or something went wrong.'));
            //return $wp_xmlrpc_server->error;
            return dm_xmlrpc_get_all_prompts( $blog_ID,$username,$password );
        } else {
            //return $prompt_categories;
            return $prompt_list;
        }
    }
    
    function dm_xmlrpc_get_all_prompts( $blog_ID,$username,$password ) {
    
    $default_categories = array(51,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,147,148,185);
    // removed howto cat #167
    
        $prompt_query_args = array(
            'posts_per_page' => -1,
            'post_type' => 'prompts',
            'post_status' => 'publish',
            'category__in' => $default_categories
        );
    
    	$prompt_query = new WP_Query( $prompt_query_args );
        $prompt_list = array();
    
        while ( $prompt_query->have_posts() ) { 
    
            $prompt_query->the_post();
            $prompt_title = get_the_title();
            $prompt_ID = get_the_ID();
    
            $prompt_categories = get_the_category();
    
     	foreach ( $prompt_categories as $key=>$category ) {
    
    		if (in_array( $category->cat_ID, $default_categories )) {
    			$prompt_category_name = $category->name;
    			$prompt_category_id = $category->cat_ID;
    			$prompt_category_array[] = array(
    				'category_name' => $prompt_category_name,
    				'category_id' => $prompt_category_id
    			);
    		}
    	}
    
            $prompt_list[] = array(
    			'categories' => $prompt_category_array,
    			'prompt_title' => html_entity_decode($prompt_title),
    			'prompt_id' => $prompt_ID
    			);
    	unset($prompt_category_array);
    	$prompt_category_array = array();
        }
    
        if ( !$prompt_list ) {
            $wp_xmlrpc_server->error = new IXR_Error(500, __('Cannot retrieve the standard prompts, or something went wrong.'));
            return $wp_xmlrpc_server->error;
        } else {
            //return $prompt_categories;
            return $prompt_list;
        }
    }
    
    function dm_xmlrpc_get_categories( $args ) {
        global $wp_xmlrpc_server;
        $wp_xmlrpc_server->escape( $args );
    
        $blog_ID = (int) $args[0];
        $username = $args[1];
        $password = $args[2];
    
        if ( !$user = $wp_xmlrpc_server->login($username, $password) ) {
        	$wp_xmlrpc_server->error = new IXR_Error(500, __('Authorization failed'));
            return $wp_xmlrpc_server->error;
        }
    
        //$my_categories = array(51,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,147,148);
        $my_categories = array(167);
    
        // membership test
        if ( ms_has_membership(1102,1103,1324,1325) ) {
        	//   storyteller (monthly, 3 month, annual) -or- acctmanager additional categories
    		$my_categories[] = 148; // +Moments
    		$my_categories[] = 147; // +My Questions
    		$my_categories[] = 144; // Show Me
    		$my_categories[] = 128; // Today...
    		$my_categories[] = 135; // ...Achievements
    		$my_categories[] = 138; // ...Art
    		$my_categories[] = 139; // ...Books
    		$my_categories[] = 133; // ...Career
    		$my_categories[] = 130; // ...Family - children
    		$my_categories[] = 131; // ...Family - grandchildren
    		$my_categories[] = 142; // ...Heirlooms
    		$my_categories[] = 136; // ...Leisure
    		$my_categories[] = 134; // ...Love
    		$my_categories[] = 129; // ...Me
    		$my_categories[] = 132; // ...Military
    		$my_categories[] = 141; // ...Music
    		$my_categories[] = 137; // ...Recipes
    		$my_categories[] = 140; // ...Stories
    		$my_categories[] = 143; // Tomorrow...
    		$my_categories[] = 51; // Yesterday...
    		$my_categories[] = 125; // ...Immigration
    		$my_categories[] = 126; // ...My Grandparents
    		$my_categories[] = 127; // ...My Parents
    		$my_categories[] = 123; // ...The Old Country – Dad’s side
    		$my_categories[] = 124; // ...The Old Country – Mom’s side
    
        } elseif ( ms_has_membership(1400) ) {
        	//   The Big Day Video Messages Package category
    		$my_categories[] = 200; // +Singer Family History
    
        } elseif ( ms_has_membership(1330) ) {
        	//   Alzheimer's monthly unlimited categories
    		$my_categories[] = 185; // Alzheimer's 1
    		$my_categories[] = 186; // Alzheimer's 2
    
        } elseif ( ms_has_membership(1820) ) {
        	//   DeJohn Memorial Stories additional category
    		$my_categories[] = 147; // +My Questions
    
        } elseif ( ms_has_membership(1141) ) {
        	//   20 Questions – Family History additional categories
    		// $my_categories[] =; // No 20 Questions categories defined atm
    
        } else {
        	$wp_xmlrpc_server->error = new IXR_Error(500, __('Cannot retrieve membership type'));
            return $wp_xmlrpc_server->error;
    
        }
    
    	//$categories = get_terms( 'category', 'orderby=ASC' );
    
    	//$default_categories = array(51,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,147,148,185);
    
    	$category[] = array ( 'category' );
    
    	$objRule = new MS_Rule_Category_Model();
    	$valid_categories = $objRule->protect_categories( $my_categories , $category );
    
    	$category_args = array(
    	        'include' => $valid_categories,
    	        'order' => 'ASC'
    	    );
    
    	$categories = get_terms( 'category', $category_args );
    
    	return $categories;
    }
  • Jude

    Hey @benjamin_barnett

    Here is the quick hack you asked for to turn off protection. Simply go to Membership > Protection Rules and remove ALL protection rules temporarily and you can still use it as a sign up system

    I will inspect the code you posted but its likely in this area thats not working.

    if ( !$user = $wp_xmlrpc_server->login($username, $password) ) {
            return $wp_xmlrpc_server->error;
        }

    The $user object is not used ever to test if a given member has a membership with access to the said categories

    Cheers
    Jude

  • Jude

    Hey @benjamin_barnett

    Just chipping in with some feedback from the developer on this .. I am attaching below some debug code that lets you know if M2 is fully activated. Please attach it before the WP_Query call to understand it if the API has loaded and functioning

    if ( ! function_exists('lib3') ) { echo 'M2 not loaded'; die(); }
    if ( ! class_exists( 'MS_Model_Plugin' ) ) { echo 'M2 not loaded'; die(); }
    if ( ! did_action( 'init' ) ) { echo 'M2 not initialized'; die(); }
    if ( ! did_action( 'template_redirect' ) ) { echo 'Protection not initialized'; die(); }
    if ( ! function_exists( 'ms_api' ) ) { echo 'M2 not fully loaded'; die(); }
    if ( ! ($api = ms_api()) ) { echo 'M2 not fully loaded'; die(); }
    echo 'M2 is loaded correctly'; die();

    Let me know what output you see in the iOS app when this code is running in the function

    Cheers
    Jude

Thank NAME, for their help.

Let NAME know exactly why they deserved these points.

Gift a custom amount of points.