* @license GPL-2.0+ * @link http://barn2.co.uk * @copyright 2016 Barn2 Media * * @wordpress-plugin * Plugin Name: Posts Table with Search & Sort * Description: This plugin provides a shortcode to show a list of your site's posts in a searchable and sortable table. * Version: 1.0.3 * Author: Barn2 Media * Author URI: http://barn2.co.uk * Text Domain: posts-data-table * Domain Path: /languages * License: GPL-2.0+ * License URI: http://www.gnu.org/licenses/gpl-2.0.txt */ // Prevent direct file access if ( ! defined ( 'ABSPATH' ) ) { exit; } class Posts_Data_Table_Plugin { private $shortcode = 'posts_data_table'; private $shortcode_defaults = array( 'columns' => 'title,content,date,author,category', 'rows_per_page' => 20, 'sort_by' => 'date', 'sort_order' => '', 'category' => '', 'search_on_click' => true, 'wrap' => true, 'content_length' => 15, 'scroll_offset' => 15 ); private $table_count = 1; public function __construct() { // Load the text domain - should go on 'plugins_loaded' hook add_action( 'plugins_loaded', array( $this, 'load_textdomain' ) ); // Register shortcode add_shortcode( $this->shortcode, array( $this, 'shortcode' ) ); // Register styles and scripts add_action( 'wp_enqueue_scripts', array( $this, 'register_styles' ) ); add_action( 'wp_enqueue_scripts', array( $this, 'register_scripts' ) ); } public function load_textdomain() { load_plugin_textdomain( 'posts-data-table', false, dirname( plugin_basename( __FILE__ ) ) . '/languages' ); } public function register_styles() { wp_enqueue_style( 'posts-data-table', plugins_url( 'assets/css/posts-data-table.min.css', __FILE__ ) ); //wp_enqueue_style( 'data-tables-plugin', plugins_url( 'assets/css/datatables.css', __FILE__ ) ); //wp_enqueue_style( 'posts-data-table', plugins_url( 'assets/css/posts-data-table.css', __FILE__ ), array( 'data-tables-plugin' ) ); } public function register_scripts() { wp_enqueue_script( 'posts-data-table', plugins_url( 'assets/js/posts-data-table.min.js', __FILE__ ), array( 'jquery' ), false, true ); //wp_enqueue_script( 'data-tables-plugin', plugins_url( 'assets/js/datatables.js', __FILE__ ), array( 'jquery' ), false, true ); //wp_enqueue_script( 'posts-data-table', plugins_url( 'assets/js/posts-data-table.js', __FILE__ ), array( 'data-tables-plugin' ), false, true ); $locale = get_locale(); $supported_locales = $this->get_supported_locales(); // Add language file to script if locale is supported (English file is not added as this is the default language) if ( array_key_exists( $locale, $supported_locales ) ) { wp_localize_script( 'pdt-script', 'posts_data_table', array( 'langurl' => $supported_locales[$locale] ) ); } } /** * Handles our posts data table shortcode. * * @param array $atts The attributes passed in to the shortcode * @param string $content The content passed to the shortcode (not used) * @return string The shortcode output */ public function shortcode( $atts, $content = '' ) { $atts = shortcode_atts( $this->shortcode_defaults, $atts, $this->shortcode ); return $this->get_posts_data_table( $atts ); } /** * Returns an array of locales supported by the plugin. * The array returned uses the locale as the array key mapped to the URL of the corresponding translation file. * * @return array The supported locales */ private function get_supported_locales() { $lang_file_base_url = plugins_url( 'languages/data-tables/', __FILE__ ); return array( 'es_ES' => $lang_file_base_url . 'Spanish.json', 'fr_FR' => $lang_file_base_url . 'French.json', 'de_DE' => $lang_file_base_url . 'German.json', ); } /** * Retrieves a data table containing a list of posts based on the specified arguments. * * @param array $args An array of options used to display the posts table * @return string The posts table HTML output */ private function get_posts_data_table( $args ) { if ( empty( $args['columns'] ) ) { $args['columns'] = $this->shortcode_defaults['columns']; } $args['rows_per_page'] = filter_var( $args['rows_per_page'], FILTER_VALIDATE_INT ); if ( ($args['rows_per_page'] < 1) || !$args['rows_per_page'] ) { $args['rows_per_page'] = false; } if ( !in_array( $args['sort_by'], array('id', 'title', 'category', 'date', 'author', 'content') ) ) { $args['sort_by'] = $this->shortcode_defaults['sort_by']; } if ( !in_array( $args['sort_order'], array('asc', 'desc') ) ) { $args['sort_order'] = $this->shortcode_defaults['sort_order']; } // Set default sort direction if ( !$args['sort_order'] ) { if ( $args['sort_by'] === 'date' ) { $args['sort_order'] = 'desc'; } else { $args['sort_order'] = 'asc'; } } $args['search_on_click'] = filter_var( $args['search_on_click'], FILTER_VALIDATE_BOOLEAN ); $args['wrap'] = filter_var( $args['wrap'], FILTER_VALIDATE_BOOLEAN ); $args['content_length'] = filter_var( $args['content_length'], FILTER_VALIDATE_INT ); $args['scroll_offset'] = filter_var( $args['scroll_offset'], FILTER_VALIDATE_INT ); $date_format = 'Y/m/d'; $output = $table_head = $table_body = $body_row_fmt = ''; // Start building the args needed for our posts query $post_args = array( 'post_type' => 'post', 'posts_per_page' => -1, 'post_status' => 'publish', 'suppress_filters' => false // Ensure WPML filters run on this query ); if ( $args['category'] ) { $category = get_category_by_slug( $args['category'] ); if ( $category ) { $post_args['category_name'] = $category->slug; } } // Get all published posts in the current language $all_posts_curr_lang = get_posts( $post_args ); if ( is_array( $all_posts_curr_lang ) && $all_posts_curr_lang ) { // if we have posts /** * Define an array of all possible columns and their default heading, priority, and column width. * * Priority values are used to determine visiblity at small screen sizes (1 = highest priority, 6 = lowest priority). * Column widths are automatically calculated by DataTables, but can be overridden by using filter 'posts_data_table_column_defaults'. */ $column_defaults = array( 'id' => array( 'heading' => __('ID', 'posts-data-table'), 'priority' => 3, 'width' => '' ), 'title' => array( 'heading' => __('Title', 'posts-data-table'), 'priority' => 1, 'width' => '' ), 'category' => array( 'heading' => __('Categories', 'posts-data-table'), 'priority' => 6, 'width' => '' ), 'date' => array( 'heading' => __('Date', 'posts-data-table'), 'priority' => 2, 'width' => '' ), 'author' => array( 'heading' => __('Author', 'posts-data-table'), 'priority' => 4, 'width' => '' ), 'content' => array( 'heading' => __('Content', 'posts-data-table'), 'priority' => 5, 'width' => '' ), ); $all_columns = array_keys( $column_defaults ); // Allow users to override defaults //@todo Allow this filter to be used per table $column_defaults = apply_filters( 'posts_data_table_column_defaults', $column_defaults ); // Get the columns to be used in this table $columns = array_map( 'trim', explode( ',', strtolower( $args['columns'] ) ) ); // If none of the user-specfied columns are valid, use the default columns instead if ( !array_intersect( $all_columns, $columns ) ) { $columns = explode( ',', $this->shortcode_defaults['columns'] ); } // Build table header $heading_fmt = '%4$s'; $cell_fmt = '%s'; foreach( $columns as $column ) { if ( array_key_exists( $column, $column_defaults ) ) { // Double-check column name is valid // Add heading to table $table_head .= sprintf( $heading_fmt, $column, $column_defaults[$column]['priority'], $column_defaults[$column]['width'], $column_defaults[$column]['heading'] ); // Add placeholder to table body format string so that content for this column is included in table output $body_row_fmt .= sprintf($cell_fmt, '{' . $column . '}'); } } $sort_column = $args['sort_by']; $sort_index = array_search( $sort_column, $columns ); if ( $sort_index === false && array_key_exists( $sort_column, $column_defaults ) ) { // Sort column is not in list of displayed columns so we'll add it as a hidden column at end of table $table_head .= sprintf( '%2$s', $sort_column, $column_defaults[$sort_column]['heading'] ); // Make sure data for this column is included in table content $body_row_fmt .= sprintf($cell_fmt, $sort_column); // Set the sort column index to be this hidden column $sort_index = count($columns); } $table_head = sprintf( '%s', $table_head ); // end table header // Build table body $body_row_fmt = '' . $body_row_fmt . ''; // Loop through posts and add a row for each foreach ( (array) $all_posts_curr_lang as $_post ) { setup_postdata( $_post ); // Format title $title = sprintf( '%2$s', get_permalink($_post), get_the_title( $_post ) ); // Format author $author = sprintf( '', esc_url( get_author_posts_url( $_post->post_author ) ), esc_attr( sprintf( __( 'Posts by %s' ), get_the_author() ) ), get_the_author() ); $post_data_trans = array( '{id}' => $_post->ID, '{title}' => $title, '{category}' => get_the_category_list( ', ', '', $_post->ID ), '{date}' => get_the_date( $date_format, $_post ), '{author}' => $author, '{content}' => $this->get_post_content( $args['content_length'] ) ); $table_body .= strtr( $body_row_fmt, $post_data_trans ); } // foreach post wp_reset_postdata(); $table_body = sprintf( '%s', $table_body ); // end table body $paging_attr = 'false'; if ( ( $args['rows_per_page'] > 1 ) && ( $args['rows_per_page'] < count($all_posts_curr_lang) ) ) { $paging_attr = 'true'; } $order_attr = ( $sort_index === false ) ? '' : sprintf( '[[%u, "%s"]]', $sort_index, $args['sort_order'] ); $offset_attr = ( $args['scroll_offset'] === false ) ? 'false' : $args['scroll_offset']; $table_class = 'posts-data-table'; if ( !$args['wrap'] ) { $table_class .= ' nowrap'; } $output = sprintf( '' . '%8$s%9$s' . '
', $this->table_count, esc_attr( $table_class ), esc_attr( $args['rows_per_page'] ), esc_attr( $paging_attr ), esc_attr( $order_attr ), ( $args['search_on_click'] ? 'true' : 'false' ), esc_attr( $offset_attr ), $table_head, $table_body ); $this->table_count++; } // if posts found return $output; } // get_comments_list /** * Retrieve the post content, truncated to the number of words specified by $num_words. * * Must be called with the Loop or a secondary loop after a call to setup_postdata(). * * @param int $num_words The number of words to trim the content to * @return string The (truncated) post content */ private function get_post_content( $num_words = 15 ) { $text = get_the_content(''); $text = strip_shortcodes( $text ); $text = apply_filters( 'the_content', $text ); $text = wp_trim_words( $text, $num_words, ' …' ); return $text; } } // end class $posts_data_table = new Posts_Data_Table_Plugin();