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