Skip to content

Instantly share code, notes, and snippets.

@brianoz
Created February 20, 2014 01:10
Show Gist options
  • Save brianoz/9105004 to your computer and use it in GitHub Desktop.
Save brianoz/9105004 to your computer and use it in GitHub Desktop.

Revisions

  1. brianoz created this gist Feb 20, 2014.
    273 changes: 273 additions & 0 deletions class-virtualthemedpage-bc.php
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,273 @@
    <?php
    /*
    * Virtual Themed Page class
    *
    * This class implements virtual pages for a plugin.
    *
    * It is designed to be included then called for each part of the plugin
    * that wants virtual pages.
    *
    * It supports multiple virtual pages and content generation functions.
    * The content functions are only called if a page matches.
    *
    * The class uses the theme templates and as far as I know is unique in that.
    * It also uses child theme templates ahead of main theme templates.
    *
    * Example code follows class.
    *
    * August 2013 Brian Coogan
    *
    */




    // There are several virtual page classes, we want to avoid a clash!
    //
    //
    class Virtual_Themed_Pages_BC
    {
    public $title = '';
    public $body = '';
    private $vpages = array(); // the main array of virtual pages
    private $mypath = '';
    public $blankcomments = "blank-comments.php";


    function __construct($plugin_path = null, $blankcomments = null)
    {
    if (empty($plugin_path))
    $plugin_path = dirname(__FILE__);
    $this->mypath = $plugin_path;

    if (! empty($blankcomments))
    $this->blankcomments = $blankcomments;

    // Virtual pages are checked in the 'parse_request' filter.
    // This action starts everything off if we are a virtual page
    add_action('parse_request', array(&$this, 'vtp_parse_request'));
    }

    function add($virtual_regexp, $contentfunction)
    {
    $this->vpages[$virtual_regexp] = $contentfunction;
    }


    // Check page requests for Virtual pages
    // If we have one, call the appropriate content generation function
    //
    function vtp_parse_request(&$wp)
    {
    //global $wp;
    if (empty($wp->query_vars['pagename']))
    return; // page isn't permalink

    //$p = $wp->query_vars['pagename'];
    $p = $_SERVER['REQUEST_URI'];

    $matched = 0;
    foreach ($this->vpages as $regexp => $func)
    {
    if (preg_match($regexp, $p))
    {
    $matched = 1;
    break;
    }
    }
    // Do nothing if not matched
    if (! $matched)
    return;

    // setup hooks and filters to generate virtual movie page
    add_action('template_redirect', array(&$this, 'template_redir'));
    add_filter('the_posts', array(&$this, 'vtp_createdummypost'));

    // we also force comments removal; a comments box at the footer of
    // a page is rather meaningless.
    // This requires the blank_comments.php file be provided
    add_filter('comments_template', array(&$this, 'disable_comments'), 11);

    // Call user content generation function
    // Called last so it can remove any filters it doesn't like
    // It should set:
    // $this->body -- body of the virtual page
    // $this->title -- title of the virtual page
    // $this->template -- optional theme-provided template
    // eg: page
    // $this->subtemplate -- optional subtemplate (eg movie)
    // Doco is unclear whether call by reference works for call_user_func()
    // so using call_user_func_array() instead, where it's mentioned.
    // See end of file for example code.
    $this->template = $this->subtemplate = null;
    $this->title = null;
    unset($this->body);
    call_user_func_array($func, array(&$this, $p));

    if (! isset($this->body)) //assert
    wp_die("Virtual Themed Pages: must save ->body [VTP07]");

    return($wp);
    }


    // Setup a dummy post/page
    // From the WP view, a post == a page
    //
    function vtp_createdummypost($posts)
    {

    // have to create a dummy post as otherwise many templates
    // don't call the_content filter
    global $wp, $wp_query;

    //create a fake post intance
    $p = new stdClass;
    // fill $p with everything a page in the database would have
    $p->ID = -1;
    $p->post_author = 1;
    $p->post_date = current_time('mysql');
    $p->post_date_gmt = current_time('mysql', $gmt = 1);
    $p->post_content = $this->body;
    $p->post_title = $this->title;
    $p->post_excerpt = '';
    $p->post_status = 'publish';
    $p->ping_status = 'closed';
    $p->post_password = '';
    $p->post_name = 'movie_details'; // slug
    $p->to_ping = '';
    $p->pinged = '';
    $p->modified = $p->post_date;
    $p->modified_gmt = $p->post_date_gmt;
    $p->post_content_filtered = '';
    $p->post_parent = 0;
    $p->guid = get_home_url('/' . $p->post_name); // use url instead?
    $p->menu_order = 0;
    $p->post_type = 'page';
    $p->post_mime_type = '';
    $p->comment_status = 'closed';
    $p->comment_count = 0;
    $p->filter = 'raw';
    $p->ancestors = array(); // 3.6

    // reset wp_query properties to simulate a found page
    $wp_query->is_page = TRUE;
    $wp_query->is_singular = TRUE;
    $wp_query->is_home = FALSE;
    $wp_query->is_archive = FALSE;
    $wp_query->is_category = FALSE;
    unset($wp_query->query['error']);
    $wp->query = array();
    $wp_query->query_vars['error'] = '';
    $wp_query->is_404 = FALSE;

    $wp_query->current_post = $p->ID;
    $wp_query->found_posts = 1;
    $wp_query->post_count = 1;
    $wp_query->comment_count = 0;
    // -1 for current_comment displays comment if not logged in!
    $wp_query->current_comment = null;
    $wp_query->is_singular = 1;

    $wp_query->post = $p;
    $wp_query->posts = array($p);
    $wp_query->queried_object = $p;
    $wp_query->queried_object_id = $p->ID;
    $wp_query->current_post = $p->ID;
    $wp_query->post_count = 1;

    return array($p);
    }


    // Virtual Movie page - tell wordpress we are using the given
    // template if it exists; otherwise we fall back to page.php.
    //
    // This func gets called before any output to browser
    // and exits at completion.
    //
    function template_redir()
    {
    // $this->body -- body of the virtual page
    // $this->title -- title of the virtual page
    // $this->template -- optional theme-provided template eg: 'page'
    // $this->subtemplate -- optional subtemplate (eg movie)
    //

    if (! empty($this->template) && ! empty($this->subtemplate))
    {
    // looks for in child first, then master:
    // template-subtemplate.php, template.php
    get_template_part($this->template, $this->subtemplate);
    }
    elseif (! empty($this->template))
    {
    // looks for in child, then master:
    // template.php
    get_template_part($this->template);
    }
    elseif (! empty($this->subtemplate))
    {
    // looks for in child, then master:
    // template.php
    get_template_part($this->subtemplate);
    }
    else
    {
    get_template_part('page');
    }

    // It would be possible to add a filter for the 'the_content' filter
    // to detect that the body had been correctly output, and then to
    // die if not -- this would help a lot with error diagnosis.

    exit;
    }


    // Some templates always include comments regardless, sigh.
    // This replaces the path of the original comments template with a
    // empty template file which returns nothing, thus eliminating
    // comments reliably.
    function disable_comments($file)
    {
    if (file_exists($this->blankcomments))
    return($this->mypath.'/'.$blankcomments);
    return($file);
    }


    } // class


    // Example code - you'd use something very like this in a plugin
    //
    if (0)
    {
    // require 'BC_Virtual_Themed_pages.php';
    // this code segment requires the WordPress environment

    $vp = new Virtual_Themed_Pages_BC();
    $vp->add('#/mypattern/unique#i', 'mytest_contentfunc');

    // Example of content generating function
    // Must set $this->body even if empty string
    function mytest_contentfunc($v, $url)
    {
    // extract an id from the URL
    $id = 'none';
    if (preg_match('#unique/(\d+)#', $url, $m))
    $id = $m[1];
    // could wp_die() if id not extracted successfully...

    $v->title = "My Virtual Page Title";
    $v->body = "Some body content for my virtual page test - id $id\n";
    $v->template = 'page'; // optional
    $v->subtemplate = 'billing'; // optional
    }
    }




    // end