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
tablepress / models / model-post.php

model-post.php in TablePress – Tables in WordPress made easy 3.4, at models/model-post.php

369 lines 11.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Post Model
4 *
5 * @package TablePress
6 * @subpackage Models
7 * @author Tobias Bäthge
8 * @since 1.0.0
9 */
10
11 declare(strict_types=1);
12
13 // Prohibit direct script loading.
14 defined( 'ABSPATH' ) || die( 'No direct script access allowed!' );
15
16 /**
17 * Post Model class
18 *
19 * @package TablePress
20 * @subpackage Models
21 * @author Tobias Bäthge
22 * @since 1.0.0
23 */
24 class TablePress_Post_Model extends TablePress_Model {
25
26 /**
27 * Name of the "Custom Post Type" for the tables.
28 *
29 * @since 1.0.0
30 * @var lowercase-string&non-empty-string
31 */
32 protected string $post_type = 'tablepress_table';
33
34 /**
35 * Inits the model by registering the Custom Post Type.
36 *
37 * @since 1.0.0
38 */
39 public function __construct() {
40 parent::__construct();
41 $this->_register_post_type(); // We are on WP "init" hook already.
42 }
43
44 /**
45 * Registers the Custom Post Type which the tables use.
46 *
47 * @since 1.0.0
48 */
49 protected function _register_post_type(): void {
50 /**
51 * Filters the "Custom Post Type" that TablePress uses for storing tables in the database.
52 *
53 * @since 1.0.0
54 *
55 * @param lowercase-string&non-empty-string $post_type The "Custom Post Type" that TablePress uses.
56 */
57 $this->post_type = apply_filters( 'tablepress_post_type', $this->post_type );
58 $post_type_args = array(
59 'labels' => array(
60 'name' => 'TablePress Tables',
61 ),
62 'public' => false,
63 'show_ui' => false,
64 'query_var' => false,
65 'rewrite' => false,
66 'capability_type' => 'tablepress_table', // This ensures, that WP's regular CPT UI respects our capabilities.
67 'map_meta_cap' => false, // Integrated WP mapping does not fit our needs, therefore use our own in a filter.
68 'supports' => array( 'title', 'editor', 'excerpt', 'revisions' ),
69 'can_export' => true,
70 );
71 /**
72 * Filters the arguments for the registration of the "Custom Post Type" that TablePress uses.
73 *
74 * @since 1.0.0
75 *
76 * @param array<string, mixed> $post_type_args Arguments for the registration of the TablePress "Custom Post Type".
77 */
78 $post_type_args = apply_filters( 'tablepress_post_type_args', $post_type_args );
79 register_post_type( $this->post_type, $post_type_args );
80 }
81
82 /**
83 * Inserts a post with the correct Custom Post Type and default values in the wp_posts table in the database.
84 *
85 * @since 1.0.0
86 *
87 * @param array<string, mixed> $post Post to insert.
88 * @return int|WP_Error Post ID of the inserted post on success, WP_Error on error.
89 */
90 public function insert( array $post ) /* : int|WP_Error */ {
91 $default_post = array(
92 'ID' => false, // false on new insert, but existing post ID on update.
93 'comment_status' => 'closed',
94 'ping_status' => 'closed',
95 'post_category' => false,
96 'post_content' => '',
97 'post_excerpt' => '',
98 'post_parent' => 0,
99 'post_password' => '',
100 'post_status' => 'publish',
101 'post_title' => '',
102 'post_type' => $this->post_type,
103 'tags_input' => '',
104 'to_ping' => '',
105 );
106 $post = array_merge( $default_post, $post );
107 // WP expects everything to be slashed.
108 $post = wp_slash( $post );
109
110 // Remove balanceTags() from sanitize_post(), as it can destroy the JSON when messing with HTML.
111 remove_filter( 'content_save_pre', 'balanceTags', 50 );
112 remove_filter( 'excerpt_save_pre', 'balanceTags', 50 );
113
114 /*
115 * Remove possible KSES filtering, as it can destroy the JSON when messing with HTML.
116 * KSES filtering is done to table cells individually, when saving.
117 */
118 $has_kses = ( false !== has_filter( 'content_save_pre', 'wp_filter_post_kses' ) );
119 if ( $has_kses ) {
120 kses_remove_filters();
121 }
122
123 $post_id = wp_insert_post( $post, true );
124
125 // Restore removed content filters.
126 add_filter( 'content_save_pre', 'balanceTags', 50 );
127 add_filter( 'excerpt_save_pre', 'balanceTags', 50 );
128 if ( $has_kses ) {
129 kses_init_filters();
130 }
131
132 // In rare cases, `wp_insert_post()` returns 0 as the post ID, when an error happens, so it's converted to a WP_Error here.
133 if ( 0 === $post_id ) { // @phpstan-ignore identical.alwaysFalse (False-positive in the PHPStan WordPress stubs.)
134 return new WP_Error( 'post_insert', '' );
135 }
136
137 return $post_id;
138 }
139
140 /**
141 * Updates an existing post with the correct Custom Post Type and default values in the wp_posts table in the database.
142 *
143 * @since 1.0.0
144 *
145 * @param array<string, mixed> $post Post.
146 * @return int|WP_Error Post ID of the updated post on success, WP_Error on error.
147 */
148 public function update( array $post ) /* : int|WP_Error */ {
149 $default_post = array(
150 'ID' => false, // false on new insert, but existing post ID on update.
151 'comment_status' => 'closed',
152 'ping_status' => 'closed',
153 'post_category' => false,
154 'post_content' => '',
155 'post_excerpt' => '',
156 'post_parent' => 0,
157 'post_password' => '',
158 'post_status' => 'publish',
159 'post_title' => '',
160 'post_type' => $this->post_type,
161 'tags_input' => '',
162 'to_ping' => '',
163 );
164 $post = array_merge( $default_post, $post );
165 // WP expects everything to be slashed.
166 $post = wp_slash( $post );
167
168 // Remove balanceTags() from sanitize_post(), as it can destroy the JSON when messing with HTML.
169 remove_filter( 'content_save_pre', 'balanceTags', 50 );
170 remove_filter( 'excerpt_save_pre', 'balanceTags', 50 );
171
172 /*
173 * Remove possible KSES filtering, as it can destroy the JSON when messing with HTML.
174 * KSES filtering is done to table cells individually, when saving.
175 */
176 $has_kses = ( false !== has_filter( 'content_save_pre', 'wp_filter_post_kses' ) );
177 if ( $has_kses ) {
178 kses_remove_filters();
179 }
180
181 $post_id = wp_update_post( $post, true );
182
183 // Restore removed content filters.
184 add_filter( 'content_save_pre', 'balanceTags', 50 );
185 add_filter( 'excerpt_save_pre', 'balanceTags', 50 );
186 if ( $has_kses ) {
187 kses_init_filters();
188 }
189
190 return $post_id;
191 }
192
193 /**
194 * Gets a post from the wp_posts table in the database.
195 *
196 * @since 1.0.0
197 *
198 * @param int $post_id Post ID.
199 * @return WP_Post|false Post on success, false on error.
200 */
201 public function get( int $post_id ) /* : WP_Post|false */ {
202 $post = get_post( $post_id );
203 if ( is_null( $post ) ) {
204 return false;
205 }
206 return $post;
207 }
208
209 /**
210 * Deletes a post (and all revisions) from the wp_posts table in the database.
211 *
212 * @since 1.0.0
213 *
214 * @param int $post_id Post ID.
215 * @return WP_Post|false|null Post data on success, false or null on failure.
216 */
217 public function delete( int $post_id ) /* : WP_Post|false|null */ {
218 return wp_delete_post( $post_id, true ); // true means force delete, although for CPTs this is automatic in this function.
219 }
220
221 /**
222 * Moves a post to the trash (if trash is globally enabled), instead of directly deleting the post.
223 * (yet unused)
224 *
225 * @since 1.0.0
226 *
227 * @param int $post_id Post ID.
228 * @return WP_Post|false|null Post data on success, false or null on failure.
229 */
230 public function trash( int $post_id ) /* : WP_Post|false|null */ {
231 return wp_trash_post( $post_id );
232 }
233
234 /**
235 * Restores a post from the trash.
236 * (yet unused)
237 *
238 * @since 1.0.0
239 *
240 * @param int $post_id Post ID.
241 * @return WP_Post|false|null Post on success, false or null on error.
242 */
243 public function untrash( int $post_id ) /* : WP_Post|false */ {
244 return wp_untrash_post( $post_id );
245 }
246
247 /**
248 * Loads all posts with one query, to prime the cache.
249 *
250 * @since 1.0.0
251 *
252 * @global wpdb $wpdb WordPress database abstraction object.
253 * @see get_post()
254 *
255 * @param int[] $all_post_ids List of Post IDs.
256 * @param bool $update_meta_cache Optional. Whether to update the Post Meta Cache (for table options and visibility).
257 */
258 public function load_posts( array $all_post_ids, bool $update_meta_cache = true ): void {
259 global $wpdb;
260
261 // Split post loading, to save memory.
262 while ( ! empty( $all_post_ids ) ) {
263 $post_ids = array_splice( $all_post_ids, 0, 100 ); // Extract 100 posts at a time.
264 // Don't load posts that are in the cache already.
265 $post_ids = _get_non_cached_ids( $post_ids, 'posts' );
266 if ( ! empty( $post_ids ) ) {
267 $post_ids_list = implode( ',', $post_ids );
268 // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
269 $posts = $wpdb->get_results( "SELECT {$wpdb->posts}.* FROM {$wpdb->posts} WHERE ID IN ({$post_ids_list})" ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.InterpolatedNotPrepared
270 update_post_cache( $posts );
271 if ( $update_meta_cache ) {
272 // Get all post meta data for all table posts, @see get_post_meta().
273 update_meta_cache( 'post', $post_ids );
274 }
275 }
276 }
277 }
278
279 /**
280 * Counts the number of posts with the model's CPT in the wp_posts table in the database.
281 * (currently for debug only)
282 *
283 * @since 1.0.0
284 *
285 * @return int Number of posts.
286 */
287 public function count_posts(): int {
288 return array_sum( (array) wp_count_posts( $this->post_type ) ); // Original return value is object with the counts for each post_status.
289 }
290
291 /**
292 * Adds a post meta field to a post.
293 *
294 * @since 1.0.0
295 *
296 * @param int $post_id ID of the post for which the field shall be added.
297 * @param string $field Name of the post meta field.
298 * @param string $value Value of the post meta field (not slashed).
299 * @return bool True on success, false on error.
300 */
301 public function add_meta_field( int $post_id, string $field, string $value ): bool {
302 $success = add_post_meta( $post_id, wp_slash( $field ), wp_slash( $value ), true ); // WP expects slashed values, `true` means unique.
303 // Make sure that $success is a boolean, as add_post_meta() returns an ID or false.
304 $success = ( false === $success ) ? false : true;
305 return $success;
306 }
307
308 /**
309 * Updatse the value of a post meta field of a post.
310 *
311 * If the field does not yet exist, it is added.
312 *
313 * @since 1.0.0
314 *
315 * @param int $post_id ID of the post for which the field shall be updated.
316 * @param string $field Name of the post meta field.
317 * @param string $value Value of the post meta field (not slashed).
318 * @return bool True on success, false on error.
319 */
320 public function update_meta_field( int $post_id, string $field, string $value ): bool {
321 $prev_value = (string) get_post_meta( $post_id, $field, true );
322 // No need to update, if values are equal (also, update_post_meta() would return false for this).
323 if ( $prev_value === $value ) {
324 return true;
325 }
326
327 return (bool) update_post_meta( $post_id, wp_slash( $field ), wp_slash( $value ), $prev_value ); // WP expects slashed values.
328 }
329
330 /**
331 * Gets the value of a post meta field of a post.
332 *
333 * @since 1.0.0
334 *
335 * @param int $post_id ID of the post for which the field shall be retrieved.
336 * @param string $field Name of the post meta field.
337 * @return string Value of the meta field.
338 */
339 public function get_meta_field( int $post_id, string $field ): string {
340 return get_post_meta( $post_id, $field, true ); // `true` means single value.
341 }
342
343 /**
344 * Deletes a post meta field of a post.
345 * (yet unused)
346 *
347 * @since 1.0.0
348 *
349 * @param int $post_id ID of the post of which the field shall be deleted.
350 * @param string $field Name of the post meta field.
351 * @return bool True on success, false on error.
352 */
353 public function delete_meta_field( int $post_id, string $field ): bool {
354 return delete_post_meta( $post_id, wp_slash( $field ) ); // WP expects a slashed value.
355 }
356
357 /**
358 * Returns the Custom Post Type that TablePress uses.
359 *
360 * @since 1.5.0
361 *
362 * @return string The used Custom Post Type.
363 */
364 public function get_post_type(): string {
365 return $this->post_type;
366 }
367
368 } // class TablePress_Post_Model
369