Custom Pages with WordPress Plugins

One of the more interesting challenges I have faced with WordPress is offering custom pages. These could be pages such taking a survey, asking a question, or suggesting a topic. Previously, I had gone about coding these kinds of pages by just sticking the php file in the web root. The problem with that strategy is twofold. The page cannot be disabled without going to the file system. The page also doesn’t respect theme changes cleanly.

The solution comes in three parts. The first step is updating the rewrite rules to map a url to an internal value. Next, we map this internal value to a template. Finally, we map this internal template to the theme template.

Most WordPress installations take advantage of URL rewriting on the part of the web server. The goal is to rewrite all blog page requests to index.php. From there, WordPress examines the original URL to determine what content to send. The WordPress rewrite rules allow us to modify this URL. In our case, we want to map a custom page to query variable that we are going to pass into index.php, sending it down the $wp_query.

add_filter('generate_rewrite_rules', 'custom_page_generate_rewrite_rules');
function custom_page_generate_rewrite_rules($wp_rewrite) {
    $custom_page_rules = array(
        'survey' => 'index.php?custom_page=survey',
    );
    $wp_rewrite->rules = $custom_page_rules + $wp_rewrite->rules;
}

Now that we are able to map a url to the $wp_query, we need to resolve that into a template. For this, we utilize the template redirect of WordPress. This allows our plugin to resolve the request into a template call and exit before wordpress attempts to resolve the request.

add_action('template_redirect', 'custom_page_template_redirect');
fnction custom_page_template_redirect() {
    global $wp_query;
    $custom_page = $wp_query->query_vars['custom_page'];
    if ($custom_page == 'survey') {
        include(ABSPATH.'wp-content/plugins/custom_page/survey_page.php');
        exit;
    }
}

For the final piece, we will take a look at just one of the custom pages, survey_page.php. The template redirect hands off the request processing to this page and exits. It is up to this page to interact with the existing theme and display our custom page. The only catch is that the theme must utilize the the_content() template function of WordPress. Otherwise, the proper hooks will not be called and our page will not display correctly.

Fetching the proper file to use from the existing theme is very simple. To do so, we resolve the page template first, single post template second, and finally default to the index.

if (file_exists(TEMPLATEPATH.'/page.php')) {
    include(get_page_template());
}
elseif (file_exists(TEMPLATEPATH.'/single.php')) {
    include(get_single_template());
}
else {
    include(TEMPLATEPATH.'/index.php');
}

Updating the content involves inserting filters onto the_title and the_content. However, we only want these filters to be called during the_loop. Surpressing the filters from get_header, get_sidebar, and get_footer will prevent the custom filters from overrideing page template content.

add_action('get_header', 'custom_page_remove_filters');
add_action('get_sidebar', 'custom_page_remove_filters');
add_action('get_footer', 'custom_page_remove_filters');
function custom_page_remove_filters() {
    remove_filter('the_title', 'custom_page_title');
    remove_filter('the_content', 'custom_page_content');
}

add_action('loop_start', 'custom_page_add_filters');
function custom_page_add_filters() {
    add_filter('the_title', 'custom_page_title');
    add_filter('the_content', 'custom_page_content');
}

I went ahead and put all of this together into a plugin on this site. You can see it in action with the following pages:

I have also attached the source code for the plugin as a full example. You can download it here.

Attachments: custom_page
August 18th, 2009