PluginProbe
TablePress – Tables in WordPress made easy / 3.0.4
TablePress – Tables in WordPress made easy v3.0.4
3.3.4 3.3.3 3.3.2 3.3.1 trunk 1.12 1.14 1.9.2 2.0.4 2.1.7 2.1.8 2.2 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.3 2.3.1 2.3.2 2.4 2.4.1 2.4.2 2.4.3 2.4.4 All 44 releases
← All changes | models/model-table.php +112 -73 2.43.0.4 View file →
@@ -24,27 +24,24 @@
24 24 /**
25 25 * Instance of the Post Type Model.
26 26 *
27 27 * @since 1.0.0
28 - * @var TablePress_Post_Model
29 28 */
30 - protected $model_post;
29 + protected \TablePress_Post_Model $model_post;
31 30
32 31 /**
33 32 * Name of the Post Meta Field for table options.
34 33 *
35 34 * @since 1.0.0
36 - * @var string
37 35 */
38 - protected $table_options_field_name = '_tablepress_table_options';
36 + protected string $table_options_field_name = '_tablepress_table_options';
39 37
40 38 /**
41 39 * Name of the Post Meta Field for table visibility.
42 40 *
43 41 * @since 1.0.0
44 - * @var string
45 42 */
46 - protected $table_visibility_field_name = '_tablepress_table_visibility';
43 + protected string $table_visibility_field_name = '_tablepress_table_visibility';
47 44
48 45 /**
49 46 * Default set of tables.
50 47 *
@@ -53,9 +50,9 @@
53 50 * @type int $last_id Last table ID that was given to a new table.
54 51 * @type array<string, int> $table_post Connections between table ID and post ID (key: table ID, value: post ID).
55 52 * }
56 53 */
57 - protected $default_tables = array(
54 + protected array $default_tables = array(
58 55 'last_id' => 0,
59 56 'table_post' => array(),
60 57 );
61 58
@@ -62,11 +59,10 @@
62 59 /**
63 60 * Instance of WP_Option class for the list of tables.
64 61 *
65 62 * @since 1.0.0
66 - * @var TablePress_WP_Option
67 63 */
68 - protected $tables;
64 + protected \TablePress_WP_Option $tables;
69 65
70 66 /**
71 67 * Init the Table model by instantiating a Post model and loading the list of tables option.
72 68 *
@@ -114,11 +110,8 @@
114 110 * @param int $post_id Post ID of an existing table, or -1 for a new table.
115 111 * @return array<string, mixed> Post.
116 112 */
117 113 protected function _table_to_post( array $table, int $post_id ): array {
118 - // Run filters on content in each cell and other fields.
119 - $table = $this->filter_content( $table );
120 -
121 114 // Sanitize each cell, table name, and table description, if the user is not allowed to work with unfiltered HTML.
122 115 if ( ! current_user_can( 'unfiltered_html' ) ) {
123 116 $table = $this->sanitize( $table );
124 117 }
@@ -283,47 +276,8 @@
283 276 return $table;
284 277 }
285 278
286 279 /**
287 - * Filter/modify the content of table cells and other fields, e.g. for security hardening.
288 - *
289 - * This is similar to the `sanitize()` method, but executed for all users.
290 - * In 1.10.0, adding `rel="noopener noreferrer"` to all HTML link elements like `<a target=` was added. See https://core.trac.wordpress.org/ticket/43187.
291 - * Since 1.13.0, and on WP 5.6, only `rel="noopener"` is added. See https://core.trac.wordpress.org/ticket/49558.
292 - *
293 - * @since 1.10.0
294 - *
295 - * @param array<string, mixed> $table Table.
296 - * @return array<string, mixed> Filtered/modified table.
297 - */
298 - public function filter_content( array $table ): array {
299 - /**
300 - * Filters whether the contents of table cells and fields should be filtered/modified.
301 - *
302 - * @since 1.10.0
303 - *
304 - * @param bool $filter Whether to filter the content of table cells and other fields. Default true.
305 - */
306 - if ( ! apply_filters( 'tablepress_filter_table_cell_content', true ) ) {
307 - return $table;
308 - }
309 -
310 - // Filter the table name and description.
311 - $fields = array( 'name', 'description' );
312 - foreach ( $fields as $field ) {
313 - $table[ $field ] = wp_targeted_link_rel( $table[ $field ] );
314 - }
315 -
316 - foreach ( $table['data'] as $row_idx => $row ) {
317 - foreach ( $row as $column_idx => $cell_content ) {
318 - $table['data'][ $row_idx ][ $column_idx ] = wp_targeted_link_rel( $cell_content );
319 - }
320 - }
321 -
322 - return $table;
323 - }
324 -
325 - /**
326 280 * Save a table.
327 281 *
328 282 * @since 1.0.0
329 283 *
@@ -491,9 +445,11 @@
491 445 return new WP_Error( 'table_delete_table_does_not_exist', '', $table_id );
492 446 }
493 447
494 448 $post_id = $this->_get_post_id( $table_id ); // No ! false check necessary, as this is covered by table_exists() check above.
495 - $deleted = $this->model_post->delete( $post_id ); // Post Meta fields will be deleted automatically by that function. // @phpstan-ignore-line .
449 +
450 + // @phpstan-ignore argument.type
451 + $deleted = $this->model_post->delete( $post_id ); // Post Meta fields will be deleted automatically by that function.
496 452 if ( false === $deleted ) {
497 453 return new WP_Error( 'table_delete_post_could_not_be_deleted', '', $post_id );
498 454 }
499 455
@@ -654,13 +610,13 @@
654 610 }
655 611
656 612 // Kinsta.
657 613 if ( isset( $GLOBALS['kinsta_cache'] ) && ! empty( $GLOBALS['kinsta_cache']->kinsta_cache_purge ) && is_callable( array( $GLOBALS['kinsta_cache']->kinsta_cache_purge, 'purge_complete_caches' ) ) ) {
658 - $GLOBALS['kinsta_cache']->kinsta_cache_purge->purge_complete_caches(); // @phpstan-ignore-line
614 + $GLOBALS['kinsta_cache']->kinsta_cache_purge->purge_complete_caches(); // @phpstan-ignore method.nonObject
659 615 }
660 616 // LiteSpeed Cache.
661 617 if ( is_callable( array( 'LiteSpeed_Cache_Tags', 'add_purge_tag' ) ) ) {
662 - LiteSpeed_Cache_Tags::add_purge_tag( '*' ); // @phpstan-ignore-line
618 + LiteSpeed_Cache_Tags::add_purge_tag( '*' ); // @phpstan-ignore class.notFound
663 619 }
664 620 // Pagely.
665 621 if ( class_exists( 'PagelyCachePurge' ) ) {
666 622 $_pagely = new PagelyCachePurge();
@@ -669,23 +625,23 @@
669 625 }
670 626 }
671 627 // Pressidum.
672 628 if ( is_callable( array( 'Ninukis_Plugin', 'get_instance' ) ) ) {
673 - $_pressidum = Ninukis_Plugin::get_instance(); // @phpstan-ignore-line
629 + $_pressidum = Ninukis_Plugin::get_instance(); // @phpstan-ignore class.notFound
674 630 if ( is_callable( array( $_pressidum, 'purgeAllCaches' ) ) ) {
675 - $_pressidum->purgeAllCaches(); // @phpstan-ignore-line
631 + $_pressidum->purgeAllCaches(); // @phpstan-ignore method.nonObject
676 632 }
677 633 }
678 634 // Savvii.
679 635 if ( defined( '\Savvii\CacheFlusherPlugin::NAME_DOMAINFLUSH_NOW' ) ) {
680 - $_savvii = new \Savvii\CacheFlusherPlugin(); // @phpstan-ignore-line
636 + $_savvii = new \Savvii\CacheFlusherPlugin(); // @phpstan-ignore class.notFound
681 637 if ( is_callable( array( $_savvii, 'domainflush' ) ) ) {
682 - $_savvii->domainflush(); // @phpstan-ignore-line
638 + $_savvii->domainflush(); // @phpstan-ignore class.notFound
683 639 }
684 640 }
685 641 // WP Fastest Cache.
686 642 if ( isset( $GLOBALS['wp_fastest_cache'] ) && is_callable( array( $GLOBALS['wp_fastest_cache'], 'deleteCache' ) ) ) {
687 - $GLOBALS['wp_fastest_cache']->deleteCache( true ); // @phpstan-ignore-line
643 + $GLOBALS['wp_fastest_cache']->deleteCache( true ); // @phpstan-ignore method.nonObject
688 644 }
689 645 // WP-Optimize.
690 646 if ( function_exists( 'WP_Optimize' ) ) {
691 647 WP_Optimize()->get_page_cache()->purge();
@@ -813,10 +769,10 @@
813 769 'last_modified' => wp_date( 'Y-m-d H:i:s' ),
814 770 'author' => get_current_user_id(),
815 771 'options' => array(
816 772 'last_editor' => get_current_user_id(),
817 - 'table_head' => true,
818 - 'table_foot' => false,
773 + 'table_head' => 1,
774 + 'table_foot' => 0,
819 775 'alternating_row_colors' => true,
820 776 'row_hover' => true,
821 777 'print_name' => false,
822 778 'print_name_position' => 'above',
@@ -912,9 +868,9 @@
912 868 array_walk_recursive(
913 869 $table['data'],
914 870 static function ( /* string|int|float|bool|null */ &$cell_content, int $col_idx ): void {
915 871 $cell_content = (string) $cell_content;
916 - }
872 + },
917 873 );
918 874 // Table Options.
919 875 if ( isset( $new_table['options'] ) ) { // Options are for example not set for newly added tables.
920 876 // Specials check for certain options.
@@ -929,8 +885,17 @@
929 885 if ( $new_table['options']['datatables_paginate_entries'] < 1 ) {
930 886 $new_table['options']['datatables_paginate_entries'] = 10; // Default value.
931 887 }
932 888 }
889 +
890 + // Backward compatibility: Convert boolean or numeric string "table_head" and "table_foot" options to integer.
891 + if ( isset( $new_table['options']['table_head'] ) ) {
892 + $new_table['options']['table_head'] = absint( $new_table['options']['table_head'] );
893 + }
894 + if ( isset( $new_table['options']['table_foot'] ) ) {
895 + $new_table['options']['table_foot'] = absint( $new_table['options']['table_foot'] );
896 + }
897 +
933 898 // Merge new options.
934 899 $default_table = $this->get_table_template();
935 900 $table['options'] = array_intersect_key( $table['options'], $default_table['options'] );
936 901 $new_table['options'] = array_intersect_key( $new_table['options'], $default_table['options'] );
@@ -944,8 +909,18 @@
944 909 // $table['created'] = wp_date( 'Y-m-d H:i:s' ); // We don't want this, as it would override the original datetime.
945 910 $table['last_modified'] = wp_date( 'Y-m-d H:i:s' );
946 911 $table['options']['last_editor'] = get_current_user_id();
947 912
913 + // Prevent issues if the "Custom Commands" field is not set, e.g. when non-admins have previously edited the table.
914 + if ( ! isset( $table['options']['datatables_custom_commands'] ) ) {
915 + $table['options']['datatables_custom_commands'] = '';
916 + }
917 +
918 + // Convert CSS classes and some DataTables 1.x parameters to the DataTables 2 variants.
919 + if ( '' !== $table['options']['datatables_custom_commands'] ) {
920 + $table['options']['datatables_custom_commands'] = TablePress::convert_datatables_api_data( $table['options']['datatables_custom_commands'] );
921 + }
922 +
948 923 return $table;
949 924 }
950 925
951 926 /**
@@ -958,9 +933,9 @@
958 933 * @return bool True on success, false on error.
959 934 */
960 935 protected function _add_table_options( int $post_id, array $options ): bool {
961 936 $options = wp_json_encode( $options, TABLEPRESS_JSON_OPTIONS );
962 - return $this->model_post->add_meta_field( $post_id, $this->table_options_field_name, $options ); // @phpstan-ignore-line
937 + return $this->model_post->add_meta_field( $post_id, $this->table_options_field_name, $options ); // @phpstan-ignore argument.type
963 938 }
964 939
965 940 /**
966 941 * Update the table options of a table (in a post meta field in the table's post).
@@ -972,9 +947,9 @@
972 947 * @return bool True on success, false on error.
973 948 */
974 949 protected function _update_table_options( int $post_id, array $options ): bool {
975 950 $options = wp_json_encode( $options, TABLEPRESS_JSON_OPTIONS );
976 - return $this->model_post->update_meta_field( $post_id, $this->table_options_field_name, $options ); // @phpstan-ignore-line
951 + return $this->model_post->update_meta_field( $post_id, $this->table_options_field_name, $options ); // @phpstan-ignore argument.type
977 952 }
978 953
979 954 /**
980 955 * Get the table options of a table (from a post meta field of the table's post).
@@ -988,9 +963,15 @@
988 963 $options = $this->model_post->get_meta_field( $post_id, $this->table_options_field_name );
989 964 if ( empty( $options ) ) {
990 965 return array();
991 966 }
992 - return (array) json_decode( $options, true );
967 + $options = (array) json_decode( $options, true );
968 +
969 + // Backward compatibility: Convert boolean "table_head" and "table_foot" options to integer.
970 + $options['table_head'] = absint( $options['table_head'] );
971 + $options['table_foot'] = absint( $options['table_foot'] );
972 +
973 + return $options;
993 974 }
994 975
995 976 /**
996 977 * Save the table visibility of a table (in a post meta field of the table's post).
@@ -1002,9 +983,9 @@
1002 983 * @return bool True on success, false on error.
1003 984 */
1004 985 protected function _add_table_visibility( int $post_id, array $visibility ): bool {
1005 986 $visibility = wp_json_encode( $visibility, TABLEPRESS_JSON_OPTIONS );
1006 - return $this->model_post->add_meta_field( $post_id, $this->table_visibility_field_name, $visibility ); // @phpstan-ignore-line
987 + return $this->model_post->add_meta_field( $post_id, $this->table_visibility_field_name, $visibility ); // @phpstan-ignore argument.type
1007 988 }
1008 989
1009 990 /**
1010 991 * Update the table visibility of a table (in a post meta field in the table's post).
@@ -1016,9 +997,9 @@
1016 997 * @return bool True on success, false on error.
1017 998 */
1018 999 protected function _update_table_visibility( int $post_id, array $visibility ): bool {
1019 1000 $visibility = wp_json_encode( $visibility, TABLEPRESS_JSON_OPTIONS );
1020 - return $this->model_post->update_meta_field( $post_id, $this->table_visibility_field_name, $visibility ); // @phpstan-ignore-line
1001 + return $this->model_post->update_meta_field( $post_id, $this->table_visibility_field_name, $visibility ); // @phpstan-ignore argument.type
1021 1002 }
1022 1003
1023 1004 /**
1024 1005 * Get the table visibility of a table (from a post meta field of the table's post).
@@ -1063,8 +1044,21 @@
1063 1044
1064 1045 // Go through all tables (this loop now uses the WP cache).
1065 1046 foreach ( $table_post as $post_id ) {
1066 1047 $table_options = $this->_get_table_options( $post_id );
1048 +
1049 + /**
1050 + * Filters the Table Options before they are merged with the default Table Options.
1051 + *
1052 + * @since 3.0.0
1053 + *
1054 + * @param array<string, mixed> $table_options Table Options.
1055 + * @param array<string, mixed> $default_table_options Default Table Options.
1056 + * @param bool $remove_old_options Whether old table options should be removed from the database.
1057 + * @param int $post_id Post ID of the table.
1058 + */
1059 + $table_options = apply_filters( 'tablepress_table_options_before_merge', $table_options, $default_table['options'], $remove_old_options, $post_id );
1060 +
1067 1061 if ( $remove_old_options ) {
1068 1062 // Remove old (i.e. no longer existing) Table Options.
1069 1063 $table_options = array_intersect_key( $table_options, $default_table['options'] );
1070 1064 }
@@ -1074,8 +1068,48 @@
1074 1068 }
1075 1069 }
1076 1070
1077 1071 /**
1072 + * Updates all tables' "Custom Commands" to use DataTables 2 variants instead of old DataTables 1.x CSS classes and parameters
1073 + *
1074 + * @since 3.0.0
1075 + */
1076 + public function update_custom_commands_datatables_tp30(): void {
1077 + $table_post = $this->tables->get( 'table_post' );
1078 + if ( empty( $table_post ) ) {
1079 + return;
1080 + }
1081 +
1082 + // Prime the meta cache with the table options of all tables.
1083 + update_meta_cache( 'post', array_values( $table_post ) );
1084 +
1085 + foreach ( $table_post as $table_id => $post_id ) {
1086 + $table_options = $this->_get_table_options( $post_id );
1087 +
1088 + // Fix tables where the "Custom Commands" entry is missing entirely.
1089 + if ( ! isset( $table_options['datatables_custom_commands'] ) ) {
1090 + $table_options['datatables_custom_commands'] = '';
1091 + $this->_update_table_options( $post_id, $table_options );
1092 + continue;
1093 + }
1094 +
1095 + // Nothing to do if there are no "Custom Commands".
1096 + if ( '' === $table_options['datatables_custom_commands'] ) {
1097 + continue;
1098 + }
1099 + // Run search/replace.
1100 + $old_custom_commands = $table_options['datatables_custom_commands'];
1101 + $table_options['datatables_custom_commands'] = TablePress::convert_datatables_api_data( $table_options['datatables_custom_commands'] );
1102 + // No need to save (which runs a DB query) if nothing was replaced in the "Custom Commands".
1103 + if ( $old_custom_commands === $table_options['datatables_custom_commands'] ) {
1104 + continue;
1105 + }
1106 +
1107 + $this->_update_table_options( $post_id, $table_options );
1108 + }
1109 + }
1110 +
1111 + /**
1078 1112 * Invalidate all table output caches, e.g. after a plugin update.
1079 1113 *
1080 1114 * @since 1.0.0
1081 1115 */
@@ -1205,18 +1239,23 @@
1205 1239 }
1206 1240
1207 1241 // Pretend that there is a `_tablepress_export_table_id` post meta field with the list of table IDs.
1208 1242 $key = '_tablepress_export_table_id';
1209 - $value = wxr_cdata( implode( ',', $table_ids ) ); // @phpstan-ignore-line
1210 1243
1244 + /**
1245 + * Load WP export functions.
1246 + */
1247 + require_once ABSPATH . 'wp-admin/includes/export.php'; // @phpstan-ignore requireOnce.fileNotFound (This is a WordPress core file that always exists.)
1248 + $value = wxr_cdata( implode( ',', $table_ids ) );
1249 +
1211 1250 // Hijack the filter and print extra XML code for our faked post meta field.
1212 1251 // phpcs:disable WordPress.Security.EscapeOutput.HeredocOutputNotEscaped
1213 1252 echo <<<WXR
1214 - <wp:postmeta>
1215 - <wp:meta_key>{$key}</wp:meta_key>
1216 - <wp:meta_value>{$value}</wp:meta_value>
1217 - </wp:postmeta>\n
1218 -WXR;
1253 + <wp:postmeta>
1254 + <wp:meta_key>{$key}</wp:meta_key>
1255 + <wp:meta_value>{$value}</wp:meta_value>
1256 + </wp:postmeta>\n
1257 + WXR;
1219 1258 // phpcs:enable
1220 1259
1221 1260 return $skip;
1222 1261 }