PluginProbe
TablePress – Tables in WordPress made easy / 3.2
TablePress – Tables in WordPress made easy v3.2
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 +132 -75 2.3.23.2 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 *
@@ -334,8 +288,17 @@
334 288 if ( empty( $table['id'] ) ) {
335 289 return new WP_Error( 'table_save_empty_table_id' );
336 290 }
337 291
292 + /**
293 + * Fires before a table is saved.
294 + *
295 + * @since 3.1.0
296 + *
297 + * @param string $table_id ID of the table to be saved.
298 + */
299 + do_action( 'tablepress_event_pre_save_table', $table['id'] );
300 +
338 301 $post_id = $this->_get_post_id( $table['id'] );
339 302 if ( false === $post_id ) {
340 303 return new WP_Error( 'table_save_no_post_id_for_table_id', '', $table['id'] );
341 304 }
@@ -372,9 +335,9 @@
372 335 * Fires after a table has been saved.
373 336 *
374 337 * @since 1.5.0
375 338 *
376 - * @param string $table_id ID of the added table.
339 + * @param string $table_id ID of the saved table.
377 340 */
378 341 do_action( 'tablepress_event_saved_table', $table['id'] );
379 342
380 343 return $table['id'];
@@ -490,10 +453,21 @@
490 453 if ( ! $this->table_exists( $table_id ) ) {
491 454 return new WP_Error( 'table_delete_table_does_not_exist', '', $table_id );
492 455 }
493 456
457 + /**
458 + * Fires before a table is deleted.
459 + *
460 + * @since 3.1.0
461 + *
462 + * @param string $table_id ID of the table to be deleted.
463 + */
464 + do_action( 'tablepress_event_pre_delete_table', $table_id );
465 +
494 466 $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 .
467 +
468 + // @phpstan-ignore argument.type
469 + $deleted = $this->model_post->delete( $post_id ); // Post Meta fields will be deleted automatically by that function.
496 470 if ( false === $deleted ) {
497 471 return new WP_Error( 'table_delete_post_could_not_be_deleted', '', $post_id );
498 472 }
499 473
@@ -654,13 +628,13 @@
654 628 }
655 629
656 630 // Kinsta.
657 631 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
632 + $GLOBALS['kinsta_cache']->kinsta_cache_purge->purge_complete_caches(); // @phpstan-ignore method.nonObject
659 633 }
660 634 // LiteSpeed Cache.
661 635 if ( is_callable( array( 'LiteSpeed_Cache_Tags', 'add_purge_tag' ) ) ) {
662 - LiteSpeed_Cache_Tags::add_purge_tag( '*' ); // @phpstan-ignore-line
636 + LiteSpeed_Cache_Tags::add_purge_tag( '*' ); // @phpstan-ignore class.notFound
663 637 }
664 638 // Pagely.
665 639 if ( class_exists( 'PagelyCachePurge' ) ) {
666 640 $_pagely = new PagelyCachePurge();
@@ -669,23 +643,23 @@
669 643 }
670 644 }
671 645 // Pressidum.
672 646 if ( is_callable( array( 'Ninukis_Plugin', 'get_instance' ) ) ) {
673 - $_pressidum = Ninukis_Plugin::get_instance(); // @phpstan-ignore-line
647 + $_pressidum = Ninukis_Plugin::get_instance(); // @phpstan-ignore class.notFound
674 648 if ( is_callable( array( $_pressidum, 'purgeAllCaches' ) ) ) {
675 - $_pressidum->purgeAllCaches(); // @phpstan-ignore-line
649 + $_pressidum->purgeAllCaches(); // @phpstan-ignore method.nonObject
676 650 }
677 651 }
678 652 // Savvii.
679 653 if ( defined( '\Savvii\CacheFlusherPlugin::NAME_DOMAINFLUSH_NOW' ) ) {
680 - $_savvii = new \Savvii\CacheFlusherPlugin(); // @phpstan-ignore-line
654 + $_savvii = new \Savvii\CacheFlusherPlugin(); // @phpstan-ignore class.notFound
681 655 if ( is_callable( array( $_savvii, 'domainflush' ) ) ) {
682 - $_savvii->domainflush(); // @phpstan-ignore-line
656 + $_savvii->domainflush(); // @phpstan-ignore class.notFound
683 657 }
684 658 }
685 659 // WP Fastest Cache.
686 660 if ( isset( $GLOBALS['wp_fastest_cache'] ) && is_callable( array( $GLOBALS['wp_fastest_cache'], 'deleteCache' ) ) ) {
687 - $GLOBALS['wp_fastest_cache']->deleteCache( true ); // @phpstan-ignore-line
661 + $GLOBALS['wp_fastest_cache']->deleteCache( true ); // @phpstan-ignore method.nonObject
688 662 }
689 663 // WP-Optimize.
690 664 if ( function_exists( 'WP_Optimize' ) ) {
691 665 WP_Optimize()->get_page_cache()->purge();
@@ -813,10 +787,10 @@
813 787 'last_modified' => wp_date( 'Y-m-d H:i:s' ),
814 788 'author' => get_current_user_id(),
815 789 'options' => array(
816 790 'last_editor' => get_current_user_id(),
817 - 'table_head' => true,
818 - 'table_foot' => false,
791 + 'table_head' => 1,
792 + 'table_foot' => 0,
819 793 'alternating_row_colors' => true,
820 794 'row_hover' => true,
821 795 'print_name' => false,
822 796 'print_name_position' => 'above',
@@ -834,9 +808,9 @@
834 808 'datatables_scrollx' => false,
835 809 'datatables_custom_commands' => '',
836 810 ),
837 811 'visibility' => array(
838 - 'rows' => array( 1 ), // One visbile row.
812 + 'rows' => array( 1 ), // One visible row.
839 813 'columns' => array( 1 ), // One visible column.
840 814 ),
841 815 );
842 816 /**
@@ -912,9 +886,9 @@
912 886 array_walk_recursive(
913 887 $table['data'],
914 888 static function ( /* string|int|float|bool|null */ &$cell_content, int $col_idx ): void {
915 889 $cell_content = (string) $cell_content;
916 - }
890 + },
917 891 );
918 892 // Table Options.
919 893 if ( isset( $new_table['options'] ) ) { // Options are for example not set for newly added tables.
920 894 // Specials check for certain options.
@@ -929,8 +903,17 @@
929 903 if ( $new_table['options']['datatables_paginate_entries'] < 1 ) {
930 904 $new_table['options']['datatables_paginate_entries'] = 10; // Default value.
931 905 }
932 906 }
907 +
908 + // Backward compatibility: Convert boolean or numeric string "table_head" and "table_foot" options to integer.
909 + if ( isset( $new_table['options']['table_head'] ) ) {
910 + $new_table['options']['table_head'] = absint( $new_table['options']['table_head'] );
911 + }
912 + if ( isset( $new_table['options']['table_foot'] ) ) {
913 + $new_table['options']['table_foot'] = absint( $new_table['options']['table_foot'] );
914 + }
915 +
933 916 // Merge new options.
934 917 $default_table = $this->get_table_template();
935 918 $table['options'] = array_intersect_key( $table['options'], $default_table['options'] );
936 919 $new_table['options'] = array_intersect_key( $new_table['options'], $default_table['options'] );
@@ -944,8 +927,18 @@
944 927 // $table['created'] = wp_date( 'Y-m-d H:i:s' ); // We don't want this, as it would override the original datetime.
945 928 $table['last_modified'] = wp_date( 'Y-m-d H:i:s' );
946 929 $table['options']['last_editor'] = get_current_user_id();
947 930
931 + // Prevent issues if the "Custom Commands" field is not set, e.g. when non-admins have previously edited the table.
932 + if ( ! isset( $table['options']['datatables_custom_commands'] ) ) {
933 + $table['options']['datatables_custom_commands'] = '';
934 + }
935 +
936 + // Convert CSS classes and some DataTables 1.x parameters to the DataTables 2 variants.
937 + if ( '' !== $table['options']['datatables_custom_commands'] ) {
938 + $table['options']['datatables_custom_commands'] = TablePress::convert_datatables_api_data( $table['options']['datatables_custom_commands'] );
939 + }
940 +
948 941 return $table;
949 942 }
950 943
951 944 /**
@@ -958,9 +951,9 @@
958 951 * @return bool True on success, false on error.
959 952 */
960 953 protected function _add_table_options( int $post_id, array $options ): bool {
961 954 $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
955 + return $this->model_post->add_meta_field( $post_id, $this->table_options_field_name, $options ); // @phpstan-ignore argument.type
963 956 }
964 957
965 958 /**
966 959 * Update the table options of a table (in a post meta field in the table's post).
@@ -972,9 +965,9 @@
972 965 * @return bool True on success, false on error.
973 966 */
974 967 protected function _update_table_options( int $post_id, array $options ): bool {
975 968 $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
969 + return $this->model_post->update_meta_field( $post_id, $this->table_options_field_name, $options ); // @phpstan-ignore argument.type
977 970 }
978 971
979 972 /**
980 973 * Get the table options of a table (from a post meta field of the table's post).
@@ -988,9 +981,15 @@
988 981 $options = $this->model_post->get_meta_field( $post_id, $this->table_options_field_name );
989 982 if ( empty( $options ) ) {
990 983 return array();
991 984 }
992 - return (array) json_decode( $options, true );
985 + $options = (array) json_decode( $options, true );
986 +
987 + // Backward compatibility: Convert boolean "table_head" and "table_foot" options to integer.
988 + $options['table_head'] = absint( $options['table_head'] );
989 + $options['table_foot'] = absint( $options['table_foot'] );
990 +
991 + return $options;
993 992 }
994 993
995 994 /**
996 995 * Save the table visibility of a table (in a post meta field of the table's post).
@@ -1002,9 +1001,9 @@
1002 1001 * @return bool True on success, false on error.
1003 1002 */
1004 1003 protected function _add_table_visibility( int $post_id, array $visibility ): bool {
1005 1004 $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
1005 + return $this->model_post->add_meta_field( $post_id, $this->table_visibility_field_name, $visibility ); // @phpstan-ignore argument.type
1007 1006 }
1008 1007
1009 1008 /**
1010 1009 * Update the table visibility of a table (in a post meta field in the table's post).
@@ -1016,9 +1015,9 @@
1016 1015 * @return bool True on success, false on error.
1017 1016 */
1018 1017 protected function _update_table_visibility( int $post_id, array $visibility ): bool {
1019 1018 $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
1019 + return $this->model_post->update_meta_field( $post_id, $this->table_visibility_field_name, $visibility ); // @phpstan-ignore argument.type
1021 1020 }
1022 1021
1023 1022 /**
1024 1023 * Get the table visibility of a table (from a post meta field of the table's post).
@@ -1063,8 +1062,21 @@
1063 1062
1064 1063 // Go through all tables (this loop now uses the WP cache).
1065 1064 foreach ( $table_post as $post_id ) {
1066 1065 $table_options = $this->_get_table_options( $post_id );
1066 +
1067 + /**
1068 + * Filters the Table Options before they are merged with the default Table Options.
1069 + *
1070 + * @since 3.0.0
1071 + *
1072 + * @param array<string, mixed> $table_options Table Options.
1073 + * @param array<string, mixed> $default_table_options Default Table Options.
1074 + * @param bool $remove_old_options Whether old table options should be removed from the database.
1075 + * @param int $post_id Post ID of the table.
1076 + */
1077 + $table_options = apply_filters( 'tablepress_table_options_before_merge', $table_options, $default_table['options'], $remove_old_options, $post_id );
1078 +
1067 1079 if ( $remove_old_options ) {
1068 1080 // Remove old (i.e. no longer existing) Table Options.
1069 1081 $table_options = array_intersect_key( $table_options, $default_table['options'] );
1070 1082 }
@@ -1074,8 +1086,48 @@
1074 1086 }
1075 1087 }
1076 1088
1077 1089 /**
1090 + * Updates all tables' "Custom Commands" to use DataTables 2 variants instead of old DataTables 1.x CSS classes and parameters
1091 + *
1092 + * @since 3.0.0
1093 + */
1094 + public function update_custom_commands_datatables_tp30(): void {
1095 + $table_post = $this->tables->get( 'table_post' );
1096 + if ( empty( $table_post ) ) {
1097 + return;
1098 + }
1099 +
1100 + // Prime the meta cache with the table options of all tables.
1101 + update_meta_cache( 'post', array_values( $table_post ) );
1102 +
1103 + foreach ( $table_post as $table_id => $post_id ) {
1104 + $table_options = $this->_get_table_options( $post_id );
1105 +
1106 + // Fix tables where the "Custom Commands" entry is missing entirely.
1107 + if ( ! isset( $table_options['datatables_custom_commands'] ) ) {
1108 + $table_options['datatables_custom_commands'] = '';
1109 + $this->_update_table_options( $post_id, $table_options );
1110 + continue;
1111 + }
1112 +
1113 + // Nothing to do if there are no "Custom Commands".
1114 + if ( '' === $table_options['datatables_custom_commands'] ) {
1115 + continue;
1116 + }
1117 + // Run search/replace.
1118 + $old_custom_commands = $table_options['datatables_custom_commands'];
1119 + $table_options['datatables_custom_commands'] = TablePress::convert_datatables_api_data( $table_options['datatables_custom_commands'] );
1120 + // No need to save (which runs a DB query) if nothing was replaced in the "Custom Commands".
1121 + if ( $old_custom_commands === $table_options['datatables_custom_commands'] ) {
1122 + continue;
1123 + }
1124 +
1125 + $this->_update_table_options( $post_id, $table_options );
1126 + }
1127 + }
1128 +
1129 + /**
1078 1130 * Invalidate all table output caches, e.g. after a plugin update.
1079 1131 *
1080 1132 * @since 1.0.0
1081 1133 */
@@ -1205,18 +1257,23 @@
1205 1257 }
1206 1258
1207 1259 // Pretend that there is a `_tablepress_export_table_id` post meta field with the list of table IDs.
1208 1260 $key = '_tablepress_export_table_id';
1209 - $value = wxr_cdata( implode( ',', $table_ids ) ); // @phpstan-ignore-line
1210 1261
1262 + /**
1263 + * Load WP export functions.
1264 + */
1265 + require_once ABSPATH . 'wp-admin/includes/export.php'; // @phpstan-ignore requireOnce.fileNotFound (This is a WordPress core file that always exists.)
1266 + $value = wxr_cdata( implode( ',', $table_ids ) );
1267 +
1211 1268 // Hijack the filter and print extra XML code for our faked post meta field.
1212 1269 // phpcs:disable WordPress.Security.EscapeOutput.HeredocOutputNotEscaped
1213 1270 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;
1271 + <wp:postmeta>
1272 + <wp:meta_key>{$key}</wp:meta_key>
1273 + <wp:meta_value>{$value}</wp:meta_value>
1274 + </wp:postmeta>\n
1275 + WXR;
1219 1276 // phpcs:enable
1220 1277
1221 1278 return $skip;
1222 1279 }