Developing Post Types Plugin with WordPress

Exclusive offer: get 50% off this eBook here
WordPress Plugin Development: Beginner's Guide

WordPress Plugin Development: Beginner's Guide — Save 50%

Build powerful, interactive plug-ins for your blog and to share online

$23.99    $12.00
by Vladimir Prelovac | February 2009 | AJAX Content Management Open Source Web Development WordPress

In this article by Vladimir Prelovac, we are going to dig deeper into the WordPress engine and discover ways to modify various aspects of the backend to match our specific needs with the help of the Post Types plugin.

Although WordPress is made primarily for the purpose of handling a blog, this basic functionality can be easily expanded to handle almost anything you want. The WordPress backend is very flexible, and can be customized to accommodate a lot of different purposes. For example, you could create a job portal or an e-commerce quite easily with WordPress, and those are just some of the possibilities.

Specifically, you will learn how to:

  • Implement localization support for users of other languages
  • Customize menus and submenus to change the way the WordPress backend looks
  • Handle file and image uploads
  • Use custom fields to add custom hidden information to your posts

And you will do all of these by developing a Post Types plugin that provide pre-defined post templates to add a photo or a link quickly to your blog.

WordPress Plugin Development: Beginner's Guide

The concepts you will learn in this article will help you discover the not so obvious capabilities of the WordPress platform that allows you to transform it into software—capable of handling much more than just a blog.

Handling localization

Localization is an important part of WordPress development as not everyone using WordPress speaks English (WordPress comes in different languages too).

Localization involves just a small amount of the extra work on your side, since the translation itself is usually done by volunteers (people who like and use your plugin).

You only need to provide some base files for translation, and don't be surprised when you start getting translated files sent to your inbox.

WordPress uses the GNU gettext localization framework, which is a standardized method of managing translations, and we will make use of it in our plugin.

Time for action – Create plugin and add localization

We will start by defining our plugin as usual, and then add localization support.

  1. Create a new folder called post-types.
  2. Create a new post-types.php file with the following content:
    <?php
    // pluginname Post Types
    // shortname PostTypes
    // dashname post-types
    /*
    Plugin Name: Post Types
    Version: 0.1
    Plugin URI:
    http://www.prelovac.com/vladimir/wordpress-plugins/post-types
    Author: Vladimir Prelovac
    Author URI: http://www.prelovac.com/vladimir
    Description: Provides pre-defined post templates to quickly add a
    photo or a link to your blog
    */
    // Avoid name collisions.
    if ( !class_exists('PostTypes') ) :
    class PostTypes
    {
    // localization domain
    var $plugin_domain='PostTypes';
    // Initialize the plugin
    function PostTypes()
    {
    global $wp_version;
    $exit_msg='Post Types requires WordPress 2.5 or newer.
    <a href="http://codex.wordpress.org/Upgrading_WordPress">
    Please update!</a>';
    if (version_compare($wp_version,"2.5","<"))
    {
    exit ($exit_msg);
    }
    }
    // Set up default values
    function install()
    {
    }
    }
    endif;
    if ( class_exists('PostTypes') ) :
    $PostTypes = new PostTypes();
    if (isset($PostTypes))
    {
    register_activation_hook( __FILE__, array(&$PostTypes,
    'install') );
    }
    endif;
  3. Adding localization is fairly simple. First we need to add a function to our class that will load the translation file:
    // Localization support
    function handle_load_domain()
    {
    // get current language
    $locale = get_locale();
    // locate translation file
    $mofile = WP_PLUGIN_DIR.'/'.plugin_basename(dirname
    (__FILE__)).'/lang/' . $this->plugin_domain . '-' .
    $locale . '.mo';
    // load translation
    load_textdomain($this->plugin_domain, $mofile);
    }
  4. Since loading the file takes resources, we will load it only when the translation is actually needed by checking the current page ($pagenow) and the list of pages pages where we need translations ($local_pages array):
    // Initialize the plugin
    function PostTypes()
    {
    global $wp_version, $pagenow;
    // pages where our plugin needs translation
    $local_pages=array('plugins.php');
    if (in_array($pagenow, $local_pages))
    $this->handle_load_domain();
    $exit_msg='Post Types requires WordPress 2.5 or newer.
    <a href="http://codex.wordpress.org/Upgrading_WordPress">
    Please update!</a>';
  5. Finally, to use the available translations, we only need to enclose our text in the __() function:
    $this->handle_load_domain();
    $exit_msg=__('Post Types requires WordPress 2.5 or newer.
    <a href="http://codex.wordpress.org/Upgrading_WordPress">
    Please update!</a>', $this->plugin_domain);
    if (version_compare($wp_version,"2.5","<"))

What just happened?

We have added localization support to our plugin by using the provided localization functions provided by WordPress.

Currently, we have only localized the error message for WordPress version checking:

$exit_msg=__('Post Types requires WordPress 2.5 or newer.
<a href="http://codex.wordpress.org/Upgrading_WordPress">
Please update!</a>', $this->plugin_domain);

We have done that by enclosing the text in the __() function, which takes the text as localized, and enclosing our unique localization domain or context within the WordPress localization files.

To load localization, we created a handle_load_domain function.

The way it works is to first get the current language in use by using the get_locale() function:

// Localization support
function handle_load_domain()
{
// get current language
$locale = get_locale();

Then it creates the language file name by adding together the plugin dir, plugin folder, and the lang folder where we will keep the translations. The file name is derived from the locale, and the *.mo language file extension:

// locate translation file
$mofile = WP_PLUGIN_DIR.'/'.plugin_basename
(dirname(__FILE__)).'/lang/' . $this->plugin_domain .
'-' . $locale . '.mo';

Finally, the localization file is loaded using the load_textdomain() function, taking our text domain and .mo file as parameters.

// load translation
load_textdomain($this->plugin_domain, $mofile);

Optimizing localization usage

The translation file needs to be loaded as the first thing in the plugin—before you output any messages. So we have placed it as the first thing in the plugin constructor.

Since loading the translation file occurs at the beginning of the constructor, which is executed every time, it is a good idea to select only the pages where the translation will be needed in order to preserve resources.

WordPress provides the global variable, $pagenow, which holds the name of the current page in use.

We can check this variable to find out if we are on a page of interest. In the case of plugin activation error message, we want to check if we are on the plugins page defined as plugins.php in WordPress:

// pages where our plugin needs translation
$local_pages=array('plugins.php');
if (in_array($pagenow, $local_pages))
$this->handle_load_domain();

You can optimize this further by querying the page parameter, if it exists, as this will—in most cases—point precisely to the usage of your page (plugins.php?page=photo):

if ($_GET['page']=='photo')

Optimizing the usage of the translation file is not required; it's just a matter of generally loading only what you need in order to speed up the whole system.

How does localization work?

For localization to work, you need to provide .po and .mo files with your plugins. These files are created by using external tools such as PoEdit.

These tools output the compiled translation file, which can be then loaded by using the load_textdomain() function. This function accepts a language domain name and a path to the file.

In order to use translated messages, you can use the __($text, $domain) and _e($text, $domain) functions. The _e() function is just an equivalent of

echo __();

These functions accept two parameters, the first being the desired text, and the second, the language domain where the message will be looked for.

If no translation was found, the text is just printed out as it is. This means that you can always safely use these functions, even if you do not provide any translation files. This will prepare the plugin for future translation.

Quick reference
$pagenow: A global variable holding the name of the currently displayed page within WordPress.
get_locale(): A function which gets the currently selected language.
load_textdomain(domain, filepath): This function loads the localization file and adds it to the specified language domain identifier.
_(); _e(): These functions are used to find the output text using a given language domain.
More information about WordPress localization is available at: http://codex.wordpress.org/Translating_WordPress
.

WordPress Plugin Development: Beginner's Guide Build powerful, interactive plug-ins for your blog and to share online
Published: February 2009
eBook Price: $23.99
Book Price: $39.99
See more
Select your format and quantity:

Adding a post template

Our next goal is to add a simple post template to the WordPress Write page.

The purpose of a custom post template is to handle writing a quick post in case we don't need the whole Write Post interface, for reasons of speed or customization.

The great thing about post templates is that they can be customized to accept any information you want—for example, job portal listings or inventory items.

Let's start with a simple Add Photo template where we want to be able to just set the title, specify a photo and publish immediately.

Time for action – Create 'add photo' post template

  1. In order to create the post template, we need a place for it in the menus. We will use the Write menu and place a submenu there:
    // add admin_menu action
    add_action('admin_menu', array(&$this, 'admin_menu'));
    }
    // Hook the admin menu
    function admin_menu()
    {
    // submenu pages
    add_submenu_page('post-new.php', __('Add Photo',
    $this->plugin_domain) , __('Photo', $this->plugin_domain) , 1 ,
    'add-photo', array(&$this, 'display_form') );
    }
  2. Since we have localized text on our new page, we want to make sure that the localization file is loaded on this page, so we will add it to our $local_pages array:
    function PostTypes()
    {
    global $wp_version, $pagenow;
    // pages where our plugin needs translation
    $local_pages=array('plugins.php', 'post-new.php');
    if (in_array($pagenow, $local_pages))
  3. Let's create the template/ folder and call our new write post template, photo.php.
  4. We will start the template with the boxes that display the information:
    <div class="wrap">
    <?php if (!empty($error)) : ?>
    <div id="message" class="error fade">
    <p><?php echo $error; ?></p>
    </div>
    <?php elseif (!empty($published_id)) : ?>
    <div id="message" class="updated fade">
    <p><strong><?php _e('Photo added.',$this->plugin_domain);
    ?></strong> <a href="<?php echo get_permalink(
    $published_id);
    ?>"><?php _e('View post',$this->plugin_domain); ?>
    &raquo;</a></p>
    </div>
    <?php endif; ?>
    <h2><?php _e('Add Photo',$this->plugin_domain); ?></h2>
    <form action="" method="post" enctype="multipart/form-data">
    <?php wp_nonce_field($_GET['page']);
  5. Then add the Publish button using the WordPress CSS classes:
    <div id="poststuff">
    <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="5" value="
    <?php _e('Publish', $this->plugin_domain); ?>" /></p>
    </div>
  6. Finally, we add the input fields to the template—title, photo URL or upload field, and description:
    <div id="post-body">
    <div id="titlediv">
    <h3><?php _e('Title',$this->plugin_domain); ?></h3>
    <div id="titlewrap"><input type="text" name="title" tabindex
    ="1" value="<?php echo $title; ?>" id="title" /></div>
    </div>
    <div class="postbox ">
    <h3><?php _e('Photo',$this->plugin_domain); ?></h3>
    <div class="inside">
    <p>
    <label for="url"><?php _e('Enter URL:
    ',$this->plugin_domain);
    ?></label><br />
    <input style="width: 415px" tabindex="2" type="text"
    name="url" id="url" value="<?php echo $url; ?>" />
    </p>
    <?php if ($uploadfile) : ?>
    <p>
    <label for="upload"><?php _e('or Upload Photo:',$this-
    >plugin_domain); ?></label><br />
    <input type="file" tabindex="3" name="upload" id=
    "upload" />
    </p>
    <?php endif; ?>
    </div>
    </div>
    <div class="postbox">
    <h3><?php _e('Description (optional)'
    ,$this->plugin_domain); ?>
    </h3>
    <div class="inside">
    <textarea name="description" id="description" rows="5"
    style="width: 415px" tabindex="4"><?php echo
    $description; ?>
    </textarea>
    </div>
    </div>
    </div>
    </div>
    </form>
    </div>
  7. The template is done! To display it, we will create a display_form() function in our main plugin class.
    // Display the Post form
    function display_form()
    {
    global $wpdb;
    $page=$_GET['page'];
    switch ($page) :
    case 'add-photo':
    include( 'template/photo.php');
    break;
    endswitch;
    }

The function does not process any information yet; it just prints out the template.

The end result is our new post template in the Write menu.

WordPress Plugin Development: Beginner's Guide

What just happened?

We have just created a quick photo post template. For the sake of simplicity, the form has been designed to have only three fields.

When creating a backend form, you can design it any way you want, but you can also decide to use the WordPress CSS classes. If you go for WordPress classes, your forms will blend into the backend and look more professional.

Backend CSS classes

The standard classes of WordPress Backend CSS (up to version 2.6) use wrap for displaying forms, and use poststuff to create the wrapper

submitbox is the right-hand side column with the Publish button that contains extra classes such as previewview and inside, and the button itself is of the class, submit:

<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="5" value="
<?php _e('Publish', $this->plugin_domain); ?>" /></p>
</div>

The main form classes are post-body followed by titlediv and titlewrap for handling post titles:

<div id="titlediv">
<h3><?php _e('Title',$this->plugin_domain); ?></h3>
<div id="titlewrap"><input type="text" name="title" tabindex=
"1" value="<?php echo $title; ?>" id="title" /></div>
</div>

Finally, we have postbox and inside for handling fields and groups of information:

<div class="postbox">
<h3><?php _e('Description (optional)',$this->
plugin_domain); ?></h3>
<div class="inside">
<textarea name="description" id="description" rows="5" style=
"width: 415px" tabindex="4"><?php echo $description; ?>
</textarea>
</div>
</div>

Since the WordPress backend evolves quickly, you should check the styling classes on all the new versions and adjust your plugin accordingly.

Handling file and image uploads

WordPress provides ready functions for handling file uploads and image manipulation.

It is always best practice to use built-in WordPress functions for checking the file, moving it and setting file permissions. We will use these functions in the following example.

Time for action – Handle uploaded image

  1. Let's add a function to handle images uploaded by the user. The function uses the built-in wp_handle_upload() function:
    function handle_image_upload($upload)
    {
    // check if image
    if (file_is_displayable_image( $upload['tmp_name'] ))
    {
    // handle the uploaded file
    $overrides = array('test_form' => false);
    $file=wp_handle_upload($upload, $overrides);
    }
    return $file;
    }
  2. Now, we need to include the image to our post. Let's expand the display_form() function to include handling of uploaded images and inserting a new post with attachment:
    // Display the Post form
    function display_form()
    {
    global $wpdb;
    $page=$_GET['page'];
    $published=isset($_POST['publish']);
    $title=$_POST['title'];
    $description=$_POST['description'];
    if ($published)
    {
    check_admin_referer($page);
    $post_status='publish';
    }
    switch ($page) :
    case 'add-photo':
    // WordPress upload dir (wp-content/uploads)
    $uploads = wp_upload_dir();
    // check permissions
    if (is_writable($uploads['path']))
    {
    $uploadfile=true;
    }
    $url=$_POST['url'];
    $upload=$_FILES['upload'];
    if ($published)
    {
    if (!empty($title) && (!empty($upload['tmp_name']) ||
    !empty($url)))
    {
    // if file uploaded
    if ($upload['tmp_name'])
    {
    // handle uploaded image
    $file=$this->handle_image_upload($upload);
    if ($file)
    {
    $image_url=$file['url'];
    // create a thumbnail
    $size='medium';
    $resized = image_make_intermediate_size( $file
    ['file'], get_option("{$size}_size_w"), get_option
    ("{$size}_size_h"), get_option("{$size}_crop") );
    if ($resized)
    $image_src='//dgdsbygo8mp3h.cloudfront.net/sites/default/files/blank.gif' data-original=$uploads['url'] .'/'.$resized['file'];
    else
    $image_src='//dgdsbygo8mp3h.cloudfront.net/sites/default/files/blank.gif' data-original=$image_url;
    $image_uploaded=true;
    }
    else
    $error=__('Please upload a valid image.',$this-
    >plugin_domain);
    }
    else // if file uploaded
    {
    $image_url=$url;
    $image_src='//dgdsbygo8mp3h.cloudfront.net/sites/default/files/blank.gif' data-original=$url;
    }
    if (!$error)
    {
    // create post content
    $content='<a href="'.$image_url.'"><img src='//dgdsbygo8mp3h.cloudfront.net/sites/default/files/blank.gif' data-original="'.
    $image_src.'"></a><p>'.$description.'</p>';
    // post information
    $data = array
    (
    'post_title' => $wpdb->escape($title),
    'post_content' => $wpdb->escape($content),
    'post_status' => $post_status
    );
    // insert post
    $published_id = wp_insert_post($data);
    // add a custom field
    add_post_meta($published_id, "post-type", __
    ('Photo',$this->plugin_domain)
    if ($image_uploaded)
    {
    $attachment = array
    (
    'post_mime_type' => $file['type'],
    'guid' => $image_url,
    'post_parent' => $published_id,
    'post_title' => $wpdb->escape($title),
    'post_content' => $wpdb->escape($description),
    );
    // insert post attachment
    $aid = wp_insert_attachment($attachment, $file
    ['file'], $published_id);
    // update metadata
    if ( !is_wp_error($aid) )
    {
    wp_update_attachment_metadata
    ( $aid, wp_generate_attachment_metadata
    ( $aid, $file['file'] ) );
    }
    }
    // clear all fields
    $title=''; $url=''; $description='';
    }
    }
    else
    $error=__('You need to enter a title and add a
    photo.',$this->plugin_domain);
    }
    include( 'template/photo.php');
    break;
    endswitch;
    }

The above code enables functionality for checking uploads, handling the uploaded file, and adding it to the post. This means that you can now start creating photo posts:

WordPress Plugin Development: Beginner's Guide

By doing this, the photo post will show up in your blog:

WordPress Plugin Development: Beginner's Guide

What just happened?

We have added support for uploading the files and also inserted the image to our post.

Let's analyze how this works.

First, we want to check if the upload dir is writeable. The location of the WordPress upload dir can be obtained by using the wp_upload_dir() function:

switch ($page) :
case 'add-photo':
// WordPress upload dir (wp-content/uploads)
$uploads = wp_upload_dir();

If the folder is writeable, we will show the file upload field on the form by setting the $uploadfile variable to true.

// WordPress upload dir (wp-content/uploads)
$uploads = wp_upload_dir();
// check permissions
if (is_writable($uploads['path']))
{
$uploadfile=true;
}

Now comes the part when we check if the post was published properly, and if there was an image uploaded with it.

The information about uploaded file(s) is stored in the global PHP $_FILES variable, and we are interested in the upload reference as that is the name of our upload file field.

$url=$_POST['url'];
$upload=$_FILES['upload'];
if ($published)
{
if (!empty($title) && (!empty($upload['tmp_name']) || !empty($url)))
{
// if file uploaded
if ($upload['tmp_name'])
{
// handle uploaded image
$file=$this->handle_image_upload($upload);

The handle_image_upload() function uses another built-in WordPress function to checkif the uploaded file—file_is_displayable_image()—is really an image.

function handle_image_upload($upload)
{
// check if image
if (file_is_displayable_image( $upload['tmp_name'] ))
{

Further on, we call the wp_handle_upload() function. The purpose of this function is to check the uploaded file (type, size etc) and move it to the WordPress uploads folder.

You can specify certain overrides; in this case we are overriding the test for form action, as we are using a custom form.

if (file_is_displayable_image( $upload['tmp_name'] ))
{
// handle the uploaded file
$overrides = array('test_form' => false);
$file=wp_handle_upload($upload, $overrides);
}
return $file;
}

The wp_handle_upload() function handles everything for our file, including moving it to the upload directory and setting correct permissions. So, we do not need to worry about it. It also returns a reference to the newly created file, which contains useful information such as the new filename and a URL to the file, which we will need for displaying the photo.

In the next piece of code back in the main function, we will create a thumbnail of the uploaded image to show it in the post and link it to the original picture.

Let's see how this is done:

if ($file)
{
$image_url=$file['url'];
// create a thumbnail
$size='medium';
$resized = image_make_intermediate_size( $file['file'],
get_option("{$size}_size_w"), get_option("{$size}_size_h"),
get_option("{$size}_crop") );
if ($resized)
$image_src='//dgdsbygo8mp3h.cloudfront.net/sites/default/files/blank.gif' data-original=$uploads['url'] .'/'.$resized['file'];
else
$image_src='//dgdsbygo8mp3h.cloudfront.net/sites/default/files/blank.gif' data-original=$image_url;
$image_uploaded=true;
}
else
$error=__('Please upload a valid image.',$this->plugin_domain);
}

We use the WordPress image_make_intermediate_size() function, which accepts the filename and resizing information as parameters. We chose to use the medium size, which is already defined in the WordPress backend; height and width are stored in the medium_size_h and medium_size_w options, respectively.

Settings for the upload folder (Uploading) and Image sizes can be found on the WordPress Miscellaneous Settings page:

We also need to handle cases where the picture is specified by a URL, in which case we will just use the provided information:

 else
$error=__('Please upload a valid image.',$this->plugin_domain);
}
else // if file uploaded
{
$image_url=$url;
$image_src='//dgdsbygo8mp3h.cloudfront.net/sites/default/files/blank.gif' data-original=$url;
}

Now, when we have the picture information, we can create the post content and insert a new post:

// create post content
$content='<a href="'.$image_url.'"><img src='//dgdsbygo8mp3h.cloudfront.net/sites/default/files/blank.gif' data-original="'.$image_src.'">
</a><p>'.$description.'</p>';
// post information
$data = array
(
'post_title' => $wpdb->escape($title),
'post_content' => $wpdb->escape($content),
'post_status' => $post_status
);
// insert post
$published_id = wp_insert_post($data);

We will insert a custom field for our post, call it post-type and specify that it is a Photo.

// add a custom field
add_post_meta($published_id,
"post-type",__('Photo',$this->plugin_domain));

Custom fields allow us to insert any type of information to WordPress posts and pages, and in fact allow us transform WordPress into a CMS. We will get back to the custom fields in more detail later.

We have uploaded and processed the image. If we want to follow it up all the way, we want to add our photo to the WordPress Media Library by using the wp_insert_attachment function.

if ($image_uploaded)
{
$attachment = array
(
'post_mime_type' => $file['type'],
'guid' => $image_url,
'post_parent' => $published_id,
'post_title' => $wpdb->escape($title),
'post_content' => $wpdb->escape($description),
);
// insert post attachment
$aid = wp_insert_attachment($attachment, $file['file'],
$published_id);
// update metadata
if ( !is_wp_error($aid) )
{
wp_update_attachment_metadata( $aid,
wp_generate_attachment_metadata( $aid, $file['file'] ) );
}

After we have done this, all new photos will turn up in the in the WordPress Media Library (Manage Media) looking like this:

Quick reference
wp_handle_upload(&$file, $overrides): A function which handles user uploaded files. Takes information provided by the $_FILES[] variable and the desired overrides (test_form, test_type, test_size).
file_is_displayable_image($file): It checks if the file is an image that WordPress can display.
wp_upload_dir(): A function which returns the path to the WordPress uploads folder.
image_make_intermediate_size($file, $width, $height, $crop=false): A function which resizes the image and returns the new file metadata.
add_post_meta($post_id, $meta_key, $meta_value, $unique): The function used to add a custom field to the specified post. The field is identified with a key ($meta_key) and its value ($meta_value).
wp_insert_attachment($attachment, $filename, $parent_post_id): This function inserts an attachment for a post, into the Media Library. More information can be found at: http://codex.wordpress.org/Function_Reference/wp_insert_attachment
wp_update_attachment_metadata($post_id, $data): This function updates attachment metadata, usually used in conjunction with wp_generate_attachment_metadata.
wp_generate_attachment_metadata($attachment_id, $file): This function generates the post Image attachment Metadata.

WordPress Plugin Development: Beginner's Guide Build powerful, interactive plug-ins for your blog and to share online
Published: February 2009
eBook Price: $23.99
Book Price: $39.99
See more
Select your format and quantity:

Using custom fields

Custom fields are used in WordPress to store additional information about a post. They are normally available to you in the Write Post screen as Key/Value pairs.

WordPress Plugin Development: Beginner's Guide

You can use custom fields to display information on the site or for some other kind of special processing (like storing a post's expiration date).

Adding custom fields

To add a custom field from the code, you can use the add_post_meta() function:

add_post_meta($post_id, $meta_key, $meta_value, $unique)
  • post_id: This parameter contains the ID of the post where the custom field is to be added.
  • meta_key: This parameter contains the key identifier (such as mood, job-type).
  • meta_value: This parameter contains the value for the key (such as happy,temporarily, and so on).
  • unique: This parameter contains is used to decide whether you want this key to be unique. WordPress supports multiple values for the same key, and if the value set is unique, only one instance for the given key is allowed.
  • prev_value: This contains the previous value of the key, which helps us to differentiate the values if the key is not unique.

To update existing fields, you can use the update_post_meta() function:

update_post_meta($post_id, $meta_key, $meta_value, $prev_value)

Retrieving custom fields

To retrieve custom fields, you can use one of the following functions:

get_post_custom($post_id): This function returns a multidimensional array with all the custom fields of a particular post or page.

get_post_custom_keys($post_id): This function returns an array containing the keys of all custom fields of a particular post or page.

get_post_custom_values($key, $post_id): This function gets the list of values for a particular key on the current post.

get_post_meta($post_id, $key, $single = false): This function gets the value for the specified key in a given post. If $single is true, then only the first key is returned (even if there are more).

For example:

get_post_meta(223, 'mood', false);

Quick reference
More on Custom Fields – http://codex.wordpress.org/Using_Custom_Fields

Quick post a link

Adding the link post type is easy when we know how to display the form and handle it. So, let's quickly add the link template to our plugin:

Time for action – Add link template

  1. Lets create template/link.php file :
    <div class="wrap">
    <?php if (!empty($error)) : ?>
    <div id="message" class="error fade">
    <p><?php echo $error; ?></p>
    </div>
    <?php elseif (!empty($published_id)) : ?>
    <div id="message" class="updated fade">
    <p><strong><?php _e('Link added.',$this->plugin_domain);
    ?>
    </strong> <a href="<?php echo get_permalink(
    $published_id);
    ?>"><?php _e('View post',$this->plugin_domain); ?>
    &raquo;</a></p>
    </div>
    <?php endif; ?>
    <h2><?php _e('Add Link',$this->plugin_domain); ?></h2>
    <form action="" method="post">
    <?php wp_nonce_field($_GET['page']); ?>
    <div id="poststuff">
    <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
    _e('Publish', $this->plugin_domain); ?>" /></p>
    </div>
    <div id="post-body">
    <div id="titlediv">
    <h3><?php _e('Title (optional)',$this->plugin_domain); ?>
    </h3>
    <div id="titlewrap"><input type="text" name="title" tabindex=
    "1" value="<?php echo $title; ?>" id="title" /></div>
    </div>
    <div class="postbox ">
    <h3><?php _e('URL',$this->plugin_domain); ?></h3>
    <div class="inside">
    <p>
    <input style="width: 415px" type="text" tabindex="2"
    name="url" id="url" value="<?php echo $url ?>" />
    </p>
    </div>
    </div>
    <div class="postbox ">
    <h3><?php _e('Description (optional)',
    $this->plugin_domain); ?></h3>
    <div class="inside">
    <textarea name="description" id="description" rows="5"
    style="width: 415px" tabindex="3"><?php echo $description ?>
    </textarea>
    </div>
    </div>
    </div>
    </div>
    </form>
    </div>
  2. Then, add a new submenu page for the Write screen :
    // Hook the admin menu
    function admin_menu()
    {
    // submenu pages
    add_submenu_page('post-new.php', __('Add Photo',$this-
    >plugin_domain) , __('Photo', $this->plugin_domain) , 1
    , 'add-photo', array(&$this, 'display_form') );
    add_submenu_page('post-new.php', __('Add URL', $this-
    >plugin_domain) , __('URL', $this->plugin_domain) , 1 ,
    'add-url', array(&$this, 'display_form') );
    }
  3. And finally, add the code to the display_form() switch/case to handle the rendering of the link form:
    include( 'template/photo.php');
    break;
    case 'add-url':
    $url=$_POST['url'];
    if ($published)
    {
    if (!empty($url))
    {
    if (empty($title))
    $title=$url;
    $content='<a href="'.$url.'">'.$title.'</a>
    <p>'.$description.'</p>';
    $data = array
    (
    'post_title' => $wpdb->escape($title),
    'post_content' => $wpdb->escape($content),
    'post_status' => $post_status
    );
    // insert post
    $published_id = wp_insert_post($data);
    // add a custom field
    add_post_meta($published_id, "post-type", __
    ('Link',$this->plugin_domain));
    // clear all fields
    $title=''; $url=''; $description='';
    }
    else
    $error=__('You need to enter a URL.',$this->plugin_domain);
    }
    include( 'template/link.php');
    break;
    endswitch;

With that done, we now have a second post-type ready:

WordPress Plugin Development: Beginner's Guide

What just happened?

We have added another post template using the previous form as a starting point. The code is very similar, so we will not go into the details.

The recipe for adding further forms is:

  • Create a new form based on a previous one and edit the form fields.
  • Add the submenu page in the admin_menu() function.
  • Add the form-specific handling code in the switch/case of display_form().

You can add as many forms as you like, for example to enter simple text, video, a quote, and so on.

Tinkering with WordPress backend menus

Since we added the URL page under the Write menu, and there is already a Link page there (for adding links to the blogroll), we would like to remove the Link page from the menus to prevent confusion.

You can easily do this (in fact, you can totally rearrange WordPress menus in any way you like).

Time for action - Remove 'Link' from the Write page

  1. Removing an item from a menu is a simple matter. Lets add this code to the admin_menu() function:
    // Hook to admin menu
    function admin_menu()
    {
    global $submenu;
    // remove 'Link' from Write menu
    unset($submenu['post-new.php'][15]);
    // submenu pages
    add_submenu_page('post-new.php', __('Add Photo',$this-
    >plugin_domain) , __('Photo', $this->plugin_domain) , 1 ,
    'add-photo', array(&$this, 'display_form') );
  2. That's all! The link page no longer shows in the menu.

What just happened?

We removed the Link page from the menus by unsetting it from the submenu array.

WordPress holds the entire backend menu structure in two globally available variables $menu and $submenu.

Using these two variables, not only can you read entries, you can also change and delete entries in the menus, and customize the menu structure to your liking by simply editing the arrays.

To do that, we must first learn what this structure looks like. The easiest way to do that is to use the print_r() function to dump the variable in a readable form.

Here is the sample output of print_r($menu):

Array
(
[0] => Array
(
[0] => Dashboard
[1] => read
[2] => index.php
)
[5] => Array
(
[0] => Write
[1] => edit_posts
[2] => post-new.php
)
[10] => Array
(
[0] => Manage
[1] => edit_posts
[2] => edit.php
)
[15] => Array
(
[0] => Design
[1] => switch_themes
[2] => themes.php
)
[20] => Array
(
[0] => Comments <span id='awaiting-mod' class='count-0'>
<span class='comment-count'>0</span></span>
[1] => edit_posts
[2] => edit-comments.php
)

As you can see, the menus are organized into multidimensional arrays. If you remove elements of this array, they will not be displayed in the menu anymore.

For example:

unset($menu[10]); // Remove Manage Menu
unset($menu[15]); // Remove Design Menu
unset($menu[20]); // Remove Comments Menu

This piece of code will cause the Manage, Design, and Comments pages to disappear from the WordPress main menu leaving with only the with Write menu:

Similarly, we remove the Link page from the submenus:

// remove 'Link' from Write menu
unset($submenu['post-new.php'][15]);

Using the menu arrays, you can customize the backend of a WordPress site to show only relevant information for your purpose. If you are turning WordPress into a CMS, implementing custom fields and tinkering with menus would be the first things on the list.

Summary

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

While creating our different Post Types, we have learned how to customize WordPress menus to our liking. We used custom fields to insert important information to the post which we do not want to be visible in the post content.

Here are the most important lessons from this article:

  • Localization: Not everyone uses WordPress in English
  • Backend CSS classes: Use them to make your forms prettier
  • Custom fields: They are the powerhouse behind WordPress CMS capabilities
  • Customize Menus: Your plugin can choose the menus in the WordPress backend

 

If you have read this article you may be interested to view :

 

About the Author :


Vladimir Prelovac

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 prelovac.com.

Contact Vladimir Prelovac

Books From Packt

WordPress Theme Design
WordPress Theme design

Building Powerful and Robust Websites with Drupal 6
Building Powerful and Robust Websites with Drupal 6

AJAX and PHP: Building Responsive Web Applications
AJAX and PHP: Building Responsive Web Applications

Magento Beginner's Guide
Magento Beginner's Guide

WordPress for Business Bloggers
WordPress for Business Bloggers

Building Websites with Joomla! 1.5
Building Websites with Joomla! 1.5

Learning jQuery : Better Interaction Design and Web Development with Simple JavaScript Techniques
Learning jQuery : Better Interaction Design and Web Development with Simple JavaScript Techniques

WordPress Complete
WordPress Complete

Your rating: None Average: 5 (3 votes)
Great post, but... by
Forgive my inexperience, but the code gives me error this time:(

Post new comment

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.
Y
8
n
z
Y
i
Enter the code without spaces and pay attention to upper/lower case.
Code Download and Errata
Packt Anytime, Anywhere
Register Books
Print Upgrades
eBook Downloads
Video Support
Contact Us
Awards Voting Nominations Previous Winners
Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
Resources
Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software