WordPress Tutorial: How to Create a Testimonials Plugin

So, here we are for our second weekly tutorial on WordPress. We have learned how to create an infinite scrolling WP site and page-flip plugin in the past. Today we’ll learn a little bit more about custom post types, and how to load their data.

This feature is really important in your site, since it allows you to show people recommending you. Testimonials are a great social proof of your awesomeness. And you don’t need complex plugins or a lot of code to achieve this, as you’ll see with a couple of files you can create a sexy presentation for your testimonials.

Let’s get started then!

Zip & Install

If you are in a hurry to check this, you can download and install it.

You’ll need to add some data then, by adding a few testimonials using the brand new testimonials are you can see in your dashboard:


Then you can use the basic shortcode to load that:

[ testimonials rand=0 max=5 ]

When you’ve added that code you’ll see something like this in your page:


Now we’ll see how to create and explore this (by adding more functions and changing the look).

The Basics

As we’ve said before, you’ll need to add the header metadata, create the plugin file and call your scripts. Long story short, you’ll create a new folder under your wp-content/plugins with your plugin’s name, then create a file with the same name as the folder to be your main plugin file.

Once you’ve done that copy and paste as follows:

Plugin Name: Testimonials
Description: Display customer testimonials.
Version: 1.0
Author: Web Revenue Blog
License: GPL2

//enqueueing scripts and styles
function plugin_scripts(){
    wp_enqueue_script('flexslider', plugins_url( 'js/jquery.flexslider-min.js' , __FILE__ ), array('jquery'), '2.2', false);
    wp_enqueue_script('testimonials', plugins_url( 'js/testimonials.js' , __FILE__ ), array('jquery'), '1.0', false);
    wp_enqueue_style('flexsliderCSS', plugins_url( 'css/flexslider.css' , __FILE__ ), false, '2.2', 'all' );
    wp_enqueue_style('testimonialsCSS', plugins_url( 'css/testimonials.css' , __FILE__ ), false, '1.0', 'all' );

Here is what we’re doing:

  • Telling to the WP what’s our plugin’s name, author, what it does
  • Creating a function, where we insert regular scripts (like jQuery) and custom scripts (like flexslider), and Stylesheets
  • Telling to the WP to load the scripts function in its default scripts call, so they’ll be actually loaded in the pages

It’s all pretty cool, but don’t forget to actually create the files under /js and /css. You can just download them in our demo content so you don’t need to dig a lot to find the flexslider files.

Now we have all basic things in place we can start the funny part.

The Custom Post Type

By default, WordPress have 2 common post types, pages and posts. But it also has a lot of internal post types (like attachments), so the “post type” definition is: Every type of data you need to store.

As our plugin will create a new functionality WP doesn’t have a built-in place to store that, so we need to create that. Don’t be afraid little grasshopper, it’s quite simple, you can use this code:

//the black magic to create the post type
function create_post_type() {
        'testimonials',//new post type
            'labels' => array(
                'name' => __( 'Testimonials' ),
                'singular_name' => __( 'Testimonial' )
            'public' => true,/*Post type is intended for public use. This includes on the front end and in wp-admin. */
            'supports' => array('title','editor','thumbnail','custom_fields'),
            'hierarchical' => false

Here we’re just using the register_post_type() function to tell to WP “Hey Buddy, we need to store this kind of data, please be ready to receive it”.

We also tell him that this kind of data is called “testimonials”, it should be available for public access (so it actually create a new menu item in your dashboard for it), the fields that we need on it, and if it’s hierarchical or not (like pages that we have parent and child pages).

Then we need to call it every time we load WordPress. This hook will do it:

add_action( 'init', 'create_post_type' );

The Custom Fields

Now our custom post type have the title (person’s name), the content (person’s testimonial), a picture (featured image) but it’s missing a link, since if the person is nice enough to talk about you, you should at least link to their site, right?

We could do this with usual custom fields, but it’s much better to have a “closed” field, where the user doesn’t need to type field's name, and also where you can add some validation rules.

First of all we need to create a new metabox, which is those nice panels you have in your post edit area, each little panel is a metabox. This function will create and call it:

//adding the URL meta box field
function add_custom_metabox() {
    add_meta_box( 'custom-metabox', __( 'Link' ), 'url_custom_metabox', 'testimonials', 'side', 'low' );
add_action( 'admin_init', 'add_custom_metabox' );

The add_meta_box() function requires these parameters:

  1. ID – The unique identifier for it. You could use anything unique here like “unicorn-eating-rainbow” or “testimonial-link”. Anything that can be used internally
  2. Title – What will be shown for the user? Here it’s important to use the __() function, it’s the function that allow users from foreign languages to translate your plugin with .po files (without editing plugin files)
  3. Callback – The function where you have the actual contents of the metabox
  4. Post Type – In our case we want it to be visible only for testimonials
  5. Context – Where in the page you want to show it
  6. Priority – If it should be before other items of the same position or after them

Now we need to create the url_custom_metabox() function, since we’ve called it.

// HTML for the admin area
function url_custom_metabox() {
    global $post;
    $urllink = get_post_meta( $post->ID, 'urllink', true );

    if ( ! preg_match( "/http(s?):///", $urllink ) && $urllink != "") {
        $errors = "This URL isn't valid";
        $urllink = "http://";

    // output invlid url message and add the http:// to the input field
    if( isset($errors) ) { echo $errors; }
    <label for="siteurl">URL:<br />
        <input id="siteurl" size="37" name="siteurl" value="<?php if( isset($urllink) ) { echo $urllink; } ?>" />

Ok, now translating this to plain English:

  • The global variable $post is called, so we can know which is the POSTID of the current item
  • We load the current value for the URL
  • We validate If the value that user inserted is valid. If there is at least one “http” or “https” occurrence the value is OK, otherwise it’s in valid and we need to use the default value
  • Then we show the errors, if there’s any
  • Now we start the actual HTML, adding the default value in the input field as we’ve got in the PHP

After this point we need to actually save what’s being sent by the user. We’ll use the “save_post” hook, so every time WordPress saves a post it’ll call this function:

//saves custom field data
function save_custom_url( $post_id ) {
    global $post;    

    if( isset($_POST['siteurl']) ) {
        update_post_meta( $post->ID, 'urllink', $_POST['siteurl'] );
add_action( 'save_post', 'save_custom_url' );

Here we just check if there’s any post data called “sitelink” which is our field name. If there is a sitelink, let’s save it.

After everything is in place, here is how your new testimonial  page will look like:


Loading our Custom Data

Now we need to actually load our items, and we’ll use the get_posts() function, mostly because we have only simple data so we don’t need a proper WP loop (that would add a lot of DB calls and we really don’t need it).

So, first let’s create a function to get our site’s link, if there’s any.

//return URL for a post
function get_url($post) {
    $urllink = get_post_meta( $post->ID, 'urllink', true );

    return $urllink;

Now, we can start the shortcode function.  A simple way to default and validate the shortcode data is creating the attributes for the loop as an array, so we can add new items as we need them, like this:

//registering the shortcode to show testimonials
function load_testimonials($a){

    $args = array(
        "post_type" => "testimonials"

    if( isset( $a['rand'] ) && $a['rand'] == true ) {
        $args['orderby'] = 'rand';
    if( isset( $a['max'] ) ) {
        $args['posts_per_page'] = (int) $a['max'];
    //getting all testimonials
    $posts = get_posts($args);


As you can see we have the shortcode attributes loaded and passed to the $args array when they validate, in the format that WordPress needs it, so we can load posts using them.

Now we need to add some HTML code there, following flexslider’s default structure. Here is how it’ll look like:

echo '<div>';
    echo '<ul>';

    foreach($posts as $post){
        //getting thumb image
        $url_thumb = wp_get_attachment_thumb_url(get_post_thumbnail_id($post->ID));
        $link = get_url($post);
        echo '<li>';
            if ( ! empty( $url_thumb ) ) { echo '<img src="'.$url_thumb.'" />'; }
            echo '<h2>'.$post->post_title.'</h2>';
            if ( ! empty( $post->post_content ) ) { echo '<p>'.$post->post_content.'<br />'; }
            if ( ! empty( $link ) ) { echo '<a href="'.$link.'">Visit Site</a></p>'; }
        echo '</li>';

    echo '</ul>';
echo '</div>';

Wait, but why would we create the HTML code inside of the PHP function? That’s because we can conditionally load content only if the user have added content, so you won’t have empty HTML tags, just waiting to mess up your layout.

What about the sidebar?

Most people just want to show testimonials in the sidebar, and this plugin won’t work really well since text widgets don’t process shortcodes. There’s a simple solution for this, just add this in your code:

add_filter('widget_text', 'do_shortcode');

What’s next?

So, did you enjoy this tutorial? What would you add as an option for your testimonial shortcode? Do you have any ideas for future posts? Let us know using the comments section!

Photo of author

Article by Rochester Oliveira