I completed this WordPress coding task as part of an interview. I'm hoping someone can review my work and comment on everything, especially my general approach to the task, and the sanitization of output. The client's previous developer had already completed this project, but was leaving the company. So the client was going to compare my code to the code from his previous dev, to see how I sized up. Here are the requirements:


  • Create a new page in the WordPress admin menu, named "Reports."
  • The Reports page will display a table listing all posts of type 'contact' (an existing custom post type.
  • The table on the Reports page will display data for only eight specific post authors. The IDs of those post authors will be given to you beforehand, and hard coded into the program.
  • The table will display only the following columns: Contact Name, Contact Creation Date, Contact Author.
  • See this screenshot for an illustration of the final product.

My code is at the very bottom of this post. Here are some of my caveats/rationale.


  • My approach was to use the built-in WordPress WP_List_Table class to create the table. Building and displaying a table like this seems to be the main purpose of that class.

  • I did not copy the WP_List_Table class to my child theme, as suggested in the Codex. I guess best practice would be to do so? I believe some of the tutorials I read on the class omitted that step--not sure why.

  • All code was added to my theme's functions.php file, with the exception of a small separate users.php file, which was added to the child theme directory. I realize custom code is typically added to a child theme. But in this case, this was a requirement of the client. Actually, the parent theme in-question was already a custom theme (created and implemented by the client).

  • In the SQL SELECT statement, I did not sanitize any of my output with esc_sql() or prepare(). As I understand, such sanitization is required only if there is a chance that the data could be harmful or unexpected code. In my case, every element of the SELECT statement is hard code, so there doesn't seem like any chance that it could be harmful. But I may be misunderstanding something there. Furthermore, I did not sanitize any of the other output because of the same reason (all input is hard code). I also assumed(!) that the WP_List_Table() class would handle any necessary escaping of output for me. I guess I should at least confirm that.

functions.php code

/*Begin code to add 'Reports' admin section.*/
    include_once( ABSPATH . 'wp-admin/includes/class-wp-list-table.php' );
/*Create a new list table package that extends the core WP_List_Table class.*/
class TV_List_Table extends WP_List_Table {
    function __construct(){
        global $status, $page;

        parent::__construct( array(
            'singular'  => 'Contact',   //singular name of the listed records
            'plural'    => 'contacts',  //plural name of the listed records
            'ajax'      => false        //does this table support ajax?
        ) );


    /* This method is called when the parent class can't find a method specifically build for a given column.*/
    function column_default($item, $column_name){
        $msg_error = "No available data.";
        $user = get_user_by( 'id', $item['post_author'] );
            case 'contact_name':
                return print_r($user->user_nicename,true);
            case 'date_created':                
                    return print_r($item['post_date'],true);
                return print_r($msg_error,true);

    /* This method is responsible for rendering the 'Contact Name' column.*/
    function column_title($item){
        return sprintf('%1$s',$item['post_title']);

    /*This method dictates the table's column slugs and headers.*/
    function get_columns(){
        $columns = array(
            'title'         => 'Contact Name',
            'date_created'  => 'Date Created',
            'contact_name'  => 'Created By'
        return $columns;

    /*Method to prepare the data that will be displayed in the table.*/
    function prepare_items() {
        global $wpdb;

        include(ABSPATH . 'wp-content/themes/contacts-db-child/users.php');

        /*Define the column headers, including the sortable and hidden columns (if any).*/
        $columns = $this->get_columns();
        $hidden = array();
        $sortable = array();
        $this->_column_headers = array($columns, $hidden, $sortable);

        //Build the users portion of the SQL statement.
        $sel_users = "";
        for ($i = 0; $i <= count($users)-1; $i++) {
            if ($i == count($users)-1) {
                    $sel_users .= "P.post_author = ". $users[$i];
            } else {
                    $sel_users .= "P.post_author = ". $users[$i] . " or ";

        $user_query = "SELECT P.ID, P.post_title, P.post_date, P.post_author
            FROM wp_posts AS P
                P.post_type = 'contact' and
                P.post_status = 'publish' and
                (" . $sel_users . ")
            ORDER BY post_title";

        $query_results = $wpdb->get_results( $user_query, ARRAY_A  );

        $this->items = $query_results;

/*Add the new 'Reports' page to the admin menu.*/
function tv_add_menu_items(){
    $reports_page = "contact_reports.php";
    add_menu_page('Reports', 'Reports', 'manage_options', $reports_page, 'tv_render_list_page', 'dashicons-clipboard', 6);
add_action('admin_menu', 'tv_add_menu_items');

/* This function will add all content to the 'Reports' page.*/
function tv_render_list_page(){
    //Create an instance of our list class
    $ListTable = new TV_List_Table();
    //Fetch, prepare, sort, and filter our data.
    <div class="wrap">
        <div id="icon-users" class="icon32"></div>
        <?php $ListTable->display() ?>
/*End code to add 'Reports' admin section.*/

users.php code

/*List of WordPress user IDs that will be used to generate 'Reports' admin section.*/
    $users = array(

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.