Book Image

WordPress Web Application Development

By : Rakhitha Nimesh Ratnayake
Book Image

WordPress Web Application Development

By: Rakhitha Nimesh Ratnayake

Overview of this book

Developing WordPress-powered websites is one of the standout trends in the modern web development world. The flexibility and power of the built-in features offered by WordPress has made developers turn their attentions to the possibility of using it as a web development framework. This book will act as a comprehensive resource for building web applications with this amazing framework. "WordPress Web Application Development" is a comprehensive guide focused on incorporating the existing features of WordPress into typical web development. This book is structured towards building a complete web application from scratch. With this book, you will build an application with a modularized structure supported by the latest trending technologies. "Wordpress Web Application Development" provides a comprehensive, practical, and example-based approach for pushing the limits of WordPress for web applications beyond your imagination. This book begins by exploring the role of existing WordPress components and discussing the reasons for choosing WordPress for web application development. As we proceed, more focus will be put into adapting WordPress features into web applications with the help of an informal use-case-based model for discussing the most prominent built-in features. While striving for web development with WordPress, you will also learn about the integration of popular client-side technologies such as Backbone, Underscore, and jQuery, and server-side technologies and techniques such as template engines, RSS feeds, Open Auth integration, and more. After reading this book, you will possess the ability to develop powerful web applications rapidly within limited time frames with the crucial advantage of benefitting low-budget and time-critical projects.
Table of Contents (18 chapters)
WordPress Web Application Development
Credits
About the Author
About the Reviewers
www.PacktPub.com
Preface
Index

Building a question-answer interface


Throughout the previous sections, we learned the basics of web application frameworks while looking at how WordPress fits into web development. By now, you should be able to visualize the potential of WordPress for application development and how it can change your career as developers. Being human, we always prefer a practical approach to learning new things over the more conventional theoretical approach.

So, I am going to complete this chapter by converting the default WordPress functionality into a simple question-answer interface such as Stack Overflow, to show you a glimpse into what we are going to develop throughout this book.

Prerequisites

We will be using Version 3.6 as the latest stable version, available at the time of writing this book. I suggest you to set up a fresh WordPress installation for this book, if you haven't already done so.

Also we will be using the TwentyTwelve theme, which is available with the default WordPress installation. Make sure to activate the TwentyTwelve theme in your WordPress installation.

First, we have to create an outline containing the list of tasks to be implemented for this scenario:

  • Create questions using the admin section of WordPress

  • Allow users to answer questions using comments

  • Allow question creators to mark each answer as correct or incorrect

  • Highlight the correct answers for each question

  • Customize the question list to include the number of answers and number of correct answers

Now it's time to get things started.

Creating questions

The goal of this application is to let people submit questions and get answers from various experts in the same field. First, we need to create a method to add questions and answers. By default, WordPress allows us to create posts and submit comments to the posts. In this scenario, the post can be considered as the question and the comments can be considered as the answers. Therefore, we have the capability of directly using normal post creation for building this interface.

However, I would like to choose a slightly different approach by using custom post types in order to keep the default functionality of posts and let the new functionality be implemented separately without affecting the existing ones.

We are going to create a plugin to implement the necessary tasks for our application. First, create a folder called wpwa-questions inside the /wp-content/plugins folder and add a new file called index.php. Next, we need to add the block comment to define our file as a plugin:

/*
Plugin Name: WP Questions
Plugin URI: -
Description: Question and Answer interface for developers
Version: 1.0
Author: Rakhitha Nimesh
Author URI: http://www.innovativephp.com/
License: GPLv2 or later
*/

Having created the main plugin file, we can move into creating a custom post type called wp_question using the following code snippet. Include the code snippet in your index.php file of the plugin:

add_action('init', 'register_wp_questions');

function register_wp_questions() {

  $labels = array(
    'name' => __( 'Questions', 'wp_question' ),
    'singular_name' => __( 'Question', 'wp_question' ),
    'add_new'  => __( 'Add New', 'wp_question' ),
    'add_new_item' => __( 'Add New Question', 'wp_question' ),
    'edit_item' => __( 'Edit Questions', 'wp_question' ),
    'new_item'  => __( 'New Question', 'wp_question' ),
    'view_item' => __( 'View Question', 'wp_question' ),
    'search_items' => __( 'Search Questions', 'wp_question' ),
    'not_found' => __( 'No Questions found', 'wp_question' ),
    'not_found_in_trash' => __( 'No Questions found in Trash', 'wp_question' ),
    'parent_item_colon' => __( 'Parent Question:', 'wp_question' ),
    'menu_name' => __( 'Questions', 'wp_question' ),
    );

  $args = array(
    'labels'  => $labels,
    'hierarchical' => true,
    'description' => __( 'Questions and Answers', 'wp_question' ),
    'supports'   => array( 'title', 'editor', 'comments' ),
    'public'                => true,
    'show_ui'               => true,
    'show_in_menu'          => true,
    'show_in_nav_menus'     => true,
    'publicly_queryable'    => true,
    'exclude_from_search'   => false,
    'has_archive'           => true,
    'query_var'             => true,
    'can_export'            => true,
    'rewrite'               => true,
    'capability_type'       => 'post'
    );

  register_post_type( 'wp_question', $args );
}

This is the most basic and default code for custom post type creation and I assume that you are familiar with the syntax. We have enabled title, editor, and comments in the support section of the configuration. These fields will act in the role of question title, question description, and answers. Other configurations contain the default values and hence explanations will be omitted. If you are not familiar, make sure you have a look at the documentation on custom post creation at http://codex.wordpress.org/Function_Reference/register_post_type.

Note

Beginner to intermediate level developers and designers tend to include the logic inside the functions.php file in the theme. It is considered bad practice as it becomes extremely difficult to maintain as your application becomes larger, so we will be using plugins to add functionality throughout this book, and the drawbacks of the functions.php technique will be discussed in the later chapters.

Once the code is included, you will get a new section in the admin area for creating questions. Add a few questions and put some comments in using different users, before we move on to the next stage.

Changing the status of answers

Once users provide their answers, the creator of the question should be able to mark them as correct or incorrect. So, we are going to implement a button for each answer to mark its status. Only the creator of the questions will be able to mark the answers. Once the button is clicked, an AJAX request will be made to store the status of the answer in the database.

First, we need to customize the existing comments list to suit the requirements of the answers list. By default, WordPress will use the wp_list_comments function inside the comments.php file to show the list of answers for each question. We need to modify the answers list in order to include the answer status button.

So we implement our own version of wp_list_comments using a custom function. First, you have to open the comments.php file of the theme and look for the call to the wp_list_comments function. You should see something similar to the following code:

<?php wp_list_comments( array( 'callback' => 'twentytwelve_comment', 'style' => 'ol' ) ); ?>

This function is used to generate a comments list for all types of posts, but we need a slightly modified version to suit the answers list. So we call the wp_list_comments function with different arguments, as shown in the following code:

<?php
if( get_post_type( $post ) == "wp_question" ){
wp_list_comments(  array( 'type' => 'comment', 'callback' => 'wpwa_comment_list', 'style' => 'ol' ) );
} else{

wp_list_comments(  array( 'type' => 'comment', 'callback' => 'twentytwelve_comment', 'style' => 'ol' ) );
}
?>

Note

Arguments of the wp_list_comments function can be either an array or a string. Here we have preferred array-based arguments over string-based arguments.

Here we include a conditional check for the post type in order to choose the correct answer list generation function. When the post type is wp_question, we call the wp_list_comments function with the callback parameter defined as wpwa_comment_list, which will be the custom function for generating the answers list.

Implementation of the wpwa_comment_list function goes inside the wpwa-questions.php file of our plugin. This function contains lengthy code, which is not necessary for our explanations, so I'll just be explaining the important sections of the code. It's best to work with the full code for the wpwa_comment_list function from the source code folder. Have a look at the following code snippet:

function wpwa_comment_list( $comment, $args, $depth ) {
  global $post;

  $GLOBALS['comment'] = $comment;

  // Get current logged in user and author of question
  $current_user           = wp_get_current_user();
  $author_id              = $post->post_author;
  $show_answer_status     = false;

  // Set the button status for authors of the question
  if ( is_user_logged_in() && $current_user->ID == $author_id ) {
    $show_answer_status = true;
  }

  // Get the correct/incorrect status of the answer
  $comment_id = get_comment_ID();
  $answer_status = get_comment_meta( $comment_id, "_wpwa_answer_status", true );

  // Rest of the Code

}

wpwa_comment_list is used as the callback function of the comments list and hence it will contain three parameters by default. Remember that the button for marking the answer status should only be visible to the creator of the question.

First, we get the current logged in user from the wp_get_current_user function. Also we can get the creator of the question using the global $post object. Next, we check whether the logged in user created the question. If so, we set the $show_answer_status variable to true. Also, we have to retrieve the status of the current answer by passing the comment ID and the _wpwa_answer_status key to the get_comment_meta function.

Then we will have to include the common code for generating the comments list with the necessary condition checks. Open the wpwa-questions.php file of the plugin and go through the rest of the wpwa_comment_list function to get an idea of how the comments loop works.

Next, we have to highlight the correct answers for each question and I'll be using an image as the highlighter. In the source code, we use the following code after the header tag to show the correct answer highlighter:

<?php
  // Display image of a tick for correct answers
  if ( $answer_status ) {
    echo "<div class='tick'><img src='".plugins_url( 'img/tick.png', __FILE__ )."' alt='Answer Status' /></div>";
  }
?>

In the source code, you will see a DIV element with the class reply for creating the comment reply link. We need to insert our answer button status code right after that, as shown in the following code:

<div>
  <?php
  // Display the button for authors to make the answer as correct or incorrect
  if ( $show_answer_status ) {

    $question_status = '';
    $question_status_text = '';
    if ( $answer_status ) {
      $question_status = 'invalid';
      $question_status_text = 'Mark as Incorrect';
    } else {
      $question_status = 'valid';
      $question_status_text = 'Mark as Correct';
    }

  ?>
  <input type="button" value="<?php echo $question_status_text; ?>"  class="answer-status answer_status-<?php echo $comment_id; ?>"
    data-ques-status="<?php echo $question_status; ?>" />
  <input type="hidden" value="<?php echo $comment_id; ?>" class="hcomment" />

  <?php
  }
  ?>
</div>

If the $show_answer_status variable is set to true, we get the comment ID, which will be our answer ID, using the get_comment_ID function. Then we get the status of the answer as true or false using the _wpwa_answer_status key from the commentmeta table. Based on the returned value, we define buttons for either Mark as Incorrect or Mark as Correct. Also, we specify some CSS classes and HTML5 data attributes to be used later with jQuery. Finally, we keep the comment ID in a hidden variable called hcomment.

Once you include the code, the button will be displayed for the author of the question, as shown in the following screenshot:

Next, we need to implement the AJAX request for marking the status of the answer as true or false. Before that, we need to see how we can include our scripts and styles into WordPress plugins.

Here is the code for including custom scripts and styles for our plugin. Copy the following code into the wpwa-questions.php file of your plugin:

function wpwa_frontend_scripts() {

  wp_enqueue_script( 'jquery' );
  wp_register_script( 'wp-questions', plugins_url( 'js/questions.js', __FILE__ ), array('jquery'), '1.0',true );
  wp_enqueue_script( 'wp-questions' );

  wp_register_style( 'questions', plugins_url( 'css/questions.css', __FILE__ ) );
  wp_enqueue_style( 'questions' );

  $config_array = array(
    'ajaxURL' => admin_url( 'admin-ajax.php' ),
    'ajaxNonce' => wp_create_nonce( 'ques-nonce' )
  );

  wp_localize_script( 'wp-questions', 'wpwaconf', $config_array );
}
add_action( 'wp_ajax_mark_answer_status', 'wpwa_mark_answer_status' );

WordPress comes in-built with an action hook called wp_enqueue_scripts, for adding JavaScript and CSS files. wp_enqueue_script is used to include script files into the page while wp_register_script is used to add custom files. Since jQuery is built into WordPress, we can just use wp_enqueue_script to include jQuery into the page. We also have a custom JavaScript file called questions.js, which will contain the functions for our application.

Inside JavaScript files, we cannot access the PHP variables directly. WordPress provides a function called wp_localize_script, to pass PHP variables into script files. The first parameter contains the handle of the script for binding data, which will be wp-questions in this scenario. The second parameter is the variable name to be used inside JavaScript files to access these values. The third and final parameter will be the configuration array with the values.

Then we can include our questions.css file using the wp_register_style and wp_enqueue_style functions, which will be similar to JavaScript, file inclusion syntax. Now everything is set up properly to create the AJAX request.

Saving the status of answers

Once the author clicks on the button, the status has to be saved to the database as true or false depending on the current status of the answer. Let's go through the jQuery code located inside the questions.js file for making the AJAX request to the server.

$jq =jQuery.noConflict();

$jq(document).ready( function() {

  $jq(".answer-status").click( function() {

    // Get the button object and current status of the answer
    var answer_button = $jq(this);
    var answer_status  = $jq(this).attr("data-ques-status");

    // Get the ID of the clicked answer using hidden field
    var comment_id = $jq(this).parent().find(".hcomment").val();
    var data = {
      "comment_id":comment_id,
      "status": answer_status
    };

    // Create the AJAX request to save the status to database
    $jq.post( wpwaconf.ajaxURL, {
      action:"mark_answer_status",
      nonce:wpwaconf.ajaxNonce,
      data : data,
    }, function( data ) {
      if("success" == data.status){
      /*Changes the display text of answer status button and toggles the answer status between valid and invalid to be displayed in frontend.*/
        if("valid" == answer_status){
          $jq(answer_button).val("Mark as Incorrect");
          $jq(answer_button).attr("data-ques-status","invalid");
        }else{
          $jq(answer_button).val("Mark as Correct");
          $jq(answer_button).attr("data-ques-status","valid");
        }
      }
    }, "json");
  });
});

The preceding code creates a basic AJAX request to the mark_answer_status action. Most of the code is self-explanatory and code comments will help you to understand the process.

The important thing to note here is that we have used the configuration settings assigned in the previous section, using the wpwaconf variable. Once the server returns the response with success status, the button will be updated to contain the new status and display the text.

The next step of this process is to implement the server-side code for handling AJAX requests. First, we need to define AJAX handler functions using the WordPress add_action function. Since logged in users are permitted to mark the status, we don't need to implement the add_action function for wp_ajax_nopriv_{action}.

add_action( 'wp_ajax_mark_answer_status', 'wpwa_mark_answer_status' );

Implementation of the wpwa_mark_answer_status function is given in the following code:

function wpwa_mark_answer_status() {
  $data = isset( $_POST['data'] ) ? $_POST['data'] : array();
  $comment_id     = isset( $data["comment_id"] ) ? absint($data["comment_id"]) : 0;
  $answer_status  = isset( $data["status"] ) ? $data["status"] : 0;

  // Mark answers in correct status to incorrect
  // or incorrect status to correct
  if ("valid" == $answer_status) {
    update_comment_meta( $comment_id, "_wpwa_answer_status", 1 );
  } else {
    update_comment_meta( $comment_id, "_wpwa_answer_status", 0 );
  }

  echo json_encode( array("status" => "success") );
  exit;
}

We can get the necessary data from the $_POST array and use it to mark the status of the answer using the update_comment_meta function. This example contains the most basic implementation of the data saving process. In real applications, we need to implement necessary validations and error handling.

Now, the author of the question has the ability to mark answers as correct or incorrect, so we have implemented a nice and simple interface for creating a question-answer site with WordPress. The final task of the process will be the implementation of the questions list.

Tip

Downloading the example code

You can download the example code files for all Packt books you have purchased from your account at http://www.packtpub.com. If you purchased this book elsewhere, you can visit http://www.packtpub.com/support and register to have the files e-mailed directly to you.

Generating the question list

Usually WordPress uses the archive.php file of the theme for generating post lists of any type. We can use a file called archive-{post type}.php for creating different layouts for different post types. In this file we are going to create a customized layout for our questions. Make a copy of the existing archive.php file of the TwentyTwelve theme and rename it archive-wp_question.php. In this file, you will find the following code section:

get_template_part( 'content', get_post_format() );

The TwentyTwelve theme uses a separate template for generating the content of each post type. We cannot modify the existing content.php file as it affects all kinds of posts, so create custom templates called content-questions.php by duplicating the content.php file and change the preceding code to the following:

get_template_part( 'content-questions', get_post_format() );

Finally, we need to consider the implementation of the content-questions.php file. In the questions list, only the question title will be displayed and therefore we don't need the content of the post, so we have to either remove or comment the functions the_excerpt and the_content in the template.

Then we also have to remove the twentytwelve_entry_meta function and create our own metadata using the following code:

<div class="answer_controls"><?php comments_popup_link(__('No Answers &darr;', 'responsive'), __('1 Answer &darr;', 'responsive'), __('% Answers &darr;', 'responsive')); ?>
</div>

<div class="answer_controls">
<?php wpwa_get_correct_answers(get_the_ID()); ?>
</div>

<div class="answer_controls">
<?php echo   get_the_date(); ?>
</div>
<div style="clear: both"></div>

The first container will make use of the existing comments_popup_link function to get the number of answers given for the questions. Then we need to display the number of correct answers for each question. The custom function called wpwa_get_correct_answers is created to get the correct answers. The following code contains the implementation of the wpwa_get_correct_answers function inside the plugin:

function wpwa_get_correct_answers( $post_id ) {
  $args = array(
    'post_id'   => $post_id,
    'status'    => 'approve',
    'meta_key'  => '_wpwa_answer_status',
    'meta_value'=> 1,
  );
  // Get number of correct answers for given question
  $comments = get_comments( $args );
  printf(__('<cite class="fn">%s</cite> correct answers'), count( $comments ) );
}

We can set the array of arguments to include the conditions for retrieving the approved answers of each post, which also contains the correct answers. The number of results generated from the get_comments function will be returned as correct answers. Now you should have a question list similar to the following screenshot:

Throughout this section we looked at how we can convert the existing functionalities of WordPress for building a simple question-answer interface. We took the quick-and-dirty path for this implementation by mixing the HTML and PHP code inside both themes and plugins.

Note

I suggest you go through the Chapter 1 source code folder and try this implementation on your own test server. This demonstration was created to show the flexibility of WordPress. Some of you might not understand the whole implementation. Don't worry as we will be developing a web application from scratch using detailed explanation in the upcoming chapters.

In the upcoming chapters, we'll see the limitations in this approach in complex web applications and how we can organize things better to write high quality, maintainable code.