Sunday, May 18, 2014

title: using wordpress WP_List_Table in the frontend WP_List_Table is a cool class to display list of items in the admin panel. You dont directly use this class rather create a child/derived class of it and override some methods defined in the base class (WP_List_Table in this case). you are done! you'll have the same wordpress look and feel, same functionality doing very little coding. But unfortunately this great piece of code is not available to use in the frontend. But tiny trick and modification can make it easy to use in the frontend part of your plugin. Here I'll tell you how i did this: firstly,create a new file which will contain a class definition that is extended from WP_List_Table.At the beginning of this file, include WP_List_Table from wp-admin/include.Probably you'll want to copy that WP_List_Table definition file into your plugin directory and include it (usually i do this).
include_once('class-wp-list-table.php'); // assuming you copied the file into your plugin directory.
class User_List extends WP_List_Table {

secondly, WP_List_Table depends on few (for the most part two) function which are not available in the frontend. copy them from wp-admin/includes/template.php and paste on the top of the file you just created. you may want to avoid copying this by modifying WP_List_Table. But I prefer to keep it intact. Required functions are:
if(!is_admin()){
	function get_submit_button( $text = NULL, $type = 'primary', $name = 'submit', $wrap = true, $other_attributes = NULL ) {
		switch ( $type ) :
			case 'primary' :
			case 'secondary' :
				$class = 'button-' . $type;
				break;
			case 'delete' :
				$class = 'button-secondary delete';
				break;
			default :
				$class = $type; // Custom cases can just pass in the classes they want to be used
		endswitch;
		$text = ( NULL == $text ) ? __( 'Save Changes' ) : $text;
	
		// Default the id attribute to $name unless an id was specifically provided in $other_attributes
		$id = $name;
		if ( is_array( $other_attributes ) && isset( $other_attributes['id'] ) ) {
			$id = $other_attributes['id'];
			unset( $other_attributes['id'] );
		}
	
		$attributes = '';
		if ( is_array( $other_attributes ) ) {
			foreach ( $other_attributes as $attribute => $value ) {
				$attributes .= $attribute . '="' . esc_attr( $value ) . '" '; // Trailing space is important
			}
		} else if ( !empty( $other_attributes ) ) { // Attributes provided as a string
			$attributes = $other_attributes;
		}
	
		$button = '';
	
		if ( $wrap ) {
			$button = '
' . $button . '
'; } /* TODO - colum_XX replace XX with your table's key/id column field value. The actions (edit, delete etc.) will be added to this column in the list */ function column_member_id($item){ //Build row actions (we are only alowing "Delete" option for this list) $actions = array(); //Use the folloiwng instead of the above if you want to add both edit and delete options for each row of the list // $actions = array( // 'edit' => sprintf('Edit',$item['id']), // 'delete' => sprintf('Delete',$_REQUEST['page'],'1',$item['id']), // ); //Return the refid column contents return $item['member_id'] . $this->row_actions($actions); } return $button; } function submit_button( $text = NULL, $type = 'primary', $name = 'submit', $wrap = true, $other_attributes = NULL ) { echo get_submit_button( $text, $type, $name, $wrap, $other_attributes ); } }
you'll notice that i wrapped the definitions with is_admin function. you can use is_front_page or function_exists. Thirdly, You need to override some methods in the child class. this is mandatory. Methods are : 1. __construct() this is typical constructor method but few things to note:
    function __construct(){
        global $status, $page;        
        //Set parent defaults
        parent::__construct( array(
            /**
            * you'll have to define a css class and you'll have to use  uncamelcased and 
            * white-space removed version ( userlist in this case)  of list name as the css class name.
            */
            'singular'  => 'User List', //singular name of the listed records
            'plural'    => 'User List', //plural name of the listed records
            'ajax'      => false //does this table support ajax?
        ) );           
   
2. column_cb() this method defines how the checkbox on the left of the table should behave. usually it represents the value of primary key of the db table. typical implementation may look like the following:
    function column_cb($item){
        return sprintf(
            '',
            /*$1%s*/ $this->_args['singular'], //Let's reuse singular label (User List)
            /*$2%s*/ $item['id'] //The value of the checkbox should be the record's key/id
        );
    }   
   
3. get_columns() you'll define which columns you want to display in the list in this method. it returns a key/value. key should be exactly the database field name and value will be the label you want to show in the table column header. 4.get_sortable_columns() you'll define which columns you want to be sortable in the function.
   function prepare_items(){
        // Lets decide how many records per page to show         
        $per_page = 30;                
        $columns = $this->get_columns();
        $hidden = array();
        $sortable = $this->get_sortable_columns();                
        $this->_column_headers = array($columns, $hidden, $sortable);                
        $this->process_bulk_action();                                      
        // This checks for sorting input and sets the sort order parameters accordingly.
        $orderby_column = isset($_GET['orderby'])?$_GET['orderby']:'';
        $sort_order = isset($_GET['order'])?$_GET['order']:'';
        if(empty($orderby_column)){//set the default orderby column if it is empty
        	$orderby_column = "member_id";
        	$sort_order = "ASC";
        }
        //Query the database table and prepare our data that will be used to list the items
    	global $wpdb;
        $query = "SELECT * FROM xyz ORDER BY $orderby_column $sort_order";        
        $data = $wpdb->get_results($query, ARRAY_A);
		//pagination requirement
        $current_page = $this->get_pagenum();        
        //pagination requirement
        $total_items = count($data);                
        //pagination requirement
        $data = array_slice($data,(($current_page-1)*$per_page),$per_page);                        
        // Now we add our *sorted* data to the items property, where it can be used by the rest of the class.
        $this->items = $data;                
        //pagination requirement
        $this->set_pagination_args( array(
            'total_items' => $total_items,                  //WE have to calculate the total number of items
            'per_page'    => $per_page,                     //WE have to determine how many items to show on a page
            'total_pages' => ceil($total_items/$per_page)   //WE have to calculate the total number of pages
        ) );   
    }
   
Lastly, Some css stuff to make the list pretty.
    .userlist{
        border: 1px solid #DFDFDF;
    }
    .userlist td, .userlist th {
        font-family: Georgia,"Times New Roman";
        font-size: 14px;
        color: #333333;
        text-align: left;
        border-style: solid;
        border-width: 1px 0;
        overflow: hidden;
        border-bottom-color: #DFDFDF;
        border-top-color: #FFFFFF;
        padding:5px;
    }
    .userlist th {
        text-shadow: 0 1px 0 rgba(255, 255, 255, 0.8);
        font-family: Georgia,"Times New Roman";
        color: #333333;
        text-align: left;    
    }
    .top{
        margin-bottom: 5px;    
    }
    .bottom{
        margin-top: 5px;
    }
    .tablenav{
        background-color: #F3F3F3;
        border: 1px solid #DDDDDD;
        height: 30px;
        padding: 4px;  
        text-align: right;  
    }
    .tablenav .tablenav-pages{  
        display:block;
        margin-top:4px 0;
        width:100%; 
    }
    .tablenav .tablenav-pages {
        cursor: default;
        font-size: 12px;
        line-height: 30px;
        color: #555555;    
    }
    .tablenav .displaying-num {
        margin-right: 10px; 
        font-size: 12px;
        font-style: italic;
        width:100%;
    }
    .tablenav .tablenav-pages a {
        border:1px solid #D1E5EE;
        background: #EEE;
    }
    .tablenav .tablenav-pages a, .tablenav-pages span.current {
        text-decoration: none;
        padding: 3px 6px;
    }
    .alternate, .alt {
        background-color: #FCFBF6;
    }
    .tablenav .actions {
        padding: 4px 8px 1px 1px;
    }
    .alignleft {
        float: left;
    }
    .actions input[type="submit"],.actions select {
        border-color: #BBBBBB;
        color: #464646;
        border-style: solid;
        background-color: #FFFFFF;
        border-width:1px;
    }
    .widefat tbody th.check-column {
        padding: 5px 0 5px;
    }
    .widefat .check-column {
        padding: 5px 0 0;
        vertical-align: top;
        width: 2.2em;    
    }
    .row-actions {
        padding: 2px 0 0;
        visibility: hidden;
    }