Creating A Custom Page With WordPress Endpoints
Today we are going to take a look at using Endpoints within WordPress. Endpoints are points that you can define on the end of the normal in-built permastructs within WordPress to provide extra functionality.
What is a Permastruct?
A permastruct is a Permalink Structure. So for example the URL you visit when viewing a single post, normally (depending on your permalink setting) in the format of yyyy/mm/dd/post-name/
, is a permastruct. There are ones for posts, pages, date archives, category archives and so on.
We are going to create an endpoint for posts. Rather pointlessly we are going to have it output the data from $wp_query->post
in a (fairly) nicely formatted list. You probably wouldn’t want to do this, but it shows how to use endpoints & that is the main thing.
Github Repo
First here is a link to the full code on Github. If you just want to have a play around with it or want to clone the repo so you can follow along rather than copypasta or retype, feel free.
Creating Our Plugin
We are going to do this in the form of a plugin. Let’s start off by fleshing out the plugin with the standard WordPress plugin header.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<?php /* Plugin Name: Virtual Page Tutorial Description: Creates a Virtual Page in WordPress while keeping the theme intact Version: 1.0 Author: Paul Robinson Author URI: http://return-true.com Copyright (c) 2015 Paul Robinson (http://return-true.com) General Public License v2 or later http://www.gnu.org/licenses/gpl-2.0.html This is a WordPress plugin (http://wordpress.org). This plugin, like WordPress, is licensed under the GPL. */ |
This is the standard WordPress plugin header, nothing new here.
Now let’s make our class.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
Class VPTutorial { function __construct() { register_activation_hook( __FILE__, array( $this, 'activate' ) ); add_action( 'init', array( $this, 'rewrite' ) ); add_action( 'template_include', array( $this, 'change_template' ) ); } } new VPTutorial; |
Here we are creating our base class & setting up a few actions within our construct. If you’ve never used a class for a plugin before make note of the slightly different method used to tell WordPress which method to execute when the actions are ran.
Next let’s add our activation hook & our rewrite
method. These go within the class, you can place them where you like (inside the class) but it is normally accepted to keep the construct at the top of the class.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
//Class VPTutorial { ... function activate() { set_transient( 'vpt_flush', 1, 60 ); } function rewrite() { add_rewrite_endpoint( 'dump', EP_PERMALINK ); if(get_transient( 'vpt_flush' )) { delete_transient( 'vpt_flush' ); flush_rewrite_rules(); } } .... //} |
These two methods are pretty simple. activate()
sets a transient set to expire after 60 seconds. We do this so we know to flush the rewrite rules when we first add the endpoint.
In the rewrite()
method we setup our ‘dump’ endpoint using the permalink EP Mask (more on that shortly) and then check for our transient. If we find it we delete the transient, so the code won’t run a second time, and flush the rewrite rules. Note: Flushing the rewrite rules is extremely resource heavy, only do it after changing a setting that will invalidate your current rewrite rules.
The EP Masks are used to tell WordPress which permastructs it should apply your endpoint to. In this case we only want to add it on the permalink (posts) permastruct. There are a fair few available though, to see them all visit the WordPress Codex page.
That’s really all that is needed to get things working, but we should really get something meaningful (or in this case not so meaningful) showing on our new endpoint. Let’s add in one last method to do that.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
//Class VPTutorial { ... function change_template( $template ) { if( get_query_var( 'dump', false ) !== false ) { //Check theme directory first $newTemplate = locate_template( array( 'template-dump.php' ) ); if( '' != $newTemplate ) return $newTemplate; //Check plugin directory next $newTemplate = plugin_dir_path( __FILE__ ) . 'templates/template-dump.php'; if( file_exists( $newTemplate ) ) return $newTemplate; } //Fall back to original template return $template; } ... //} |
One thing to note about endpoints is that they automatically register a query variable for you. This can be used for a number of things but here we are using it to check if our endpoint is being visited so we can load a custom template.
Just to add a little complexity to the plugin we are checking to see if a template file, called template-dump.php
, is found in the theme folder. If there isn’t a template in the theme folder then we are loading a default one located in a folder called templates from within our plugin folder. I’m not going to show the code for the template file here as it is just a bog standard template file, instead check it out on the Github repo if you really want to see the code.
Make yourself a template file and place it inside a templates folder in your plugin folder. Once you’ve done that you are ready to go.
That’s all there is to it. Here is a full copy of the plugin code.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
Class VPTutorial { function __construct() { register_activation_hook( __FILE__, array( $this, 'activate' ) ); add_action( 'init', array( $this, 'rewrite' ) ); add_action( 'template_include', array( $this, 'change_template' ) ); } function activate() { set_transient( 'vpt_flush', 1, 60 ); } function rewrite() { add_rewrite_endpoint( 'dump', EP_PERMALINK ); if(get_transient( 'vpt_flush' )) { delete_transient( 'vpt_flush' ); flush_rewrite_rules(); } } function change_template( $template ) { if( get_query_var( 'dump', false ) !== false ) { //Check theme directory first $newTemplate = locate_template( array( 'template-dump.php' ) ); if( '' != $newTemplate ) return $newTemplate; //Check plugin directory next $newTemplate = plugin_dir_path( __FILE__ ) . 'templates/template-dump.php'; if( file_exists( $newTemplate ) ) return $newTemplate; } //Fall back to original template return $template; } } new VPTutorial; |
Just visit a single post view & add /dump
to the end of the URL & you will see the template file you created.
Frequently Asked Questions
How do I make a static page that just has a simple URL?
That is something I’m going to cover in my next tutorial. As far as I am aware you cannot use endpoints as they are restricted to the built in permastructs. Instead you need to use custom rewrites instead.
What use would this be anyway?
This would be ideal if you have a gallery per post that you’d like to display separately, or if you want a way for people to access a JSON representation of a post. You can do anything you like really.
Webucator Video Tutorial
This tutorial has been made into a video tutorial by the lovely people over at Webucator, who offer online & onsite WordPress training. Check out the video below.
Hopefully this has been helpful, if you have any questions or notice any mistakes just leave a comment & I’ll answer as soon as I can.
8 Comments
Kathy
Hi Paul, this turned out pretty well! I have a couple of suggestions.
1. Activation hooks require a static function if you’re inside of a __construct(). $this will always through a warning in WP_DEBUG mode.
2. Are you flushing the permalinks every 60 seconds? I think you should be able to set the rewrite rule on the init hook and flush the rules one time on activation. All the rewrite rules are “remembered” in an option.
Otherwise, cool article!
Paul Robinson
Hey Kathy,
1. I have my WP test install (via Vagrant) setup with WP_DEBUG enabled and haven’t seen any warnings. Will give it a check tonight though.
2. No! I don’t think so? The transient was just a nice way to run the rewrite rule refresh on first run (after activation) making sure that the endpoint had been registered, and make sure the transient will be removed whether I delete it or not. Unless I’m missing something I’m not sure how that will flush the rules every 60s. 😕
Kathy
Static functions in activation is mentioned in the codex, but I just checked out the repo and you’re right, I don’t see an error. Maybe that’s changed? I’d swear I have seen that notice in the past.
The third parameter of set_transient is number of seconds to keep a transient. But now I see that you are only temporarily setting the transient as a “flag”. I may have had a few margaritas last night. 😉 Are activation hooks just being considered not that reliable? I’ve seen many of plugins move their activation/upgrade routines to init and doing a kind of quick version check against a db option that was placed on activation.
Paul Robinson
Yeah I just noticed that in the codex myself, but I’m not seeing any errors. Very odd.
Indeed. The reason I used it is I’ve seen issues in the past (on some servers) where a plugin uses
add_option()
and something happens & the option doesn’t get deleted. That causes the plugin to be in a perpetual state of activation. Using a transient at least means that WordPress will remove it for me after 60s if I don’t for some reason.Yeah. Honestly I did have it exactly the way you mentioned when testing, but while the flush ran it didn’t actually work & I had to manually do it by saving the permalinks page. I figured it might be due to the fact the activation routine runs, then does a refresh (according to the codex) and therefore may not be running my code before flushing.
Either way I guess it’s a good way of showing people how to use that delayed activation system for instances where you need to run certain things like
add_action()
which you can’t normally in activation functions. 🙂Save me a margarita I’m now off work for a week. 🙂
P.S. I have no idea why I didn’t reply to your original comment & instead made a new comment… I’m an idiot.
Xaib Aslam
Finally, I found it and its really awesome. Can you help me with this, If I have more than 1 template then what to do?
Xaib Aslam
I got your code but I want to know how I can give title for the custom template, its using parent post title. How I can use my own title for the template?
Paul Robinson
Hi Xaib,
Apologies for not replying to you the Holiday season was extremely busy for me and to top it off there was an issue with the service I use for email delivery (to get comment notifications) and I didn’t find out until the New Year.
Just in case you still need an answer, and to help others with the same problem, you would add a custom title by using WordPress’
wp_title
filter. Since you’d need to detect the same template in multiple functions it might be a good idea to have a property on your class that holds the template data such as filenames and other data (like the title).You can then access that variable from both the
change_template
function and the function hooked towp_title
.Hope that helps.
Charan
How can i create Custome endpoints to the pages