Managing Posts with WordPress Plugin


📌 MemberPress: advanced WordPress plugin for subscriptions & membership sites

The WordPress backend is very flexible, and can easily be customized to accommodate a lot of different purposes. In this article by Vladimir Prelovac, we shall learn how to customize the Manage Panel. We will also explore the possibility of turning WordPress into a Content Management System (CMS), using methods provided to us by WordPress (for more info, read WordPress Plugin Development from Packt Publishing).

In this article, you will learn how to:

  • Customize the Manage Page output
  • Use the error message class to handle display of errors
  • Use built-in WordPress capabilities to handle user permissions

By the end of this article you will learn how WordPress can transform into a CMS.

Programming the Manage panel

The Manage Posts screen can be changed to show extra columns, or remove unwanted columns in the listing.

Let’s say that we want to show the post type—Normal, Photo, or Link. Remember the custom field post-type that we added to our posts? We can use it now to differentiate post types.

Time for action – Add post type column in the Manage panel

We want to add a new column to the Manage panel, and we will call it Type. The value of the column will represent the post type—Normal, Photo, or Link.

  1. Expand the admin_menu() function to load the function to handle Manage Page hooks:
    add_submenu_page('post-new.php', __('Add URL',
    $this->plugin_domain) , __('URL', $this->plugin_domain) , 1 ,
    'add-url', array(&$this, 'display_form') );
    // handle Manage page hooks
    add_action('load-edit.php', array(&$this, 'handle_load_edit') );
    }
  2. Add the hooks to the columns on the Manage screen:
    // Manage page hooks
    function handle_load_edit()
    {
     // handle Manage screen functions
     add_filter('manage_posts_columns', array(&$this,
     'handle_posts_columns'));
     add_action('manage_posts_custom_column', array(&$this,
     'handle_posts_custom_column'), 10, 2);
    }
  3. Then implement the function to add a new Column, remove the author and replace the date with our date format:
    // Handle Column header
    function handle_posts_columns($columns)
    {
     // add 'type' column
     $columns['type'] = __('Type',$this->plugin_domain);
     return $columns;
    }
  4. For date key replacement, we need an extra function:
    function array_change_key_name( $orig, $new, &$array )
    {
     foreach ( $array as $k => $v )
     $return[ ( $k === $orig ) ? $new : $k ] = $v;
     return ( array ) $return;
    }
  5. And finally, insert a function to handle the display of information in that column:
    // Handle Type column display
    function handle_posts_custom_column($column_name, $id)
    {
     // 'type' column handling based on post type
     if( $column_name == 'type' )
     {
     $type=get_post_meta($id, 'post-type', true);
     echo $type ? $type : __('Normal',$this->plugin_domain);
     }
    }
  6. Don’t forget to add the Manage page to the list of localized pages:
    // pages where our plugin needs translation
    $local_pages=array('plugins.php', 'post-new.php', 'edit.php');
    if (in_array($pagenow, $local_pages))

As a result, we now have a new column that displays the post type using information from a post custom field.

What just happened?

We have used the load-edit.php action to specify that we want our hooks to be assigned only on the Manage Posts page (edit.php). This is similar to the optimization we did when we loaded the localization files.

The handle_posts_columns is a filter that accepts the columns as a parameter and allows you to insert a new column:

function handle_posts_columns($columns)
{
 $columns['type'] = __('Type',$this->plugin_domain);
 return $columns;
}

You are also able to remove a column. This example would remove the Author column:

unset($columns['author']);

To handle information display in that column, we use the handle_posts_custom_column action.

The action is called for each entry (post), whenever an unknown column is encountered. WordPress passes the name of the column and current post ID as parameters.

That allows us to extract the post type from a custom field:

function handle_posts_custom_column($column_name, $id)
{
 if( $column_name == 'type' )
 {
 $type=get_post_meta($id, 'post-type', true);

It also allows us to print it out:

 echo $type ? $type : __('Normal',$this->plugin_domain);
 }
}

Modifying an existing column

We can also modify an existing column. Let’s say we want to change the way Date is displayed.

Here are the changes we would make to the code:

// Handle Column header
function handle_posts_columns($columns)
{
 // add 'type' column
 $columns['type'] = __('Type',$this->plugin_domain);
 // remove 'author' column
 //unset($columns['author']);
 // change 'date' column
 $columns = $this->array_change_key_name( 'date', 'date_new',
 $columns );
 return $columns;
}
// Handle Type column display
function handle_posts_custom_column($column_name, $id)
{
 // 'type' column handling based on post type
 if( $column_name == 'type' )
 {
 $type=get_post_meta($id, 'post-type', true);
 echo $type ? $type : __('Normal',$this->plugin_domain);
 }
 // new date column handling
 if( $column_name == 'date_new' )
 {
 the_time('Y-m-d <br > g:i:s a');
 }
 }
function array_change_key_name( $orig, $new, &$array )
{
foreach ( $array as $k => $v )
$return[ ( $k === $orig ) ? $new : $k ] = $v;
return ( array ) $return;
}

The example replaces the date column with our own date_new column and uses it to display the date with our preferred formatting.

Manage screen search filter

WordPress allows us to show all the posts by date and category, but what if we want to show all the posts depending on post type?

No problem! We can add a new filter select box straight to the Manage panel.

Time for action – Add a search filter box

  1. Let’s start by adding two more hooks to the handle_load_edit() function. The restrict_manage_posts function draws the search box and the posts_where alters the database query to select only the posts of the type we want to show.
    // Manage page hooks
    function handle_load_edit()
    {
     // handle Manage screen functions
     add_filter('manage_posts_columns',
     array(&$this, 'handle_posts_columns'));
     add_action('manage_posts_custom_column',
     array(&$this, 'handle_posts_custom_column'), 10, 2);
     // handle search box filter
    add_filter('posts_where',
    array(&$this, 'handle_posts_where'));
    add_action('restrict_manage_posts',
    array(&$this, 'handle_restrict_manage_posts'));
    }
  2. Let’s write the corresponding function to draw the select box:
    // Handle select box for Manage page
    function handle_restrict_manage_posts()
    {
     ?>
     <select name="post_type" id="post_type" class="postform">
     <option value="0">View all types</option>
     <option value="normal" <?php if( $_GET['post_type']=='normal')
     echo 'selected="selected"' ?>><?php _e
     ('Normal',$this->plugin_domain); ?></option>
     <option value="photo" <?php if( $_GET['post_type']=='photo')
     echo 'selected="selected"' ?>><?php _e
     ('Photo',$this->plugin_domain); ?></option>
     <option value="link" <?php if( $_GET['post_type']=='link')
     echo 'selected="selected"' ?>><?php _e
     ('Link',$this->plugin_domain); ?></option>
     </select>
     <?php
    }
  3. And finally, we need a function that will change the query to retrieve only the posts of the selected type:
    // Handle query for Manage page
    function handle_posts_where($where)
    {
     global $wpdb;
     if( $_GET['post_type'] == 'photo' )
     {
     $where .= " AND ID IN (SELECT post_id FROM {$wpdb->postmeta}
     WHERE meta_key='post-type' AND meta_value='".__
     ('Photo',$this->plugin_domain)."' )";
     }
     else if( $_GET['post_type'] == 'link' )
     {
     $where .= " AND ID IN (SELECT post_id FROM {$wpdb->postmeta}
     WHERE meta_key='post-type' AND meta_value='".__
     ('Link',$this->plugin_domain)."' )";
     }
     else if( $_GET['post_type'] == 'normal' )
     {
     $where .= " AND ID NOT IN (SELECT post_id FROM
     {$wpdb->postmeta} WHERE meta_key='post-type' )";
     }
     return $where;
    }_

What just happened?

We have added a new select box to the header of the Manage panel. It allows us to filter the post types we want to show.

We added the box using the restrict_manage_posts action that is triggered at the end of the Manage panel header and allows us to insert HTML code, which we used to draw a select box.

To actually perform the filtering, we use the posts_where filter, which is run when a query is made to fetch the posts from the database.

if( $_GET['post_type'] == 'photo' )
{
 $where .= " AND ID IN (SELECT post_id FROM {$wpdb->postmeta}
 WHERE meta_key='post-type' AND meta_value='".__
 ('Photo',$this->plugin_domain)."' )";

If a photo is selected, we inspect the WordPress database postmeta table and select posts that have the post-type key with the value, Photo.

At this point, we have a functional plugin. What we can do further to improve it is to add user permissions checks, so that only those users allowed to write posts and upload files are allowed to use it.

Quick reference
manage_posts_columns($columns): This acts as a filter for adding/removing columns in the Manage Posts panel. Similarly, we use the function, manage_pages_columns for the Manage Pages panel.
manage_posts_custom_column($column, $post_id): This acts as an action to display information for the given column and post. Alternatively, manage_pages_custom_column for Manage Pages panel.
posts_where($where): This acts as a filter for the where clause in the query that gets the posts.
restrict_manage_posts: This acts as an action that runs at the end of the Manage panel header and allows you to insert HTML.


Handling error messages

WordPress provides a simple class called WP_Error that allows us to keep all our error messages tidy and in one place.

Time for action – Adding support for errors

  1. Change the plugin constructor to add initialization of error class:
    if (version_compare($wp_version,"2.5","<"))
    {
     exit ($exit_msg);
    }
    // initialize the error class
    $this->error = new WP_Error();
    $this->init_errors(); // add admin_menu action add_action('admin_menu', array(&$this, 'admin_menu'));
  2. Initialize all our errors by defining the error code and the message:
    // Init error messages
    function init_errors()
    {
     $this->error->add('e_image', __('Please upload a valid
     image.',$this->plugin_domain));
     $this->error->add('e_title', __('You need to enter a title
     and add a photo.',$this->plugin_domain));
     $this->error->add('e_url', __('You need to enter a
     URL.',$this->plugin_domain));
    }
  3. Add a function to retrieve an error:
    // Retrieve an error message
    function my_error($e = '')
    {
     $msg = $this->error->get_error_message($e);
     if ($msg == null)
     {
     return __("Unknown error occured, please contact the
     administrator.", $this->plugin_domain);
     }
     return $msg;
    }
  4. Finally replace the old error messages with our new function:
     else
     $error=$this->my_error('e_image');
     }
     else // if file uploaded
     else
     $error=$this->my_error('e_title');
    }
    include( 'template/photo.php');
    else
     $error=$this->my_error('e_url');
    }
    include( 'template/link.php');

What just happened?

We used the integrated WordPress error class for displaying error messages.

We initialize the class in the plugin constructor and call the error message initialization function:

$this->error = new WP_Error();
$this->init_errors();

To add new errors, we use the add method of the error class, and define the message with code and text:

$this->error->add('e_image', __('Please upload a valid
image.',$this->plugin_domain));

We then created the my_error function that retrieves the error message using the get_error_message method:

$msg = $this->error->get_error_message($e);

Quick reference

WP_Error: This is the WordPress class for handling error messages. Use the add and get_error_message methods to store and retrieve messages.

User roles and capabilities

Let’s add some checks to our plugin to prevent unauthorized users from publishing posts and uploading files using our post templates.

This can be done using the WordPress capabilities system, providing us with functions to check if the user is allowed to perform a certain action.

Time for action – Add user capability checks

  1. Let’s add our first check, to see if the user can publish posts. All capability checks are performed using the current_user_can() function.
    We will include a new variable in the display_form() function to hold the new post’s status:
    if ($published)
    {
     check_admin_referer($page);
     $post_status = current_user_can('publish_posts') ?
    'publish' : 'pending'; }
  2. Change the Publish button in both the template files, link.php and photo.php, to show different text if the user can not publish:
    <div class="submitbox" id="submitpost">
     <div id="previewview"></div>
     <div class="inside"></div>
    <p class="submit"><input name="publish" type="submit"
    class="button button-highlighted" tabindex="4"
    value="<?php if (current_user_can('publish_posts'))
    _e('Publish', $this->plugin_domain); else _e('Submit',
    $this->plugin_domain); ?>" /></p> </div>
  3. Finally, edit the display_form() to check if the current user is allowed to upload files:
     // check permissions
    if (is_writable($uploads['path']) &&
    current_user_can('upload_files')) { $uploadfile=true; }

With the capabilities systems in place, we are now able to restrict the actions of users without enough privileges to publish the post or upload files to the server.

Congratulations! That was the last bit of code needed to finish our last plugin!

What just happened?

User capabilities provide a flexible model for checking the permissions of the user.

Roles and capabilities are interconnected, but the capabilities of a role can be changed dynamically by other plugins. So it is advisable to always check for capabilities instead of roles.

User capabilities are checked using the current_user_can($action) function, which accepts the desired action as a parameter. Actions can be edit_posts, or upload_plugins, and so on.

Bearing in mind that WordPress sites can be used by multiple users, you should always make sure to assign the correct capabilities for the features of you plugin.

Quick reference
current_user_can($action): This function is used to check if the current user is capable of performing a certain action.

A full reference for roles and capabilities is available at http://codex.wordpress.org/Roles_and_Capabilities.

Have a go Hero

With this plugin, we explored the core of the WordPress backend.

There are a few features you can consider now, that could improve this plugin further:

  • Include tags and perhaps categories to the post templates
  • Create more interesting post templates
  • For the photo template, you can download the image instead of linking to it, if the image is specified with a URL.
  • Create a mobile phone friendly admin panel by removing all other unnecessary menus and leaving only the quick post templates

Summary

The purpose of the Post Types plugin was to introduce you to different aspects of WordPress backend development.

We learned how to modify the Manage Posts panel to display the information we want. We also covered user capabilities, and how using them, we can make sure our plugin is working in multi-user environments.

Here are the most important lessons from this article:

  • Manage Panels: Customize the display of information to your liking, using custom columns and filters
  • Manage Errors: Use the WordPress WP_Error class to handle errors in your plugin
  • User capabilities: Use this to restrict access to functions for users without the relevant permissions

If you have read this article you may be interested to view :
WordPress Plugin Development (Beginner’s Guide)
  • Build powerful, interactive plug-ins for your blog and to share online
  • Everything you need to create and distribute your own plug-ins following WordPress coding standards
  • Walk through the development of six complete, feature-rich, real-world plug-ins that are being used by thousands of WP users
  • Written by Vladimir Prelovac, WordPress expert and developer of WordPress plug-ins such as Smart YouTube and Plugin Central
  • Part of Packt’s Beginners Guide series: expect step-by-step instructions with an emphasis on experimentation and tweaking code

http://www.packtpub.com/wordpress-plug-in-development/book


About the Author

Vladimir Prelovac is the author of many popular WordPress plugins and articles about WordPress optimization, security and maintenance. He actively uses WordPress platform as a base for Internet development strategy for small & mid-sized businesses.

For Vladimir, WordPress development is a full time job about which he happily blogs on his web site www.prelovac.com/vladimir.

More WordPress reading: