· 7 years ago · Jan 10, 2019, 05:54 AM
1<?php
2/*
3
4
5
6
7/*************************** LOAD THE BASE CLASS *******************************
8 *******************************************************************************
9 * The WP_List_Table class isn't automatically available to plugins, so we need
10 * to check if it's available and load it if necessary. In this tutorial, we are
11 * going to use the WP_List_Table class directly from WordPress core.
12 *
13 * IMPORTANT:
14 * Please note that the WP_List_Table class technically isn't an official API,
15 * and it could change at some point in the distant future. Should that happen,
16 * I will update this plugin with the most current techniques for your reference
17 * immediately.
18 *
19 * If you are really worried about future compatibility, you can make a copy of
20 * the WP_List_Table class (file path is shown just below) to use and distribute
21 * with your plugins. If you do that, just remember to change the name of the
22 * class to avoid conflicts with core.
23 *
24 * Since I will be keeping this tutorial up-to-date for the foreseeable future,
25 * I am going to work with the copy of the class provided in WordPress core.
26 */
27if(!class_exists('WP_List_Table')){
28 require_once( ABSPATH . 'wp-admin/includes/class-wp-list-table.php' );
29}
30
31
32
33
34/************************** CREATE A PACKAGE CLASS *****************************
35 *******************************************************************************
36 * Create a new list table package that extends the core WP_List_Table class.
37 * WP_List_Table contains most of the framework for generating the table, but we
38 * need to define and override some methods so that our data can be displayed
39 * exactly the way we need it to be.
40 *
41 * To display this example on a page, you will first need to instantiate the class,
42 * then call $yourInstance->prepare_items() to handle any data manipulation, then
43 * finally call $yourInstance->display() to render the table to the page.
44 *
45 * Our theme for this list table is going to be movies.
46 */
47class SBM_LMS_List_Table_Courses extends WP_List_Table {
48
49 /** ************************************************************************
50 * Normally we would be querying data from a database and manipulating that
51 * for use in your list table. For this example, we're going to simplify it
52 * slightly and create a pre-built array. Think of this as the data that might
53 * be returned by $wpdb->query()
54 *
55 * In a real-world scenario, you would make your own custom query inside
56 * this class' prepare_items() method.
57 *
58 * @var array
59 **************************************************************************/
60 var $sbm_table_name = 'result_courses';
61 var $sbm_sort_by = 'added';
62
63
64 /** ************************************************************************
65 * REQUIRED. Set up a constructor that references the parent constructor. We
66 * use the parent reference to set some default configs.
67 ***************************************************************************/
68 function __construct(){
69 global $status, $page;
70
71 //Set parent defaults
72 parent::__construct( array(
73 'singular' => 'course', //singular name of the listed records
74 'plural' => 'courses', //plural name of the listed records
75 'ajax' => false //does this table support ajax?
76 ) );
77
78 }
79
80
81 /** ************************************************************************
82 * Recommended. This method is called when the parent class can't find a method
83 * specifically build for a given column. Generally, it's recommended to include
84 * one method for each column you want to render, keeping your package class
85 * neat and organized. For example, if the class needs to process a column
86 * named 'title', it would first see if a method named $this->column_title()
87 * exists - if it does, that method will be used. If it doesn't, this one will
88 * be used. Generally, you should try to use custom column methods as much as
89 * possible.
90 *
91 * Since we have defined a column_title() method later on, this method doesn't
92 * need to concern itself with any column with a name of 'title'. Instead, it
93 * needs to handle everything else.
94 *
95 * For more detailed insight into how columns are handled, take a look at
96 * WP_List_Table::single_row_columns()
97 *
98 * @param array $item A singular item (one full row's worth of data)
99 * @param array $column_name The name/slug of the column to be processed
100 * @return string Text or HTML to be placed inside the column <td>
101 **************************************************************************/
102 function column_default($item, $column_name){
103 switch($column_name){
104 case 'user_id':
105 case 'pass_fail':
106 case 'cert_id':
107 case 'cert_expiry_type':
108 case 'cert_expires':
109 case 'added':
110 return $item[$column_name];
111 default:
112 return print_r($item,true); //Show the whole array for troubleshooting purposes
113 }
114 }
115
116
117 /** ************************************************************************
118 * Recommended. This is a custom column method and is responsible for what
119 * is rendered in any column with a name/slug of 'title'. Every time the class
120 * needs to render a column, it first looks for a method named
121 * column_{$column_title} - if it exists, that method is run. If it doesn't
122 * exist, column_default() is called instead.
123 *
124 * This example also illustrates how to implement rollover actions. Actions
125 * should be an associative array formatted as 'slug'=>'link html' - and you
126 * will need to generate the URLs yourself. You could even ensure the links
127 *
128 *
129 * @see WP_List_Table::::single_row_columns()
130 * @param array $item A singular item (one full row's worth of data)
131 * @return string Text to be placed inside the column <td> (movie title only)
132 **************************************************************************/
133 // function column_course_id($item){
134
135 // //Build row actions
136 // $actions = array(
137 // 'edit' => sprintf('<a href="?page=%s&action=%s&movie=%s">Edit</a>',$_REQUEST['page'],'edit',$item['id']),
138 // 'delete' => sprintf('<a href="?page=%s&action=%s&movie=%s">Delete</a>',$_REQUEST['page'],'delete',$item['id']),
139 // );
140
141 // //Return the title contents
142 // return sprintf('%1$s <span style="color:silver">(id:%2$s)</span>%3$s',
143 // /*$1%s*/ $item['course_id'],
144 // /*$2%s*/ $item['id'],
145 // /*$3%s*/ $this->row_actions($actions)
146 // );
147 // }
148
149 function column_course_id($item){
150
151 //Build row actions
152 $actions = array(
153 'edit' => sprintf('<a href="?page=%s&action=%s&id=%s">Edit</a>',$_REQUEST['page'],'edit',$item['id']),
154 'delete' => sprintf('<a href="?page=%s&action=%s&id=%s">Delete</a>',$_REQUEST['page'],'delete',$item['id']),
155 );
156
157 //Return the title contents
158 return sprintf('%1$s <br><span style="color:silver">(id:%2$s)</span>%3$s',
159 /*$1%s*/ get_the_title($item['course_id']),
160 /*$2%s*/ $item['course_id'],
161 /*$3%s*/ $this->row_actions($actions)
162 );
163 }
164
165 /** ************************************************************************
166 * REQUIRED if displaying checkboxes or using bulk actions! The 'cb' column
167 * is given special treatment when columns are processed. It ALWAYS needs to
168 * have it's own method.
169 *
170 * @see WP_List_Table::::single_row_columns()
171 * @param array $item A singular item (one full row's worth of data)
172 * @return string Text to be placed inside the column <td> (movie title only)
173 **************************************************************************/
174 function column_cb($item){
175 return sprintf(
176 '<input type="checkbox" name="%1$s[]" value="%2$s" />',
177 /*$1%s*/ $this->_args['singular'], //Let's simply repurpose the table's singular label ("movie")
178 /*$2%s*/ $item['id'] //The value of the checkbox should be the record's id
179 );
180 }
181
182
183 /** ************************************************************************
184 * REQUIRED! This method dictates the table's columns and titles. This should
185 * return an array where the key is the column slug (and class) and the value
186 * is the column's title text. If you need a checkbox for bulk actions, refer
187 * to the $columns array below.
188 *
189 * The 'cb' column is treated differently than the rest. If including a checkbox
190 * column in your table you must create a column_cb() method. If you don't need
191 * bulk actions or checkboxes, simply leave the 'cb' entry out of your array.
192 *
193 * @see WP_List_Table::::single_row_columns()
194 * @return array An associative array containing column information: 'slugs'=>'Visible Titles'
195 **************************************************************************/
196 function get_columns(){
197 $columns = array(
198 'cb' => '<input type="checkbox" />', //Render a checkbox instead of text
199 'course_id' => 'Course ID',
200 'user_id' => 'User ID',
201 'pass_fail' => 'Pass/Fail',
202 'cert_id' => 'Cert ID',
203 'cert_expiry_type' => 'Cert Expiry Type',
204 'cert_expires' => 'Cert Expires',
205 'added' => 'Added'
206 );
207 return $columns;
208 }
209
210
211 /** ************************************************************************
212 * Optional. If you want one or more columns to be sortable (ASC/DESC toggle),
213 * you will need to register it here. This should return an array where the
214 * key is the column that needs to be sortable, and the value is db column to
215 * sort by. Often, the key and value will be the same, but this is not always
216 * the case (as the value is a column name from the database, not the list table).
217 *
218 * This method merely defines which columns should be sortable and makes them
219 * clickable - it does not handle the actual sorting. You still need to detect
220 * the ORDERBY and ORDER querystring variables within prepare_items() and sort
221 * your data accordingly (usually by modifying your query).
222 *
223 * @return array An associative array containing all the columns that should be sortable: 'slugs'=>array('data_values',bool)
224 **************************************************************************/
225 function get_sortable_columns() {
226 $sortable_columns = array(
227 'course_id' => array('course_id',false), //true means it's already sorted
228 'user_id' => array('user_id',false),
229 'pass_fail' => array('pass_fail',false),
230 'cert_expiry_type' => array('cert_expiry_type',false),
231 'cert_expires' => array('cert_expires',false),
232 'added' => array('added',false)
233 );
234 return $sortable_columns;
235 }
236
237
238 /** ************************************************************************
239 * Optional. If you need to include bulk actions in your list table, this is
240 * the place to define them. Bulk actions are an associative array in the format
241 * 'slug'=>'Visible Title'
242 *
243 * If this method returns an empty value, no bulk action will be rendered. If
244 * you specify any bulk actions, the bulk actions box will be rendered with
245 * the table automatically on display().
246 *
247 * Also note that list tables are not automatically wrapped in <form> elements,
248 * so you will need to create those manually in order for bulk actions to function.
249 *
250 * @return array An associative array containing all the bulk actions: 'slugs'=>'Visible Titles'
251 **************************************************************************/
252 function get_bulk_actions() {
253 $actions = array(
254 'delete' => 'Delete'
255 );
256 return $actions;
257 }
258
259
260 /** ************************************************************************
261 * Optional. You can handle your bulk actions anywhere or anyhow you prefer.
262 * For this example package, we will handle it in the class to keep things
263 * clean and organized.
264 *
265 * @see $this->prepare_items()
266 **************************************************************************/
267 function process_bulk_action() {
268
269 //Detect when a bulk action is being triggered...
270 if( 'delete'===$this->current_action() ) {
271 wp_die('Items deleted (or they would be if we had items to delete)!');
272 }
273
274 }
275
276
277 /** ************************************************************************
278 * REQUIRED! This is where you prepare your data for display. This method will
279 * usually be used to query the database, sort and filter the data, and generally
280 * get it ready to be displayed. At a minimum, we should set $this->items and
281 * $this->set_pagination_args(), although the following properties and methods
282 * are frequently interacted with here...
283 *
284 * @global WPDB $wpdb
285 * @uses $this->_column_headers
286 * @uses $this->items
287 * @uses $this->get_columns()
288 * @uses $this->get_sortable_columns()
289 * @uses $this->get_pagenum()
290 * @uses $this->set_pagination_args()
291 **************************************************************************/
292 function prepare_items() {
293 global $wpdb; //This is used only if making any database queries
294
295 /**
296 * First, lets decide how many records per page to show
297 */
298 $per_page = 25;
299
300
301 /**
302 * REQUIRED. Now we need to define our column headers. This includes a complete
303 * array of columns to be displayed (slugs & titles), a list of columns
304 * to keep hidden, and a list of columns that are sortable. Each of these
305 * can be defined in another method (as we've done here) before being
306 * used to build the value for our _column_headers property.
307 */
308 $columns = $this->get_columns();
309 $hidden = array();
310 $sortable = $this->get_sortable_columns();
311
312
313 /**
314 * REQUIRED. Finally, we build an array to be used by the class for column
315 * headers. The $this->_column_headers property takes an array which contains
316 * 3 other arrays. One for all columns, one for hidden columns, and one
317 * for sortable columns.
318 */
319 $this->_column_headers = array($columns, $hidden, $sortable);
320
321
322 /**
323 * Optional. You can handle your bulk actions however you see fit. In this
324 * case, we'll handle them within our package just to keep things clean.
325 */
326 $this->process_bulk_action();
327
328
329 /**
330 * Instead of querying a database, we're going to fetch the example data
331 * property we created for use in this plugin. This makes this example
332 * package slightly different than one you might build on your own. In
333 * this example, we'll be using array manipulation to sort and paginate
334 * our data. In a real-world implementation, you will probably want to
335 * use sort and pagination data to build a custom query instead, as you'll
336 * be able to use your precisely-queried data immediately.
337 */
338 // $data = $this->example_data;
339 $table_name = $wpdb->prefix . $this->sbm_table_name;
340 $querydata = $wpdb->get_results( "SELECT * FROM $table_name" );
341 $data=array();
342 foreach ($querydata as $querydatum ){
343 array_push($data, (array)$querydatum);
344 }
345 /**
346 * This checks for sorting input and sorts the data in our array accordingly.
347 *
348 * In a real-world situation involving a database, you would probably want
349 * to handle sorting by passing the 'orderby' and 'order' values directly
350 * to a custom query. The returned data will be pre-sorted, and this array
351 * sorting technique would be unnecessary.
352 */
353 function usort_reorder($a,$b){
354 $orderby = (!empty($_REQUEST['orderby'])) ? $_REQUEST['orderby'] : 'added'; //If no sort, default to title
355 $order = (!empty($_REQUEST['order'])) ? $_REQUEST['order'] : 'asc'; //If no order, default to asc
356 $result = strcmp($a[$orderby], $b[$orderby]); //Determine sort order
357 return ($order==='asc') ? $result : -$result; //Send final sort direction to usort
358 }
359 usort($data, 'usort_reorder');
360
361
362 /***********************************************************************
363 * ---------------------------------------------------------------------
364 * vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
365 *
366 * In a real-world situation, this is where you would place your query.
367 *
368 * For information on making queries in WordPress, see this Codex entry:
369 * http://codex.wordpress.org/Class_Reference/wpdb
370 *
371 * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
372 * ---------------------------------------------------------------------
373 **********************************************************************/
374
375
376 /**
377 * REQUIRED for pagination. Let's figure out what page the user is currently
378 * looking at. We'll need this later, so you should always include it in
379 * your own package classes.
380 */
381 $current_page = $this->get_pagenum();
382
383 /**
384 * REQUIRED for pagination. Let's check how many items are in our data array.
385 * In real-world use, this would be the total number of items in your database,
386 * without filtering. We'll need this later, so you should always include it
387 * in your own package classes.
388 */
389 $total_items = count($data);
390
391
392 /**
393 * The WP_List_Table class does not handle pagination for us, so we need
394 * to ensure that the data is trimmed to only the current page. We can use
395 * array_slice() to
396 */
397 $data = array_slice($data,(($current_page-1)*$per_page),$per_page);
398
399
400
401 /**
402 * REQUIRED. Now we can add our *sorted* data to the items property, where
403 * it can be used by the rest of the class.
404 */
405 $this->items = $data;
406
407
408 /**
409 * REQUIRED. We also have to register our pagination options & calculations.
410 */
411 $this->set_pagination_args( array(
412 'total_items' => $total_items, //WE have to calculate the total number of items
413 'per_page' => $per_page, //WE have to determine how many items to show on a page
414 'total_pages' => ceil($total_items/$per_page) //WE have to calculate the total number of pages
415 ) );
416 }
417
418
419}
420
421
422
423
424
425/** ************************ REGISTER THE TEST PAGE ****************************
426 *******************************************************************************
427 * Now we just need to define an admin page. For this example, we'll add a top-level
428 * menu item to the bottom of the admin menus.
429 */
430function sbm_add_menu_items(){
431 add_menu_page('Course Results', 'Course Results', 'manage_options', 'sbm_course_results', 'sbm_render_list_page');
432} add_action('admin_menu', 'sbm_add_menu_items');
433
434
435
436
437
438/** *************************** RENDER TEST PAGE ********************************
439 *******************************************************************************
440 * This function renders the admin page and the example list table. Although it's
441 * possible to call prepare_items() and display() from the constructor, there
442 * are often times where you may need to include logic here between those steps,
443 * so we've instead called those methods explicitly. It keeps things flexible, and
444 * it's the way the list tables are used in the WordPress core.
445 */
446function sbm_render_list_page(){
447
448 //Create an instance of our package class...
449 $testListTable = new SBM_LMS_List_Table_Courses();
450 //Fetch, prepare, sort, and filter our data...
451 $testListTable->prepare_items();
452
453 ?>
454 <div class="wrap">
455
456 <div id="icon-users" class="icon32"><br/></div>
457 <h2>Course Results</h2>
458
459 <div style="background:#ECECEC;border:1px solid #CCC;padding:0 10px;margin-top:5px;border-radius:5px;-moz-border-radius:5px;-webkit-border-radius:5px;">
460 <p>
461 This will display all the course results.
462 </p>
463 <!--<p>This page demonstrates the use of the <tt><a href="http://codex.wordpress.org/Class_Reference/WP_List_Table" target="_blank" style="text-decoration:none;">WP_List_Table</a></tt> class in plugins.</p>
464 <p>For a detailed explanation of using the <tt><a href="http://codex.wordpress.org/Class_Reference/WP_List_Table" target="_blank" style="text-decoration:none;">WP_List_Table</a></tt>
465 class in your own plugins, you can view this file <a href="<?php echo admin_url( 'plugin-editor.php?plugin='.plugin_basename(__FILE__) ); ?>" style="text-decoration:none;">in the Plugin Editor</a> or simply open <tt style="color:gray;"><?php echo __FILE__ ?></tt> in the PHP editor of your choice.</p>
466 <p>Additional class details are available on the <a href="http://codex.wordpress.org/Class_Reference/WP_List_Table" target="_blank" style="text-decoration:none;">WordPress Codex</a>.</p>-->
467 </div>
468
469 <!-- Forms are NOT created automatically, so you need to wrap the table in one to use features like bulk actions -->
470 <form id="movies-filter" method="get">
471 <!-- For plugins, we also need to ensure that the form posts back to our current page -->
472 <input type="hidden" name="page" value="<?php echo $_REQUEST['page'] ?>" />
473 <!-- Now we can render the completed list table -->
474 <?php $testListTable->display() ?>
475 </form>
476
477 </div>
478 <?php
479}
480?>