Create New Item
Item Type
File
Folder
Item Name
Search file in folder and subfolders...
Are you sure want to rename?
optometrists
/
cache
/
cache
/
cache
/
.wp-cli
/
wp-content
/
plugins
/
wp-data-access
/
WPDataAccess
/
List_Table
:
WPDA_List_Table.php
Advanced Search
Upload
New Item
Settings
Back
Back Up
Advanced Editor
Save
<?php /** * Suppress "error - 0 - No summary was found for this file" on phpdoc generation * * @package WPDataAccess\List_Table */ namespace WPDataAccess\List_Table; use WPDataAccess\Connection\WPDADB; use WPDataAccess\Dashboard\WPDA_Dashboard; use WPDataAccess\Data_Dictionary\WPDA_Dictionary_Exist; use WPDataAccess\Data_Dictionary\WPDA_Dictionary_Lists; use WPDataAccess\Data_Dictionary\WPDA_List_Columns; use WPDataAccess\Macro\WPDA_Macro; use WPDataAccess\Plugin_Table_Models\WPDA_CSV_Uploads_Model; use WPDataAccess\Plugin_Table_Models\WPDA_Media_Model; use WPDataAccess\Utilities\WPDA_Import; use WPDataAccess\Utilities\WPDA_Message_Box; use WPDataAccess\Utilities\WPDA_Repository; use WPDataAccess\Wordpress_Original; use WPDataAccess\WPDA; use WPDataProjects\WPDP; use WPDataAccess\Plugin_Table_Models\WPDA_Table_Settings_Model; use WPDataAccess\Plugin_Table_Models\WPDP_Project_Design_Table_Model; /** * Class WPDA_List_Table * * This class extends WordPress class WP_List_Table. The WordPress WP_List_Table is contained in the package as * advised by WordPress and might be updated in future releases. * * @author Peter Schulz * @since 1.0.0 */ class WPDA_List_Table extends Wordpress_Original\WP_List_Table { /** * Base table for list of all tables */ const LIST_BASE_TABLE = 'information_schema.tables'; /** * Default value bulk actions enabled */ const DEFAULT_BULK_ACTIONS_ENABLED = true; /** * Default value bulk export enabled */ const DEFAULT_BULK_EXPORT_ENABLED = true; /** * Default value bulk search enabled */ const DEFAULT_SEARCH_BOX_ENABLED = true; /** * Table row number within the current response * * @var int */ protected static $list_number = 0; /** * Menu slug of the current page * * @var string */ protected $page; /** * Page title * * @var string */ protected $title; /** * Page subtitle * * @var string */ protected $subtitle = ''; protected $show_page_title = true; /** * Database schema name * * @var string */ protected $schema_name = ''; /** * Database table name * * @var string */ protected $table_name = ''; /** * Where clause * * @var string */ protected $where = ''; /** * Order by clause * * @var string */ protected $orderby = ''; /** * Columns in query * * @var array */ protected $columns_queried; /** * Number of rows displayed in list box * * @var int */ protected $items_per_page; /** * Page number * * @var int */ protected $current_page = 1; /** * Add search box? * * @var bool */ protected $search_box_enabled = self::DEFAULT_SEARCH_BOX_ENABLED; /** * Enable bulk actions? * * @var bool */ protected $bulk_actions_enabled = self::DEFAULT_BULK_ACTIONS_ENABLED; /** * Enable exports? * * @var bool */ protected $bulk_export_enabled = self::DEFAULT_BULK_EXPORT_ENABLED; /** * Show view link? * * @var string on|off */ protected $show_view_link = null; /** * Allow inserts? * * @var string on|off */ protected $allow_insert = null; /** * Allow updates? * * @var string on|off */ protected $allow_update = null; /** * Allow deletes? * * @var string on|off */ protected $allow_delete = null; /** * Hides tablenav * * @var bool */ protected $hide_navigation = false; /** * Reference to column list * * @var WPDA_List_Columns */ protected $wpda_list_columns; /** * Reference to dictionary object * * @var WPDA_Dictionary_Exist */ protected $wpda_data_dictionary; /** * Column headers (labels) * * @var array */ protected $column_headers; /** * Reference to import object * * @var WPDA_Import */ protected $wpda_import = null; /** * Child tab clicked (used for parent child relationships only) * * @var null */ protected $child_tab = ''; /** * Search string (entered by user or taken from cookie) * * @var null|string */ protected $search_value = null; /** * Previous search string * * @var null|string */ protected $search_value_old = null; /** * Page number item name (default 'page_number') * * The name can be changed for pages on multiple levels. This is needed to get back to the right page in * parent-child page. * * @var string */ protected $page_number_item_name = 'page_number'; /** * Real page number * * @var null */ protected $page_number_link = ''; /** * Page number text item * * @var null */ protected $page_number_item = ''; /** * Make the default accessible to other classes */ const SEARCH_ITEM_NAME_DEFAULT = 'wpda_s'; /** * Name of search item (WP default 's') * * @var string */ protected $search_item_name = self::SEARCH_ITEM_NAME_DEFAULT; /** * Name of the column containing action links * * Default is the first column displayed * * @var string */ protected $first_display_column = ''; /** * Table settings * * @var null|object */ protected $wpda_table_settings = null; /** * Project table settings * * @var null|object */ protected $wpda_project_table_settings = null; /** * Show help icon if help url is available * * @var null|string */ protected $help_url = null; /** * Store columns in array with column name as index for fast access * * @var array */ protected $columns_indexed = array(); /** * Variables used to store table estimate row count data * * @var string */ protected $table_engine = ''; /** * Table rows * * @var mixed|string */ protected $table_rows = ''; /** * Estimated row count * * @var array|int */ protected $row_count_estimate = array(); /** * Project page id * * Helper to hide schema and table name in export links on web pages * * @var null */ protected $pid = null; /** * Cache columns * * @var null */ protected $wpda_cached_columns = null; /** * WPDA_List_Table constructor * * A table name must be provided in the constructor call. The table must be a valid MySQL database table to * which access (to the back-end) is granted. If no table name is provided, the table does not exist or access * to the back-end for the given table is not granted, processing is stopped. It makes no sense to continue * without a valid table. A check for table existence is performed to prevent SQL injection. * * There are two types of tables to build a list table on: * + List of tables in the WordPress database schema * + List of rows in a specific table * * To build a list of tables in the WordPress database schema, we query table 'information_schema.tables' * (which is in fact a view). This is the only query allowed outside the WordPress database schema. The * table/view name is stored in constant WPDA_List_Table::LIST_BASE_TABLE for validation purposes. * * A list of rows for a specific table is based on WordPress class WP_List_Table. * * WPDA_List_Table can be used to build list tables for views as well. View based list tables however, do not * support insert, update, delete, import and export actions. * * A table name is not the only thing we need to build a list table. We also need to have access to the * table columns. If no table columns are provided execution is stopped as well. * * @param array $args [ * * 'table_name' => (string) Database table name * * 'wpda_list_columns' => (object) Reference to a WPDA_List_Columns object * * 'singular' => (string) Singular object label * * 'plural' => (string) plural object label * * 'ajax' => (string) TRUE = list table support Ajax * * 'column_headers' => (array|string) Column headers * * 'title' => (string) Page title * * 'subtitle' => (string) Page subtitle * * 'bulk_export_enabled' => (boolean) * * 'search_box_enabled' => (boolean) * * 'bulk_actions_enabled' => (boolean) * * 'show_view_link' => (string) on|off * * 'allow_insert' => (string) on|off * * 'allow_update' => (string) on|off * * 'allow_delete' => (string) on|off * * 'allow_import' => (string) on|off * * 'hide_navigation' => (boolean) * * 'default_where' => (string) * * ] * @since 1.0.0 */ public function __construct( $args = array() ) { global $wpdb; if ( !isset( $args['table_name'] ) ) { // Calling WPDA_List_Table without a table_name doesn't make sense. wp_die( __( 'ERROR: Wrong arguments [no table argument]', 'wp-data-access' ) ); } if ( !isset( $args['wpda_list_columns'] ) ) { // Calling WPDA_List_Table without a column list is not allowed. wp_die( __( 'ERROR: Wrong arguments [no columns argument]', 'wp-data-access' ) ); } parent::__construct( array( 'singular' => ( isset( $args['singular'] ) ? $args['singular'] : __( 'Row', 'wp-data-access' ) ), 'plural' => ( isset( $args['plural'] ) ? $args['plural'] : __( 'Rows', 'wp-data-access' ) ), 'ajax' => ( isset( $args['ajax'] ) ? $args['ajax'] : false ), ) ); // Set schema name is available. if ( isset( $args['wpdaschema_name'] ) ) { $this->schema_name = $args['wpdaschema_name']; } // Set table name (availability already checked). $this->table_name = $args['table_name']; if ( self::LIST_BASE_TABLE !== $this->table_name ) { // Whenever a table name is provided through a URL we are risking an SQL injection attack. Our // defence mechanism here is to check whether the table name provided exists in our database. // If it does not we'll terminate the process with an error. // Although this check is only needed when a table name is provided through the URL we will perform // it in all situations. It is a fast query which makes our application much more safe and reliable. $this->wpda_data_dictionary = new WPDA_Dictionary_Exist($this->schema_name, $this->table_name); if ( !$this->wpda_data_dictionary->table_exists() ) { wp_die( __( 'ERROR: Invalid table name or not authorized', 'wp-data-access' ) ); } } $this->pid = ( isset( $args['pid'] ) ? $args['pid'] : '' ); // Get menu slag of current page. if ( isset( $_REQUEST['page'] ) ) { $this->page = sanitize_text_field( wp_unslash( $_REQUEST['page'] ) ); // input var okay. } else { // In order to show a list table we need a page. wp_die( __( 'ERROR: Wrong arguments [no page argument]', 'wp-data-access' ) ); } // Use column list: argument wpda_list_columns (availability already checked). $this->wpda_list_columns =& $args['wpda_list_columns']; // Set pagination. if ( is_admin() ) { $this->items_per_page = WPDA::get_option( WPDA::OPTION_BE_PAGINATION ); } else { $this->items_per_page = WPDA::get_option( WPDA::OPTION_FE_PAGINATION ); } // Overwrite column header text if column headers were provided. $this->column_headers = ( isset( $args['column_headers'] ) ? $args['column_headers'] : '' ); // Set title. if ( isset( $args['title'] ) ) { $this->title = $args['title']; } else { $this->title = __( 'Table', 'wp-data-access' ) . ' ' . strtoupper( $this->table_name ); } // Set subtitle. if ( isset( $args['subtitle'] ) ) { $this->subtitle = $args['subtitle']; } else { $wp_tables = $wpdb->tables( 'all', true ); if ( isset( $wp_tables[substr( $this->table_name, strlen( $wpdb->prefix ) )] ) ) { $this->subtitle = '<span class="dashicons dashicons-warning"></span> ' . WPDA::get_table_type_text( WPDA::TABLE_TYPE_WP ); } elseif ( WPDA::is_wpda_table( $this->table_name ) ) { $this->subtitle = '<span class="dashicons dashicons-warning"></span> ' . WPDA::get_table_type_text( WPDA::TABLE_TYPE_WPDA ); } } if ( !(isset( $args['allow_import'] ) && 'off' === $args['allow_import']) ) { try { // Instantiate WPDA_Import. $this->wpda_import = new WPDA_Import(( is_admin() ? "?page={$this->page}" : '' ), $this->schema_name, $this->table_name); } catch ( \Exception $e ) { // If import is turned off instantiation will fail. Handle is set to null (check in future calls). $this->wpda_import = null; } } if ( isset( $args['bulk_export_enabled'] ) ) { $this->bulk_export_enabled = $args['bulk_export_enabled']; } if ( isset( $args['search_box_enabled'] ) ) { $this->search_box_enabled = $args['search_box_enabled']; } if ( isset( $args['bulk_actions_enabled'] ) ) { $this->bulk_actions_enabled = $args['bulk_actions_enabled']; } if ( 'on' !== WPDA::get_option( WPDA::OPTION_BE_VIEW_LINK ) ) { $this->show_view_link = WPDA::get_option( WPDA::OPTION_BE_VIEW_LINK ); } else { if ( isset( $args['show_view_link'] ) ) { $this->show_view_link = $args['show_view_link']; } else { $this->show_view_link = WPDA::get_option( WPDA::OPTION_BE_VIEW_LINK ); } } if ( 'on' !== WPDA::get_option( WPDA::OPTION_BE_ALLOW_INSERT ) ) { $this->allow_insert = WPDA::get_option( WPDA::OPTION_BE_ALLOW_INSERT ); } else { if ( isset( $args['allow_insert'] ) ) { $this->allow_insert = $args['allow_insert']; } else { $this->allow_insert = WPDA::get_option( WPDA::OPTION_BE_ALLOW_INSERT ); } } if ( 'on' !== WPDA::get_option( WPDA::OPTION_BE_ALLOW_UPDATE ) ) { $this->allow_update = WPDA::get_option( WPDA::OPTION_BE_ALLOW_UPDATE ); } else { if ( isset( $args['allow_update'] ) ) { $this->allow_update = $args['allow_update']; } else { $this->allow_update = WPDA::get_option( WPDA::OPTION_BE_ALLOW_UPDATE ); } } if ( 'on' !== WPDA::get_option( WPDA::OPTION_BE_ALLOW_DELETE ) ) { $this->allow_delete = WPDA::get_option( WPDA::OPTION_BE_ALLOW_DELETE ); } else { if ( isset( $args['allow_delete'] ) ) { $this->allow_delete = $args['allow_delete']; } else { $this->allow_delete = WPDA::get_option( WPDA::OPTION_BE_ALLOW_DELETE ); } } if ( isset( $args['hide_navigation'] ) ) { $this->hide_navigation = $args['hide_navigation']; } $this->search_value = ( is_scalar( $this->get_search_value() ) ? str_replace( "\\'", '', (string) $this->get_search_value() ) : '' ); if ( isset( $_REQUEST["{$this->search_item_name}_old_value"] ) ) { $this->search_value_old = sanitize_text_field( wp_unslash( $_REQUEST["{$this->search_item_name}_old_value"] ) ); // input var okay. } else { $this->search_value_old = $this->search_value; } // Get page number(s). if ( 'page_number' !== $this->page_number_item_name ) { if ( isset( $_REQUEST['page_number'] ) ) { $requested_page_number = sanitize_text_field( wp_unslash( $_REQUEST['page_number'] ) ); // input var okay. $this->page_number_link = '&page_number=' . $requested_page_number; $this->page_number_item = "<input type='hidden' name='page_number' value='" . $requested_page_number . "' />"; } } $this->page_number_link .= '&paged=' . $this->get_pagenum(); $this->page_number_item .= "<input type='hidden' name='" . $this->page_number_item_name . "' value='" . $this->get_pagenum() . "' />"; // Add search arguments to link to return to same page. foreach ( $_REQUEST as $key => $value ) { if ( substr( $key, 0, 19 ) === 'wpda_search_column_' ) { if ( is_array( $value ) ) { foreach ( $value as $elem_key => $elem_value ) { $this->page_number_link .= "&{$elem_key}={$elem_value}"; $this->page_number_item .= "<input type='hidden' name='{$key}[]' value='{$elem_value}' />"; } } else { $this->page_number_link .= "&{$key}={$value}"; $this->page_number_item .= "<input type='hidden' name='{$key}' value='{$value}' />"; } } } // Check if a WHERE clause (filter) was defined. if ( isset( $args['default_where'] ) ) { $this->where = $args['default_where']; } // Get table settings. $wpda_table_settings = WPDA_Table_Settings_Model::query( $this->table_name, $this->schema_name ); if ( isset( $wpda_table_settings[0]['wpda_table_settings'] ) ) { $this->wpda_table_settings = json_decode( $wpda_table_settings[0]['wpda_table_settings'] ); } // Get estimated row count. $this->row_count_estimate = WPDA::get_row_count_estimate( $this->schema_name, $this->table_name, $this->wpda_table_settings ); $this->table_rows = $this->row_count_estimate['row_count']; // Get project table settings. global $wpda_project_mode; if ( is_array( $wpda_project_mode ) ) { $setname = $wpda_project_mode['setname']; $wpda_project_table_settings = null; $wpda_project_table_settings_db = WPDP_Project_Design_Table_Model::static_query( $this->schema_name, $this->table_name, $setname ); if ( isset( $wpda_project_table_settings_db->tableinfo ) ) { $this->wpda_project_table_settings = $wpda_project_table_settings_db->tableinfo; } } if ( isset( $args['show_page_title'] ) && false === $args['show_page_title'] ) { $this->show_page_title = $args['show_page_title']; } if ( isset( $args['help_url'] ) ) { $this->help_url = $args['help_url']; } foreach ( $this->wpda_list_columns->get_table_columns() as $table_columns ) { $this->columns_indexed[$table_columns['column_name']] = $table_columns; } } /** * Overwrite method * * @return int|void */ public function get_pagenum() { if ( isset( $_REQUEST[$this->page_number_item_name] ) ) { $pagenum = ( isset( $_REQUEST[$this->page_number_item_name] ) ? absint( $_REQUEST[$this->page_number_item_name] ) : 0 ); if ( isset( $this->_pagination_args['total_pages'] ) && $pagenum > $this->_pagination_args['total_pages'] ) { $pagenum = $this->_pagination_args['total_pages']; } return max( 1, $pagenum ); } else { return parent::get_pagenum(); } } /** * Set columns to be queries * * Set columns to be queried and shown in the list table. * By default all columns in the table will be displayed. * * @param mixed $columns_queried Column list. * * @since 1.0.0 */ public function set_columns_queried( $columns_queried ) { $this->columns_queried = $columns_queried; } /** * Enable or disable bulk actions * * @param boolean $bulk_actions_enabled TRUE = allowed, FALSE = not allowed. * * @since 1.0.0 */ public function set_bulk_actions_enabled( $bulk_actions_enabled ) { $this->bulk_actions_enabled = $bulk_actions_enabled; } /** * Enable or disable bulk export options * * @param boolean $bulk_export_enabled TRUE = allowed, FALSE = not allowed. * * @since 1.0.0 */ public function set_bulk_export_enabled( $bulk_export_enabled ) { $this->bulk_export_enabled = $bulk_export_enabled; } /** * Show or hide search box * * @param boolean $search_box_enabled TRUE = show search box, FALSE = do not show search bow. * * @since 1.0.0 */ public function set_search_box_enabled( $search_box_enabled ) { $this->search_box_enabled = $search_box_enabled; } /** * Set page title * * @param string $title Page title. * * @since 1.0.0 */ public function set_title( $title ) { $this->title = $title; } /** * Set page subtitle * * @param string $subtitle Page subtitle. * * @since 1.0.0 */ public function set_subtitle( $subtitle ) { $this->subtitle = $subtitle; } /** * I18n text displayed when no data found * * @since 1.0.0 */ public function no_items() { echo __( 'No data found', 'wp-data-access' ); } /** * Use this method to build parent child relationships. * * Overwrite this function if you want to use the list table as a child list table related to some parent * form. You can add parent arguments to calls to make sure you get back to the right parent. * * @param array $item Column info. * @return string String contains parent arguments. * @since 1.5.0 */ protected function add_parent_args_as_string( $item ) { return ''; } /** * Render columns * * Three links are provided for every list table row: * + 'View' => Link to view form (readonly data entry form) * + 'Edit' => Link to data entry form (updatable) * + 'Delete' => Link to delete record from database table * * Links to view, edit and delete records are only available if a primary key for the table is found. Without * a primary key unique identification of records is not possible. * * Links to action are offered through hidden forms to prevent long URL's and problem when refreshing pages * manually. More information about how and why is provided in source code at place where I thought the * information could be helpful. * * @param array $item Column info. * @param string $column_name Column name. * * @return mixed Value display in list table column * @since 1.0.0 */ public function column_default( $item, $column_name ) { if ( $this->wpda_list_columns->get_table_columns()[0]['column_name'] === $column_name || $column_name === $this->first_display_column ) { // First column: add row actions. $count = count( $this->wpda_list_columns->get_table_primary_key() ); //phpcs:ignore - 8.1 proof if ( 0 === $count ) { // No actions without a primary key! // This automatically covers view processing correctly. return $this->render_column_content( $item, $column_name ) . $this->row_actions( array( 'wpda_hide_column' => '', ) ); } else { // Check rights. if ( 'off' === $this->show_view_link && 'off' === $this->allow_update && 'off' === $this->allow_delete ) { // No rights! $actions = array(); $this->column_default_add_action( $item, $column_name, $actions ); if ( is_array( $actions ) && count( $actions ) > 0 ) { //phpcs:ignore - 8.1 proof return sprintf( '%1$s %2$s', $this->render_column_content( $item, $column_name ), $this->row_actions( $actions ) ); } else { return sprintf( '%1$s', $this->render_column_content( $item, $column_name ) ); } } // Check if row security is enabled. $row_security = isset( $this->wpda_table_settings->table_settings->row_level_security ) && 'true' === $this->wpda_table_settings->table_settings->row_level_security; // To prevent our URLs containing many arguments, we'll use post to submit row actions. Since our // list table is build within a form and we cannot use nested forms we'll use a container (id = // wpda_invisible_container) defined outside the list table, and add our forms to that container. // We'll use jQuery to add our forms to the container. From the links in our rows we can then just // submit any form in that container with jQuery as well. $form_id = '_' . self::$list_number++; $wp_nonce_keys = ''; $row_security_keys = ''; // We need to add keys and values for multi-column primary keys. foreach ( $this->wpda_list_columns->get_table_primary_key() as $key ) { $wp_nonce_keys .= "-{$item[$key]}"; if ( $row_security ) { $row_security_keys .= "-{$key}-{$item[$key]}"; } } if ( $row_security ) { $row_security_nonce = wp_create_nonce( "wpda-row-level-security-{$this->table_name}{$row_security_keys}" ); $row_security_nonce_field = "<input type='hidden' name='rownonce' value='" . esc_attr( $row_security_nonce ) . "' />"; } else { $row_security_nonce_field = ''; } // Prepare url. if ( is_admin() ) { $url = "?page={$this->page}"; } else { $url = ''; } if ( 'on' === $this->show_view_link ) { $wp_nonce_action = "wpda-query-{$this->table_name}{$wp_nonce_keys}"; $wp_nonce = wp_create_nonce( $wp_nonce_action ); // Build the row action. Use jQuery to add form to container. // All items escaped in create_post_form(). $view_form = $this->create_post_form( $item, "view_form{$form_id}", 'view', $url, $wp_nonce, $row_security_nonce_field, $this->schema_name, $this->table_name ); ?> <script type='text/javascript'> jQuery("#wpda_invisible_container").append("<?php echo $view_form; // phpcs:ignore WordPress.Security.EscapeOutput ?>"); </script> <?php // Add link to submit form. $actions['view'] = sprintf( '<a href="javascript:void(0)" class="edit wpda_tooltip" title="%s" onclick="jQuery(\'#%s\').submit()"> <span style="white-space: nowrap"> <i class="fas fa-eye wpda_icon_on_button"></i> %s </span> </a> ', __( 'View row data', 'wp-data-access' ), "view_form{$form_id}", __( 'View', 'wp-data-access' ) ); } if ( 'on' === $this->allow_update || WPDA::is_wpda_table( $this->table_name ) ) { $wp_nonce_action = "wpda-query-{$this->table_name}{$wp_nonce_keys}"; $wp_nonce = wp_create_nonce( $wp_nonce_action ); // Build the row action. Use jQuery to add form to container. // All items escaped in create_post_form(). $edit_form = $this->create_post_form( $item, "edit_form{$form_id}", 'edit', $url, $wp_nonce, $row_security_nonce_field, $this->schema_name, $this->table_name ); ?> <script type='text/javascript'> jQuery("#wpda_invisible_container").append("<?php echo $edit_form; // phpcs:ignore WordPress.Security.EscapeOutput ?>"); </script> <?php // Add link to submit form. $actions['edit'] = sprintf( '<a href="javascript:void(0)" class="edit wpda_tooltip" title="%s" onclick="jQuery(\'#%s\').submit()"> <span style="white-space: nowrap"> <i class="fas fa-pen wpda_icon_on_button"></i> %s </span> </a> ', __( 'Edit row data', 'wp-data-access' ), "edit_form{$form_id}", __( 'Edit', 'wp-data-access' ) ); } if ( 'on' === $this->allow_delete || WPDA::is_wpda_table( $this->table_name ) ) { $wp_nonce_action = "wpda-delete-{$this->table_name}{$wp_nonce_keys}"; $wp_nonce = wp_create_nonce( $wp_nonce_action ); // Build the row action. Use jQuery to add form to container. // All items escaped in create_post_form(). $delete_form = $this->create_post_form( $item, "delete_form{$form_id}", 'delete', $url, $wp_nonce, $row_security_nonce_field, $this->schema_name, $this->table_name ); ?> <script type='text/javascript'> jQuery("#wpda_invisible_container").append("<?php echo $delete_form; // phpcs:ignore WordPress.Security.EscapeOutput ?>"); </script> <?php // Add link to submit form. $warning = __( "You are about to permanently delete these items from your site.\\nThis action cannot be undone.\\n\\'Cancel\\' to stop, \\'OK\\' to delete.", 'wp-data-access' ); $actions['delete'] = sprintf( '<a href="javascript:void(0)" class="delete wpda_tooltip" title="%s" onclick="if (confirm(\'%s\')) jQuery(\'#%s\').submit()"> <span style="white-space: nowrap"> <i class="fas fa-trash wpda_icon_on_button"></i> %s </span> </a> ', __( 'Delete row data (this cannot be undone)', 'wp-data-access' ), $warning, "delete_form{$form_id}", __( 'Delete', 'wp-data-access' ) ); } // Developers can add actions by adding their own implementation of following method. $this->column_default_add_action( $item, $column_name, $actions ); if ( has_filter( 'wpda_column_default' ) ) { // Use filter. $filter = apply_filters( 'wpda_column_default', '', $item, $column_name, $this->table_name, $this->schema_name, $this->wpda_list_columns, self::$list_number, $this ); if ( null !== $filter ) { return sprintf( '%1$s %2$s', $filter, $this->row_actions( $actions ) ); } } return sprintf( '%1$s %2$s', $this->render_column_content( $item, $column_name ), $this->row_actions( $actions ) ); } } else { if ( substr( $column_name, 0, 15 ) === 'wpda_hyperlink_' ) { $hyperlink_no = substr( $column_name, 15 ); if ( isset( $this->wpda_table_settings->hyperlinks[$hyperlink_no] ) ) { $hyperlink = $this->wpda_table_settings->hyperlinks[$hyperlink_no]; $hyperlink_html = ( isset( $hyperlink->hyperlink_html ) ? $hyperlink->hyperlink_html : '' ); if ( '' !== $hyperlink_html ) { // Substitute values. foreach ( $item as $key => $value ) { $hyperlink_html = str_replace( "\$\${$key}\$\$", str_replace( ' ', '%20', (string) $value ), $hyperlink_html ); } } $macro = new WPDA_Macro($hyperlink_html); $hyperlink_html = $macro->exe_macro(); if ( '' !== $hyperlink_html ) { // Link is not empty AFTER substitution. if ( false !== strpos( ltrim( $hyperlink_html ), '<' ) ) { return html_entity_decode( $hyperlink_html ); } else { $hyperlink_label = ( isset( $hyperlink->hyperlink_label ) ? $hyperlink->hyperlink_label : '' ); $hyperlink_target = ( isset( $hyperlink->hyperlink_target ) ? $hyperlink->hyperlink_target : false ); $target = ( true === $hyperlink_target ? "target='_blank'" : '' ); return "<a href='" . str_replace( ' ', '+', trim( $hyperlink_html ) ) . "' {$target}>{$hyperlink_label}</a>"; } } else { return ''; } } return 'ERROR'; } else { // Check if column is of type media. $media_type = WPDA_Media_Model::get_column_media( $this->table_name, $column_name, $this->schema_name ); if ( 'Image' === $media_type ) { $image_ids = explode( ',', (string) $item[$column_name] ); //phpcs:ignore - 8.1 proof $image_src = ''; foreach ( $image_ids as $image_id ) { $url = wp_get_attachment_url( esc_attr( $image_id ) ); if ( false !== $url ) { $title = get_the_title( esc_attr( $image_id ) ); $image_src .= ( '' !== $image_src ? '<br/>' : '' ); $image_src .= sprintf( '<img src="%s" class="wpda_tooltip" title="%s" width="100%%">', $url, $title ); } } return $image_src; } elseif ( 'ImageURL' === $media_type ) { return sprintf( '<img src="%s" class="wpda_tooltip" width="100%%">', $item[$column_name] ); } elseif ( 'Attachment' === $media_type ) { $media_ids = explode( ',', (string) $item[$column_name] ); //phpcs:ignore - 8.1 proof $media_links = ''; foreach ( $media_ids as $media_id ) { $url = wp_get_attachment_url( esc_attr( $media_id ) ); if ( false !== $url ) { $mime_type = get_post_mime_type( $media_id ); if ( false !== $mime_type ) { $title = get_the_title( esc_attr( $media_id ) ); $media_links .= self::column_media_attachment( $url, $title, $mime_type ); } } } return $media_links; } elseif ( 'Hyperlink' === $media_type ) { if ( null === $item[$column_name] || '' === $item[$column_name] ) { return ''; } else { if ( !(isset( $this->wpda_table_settings->table_settings->hyperlink_definition ) && 'text' === $this->wpda_table_settings->table_settings->hyperlink_definition) ) { $hyperlink = json_decode( $item[$column_name], true ); if ( is_array( $hyperlink ) && isset( $hyperlink['label'] ) && isset( $hyperlink['url'] ) && isset( $hyperlink['target'] ) ) { if ( '' === $hyperlink['url'] ) { return ''; } else { return "<a href='{$hyperlink['url']}' target='{$hyperlink['target']}'>{$hyperlink['label']}</a>"; } } else { return ''; } } else { $hyperlink_label = $this->wpda_list_columns->get_column_label( $column_name ); return "<a href='{$item[$column_name]}' target='_blank'>{$hyperlink_label}</a>"; } } } elseif ( 'Audio' === $media_type ) { $audio_ids = explode( ',', (string) $item[$column_name] ); //phpcs:ignore - 8.1 proof $audio_src = ''; foreach ( $audio_ids as $audio_id ) { if ( 'audio' === substr( get_post_mime_type( $audio_id ), 0, 5 ) ) { $url = wp_get_attachment_url( esc_attr( $audio_id ) ); if ( false !== $url ) { $title = get_the_title( esc_attr( $audio_id ) ); if ( false !== $url ) { $audio_src .= '<div title="' . $title . '" class="wpda_tooltip">' . do_shortcode( '[audio src="' . $url . '"]' ) . '</div>'; } } } } return $audio_src; } elseif ( 'Video' === $media_type ) { $video_ids = explode( ',', (string) $item[$column_name] ); //phpcs:ignore - 8.1 proof $video_src = ''; foreach ( $video_ids as $video_id ) { if ( 'video' === substr( get_post_mime_type( $video_id ), 0, 5 ) ) { $url = wp_get_attachment_url( esc_attr( $video_id ) ); if ( false !== $url ) { if ( false !== $url ) { $video_src .= do_shortcode( '[video src="' . $url . '"]' ); } } } } return $video_src; } } if ( has_filter( 'wpda_column_default' ) ) { // Use filter. $filter = apply_filters( 'wpda_column_default', '', $item, $column_name, $this->table_name, $this->schema_name, $this->wpda_list_columns, self::$list_number, $this ); if ( null !== $filter ) { return $filter; } } if ( 'csv' !== WPDA::get_option( WPDA::OPTION_PLUGIN_SET_FORMAT ) && isset( $this->columns_indexed[$column_name]['data_type'] ) && 'set' === $this->columns_indexed[$column_name]['data_type'] ) { $list = '<' . WPDA::get_option( WPDA::OPTION_PLUGIN_SET_FORMAT ) . '>'; $listarray = explode( ',', (string) $item[$column_name] ); //phpcs:ignore - 8.1 proof foreach ( $listarray as $listitem ) { $list .= "<li>{$listitem}</li>"; } $list .= '</' . WPDA::get_option( WPDA::OPTION_PLUGIN_SET_FORMAT ) . '>'; return $list; } return $this->render_column_content( $item, $column_name ); } } /** * Create post form which is added to the invisible container * * @param array $item Column info. * @param string $form_id Form ID. * @param string $action Form action. * @param string $url URL admin page. * @param string $wp_nonce Nonce. * @param string $row_security_nonce_field Nonce for row level access security. * @param string $schema_name Database schema name. * @param string $table_name Database table name. * * @return array|string|string[] */ private function create_post_form( $item, $form_id, $action, $url, $wp_nonce, $row_security_nonce_field, $schema_name, $table_name ) { // Already escaped: $url, $row_security_nonce_field, get_key_input_fields(), add_parent_args_as_string(). $esc_attr = 'esc_attr'; $get_key_input_fields = $this->get_key_input_fields( $item ); $add_parent_args_as_string = $this->add_parent_args_as_string( $item ); $page_number_item = $this->page_number_item; $case_sensitive_search = ( isset( $_REQUEST['wpda_c'] ) && 'true' === $_REQUEST['wpda_c'] ? "<input type='hidden' name='wpda_c' value='true'>" : '' ); $add_schema_and_table_name = ( !is_admin() ? '' : "\n\t\t\t\t\t<input type='hidden' name='wpdaschema_name' value='{$esc_attr( $schema_name )}' />\n\t\t\t\t\t<input type='hidden' name='table_name' value='{$esc_attr( $table_name )}' />\n\t\t\t\t" ); // Hide schema and table name on front-end $form = <<<EOT \t\t\t\t<form id='{$esc_attr( $form_id )}' action='{$url}' method='post'> \t\t\t\t\t{$get_key_input_fields} \t\t\t\t\t{$add_parent_args_as_string} \t\t\t\t\t{$add_schema_and_table_name} \t\t\t\t\t<input type='hidden' name='action' value='{$esc_attr( $action )}' /> \t\t\t\t\t<input type='hidden' name='_wpnonce' value='{$esc_attr( $wp_nonce )}'> \t\t\t\t\t{$row_security_nonce_field} \t\t\t\t\t{$page_number_item} \t\t\t\t\t{$case_sensitive_search} \t\t\t\t</form> EOT; return str_replace( array("\n", "\r"), '', $form ); } /** * Create media link * * @param string $url Media URL. * @param string $title Media title. * @param string $mime_type Mime type. * * @return string */ public static function column_media_attachment( $url, $title, $mime_type ) { if ( 'image' === substr( $mime_type, 0, 5 ) ) { $class = 'dashicons-format-image'; } elseif ( 'audio' === substr( $mime_type, 0, 5 ) ) { $class = 'dashicons-playlist-audio'; } elseif ( 'video' === substr( $mime_type, 0, 5 ) ) { $class = 'dashicons-playlist-video'; } elseif ( 'application' === substr( $mime_type, 0, 11 ) ) { $class = 'dashicons-media-document'; } else { $class = 'dashicons-external'; } return sprintf( '<a href="%s" title="%s" target="_blank"><span class="dashicons %s wpda_attachment_icon"></span></a>', $url, $title, $class ); } /** * Overwrite method to prevent double row actions * * @param object $item Table column. * @param string $column_name Column name. * @param string $primary Primary key. * * @return string */ protected function handle_row_actions( $item, $column_name, $primary ) { return ''; } /** * Render column content * * Strip content if too long and replace & character. * * @param object $item Table column. * @param string $column_name Database column name. * * @return string Rendered column content */ protected function render_column_content( $item, $column_name, $substitute_newlines = true ) { $column_content = ( isset( $item["lookup_value_{$column_name}"] ) ? $item["lookup_value_{$column_name}"] : $item[$column_name] ); if ( 'off' === WPDA::get_option( WPDA::OPTION_BE_TEXT_WRAP_SWITCH ) && WPDA::get_option( WPDA::OPTION_BE_TEXT_WRAP ) < strlen( (string) $column_content ) ) { $title = sprintf( __( 'Output limited to %1$s characters', 'wp-data-access' ), WPDA::get_option( WPDA::OPTION_BE_TEXT_WRAP ) ); if ( $substitute_newlines ) { return str_replace( "\n", '<br/>', substr( esc_html( str_replace( '&', '&', (string) $column_content ) ), 0, WPDA::get_option( WPDA::OPTION_BE_TEXT_WRAP ) ) . ' <a href="javascript:void(0)" title="' . $title . '">•••</a>' ); } else { return substr( esc_html( str_replace( '&', '&', (string) $column_content ) ), 0, WPDA::get_option( WPDA::OPTION_BE_TEXT_WRAP ) ) . ' <a href="javascript:void(0)" title="' . $title . '">•••</a>'; } } else { $column_data_type = $this->wpda_list_columns->get_column_data_type( $column_name ); switch ( $column_data_type ) { case 'date': // date only. if ( '' === $column_content || null === $column_content ) { $column_content = ''; } else { $column_content = date_i18n( get_option( 'date_format' ), strtotime( $column_content ) ); } break; case 'time': // time only. if ( '' === $column_content || null === $column_content ) { $column_content = ''; } else { $column_content = date_i18n( get_option( 'time_format' ), strtotime( $column_content ) ); } break; case 'datetime': // date + time. case 'timestamp': if ( '' === $column_content || null === $column_content ) { $column_content = ''; } else { $column_content = date_i18n( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), strtotime( $column_content ) ); } } if ( $substitute_newlines ) { return str_replace( "\n", '<br/>', esc_html( str_replace( '&', '&', (string) $column_content ) ) ); } else { return esc_html( str_replace( '&', '&', (string) $column_content ) ); } } } /** * Generate hidden fields for keys * * @param array $item Column info. * * @return string String containing key items and values. */ protected function get_key_input_fields( $item ) { $key_input_fields = ''; foreach ( $this->wpda_list_columns->get_table_primary_key() as $key ) { $key_name = esc_attr( $key ); $key_value = esc_attr( $item[$key] ); $key_input_fields .= "<input type='hidden' name='{$key_name}' value='{$key_value}' />"; } return $key_input_fields; } /** * Override this method to add actions to first column of row * * @param array $item Item information. * @param string $column_name Column name. * @param array $actions Array of actions to be added to row. */ protected function column_default_add_action( $item, $column_name, &$actions ) { if ( has_filter( 'wpda_column_default_add_action' ) ) { $filter = apply_filters( 'wpda_column_default_add_action', '', $item, $column_name, $this->table_name, $this->schema_name, $this->wpda_list_columns, self::$list_number, $actions, $this ); if ( null !== $filter ) { $actions = $filter; } } } /** * Render bulk edit checkbox * * @param array $item Column list. * * @return string Content for checkbox column. * @since 1.0.0 */ public function column_cb( $item ) { if ( !$this->bulk_actions_enabled ) { // Bulk actions disabled. return ''; } if ( empty( $this->wpda_list_columns->get_table_primary_key() ) ) { // Table has no primary key: no bulk actions allowed! // Primary key is used to ensure uniqueness! return ''; } // Build CB value: Use json format for multi column primary keys. $cb_value = (object) null; foreach ( $this->wpda_list_columns->get_table_primary_key() as $primary_key_column ) { // JSON string key and values will be double quoted. Therefore a slash must be added to double quotes in values. $cb_value->{$primary_key_column} = esc_attr( str_replace( '"', '\\"', $item[$primary_key_column] ) ); } return "<input type='checkbox' name='bulk-selected[]' value='" . wp_json_encode( $cb_value ) . "' />"; } /** * Show page title * * @return void */ protected function show_title() { if ( self::LIST_BASE_TABLE !== $this->table_name && (substr( $this->page, 0, 13 ) === \WP_Data_Access_Admin::PAGE_EXPLORER || \WP_Data_Access_Admin::PAGE_MAIN === $this->page) ) { $this->subtitle = $this->title; $this->title = 'Data Explorer'; } echo esc_attr( $this->title ); } /** * Show page sub title * * @return void */ protected function show_sub_title() { ?> <div class="wpda_subtitle"><strong><?php echo wp_kses( $this->subtitle, array( 'span' => array( 'class' => array(), ), ) ); ?></strong></div> <?php } /** * Show list table page * * Inside the list table page, the list table is shown. The necessary functionality to show the list table * specifically is found in method {@see WPDA_List_Table::display()}. * * @since 1.0.0 * * @see WPDA_List_Table::display() */ public function show() { // Check for import requested. if ( null !== $this->wpda_import ) { $this->wpda_import->check_post(); } // Prepare list table items. $this->prepare_items(); // Show list table. ?> <div class="wrap<?php echo ( is_admin() ? '' : ' wp-core-ui' ); ?>"> <?php if ( $this->show_page_title ) { ?> <h1 class="wp-heading-inline"> <?php if ( self::LIST_BASE_TABLE !== $this->table_name && (current_user_can( 'manage_options' ) && \WP_Data_Access_Admin::PAGE_MAIN === $this->page) ) { ?> <a href="?page=<?php echo esc_attr( $this->page ); ?>" class="dashicons dashicons-arrow-left-alt2 wpda_tooltip" title="Data Explorer" ></a> <?php } $this->show_title(); ?> </h1> <?php } ?> <?php if ( !is_admin() || 'wpda_wpdp_' === substr( $this->page, 0, 10 ) || WPDP::PAGE_TEMPLATES === $this->page || substr( $this->page, 0, 13 ) === \WP_Data_Access_Admin::PAGE_EXPLORER ) { $this->add_header_button(); } $this->add_header_actions(); if ( 'diehard' === $this->page ) { $action_url = admin_url( 'admin-ajax.php' ); } else { $action_url = admin_url( 'admin.php' ); } ?> <div id="wpda_invisible_container" style="display:none;"> <form id="wpda_main_export_form" method="post" action="<?php echo esc_url( $action_url ); ?>?action=wpda_export" target="_blank"> <input type="text" name="type" id="wpda_main_export_form__type" value="table" /> <input type="text" name="wpdaschema_name" id="wpda_main_export_form__schema_name" /> <input type="text" name="_wpnonce" id="wpda_main_export_form__wpnonce" /> </form> <form id="wpda_table_export_form" method="post" action="<?php echo esc_url( $action_url ); ?>?action=wpda_export" target="_blank"> <input type="text" name="type" id="wpda_table_export_form__type" value="table" /> <input type="text" name="wpdaschema_name" id="wpda_table_export_form__schema_name" /> <input type="text" name="table_names" id="wpda_table_export_form__table_names" /> <input type="text" name="format_type" id="wpda_table_export_form__format_type" /> <input type="text" name="include_table_settings" id="wpda_table_export_form__include_table_settings" /> <input type="text" name="_wpnonce" id="wpda_table_export_form__wpnonce" /> </form> <form id="wpda_row_export_form" method="post" action="<?php echo esc_url( $action_url ); ?>?action=wpda_export" target="_blank"> <input type="text" name="type" id="wpda_row_export_form__type" value="row" /> <input type="text" name="pid" id="wpda_row_export_form__pid" /> <input type="text" name="wpdaschema_name" id="wpda_row_export_form__schema_name" /> <input type="text" name="table_names" id="wpda_row_export_form__table_names" /> <input type="text" name="mysql_set" id="wpda_row_export_form__mysql_set" /> <input type="text" name="show_create" id="wpda_row_export_form__show_create" /> <input type="text" name="show_comments" id="wpda_row_export_form__show_comments" /> <input type="text" name="format_type" id="wpda_row_export_form__format_type" /> <input type="text" name="_wpnonce" id="wpda_row_export_form__wpnonce" /> </form> </div> <?php // Add import container. if ( null !== $this->wpda_import ) { $this->wpda_import->add_container(); } // Add custom code before the list table. do_action_ref_array( 'wpda_before_list_table', array($this) ); // Prepare url. if ( is_admin() ) { $url = "?page={$this->page}"; } else { $url = ''; } ?> <form id="wpda_main_form" method="post" action="<?php echo esc_attr( $url ); ?>" style=" <?php if ( '' !== $this->subtitle ) { echo 'margin-top: 10px'; } ?> " > <?php $this->show_sub_title(); if ( $this->search_box_enabled ) { $this->search_box( __( 'search', 'wp-data-access' ), 'search_id' ); } $this->display(); if ( '' === $this->get_bulk_actions() ) { // Add action item containing value -1. This will allow sorting if no bulk action listbox is displayed. ?> <input type="hidden" name="action" value="-1"/> <?php } if ( self::LIST_BASE_TABLE !== $this->table_name ) { ?> <input type="hidden" name="wpdaschema_name" value="<?php echo esc_attr( $this->schema_name ); // input var okay. ?>"/> <input type="hidden" name="table_name" value="<?php echo esc_attr( $this->table_name ); // input var okay. ?>"/> <?php } ?> <input id="wpda_main_form_orderby" type="hidden" name="orderby" value="<?php echo ( isset( $_REQUEST['orderby'] ) ? esc_attr( sanitize_text_field( wp_unslash( $_REQUEST['orderby'] ) ) ) : '' ); // input var okay. ?>"/> <input id="wpda_main_form_order" type="hidden" name="order" value="<?php echo ( isset( $_REQUEST['order'] ) ? esc_attr( sanitize_text_field( wp_unslash( $_REQUEST['order'] ) ) ) : '' ); // input var okay. ?>"/> <input id="wpda_main_form_post_mime_type" type="hidden" name="post_mime_type" value="<?php echo ( isset( $_REQUEST['post_mime_type'] ) ? esc_attr( sanitize_text_field( wp_unslash( $_REQUEST['post_mime_type'] ) ) ) : '' ); // input var okay. ?>"/> <input id="wpda_main_form_detached" type="hidden" name="detached" value="<?php echo ( isset( $_REQUEST['detached'] ) ? esc_attr( sanitize_text_field( wp_unslash( $_REQUEST['detached'] ) ) ) : '' ); // input var okay. ?>"/> <input id="wpda_main_db_schema" type="hidden" name="wpda_main_db_schema" value="<?php echo ( isset( $_REQUEST['wpda_main_db_schema'] ) ? esc_attr( sanitize_text_field( wp_unslash( $_REQUEST['wpda_main_db_schema'] ) ) ) : '' ); // input var okay. ?>"/> <input id="wpda_main_favourites" type="hidden" name="wpda_main_favourites" value="<?php echo ( isset( $_REQUEST['wpda_main_favourites'] ) ? esc_attr( sanitize_text_field( wp_unslash( $_REQUEST['wpda_main_favourites'] ) ) ) : '' ); // input var okay. ?>"/> <?php wp_nonce_field( 'wpda-export-' . wp_json_encode( $this->table_name ), '_wpnonce', false ); ?> <?php wp_nonce_field( "wpda-delete-{$this->table_name}", '_wpnonce2', false ); ?> <?php if ( self::LIST_BASE_TABLE === $this->table_name ) { wp_nonce_field( 'wpda-drop-' . WPDA::get_current_user_login(), '_wpnonce3', false ); wp_nonce_field( 'wpda-truncate-' . WPDA::get_current_user_login(), '_wpnonce4', false ); } ?> <?php foreach ( $_REQUEST as $key => $value ) { if ( substr( $key, 0, 19 ) === 'wpda_search_column_' ) { if ( is_array( $value ) ) { foreach ( $value as $elem_key => $elem_value ) { echo '<input type="hidden" name="' . esc_attr( $key ) . '[]" value="' . esc_attr( $elem_value ) . '"/>'; } } else { echo '<input type="hidden" name="' . esc_attr( $key ) . '" value="' . esc_attr( $value ) . '"/>'; } } } ?> </form> </div> <script type='text/javascript'> jQuery(function() { jQuery( '.wpda_tooltip' ).tooltip(); // Add toolbar events jQuery("#wpda_toolbar_icon_go_backup").on("click", function() { jQuery("#wpda_goto_backup").submit(); }); jQuery("#wpda_toolbar_icon_add_row").on("click", function() { jQuery("#wpda_new_row_table_name").val("<?php echo esc_attr( $this->table_name ); ?>"); jQuery("#wpda_new_row").submit(); }); jQuery("#wpda_toolbar_icon_add_publication").on("click", function() { jQuery("#wpda_new_publication_table_name").val("<?php echo esc_attr( $this->table_name ); ?>"); jQuery("#wpda_new_publication").submit(); }); }); </script> <?php $this->bind_action_buttons(); ?> <?php // Add custom code after the list table. do_action_ref_array( 'wpda_after_list_table', array($this) ); } /** * Placeholder for subclasses to add additional actions * * @return void */ protected function add_header_actions() { } /** * Bind javascript code to action buttons * * @since 1.6.2 */ protected function bind_action_buttons() { ?> <script type='text/javascript'> jQuery(function () { jQuery("#doaction").click(function () { return wpda_action_button(); }); jQuery("#doaction2").click(function () { return wpda_action_button(); }); }); </script> <?php } /** * Add button to page header * * By default "add new" and "import" buttons are added (depending on the settings). Overwrite this method to * add your own buttons. * * @since 1.0.1 */ protected function add_header_button() { if ( 'off' === $this->allow_insert ) { if ( null !== $this->wpda_import ) { $this->wpda_import->add_button(); } } else { //phpcs:ignore - 8.1 proof if ( WPDA::is_wpda_table( $this->table_name ) || ('on' === WPDA::get_option( WPDA::OPTION_BE_ALLOW_INSERT ) && count( $this->wpda_list_columns->get_table_primary_key() )) > 0 ) { $storage_type = ( WPDA::is_wpda_table( $this->table_name ) ? __( 'respository', 'wp-data-access' ) : __( 'table', 'wp-data-access' ) ); // Prepare url. if ( is_admin() ) { $url = "?page={$this->page}"; } else { $url = ''; } ?> <form method="post" action="<?php echo esc_attr( $url ); ?>" style="display: inline-block; vertical-align: baseline;" > <div> <input type="hidden" name="wpdaschema_name" value="<?php echo esc_attr( $this->schema_name ); // input var okay. ?>"/> <input type="hidden" name="table_name" value="<?php echo esc_attr( $this->table_name ); // input var okay. ?>"/> <input type="hidden" name="action" value="new"> <button type="submit" class="page-title-action wpda_tooltip" title="<?php echo sprintf( __( 'Add new %1$s to %2$s', 'wp-data-access' ), esc_attr( $this->_args['singular'] ), esc_attr( $storage_type ) ); ?>" > <i class="fas fa-plus-circle wpda_icon_on_button"></i> <?php echo __( 'Add New', 'wp-data-access' ); ?> </button> <?php // Add import button to title. if ( null !== $this->wpda_import ) { $this->wpda_import->add_button(); } ?> </div> </form> <?php } else { // Add import button to title. if ( null !== $this->wpda_import ) { $this->wpda_import->add_button(); } } } } /** * Prepares the list of items for displaying * * Overwrites WP_List_Table::prepare_items() * * @since 1.0.0 */ public function prepare_items() { // Construct where clause with search values provided in the search box. // Result (where clause) is written to $this->where. $this->construct_where_clause(); $this->process_bulk_action(); if ( is_admin() ) { if ( $this->table_name === self::LIST_BASE_TABLE ) { $option = 'wpda_rows_per_page_' . str_replace( '.', '_', self::LIST_BASE_TABLE ); } else { $option = 'wpda_rows_per_page_' . str_replace( '.', '_', $this->schema_name . $this->table_name ); } $this->items_per_page = $this->get_items_per_page( $option, WPDA::get_option( WPDA::OPTION_BE_PAGINATION ) ); } else { $this->items_per_page = WPDA::get_option( WPDA::OPTION_FE_PAGINATION ); } $this->current_page = $this->get_pagenum(); $total_items = $this->record_count(); $total_pages = ceil( $total_items / $this->items_per_page ); if ( $this->search_value !== $this->search_value_old ) { $this->current_page = 1; } if ( $this->current_page > $total_pages ) { $this->current_page = $total_pages; } $this->set_pagination_args( array( 'total_items' => $total_items, 'total_pages' => $total_pages, 'per_page' => $this->items_per_page, ) ); $this->get_rows(); // Written to $this->items in base class (WP_List_Table). $columns = $this->get_columns(); $hidden = $this->get_hidden_columns(); if ( false === $hidden ) { if ( is_admin() ) { // List table in backend. if ( self::LIST_BASE_TABLE === $this->table_name ) { $table_name = str_replace( '.', '_', self::LIST_BASE_TABLE ); } else { global $wpdb; if ( $this->schema_name === $wpdb->dbname && WPDA_CSV_Uploads_Model::get_base_table_name() === $this->table_name ) { $table_name = $this->table_name; // csv upload = exception. } else { $table_name = str_replace( '.', '_', $this->schema_name . $this->table_name ); } } $hidden = get_user_option( WPDA_List_View::HIDDENCOLUMNS_PREFIX . get_current_screen()->id . $table_name ); if ( false === $hidden ) { $hidden = array(); } } } if ( !is_array( $hidden ) ) { $hidden = array(); } $sortable = $this->get_sortable_columns(); $primary = $this->get_primary_column(); $this->_column_headers = array( $columns, $hidden, $sortable, $primary ); } /** * Build where clause * * Arguments might come from the URL so we need to check for SQL injection and use prepare. The resulting * where clause is written directly to member $this->where. * * @since 1.0.0 */ protected function construct_where_clause() { $whereclause = $this->get_where_clause(); if ( '' !== $whereclause ) { $this->where = ( '' === $this->where ? " where ({$whereclause}) " : " {$this->where} and ({$whereclause}) " ); } } /** * Returns the where clause (uses filters if available) * * @return string */ public function get_where_clause() { return WPDA::construct_where_clause( $this->schema_name, $this->table_name, $this->wpda_list_columns->get_searchable_table_columns(), $this->search_value, isset( $_REQUEST['wpda_c'] ) && 'true' === $_REQUEST['wpda_c'] ); } /** * Process bulk actions * * Delete and bulk-delete actions use the primary key list as a reference. Before performing the actual delete * statement(s) we'll check the provided column names against the data dictionary. This will protect us against * SQL injection attacks. * * For export actions table names will not be checked here. They are handed over WPDA_Export where the validity * and access rights are checked anyway (to be safe). * * All actions can be processed only with a valid wpnonce value. * * To perform actions the user must have the appropriate rights. * * @since 1.0.0 */ public function process_bulk_action() { switch ( $this->current_action() ) { case 'delete': // Check access rights. if ( 'on' !== $this->allow_delete ) { // Deleting records from list table is not allowed. wp_die( __( 'ERROR: Not authorized [delete not allowed]', 'wp-data-access' ) ); } // Prepare wp_nonce action security check. $wp_nonce_action = "wpda-delete-{$this->table_name}"; $row_to_be_deleted = array(); // Gonna hold the row to be deleted. $i = 0; // Index, necessary for multi column keys. // Check all key columns. foreach ( $this->wpda_list_columns->get_table_primary_key() as $key ) { // Check if key is available. if ( !isset( $_REQUEST[$key] ) ) { // input var okay. wp_die( __( 'ERROR: Invalid URL [missing primary key values]', 'wp-data-access' ) ); } // Write key value pair to array. $row_to_be_deleted[$i]['key'] = $key; $row_to_be_deleted[$i]['value'] = sanitize_text_field( wp_unslash( $_REQUEST[$key] ) ); // input var okay. $i++; // Add key values to wp_nonce action. $wp_nonce_action .= '-' . sanitize_text_field( wp_unslash( $_REQUEST[$key] ) ); // input var okay. } // Check if delete is allowed. $wp_nonce = ( isset( $_REQUEST['_wpnonce'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['_wpnonce'] ) ) : '' ); // input var okay. if ( !wp_verify_nonce( $wp_nonce, $wp_nonce_action ) ) { wp_die( __( 'ERROR: Not authorized', 'wp-data-access' ) ); } // All key column values available: delete record. // Prepare named array for delete operation. $next_row_to_be_deleted = array(); $count_rows = count( $row_to_be_deleted ); //phpcs:ignore - 8.1 proof for ($i = 0; $i < $count_rows; $i++) { $next_row_to_be_deleted[$row_to_be_deleted[$i]['key']] = $row_to_be_deleted[$i]['value']; } $engine = WPDA::get_table_engine( $this->schema_name, $this->table_name ); if ( $this->delete_row( $next_row_to_be_deleted, $engine ) ) { $msg = new WPDA_Message_Box(array( 'message_text' => __( 'Row deleted', 'wp-data-access' ), )); $msg->box(); } else { $msg = new WPDA_Message_Box(array( 'message_text' => __( 'Could not delete row', 'wp-data-access' ), 'message_type' => 'error', 'message_is_dismissible' => false, )); $msg->box(); } break; case 'bulk-delete': // Check access rights. if ( 'on' !== $this->allow_delete ) { // Deleting records from list table is not allowed. die( __( 'ERROR: Not authorized [delete not allowed]', 'wp-data-access' ) ); } // We first need to check if all the necessary information is available. if ( !isset( $_REQUEST['bulk-selected'] ) ) { // input var okay. // Nothing to delete. $msg = new WPDA_Message_Box(array( 'message_text' => __( 'Nothing to delete', 'wp-data-access' ), )); $msg->box(); return; } // Check if delete is allowed. $wp_nonce_action = "wpda-delete-{$this->table_name}"; $wp_nonce = ( isset( $_REQUEST['_wpnonce2'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['_wpnonce2'] ) ) : '' ); // input var okay. if ( !wp_verify_nonce( $wp_nonce, $wp_nonce_action ) ) { die( __( 'ERROR: Not authorized', 'wp-data-access' ) ); } $bulk_rows = (array) $_REQUEST['bulk-selected']; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput $no_rows = count( $bulk_rows ); // # rows to be deleted. //phpcs:ignore - 8.1 proof being safe $rows_to_be_deleted = array(); // Gonna hold rows to be deleted. for ($i = 0; $i < $no_rows; $i++) { // Write "json" to named array. Need to strip slashes twice. Once for the normal conversion // and once extra for the pre-conversion of double quotes in method column_cb(). $row_object = json_decode( stripslashes( stripslashes( $bulk_rows[$i] ) ), true ); if ( $row_object ) { $j = 0; // Index used to build array. // Check all key columns. foreach ( $this->wpda_list_columns->get_table_primary_key() as $key ) { // Check if key is available. if ( !isset( $row_object[$key] ) ) { wp_die( __( 'ERROR: Invalid URL [missing primary key values]', 'wp-data-access' ) ); } // Write key value pair to array. $rows_to_be_deleted[$i][$j]['key'] = $key; $rows_to_be_deleted[$i][$j]['value'] = $row_object[$key]; $j++; } } } // Looks like everything is there. Delete records from table... $no_key_cols = count( $this->wpda_list_columns->get_table_primary_key() ); //phpcs:ignore - 8.1 proof $rows_successfully_deleted = 0; // Number of rows successfully deleted. $rows_with_errors = 0; // Number of rows that could not be deleted. $engine = WPDA::get_table_engine( $this->schema_name, $this->table_name ); for ($i = 0; $i < $no_rows; $i++) { // Prepare named array for delete operation. $next_row_to_be_deleted = array(); $row_found = true; for ($j = 0; $j < $no_key_cols; $j++) { if ( isset( $rows_to_be_deleted[$i][$j]['key'] ) ) { $next_row_to_be_deleted[$rows_to_be_deleted[$i][$j]['key']] = $rows_to_be_deleted[$i][$j]['value']; } else { $row_found = false; } } if ( $row_found ) { if ( $this->delete_row( $next_row_to_be_deleted, $engine ) ) { // Row(s) successfully deleted. $rows_successfully_deleted++; } else { // An error occurred during the delete operation: increase error count. $rows_with_errors++; } } else { // An error occurred during the delete operation: increase error count. $rows_with_errors++; } } // Inform user about the results of the operation. $message = ''; if ( 1 === $rows_successfully_deleted ) { $message = __( 'Row deleted', 'wp-data-access' ); } elseif ( $rows_successfully_deleted > 1 ) { $message = "{$rows_successfully_deleted} " . __( 'rows deleted', 'wp-data-access' ); } if ( '' !== $message ) { $msg = new WPDA_Message_Box(array( 'message_text' => $message, )); $msg->box(); } $message = ''; if ( $rows_with_errors > 0 ) { $message = __( 'Not all rows have been deleted', 'wp-data-access' ); } if ( '' !== $message ) { $msg = new WPDA_Message_Box(array( 'message_text' => $message, 'message_type' => 'error', 'message_is_dismissible' => false, )); $msg->box(); } break; case 'bulk-export': case 'bulk-export-xml': case 'bulk-export-json': case 'bulk-export-excel': case 'bulk-export-csv': // Check access rights. if ( !WPDA::is_wpda_table( $this->table_name ) ) { if ( 'on' !== WPDA::get_option( WPDA::OPTION_BE_EXPORT_ROWS ) ) { // Exporting rows from list table is not allowed. die( __( 'ERROR: Not authorized [export not allowed]', 'wp-data-access' ) ); } } // We first need to check if all the necessary information is available. if ( !isset( $_REQUEST['bulk-selected'] ) ) { // input var okay. // Nothing to export. $msg = new WPDA_Message_Box(array( 'message_text' => __( 'Nothing to export', 'wp-data-access' ), )); $msg->box(); return; } // Check if export is allowed. $wp_nonce = ( isset( $_REQUEST['_wpnonce'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['_wpnonce'] ) ) : '' ); // input var okay. if ( !wp_verify_nonce( $wp_nonce, 'wpda-export-' . wp_json_encode( $this->table_name ) ) ) { die( __( 'ERROR: Not authorized', 'wp-data-access' ) ); } $bulk_rows = (array) $_REQUEST['bulk-selected']; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput $no_rows = count( $bulk_rows ); // # rows to be exported. //phpcs:ignore - 8.1 proof being safe $format_type = ''; switch ( $this->current_action() ) { case 'bulk-export-xml': $format_type = 'xml'; break; case 'bulk-export-json': $format_type = 'json'; break; case 'bulk-export-excel': $format_type = 'excel'; break; case 'bulk-export-csv': $format_type = 'csv'; } if ( 'diehard' === $this->page && null !== $this->pid ) { $wp_nonce = wp_create_nonce( "wpda-export-{$this->pid}" ); $submit_schema_name = ''; $submit_table_name = ''; } else { $submit_schema_name = $this->schema_name; $submit_table_name = $this->table_name; } $j = 0; $columns = array(); for ($i = 0; $i < $no_rows; $i++) { // Write "json" to named array. Need to strip slashes twice. Once for the normal conversion // and once extra for the pre-conversion of double quotes in method column_cb(). $row_object = json_decode( stripslashes( stripslashes( $bulk_rows[$i] ) ), true ); if ( $row_object ) { // Check all key columns. foreach ( $this->wpda_list_columns->get_table_primary_key() as $key ) { // Check if key is available. if ( !isset( $row_object[$key] ) ) { wp_die( __( 'ERROR: Invalid URL', 'wp-data-access' ) ); } if ( !isset( $columns[$key] ) ) { $columns[$key] = array(); } $columns[$key][] = $row_object[$key]; } $j++; } } ?> <script type="application/javascript"> jQuery(function() { wpda_row_export( '<?php echo esc_attr( $this->pid ); ?>', '<?php echo esc_attr( $submit_schema_name ); ?>', '<?php echo esc_attr( $submit_table_name ); ?>', '<?php echo esc_attr( $wp_nonce ); ?>', '<?php echo esc_attr( $format_type ); ?>', 'off', 'off', 'off', '<?php echo wp_json_encode( $columns ); ?>' ); }); </script> <?php } } /** * Delete record from database table * * The table must have a primary key and the values for all primary key columns must be provided in the * request. The where clause must be a named array in format: ['column_name'] = 'value' * * @param string $where where clause. * @param string $engine connect or empty. * * @return mixed * @since 1.0.0 */ public function delete_row( $where, $engine = '' ) { $wpdadb = WPDADB::get_db_connection( $this->schema_name ); if ( null === $wpdadb ) { if ( is_admin() ) { wp_die( sprintf( __( 'ERROR - Remote database %s not available', 'wp-data-access' ), esc_attr( $this->schema_name ) ) ); } else { die( sprintf( __( 'ERROR - Remote database %s not available', 'wp-data-access' ), esc_attr( $this->schema_name ) ) ); } } $row_deleted = $wpdadb->delete( $this->table_name, $where ); if ( 'connect' === strtolower( $engine ) ) { // Connect engine does not return number of rows deleted. // Presuming delete was successful when no error message was returned. return '' === $wpdadb->last_error; } return $row_deleted; } /** * Number of records in database table * * Returns the number of records in the database table. Where clause must be prepared in advance and written * to $this->where ({@see WPDA_List_Table::construct_where_clause()}) * * @return null|string Number of rows in the current table * @since 1.0.0 */ public function record_count() { if ( !$this->row_count_estimate['do_real_count'] && '' === $this->where ) { // Use estimate row count. return $this->table_rows; } // Get real row count. $wpdadb = WPDADB::get_db_connection( $this->schema_name ); if ( null === $wpdadb ) { if ( is_admin() ) { wp_die( sprintf( __( 'ERROR - Remote database %s not available', 'wp-data-access' ), esc_attr( $this->schema_name ) ) ); } else { die( sprintf( __( 'ERROR - Remote database %s not available', 'wp-data-access' ), esc_attr( $this->schema_name ) ) ); } } if ( '' === $this->schema_name ) { $query = "\n\t\t\t\t\tselect count(*)\n\t\t\t\t\tfrom `{$this->table_name}`\n\t\t\t\t\t{$this->where}\n\t\t\t\t"; } else { if ( self::LIST_BASE_TABLE === $this->table_name ) { $query = "\n\t\t\t\t\t\tselect count(*)\n\t\t\t\t\t\tfrom {$this->table_name}\n\t\t\t\t\t\t{$this->where}\n\t\t\t\t\t"; } else { $query = "\n\t\t\t\t\t\tselect count(*)\n\t\t\t\t\t\tfrom `{$wpdadb->dbname}`.`{$this->table_name}`\n\t\t\t\t\t\t{$this->where}\n\t\t\t\t\t"; } } return $wpdadb->get_var( $query ); // phpcs:ignore Standard.Category.SniffName.ErrorCode } /** * Perform query to retrieve rows from database * * Where clause must be prepared in advance and written to $this->where * ({@see WPDA_List_Table::construct_where_clause()}). * * No return value. Result is directly written to $this->items (base class member). * * @since 1.0.0 */ public function get_rows() { $wpdadb = WPDADB::get_db_connection( $this->schema_name ); if ( null === $wpdadb ) { if ( is_admin() ) { wp_die( sprintf( __( 'ERROR - Remote database %s not available', 'wp-data-access' ), esc_attr( $this->schema_name ) ) ); } else { die( sprintf( __( 'ERROR - Remote database %s not available', 'wp-data-access' ), esc_attr( $this->schema_name ) ) ); } } // Selected columns cannot be changed by the user at this time. No check for SQL injection needed now. // This might change in the future when users are allowed to change or set this value. A method named // column_exists() in WPDA_Dictionary_Checks is already available for this purpose. $selected_columns = '*'; if ( isset( $this->columns_queried ) ) { $selected_columns = implode( ',', $this->columns_queried ); } if ( isset( $this->wpda_table_settings->hyperlinks ) ) { $i = 0; foreach ( $this->wpda_table_settings->hyperlinks as $hyperlink ) { $skip_column = false; if ( null !== $this->wpda_project_table_settings ) { $hyperlink_label = $hyperlink->hyperlink_label; if ( !property_exists( $this, 'is_child' ) ) { if ( isset( $this->wpda_project_table_settings->hyperlinks_parent->{$hyperlink_label} ) && !$this->wpda_project_table_settings->hyperlinks_parent->{$hyperlink_label} ) { $skip_column = true; } } else { if ( isset( $this->wpda_project_table_settings->hyperlinks_child->{$hyperlink_label} ) && !$this->wpda_project_table_settings->hyperlinks_child->{$hyperlink_label} ) { $skip_column = true; } } } if ( !$skip_column && isset( $hyperlink->hyperlink_list ) && true === $hyperlink->hyperlink_list ) { $hyperlink_label = ( isset( $hyperlink->hyperlink_label ) ? $hyperlink->hyperlink_label : '' ); $hyperlink_html = ( isset( $hyperlink->hyperlink_html ) ? $hyperlink->hyperlink_html : '' ); if ( '' !== $hyperlink_label && '' !== $hyperlink_html ) { // Add hyperlink columns (details are added in method column_default). $selected_columns .= ", '' as wpda_hyperlink_{$i}"; } } $i++; } } if ( '' === $this->schema_name ) { $query = "\n\t\t\t\t\tselect {$selected_columns}\n\t\t\t\t\tfrom `{$this->table_name}`\n\t\t\t\t\t{$this->where}\n\t\t\t\t"; } else { if ( self::LIST_BASE_TABLE === $this->table_name ) { $query = "\n\t\t\t\t\t\tselect {$selected_columns}\n\t\t\t\t\t\tfrom {$this->table_name}\n\t\t\t\t\t\t{$this->where}\n\t\t\t\t\t"; } else { $query = "\n\t\t\t\t\t\tselect {$selected_columns}\n\t\t\t\t\t\tfrom `{$wpdadb->dbname}`.`{$this->table_name}`\n\t\t\t\t\t\t{$this->where}\n\t\t\t\t\t"; } } $query .= $this->get_order_by(); // Add order by. $query .= " limit {$this->items_per_page}"; $offset = ($this->current_page - 1) * $this->items_per_page; if ( $offset > 0 ) { $query .= " offset {$offset}"; } // Debug query. // var_dump( $query ); $this->items = $wpdadb->get_results( $query, 'ARRAY_A' ); // phpcs:ignore Standard.Category.SniffName.ErrorCode } /** * Get order by * * @return string|void */ protected function get_order_by() { if ( !empty( $_REQUEST['orderby'] ) ) { $orderby_arg = sanitize_sql_orderby( wp_unslash( $_REQUEST['orderby'] ) ); // input var okay. if ( !empty( $_REQUEST['order'] ) ) { $order_arg = sanitize_text_field( wp_unslash( $_REQUEST['order'] ) ); // input var okay. } else { $order_arg = ''; } $columns = $this->get_sortable_columns(); // Check column name for SQL injection. if ( isset( $columns[$orderby_arg] ) || $this->wpda_data_dictionary->column_exists( $orderby_arg ) ) { // Column name exists in current table, safely continue... $orderby = " order by {$orderby_arg}"; // Prevent SQL injection for order. If 'desc' is found result will be ordered desc. In all other // cases we'll order asc. $orderby .= ( strtolower( trim( $order_arg ) ) === 'desc' ? ' desc' : ' asc' ); return $orderby; } else { // The user provided a column name which is not in the table. Most probably the result of a // SQL injection attack, so let's terminate. wp_die( __( 'ERROR: Invalid URL [invalid column name]', 'wp-data-access' ) ); } } else { return ''; } } /** * Return an associative array of columns * * @return array * @since 1.0.0 */ public function get_columns() { if ( null !== $this->wpda_cached_columns && is_array( $this->wpda_cached_columns ) ) { return $this->wpda_cached_columns; } $columns = array(); if ( $this->bulk_actions_enabled ) { if ( !empty( $this->wpda_list_columns->get_table_primary_key() ) ) { // Tables has primary key: bulk actions allowed! // Primary key is used to ensure uniqueness. $actions = $this->get_bulk_actions(); if ( is_array( $actions ) && 0 < count( $actions ) ) { //phpcs:ignore - 8.1 proof $columns = array( 'cb' => '<input type="checkbox" />', ); } } } $columnlist = $this->wpda_list_columns->get_table_column_headers(); foreach ( $columnlist as $key => $value ) { $columns[$key] = $value; // Check for alternative column header. if ( isset( $this->column_headers[$key] ) ) { // Alternative header found: use it. $columns[$key] = $this->column_headers[$key]; } else { // Default behaviour: get column header from generated label. $columns[$key] = $this->wpda_list_columns->get_column_label( $key ); } } if ( isset( $this->wpda_table_settings->hyperlinks ) ) { $i = 0; foreach ( $this->wpda_table_settings->hyperlinks as $hyperlink ) { if ( isset( $hyperlink->hyperlink_list ) && true === $hyperlink->hyperlink_list ) { $skip_column = false; if ( null !== $this->wpda_project_table_settings ) { $hyperlink_label = $hyperlink->hyperlink_label; if ( !property_exists( $this, 'is_child' ) ) { if ( isset( $this->wpda_project_table_settings->hyperlinks_parent->{$hyperlink_label} ) && !$this->wpda_project_table_settings->hyperlinks_parent->{$hyperlink_label} ) { $skip_column = true; } } else { if ( isset( $this->wpda_project_table_settings->hyperlinks_child->{$hyperlink_label} ) && !$this->wpda_project_table_settings->hyperlinks_child->{$hyperlink_label} ) { $skip_column = true; } } } if ( !$skip_column ) { $hyperlink_label = ( isset( $hyperlink->hyperlink_label ) ? $hyperlink->hyperlink_label : '' ); $columns["wpda_hyperlink_{$i}"] = $hyperlink_label; // Add hyperlink label. } } $i++; } } // Cache columns. $this->wpda_cached_columns = $columns; return $columns; } /** * List of columns to make sortable * * @return array * @since 1.0.0 */ public function get_sortable_columns() { $columns = array(); // Get column names from result set. if ( $this->items ) { foreach ( $this->items[0] as $key => $value ) { if ( 'wpda_hyperlink_' !== substr( $key, 0, 15 ) ) { $columns[$key] = array($key, false); } } } return $columns; } /** * Display the search box * * @param string $text The 'submit' button label. * @param string $input_id ID attribute value for the search input field. * * @since 1.0.0 */ public function search_box( $text, $input_id ) { $input_id = $input_id . '-search-input'; // Allow external user to add search actions (like icons) to the search box. do_action( 'wpda_add_search_actions', $this->schema_name, $this->table_name, $this->wpda_table_settings, $this->wpda_list_columns ); ?> <p class="search-box" <?php echo ( !$this->has_items() ? 'style="padding-bottom:10px;"' : '' ); ?>> <input type="search" id="<?php echo esc_attr( $input_id ); ?>" name="<?php echo esc_attr( $this->search_item_name ); ?>" value="<?php echo esc_attr( $this->search_value ); ?>"/> <?php if ( is_admin() ) { submit_button( $text, '', '', false, array( 'id' => 'search-submit', ) ); } else { wpdadiehard_submit_button( $text, '', '', false, array( 'id' => 'search-submit', ) ); } ?> <input type="hidden" name="<?php echo esc_attr( $this->search_item_name ); ?>_old_value" value="<?php echo esc_attr( $this->search_value ); ?>"/> </p> <?php // Allow external user to add search html (like extra items) below the search box. do_action( 'wpda_add_search_filter', $this->schema_name, $this->table_name, $this->wpda_table_settings, $this->wpda_list_columns ); } /** * Get search value (entered by the user or taken from cookie). * * @return string * @since 1.5.0 */ protected function get_search_value() { if ( 'off' === WPDA::get_option( WPDA::OPTION_BE_REMEMBER_SEARCH ) ) { if ( isset( $_REQUEST[$this->search_item_name] ) ) { return wp_filter_nohtml_kses( wp_unslash( $_REQUEST[$this->search_item_name] ) ); // input var okay. } } if ( 'wpda_wpdp_' === substr( $this->page, 0, 10 ) ) { $cookie_name = $this->page; if ( isset( $_REQUEST['child_request'] ) ) { $cookie_name = 'NOCOOKIESFORCHILDREQUESTS'; // No search values stored for child tables. } foreach ( $_REQUEST as $key => $val ) { if ( strpos( $key, 'WPDA_PARENT_KEY*' ) === 0 ) { $cookie_name = 'NOCOOKIESFORCHILDREQUESTS'; // No search values stored for child tables. } } } else { $cookie_name = $this->page . '_search_' . str_replace( '.', '_', $this->table_name ); } if ( isset( $_REQUEST[$this->search_item_name] ) && '' !== $_REQUEST[$this->search_item_name] ) { // input var okay. return wp_filter_nohtml_kses( wp_unslash( $_REQUEST[$this->search_item_name] ) ); // input var okay. } elseif ( isset( $_COOKIE[$cookie_name] ) ) { return wp_filter_nohtml_kses( wp_unslash( $_COOKIE[$cookie_name] ) ); // input var okay. } else { return null; } } /** * Print column headers * * Overriding original method print_column_headers to support post instead of get. * Changes are marked! * * @param boolean $with_id Whether to set the id attribute or not. * * @since 1.0.0 * * @staticvar $cb_counter int */ public function print_column_headers( $with_id = true ) { list( $columns, $hidden, $sortable, $primary ) = $this->get_column_info(); //phpcs:ignore - 8.1 proof // ********************* // *** BEGIN CHANGES *** // ********************* // Code removed. // ******************* // *** END CHANGES *** // ******************* if ( isset( $_REQUEST['orderby'] ) ) { $current_orderby = sanitize_text_field( wp_unslash( $_REQUEST['orderby'] ) ); // input var okay. } else { $current_orderby = ''; } if ( isset( $_REQUEST['order'] ) && 'desc' === $_REQUEST['order'] ) { // input var okay. $current_order = 'desc'; } else { $current_order = 'asc'; } if ( !empty( $columns['cb'] ) ) { static $cb_counter = 1; $columns['cb'] = '<label class="screen-reader-text" for="cb-select-all-' . $cb_counter . '">' . __( 'Select All' ) . '</label>' . '<input id="cb-select-all-' . $cb_counter . '" type="checkbox" />'; $cb_counter++; } foreach ( $columns as $column_key => $column_display_name ) { $class = array('manage-column', "column-{$column_key}"); if ( in_array( $column_key, (array) $hidden ) ) { //phpcs:ignore - 8.1 proof $class[] = 'hidden'; } if ( 'cb' === $column_key ) { $class[] = 'check-column'; } elseif ( in_array( $column_key, array('posts', 'comments', 'links') ) ) { $class[] = 'num'; } if ( $column_key === $primary ) { $class[] = 'column-primary'; } if ( isset( $sortable[$column_key] ) ) { list( $orderby, $desc_first ) = (array) $sortable[$column_key]; //phpcs:ignore - 8.1 proof if ( $current_orderby === $orderby ) { $order = ( 'asc' === $current_order ? 'desc' : 'asc' ); $class[] = 'sorted'; $class[] = $current_order; } else { $order = ( $desc_first ? 'desc' : 'asc' ); $class[] = 'sortable'; $class[] = ( $desc_first ? 'asc' : 'desc' ); } // ********************* // *** BEGIN CHANGES *** // ********************* // Code removed. // ******************* // *** END CHANGES *** // ******************* } $tag = ( 'cb' === $column_key ? 'td' : 'th' ); $scope = ( 'th' === $tag ? 'scope="col"' : '' ); $id = ( $with_id ? "id='{$column_key}'" : '' ); if ( !empty( $class ) ) { $class = "class='" . join( ' ', $class ) . "'"; } // ********************* // *** BEGIN CHANGES *** // ********************* echo '<' . esc_attr( $tag ) . ' ' . wp_kses_data( $scope ) . ' ' . wp_kses_data( $id ) . ' ' . wp_kses_data( $class ) . '>'; if ( isset( $sortable[$column_key] ) ) { ?> <a href="javascript:void(0)" onclick="jQuery('#wpda_main_form_orderby').val('<?php echo esc_attr( $orderby ); ?>'); jQuery('#wpda_main_form_order').val('<?php echo esc_attr( $order ); ?>'); jQuery('#wpda_main_form').submit();"> <span><?php echo wp_kses_data( $column_display_name ); ?></span><span class="sorting-indicator"></span> </a> <?php } else { echo wp_kses( $column_display_name, array( 'input' => array( 'id' => array(), 'type' => array(), ), 'label' => array( 'class' => array(), 'for' => array(), ), ) ); } echo '</' . esc_attr( $tag ) . '>'; // ******************* // *** END CHANGES *** // ******************* } } /** * Returns an associative array containing the bulk action * * @return array * @since 1.0.0 */ public function get_bulk_actions() { if ( !$this->bulk_actions_enabled ) { // Bulk actions disabled. return ''; } if ( empty( $this->wpda_list_columns->get_table_primary_key() ) ) { // Tables has no primary key: no bulk actions allowed! // Primary key is necessary to ensure uniqueness. return ''; } $actions = array(); if ( 'on' === $this->allow_delete ) { $actions = array( 'bulk-delete' => __( 'Delete Permanently', 'wp-data-access' ), ); } if ( $this->bulk_export_enabled && (WPDA::is_wpda_table( $this->table_name ) || WPDA::get_option( WPDA::OPTION_BE_EXPORT_ROWS ) === 'on') ) { if ( 'diehard' !== $this->page ) { // Not allowed on web page. $actions['bulk-export'] = __( 'Export to SQL', 'wp-data-access' ); } $actions['bulk-export-xml'] = __( 'Export to XML', 'wp-data-access' ); $actions['bulk-export-json'] = __( 'Export to JSON', 'wp-data-access' ); $actions['bulk-export-excel'] = __( 'Export to Excel', 'wp-data-access' ); $actions['bulk-export-csv'] = __( 'Export to CSV', 'wp-data-access' ); } return $actions; } /** * Generates the table navigation * * Generates the table navigation above or bellow the table and removes the * _wp_http_referrer and _wpnonce because it generates a error about URL too large. * * @param string $which CSS Class name. * * @return void * @since 1.0.0 */ protected function display_tablenav( $which ) { if ( !$this->hide_navigation && $this->items ) { ?> <div class="tablenav <?php echo esc_attr( $which ); ?>"> <div class="alignleft actions"> <?php $this->bulk_actions( $which ); ?> </div> <span id="wpda_row_export_anchor"></span> <?php $this->add_full_table_downloads(); $this->extra_tablenav( $which ); $this->pagination( $which ); ?> <br class="clear"/> </div> <?php } else { ?> <br class="clear"/> <?php } } /** * Add full export to CSV and JSON buttons * Needs to be granted in project page configuration */ protected function add_full_table_downloads() { } // Override to add arguments to CSV and JSON full table exports. protected function add_full_table_downloads_add_args() { } /** * Display the pagination * * Overriding original method pagination to support post instead of get. * Changes are marked! * * @param string $which CSS Class name. * * @since 1.0.0 */ protected function pagination( $which ) { if ( empty( $this->_pagination_args ) ) { return; } $total_items = $this->_pagination_args['total_items']; $total_pages = $this->_pagination_args['total_pages']; $infinite_scroll = false; if ( isset( $this->_pagination_args['infinite_scroll'] ) ) { $infinite_scroll = $this->_pagination_args['infinite_scroll']; } if ( 'top' === $which && $total_pages > 1 ) { $this->screen->render_screen_reader_content( 'heading_pagination' ); } // Add estimate character if row_count_estimate is enabled. $estimate = ( $this->row_count_estimate['is_estimate'] ? '~' : '' ); /* translators: %s: number of items (2x) */ $output = '<span class="displaying-num">' . sprintf( _n( '%s item', '%s items', $total_items ), $estimate . number_format_i18n( $total_items ) ) . '</span>'; $current = $this->get_pagenum(); if ( $this->search_value !== $this->search_value_old ) { $current = 1; } // ********************* // *** BEGIN CHANGES *** // ********************* // Code removed. // ******************* // *** END CHANGES *** // ******************* $page_links = array(); $total_pages_before = '<span class="paging-input">'; $total_pages_after = '</span></span>'; $disable_first = $disable_last = $disable_prev = $disable_next = false; if ( 1 === (int) $current ) { $disable_first = true; $disable_prev = true; } if ( 2 === (int) $current ) { $disable_first = true; } if ( (int) $current === (int) $total_pages ) { $disable_last = true; $disable_next = true; } if ( (int) $current === (int) $total_pages - 1 ) { $disable_last = true; } // ********************* // *** BEGIN CHANGES *** // ********************* $link_with_post_support = "\n <a class='%s'\n href='javascript:void(0)'\n onclick='jQuery(\"#current-page-selector\").val(\"%s\"); jQuery(\"#wpda_main_form\").submit();'>\n <span class='screen-reader-text'>%s</span>\n <span aria-hidden='true'>%s</span>\n </a>"; // ******************* // *** END CHANGES *** // ******************* if ( $disable_first ) { $page_links[] = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">«</span>'; } else { // ********************* // *** BEGIN CHANGES *** // ********************* $page_links[] = sprintf( $link_with_post_support, 'first-page button', '', __( 'First page' ), '«' ); // ******************* // *** END CHANGES *** // ******************* } if ( $disable_prev ) { $page_links[] = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">‹</span>'; } else { // ********************* // *** BEGIN CHANGES *** // ********************* $page_links[] = sprintf( $link_with_post_support, 'prev-page button', max( 1, $current - 1 ), __( 'Previous page' ), '‹' ); // ******************* // *** END CHANGES *** // ******************* } if ( 'bottom' === $which ) { $html_current_page = $current; $total_pages_before = '<span class="screen-reader-text">' . __( 'Current Page' ) . '</span><span id="table-paging" class="paging-input"><span class="tablenav-paging-text">'; } else { $html_current_page = sprintf( "%s<input class='current-page' id='current-page-selector' type='text' name='paged' value='%s' size='%d' aria-describedby='table-paging' /><span class='tablenav-paging-text'>", '<label for="current-page-selector" class="screen-reader-text">' . __( 'Current Page' ) . '</label>', $current, strlen( (string) $total_pages ) ); } $html_total_pages = sprintf( "<span class='total-pages'>%s</span>", number_format_i18n( $total_pages ) ); /* translators: %s: current page/total pages */ $page_links[] = $total_pages_before . sprintf( _x( '%1$s of %2$s', 'paging' ), $html_current_page, $html_total_pages ) . $total_pages_after; if ( $disable_next ) { $page_links[] = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">›</span>'; } else { // ********************* // *** BEGIN CHANGES *** // ********************* $page_links[] = sprintf( $link_with_post_support, 'next-page button', min( $total_pages, $current + 1 ), __( 'Next page' ), '›' ); // ******************* // *** END CHANGES *** // ******************* } if ( $disable_last ) { $page_links[] = '<span class="tablenav-pages-navspan button disabled" aria-hidden="true">»</span>'; } else { // ********************* // *** BEGIN CHANGES *** // ********************* $page_links[] = sprintf( $link_with_post_support, 'last-page button', $total_pages, __( 'Last page' ), '»' ); // ******************* // *** END CHANGES *** // ******************* } $pagination_links_class = 'pagination-links'; if ( !empty( $infinite_scroll ) ) { $pagination_links_class = ' hide-if-js'; } $output .= "\n<span class='{$pagination_links_class}'>" . join( "\n", $page_links ) . '</span>'; if ( $total_pages ) { $page_class = ( $total_pages < 2 ? ' one-page' : '' ); } else { $page_class = ' no-pages'; } $this->_pagination = "<div class='tablenav-pages{$page_class}'>{$output}</div>"; echo $this->_pagination; // phpcs:ignore WordPress.Security.EscapeOutput } }