Change role after the user has read all the posts in a category

I’m creating a platform with “X” categories and “X” roles with the same name to each category.

I would like to know how to change the user’s role after they have read all the posts in a category. When the role changes, the user will have access to the next category.

Read More

To make the categories private and to create the related groups of users I’m using the Role Scoper plugin.

This solution is very similar to what I mean: https://stackoverflow.com/questions/9181440/wordpress-change-user-role-conditionally

Thanks

Related posts

Leave a Reply

1 comment

  1. Just to clarify:
    You have, for example, the categories ‘foo’, ‘bar’ and ‘baz’. New users with the role ‘foo’ can only read posts in the category ‘foo’. After they have read all the posts in ‘foo’, their role will be changed to ‘bar’ so they can read the posts in category ‘bar’. And so on…

    Everything starts with a good plan, so let’s make a plan.

    • The first thing is to keep the post IDs the user has read. Every time the user opens a post (i.e. opens the single view of a post), we save the post ID of this post.
    • If we save a post ID, we have to check if the user has read all needed posts to get updated to the next level. This means the user has read all the posts from category X.
    • If the user has read all the necessary posts, update the user to the next level.

    Sounds easy, doesn’t it? I put it all together in one class because this saves some code and makes some things easier. Call this class with $updated = new WPSE78727(); inside your single post template (single.php), e.g. at the beginning of the template.

    If the user was updated, $updated->updated is set to the new role of the user, otherwise it is set to false.

    <?php
    class WPSE78727
    {
        // set this to a proper name for a metakey    
        const METAKEY = 'YourUserMetaKey';
    
        public $user_ID      = 0;
        public $user_data    = null;
        public $current_role = '';
        public $updated      = false;
    
        // edit this to your needs
        // this is the sequence of category names the user
        // has to read to get updated to the next level
        public $role_sequence = array(
            'foo',
            'bar',
            'baz'
        );
    
        public function __construct() {
    
            if( is_single() || user_is_logged_in() ) {
    
                $this->get_user_data();
                $updated = $this->save_post_id_in_user_meta();
    
                $this->updated = ( is_string( $updated ) ) ?
                     $updated : false;
    
            }
    
        }
    
        public function get_user_data() {
    
            $user               = wp_get_current_user();
            $this->user_ID      = $user->ID;
            $this->user_data    = get_userdata( $this->user_ID );
            $this->current_role = $user_data->roles[0]; // get the first role
    
            return true;
        }
    
        public function save_post_id_in_user_meta() {
    
          global $post;
    
            // make the code more readable
            $role = &$this->current_role;
    
            // $read_post should be an array
            $posts_read = get_user_meta( $this->user_ID, self::METAKEY, true );
    
            // be sure $posts_read is an array
            if( ! is_array( $posts_read ) )
                $posts_read = array();
    
                // make an array for the post IDs which belongs to the current role
            if( ! isset( $posts_read[$role] ) || ! is_array( $posts_read[$role] ) )
                $read_posts[$role] = array();
    
            // if the current post ID is not in the array, push it into the array
            if( ! in_array( $post->ID, $posts_read[$role] ) )
                array_push( $posts_read[$role], $post->ID );
    
                // update the metadata
            update_user_meta( $this->user_ID, self::METAKEY, $posts_read );
    
            $new_role = $this->maybe_all_posts_read( $posts_read );
    
            return ( ! is_array( $new_role ) ) ?
                $new_role : $posts_read;
    
        }
    
        public function maybe_all_posts_read( $posts_read = array() ) {
    
            $current_cat_id = get_cat_ID( $this->current_role );
    
            // get all post IDs from this category
            $post_ids = get_posts(
              array(
                'numberposts'   => -1, // get all posts.
                'tax_query'     => array(
                    array(
                        'taxonomy'  => 'category',
                        'field'     => 'id',
                        'terms'     => $current_cat_id,
                    ),
                ),
                'fields'        => 'ids', // Only get post IDs
            ));
    
            // check if the user has read all posts. Simply check the differences between
            // $post_ids (all post IDs in the current category (e.g. foo) and the read posts
            // stored in user meta
            $left_posts = array_diff( $post_ids, $posts_read[$this->current_role] );
    
            // if no post is left, update the user
            if( empty( $left_posts ) )
                $new_role = $this->update_user();
    
            return ( empty( $left_posts ) ) ?
                $new_role : $left_posts;
    
        }
    
        public function update_user() {
    
            // get the next role out of the sequence
            $key = array_search( $this->current_role, $this->role_sequence );
    
    
            // don't run out of the array!
            $max = count( $this->role_sequence );
            if( $key > $max )
                $key = $max - 1;
    
            $new_role = $role_sequence[$key+1];
    
            // merge old and new data
            $new_user_data = array_merge(
                $this->user_data,
                array(
                    'ID'   => $id,
                    'role' => $new_role
                )
            );
    
            // using wp_insert_user instead of wp_update_user without any reasons
            wp_insert_user( $new_user_data );
    
            return $new_role;
    
        }
    
    }
    

    This code is untested and may be buggy. It should be considered a starting point and not as a complete solution.