PluginProbe
TablePress – Tables in WordPress made easy / 3.4
TablePress – Tables in WordPress made easy v3.4
3.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 All 45 releases
← All changes | models/model-table.php +161 -87 2.2.2 → 3.4 View file →
@@ -7,8 +7,10 @@
7 7 * @author Tobias Bäthge
8 8 * @since 1.0.0
9 9 */
10 10
11 +declare(strict_types=1);
12 +
11 13 // Prohibit direct script loading.
12 14 defined( 'ABSPATH' ) || die( 'No direct script access allowed!' );
13 15
14 16 /**
@@ -24,27 +26,24 @@
24 26 /**
25 27 * Instance of the Post Type Model.
26 28 *
27 29 * @since 1.0.0
28 - * @var TablePress_Post_Model
29 30 */
30 - protected $model_post;
31 + protected \TablePress_Post_Model $model_post;
31 32
32 33 /**
33 34 * Name of the Post Meta Field for table options.
34 35 *
35 36 * @since 1.0.0
36 - * @var string
37 37 */
38 - protected $table_options_field_name = '_tablepress_table_options';
38 + protected string $table_options_field_name = '_tablepress_table_options';
39 39
40 40 /**
41 41 * Name of the Post Meta Field for table visibility.
42 42 *
43 43 * @since 1.0.0
44 - * @var string
45 44 */
46 - protected $table_visibility_field_name = '_tablepress_table_visibility';
45 + protected string $table_visibility_field_name = '_tablepress_table_visibility';
47 46
48 47 /**
49 48 * Default set of tables.
50 49 *
@@ -53,9 +52,9 @@
53 52 * @type int $last_id Last table ID that was given to a new table.
54 53 * @type array<string, int> $table_post Connections between table ID and post ID (key: table ID, value: post ID).
55 54 * }
56 55 */
57 - protected $default_tables = array(
56 + protected array $default_tables = array(
58 57 'last_id' => 0,
59 58 'table_post' => array(),
60 59 );
61 60
@@ -62,11 +61,10 @@
62 61 /**
63 62 * Instance of WP_Option class for the list of tables.
64 63 *
65 64 * @since 1.0.0
66 - * @var TablePress_WP_Option
67 65 */
68 - protected $tables;
66 + protected \TablePress_WP_Option $tables;
69 67
70 68 /**
71 69 * Init the Table model by instantiating a Post model and loading the list of tables option.
72 70 *
@@ -114,11 +112,8 @@
114 112 * @param int $post_id Post ID of an existing table, or -1 for a new table.
115 113 * @return array<string, mixed> Post.
116 114 */
117 115 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 116 // Sanitize each cell, table name, and table description, if the user is not allowed to work with unfiltered HTML.
122 117 if ( ! current_user_can( 'unfiltered_html' ) ) {
123 118 $table = $this->sanitize( $table );
124 119 }
@@ -153,9 +148,9 @@
153 148 $table = array(
154 149 'id' => $table_id,
155 150 'name' => $post->post_title,
156 151 'description' => $post->post_excerpt,
157 - 'author' => $post->post_author,
152 + 'author' => (int) $post->post_author,
158 153 // 'created' => $post->post_date,
159 154 'last_modified' => $post->post_modified,
160 155 );
161 156
@@ -188,9 +183,9 @@
188 183 * @param bool $load_data Whether the table data shall be loaded.
189 184 * @param bool $load_options_visibility Whether the table options and table visibility shall be loaded.
190 185 * @return array<string, mixed>|WP_Error Table as an array on success, WP_Error on error.
191 186 */
192 - public function load( string $table_id, bool $load_data = true, bool $load_options_visibility = true ) /* : array|WP_Post */ {
187 + public function load( string $table_id, bool $load_data = true, bool $load_options_visibility = true ) /* : array|WP_Error */ {
193 188 if ( empty( $table_id ) ) {
194 189 return new WP_Error( 'table_load_empty_table_id' );
195 190 }
196 191
@@ -232,11 +227,13 @@
232 227
233 228 // This loop now uses the WP cache.
234 229 $table_ids = array();
235 230 foreach ( $table_post as $table_id => $post_id ) {
236 - $table_id = (string) $table_id;
231 + $table_id = (string) $table_id; // Ensure that the table ID is a string, as it comes from an array key where numeric strings are converted to integers.
232 +
237 233 // Load table without data and options to save memory.
238 234 $table = $this->load( $table_id, false, false );
235 +
239 236 // Skip tables that could not be loaded properly.
240 237 if ( ! is_wp_error( $table ) ) {
241 238 $table_ids[] = $table_id;
242 239 }
@@ -281,47 +278,8 @@
281 278 return $table;
282 279 }
283 280
284 281 /**
285 - * Filter/modify the content of table cells and other fields, e.g. for security hardening.
286 - *
287 - * This is similar to the `sanitize()` method, but executed for all users.
288 - * 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.
289 - * Since 1.13.0, and on WP 5.6, only `rel="noopener"` is added. See https://core.trac.wordpress.org/ticket/49558.
290 - *
291 - * @since 1.10.0
292 - *
293 - * @param array<string, mixed> $table Table.
294 - * @return array<string, mixed> Filtered/modified table.
295 - */
296 - public function filter_content( array $table ): array {
297 - /**
298 - * Filters whether the contents of table cells and fields should be filtered/modified.
299 - *
300 - * @since 1.10.0
301 - *
302 - * @param bool $filter Whether to filter the content of table cells and other fields. Default true.
303 - */
304 - if ( ! apply_filters( 'tablepress_filter_table_cell_content', true ) ) {
305 - return $table;
306 - }
307 -
308 - // Filter the table name and description.
309 - $fields = array( 'name', 'description' );
310 - foreach ( $fields as $field ) {
311 - $table[ $field ] = wp_targeted_link_rel( $table[ $field ] );
312 - }
313 -
314 - foreach ( $table['data'] as $row_idx => $row ) {
315 - foreach ( $row as $column_idx => $cell_content ) {
316 - $table['data'][ $row_idx ][ $column_idx ] = wp_targeted_link_rel( $cell_content );
317 - }
318 - }
319 -
320 - return $table;
321 - }
322 -
323 - /**
324 282 * Save a table.
325 283 *
326 284 * @since 1.0.0
327 285 *
@@ -332,8 +290,17 @@
332 290 if ( empty( $table['id'] ) ) {
333 291 return new WP_Error( 'table_save_empty_table_id' );
334 292 }
335 293
294 + /**
295 + * Fires before a table is saved.
296 + *
297 + * @since 3.1.0
298 + *
299 + * @param string $table_id ID of the table to be saved.
300 + */
301 + do_action( 'tablepress_event_pre_save_table', $table['id'] );
302 +
336 303 $post_id = $this->_get_post_id( $table['id'] );
337 304 if ( false === $post_id ) {
338 305 return new WP_Error( 'table_save_no_post_id_for_table_id', '', $table['id'] );
339 306 }
@@ -370,9 +337,9 @@
370 337 * Fires after a table has been saved.
371 338 *
372 339 * @since 1.5.0
373 340 *
374 - * @param string $table_id ID of the added table.
341 + * @param string $table_id ID of the saved table.
375 342 */
376 343 do_action( 'tablepress_event_saved_table', $table['id'] );
377 344
378 345 return $table['id'];
@@ -488,10 +455,21 @@
488 455 if ( ! $this->table_exists( $table_id ) ) {
489 456 return new WP_Error( 'table_delete_table_does_not_exist', '', $table_id );
490 457 }
491 458
459 + /**
460 + * Fires before a table is deleted.
461 + *
462 + * @since 3.1.0
463 + *
464 + * @param string $table_id ID of the table to be deleted.
465 + */
466 + do_action( 'tablepress_event_pre_delete_table', $table_id );
467 +
492 468 $post_id = $this->_get_post_id( $table_id ); // No ! false check necessary, as this is covered by table_exists() check above.
493 - $deleted = $this->model_post->delete( $post_id ); // Post Meta fields will be deleted automatically by that function. // @phpstan-ignore-line .
469 +
470 + // @phpstan-ignore argument.type
471 + $deleted = $this->model_post->delete( $post_id ); // Post Meta fields will be deleted automatically by that function.
494 472 if ( false === $deleted ) {
495 473 return new WP_Error( 'table_delete_post_could_not_be_deleted', '', $post_id );
496 474 }
497 475
@@ -526,11 +504,13 @@
526 504 return;
527 505 }
528 506
529 507 foreach ( $tables['table_post'] as $table_id => $post_id ) {
530 - $table_id = (string) $table_id;
508 + $table_id = (string) $table_id; // Ensure that the table ID is a string, as it comes from an array key where numeric strings are converted to integers.
509 +
531 510 $this->model_post->delete( $post_id ); // Post Meta fields will be deleted automatically by that function.
532 511 unset( $tables['table_post'][ $table_id ] );
512 +
533 513 // Invalidate table output caches that belong to this table.
534 514 $this->invalidate_table_output_cache( $table_id );
535 515 }
536 516
@@ -648,15 +628,22 @@
648 628 foreach ( $cache_flush_hooks as $cache_flush_hook ) {
649 629 do_action( $cache_flush_hook );
650 630 }
651 631
632 + // Cloudflare APO.
633 + if ( class_exists( '\Cloudflare\APO\WordPress\Hooks' ) ) {
634 + $_cloudflare = new \Cloudflare\APO\WordPress\Hooks();
635 + if ( is_callable( array( $_cloudflare, 'purgeCacheEverything' ) ) ) {
636 + $_cloudflare->purgeCacheEverything();
637 + }
638 + }
652 639 // Kinsta.
653 640 if ( isset( $GLOBALS['kinsta_cache'] ) && ! empty( $GLOBALS['kinsta_cache']->kinsta_cache_purge ) && is_callable( array( $GLOBALS['kinsta_cache']->kinsta_cache_purge, 'purge_complete_caches' ) ) ) {
654 - $GLOBALS['kinsta_cache']->kinsta_cache_purge->purge_complete_caches(); // @phpstan-ignore-line
641 + $GLOBALS['kinsta_cache']->kinsta_cache_purge->purge_complete_caches(); // @phpstan-ignore method.nonObject
655 642 }
656 643 // LiteSpeed Cache.
657 644 if ( is_callable( array( 'LiteSpeed_Cache_Tags', 'add_purge_tag' ) ) ) {
658 - LiteSpeed_Cache_Tags::add_purge_tag( '*' ); // @phpstan-ignore-line
645 + LiteSpeed_Cache_Tags::add_purge_tag( '*' ); // @phpstan-ignore class.notFound
659 646 }
660 647 // Pagely.
661 648 if ( class_exists( 'PagelyCachePurge' ) ) {
662 649 $_pagely = new PagelyCachePurge();
@@ -663,25 +650,25 @@
663 650 if ( is_callable( array( $_pagely, 'purgeAll' ) ) ) {
664 651 $_pagely->purgeAll();
665 652 }
666 653 }
667 - // Pressidum.
654 + // Pressidium.
668 655 if ( is_callable( array( 'Ninukis_Plugin', 'get_instance' ) ) ) {
669 - $_pressidum = Ninukis_Plugin::get_instance(); // @phpstan-ignore-line
656 + $_pressidum = Ninukis_Plugin::get_instance(); // @phpstan-ignore class.notFound
670 657 if ( is_callable( array( $_pressidum, 'purgeAllCaches' ) ) ) {
671 - $_pressidum->purgeAllCaches(); // @phpstan-ignore-line
658 + $_pressidum->purgeAllCaches(); // @phpstan-ignore method.nonObject
672 659 }
673 660 }
674 661 // Savvii.
675 662 if ( defined( '\Savvii\CacheFlusherPlugin::NAME_DOMAINFLUSH_NOW' ) ) {
676 - $_savvii = new \Savvii\CacheFlusherPlugin(); // @phpstan-ignore-line
663 + $_savvii = new \Savvii\CacheFlusherPlugin(); // @phpstan-ignore class.notFound
677 664 if ( is_callable( array( $_savvii, 'domainflush' ) ) ) {
678 - $_savvii->domainflush(); // @phpstan-ignore-line
665 + $_savvii->domainflush(); // @phpstan-ignore class.notFound
679 666 }
680 667 }
681 668 // WP Fastest Cache.
682 669 if ( isset( $GLOBALS['wp_fastest_cache'] ) && is_callable( array( $GLOBALS['wp_fastest_cache'], 'deleteCache' ) ) ) {
683 - $GLOBALS['wp_fastest_cache']->deleteCache( true ); // @phpstan-ignore-line
670 + $GLOBALS['wp_fastest_cache']->deleteCache( true ); // @phpstan-ignore method.nonObject
684 671 }
685 672 // WP-Optimize.
686 673 if ( function_exists( 'WP_Optimize' ) ) {
687 674 WP_Optimize()->get_page_cache()->purge();
@@ -809,10 +796,10 @@
809 796 'last_modified' => wp_date( 'Y-m-d H:i:s' ),
810 797 'author' => get_current_user_id(),
811 798 'options' => array(
812 799 'last_editor' => get_current_user_id(),
813 - 'table_head' => true,
814 - 'table_foot' => false,
800 + 'table_head' => 1,
801 + 'table_foot' => 0,
815 802 'alternating_row_colors' => true,
816 803 'row_hover' => true,
817 804 'print_name' => false,
818 805 'print_name_position' => 'above',
@@ -830,9 +817,9 @@
830 817 'datatables_scrollx' => false,
831 818 'datatables_custom_commands' => '',
832 819 ),
833 820 'visibility' => array(
834 - 'rows' => array( 1 ), // One visbile row.
821 + 'rows' => array( 1 ), // One visible row.
835 822 'columns' => array( 1 ), // One visible column.
836 823 ),
837 824 );
838 825 /**
@@ -908,9 +895,9 @@
908 895 array_walk_recursive(
909 896 $table['data'],
910 897 static function ( /* string|int|float|bool|null */ &$cell_content, int $col_idx ): void {
911 898 $cell_content = (string) $cell_content;
912 - }
899 + },
913 900 );
914 901 // Table Options.
915 902 if ( isset( $new_table['options'] ) ) { // Options are for example not set for newly added tables.
916 903 // Specials check for certain options.
@@ -925,8 +912,17 @@
925 912 if ( $new_table['options']['datatables_paginate_entries'] < 1 ) {
926 913 $new_table['options']['datatables_paginate_entries'] = 10; // Default value.
927 914 }
928 915 }
916 +
917 + // Backward compatibility: Convert boolean or numeric string "table_head" and "table_foot" options to integer.
918 + if ( isset( $new_table['options']['table_head'] ) ) {
919 + $new_table['options']['table_head'] = absint( $new_table['options']['table_head'] );
920 + }
921 + if ( isset( $new_table['options']['table_foot'] ) ) {
922 + $new_table['options']['table_foot'] = absint( $new_table['options']['table_foot'] );
923 + }
924 +
929 925 // Merge new options.
930 926 $default_table = $this->get_table_template();
931 927 $table['options'] = array_intersect_key( $table['options'], $default_table['options'] );
932 928 $new_table['options'] = array_intersect_key( $new_table['options'], $default_table['options'] );
@@ -940,8 +936,18 @@
940 936 // $table['created'] = wp_date( 'Y-m-d H:i:s' ); // We don't want this, as it would override the original datetime.
941 937 $table['last_modified'] = wp_date( 'Y-m-d H:i:s' );
942 938 $table['options']['last_editor'] = get_current_user_id();
943 939
940 + // Prevent issues if the "Custom Commands" field is not set, e.g. when non-admins have previously edited the table.
941 + if ( ! isset( $table['options']['datatables_custom_commands'] ) ) {
942 + $table['options']['datatables_custom_commands'] = '';
943 + }
944 +
945 + // Convert CSS classes and some DataTables 1.x parameters to the DataTables 2 variants.
946 + if ( '' !== $table['options']['datatables_custom_commands'] ) {
947 + $table['options']['datatables_custom_commands'] = TablePress::convert_datatables_api_data( $table['options']['datatables_custom_commands'] );
948 + }
949 +
944 950 return $table;
945 951 }
946 952
947 953 /**
@@ -954,9 +960,9 @@
954 960 * @return bool True on success, false on error.
955 961 */
956 962 protected function _add_table_options( int $post_id, array $options ): bool {
957 963 $options = wp_json_encode( $options, TABLEPRESS_JSON_OPTIONS );
958 - return $this->model_post->add_meta_field( $post_id, $this->table_options_field_name, $options ); // @phpstan-ignore-line
964 + return $this->model_post->add_meta_field( $post_id, $this->table_options_field_name, $options ); // @phpstan-ignore argument.type
959 965 }
960 966
961 967 /**
962 968 * Update the table options of a table (in a post meta field in the table's post).
@@ -968,9 +974,9 @@
968 974 * @return bool True on success, false on error.
969 975 */
970 976 protected function _update_table_options( int $post_id, array $options ): bool {
971 977 $options = wp_json_encode( $options, TABLEPRESS_JSON_OPTIONS );
972 - return $this->model_post->update_meta_field( $post_id, $this->table_options_field_name, $options ); // @phpstan-ignore-line
978 + return $this->model_post->update_meta_field( $post_id, $this->table_options_field_name, $options ); // @phpstan-ignore argument.type
973 979 }
974 980
975 981 /**
976 982 * Get the table options of a table (from a post meta field of the table's post).
@@ -984,9 +990,15 @@
984 990 $options = $this->model_post->get_meta_field( $post_id, $this->table_options_field_name );
985 991 if ( empty( $options ) ) {
986 992 return array();
987 993 }
988 - return (array) json_decode( $options, true );
994 + $options = (array) json_decode( $options, true );
995 +
996 + // Backward compatibility: Convert boolean "table_head" and "table_foot" options to integer.
997 + $options['table_head'] = absint( $options['table_head'] );
998 + $options['table_foot'] = absint( $options['table_foot'] );
999 +
1000 + return $options;
989 1001 }
990 1002
991 1003 /**
992 1004 * Save the table visibility of a table (in a post meta field of the table's post).
@@ -992,15 +1004,15 @@
992 1004 * Save the table visibility of a table (in a post meta field of the table's post).
993 1005 *
994 1006 * @since 1.0.0
995 1007 *
996 - * @param int $post_id Post ID.
997 - * @param array<string, array{rows: int[], columns: int[]}> $visibility Table visibility.
1008 + * @param int $post_id Post ID.
1009 + * @param array{rows: int[], columns: int[]} $visibility Table visibility.
998 1010 * @return bool True on success, false on error.
999 1011 */
1000 1012 protected function _add_table_visibility( int $post_id, array $visibility ): bool {
1001 1013 $visibility = wp_json_encode( $visibility, TABLEPRESS_JSON_OPTIONS );
1002 - return $this->model_post->add_meta_field( $post_id, $this->table_visibility_field_name, $visibility ); // @phpstan-ignore-line
1014 + return $this->model_post->add_meta_field( $post_id, $this->table_visibility_field_name, $visibility ); // @phpstan-ignore argument.type
1003 1015 }
1004 1016
1005 1017 /**
1006 1018 * Update the table visibility of a table (in a post meta field in the table's post).
@@ -1006,15 +1018,15 @@
1006 1018 * Update the table visibility of a table (in a post meta field in the table's post).
1007 1019 *
1008 1020 * @since 1.0.0
1009 1021 *
1010 - * @param int $post_id Post ID.
1011 - * @param array<string, array{rows: int[], columns: int[]}> $visibility Table visibility.
1022 + * @param int $post_id Post ID.
1023 + * @param array{rows: int[], columns: int[]} $visibility Table visibility.
1012 1024 * @return bool True on success, false on error.
1013 1025 */
1014 1026 protected function _update_table_visibility( int $post_id, array $visibility ): bool {
1015 1027 $visibility = wp_json_encode( $visibility, TABLEPRESS_JSON_OPTIONS );
1016 - return $this->model_post->update_meta_field( $post_id, $this->table_visibility_field_name, $visibility ); // @phpstan-ignore-line
1028 + return $this->model_post->update_meta_field( $post_id, $this->table_visibility_field_name, $visibility ); // @phpstan-ignore argument.type
1017 1029 }
1018 1030
1019 1031 /**
1020 1032 * Get the table visibility of a table (from a post meta field of the table's post).
@@ -1021,14 +1033,17 @@
1021 1033 *
1022 1034 * @since 1.0.0
1023 1035 *
1024 1036 * @param int $post_id Post ID.
1025 - * @return array<string, array{rows: int[], columns: int[]}> Table visibility on success, empty array on error.
1037 + * @return array{rows: int[], columns: int[]} Table visibility on success, empty array on error.
1026 1038 */
1027 1039 protected function _get_table_visibility( int $post_id ): array {
1028 1040 $visibility = $this->model_post->get_meta_field( $post_id, $this->table_visibility_field_name );
1029 1041 if ( empty( $visibility ) ) {
1030 - return array();
1042 + return array(
1043 + 'rows' => array(),
1044 + 'columns' => array(),
1045 + );
1031 1046 }
1032 1047 return json_decode( $visibility, true );
1033 1048 }
1034 1049
@@ -1054,10 +1069,23 @@
1054 1069 // Get default Table with default Table Options.
1055 1070 $default_table = $this->get_table_template();
1056 1071
1057 1072 // Go through all tables (this loop now uses the WP cache).
1058 - foreach ( $table_post as $table_id => $post_id ) {
1073 + foreach ( $table_post as $post_id ) {
1059 1074 $table_options = $this->_get_table_options( $post_id );
1075 +
1076 + /**
1077 + * Filters the Table Options before they are merged with the default Table Options.
1078 + *
1079 + * @since 3.0.0
1080 + *
1081 + * @param array<string, mixed> $table_options Table Options.
1082 + * @param array<string, mixed> $default_table_options Default Table Options.
1083 + * @param bool $remove_old_options Whether old table options should be removed from the database.
1084 + * @param int $post_id Post ID of the table.
1085 + */
1086 + $table_options = apply_filters( 'tablepress_table_options_before_merge', $table_options, $default_table['options'], $remove_old_options, $post_id );
1087 +
1060 1088 if ( $remove_old_options ) {
1061 1089 // Remove old (i.e. no longer existing) Table Options.
1062 1090 $table_options = array_intersect_key( $table_options, $default_table['options'] );
1063 1091 }
@@ -1067,8 +1095,48 @@
1067 1095 }
1068 1096 }
1069 1097
1070 1098 /**
1099 + * Updates all tables' "Custom Commands" to use DataTables 2 variants instead of old DataTables 1.x CSS classes and parameters
1100 + *
1101 + * @since 3.0.0
1102 + */
1103 + public function update_custom_commands_datatables_tp30(): void {
1104 + $table_post = $this->tables->get( 'table_post' );
1105 + if ( empty( $table_post ) ) {
1106 + return;
1107 + }
1108 +
1109 + // Prime the meta cache with the table options of all tables.
1110 + update_meta_cache( 'post', array_values( $table_post ) );
1111 +
1112 + foreach ( $table_post as $table_id => $post_id ) {
1113 + $table_options = $this->_get_table_options( $post_id );
1114 +
1115 + // Fix tables where the "Custom Commands" entry is missing entirely.
1116 + if ( ! isset( $table_options['datatables_custom_commands'] ) ) {
1117 + $table_options['datatables_custom_commands'] = '';
1118 + $this->_update_table_options( $post_id, $table_options );
1119 + continue;
1120 + }
1121 +
1122 + // Nothing to do if there are no "Custom Commands".
1123 + if ( '' === $table_options['datatables_custom_commands'] ) {
1124 + continue;
1125 + }
1126 + // Run search/replace.
1127 + $old_custom_commands = $table_options['datatables_custom_commands'];
1128 + $table_options['datatables_custom_commands'] = TablePress::convert_datatables_api_data( $table_options['datatables_custom_commands'] );
1129 + // No need to save (which runs a DB query) if nothing was replaced in the "Custom Commands".
1130 + if ( $old_custom_commands === $table_options['datatables_custom_commands'] ) {
1131 + continue;
1132 + }
1133 +
1134 + $this->_update_table_options( $post_id, $table_options );
1135 + }
1136 + }
1137 +
1138 + /**
1071 1139 * Invalidate all table output caches, e.g. after a plugin update.
1072 1140 *
1073 1141 * @since 1.0.0
1074 1142 */
@@ -1078,8 +1146,9 @@
1078 1146 return;
1079 1147 }
1080 1148
1081 1149 foreach ( $table_post as $table_id => $post_id ) {
1150 + $table_id = (string) $table_id; // Ensure that the table ID is a string, as it comes from an array key where numeric strings are converted to integers.
1082 1151 $this->invalidate_table_output_cache( $table_id );
1083 1152 }
1084 1153 }
1085 1154
@@ -1197,18 +1266,23 @@
1197 1266 }
1198 1267
1199 1268 // Pretend that there is a `_tablepress_export_table_id` post meta field with the list of table IDs.
1200 1269 $key = '_tablepress_export_table_id';
1201 - $value = wxr_cdata( implode( ',', $table_ids ) ); // @phpstan-ignore-line
1202 1270
1271 + /**
1272 + * Load WP export functions.
1273 + */
1274 + require_once ABSPATH . 'wp-admin/includes/export.php';
1275 + $value = wxr_cdata( implode( ',', $table_ids ) );
1276 +
1203 1277 // Hijack the filter and print extra XML code for our faked post meta field.
1204 1278 // phpcs:disable WordPress.Security.EscapeOutput.HeredocOutputNotEscaped
1205 1279 echo <<<WXR
1206 - <wp:postmeta>
1207 - <wp:meta_key>{$key}</wp:meta_key>
1208 - <wp:meta_value>{$value}</wp:meta_value>
1209 - </wp:postmeta>\n
1210 -WXR;
1280 + <wp:postmeta>
1281 + <wp:meta_key>{$key}</wp:meta_key>
1282 + <wp:meta_value>{$value}</wp:meta_value>
1283 + </wp:postmeta>\n
1284 + WXR;
1211 1285 // phpcs:enable
1212 1286
1213 1287 return $skip;
1214 1288 }