0

Background:
I tried both https://wordpress.org/plugins/bainternet-posts-creation-limits and https://wordpress.org/plugins/limit-posts/, but they didn't work for some reason with my custom posts. So I decided to write my own php script, which I put into my child theme's function.php, to limit the number of certain custom posts types that certain users can create.

Structure:
I'm using wordpress 5.4.2 installed on bluehost. I am using geodirectory to create the custom post types (practitioners and organizations) and memberpress to manage permissions.
I decided to filter wp_post_data because all I want to do is change a post's status when first created, depending on how many posts the author has already created. Some suggested wp_insert_post_empty_content, but I think a better use-case is to allow them to create the actual post as a draft first, then ask them to get the proper membership to publish it.

Constraints:
Because another plugin on my site gives privileges to only a particular role, I am giving everyone who is not an admin that particular user role and therefore cannot limit posting privileges by user role. That is why I am using memberpress membership to govern publishing capability.

Issue/What is wrong:
After running the code:
1- All users, instead of just the non-admins with insufficient memberships, cannot save new CPT's. Even the admin cannot save a CPT.
2- Users get this message when they click submit: "You do not have the privileges to perform this action." I wrote a different message to give to a user who has the wrong membership for this action.

Approach:

<?php
//Before a newly created post is saved to the database, filter by membership
add_filter('wp_insert_post_data', 'tml_filter_by_membership', 99, 2);
/**
 99 refers to the priority when this filter is executed; 99 indicates that it is one of the last filters executed on this action
 2 indicates that we want both arguments given by the filter (because the second one contains sanitized but unmodified data--which is the data we want). Without 2, the function defaults to 1, giving us only slashed post data
 *
 */

//filter by membership only acts on non-admin posts of certain post types
function tml_filter_by_membership($data, $postarr)
{

    //take only non-admin posts
    if (!user_can($postarr['post_author'], 'manage_options'))
    {

        // continue only if the post type is gd_practitioner or gd_studio
        if ($postarr['post_type'] == 'gd_practitioner' || $postarr['post_type'] == 'gd_organization')
        {

            //Set membership post limits by post_type
            $membership_post_limits = tml_set_membership_post_limits_by_post_type();

            //Get author's membership
            $author_memberships = tml_get_post_author_memberships($postarr);

            //Look up the author's post limit for that post type within the membership post limits array
            $author_post_limit = tml_get_author_post_limit($author_memberships, $membership_post_limits, $postarr);

            //Count author's published posts of the particular post type
            $number_of_published_posts_by_author = tml_get_author_published_posts($postarr);

            //Compare number of published posts to author's post limit and return $postarr
            tml_compare_posts_to_limit($author_post_limit, $number_of_published_posts_by_author, $postarr);
        }
    }
}

//set membership post limits by post_type
function tml_set_membership_post_limits_by_post_type()
{
    $membership_post_limits = Array(
        462 => Array(
            'gd_practitioner' => 1,
            'gd_organization' => 1
        ) ,
        463 => Array(
            'gd_practitioner' => 1,
            'gd_organization' => 1
        ) ,
        464 => Array(
            'gd_practitioner' => 1,
            'gd_organization' => 1
        ) ,
        465 => Array(
            'gd_practitioner' => 10,
            'gd_organization' => 10
        ) ,
        466 => Array(
            'gd_practitioner' => 1,
            'gd_organization' => 1
        ) ,
        467 => Array(
            'gd_practitioner' => 10,
            'gd_organization' => 10
        ) ,
        752 => Array(
            'gd_practitioner' => 1,
            'gd_organization' => 1
        ) ,
        753 => Array(
            'gd_practitioner' => 1,
            'gd_organization' => 1
        ) ,
        754 => Array(
            'gd_practitioner' => 1,
            'gd_organization' => 1
        )
    );
    return $membership_post_limits;
}

//Get author's membership
function tml_get_post_author_memberships($postarr)
{
    if (class_exists('MeprUser'))
    {
        $user_id = $postarr['post_author'];
        $user = new MeprUser($user_id);
        $get_memberships = $user->active_product_subscriptions();
        $author_memberships = array_values(array_unique($get_memberships));
        return $author_memberships;
    }
    else
    {
        return false;
    }
}

//Look up the author's post limit for that post type within the membership post limits array, using $postarr to give the post type, and $user_memberships to give the author's memberships
function tml_get_author_post_limit($author_memberships, $membership_post_limits, $postarr)
{
    $author_post_limit = 1;
    foreach ($author_memberships as $membership)
    {
        if ($author_post_limit < $membership_post_limits[$membership][$postarr['post_type']])
        {
            $author_post_limit = $membership_post_limits[$membership][$postarr['post_type']];
        }
    }
    return $author_post_limit;
}

//Count author's published posts of the particular post type
function tml_get_author_published_posts($postarr)
{
    $posts = get_posts(array(
        'author' => $postarr['post_author'],
        'post_type ' => $postarr['post_type'],
        'numberposts ' => - 1,
        'post_status ' => 'publish'
    ));
    $number_of_published_posts_by_author = count($posts);
    return $number_of_published_posts_by_author;
}

/**
 //Another option, using count_user_posts function instead
 function tml_get_author_published_posts($postarr){
 $author= $postarr['post_author'];
 $post_type = $postarr['post_type'];
 $number_of_published_posts_by_author=count_user_posts($author, $post_type);
 return $number_of_published_posts_by_author;
 }
 *
 */

//Compare number of published posts to author's post limit
function tml_compare_posts_to_limit($author_post_limit, $number_of_published_posts_by_author, $postarr)
{
    $message = "";
    if ($number_of_published_posts_by_author >= $author_post_limit)
    {
        $postarr['post_status'] = 'draft';
        $message = "You have exceeded the number of listings for your membership";
    }
    echo "<div> ${message} </div>";
    return $postarr;
}

1
  • @Himad thanks for the edits. Jun 23 '20 at 22:22
0

Welcome to StackExchange! I made some changes to your code that I pointed out with comments. I'm not sure if this will actually work so you have to try it, and probably your way of echoing the message won't work because after updating there's a redirect so you'll have to work with the admin_notices action and find some way to persist the message after redirection, however I think that's a topic for another question so I replaced the echoing part with an error_log which will be enough to know if this is working as intended by looking at the logs.

<?php
add_filter('wp_insert_post_data', 'tml_filter_by_membership', 99, 2);

/* 
When using a filter, the first argument is always the data to be modified and returned. 
Usually you'll do your checks to see if you want to modify something, otherwise you'll return
the first argument ($data) so other filters can work as well. If you return nothing,
then you will probably break something. Here you should use $data instead of $postarr,
which are the same array but the later ($postarr) is unescaped.
*/
function tml_filter_by_membership($data, $postarr)
{

    /* If you want to take only non-administrator users, don't check for capacity, check
 instead for that specific role. */
    if (!in_array('administrator', get_user_by('ID', $data['post_author'])->roles))
    {

        if ($data['post_type'] == 'gd_practitioner' || $data['post_type'] == 'gd_organization')
        {

            $membership_post_limits = tml_set_membership_post_limits_by_post_type();

            $author_memberships = tml_get_post_author_memberships($data['post_author']);

            $author_post_limit = tml_get_author_post_limit($author_memberships, $membership_post_limits, $data);

            $number_of_published_posts_by_author = tml_get_author_published_posts($data);
            /* I thought the last function was unnecessary so I just deleted it and moved
 it's code in here. */
            if ($number_of_published_posts_by_author >= $author_post_limit)
            {
                $data['post_status'] = 'draft';
                /* Check yours logs to see if this is actually working. Displaying the
 message is something you can solve afterwards. */
                error_log('You have exceeded the number of listings for your membership');
            }
            /* Return your modified object. Before this, you were using a function which
 returned something, but the return has to be inside the scope of the function linked to
 the filter in order to actually return it. Previously you were returning nothing. */
            return $data;
            
        }
    }

    return $data;
}

function tml_set_membership_post_limits_by_post_type()
{
    $membership_post_limits = Array(
        462 => Array(
            'gd_practitioner' => 1,
            'gd_organization' => 1
        ) ,
        463 => Array(
            'gd_practitioner' => 1,
            'gd_organization' => 1
        ) ,
        464 => Array(
            'gd_practitioner' => 1,
            'gd_organization' => 1
        ) ,
        465 => Array(
            'gd_practitioner' => 10,
            'gd_organization' => 10
        ) ,
        466 => Array(
            'gd_practitioner' => 1,
            'gd_organization' => 1
        ) ,
        467 => Array(
            'gd_practitioner' => 10,
            'gd_organization' => 10
        ) ,
        752 => Array(
            'gd_practitioner' => 1,
            'gd_organization' => 1
        ) ,
        753 => Array(
            'gd_practitioner' => 1,
            'gd_organization' => 1
        ) ,
        754 => Array(
            'gd_practitioner' => 1,
            'gd_organization' => 1
        )
    );
    return $membership_post_limits;
}

function tml_get_post_author_memberships($user_id)
{
    if (class_exists('MeprUser'))
    {

        $user = new MeprUser($user_id);
        $get_memberships = $user->active_product_subscriptions();
        $author_memberships = array_values(array_unique($get_memberships));
        return $author_memberships;
    }
    else
    {
        return false;
    }
}


function tml_get_author_post_limit($author_memberships, $membership_post_limits, $data)
{
    $author_post_limit = 1;
    foreach ($author_memberships as $membership)
    {
        if ($author_post_limit < $membership_post_limits[$membership][$data['post_type']])
        {
            $author_post_limit = $membership_post_limits[$membership][$data['post_type']];
        }
    }
    return $author_post_limit;
}

function tml_get_author_published_posts($data)
{

    return count(get_posts(array(
        'author' => $data['post_author'],
        'post_type ' => $data['post_type'],
        'numberposts ' => - 1,
        'post_status ' => 'publish'
    )));

}


1
  • Thanks for the suggestion. I've implemented it and although it works better (meaning some odd behavior on my post submission page was removed by your suggestions) moving posts into draft status still wasn't achieved. my logs have 2 entries related to memberpress: PHP Notice: Trying to get property 'post_type' of non-object in plugins/memberpress/app/controllers/MeprCouponsCtrl.php on line 268 PHP Notice: Trying to get property 'post_type' of non-object in plugins/memberpress/app/controllers/MeprRulesCtrl.php on line 86 So I'm investigating tml_get_post_author_memberships. Jul 3 '20 at 5:38

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.