We’ll write some PHP code to create sharing links for Draft Posts in WordPress. These links can be used to share draft (or private) content with non-logged-in users. We’ll make a small meta box on the Edit Post page too, so it’s super easy to copy the draft post link.
WordPress uses links like this to preview draft posts, but they only work if you’re logged in:
https://wp-tutorials.tech/?p=1234
Our code will look for a magic query parameter at the end of the URL and, if it’s valid, it’ll adjust the WordPress Query to allow the post to be displayed. The links will look something like this:
https://wp-tutorials.tech/?p=1234&wptsdp=abc123543def
Scaffold the code
There’s not much code for this tutorial, and it all fits into a single (small) PHP file. Create a file in your WordPress child theme called wpt-share-draft-link.php and paste the following into it:
<?php /** * Headwall WP Tutorials Share Draft Post Link (WPTSDP) * * https://wp-tutorials.tech/add-functionality/display-taxonomy-terms-in-an-indexed-list/ * * Changes * * 2024-10-27 : Added better support for specifying post types. * * 2024-10-10 : Added the option to share links to scheduled posts (rather than * just private & draft). */ // Block direct access. defined('WPINC') || die(); const WPTSDP_KEY = 'wptsdp'; const WPTSDP_SHARE_DRAFT = true; const WPTSDP_SHARE_PRIVATE = true; const WPTSDP_SHARE_SCHEDULED = true; // Add your post types into here. const WPTSDP_POST_TYPES = ['post', 'page', 'product']; /** * Create a key that's unique for this post. It's not very "secure", * but it only need to look "obscure" to the user. You can increase * the complexity of this function if you want to make it more than * tamper-proof. */ function wptsdp_get_magic_key_for_post(int $post_id) { return md5('wptsdp_' . $post_id); } /** * Add a small meta box to the Edit Post page so we can copy the * link URL. */ function wptsdp_register_meta_boxes($post_type, $post) { if (empty($post_type)) { // No psot type - do nothing. } elseif (!in_array($post_type, WPTSDP_POST_TYPES)) { // We're not configured for this post type - do nothing. } else { add_meta_box( 'wptsdp_post_mb', // A unique identifier 'Share Draft', // Title 'wptsdp_render_meta_box', // Function used to render the contents of the meta box null, 'side', // location 'high' // priority ); } } add_action('add_meta_boxes', 'wptsdp_register_meta_boxes', 10, 2); /** * Render the contents of our meta box on the Edit Post page. */ function wptsdp_render_meta_box() { // The post ID we're editing. $post_id = get_the_ID(); // Construct the actual share link for this post. $share_url = sprintf( '%s?p=%d&%s=%s', // ... site_url('/'), $post_id, urlencode(WPTSDP_KEY), urlencode(wptsdp_get_magic_key_for_post($post_id)) ); printf('<p>If you send this URL to someone, they can use it to read this post even if it is still in Draft</p>'); printf('<input type="text" value="%s" readonly class="widefat" />', esc_url($share_url)); } /** * Before WordPress runs its main query, check to see if we have a * match for the secret key. If so, add "draft" to the query's post * status query variable. */ function wptsdp_include_draft_posts($query) { // Modify the $query (WP_Query) object here. // ... } add_action('pre_get_posts', 'wptsdp_include_draft_posts');
The add the following couple of lines to your child theme’s functions.php file.
// Headwall WP Tutorials : Share draft post link. require_once dirname(__FILE__) . '/wpt-share-draft-link.php';
Create a new post, give it a title and save it as a draft – don’t publish it. Have a look in your post’s side bar and find the new meta box. Copy the link, open a new Private Browsing (Incognito) window and navigate to the link you just copied. You should see your 404 Page (not found). That’s normal, because we’ve not written the last bit of code yet.
If you read through the code, you’ll see we’ve got a couple of functions that register and render our meta box, and we’ve got a function that takes $post_id
and returns a gobbledygook string that we can use as that post’s magic key.
The core PHP function
Edit “wpt-share-draft-link.php” again and replace the wptsdp_include_draft_posts()
function with the following:
function wptsdp_include_draft_posts($query) { if (!$query->is_main_query()) { // This is not the main query - don't alter the query object. } elseif (!$query->is_singular()) { // Make sure we're not in an archive page. } elseif (!array_key_exists(WPTSDP_KEY, $_GET)) { // The preview magic key is not specified in the URL. } elseif (empty(($post_id = $query->get('p')))) { // p=XXX is not specified in the URL. } elseif ($_GET[WPTSDP_KEY] !== wptsdp_get_magic_key_for_post($post_id)) { // The magic key is incorrect for this post. Uncomment the // next line to see what the magic key SHOULD be for this post. // error_log('KEY: ' . wptsdp_get_magic_key_for_post($post_id)); } else { $post_statuses = ['publish']; if (WPTSDP_SHARE_DRAFT) { $post_statuses[] = 'draft'; } if (WPTSDP_SHARE_PRIVATE) { $post_statuses[] = 'private'; } if (WPTSDP_SHARE_SCHEDULED) { $post_statuses[] = 'future'; } // Adjust the main query object to allow our posts statuses. $query->set('post_status', $post_statuses); // We need to set our post types here so that post types other // than "Post" will work. $query->set('post_type', WPTSDP_POST_TYPES); } }
Save your changes, then switch back to your private browsing window and reload the page. Your draft post should now be visible… If it’s not, double-check that “&wptsdp=…” is still in the URL.
That’s it. A simple addition to WordPress that’s genuinely useful for content creators who need to share links before publication.
Happy sharing 😎👍