Content restriction plugin 03

The template restrictor class logic.

How it works

As described in an earlier post the content restriction will happen on the query level and on the single post level; in template terms it roughly means archives and singles will be restricted.
When it comes to restricting queries the restriction happens adding a taxonomy query for each restriction taxonomy to the query allowing, for each taxonomy, just the terms (as in “taxonomy terms”) the user is able to access.

Given a restriction based on the subscription taxonomy applied to the post and page post type, given a user that can access the posts assigned the free and basic subscription taxonomy terms when the post archive has to be shown to the user then the following taxonomy query will be added to the query

array(
    'taxonomy' => 'subscription',
    'field'    => 'slug',
    'terms'    => array('free', 'basic'),
    'operator' => 'IN'
)

That’s basically all there is to it and the plugin will just offer options ant take into account the varying possibilities about which taxonomies will be used to restrict content and how the list of allowed taxonomy terms slugs for each restricting taxonomy has to be fetched for a user.

Every post should have at least a term

From the small piece of code above it’s easy to understand that for the restriction to properly work and apply each post should have at least one taxonomy term applied for each restricting taxonomy: posts with no taxonomy terms applied will just not match the query above and would be left out of the query.

Restricting singles

Restricting user access to single templates, like page.php or single.php, would yield unexpected results where the template has been loaded but the have_posts function might return no posts.
While some themes might gracefully fail the assumption is usually made that the post object of a single template is actually there hence the redirection will happen before, at template_include filter time, with a series of some rather simple checks in the trc_TemplateRestrictor class:


class trc_QueryRestrictor {

        /**
         * @var trc_PostTypes
         */
        protected $post_types;

        /**
         * @var trc_Taxonomies
         */
        protected $taxonomies;

        /**
         * @var trc_FilteringTaxonomy
         */
        protected $filtering_taxonomy;

        /**
         * @var trc_Queries
         */
        protected $queries;

        /**
         * @var trc_User
         */
        protected $user;

        public static function instance() {
            $instance = new self;

            $instance->post_types         = trc_PostTypes::instance();
            $instance->taxonomies         = trc_Taxonomies::instance();
            $instance->filtering_taxonomy = trc_FilteringTaxonomy::instance();

            return $instance;
        }

        public function init() {
            if ( is_admin() ) {
                // restrictions will not apply to back-end
                return $this;
            }
            add_action( 'pre_get_posts', array( $this, 'maybe_restrict_query' ) );

            return $this;
        }

        public function maybe_restrict_query( WP_Query &$query ) {
            if ( ! $this->should_restrict_query( $query ) ) {
                return;
            }

            $this->restrict_query( $query );
        }

        /**
         * @param WP_Query $query
         *
         * @return bool
         */
        public function should_restrict_query( WP_Query &$query ) {
            if ( empty( $this->taxonomies->get_restricting_taxonomies() ) ) {
                return false;
            }

            if ( ! $this->queries->should_restrict_queries() ) {
                return false;
            }
            if ( ! $this->queries->should_restrict_query( $query ) ) {
                return false;
            }

            if ( ! $this->post_types->is_restricted_post_type( $query->get( 'post_type' ) ) ) {
                return false;
            }

            if ( $this->user->can_access_query( $query ) ) {
                return false;
            }

            return true;
        }

        /**
         * @param WP_Query $query
         */
        public function restrict_query( WP_Query &$query ) {
            $restricting_taxonomies = $this->taxonomies->get_restricting_taxonomies();

            foreach ( $restricting_taxonomies as $restricting_tax_name ) {
                $query->tax_query->queries[] = $this->filtering_taxonomy->get_array_for( $restricting_tax_name );
            }
            $query->query_vars['tax_query'] = $query->tax_query->queries;
        }

        /**
         * @param trc_User $user
         */
        public function set_user( trc_User $user ) {
            $this->user = $user;
        }

        /**
         * @param trc_PostTypes $post_types
         */
        public function set_post_types( trc_PostTypes $post_types ) {
            $this->post_types = $post_types;
        }

        /**
         * @param trc_Taxonomies $taxonomies
         */
        public function set_taxonomies( trc_Taxonomies $taxonomies ) {
            $this->taxonomies = $taxonomies;
        }

        /**
         * @param trc_FilteringTaxonomy $filtering_taxonomy
         */
        public function set_filtering_taxonomy( trc_FilteringTaxonomy $filtering_taxonomy ) {
            $this->filtering_taxonomy = $filtering_taxonomy;
        }

        /**
         * @param trc_Queries $queries
         */
        public function set_queries( trc_Queries $queries ) {
            $this->queries = $queries;
        }
    }


The logic is pushed out of the class and delegated to more specialized satellite classes like trc_Taxonomies or trc_PostTypes.
Current code is on GitHub.

Next

I will walk each satellite class and develot its functionalities starting from the trc_User one as that’s probably the one requiring the more work.