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 Editorwp-content/uploads/2011/06/a98-nextpage-visual.gif 533w" sizes="(max-width: 300px) 100vw, 300px" />

--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:

// 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:

	$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:

// 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:

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:

// 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:

<?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:

<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:

<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:

	$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/ []

Take Social Sharing to
the Next Level with Monarch!

Take Social Sharing to the Next Level with Monarch!
Print Article Trackback Trackback to this Article   Subscribe to Comments RSS Subscribe to Comments RSS

38 Opinions for Create Fake Pages in WordPress (16 Trackbacks)

  1. User's Gravatar
    1
    Hieudt June 14, 2011 at 4:26 pm – Permalink

    Yeah! This post is exactly what I need. With this way, we can separate the comments section from post content for faster loading. A website about books should have some tabs like Description, Reviews, Discussions, Similar Books for better content grouping.
    Thank you for great code!

  2. User's Gravatar
    2
    Amada June 15, 2011 at 2:33 pm – Permalink

    Nice write up! Looking forward to the follow-up though.

  3. User's Gravatar
    3
    Dave Ross July 8, 2011 at 9:29 pm – Permalink

    Hey, thanks for plugging Virtual Pages, even if it didn’t meet your needs. It’s definitely still a beta: there’s a good number of users who have great success with it, but some for whom it doesn’t work at all. Still trying to figure that one out, and I haven’t even tried it with WordPress 3.2 yet. I’ve been afraid to.

    Anyway, if any WordPress coders out there want to try improving it, I’d welcome any help you can give!

    • User's Gravatar
      4
      OddOneOut July 9, 2011 at 9:21 am – Permalink

      Hi Dave,

      So you’re the author of Virtual Pages, nice to meet you ;). I have not tested your plugin on 3.2 either, but it works fine for me on 3.1 (not a complete test though).

      I find your plugin very promising because it does add an important feature to WordPress. I’ve also glanced through the codes and they look clean to me, some parts can be improved and I’m happy to participate. Do you have your plugin on Github or something? I will contribute when things on my side are settled.

      Until then, good luck ;).

  4. User's Gravatar
    5
    Dave Ross July 12, 2011 at 12:29 pm – Permalink

    I put the code up on Github at https://github.com/daveross/virtual-pages

    Go ahead and play around with it when you have time, send me a pull request if you come up with any awesome changes.

  5. User's Gravatar
    6
    sebastien July 21, 2011 at 11:53 pm – Permalink

    i add a page with this content

    page1
    
    page2
    
    page3
    
    page4
    
    page5
    

    in functions.php i add this code :

    // Codes in posts
    

    i add this file bwp-plugin-page.php

    // Codes in posts
    

    my structure is : /%category%/%postname%
    but the result is : http://demo3.archiparmentier.com/test

    where are the pages ?

    could you help me please ? Where is my error ?

  6. User's Gravatar
    7
    sebastien July 21, 2011 at 11:54 pm – Permalink

    all BR and P are not in my original code of course 🙂

  7. User's Gravatar
    8
    sebastien July 22, 2011 at 2:08 am – Permalink

    ok, that’s right now.
    don’t forget to choose the template of your page 🙂

    • User's Gravatar
      9
      OddOneOut July 22, 2011 at 11:42 am – Permalink

      Glad you got it working ;). When you want to post a block of codes, please use the <pre> tag instead of the <code> tag, thanks!

  8. User's Gravatar
    10
    jevusi July 26, 2011 at 5:58 pm – Permalink

    In my case i don’t use the_content but values of several custom fields.
    Is it possible to something like that ? :

    $type = get_post_meta($post->ID, 'type', true);
    $color = get_post_meta($post->ID, 'color', true);
    $size = get_post_meta($post->ID, 'size', true);
    
    $MyContent = " the type is" . $type . "\n";
    $MyContent = "\n";
    $MyContent = " the color is" . $color. "\n";
    $MyContent = "\n";
    $MyContent = " the size is" . $size. "\n";
    
    $MyContent = apply_filters('the_content', $MyContent );
    echo $MyContent;

    ps : this code doesn’t work

    • User's Gravatar
      11
      OddOneOut July 26, 2011 at 9:28 pm – Permalink

      Hi jevusi,

      In your codes, when you want to append a variable you must use .=, not =. So, you should replace line 5 to line 9 with this:

      $MyContent = " the type is" . $type . "\n";
      $MyContent .= "\n";
      $MyContent .= " the color is" . $color. "\n";
      $MyContent .= "\n";
      $MyContent .= " the size is" . $size. "\n";
      

      Next, you must pass the contents of $MyContent back to $post, otherwise WordPress will not be able to split the pages for you. Line 11 to 12 should be replaced with this:

      $post->post_content = $MyContent;
      <?php if (have_posts()) while (have_posts()) : the_post(); ?>
      	<?php $page = $count; the_content(); ?>
      <?php endwhile; ?>
      

      Hope that helps.

  9. User's Gravatar
    13
    jevusi July 26, 2011 at 11:35 pm – Permalink

    Sorry but that doesn’t work

    That works if i use this code

    if (have_posts()) : while (have_posts()) : the_post(); $page = $count; ?>
    		
    $type = get_post_meta($post->ID, 'test1', true);
    $color = get_post_meta($post->ID, 'test2', true);
    $size = get_post_meta($post->ID, 'test3', true);
    
    $MyContent = " the type is" . $type . "\n";
    $MyContent .= "\n";
    $MyContent .= " the color is" . $color. "\n";
    $MyContent .= "\n";
    $MyContent .= " the size is" . $size. "\n";
    
    global $post;
    $post->post_content = $MyContent;
    setup_postdata($post);
    
    the_content(); 

    but in this case the url rewriting doesn’t work
    only the pagination of wp_link_pages works

    What can i do to have a rewriting url ?

    • User's Gravatar
      14
      OddOneOut July 27, 2011 at 4:24 pm – Permalink

      Do you have an online demo so I can see? What does wp_link_pages give you? Have you added the necessary rewrite rules?

  10. User's Gravatar
    15
    jevusi July 27, 2011 at 4:51 pm – Permalink

    yes i have an online demo : http://demo3.archiparmentier.com/testseb

    the content of my function.php is exactly your code :

    // look in post
    
    • User's Gravatar
      16
      OddOneOut July 27, 2011 at 11:28 pm – Permalink

      Look like you’re missing a codeblock. Please search for “Open bwp-plugin-page.php and put the following snippet at its top” and follow instructions there.

  11. User's Gravatar
    17
    jevusi July 28, 2011 at 5:32 pm – Permalink

    no missing code…
    the code of my bwp-plugin-page.php is :

    $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;
    		}
    	}
    }
    
    
    
    if (have_posts()) : while (have_posts()) : the_post(); $page = $count;
    
    
    $type = get_post_meta($post->ID, 'test1', true);
    $color = get_post_meta($post->ID, 'test2', true);
    $size = get_post_meta($post->ID, 'test3', true);
    
    $MyContent = " the type is" . $type . "\n";
    $MyContent .= "\n";
    $MyContent .= " the color is" . $color. "\n";
    $MyContent .= "\n";
    $MyContent .= " the size is" . $size. "\n";
    
    global $post;
    $post->post_content = $MyContent;
    setup_postdata($post);
    
    the_content(); ?>
    
    <?php wp_link_pages(array('before' => '' . __('Pages:', 'kubrick') . ' ', 'after' => '', 'next_or_number' => 'number')); ?>
    		
    		    
        <?php foreach ($my_fake_pages as $slug => $title) { ?>
            <?php echo $title; ?>
        <?php } ?>
        
    <?php endwhile; endif; ?>
    
    

    maybe the problem comes from setup_postdata($post).

    • User's Gravatar
      18
      OddOneOut July 29, 2011 at 8:44 am – Permalink

      You’re doing it wrong, I’ve got the codes working here. Again, replace from line 18 with this:

      $type = get_post_meta($post->ID, 'test1', true);
      $color = get_post_meta($post->ID, 'test2', true);
      $size = get_post_meta($post->ID, 'test3', true);
      
      $MyContent = " the type is" . $type . "\n";
      $MyContent .= "\n";
      $MyContent .= " the color is" . $color. "\n";
      $MyContent .= "\n";
      $MyContent .= " the size is" . $size. "\n";
      
      $post->post_content = $MyContent;
      if (have_posts()) while (have_posts()) : the_post();
      	$page = $count; the_content();
      endwhile; ?>
      
      
      

      Tested and worked.

      • User's Gravatar
        19
        jevusi July 29, 2011 at 6:11 pm – Permalink

        thanks but that doesn’t work.
        With this code, the_content returns… the real content
        I think that’s normal because $post->post_content = $MyContent; is above
        if (have_posts()) while (have_posts()) : the_post();

        • User's Gravatar
          20
          OddOneOut July 29, 2011 at 6:52 pm – Permalink

          No not really, those objects are passed by reference so when you modify $post, the_post will be modified accordingly.

          Please use this: http://betterwp.net/contact/ and send me your bwp-plugin-page.php file.

  12. User's Gravatar
    21
    jevusi July 29, 2011 at 7:41 pm – Permalink

    send !

  13. User's Gravatar
    22
    Erlend Sogge Heggen August 7, 2011 at 6:53 am – Permalink

    I would love to see that follow up article. We are contemplating a rather elaborate redesign of jMonkeyEngine’s wiki page, which involves a neat WordPress + JavaDoc hybrid. Our ideal setup would require “dynamically structured fake pages (e.g. a tutorial with different titles and slugs from page to page)” – hopefully that would also mean the individual pages would work with comments.

    We will make sure to credit you if you could help us out with another article on this subject.

    Also, if this code could ultimately be wrapped into a plugin, i.e. join up with Dave as a co-author of Virtual Pages, I think that could be of great help to a lot of people.

    Thanks for a great article!

    • User's Gravatar
      23
      OddOneOut August 8, 2011 at 10:39 am – Permalink

      Hi Erlend, and thanks for your interest in my article.

      hopefully that would also mean the individual pages would work with comments.

      It is possible, but not easy, and I’m not sure it will be available in the follow up article as it is a rather complicated matter.

      Also, if this code could ultimately be wrapped into a plugin, i.e. join up with Dave as a co-author of Virtual Pages, I think that could be of great help to a lot of people.

      I surely will, it’s a very good plugin.

      Anyway, it will depend greatly on what you would like to achieve, so feel free to contact me or keep commenting and I will answer whenever possible.

      Good luck with your project!

  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 [...]

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: