Back to Top

Create Fake Pages in WordPress

Previous Post:

Create Fake Pages in WordPress

People’s need is changing everyday, and so is WordPress. From a simple blogging software, WordPress has evolved into a somewhat feature-rich Content Management System. It is, therefore, not a surprise if we, users and developers, always try to do crazy things with WordPress these days, and creating virtual (fake) WordPress pages is simply one of those unusual objectives.

So, what do we need fake pages for anyway? What I find particularly useful about fake pages is that you can create a page with its own structure on the fly. For example, if you are a plugin developer, you might want some tabs for documentation (like what we have on WordPress.org’s plugin directory). Or, if you are a tutorial writer, you might want to show each step of the tutorial on a separate page with its own title, and want those pages to get indexed by search engines without issues.

In fact, someone has already come up with a solution, but it seems that such solution doesn’t work anymore. There are other solutions that you can easily find using Google, but in my opinion they are incomplete and, to some extent, hackish. If you want a plugin, you can check out Virtual Pages, a nice little one but is still in its beta stage. I don’t actually use that plugin because my need remains very simple, and doable without much coding (a statically structured and multi-page plugin documentation).

Preparing your contents

Within the scope of this article, I will go after the same thing, a plugin’s documentation with fake and statically structured child pages (i.e. static slugs and titles). We will create 5 fake pages, including Installation, Usage, Screenshot, Changelog, and Users’ Feedbacks. Of course it is possible to have dynamically structured fake pages (e.g. a tutorial with different titles and slugs from page to page), but let’s leave such issue for a follow-up, otherwise this article could get pretty long (and confusing).

Please note that I use the word ‘page’ in its original meaning (i.e. a web page), and not a post type in WordPress, so basically you can use any custom post type for this task. However, it is best to use WordPress page or a custom post type with hierarchical support, because, obviously, we are creating fake child pages here.

In order to have separate contents for each fake page, you can use some post meta, but for the sake of simplicity, I will just use the built-in multi-page feature of WordPress, i.e. the <!--nextpage--> tag (sounds unfamiliar?) FYI, in previous versions of WordPress you should see a <!--nextpage--> button next to the <!--more--> button, and its task is to split your post into multiple pages. The bad thing is, it has been removed from the core WordPress (don’t really know why though). Adding it back should be simple, but I will just remember and use the <!--nextpage--> tag instead.

Now, to actually use it, open your HTML editor and put that tag on a separate line between any contents you want to split, like so:

First page's contents

<!--nextpage-->

Second page's contents

and in the Visual Editor, you will see something like this:

tag in the Visual Editor

--nextpage-- tag in the Visual Editor

Looks cool, but is there any catch? Well, a rather obvious drawback is that you won’t have comments for those fake pages (which doesn’t matter in my opinion), and editing the contents of this real page can be a little slower than usual (because it has all the contents of those fake pages in one place).

Coding Time!

Let’s not waste any more time, shall we? Fire up your theme’s functions.php and put the below snippet wherever you want:

  1. // Fake pages' permalinks and titles
  2. $my_fake_pages = array(
  3.     'installation' => 'Plugin Installation',
  4.     'usage' => 'Plugin Usage',
  5.     'screenshots' => 'Plugin Screenshots',
  6.     'changelog' => 'Plugin Changelog',
  7.     'feedbacks' => 'Users\' Feedbacks',
  8. );
  9.  
  10. add_filter('rewrite_rules_array', 'bwp_insertrules');
  11. add_filter('query_vars', 'bwp_insertqv');
  12.  
  13. // Adding fake pages' rewrite rules
  14. function bwp_insertrules($rules)
  15. {
  16.     global $my_fake_pages;
  17.  
  18.     $newrules = array();
  19.     foreach ($my_fake_pages as $slug => $title)
  20.         $newrules['(.+?)/' . $slug . '/?$'] = 'index.php?pagename=$matches[1]&fpage=' . $slug;
  21.  
  22.     return $newrules + $rules;
  23. }
  24.  
  25. // Tell WordPress to accept our custom query variable
  26. function bwp_insertqv($vars)
  27. {
  28.     array_push($vars, 'fpage');
  29.     return $vars;
  30. }
// Fake pages' permalinks and titles
$my_fake_pages = array(
    'installation' => 'Plugin Installation',
    'usage' => 'Plugin Usage',
    'screenshots' => 'Plugin Screenshots',
    'changelog' => 'Plugin Changelog',
    'feedbacks' => 'Users\' Feedbacks',
);

add_filter('rewrite_rules_array', 'bwp_insertrules');
add_filter('query_vars', 'bwp_insertqv');

// Adding fake pages' rewrite rules
function bwp_insertrules($rules)
{
	global $my_fake_pages;

	$newrules = array();
	foreach ($my_fake_pages as $slug => $title)
		$newrules['(.+?)/' . $slug . '/?$'] = 'index.php?pagename=$matches[1]&fpage=' . $slug;

	return $newrules + $rules;
}

// Tell WordPress to accept our custom query variable
function bwp_insertqv($vars)
{
	array_push($vars, 'fpage');
	return $vars;
}

As you can see, we store some basic information about our fake pages in a global variable named $my_fake_pages, and it contains the slugs and titles (‘slug’ => ‘title’).

Now please pay attention to this part:

  1.     $newrules = array();
  2.     foreach ($my_fake_pages as $slug => $title)
  3.         $newrules['(.+?)/' . $slug . '/?$'] = 'index.php?pagename=$matches[1]&fpage=' . $slug;
	$newrules = array();
	foreach ($my_fake_pages as $slug => $title)
		$newrules['(.+?)/' . $slug . '/?$'] = 'index.php?pagename=$matches[1]&fpage=' . $slug;

It allows you to specify the permalink structure you are going to use for your fake pages. Basically, we are asking WordPress to accept requests such as http://example.com/slug-to-a-real-page/fake-slug/ and use a page template file (in this case: page.php) to serve the contents.

These rewrite rules will ensure that WordPress only accepts certain fake slugs and nothing more. For example, a request to http://example.com/slug-to-page/unallowed-slug/ will be greeted with a 404 error instead of any contents from our actual page (there will be no 404 error if you have a real page at that location, of course).

After adding your custom permalinks this way, you will have to flush the rewrite rules so that your newly added rewrite rules are used. The fastest way to flush the rules is to visit Settings → Permalinks and then click on Save Changes. Now if you try to navigate to any fake URL, such as http://example.com/my-product-page/installation/, you will be greeted with the contents of my-product-page instead of any 404 error, sweet!

The next thing we have to do is to make our fake permalinks canonical. To do that, you can use something like this in functions.php:

  1. // Remove WordPress's default canonical handling function
  2. remove_filter('wp_head', 'rel_canonical');
  3. add_filter('wp_head', 'bwp_rel_canonical');
  4. function bwp_rel_canonical()
  5. {
  6.     global $fpage, $wp_the_query;
  7.  
  8.     if (!is_singular())
  9.         return;
  10.  
  11.     if (!$id = $wp_the_query->get_queried_object_id())
  12.         return;
  13.  
  14.     $link = trailingslashit(get_permalink($id));
  15.  
  16.     // Make sure fake pages' permalinks are canonical
  17.     if (!empty($fpage))
  18.         $link .= user_trailingslashit($fpage);
  19.  
  20.     echo "<link rel='canonical' href='$link' />\n";
  21. }
// Remove WordPress's default canonical handling function
remove_filter('wp_head', 'rel_canonical');
add_filter('wp_head', 'bwp_rel_canonical');
function bwp_rel_canonical()
{
	global $fpage, $wp_the_query;

	if (!is_singular())
		return;

	if (!$id = $wp_the_query->get_queried_object_id())
		return;

	$link = trailingslashit(get_permalink($id));

	// Make sure fake pages' permalinks are canonical
	if (!empty($fpage))
		$link .= user_trailingslashit($fpage);

	echo "<link rel='canonical' href='$link' />\n";
}

Please note the use of trailingslashit and user_trailingslashit, they will help you get the correct permalinks you need. You can read more information about them here if interested.

Next, it is important to mention that every single page on your site will now have those fake child pages. To limit the availability of fake child pages to certain pages only, you can, again, use a post meta, or better, use a page template.

With a page template you can have a separate template file for your fake pages, and you won’t have to check for anything (just choose your desired page template in the Page Attributes panel and WordPress will automatically load it). In this example I will name my page template bwp-plugin-page.php.

To limit the use of fake child pages to only the page template you choose, put this in your functions.php:

  1. add_action('template_redirect', 'bwp_non_fake_template_redirect');
  2.  
  3. function bwp_non_fake_template_redirect()
  4. {
  5.     global $fpage, $wp_the_query;
  6.  
  7.     $id = $wp_the_query->get_queried_object_id();
  8.     $template = get_post_meta($id, '_wp_page_template', true);
  9.  
  10.     // If a fake page is being requested but this post does not use
  11.     // the desired page template then we redirect to a 404 error
  12.     if (!empty($fpage) && (empty($template) || 'default' == $template))
  13.     {
  14.         // Just redirect to any non-existent page on your site
  15.         wp_redirect(get_option('home') . '/404/');
  16.         exit;
  17.     }
  18. }
add_action('template_redirect', 'bwp_non_fake_template_redirect');

function bwp_non_fake_template_redirect()
{
	global $fpage, $wp_the_query;

	$id = $wp_the_query->get_queried_object_id();
	$template = get_post_meta($id, '_wp_page_template', true);

	// If a fake page is being requested but this post does not use
	// the desired page template then we redirect to a 404 error
	if (!empty($fpage) && (empty($template) || 'default' == $template))
	{
		// Just redirect to any non-existent page on your site
		wp_redirect(get_option('home') . '/404/');
		exit;
	}
}

All requests should now be handled correctly (i.e. canonical permalinks and correct template file loaded).

Next, you will have to match each page you’ve separated using the <!--nextpage--> tag to a specific fake page. In my opinion, it’s simplest to match using their ordinals. So in this case, the first page will be an overview of your plugin, the second will be Installation, and so on.

Open bwp-plugin-page.php and put the following snippet at its top:

  1. // This is actually not needed if you use WP page,
  2. // but included for completeness
  3. $fpage = get_query_var('fpage');
  4. // If no fake page is requested, we show the real page
  5. $count = 1;
  6. if (!empty($fpage))
  7. {
  8.     foreach ($my_fake_pages as $slug => $title)
  9.     {
  10.         $count++; // start counting from 2
  11.         if ($fpage == $slug)
  12.         {
  13.             $showing_slug = $slug; // optional
  14.             $showing_title = $title; // optional
  15.             break;
  16.         }
  17.     }
  18. }
  19. // Other stuff here, such as get_header(), etc.
// This is actually not needed if you use WP page,
// but included for completeness
$fpage = get_query_var('fpage');
// If no fake page is requested, we show the real page
$count = 1;
if (!empty($fpage))
{
	foreach ($my_fake_pages as $slug => $title)
	{
		$count++; // start counting from 2
		if ($fpage == $slug)
		{
			$showing_slug = $slug; // optional
			$showing_title = $title; // optional
			break;
		}
	}
}
// Other stuff here, such as get_header(), etc.

Now at the place where you want to show the contents, just use the_content(); as you normally would, but with a minor (but important) adjustment:

  1. <?php if (have_posts()) while (have_posts()) : the_post(); ?>
  2.     <?php $page = $count; the_content(); ?>
  3. <?php endwhile; ?>
<?php if (have_posts()) while (have_posts()) : the_post(); ?>
	<?php $page = $count; the_content(); ?>
<?php endwhile; ?>

Notice the $page = $count; thing? It will effectively match split pages to your fake pages, so that a request to http://example.com/my-product-page/feedbacks/ will be served with the 5th page’s contents, etc.

Finally, you should also provide links to all fake pages so your visitors will be able to navigate through them easily:

  1. <ul>
  2. <?php foreach ($my_fake_pages as $slug => $title) { ?>
  3.     <li><a href="<?php echo trailingslashit(get_permalink()) . user_trailingslashit($slug); ?>"><?php echo $title; ?></a></li>
  4. <?php } ?>
  5. </ul>
<ul>
<?php foreach ($my_fake_pages as $slug => $title) { ?>
	<li><a href="<?php echo trailingslashit(get_permalink()) . user_trailingslashit($slug); ?>"><?php echo $title; ?></a></li>
<?php } ?>
</ul>

That’s it! Refresh your fake pages and see them shining just like a perfectly real WordPress page!

Customization

The <title> HTML tag

One thing you will probably notice is that your fake pages’ title tags might have something like ‘Page <number here>’ as their suffixes. Changing this depends greatly on how you generate the title tag for your HTML document, but generally, you can use the optional $showing_title global variables1 to construct <title>, like so:

  1. <title><?php
  2.     global $showing_title;
  3.     wp_title('|', true, 'right');
  4.     if (!empty($showing_title))
  5.         echo ' | ' . $showing_title;
  6. ?></title>
<title><?php
	global $showing_title;
	wp_title('|', true, 'right');
	if (!empty($showing_title))
		echo ' | ' . $showing_title;
?></title>

With the above snippet, your fake pages’ title will look similar to: My Plugin | Plugin Installation, My Plugin | Plugin Changelog, and so on.

Custom Post Types

You can of course use other custom post types to achieve the same thing, but as far as I know, you can’t use a page template with a custom post type. It would not be a problem, though, because we already have a separate template for custom post type. So if you have a post type named ‘plugin’, just put in your theme’s directory a file named single-plugin.php and it will be used just like bwp-plugin-page.php.

To use a custom post type named ‘plugin’, you will have to change the rewrite rules in our example to something like this:

  1.     $newrules = array();
  2.     foreach ($my_fake_pages as $slug => $title)
  3.         $newrules['plugin/([^/]+)/' . $slug . '/?$'] = 'index.php?plugin=$matches[1]&fpage=' . $slug;
	$newrules = array();
	foreach ($my_fake_pages as $slug => $title)
		$newrules['plugin/([^/]+)/' . $slug . '/?$'] = 'index.php?plugin=$matches[1]&fpage=' . $slug;

Next, remove the bwp_non_fake_template_redirect filter (we no longer use page template) and make sure you enable appropriate features for your post type, namely publicly_queryable and query_var2, so that our requests are happily accepted by WordPress.

So that’s it, I hope you enjoy this article! Any questions, suggestions are welcomed! Don’t forget to check out the follow-up that will address the dynamic title and slug issue. Till next time!

Further Reading

References

  1. 8-using-global-variables-in-wordpress/ []
  2. 11-wordpress-custom-post-types/ []
Print Article Trackback Trackback to this Article   Subscribe to Comments RSS Subscribe to Comments RSS

36 Opinions for Create Fake Pages in WordPress (4 Trackbacks)

  1. User's Gravatar
    28
    Tom October 5, 2012 at 7:53 pm – Permalink

    Hi,

    Your plugin is exactly what I am looking for.

    But I need one things more.

    $my_fake_pages = array – now is in functions.php

    In my example I need insert it in another themes file. For example single-{post_type}.php

    Can you help me?

    • User's Gravatar
      29
      OddOneOut October 7, 2012 at 10:32 pm – Permalink

      Why would you want to have that variable in single-[post-type].php?

      • User's Gravatar
        30
        Tom October 8, 2012 at 4:01 pm – Permalink

        Because then I will be able to create variables on the fly.

        • User's Gravatar
          31
          OddOneOut October 9, 2012 at 9:28 pm – Permalink

          I’m still unsure what you want to achieve, but if you want to have fake pages for a specific custom post type, you can just use functions.php, no need to put that variable in your theme’s file.

          • User's Gravatar
            32
            Tom October 10, 2012 at 1:31 am – Permalink

            Yeah, I know.

            But if when this array is in single-[post-type].php, it could generate it on the fly.

            For example, I have a variety of fields in the post. I use these fields to create a slug. And many other possibilities.

            But to generate the array, it must be in single-[post-type].php

            Tom

          • User's Gravatar
            33
            OddOneOut October 12, 2012 at 4:49 am – Permalink

            I see. So you use the value of those fields to generate pages on the fly?

  2. User's Gravatar
    34
    Chris Rutherford January 3, 2013 at 3:58 am – Permalink

    I posted another method of doing this on my blog. Based completely upon what you have done, just modified to fit my needs (allowing dynamic sub pages for a custom post type):

    http://www.placementedge.com/blog/create-post-sub-pages/

    • User's Gravatar
      35
      OddOneOut January 7, 2013 at 12:56 am – Permalink

      Feel free to post a link to that article, no need to remove the http prefix :).

  1. Ten Useful WordPress Constants You Might Love - Better WordPress

    [...] no images, and no markups at all! Needless to say, this certainly allows us to create any kinds of fake pages, with just any kinds of [...]

  2. Great WordPress Plugins (Small Giants) - Part 2 - Better WordPress

    [...] know that adding a fake page in WordPress isn’t easy, so this plugin can act as a friendly front end so that any user can build their fake pages without [...]

  3. Building the final ticketing feature - Philip Rabbett - Multimedia Portfolio

    [...] After creating the custom post type “ticket” for data input, I want to have a stage (step-by-step) purchasing process, WordPress enjoys making this a little hard but thankfully I came across a page detailing how to Create Fake Pages in WordPress. [...]

  4. Integrare un proprio CMS in Wordpress nel modo giusto - weblogix.biz

    [...] possibile, perché lo fanno per esempio i plugin di e-commerce, quindi indagando ho scoperto che si possono modificare le regole di rewrite interne per creare vere e proprie fake pages. Nel mio caso ecco cosa ho integrato nel file functions.php del [...]

Speak Up Your Mind!

An asterisk (*) indicates a required field and must be filled.




  • Web page and e-mail addresses turn into links automatically.
  • Wrap codes in: <code lang=""></code> or <pre lang="" extra="">
  • Lines and paragraphs break automatically.

Next Post: