Pre-Populate $wp_query settings with custom rewrite rules and custom template_redirect

I’m currently working on a file browser plugin which requires frontend editing capabilities for a registered user. For this scenario, I have registered some custom rewrite rules for my custom post type.

Rewrite Analyzer

Rewrite Analyzer Window

Read More

As you can see in the screenshot above, the plugin has a basic CRUD interface for managing the downloads. The routing is made through the template_redirect hook, which checks if the query variable action is set and then make a call to the appropriate action method.

add_action('template_redirect', array($this, '_requestHandler'), 9);
// ...
public function _requestHandler() {
    try {
        $requestedPage = get_query_var('pagename');

        if((isset($requestedPage) && $requestedPage == 'webeo-download')) {
            $action = get_query_var('action');
            $this->action = (isset($action)) ? $action : null;

            $downloadId = get_query_var('download');
            $this->downloadId = (isset($downloadId)) ? (int) $downloadId : null;

            if(isset($this->action) && !is_null($this->action) && strlen($this->action) > 0) {
                $method = 'action' . ucfirst($this->action);

                if(method_exists($this, $method)) {
                    call_user_func(array($this, $method));
                } else {
                    throw new Webeo_Exception(sprintf(__('Action method <code>%s</code> does not exists.', WEBEO_DOWNLOAD_TEXTDOMAIN), $method));
                }
            } else {
                $this->actionIndex();
            }

            echo $this->view->render($this->view->viewTemplate);
            exit();
        }
    } catch (Webeo_Exception $e) {
        $this->view->assign('error', $e);
        echo $this->view->render('default.phtml');
        exit();
    }
}

This works really well, except one thing. I wanted to prevent WordPress from generating any default rewrite rules for my custom post type. Access to my posts from outside should also be restricted. Only my controller above should be responsible to serve any download data to the user.

To do this, I have set the arguments public and publicly_queryable to false inside the register_post_type function. I’ve also set the argument exclude_from_search to true.

This seems to work. No post is showing under the default rewrite rule (e.g. example.com/downloads/<postname>) nor is a download listed in the default search results. Unfortunately the $wp_query arguments are also not set anymore. Therefore I’m not able to use comments_template() or any other loop-function inside my templates.

It’s clear: WordPress doesn’t know my page structure and is not able to generate the correct settings. I’ve tried to manually pre-populate the $wp_query arguments before the redirect inside the template_redirect method. But this doesn’t seem to work. I’m probably to late in the chain.

global $wp_query, $post, $withcomments;
$wp_query->is_single = true;
$wp_query->is_page = true;
$post = get_post($this->downloadId);
$withcomments = true;
wp_reset_query();

Any suggestions?

Thanks in advance
Roman

Related posts

1 comment

  1. I’ve probably found the solution for the above problem. To pre-populate the global variable $wp_query you have to run the query_posts() function with the correct arguments and reset the $wp_query->post with the function wp_reset_postdata(). This must be done on the template_redirect hook or earlier.

    class Webeo_Download {
        protected $downloadId;
        protected $action;
    
        public function setup() {
            // ...
            add_action('template_redirect', array($this, '_requestHandler'), 9);
        }
    
        public function _requestHandler() {
            $this->action = get_query_var('action');
            $this->downloadId = get_query_var('download');
    
            // ... load appropirate action method based on the query variable "action". See code example in the question above. 
        }
    
        public function actionView() {
            // Check permissions
            if(!is_user_logged_in()) {
                wp_redirect(wp_login_url());
                exit;
            }
    
            // Populate $wp_query for single download view
            global $wp_query;
            $args = array('post_type' => 'download', 'p' => $this->downloadId);
            query_posts($args);
            wp_reset_postdata();
    
            // Load the template
        }
    
        public function actionIndex() {
            // Do the same for all other action methods
        }
    
        public function actionAdd() {
            // ...
        }
    
        public function actionEdit() {
            // ...
        }
    
        public function actionDelete() {
            // ...
        }
    
        public function actionDownload() {
            // ...
        }
    }
    

    If someone has a better approach, please let me know!

Comments are closed.