File Editor
Directories:
.. (Back)
Files:
class-admin-page-helper.php
class-controller.php
class-css.php
class-evaluate-legacy.php
class-evaluate-phpspreadsheet.php
class-evaluate.php
class-export.php
class-import-base.php
class-import-file.php
class-import-legacy.php
class-import-phpspreadsheet.php
class-import.php
class-model.php
class-render.php
class-tablepress.php
class-view.php
class-wp_option.php
class-wp_user_option.php
index.php
Create New File
Create
Edit File: class-tablepress.php
<?php /** * TablePress Class * * @package TablePress * @author Tobias Bäthge * @since 1.0.0 */ // Prohibit direct script loading. defined( 'ABSPATH' ) || die( 'No direct script access allowed!' ); /** * TablePress class * * @package TablePress * @author Tobias Bäthge * @since 1.0.0 */ abstract class TablePress { /** * TablePress version. * * Increases whenever a new plugin version is released. * * @since 1.0.0 * @const string */ public const version = '3.0.1'; // phpcs:ignore Generic.NamingConventions.UpperCaseConstantName.ClassConstantNotUpperCase /** * TablePress internal plugin version ("options scheme" version). * * Increases whenever the scheme for the plugin options changes, or on a plugin update. * * @since 1.0.0 * @const int */ public const db_version = 97; // phpcs:ignore Generic.NamingConventions.UpperCaseConstantName.ClassConstantNotUpperCase /** * TablePress "table scheme" (data format structure) version. * * Increases whenever the scheme for a $table changes, * used to be able to update plugin options and table scheme independently. * * @since 1.0.0 * @const int */ public const table_scheme_version = 3; // phpcs:ignore Generic.NamingConventions.UpperCaseConstantName.ClassConstantNotUpperCase /** * Instance of the Options Model. * * @since 1.3.0 */ public static \TablePress_Options_Model $model_options; /** * Instance of the Table Model. * * @since 1.3.0 */ public static \TablePress_Table_Model $model_table; /** * Instance of the controller. * * @since 1.0.0 */ public static \TablePress_Frontend_Controller $controller; /** * Name of the Shortcode to show a TablePress table. * * Should only be modified through the filter hook 'tablepress_table_shortcode'. * * @since 1.0.0 */ public static string $shortcode = 'table'; /** * Name of the Shortcode to show extra information of a TablePress table. * * Should only be modified through the filter hook 'tablepress_table_info_shortcode'. * * @since 1.0.0 */ public static string $shortcode_info = 'table-info'; /** * List of TablePress premium modules. * * @since 2.1.0 * @var array<string, array{name: string, description: string, category: string, class: string, incompatible_classes: string[], minimum_plan: string, default_active: bool}> */ public static array $modules = array(); /** * Start-up TablePress (run on WordPress "init") and load the controller for the current state. * * @since 1.0.0 */ public static function run(): void { /** * Fires before TablePress is loaded. * * The `tablepress_loaded` action hook might be a better choice in most situations, as TablePress options will then be available. * * @since 1.0.0 */ do_action( 'tablepress_run' ); /** * Filters the string that is used as the [table] Shortcode. * * @since 1.0.0 * * @param string $shortcode The [table] Shortcode string. */ self::$shortcode = apply_filters( 'tablepress_table_shortcode', self::$shortcode ); /** * Filters the string that is used as the [table-info] Shortcode. * * @since 1.0.0 * * @param string $shortcode_info The [table-info] Shortcode string. */ self::$shortcode_info = apply_filters( 'tablepress_table_info_shortcode', self::$shortcode_info ); // Load modals for table and options, to be accessible from everywhere via `TablePress::$model_options` and `TablePress::$model_table`. self::$model_options = self::load_model( 'options' ); self::$model_table = self::load_model( 'table' ); // Exit early, i.e. before a controller is loaded, if TablePress functionality is likely not needed. $exit_early = false; if ( ( isset( $_SERVER['SCRIPT_FILENAME'] ) && 'wp-login.php' === basename( $_SERVER['SCRIPT_FILENAME'] ) ) // Detect the WordPress Login screen. || ( defined( 'XMLRPC_REQUEST' ) && XMLRPC_REQUEST ) || wp_doing_cron() ) { $exit_early = true; } /** * Filters whether TablePress should exit early, e.g. during wp-login.php, XML-RPC, and WP-Cron requests. * * @since 2.0.0 * * @param bool $exit_early Whether TablePress should exit early. */ if ( apply_filters( 'tablepress_exit_early', $exit_early ) ) { return; } if ( is_admin() ) { $controller = 'admin'; if ( wp_doing_ajax() ) { $controller = 'admin_ajax'; } self::load_controller( $controller ); } // Load the frontend controller in all scenarios, so that Shortcode render functions are always available. self::$controller = self::load_controller( 'frontend' ); // Add filters and actions for the integration into the WP WXR exporter and importer. add_action( 'wp_import_insert_post', array( TablePress::$model_table, 'add_table_id_on_wp_import' ), 10, 4 ); // phpcs:ignore Squiz.Classes.SelfMemberReference.NotUsed add_filter( 'wp_import_post_meta', array( TablePress::$model_table, 'prevent_table_id_post_meta_import_on_wp_import' ), 10, 3 ); // phpcs:ignore Squiz.Classes.SelfMemberReference.NotUsed add_filter( 'wxr_export_skip_postmeta', array( TablePress::$model_table, 'add_table_id_to_wp_export' ), 10, 3 ); // phpcs:ignore Squiz.Classes.SelfMemberReference.NotUsed /** * Fires after TablePress is loaded. * * The `tablepress_run` action hook can be used if code has to run before TablePress is loaded. * * @since 2.0.0 */ do_action( 'tablepress_loaded' ); } /** * Load a file with require_once(), after running it through a filter. * * @since 1.0.0 * * @param string $file Name of the PHP file. * @param string $folder Name of the folder with the file. */ public static function load_file( string $file, string $folder ): void { $full_path = TABLEPRESS_ABSPATH . $folder . '/' . $file; /** * Filters the full path of a file that shall be loaded. * * @since 1.0.0 * * @param string $full_path Full path of the file that shall be loaded. * @param string $file File name of the file that shall be loaded. * @param string $folder Folder name of the file that shall be loaded. */ $full_path = apply_filters( 'tablepress_load_file_full_path', $full_path, $file, $folder ); if ( $full_path ) { require_once $full_path; } } /** * Create a new instance of the $class_name, which is stored in $file in the $folder subfolder * of the plugin's directory. * * @since 1.0.0 * * @param string $class_name Name of the class. * @param string $file Name of the PHP file with the class. * @param string $folder Name of the folder with $class_name's $file. * @param mixed[]|string|null $params Optional. Parameters that are passed to the constructor of $class_name. * @return object Initialized instance of the class. */ public static function load_class( string $class_name, string $file, string $folder, /* ?array|string */ $params = null ): object { /** * Filters name of the class that shall be loaded. * * @since 1.0.0 * * @param string $class_name Name of the class that shall be loaded. */ $class_name = apply_filters( 'tablepress_load_class_name', $class_name ); if ( ! class_exists( $class_name, false ) ) { self::load_file( $file, $folder ); } $the_class = new $class_name( $params ); return $the_class; } /** * Create a new instance of the $model, which is stored in the "models" subfolder. * * @since 1.0.0 * * @param string $model Name of the model. * @return object Instance of the initialized model. */ public static function load_model( string $model ): object { // Model Base Class. self::load_file( 'class-model.php', 'classes' ); // Make first letter uppercase for a better looking naming pattern. $ucmodel = ucfirst( $model ); $the_model = self::load_class( "TablePress_{$ucmodel}_Model", "model-{$model}.php", 'models' ); return $the_model; } /** * Create a new instance of the $view, which is stored in the "views" subfolder, and set it up with $data. * * @since 1.0.0 * * @param string $view Name of the view to load. * @param array<string, mixed> $data Optional. Parameters/PHP variables that shall be available to the view. * @return object Instance of the initialized view, already set up, just needs to be rendered. */ public static function load_view( string $view, array $data = array() ): object { // View Base Class. self::load_file( 'class-view.php', 'classes' ); // Make first letter uppercase for a better looking naming pattern. $ucview = ucfirst( $view ); $the_view = self::load_class( "TablePress_{$ucview}_View", "view-{$view}.php", 'views' ); $the_view->setup( $view, $data ); return $the_view; } /** * Create a new instance of the $controller, which is stored in the "controllers" subfolder. * * @since 1.0.0 * * @param string $controller Name of the controller. * @return object Instance of the initialized controller. */ public static function load_controller( string $controller ): object { // Controller Base Class. self::load_file( 'class-controller.php', 'classes' ); // Make first letter uppercase for a better looking naming pattern. $uccontroller = ucfirst( $controller ); $the_controller = self::load_class( "TablePress_{$uccontroller}_Controller", "controller-{$controller}.php", 'controllers' ); return $the_controller; } /** * Generate the complete nonce string, from the nonce base, the action and an item, e.g. tablepress_delete_table_3. * * @since 1.0.0 * * @param string $action Action for which the nonce is needed. * @param string|false $item Optional. Item for which the action will be performed, like "table". false if no item should be used in the nonce. * @return string The resulting nonce string. */ public static function nonce( string $action, /* string|false */ $item = false ): string { $nonce = "tablepress_{$action}"; if ( $item ) { $nonce .= "_{$item}"; } return $nonce; } /** * Check whether a nonce string is valid. * * @since 1.0.0 * * @param string $action Action for which the nonce should be checked. * @param string|false $item Optional. Item for which the action should be performed, like "table". false if no item should be used in the nonce. * @param string $query_arg Optional. Name of the nonce query string argument in $_POST. * @param bool $ajax Whether the nonce comes from an AJAX request. */ public static function check_nonce( string $action, /* string|false */ $item = false, string $query_arg = '_wpnonce', bool $ajax = false ): void { $nonce_action = self::nonce( $action, $item ); if ( $ajax ) { check_ajax_referer( $nonce_action, $query_arg ); } else { check_admin_referer( $nonce_action, $query_arg ); } } /** * Calculate the column index (number) of a column header string (example: A is 1, AA is 27, ...). * * For the opposite, @see number_to_letter(). * * @since 1.0.0 * * @param string $column Column string. * @return int Column number, 1-based. */ public static function letter_to_number( string $column ): int { $column = (string) preg_replace( '/[^A-Za-z]/', '', $column ); $column = strtoupper( $column ); $count = strlen( $column ); $number = 0; for ( $i = 0; $i < $count; $i++ ) { $number += ( ord( $column[ $count - 1 - $i ] ) - 64 ) * 26 ** $i; } return $number; } /** * "Calculate" the column header string of a column index (example: 2 is B, AB is 28, ...). * * For the opposite, @see letter_to_number(). * * @since 1.0.0 * * @param int $number Column number, 1-based. * @return string Column string. */ public static function number_to_letter( int $number ): string { $column = ''; while ( $number > 0 ) { $column = chr( 65 + ( ( $number - 1 ) % 26 ) ) . $column; $number = intdiv( $number - 1, 26 ); } return $column; } /** * Get a nice looking date and time string from the mySQL format of datetime strings for output. * * @since 1.0.0 * * @param string $datetime_string DateTime string, often in mySQL format.. * @param string $separator_or_format Optional. Separator between date and time, or format string. * @return string Nice looking string with the date and time. */ public static function format_datetime( string $datetime_string, string $separator_or_format = ' ' ): string { $timezone = wp_timezone(); $datetime = date_create( $datetime_string, $timezone ); if ( false === $datetime ) { return $datetime_string; } $timestamp = $datetime->getTimestamp(); switch ( $separator_or_format ) { case ' ': case '<br />': case '<br/>': case '<br>': $date = wp_date( get_option( 'date_format' ), $timestamp, $timezone ); $time = wp_date( get_option( 'time_format' ), $timestamp, $timezone ); $output = "{$date}{$separator_or_format}{$time}"; break; default: $output = (string) wp_date( $separator_or_format, $timestamp, $timezone ); break; } return $output; } /** * Get the name from a WP user ID (used to store information on last editor of a table). * * @since 1.0.0 * * @param int $user_id WP user ID. * @return string Nickname of the WP user with the $user_id. */ public static function get_user_display_name( int $user_id ): string { $user = get_userdata( $user_id ); return $user->display_name ?? sprintf( '<em>%s</em>', __( 'unknown', 'tablepress' ) ); } /** * Sanitizes a CSS class to ensure it only contains valid characters. * * Strips the string down to A-Z, a-z, 0-9, :, _, -. * This is an extension to WP's `sanitize_html_class()`, to also allow `:` which are used in some CSS frameworks. * * @since 1.11.0 * * @param string $css_class The CSS class name to be sanitized. * @return string The sanitized CSS class. */ public static function sanitize_css_class( string $css_class ): string { // Strip out any %-encoded octets. $sanitized_css_class = (string) preg_replace( '|%[a-fA-F0-9][a-fA-F0-9]|', '', $css_class ); // Limit to A-Z, a-z, 0-9, ':', '_', and '-'. $sanitized_css_class = (string) preg_replace( '/[^A-Za-z0-9:_-]/', '', $sanitized_css_class ); return $sanitized_css_class; } /** * Extracts the top-level keys from a JavaScript object string. * * This function is used to extract the keys of the "Custom Commands" JavaScript object string, to check for overrides. * It covers most cases, like normal object properties with and without quotes, shorthand properties, and shorthand methods, * and also ignores single-line and multi-line comments. * It does not cover all possible JavaScript syntax (like template literals, special characters, ...), * but should be sufficient for the use case. * * @since 3.0.0 * * @param string $js_object_string A JavaScript object as a string. * @return string[] Array of top-level keys of the object. */ public static function extract_keys_from_js_object_string( string $js_object_string ): array { $object_keys = array(); $length = strlen( $js_object_string ); $depth = 0; $key_expected = true; $in_quotes = false; $quote_char = ''; $in_function_declaration = false; $in_single_line_comment = false; $in_multi_line_comment = false; $object_key = ''; for ( $i = 0; $i < $length; $i++ ) { $char = $js_object_string[ $i ]; // Skip parsing single-line comments. if ( $in_single_line_comment ) { if ( "\n" === $char ) { $in_single_line_comment = false; } continue; } else { // phpcs:ignore Universal.ControlStructures.DisallowLonelyIf.Found if ( '/' === $char && $i + 1 < $length && '/' === $js_object_string[ $i + 1 ] ) { $in_single_line_comment = true; ++$i; // Skip the second '/'. continue; } } // Skip parsing multi-line comments. if ( $in_multi_line_comment ) { if ( '*' === $char && $i + 1 < $length && '/' === $js_object_string[ $i + 1 ] ) { $in_multi_line_comment = false; ++$i; // Skip the '/' that ends the multi-line comment. } continue; } else { // phpcs:ignore Universal.ControlStructures.DisallowLonelyIf.Found if ( '/' === $char && $i + 1 < $length && '*' === $js_object_string[ $i + 1 ] ) { $in_multi_line_comment = true; ++$i; // Skip the '*'. continue; } } // Skip parsing while inside a quoted string. if ( $in_quotes ) { if ( $quote_char === $char ) { $in_quotes = false; } continue; } else { // phpcs:ignore Universal.ControlStructures.DisallowLonelyIf.Found if ( '"' === $char || "'" === $char ) { $in_quotes = true; $quote_char = $char; continue; } } /* * Skip parsing while inside a `function abc( ... )` declaration string. * The `$key_expected` check limits search the "function" string to object values. * The check for the plain `f` reduces expensive `substr()` calls. */ if ( ! $key_expected ) { if ( $in_function_declaration ) { if ( ')' === $char ) { $in_function_declaration = false; } continue; } else { // phpcs:ignore Universal.ControlStructures.DisallowLonelyIf.Found if ( 'f' === $char && 'function' === substr( $js_object_string, $i, 8 ) ) { $in_function_declaration = true; $i += 7; // Skip the rest of the "function" string. continue; } } } // Handle object depth, so that most parsing can be limited to the top level. if ( '{' === $char || '[' === $char ) { ++$depth; } // Extract only keys at the top level. if ( 1 === $depth ) { if ( $key_expected ) { if ( ':' === $char ) { // Check for normal keys, with value after :. // Go backwards to find the start of the key. $j = $i - 1; while ( $j >= 0 && preg_match( '/\s/', $js_object_string[ $j ] ) ) { --$j; } $key_end = $j; // Position of the last character of the key (potentially with quote). if ( '"' === $js_object_string[ $j ] || "'" === $js_object_string[ $j ] ) { // Quoted key. $quote_char = $js_object_string[ $j ]; --$j; while ( $j >= 0 && $quote_char !== $js_object_string[ $j ] ) { --$j; } $key_start = $j + 1; } else { // Unquoted key. while ( $j >= 0 && preg_match( '/[\w]/', $js_object_string[ $j ] ) ) { --$j; } $key_start = $j + 1; } $object_key = substr( $js_object_string, $key_start, $key_end - $key_start + 1 ); $object_key = trim( $object_key, "\"'" ); if ( '' !== $object_key && ! in_array( $object_key, $object_keys, true ) ) { $object_keys[] = $object_key; } $key_expected = false; } elseif ( ( ',' === $char || '}' === $char ) ) { // The `}` case is for the last key. // Check for shorthand properties (which must be unquoted). // Go backwards to find the start of the shorthand key. $j = $i - 1; while ( $j >= 0 && preg_match( '/\s/', $js_object_string[ $j ] ) ) { --$j; } $key_end = $j; // Position of the last character of the key (without a quote). while ( $j >= 0 && preg_match( '/[\w]/', $js_object_string[ $j ] ) ) { --$j; } $key_start = $j + 1; $object_key = substr( $js_object_string, $key_start, $key_end - $key_start + 1 ); if ( '' !== $object_key && ! in_array( $object_key, $object_keys, true ) ) { $object_keys[] = $object_key; } } elseif ( '(' === $char ) { // Detect shorthand method definitions. // Go back to find the start of the method name. $j = $i - 1; while ( $j >= 0 && preg_match( '/\s/', $js_object_string[ $j ] ) ) { --$j; } $key_end = $j; while ( $j >= 0 && preg_match( '/[\w]/', $js_object_string[ $j ] ) ) { --$j; } $key_start = $j + 1; $object_key = substr( $js_object_string, $key_start, $key_end - $key_start + 1 ); if ( '' !== $object_key && ! in_array( $object_key, $object_keys, true ) ) { $object_keys[] = $object_key; } } } // Reset the "key expected" flag after a comma or closing brace. if ( ',' === $char || '}' === $char ) { $key_expected = true; } } // Handle object depth. if ( '}' === $char || ']' === $char ) { --$depth; } } return $object_keys; } /** * Converts old DataTables 1.x CSS classes and parameters to the DataTables 2 variants. * * This function is used to modernize "Custom CSS" and "Custom Commands" for compatibility with DataTables 2.x. * It probably does not catch all possible cases. * * @since 3.0.0 * * @param string $code Code that contains DataTables 1.x CSS classes and parameters. * @return string Updated code with DataTables 2.x CSS classes and parameters. */ public static function convert_datatables_api_data( string $code ): string { /** * Mappings for DataTables 1.x CSS class or parameter to DataTables 2 variants. * As this array is used in `strtr()`, it's pre-sorted for descending string length of the array keys. */ static $datatables_api_data_mappings = array( // CSS classes. '.tablepress thead .sorting:hover' => '.tablepress thead .dt-orderable-asc:hover,.tablepress thead .dt-orderable-desc:hover', '.tablepress thead .sorting_desc' => '.tablepress thead .dt-ordering-desc', '.dataTables_filter label input' => '.dt-container .dt-search input', '.tablepress thead .sorting_asc' => '.tablepress thead .dt-ordering-asc', '.dataTables_scrollFootInner' => '.dt-scroll-footInner', '.dataTables_scrollHeadInner' => '.dt-scroll-headInner', '.tablepress thead .sorting' => '.tablepress thead .dt-orderable-asc,.tablepress thead .dt-orderable-desc', '.dataTables_processing' => '.dt-processing', '.dataTables_scrollBody' => '.dt-scroll-body', '.dataTables_scrollFoot' => '.dt-scroll-foot', '.dataTables_scrollHead' => '.dt-scroll-head', '.dataTables_paginate' => '.dt-paging', '.tablepress .even td' => '.tablepress>:where(tbody.row-striping)>:nth-child(odd)>*', '.dataTables_wrapper' => '.dt-container', '.tablepress .odd td' => '.tablepress>:where(tbody.row-striping)>:nth-child(even)>*', '.dataTables_filter' => '.dt-search', '.dataTables_length' => '.dt-length', '.dataTables_scroll' => '.dt-scroll', '.dataTables_empty' => '.dt-empty', '.dataTables_info' => '.dt-info', '.paginate_button' => '.dt-paging-button', // DataTables API functions. '$.fn.dataTable.' => 'DataTable.', ); $code = strtr( $code, $datatables_api_data_mappings ); // HTML ID mappings, which were removed. if ( str_contains( $code, '#tablepress-' ) ) { $code = (string) preg_replace( array( '/#tablepress-([A-Za-z1-9_-]|[A-Za-z0-9_-]{2,})_paginate/', '/#tablepress-([A-Za-z1-9_-]|[A-Za-z0-9_-]{2,})_filter/', '/#tablepress-([A-Za-z1-9_-]|[A-Za-z0-9_-]{2,})_length/', '/#tablepress-([A-Za-z1-9_-]|[A-Za-z0-9_-]{2,})_info/', ), array( '#tablepress-$1_wrapper .dt-paging', '#tablepress-$1_wrapper .dt-search', '#tablepress-$1_wrapper .dt-length', '#tablepress-$1_wrapper .dt-info', ), $code, ); } return $code; } /** * Retrieves all information of a WP_Error object as a string. * * @since 1.4.0 * * @param WP_Error $wp_error A WP_Error object. * @return string All error codes, messages, and data of the WP_Error. */ public static function get_wp_error_string( WP_Error $wp_error ): string { $error_strings = array(); $error_codes = $wp_error->get_error_codes(); // Reverse order to get latest errors first. $error_codes = array_reverse( $error_codes ); foreach ( $error_codes as $error_code ) { $error_strings[ $error_code ] = $error_code; $error_messages = $wp_error->get_error_messages( $error_code ); $error_messages = implode( ', ', $error_messages ); if ( ! empty( $error_messages ) ) { $error_strings[ $error_code ] .= " ({$error_messages})"; } $error_data = $wp_error->get_error_data( $error_code ); if ( is_string( $error_data ) ) { $error_strings[ $error_code ] .= " [{$error_data}]"; } elseif ( is_array( $error_data ) ) { foreach ( $error_data as $key => $value ) { $error_data[ $key ] = "{$key}: {$value}"; } $error_data = implode( ', ', $error_data ); $error_strings[ $error_code ] .= " [{$error_data}]"; } } return implode( ";\n", $error_strings ); } /** * Generate the action URL, to be used as a link within the plugin (e.g. in the submenu navigation or List of Tables). * * @since 1.0.0 * * @param array<string, mixed> $params Optional. Parameters to form the query string of the URL. * @param bool $add_nonce Optional. Whether the URL shall be nonced by WordPress. * @param string $target Optional. Target File, e.g. "admin-post.php" for POST requests. * @return string The URL for the given parameters (already run through esc_url() with $add_nonce === true!). */ public static function url( array $params = array(), bool $add_nonce = false, string $target = '' ): string { // Default action is "list", if no action given. if ( ! isset( $params['action'] ) ) { $params['action'] = 'list'; } $nonce_action = $params['action']; if ( '' !== $target ) { $params['action'] = "tablepress_{$params['action']}"; } else { $params['page'] = 'tablepress'; // Top-level parent page needs special treatment for better action strings. if ( self::$controller->is_top_level_page ) { $target = 'admin.php'; if ( ! in_array( $params['action'], array( 'list', 'edit' ), true ) ) { $params['page'] = "tablepress_{$params['action']}"; } if ( ! in_array( $params['action'], array( 'edit' ), true ) ) { $params['action'] = false; } } else { $target = self::$controller->parent_page; } } // $default_params also determines the order of the values in the query string. $default_params = array( 'page' => false, 'action' => false, 'item' => false, ); $params = array_merge( $default_params, $params ); $url = add_query_arg( $params, admin_url( $target ) ); if ( $add_nonce ) { $url = wp_nonce_url( $url, self::nonce( $nonce_action, $params['item'] ) ); // wp_nonce_url() does esc_html(). } return $url; } /** * Create a redirect URL from the $target_parameters and redirect the user. * * @since 1.0.0 * * @param array<string, mixed> $params Optional. Parameters from which the target URL is constructed. * @param bool $add_nonce Optional. Whether the URL shall be nonced by WordPress. */ public static function redirect( array $params = array(), bool $add_nonce = false ): void { $redirect = self::url( $params ); if ( $add_nonce ) { if ( ! isset( $params['item'] ) ) { $params['item'] = false; } // Don't use wp_nonce_url(), as that uses esc_html(). $redirect = add_query_arg( '_wpnonce', wp_create_nonce( self::nonce( $params['action'], $params['item'] ) ), $redirect ); } wp_redirect( $redirect ); exit; } /** * Determines whether the site uses the block editor, so that certain text and input fields referring to Shortcodes can be displayed or not. * * @since 2.0.1 * * @return bool True if the site uses the block editor, false otherwise. */ public static function site_uses_block_editor(): bool { $site_uses_block_editor = use_block_editor_for_post_type( 'post' ) && ! is_plugin_active( 'beaver-builder-lite-version/fl-builder.php' ) && ! is_plugin_active( 'classic-editor/classic-editor.php' ) && ! is_plugin_active( 'classic-editor-addon/classic-editor-addon.php' ) && ! is_plugin_active( 'elementor/elementor.php' ) && ! is_plugin_active( 'siteorigin-panels/siteorigin-panels.php' ); /** * Filters the outcome of the check whether the site uses the block editor. * * This can be used when certain conditions (e.g. new site builders) are not (yet) accounted for. * * @since 2.0.1 * * @param bool $site_uses_block_editor True if the site uses the block editor, false otherwise. */ $site_uses_block_editor = (bool) apply_filters( 'tablepress_site_uses_block_editor', $site_uses_block_editor ); return $site_uses_block_editor; } /** * Initializes the list of TablePress premium modules. * * @since 2.1.0 */ public static function init_modules(): void { self::$modules = array( 'advanced-access-rights' => array( 'name' => __( 'Advanced Access Rights', 'tablepress' ), 'description' => __( 'Restrict access to individual tables for individual users.', 'tablepress' ), 'category' => 'backend', 'class' => 'TablePress_Module_Advanced_Access_Rights', 'incompatible_classes' => array( 'TablePress_Advanced_Access_Rights_Controller' ), 'minimum_plan' => 'max', 'default_active' => false, ), 'automatic-periodic-table-import' => array( 'name' => __( 'Automatic Periodic Table Import', 'tablepress' ), 'description' => __( 'Periodically update tables from a configured import source.', 'tablepress' ), 'category' => 'backend', 'class' => 'TablePress_Module_Automatic_Periodic_Table_Import', 'incompatible_classes' => array( 'TablePress_Table_Auto_Update' ), 'minimum_plan' => 'max', 'default_active' => true, ), 'automatic-table-export' => array( 'name' => __( 'Automatic Table Export', 'tablepress' ), 'description' => __( 'Export and save tables to files on the server after they were modified.', 'tablepress' ), 'category' => 'backend', 'class' => 'TablePress_Module_Automatic_Table_Export', 'incompatible_classes' => array(), 'minimum_plan' => 'pro', 'default_active' => false, ), 'cell-highlighting' => array( 'name' => __( 'Cell Highlighting', 'tablepress' ), 'description' => __( 'Add CSS classes to cells for highlighting based on their content.', 'tablepress' ), 'category' => 'frontend', 'class' => 'TablePress_Module_Cell_Highlighting', 'incompatible_classes' => array( 'TablePress_Cell_Highlighting' ), 'minimum_plan' => 'pro', 'default_active' => false, ), 'column-order' => array( 'name' => __( 'Column Order', 'tablepress' ), 'description' => __( 'Order the columns in different ways when a table is shown.', 'tablepress' ), 'category' => 'data-management', 'class' => 'TablePress_Module_Column_Order', 'incompatible_classes' => array( 'TablePress_Column_Order' ), 'minimum_plan' => 'pro', 'default_active' => false, ), 'datatables-advanced-loading' => array( 'name' => __( 'Advanced Loading', 'tablepress' ), 'description' => __( 'Load the table data from a JSON array for faster loading.', 'tablepress' ), 'category' => 'backend', 'class' => 'TablePress_Module_DataTables_Advanced_Loading', 'incompatible_classes' => array( 'TablePress_DataTables_Advanced_Loading' ), 'minimum_plan' => 'max', 'default_active' => false, ), 'datatables-alphabetsearch' => array( 'name' => __( 'Alphabet Search', 'tablepress' ), 'description' => __( 'Show Alphabet buttons above the table to filter rows by their first letter.', 'tablepress' ), 'category' => 'search-filter', 'class' => 'TablePress_Module_DataTables_Alphabetsearch', 'incompatible_classes' => array(), 'minimum_plan' => 'pro', 'default_active' => false, ), 'datatables-auto-filter' => array( 'name' => __( 'Automatic Filter', 'tablepress' ), 'description' => __( 'Pre-filter a table when it is shown.', 'tablepress' ), 'category' => 'search-filter', 'class' => 'TablePress_Module_DataTables_Auto_Filter', 'incompatible_classes' => array( 'TablePress_DataTables_Auto_Filter' ), 'minimum_plan' => 'pro', 'default_active' => false, ), 'datatables-buttons' => array( 'name' => __( 'User Action Buttons', 'tablepress' ), 'description' => __( 'Add buttons for downloading, copying, printing, and changing column visibility of tables.', 'tablepress' ), 'category' => 'frontend', 'class' => 'TablePress_Module_DataTables_Buttons', 'incompatible_classes' => array( 'TablePress_DataTables_Buttons' ), 'minimum_plan' => 'pro', 'default_active' => true, ), 'datatables-columnfilterwidgets' => array( 'name' => __( 'Column Filter Dropdowns', 'tablepress' ), 'description' => __( 'Add a search dropdown for each column above the table.', 'tablepress' ), 'category' => 'search-filter', 'class' => 'TablePress_Module_DataTables_ColumnFilterWidgets', 'incompatible_classes' => array(), 'minimum_plan' => 'pro', 'default_active' => true, ), 'datatables-column-filter' => array( 'name' => __( 'Individual Column Filtering', 'tablepress' ), 'description' => __( 'Add a search field for each column to the table head or foot row.', 'tablepress' ), 'category' => 'search-filter', 'class' => 'TablePress_Module_DataTables_Column_Filter', 'incompatible_classes' => array(), 'minimum_plan' => 'pro', 'default_active' => false, ), 'datatables-counter-column' => array( 'name' => __( 'Index Column', 'tablepress' ), 'description' => __( 'Make the first column an index or counter column with the row position.', 'tablepress' ), 'category' => 'frontend', 'class' => 'TablePress_Module_DataTables_Counter_Column', 'incompatible_classes' => array(), 'minimum_plan' => 'pro', 'default_active' => false, ), 'datatables-fixedheader-fixedcolumns' => array( 'name' => __( 'Fixed Rows and Columns', 'tablepress' ), 'description' => __( 'Fix the header and footer row and the first and last column when scrolling the table.', 'tablepress' ), 'category' => 'frontend', 'class' => 'TablePress_Module_DataTables_FixedHeader_FixedColumns', 'incompatible_classes' => array( 'TablePress_DataTables_FixedHeader', 'TablePress_DataTables_FixedColumns', ), 'minimum_plan' => 'pro', 'default_active' => true, ), 'datatables-layout' => array( 'name' => __( 'Table Layout', 'tablepress' ), 'description' => __( 'Customize the layout and position of features around a table.', 'tablepress' ), 'category' => 'frontend', 'class' => 'TablePress_Module_DataTables_Layout', 'incompatible_classes' => array(), 'minimum_plan' => 'pro', 'default_active' => true, ), 'datatables-fuzzysearch' => array( 'name' => __( 'Fuzzy Search', 'tablepress' ), 'description' => __( 'Let the search account for spelling mistakes and typos and find similar matches.', 'tablepress' ), 'category' => 'search-filter', 'class' => 'TablePress_Module_DataTables_FuzzySearch', 'incompatible_classes' => array(), 'minimum_plan' => 'max', 'default_active' => false, ), 'datatables-inverted-filter' => array( 'name' => __( 'Inverted Filtering', 'tablepress' ), 'description' => __( 'Turn the filtering into a search and hide the table if no search term is entered.', 'tablepress' ), 'category' => 'search-filter', 'class' => 'TablePress_Module_DataTables_Inverted_Filter', 'incompatible_classes' => array(), 'minimum_plan' => 'max', 'default_active' => false, ), 'datatables-pagination' => array( 'name' => __( 'Advanced Pagination Settings', 'tablepress' ), 'description' => __( 'Customize the pagination settings of the table.', 'tablepress' ), 'category' => 'frontend', 'class' => 'TablePress_Module_DataTables_Pagination', 'incompatible_classes' => array(), 'minimum_plan' => 'pro', 'default_active' => false, ), 'datatables-rowgroup' => array( 'name' => __( 'Row Grouping', 'tablepress' ), 'description' => __( 'Group table rows by a common keyword, category, or title.', 'tablepress' ), 'category' => 'frontend', 'class' => 'TablePress_Module_DataTables_RowGroup', 'incompatible_classes' => array( 'TablePress_DataTables_RowGroup' ), 'minimum_plan' => 'pro', 'default_active' => false, ), 'datatables-searchbuilder' => array( 'name' => __( 'Custom Search Builder', 'tablepress' ), 'description' => __( 'Show a search builder interface for filtering from groups and using conditions.', 'tablepress' ), 'category' => 'search-filter', 'class' => 'TablePress_Module_DataTables_SearchBuilder', 'incompatible_classes' => array(), 'minimum_plan' => 'max', 'default_active' => false, ), 'datatables-searchhighlight' => array( 'name' => __( 'Search Highlighting', 'tablepress' ), 'description' => __( 'Highlight found search terms in the table.', 'tablepress' ), 'category' => 'search-filter', 'class' => 'TablePress_Module_DataTables_SearchHighlight', 'incompatible_classes' => array(), 'minimum_plan' => 'pro', 'default_active' => false, ), 'datatables-searchpanes' => array( 'name' => __( 'Search Panes', 'tablepress' ), 'description' => __( 'Show panes for filtering the columns.', 'tablepress' ), 'category' => 'search-filter', 'class' => 'TablePress_Module_DataTables_SearchPanes', 'incompatible_classes' => array(), 'minimum_plan' => 'pro', 'default_active' => false, ), 'datatables-serverside-processing' => array( 'name' => __( 'Server-side Processing', 'tablepress' ), 'description' => __( 'Process sorting, filtering, and pagination on the server for faster loading of large tables.', 'tablepress' ), 'category' => 'backend', 'class' => 'TablePress_Module_DataTables_ServerSide_Processing', 'incompatible_classes' => array(), 'minimum_plan' => 'max', 'default_active' => true, ), 'default-style-customizer' => array( 'name' => __( 'Default Style Customizer', 'tablepress' ), 'description' => __( 'Change the default styling of your tables in the visual style customizer.', 'tablepress' ), 'category' => 'frontend', 'class' => 'TablePress_Module_Default_Style_Customizer', 'incompatible_classes' => array(), 'minimum_plan' => 'pro', 'default_active' => true, ), 'responsive-tables' => array( 'name' => __( 'Responsive Tables', 'tablepress' ), 'description' => __( 'Make your tables look good on different screen sizes.', 'tablepress' ), 'category' => 'frontend', 'class' => 'TablePress_Module_Responsive_Tables', 'incompatible_classes' => array( 'TablePress_Responsive_Tables' ), 'minimum_plan' => 'pro', 'default_active' => true, ), 'rest-api' => array( 'name' => __( 'REST API', 'tablepress' ), 'description' => __( 'Read table data via the WordPress REST API, e.g. in external apps.', 'tablepress' ), 'category' => 'backend', 'class' => 'TablePress_Module_REST_API', 'incompatible_classes' => array( 'TablePress_REST_API_Controller' ), 'minimum_plan' => 'max', 'default_active' => false, ), 'row-filtering' => array( 'name' => __( 'Row Filtering', 'tablepress' ), 'description' => __( 'Show only table rows that contain defined keywords.', 'tablepress' ), 'category' => 'data-management', 'class' => 'TablePress_Module_Row_Filtering', 'incompatible_classes' => array( 'TablePress_Row_Filter' ), 'minimum_plan' => 'pro', 'default_active' => true, ), 'row-highlighting' => array( 'name' => __( 'Row Highlighting', 'tablepress' ), 'description' => __( 'Add CSS classes to rows for highlighting based on their content.', 'tablepress' ), 'category' => 'frontend', 'class' => 'TablePress_Module_Row_Highlighting', 'incompatible_classes' => array( 'TablePress_Row_Highlighting' ), 'minimum_plan' => 'pro', 'default_active' => false, ), 'row-order' => array( 'name' => __( 'Row Order', 'tablepress' ), 'description' => __( 'Order the rows in different ways when a table is shown.', 'tablepress' ), 'category' => 'data-management', 'class' => 'TablePress_Module_Row_Order', 'incompatible_classes' => array( 'TablePress_Row_Order' ), 'minimum_plan' => 'pro', 'default_active' => false, ), ); } } // class TablePress
Save Changes
Rename File
Rename