Widget with random posts from a blog for external sites

To create a special output of random posts:

  1. Register an endpoint to the root of your blog. See A (Mostly) Complete Guide to the WordPress Rewrite API for details.
  2. Refresh the permalink settings. I would do this on (de)activation only.
  3. Hook into 'template_redirect' and return your output depending on the details of the requested endpoint. Then exit.

That’s all.

Well … some code may help to understand the details. 🙂

Let’s build a small plugin for HTML and JSON output.

I have named it T5 Random Posts Endpoint; all code goes into a class with static methods to keep the global namespace clean.

add_action( 'init', array ( 'T5_Random_Posts_Endpoint', 'init' ) );

/**
 * Build a root endpoint.
 */
class T5_Random_Posts_Endpoint
{
    /**
     * Register endpoint and actions.
     *
     * @wp-hook 'init'
     * @return  void
     */
    public static function init()
    {
        add_rewrite_endpoint( 'randomposts', EP_ROOT );
        add_filter( 'request', array ( __CLASS__, 'filter_request' ) );
        add_action( 'template_redirect', array ( __CLASS__, 'render' ) );
    }
}

As you can see, we call a central init method on 'init' to … initialize our plugin. Creative naming, huh?

The endpoint is named randomposts and attached to the root. So after activation you can get the out put on example.com/randomposts/ or example.com/randomposts/json/.

But … if you call the first URL the endpoint works and the request variable randomposts is set – but it is empty. That’s why we hook into 'request' and give the variable a default value we can work with later. Let’s use html as default:

    /**
     * Prepare the endpoint variable so it has always a value.
     *
     * @wp-hook 'request'
     * @param   array $request
     * @return  array
     */
    public static function filter_request( $request )
    {
        if ( isset ( $request['randomposts'] )
            and empty ( $request['randomposts'] )
        )
        {
            $request['randomposts'] = 'html';
        }

        return $request;
    }

Now we hook into 'template_redirect' so WordPress will not use the default template index.php but our own output.

    /**
     * Create output.
     *
     * @wp-hook 'template_redirect'
     * @return  void
     */
    public static function render()
    {
        // This is not our endpoint.
        if ( '' === $type = get_query_var( 'randomposts' ) )
        {
            return;
        }

        // Someone is poking around.
        // You can extend this and build different output variants.
        if ( ! in_array( $type, array ( 'html', 'json' ) ) )
        {
            status_header( 414 );
            print 'Unsupported Media Type. Use "html" or "json" only.';
            exit;
        }

        // Empty blog?
        if ( ! $posts = get_posts( array ( 'numberposts' => 10, 'orderby' => 'rand' ) ) )
        {
            status_header( 404 );
            print 'No posts found.';
            exit;
        }

        self::$type( self::prepare_post_data( $posts ) );
        exit;
    }

First we check if we are really on our endpoint by looking into get_query_var( 'randomposts' ). We filled this variable in our request filter, so if it is still empty, we know for sure that is not our business and we return quickly.

Then we test if the request was for one of our predefined output variants html or json. If someone is calling /randomposts/css or /randomposts/lala we raise a 414 error with a helpful message. And exit.

Then we get finally our posts with get_posts() and remove all the data we don’t need in a dedicated method named prepare_post_data():

    /**
     * Build a simple array with just titles and permalinks.
     *
     * @wp-hook 'template_redirect'
     * @param   array $posts
     * @return void
     */
    protected static function prepare_post_data( $posts )
    {
        $data = array ();

        foreach ( $posts as $post )
        {
            if ( empty ( $post->post_title) )
            {
                continue;
            }

            $data[ $post->ID ] = array (
                'title' => strip_tags( $post->post_title ),
                'url'   => get_permalink( $post->ID )
            );
        }

        return $data;
    }

We pass this simple array to a method named exactly like our output: html() or json().
Let’s start with json(); that’s the easy part. See How to encode post content as JSON? for some background.

    /**
     * Render JSON output
     *
     * @wp-hook 'template_redirect'
     * @param  array $data
     * @return void
     */
    protected static function json( array $data )
    {
        header( 'Content-type: application/json' );
        print json_encode( $data );
    }

Easy, isn’t it? The html() is not that much harder, just longer:

        /**
         * Render HTML output
         *
         * @wp-hook 'template_redirect'
         * @param  array $data
         * @return void
         */
        protected static function html( array $data )
        {
            ?>
<!doctype html>
<title><?php bloginfo( 'name' ); ?></title>
<meta name="viewport" content="width=device-width; initial-scale=1.0">
<?php
// Just a sample style. Be creative! :)
?>
<style>
body, body *
{
    display:         block;
    font:            1em/1.4 Calibri, sans-serif;
    list-style:      none;
    margin:          0;
    padding:         0;
    text-decoration: none;
}
a
{
    background:      #f5f5f5;
    border-bottom:   1px solid #ddd;
    color:           #333;
    padding:         5px 10px;
}
a:hover, a:focus
{
    background:      #333;
    color:           #f5f5f5;
}
h1 a
{
    font-size:      1.2em;
    font-weight:    bold;
}
</style>
<h1><a href="https://wordpress.stackexchange.com/questions/57155/<?php bloginfo("url' ); ?>"><?php bloginfo( 'name' ); ?></a></h1>
<ul>
<?php
foreach ( $data as $post )
{
    print '<li><a href="' . $post['url'] . '">' . $post['title'] . '</a></li>';
}
?>
</ul>
<?php
        }

This is how it looks with the theme test data:

enter image description here

Now our output is finished – we are still in render() – and we call exit because otherwise WordPress would include the index.php from the active theme.

Now we need just a method to refresh the permalink settings:

    /**
     * Reset permalinks.
     *
     * @wp-hook 'activate_' . __FILE__
     * @wp-hook 'deactivate_' . __FILE__
     * @return  void
     */
    public static function flush_rewrite_rules()
    {
        remove_action( 'init', array ( __CLASS__, 'init' ) );
        add_action( 'init', 'flush_rewrite_rules', 11 );
    }

We prevent our default init hook here because on activation we are on the plugin list where we don’t need the endpoint, and on deactivation we do not want to re-register the now unused permalink.

We attach this method to (de)activation with:

// refresh permalink settings
register_activation_hook(
    __FILE__ ,
    array ( 'T5_Random_Posts_Endpoint', 'flush_rewrite_rules' )
);
register_deactivation_hook(
    __FILE__ ,
    array ( 'T5_Random_Posts_Endpoint', 'flush_rewrite_rules' )
);

And now we can say: That’s all. Really.

If you made it this far grab the complete plugin from GitHub.

Leave a Comment