The WordPress class WP_List_Table (sits in "wp-admin/includes/class-wp-list-table.php" file) creates and handles all the "list tables" at admin area, like "Posts", "Users" etc. Lists are good looking, at all the screens, responsive, well structured, sortable and allow customisations, thus they are a good target to hack it in your frontend (at customer area pages, out of the admin area). And it is easy to use them in admin pages of your plugin. Even there exists a plugin "Custom List Table Example", that is designed to document the process and to give you examples. Even better, if you read the code of the class itself, you can find more settings, search function etc. But...
The class is not designed to work at frontend. It is "tied" with admin area code. So we need to create "environment" at frontend, to be possible to use it. I read lot of articled and find this code works for me:
global $hook_suffix; $hook_suffix = ''; if(isset($page_hook)) { $hook_suffix = $page_hook; } else if(isset($plugin_page)) { $hook_suffix = $plugin_page; } else if(isset($pagenow)) { $hook_suffix = $pagenow; } require_once(ABSPATH.'wp-admin/includes/screen.php' ); require_once(ABSPATH.'wp-admin/includes/class-wp-screen.php' ); require_once(ABSPATH.'wp-admin/includes/template.php' );
First part is a completely hack, the $hook_suffix variable is missing, and this is a way to set it. Second part loads need classes and functions, to be possible our class to work. And it works! But...
There is also CSS need... and I couldn't figure which one. Most likely, there also needs JS code... Anyway, to looks good it was need to set some CSS in the theme. But...
Pagination of the list table don't works at front-end. At admin area pagination procedure needs form/URL parameter "paged" ($_REQUEST['paged']) . Means, if you have a list at "http://mysite.com/my_list", with GET method the URL must be like "http://mysite.com/my_list?paged=2" to see the second page of your list. But...
Pagination at frontend works differently. Even you manually go to the URL above, the WP core don't returns the "paged" parameter (I think this is some internal redirection, but didn't checked), so the WP_List_Table class don't really knows that you want some page, different than the first one. WP core returns that info as part of the URL, like "http://mysite.com/my_list/page/2". Means, pagination at front-end produces "wrong" URL, not the one that our class expects. Problem. But... not for a programmer :-)
Let we generate the need element of the $_REQUEST array:
if(!isset($_REQUEST['paged'])) { $_REQUEST['paged'] = explode('/page/', $_SERVER['REQUEST_URI'], 2); if(isset($_REQUEST['paged'][1])) list($_REQUEST['paged'],) = explode('/', $_REQUEST['paged'][1], 2); if(isset($_REQUEST['paged']) and $_REQUEST['paged'] != '') { $_REQUEST['paged'] = intval($_REQUEST['paged']); if($_REQUEST['paged'] < 2) $_REQUEST['paged'] = ''; } else { $_REQUEST['paged'] = ''; } }
Put the code above at the beginning of the function, that you create to handle your list. Let we call it "generate_list()". Put it before the initialization of WP_List_Table class, this way we are sure that "paged" parameter exists. Now the WP_List_Table class (by your extending class) will recognize the "existing page" and will do pagination well.
You even can use pagination with your views (function "get_views()"), if you need so:
$paged = isset($_REQUEST['paged'])?$_REQUEST['paged']:'1'; $actions['do_it'] = sprintf('<a href="?paged=%s&action=%s&id=%s">%s', $paged, 'do_it', $item['id'], __('Do it', 'text-domain'));
And now we have it working. The URL now will looks like "http://mysite.com/my_list/page/3?paged=2". Who cares if it looks strange, it works - "page 3" is the starting page, "page 2" is the requested one. But...
At pagination links you also have to have "paged" parameter, to be possible to send the right signal to the class. Looks at the second code piece (where we set $_REQUEST['paged']) again, we use the URL to find the existing page if no "paged" parameter is set. So if it is set, we use it, the parameter, instead the URL info. But...
The code in the class removes the "paged" parameter from the "first page" link. So this link don't works (or we think so). With combination of our second code snippet your list will show the last page you ask before to click "first page" link, because in the link the "paged" parameter is missing, and our code generated it from the URL... So needs more code, we need to fix the "first page" link. But...
There is no good hook in the class code to fix that problem. So we need to do it outside - will use the class to generate the table and pagination links, and then will fix "first page" links. I find this good for me:
ob_start(); generate_list(); $deals = ob_get_clean(); $pagination = explode("<span class="'pagination-links", $deals); if(count($pagination) > 1) { $deals = ""; foreach($pagination as $k => $links) { $url = array(); $first = explode("<a class='first-page' href='", $links, 2); if(isset($first[1])) { $url = explode("'>", $first[1], 2); $url[0] .= (false === strpos($url[0], "?"))?"?":"&"; $links = $first[0]."</a><a class='first-page' href='".$url[0]."paged=1'>".$url[1]; } $deals .= $links; if($k < count($pagination)-1) $deals .= "<span class='pagination-links"; } } echo $deals;
We capture the output and modify it, just to add the missing parameter. Note the " and ' - they are important. The class produces the link with HTML attributes in ', and we read the output of the class directly.
Well, that's all, folks, it works. Use it and do your front end pages beauty. :-) If you want to see a life example, check the "My deals" page (in the user's account) at BitcoinBorsa.bg site.
Limitations:
- Can not use another routine, that need "paged" parameter at the same page with your list - either pagination, or the other code will not works.
- All the code above is for the front-end lists only. Admin area lists works without it, thus you must be careful, if you use your extension class both in front-end and admin area.