Book Image

WordPress Web Application Development

By : Rakhitha Nimesh Ratnayake
Book Image

WordPress Web Application Development

By: Rakhitha Nimesh Ratnayake

Overview of this book

Table of Contents (19 chapters)
WordPress Web Application Development Second Edition
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 practical approach to learn new things over the more conventional theoretical approach.

So, I will complete this chapter by converting default WordPress functionality into a simple question-answer interface such as Stack Overflow, to give you a glimpse of what we will develop throughout this book.

Prerequisites for building a question-answer interface

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

Tip

Also, we will be using the Twenty Fourteen theme, which is available with default WordPress installation. Make sure that you activate the Twenty Fourteen theme in your WordPress installation.

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

  1. Create questions using the admin section of WordPress.

  2. Allow users to answer questions using comments.

  3. Allow question creators to mark each answer as correct or incorrect.

  4. Highlight the correct answers of each question.

  5. Customize the question list to include a 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 off, 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, a post can be considered as the question and 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 plugin, which you can find at http://codex.wordpress.org/Post_Types#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 will create a plugin to implement the necessary tasks for our application:

  1. First off, create a folder called wpwa-questions inside the /wp-content/plugins folder and add a new file called wpwa-questions.php.

  2. Next, we need to add the block comment to define our file as a plugin:

    /*
    Plugin Name: WPWA 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
    Text Domain: wpwa-questions
    */
  3. Having created the main plugin file, we can move into creating a custom post type called wpwa-question using the following code snippet.

  4. Include this code snippet in your wpwa-questions.php file of the plugin:

    add_action('init', 'register_wp_questions');
    function register_wp_questions() {
      $labels = array(
      'name' => __( 'Questions', 'wpwa_questions' ),
      'singular_name' => __( 'Question', 'wpwa_questions'),
      'add_new' => __( 'Add New', 'wpwa_questions'),
      'add_new_item' => __( 'Add New Question', 'wpwa_questions'),
      'edit_item' => __( 'Edit Questions', 'wpwa_questions'),
      'new_item' => __( 'New Question', 'wpwa_questions'),
      'view_item' => __( 'View Question', 'wpwa_questions'),
      'search_items' => __( 'Search Questions', 'wpwa_questions'),
      'not_found' => __( 'No Questions found', 'wpwa_questions'),
      'not_found_in_trash' => __( 'No Questions found in Trash', 'wpwa_questions'),
      'parent_item_colon' => __( 'Parent Question:', 'wpwa_questions'),
      'menu_name' => __( 'Questions', 'wpwa_questions'),
      );
      $args = array(
      'labels' => $labels,
      'hierarchical' => true,
      'description' => __( 'Questions and Answers', 'wpwa_questions'),
      '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( 'wpwa_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 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 to have a look at 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. This is considered a bad practice as it becomes extremely difficult to maintain because your application becomes larger. So, we will be using plugins to add functionality throughout this book and drawbacks of the functions.php technique will be discussed in later chapters.

  5. Once the code is included, you will get a new section on the admin area for creating questions. This section will be similar to the posts section inside the WordPress admin. Add few questions and insert some comments using different users before we move into the next stage.

Before we go into the development of questions and answers, we need to make some configurations so that our plugin works without any issues. Let's look at the configuration process:

  1. First, we have to look at the comment-related settings inside Discussion Settings the WordPress Settings section. Here, you can find a setting called Before a comment appears.

  2. Disable both checkboxes so that users can answer and get their answers displayed without approval process. Depending on the complexity of application, you can decide whether to enable these checkboxes and change the implementation.

  3. The second setting we have to change is the Permalinks. Once we create a new custom post type and view it on browser, it will redirect you to a 404 page not found page. Therefore, we have to go to the Permalinks section of WordPress Settings and update the Permalinks using the Save Changes button. This won't change the Permalinks. However, this will flush the rewrite rules so that we can use the new custom post type without 404 errors.

Now, we can start working with the answer-related features.

Customizing the comments template

Usually, the comments section is designed to show comments of a normal post.

While using comments for custom features such as answers, we need to customize the existing template and use our own designs.

  1. So, open the comments.php file inside the Twenty Fourteen theme.

  2. Navigate through the code and you will find a code section similar to the following one:

    wp_list_comments( array(
      'style' => 'ol',
      'short_ping' => true,
      'avatar_size' => 34,
    ) );
  3. 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 will change the previous implementation as following:

    if(get_post_type( $post ) == "wpwa_question"){
      wp_list_comments('avatar_size=60&type=comment&callback=wpwa_comment_list&style=ol');
    }else{
      wp_list_comments( array(
        'style' => 'ol',
        'short_ping' => true,
        'avatar_size' => 34,
      ) );
    }
  4. Here, we will include a conditional check for the post type in order to choose the correct answer list generation function. When the post type is wpwa_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 answers list.

    Note

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

In the next section, we will be completing the customization of comments template by adding answer statuses and allowing users to change the statuses.

Changing the status of answers

Once the users provide their answers, the creator of the question should be able to mark them as correct or incorrect answers. So, we will implement a button for each answer to mark the 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.

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. Hence, I'll be explaining the important sections of the code. It's ideal to work with the full code for the wpwa_comment_list function from the source code folder:

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
}

The wpwa_comment_list function 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 be only visible to the creator of the question.

In order to change the status of answers, follow these steps:

  1. First, we will 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.

  2. Next, we will check whether the logged-in user created the question. If so, we will set show_answer_status to true. Also, we have to retrieve the status of the current answer by passing the comment_id and _wpwa_answer_status keys to the get_comment_meta function.

  3. Then, we will have to include the common code for generating comments list with necessary condition checks.

  4. Open the wpwa-questions.php file of the plugin and go through the rest of wpwa_comment_list function to get an idea of how comments loop works.

  5. Next, we have to highlight correct answers of each question and I'll be using an image as the highlighter. In the source code, we use 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>";
      }
    ?>
  6. In the source code, you will see a <div> element with the class reply for creating the comment reply link. We will need to insert our answer button status code right after this, 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','wpwa_questions');
        } else {
          $question_status = 'valid';
          $question_status_text = __('Mark as Correct','wpwa_questions');
        }
    ?>
      <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>
  7. 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 will get the status of answer as true or false using the _wpwa_answer_status key from the wp_commentmeta table.

  8. Based on the returned value, we will define buttons for either Mark as Incorrect or Mark as Correct. Also, we will specify some CSS classes and HTML5 data attribute to be used later with jQuery.

  9. Finally, we keep the comment_id in a hidden variable called hcomment.

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

  11. Next, we need to implement the AJAX request for marking the status of the answer as true or false.

Before this, 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( 'wpwa-questions', plugins_url( 'js/questions.js', __FILE__ ), array('jquery'), '1.0', TRUE );
  wp_enqueue_script( 'wpwa-questions' );
  wp_register_style( 'wpwa-questions-css', plugins_url( 'css/questions.css', __FILE__ ) );
  wp_enqueue_style( 'wpwa-questions-css' );
  $config_array = array(
    'ajaxURL' => admin_url( 'admin-ajax.php' ),
    'ajaxNonce' => wp_create_nonce( 'ques-nonce' )
  );
  wp_localize_script( 'wpwa-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. The wp_enqueue_script action is used to include script files into the page while the wp_register_script action is used to add custom files. Since jQuery is built-in to WordPress, we can just use the wp_enqueue_script action 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 parameters 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, we discussed previously. Now, everything is set up properly to create the AJAX request.

Saving the status of answers

Once the author clicks 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:

jQuery(document).ready(function($) {
  $(".answer-status").click( function() {
    $(body).on("click", ".answer-status" , function() {
      // Get the button object and current status of the answer
      var answer_button = $(this);
      var answer_status  = $(this).attr("data-ques-status");
      // Get the ID of the clicked answer using hidden field
      var comment_id = $(this).parent().find(".hcomment").val();
      var data = {
      "comment_id":comment_id,
      "status": answer_status
    };
  // Create the AJAX request to save the status to database
  $.post( wpwaconf.ajaxURL, {
    action:"mark_answer_status",
    nonce:wpwaconf.ajaxNonce,
    data : data,
  }, function( data ) {
    if("success" == data.status){
      if("valid" == answer_status){
         answer_buttonval("Mark as Incorrect");
         answer_button.attr(" data-ques-status","invalid");
      }else{
        answer_button.val("Mark as Correct");
        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.

Note

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

The next step of this process is to implement the server-side code for handling AJAX request. 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 to mark the status of the answer using the update_comment_meta function. This example contains the most basic implementation of data saving process. In real applications, we need to implement necessary validations and error handling.

Now, the author who asked 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 questions list.

Generating a 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:

  1. Here, we will create a customized layout for our questions.

  2. Make a copy of the existing archive.php file of the TwentyFourteen theme and rename it as archive-wpwa_question.php. Here, you will find the following code section:

    get_template_part( 'content', get_post_format() );
  3. The TwentyFourteen 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 a custom template called content-questions.php by duplicating the content.php file and change the preceding code to the following:

    get_template_part( 'content-wpwa_question', get_post_format() );
  4. Finally, we need to consider the implementation of content-wpwa_question.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 the_excerpt and the_content functions of the template. We can comment the following line within this template:

    the_content( __( 'Continue reading <span class="meta-nav">&rarr;</span>', 'twentyfourteen' ) );
  5. Then, we will create our own metadata by adding the following code to the <div> element with the entry-content class:

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

  6. Then, we need to display the number of correct answers of 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 to retrieve the approved answers of each post, which 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 following image:

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 HTML and PHP code inside both, themes and plugins.

Note

I suggest that you go through the Chapter01 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 develop a web application from scratch using detailed explanation in the following chapters.