PluginProbe
AutomatorWP – No-Code Workflow Automation, Integration & Webhooks Plugin, now with AI / 6.0.3
AutomatorWP – No-Code Workflow Automation, Integration & Webhooks Plugin, now with AI v6.0.3
6.0.3 6.0.2 6.0.1 6.0.0 5.8.6 5.8.5 5.8.4 5.8.3 5.8.2 5.8.1 5.8.0 5.7.9.2 5.7.9.1 5.7.8 5.7.9 5.7.6 5.7.7 5.7.5 5.7.4 5.7.3 5.7.2 5.7.1 trunk 5.6.0 5.6.1 All 34 releases
← All changes | libraries/ct/includes/functions.php +1870 -0 5.7.8 → 6.0.3 View file →
@@ -1,0 +1,1870 @@
1 +<?php
2 +/**
3 + * CT Functions
4 + *
5 + * @since 1.0.0
6 + */
7 +// Exit if accessed directly
8 +defined( 'ABSPATH' ) || exit;
9 +
10 +/**
11 + * Register a custom table
12 + *
13 + * @since 1.0.0
14 + *
15 + * @param string $name
16 + * @param array $args
17 + *
18 + * @return CT_Table
19 + */
20 +function ct_register_table( $name, $args ) {
21 +
22 + global $ct_registered_tables;
23 +
24 + if ( ! is_array( $ct_registered_tables ) ) {
25 + $ct_registered_tables = array();
26 + }
27 +
28 + $name = sanitize_key( $name );
29 +
30 + if( isset( $ct_registered_tables[$name] ) ) {
31 + return $ct_registered_tables[$name];
32 + }
33 +
34 + $ct_table = new CT_Table( $name, $args );
35 +
36 + $ct_registered_tables[$name] = $ct_table;
37 +
38 + return $ct_table;
39 +
40 +}
41 +
42 +/**
43 + * Setup the global table
44 + *
45 + * @since 1.0.0
46 + *
47 + * @param CT_Table|string $object CT_Table object or CT_Table name
48 + *
49 + * @return CT_Table $ct_table
50 + */
51 +function ct_setup_table( $object ) {
52 +
53 + global $ct_registered_tables, $ct_previous_table, $ct_table;
54 +
55 + if( is_object( $ct_table ) ) {
56 + $ct_previous_table = $ct_table;
57 + }
58 +
59 + if( is_object( $object ) ) {
60 + $ct_table = $object;
61 + } else if( gettype( $object ) === 'string' && isset( $ct_registered_tables[$object] ) ) {
62 + $ct_table = $ct_registered_tables[$object];
63 + }
64 +
65 + return $ct_table;
66 +
67 +}
68 +
69 +/**
70 + * Setup the global table
71 + *
72 + * @since 1.0.0
73 + *
74 + * @return CT_Table $ct_table
75 + */
76 +function ct_reset_setup_table() {
77 +
78 + global $ct_registered_tables, $ct_previous_table, $ct_table;
79 +
80 + if( is_object( $ct_previous_table ) ) {
81 + $ct_table = $ct_previous_table;
82 + }
83 +
84 + return $ct_table;
85 +
86 +}
87 +
88 +/**
89 + * get the CT_Table object of given table name
90 + *
91 + * @param CT_Table|string $name CT_Table object or CT_Table name
92 + *
93 + * @since 1.0.0
94 + *
95 + * @return CT_Table
96 + */
97 +function ct_get_table_object( $name ) {
98 +
99 + global $ct_registered_tables;
100 +
101 + if( is_object( $name ) ) {
102 + $ct_table = $name;
103 + } else if( gettype( $name ) === 'string' && isset( $ct_registered_tables[$name] ) ) {
104 + $ct_table = $ct_registered_tables[$name];
105 + }
106 +
107 + return $ct_table;
108 +}
109 +
110 +/**
111 + * Get the table labels
112 + *
113 + * @since 1.0.0
114 + *
115 + * @param CT_Table $ct_table
116 + *
117 + * @return array
118 + */
119 +function ct_get_table_labels( $ct_table ) {
120 +
121 + $default_labels = array(
122 + // translators: %1$s: Singular
123 + 'name' => '%1$s',
124 + // translators: %1$s: Singular
125 + 'singular_name' => '%1$s',
126 + // translators: %2$s: Plural
127 + 'plural_name' => '%2$s',
128 + 'add_new' => __('Add New', 'ct'),
129 + // translators: %1$s: Singular
130 + 'add_new_item' => __('Add New %1$s', 'ct'),
131 + // translators: %1$s: Singular
132 + 'edit_item' => __('Edit %1$s', 'ct'),
133 + // translators: %1$s: Singular
134 + 'new_item' => __('New %1$s', 'ct'),
135 + // translators: %1$s: Singular
136 + 'view_item' => __('View %1$s', 'ct'),
137 + // translators: %2$s: Plural
138 + 'view_items' => __('View %2$s', 'ct'),
139 + // translators: %2$s: Plural
140 + 'search_items' => __( 'Search %2$s', 'ct' ),
141 + // translators: %2$s: Plural
142 + 'not_found' => __( 'No %2$s found.', 'ct' ),
143 + // translators: %2$s: Plural
144 + 'not_found_in_trash' => __( 'No %2$s found in Trash.', 'ct' ),
145 + // translators: %1$s: Singular
146 + 'parent_item_colon' => __( 'Parent %1$s:', 'ct' ),
147 + // translators: %2$s: Plural
148 + 'all_items' => __( 'All %2$s', 'ct' ),
149 + // translators: %1$s: Singular
150 + 'archives' => __( '%1$s Archives', 'ct' ),
151 + // translators: %1$s: Singular
152 + 'attributes' => __( '%1$s Attributes', 'ct' ),
153 + // translators: %1$s: Singular
154 + 'insert_into_item' => __( 'Insert into %1$s', 'ct' ),
155 + // translators: %1$s: Singular
156 + 'uploaded_to_this_item' => __( 'Uploaded to this %1$s', 'ct' ),
157 + 'featured_image' => __( 'Featured Image', 'ct' ),
158 + 'set_featured_image' => __( 'Set featured image', 'ct' ),
159 + 'remove_featured_image' => __( 'Remove featured image', 'ct' ),
160 + 'use_featured_image' => __( 'Use as featured image', 'ct' ),
161 + // translators: %2$s: Plural
162 + 'filter_items_list' => __( 'Filter %2$s list', 'ct' ),
163 + // translators: %2$s: Plural
164 + 'items_list_navigation' => __( '%2$s list navigation', 'ct' ),
165 + // translators: %2$s: Plural
166 + 'items_list' => __( '%2$s list', 'ct' ),
167 +
168 + // -- VIEWS --
169 +
170 + // translators: %2$s: Plural
171 + 'list_page_title' => '%2$s',
172 + // translators: %2$s: Plural
173 + 'list_menu_title' => __( 'All %2$s', 'ct' ),
174 + 'add_page_title' => __('Add New', 'ct'),
175 + 'add_menu_title' => __('Add New', 'ct'),
176 + // translators: %1$s: Singular
177 + 'edit_page_title' => __('Edit %1$s', 'ct'),
178 + // translators: %1$s: Singular
179 + 'edit_menu_title' => __('Edit %1$s', 'ct'),
180 +
181 + // -- MESSAGES --
182 +
183 + // translators: %2$s: Plural
184 + 'add_item_not_allowed' => __( 'Sorry, you are not allowed to add new %2$s.', 'ct' ),
185 + // translators: %1$s: Singular
186 + 'edit_item_not_allowed' => __( 'Sorry, you are not allowed to edit this %1$s.', 'ct' ),
187 + // translators: %2$s: Plural
188 + 'edit_items_not_allowed' => __( 'Sorry, you are not allowed to edit %2$s.', 'ct' ),
189 + // translators: %1$s: Singular
190 + 'edit_item_deleted' => __( 'You attempted to edit a %1$s that doesn&#8217;t exist. Perhaps it was deleted?', 'ct' ),
191 + // translators: %1$s: Singular
192 + 'delete_item_not_allowed' => __( 'Sorry, you are not allowed to delete this %1$s.', 'ct' ),
193 + // translators: %2$s: Plural
194 + 'delete_items_not_allowed' => __( 'Sorry, you are not allowed to delete %2$s.', 'ct' ),
195 + // translators: %s: Singular
196 + 'delete_item_confirm' => __( "Are you sure you want to delete this %s?\\n\\nClick \\'Cancel\\' to go back, \\'OK\\' to confirm the delete.", 'ct' ),
197 +
198 + // -- TEXTS --
199 +
200 + // translators: %s: Singular %d: ID
201 + 'select_item' => __( 'Select %s #%d', 'ct' ),
202 + // translators: %s: Singular %d: ID
203 + 'item_locked' => __( '&#8220;%s #%d&#8221; is locked', 'ct' ),
204 +
205 + // -- ERROR --
206 +
207 + // translators: %1$s: Singular
208 + 'insert_error' => __( 'Could not insert %1$s into the database', 'ct' ),
209 + // translators: %s: Singular
210 + 'update_error' => __( '%s could not be updated.', 'ct' ),
211 + // translators: %s: Singular
212 + 'update_success' => __( '%s updated successfully.', 'ct' ),
213 + // translators: %1$s: Singular
214 + 'delete_error' => __( 'The %1$s cannot be deleted.', 'ct' ),
215 +
216 +
217 + );
218 +
219 + // Use this hook to override default labels in your plugin with the plugin text domain
220 + $default_labels = apply_filters( 'ct_default_table_labels', $default_labels );
221 +
222 + foreach( $default_labels as $label => $pattern ) {
223 + $default_labels[$label] = sprintf( $pattern, $ct_table->singular, $ct_table->plural );
224 + }
225 +
226 + return (object) $default_labels;
227 +
228 +}
229 +
230 +/**
231 + * Get a table label if exists
232 + *
233 + * @since 1.0.0
234 + *
235 + * @param string $name
236 + * @param string $label
237 + *
238 + * @return string
239 + */
240 +function ct_get_table_label( $name, $label ) {
241 +
242 + global $ct_registered_tables;
243 +
244 + if( isset( $ct_registered_tables[$name] ) ) {
245 +
246 + if( property_exists( $ct_registered_tables[$name]->labels, $label ) ) {
247 + return $ct_registered_tables[$name]->labels->$label;
248 + }
249 +
250 + }
251 +
252 + return '';
253 +
254 +}
255 +
256 +/**
257 + * Get the table fields
258 + *
259 + * @since 1.0.0
260 + *
261 + * @param CT_Table $ct_table
262 + *
263 + * @return array
264 + */
265 +function ct_get_table_fields( $ct_table ) {
266 +
267 + return $ct_table->db->schema->fields;
268 +
269 +}
270 +
271 +/**
272 + * Execute CT role creation.
273 + *
274 + * @since 1.0.0
275 + */
276 +function ct_populate_roles() {
277 +
278 + // Add caps for Administrator role
279 + $role = get_role( 'administrator' );
280 +
281 + // Bail if administrator role is not setup
282 + if( ! $role ) {
283 + return;
284 + }
285 +
286 + $args = (object) array(
287 + 'capabilities' => array(),
288 + 'capability_type' => 'item',
289 + 'map_meta_cap' => null
290 + );
291 +
292 + $capabilities = ct_get_table_capabilities( $args );
293 +
294 + foreach( $capabilities as $cap ) {
295 + $role->add_cap( $cap );
296 + }
297 +
298 +}
299 +
300 +/**
301 + * Build an object with all tables capabilities out of a table object
302 + *
303 + * Post type capabilities use the 'capability_type' argument as a base, if the
304 + * capability is not set in the 'capabilities' argument array or if the
305 + * 'capabilities' argument is not supplied.
306 + *
307 + * The capability_type argument can optionally be registered as an array, with
308 + * the first value being singular and the second plural, e.g. array('story, 'stories')
309 + * Otherwise, an 's' will be added to the value for the plural form. After
310 + * registration, capability_type will always be a string of the singular value.
311 + *
312 + * By default, seven keys are accepted as part of the capabilities array:
313 + *
314 + * - edit_item, read_item, and delete_item are meta capabilities, which are then
315 + * generally mapped to corresponding primitive capabilities depending on the
316 + * context, which would be the item being edited/read/deleted and the user or
317 + * role being checked. Thus these capabilities would generally not be granted
318 + * directly to users or roles.
319 + *
320 + * - edit_items - Controls whether objects of this item type can be edited.
321 + * - edit_others_items - Controls whether objects of this type owned by other users
322 + * can be edited. If the item type does not support an author, then this will
323 + * behave like edit_items.
324 + * - publish_items - Controls publishing objects of this item type.
325 + * - read_private_items - Controls whether private objects can be read.
326 + *
327 + * These four primitive capabilities are checked in core in various locations.
328 + * There are also seven other primitive capabilities which are not referenced
329 + * directly in core, except in map_meta_cap(), which takes the three aforementioned
330 + * meta capabilities and translates them into one or more primitive capabilities
331 + * that must then be checked against the user or role, depending on the context.
332 + *
333 + * - read - Controls whether objects of this item type can be read.
334 + * - delete_items - Controls whether objects of this item type can be deleted.
335 + * - delete_private_items - Controls whether private objects can be deleted.
336 + * - delete_published_items - Controls whether published objects can be deleted.
337 + * - delete_others_items - Controls whether objects owned by other users can be
338 + * can be deleted. If the item type does not support an author, then this will
339 + * behave like delete_items.
340 + * - edit_private_items - Controls whether private objects can be edited.
341 + * - edit_published_items - Controls whether published objects can be edited.
342 + *
343 + * These additional capabilities are only used in map_meta_cap(). Thus, they are
344 + * only assigned by default if the item type is registered with the 'map_meta_cap'
345 + * argument set to true (default is false).
346 + *
347 + * @since 1.0.0
348 + *
349 + * @see register_post_type()
350 + * @see map_meta_cap()
351 + *
352 + * @param object $args Post type registration arguments.
353 + * @return object object with all the capabilities as member variables.
354 + */
355 +function ct_get_table_capabilities( $args ) {
356 + if ( ! is_array( $args->capability_type ) ) {
357 + $args->capability_type = array( $args->capability_type, $args->capability_type . 's' );
358 + }
359 +
360 + // Singular base for meta capabilities, plural base for primitive capabilities.
361 + list( $singular_base, $plural_base ) = $args->capability_type;
362 +
363 + $default_capabilities = array(
364 + // Meta capabilities
365 + 'edit_item' => 'edit_' . $singular_base,
366 + 'read_item' => 'read_' . $singular_base,
367 + 'delete_item' => 'delete_' . $singular_base,
368 + 'delete_items' => 'delete_' . $plural_base,
369 + // Primitive capabilities used outside of map_meta_cap():
370 + 'edit_items' => 'edit_' . $plural_base,
371 + 'edit_others_items' => 'edit_others_' . $plural_base,
372 + 'publish_items' => 'publish_' . $plural_base,
373 + 'read_private_items' => 'read_private_' . $plural_base,
374 + );
375 +
376 + // Primitive capabilities used within map_meta_cap():
377 + if ( $args->map_meta_cap ) {
378 + $default_capabilities_for_mapping = array(
379 + 'read' => 'read',
380 + 'delete_items' => 'delete_' . $plural_base,
381 + 'delete_private_items' => 'delete_private_' . $plural_base,
382 + 'delete_published_items' => 'delete_published_' . $plural_base,
383 + 'delete_others_items' => 'delete_others_' . $plural_base,
384 + 'edit_private_items' => 'edit_private_' . $plural_base,
385 + 'edit_published_items' => 'edit_published_' . $plural_base,
386 + 'add_post_meta' => 'add_' . $singular_base . '_meta',
387 + 'edit_post_meta' => 'edit_' . $singular_base . '_meta',
388 + 'delete_post_meta' => 'delete_' . $singular_base . '_meta',
389 + );
390 + $default_capabilities = array_merge( $default_capabilities, $default_capabilities_for_mapping );
391 + }
392 +
393 + $capabilities = array_merge( $default_capabilities, $args->capabilities );
394 +
395 + // Post creation capability simply maps to edit_items by default:
396 + if ( ! isset( $capabilities['create_items'] ) ) {
397 + $capabilities['create_items'] = $capabilities['edit_items'];
398 + }
399 +
400 + // Remember meta capabilities for future reference.
401 + if ( $args->map_meta_cap ) {
402 + ct_meta_capabilities( $capabilities );
403 + }
404 +
405 + // Shortcut to override all capabilities by a specific capability
406 + if( property_exists( $args, 'capability' ) && ! empty( $args->capability ) ) {
407 + foreach ( $capabilities as $key => $value ) {
408 + $capabilities[$key] = $args->capability;
409 + }
410 + }
411 +
412 + return (object) $capabilities;
413 +}
414 +
415 +/**
416 + * Store or return a list of table meta caps for map_meta_cap().
417 + *
418 + * @since 1.0.0
419 + * @access private
420 + *
421 + * @global array $post_type_meta_caps Used to store meta capabilities.
422 + *
423 + * @param array $capabilities Post type meta capabilities.
424 + */
425 +function ct_meta_capabilities( $capabilities = null ) {
426 + global $ct_meta_caps;
427 +
428 + foreach ( $capabilities as $core => $custom ) {
429 + if ( in_array( $core, array( 'read_item', 'delete_item', 'edit_item' ) ) ) {
430 + $ct_meta_caps[ $custom ] = $core;
431 + }
432 + }
433 +}
434 +
435 +/**
436 + * Retrieves object data given a object primary key or object object.
437 + *
438 + * @since 1.0.0
439 + *
440 + * @param int|stdClass|null $object Optional. Object primary key or object object.
441 + * @param string $output Optional. The required return type. One of OBJECT, ARRAY_A, or ARRAY_N, which correspond to
442 + * a WP_Post object, an associative array, or a numeric array, respectively. Default OBJECT.
443 + * @param string $filter Optional. Type of filter to apply. Accepts 'raw', 'edit', 'db',
444 + * or 'display'. Default 'raw'.
445 + * @return stdClass|array|null Type corresponding to $output on success or null on failure.
446 + * When $output is OBJECT, a `stdClass` instance is returned.
447 + */
448 +function ct_get_object( $object = null, $output = OBJECT, $filter = 'raw' ) {
449 +
450 + global $wpdb, $ct_table;
451 +
452 + if ( is_object( $object ) ) {
453 + $primary_key = $ct_table->db->primary_key;
454 +
455 + $object = ct_get_object_instance( $object->$primary_key );
456 + } else {
457 + $object = ct_get_object_instance( $object );
458 + }
459 +
460 + if ( ! $object )
461 + return null;
462 +
463 + if ( $output == ARRAY_A )
464 + return (array) $object;
465 + elseif ( $output == ARRAY_N )
466 + return array_values( (array) $object );
467 +
468 + return $object;
469 +
470 +}
471 +
472 +/**
473 + * Retrieve the object instance.
474 + *
475 + * @since 1.0.0
476 + *
477 + * @global wpdb $wpdb WordPress database abstraction object.
478 + *
479 + * @param int $object_id Object primary key.
480 + * @return stdClass|false Object std object, false otherwise.
481 + */
482 +function ct_get_object_instance( $object_id = null ) {
483 +
484 + global $wpdb, $ct_table;
485 +
486 + $object_id = (int) $object_id;
487 + if ( ! $object_id ) {
488 + return false;
489 + }
490 +
491 + $object = wp_cache_get( $object_id, $ct_table->name );
492 +
493 + if ( ! $object ) {
494 + $object = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$ct_table->db->table_name} WHERE {$ct_table->db->primary_key} = %d LIMIT 1", $object_id ) );
495 +
496 + if ( ! $object )
497 + return false;
498 +
499 + wp_cache_add( $object_id, $object, $ct_table->name );
500 + }
501 +
502 + return $object;
503 +
504 +}
505 +
506 +/**
507 + * Will clean the object in the cache.
508 + *
509 + * Cleaning means delete from the cache of the object.
510 + *
511 + * This function not run if $_wp_suspend_cache_invalidation is not empty. See wp_suspend_cache_invalidation().
512 + *
513 + * @since 1.0.0
514 + *
515 + * @global bool $_wp_suspend_cache_invalidation
516 + *
517 + * @param int|Object $object Post ID or post object to remove from the cache.
518 + */
519 +function ct_clean_object_cache( $object ) {
520 + global $_wp_suspend_cache_invalidation, $ct_table;
521 +
522 + if ( ! empty( $_wp_suspend_cache_invalidation ) )
523 + return;
524 +
525 + $object = ct_get_object( $object );
526 + if ( empty( $object ) )
527 + return;
528 +
529 + $primary_key = $ct_table->db->primary_key;
530 +
531 + wp_cache_delete( $object->$primary_key, $ct_table->name );
532 +
533 + //wp_cache_delete( 'wp_get_archives', 'general' );
534 +
535 + /**
536 + * Fires immediately after the given object's cache is cleaned.
537 + *
538 + * @since 1.0.0
539 + *
540 + * @param int $post_id Post ID.
541 + * @param WP_Post $post Post object.
542 + */
543 + do_action( 'ct_clean_object_cache', $object->$primary_key, $object );
544 +
545 + wp_cache_set( 'last_changed', microtime(), $ct_table->name );
546 +}
547 +
548 +/**
549 + * Update an object with new data.
550 + *
551 + * The date does not have to be set for drafts. You can set the date and it will
552 + * not be overridden.
553 + *
554 + * @since 1.0.0
555 + *
556 + * @param array|object $object_data Optional. Object data. Arrays are expected to be escaped, objects are not. Default array.
557 + * @param bool $wp_error Optional. Allow return of WP_Error on failure. Default false.
558 + * @return int|WP_Error The value 0 or WP_Error on failure. The post ID on success.
559 + */
560 +function ct_update_object( $object_data = array(), $wp_error = false ) {
561 +
562 + global $ct_table;
563 +
564 + if ( is_object( $object_data ) ) {
565 + // Non-escaped post was passed.
566 + $object_data = get_object_vars( $object_data );
567 + $object_data = wp_slash( $object_data );
568 + }
569 +
570 + $primary_key = $ct_table->db->primary_key;
571 +
572 + // First, get all of the original fields.
573 + $object = ct_get_object( $object_data[$primary_key], ARRAY_A );
574 +
575 + if ( is_null( $object ) ) {
576 + if ( $wp_error )
577 + return new WP_Error( 'invalid_object', __( 'Invalid item ID.' ) );
578 + return 0;
579 + }
580 +
581 + // Escape data pulled from DB.
582 + $object = wp_slash( $object );
583 +
584 + // Merge old and new fields with new fields overwriting old ones.
585 + $object_data = array_merge( $object, $object_data );
586 +
587 + return ct_insert_object( $object_data, $wp_error );
588 +
589 +}
590 +
591 +/**
592 + * Insert or update an object.
593 + *
594 + * @since 1.0.0
595 + *
596 + * @global wpdb $wpdb WordPress database abstraction object.
597 + * @global CT_Table $ct_table Custom Tables CT_Table object type.
598 + *
599 + * @param array $object_data An array of elements that make up a post to update or insert.
600 + * @param bool $wp_error Optional. Whether to return a WP_Error on failure. Default false.
601 + *
602 + * @return int|WP_Error The post ID on success. The value 0 or WP_Error on failure.
603 + */
604 +function ct_insert_object( $object_data, $wp_error = false ) {
605 +
606 + global $wpdb, $ct_table;
607 +
608 + if( ! is_a( $ct_table, 'CT_Table' ) ) {
609 + return new WP_Error( 'invalid_ct_table', __( 'Invalid CT Table object.', 'ct' ) );
610 + }
611 +
612 + /**
613 + * Setup the default object for the add new view.
614 + *
615 + * The dynamic portion of the hook, `$this->name`, refers to the object type of the object.
616 + *
617 + * @since 1.0.0
618 + *
619 + * @param array $default_data Default data to be filtered.
620 + */
621 + $defaults = apply_filters( "ct_{$ct_table->name}_default_data", array() );
622 +
623 + $object_data = wp_parse_args( $object_data, $defaults );
624 +
625 + // Are we updating or creating?
626 + $object_id = 0;
627 + $original_object_data = array();
628 + $update = false;
629 + $primary_key = $ct_table->db->primary_key;
630 +
631 + if ( ! empty( $object_data[$primary_key] ) ) {
632 + $update = true;
633 +
634 + // Get the object ID.
635 + $object_id = $object_data[$primary_key];
636 + $object_before = ct_get_object( $object_id );
637 + $original_object_data = ct_get_object( $object_id, ARRAY_A );
638 +
639 + if ( is_null( $original_object_data ) ) {
640 +
641 + if ( $wp_error ) {
642 + return new WP_Error( 'invalid_object', __( 'Invalid item ID.' ) );
643 + }
644 +
645 + return 0;
646 + }
647 + }
648 +
649 + /**
650 + * Filters slashed post data just before it is inserted into the database.
651 + *
652 + * @since 1.0.0
653 + *
654 + * @param array $object_data An array with new object data.
655 + * @param array $original_object_data An array with the original object data.
656 + */
657 + $object_data = apply_filters( 'ct_insert_object_data', $object_data, $original_object_data );
658 +
659 + $object_data = wp_unslash( $object_data );
660 +
661 + $where = array( $primary_key => $object_id );
662 +
663 + if ( $update ) {
664 +
665 + /**
666 + * Fires immediately before an existing object is updated in the database.
667 + *
668 + * @since 1.0.0
669 + *
670 + * @param int $object_id Object ID.
671 + * @param array $data Array of unslashed object data.
672 + */
673 + do_action( 'ct_pre_object_update', $object_id, $object_data );
674 + // Backward Compatibility
675 + do_action( 'pre_object_update', $object_id, $object_data );
676 +
677 + if ( false === $ct_table->db->update( $object_data, $where ) ) {
678 + if ( $wp_error ) {
679 + return new WP_Error('db_update_error', __( 'Could not update object in the database', 'ct' ), $wpdb->last_error);
680 + } else {
681 + return 0;
682 + }
683 + }
684 + } else {
685 +
686 + $import_id = isset( $object_data['import_id'] ) ? $object_data['import_id'] : 0;
687 +
688 + // If there is a suggested ID, use it if not already present.
689 + if ( ! empty( $import_id ) ) {
690 +
691 + $import_id = (int) $import_id;
692 + if ( ! $wpdb->get_var( $wpdb->prepare("SELECT {$primary_key} FROM {$ct_table->db->table_name} WHERE {$primary_key} = %d", $import_id) ) ) {
693 + $object_data[$primary_key] = $import_id;
694 + }
695 + }
696 +
697 + if ( false === $wpdb->insert( $ct_table->db->table_name, $object_data ) ) {
698 +
699 + if ( $wp_error ) {
700 + return new WP_Error('db_insert_error', ct_get_table_label( $ct_table->name, 'insert_error' ), $wpdb->last_error);
701 + } else {
702 + return 0;
703 + }
704 +
705 + }
706 +
707 + $object_id = (int) $wpdb->insert_id;
708 + }
709 +
710 + // If isset meta_input and object supports meta, then add meta data
711 + if ( ! empty( $object_data['meta_input'] ) && $ct_table->meta ) {
712 + foreach ( $object_data['meta_input'] as $field => $value ) {
713 + ct_update_object_meta( $object_id, $field, $value );
714 + }
715 + }
716 +
717 + ct_clean_object_cache( $object_id );
718 +
719 + $object = ct_get_object( $object_id );
720 +
721 + if ( $update ) {
722 + /**
723 + * Fires once an existing post has been updated.
724 + *
725 + * @since 1.0.0
726 + *
727 + * @param int $post_ID Post ID.
728 + * @param WP_Post $post Post object.
729 + */
730 + do_action( 'ct_edit_object', $object_id, $object );
731 +
732 + $object_after = ct_get_object( $object_id );
733 +
734 + /**
735 + * Fires once an existing post has been updated.
736 + *
737 + * @since 1.0.0
738 + *
739 + * @param int $object_id Object ID.
740 + * @param WP_Post $object_after Object following the update.
741 + * @param WP_Post $object_before Object before the update.
742 + */
743 + do_action( 'ct_object_updated', $object_id, $object_after, $object_before);
744 + }
745 +
746 + /**
747 + * Fires once a object has been saved.
748 + *
749 + * The dynamic portion of the hook name, `{$ct_table->name}`, refers to the object type.
750 + *
751 + * @since 1.0.0
752 + *
753 + * @param int $object_id Object ID.
754 + * @param stdClass $object Object.
755 + * @param bool $update Whether this is an existing object being updated or not.
756 + */
757 + do_action( "ct_save_object_{$ct_table->name}", $object_id, $object, $update );
758 +
759 + /**
760 + * Fires once a post has been saved.
761 + *
762 + * @since 1.0.0
763 + *
764 + * @param int $object_id Object ID.
765 + * @param stdClass $object Object.
766 + * @param bool $update Whether this is an existing object being updated or not.
767 + */
768 + do_action( 'ct_save_object', $object_id, $object, $update );
769 +
770 + /**
771 + * Fires once a post has been saved.
772 + *
773 + * @since 1.0.0
774 + *
775 + * @param int $object_id Object ID.
776 + * @param stdClass $object Object.
777 + * @param bool $update Whether this is an existing object being updated or not.
778 + */
779 + do_action( 'ct_insert_object', $object_id, $object, $update );
780 +
781 + return $object_id;
782 +}
783 +
784 +/**
785 + * Trash or delete an object.
786 + *
787 + * When the post and page is permanently deleted, everything that is tied to
788 + * it is deleted also. This includes comments, post meta fields, and terms
789 + * associated with the post.
790 + *
791 + * The post or page is moved to trash instead of permanently deleted unless
792 + * trash is disabled, item is already in the trash, or $force_delete is true.
793 + *
794 + * @since 1.0.0
795 + *
796 + * @global wpdb $wpdb WordPress database abstraction object.
797 + * @see wp_delete_attachment()
798 + * @see wp_trash_post()
799 + *
800 + * @param int $object_id Optional. Object ID. Default 0.
801 + * @param bool $force_delete Optional. Whether to bypass trash and force deletion.
802 + * Default false.
803 + * @return WP_Post|false|null Post data on success, false or null on failure.
804 + */
805 +function ct_delete_object( $object_id = 0, $force_delete = false ) {
806 +
807 + global $wpdb, $ct_table;
808 +
809 + if( ! is_a( $ct_table, 'CT_Table' ) ) {
810 + return new WP_Error( 'invalid_ct_table', __( 'Invalid CT Table object.', 'ct' ) );
811 + }
812 +
813 + $ct_object = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$ct_table->db->table_name} WHERE {$ct_table->db->primary_key} = %d", $object_id ) );
814 +
815 + if ( ! $ct_object ) {
816 + return $ct_object;
817 + }
818 +
819 + $ct_object = ct_get_object( $ct_object );
820 +
821 + // TODO: Add support for trash functionality
822 + //if ( ! $force_delete && ( 'post' === $post->post_type || 'page' === $post->post_type ) && 'trash' !== get_post_status( $postid ) && EMPTY_TRASH_DAYS ) {
823 + //return ct_trash_object( $object_id );
824 + //}
825 +
826 + /**
827 + * Filters whether a post deletion should take place.
828 + *
829 + * @since 1.0.0
830 + *
831 + * @param bool $delete Whether to go forward with deletion.
832 + * @param WP_Post $post Post object.
833 + * @param bool $force_delete Whether to bypass the trash.
834 + */
835 + $check = apply_filters( 'ct_pre_delete_object', null, $ct_object, $force_delete );
836 + // Backward Compatibility
837 + $check = apply_filters( 'pre_delete_object', $check, $ct_object, $force_delete );
838 + if ( null !== $check ) {
839 + return $check;
840 + }
841 +
842 + /**
843 + * Fires before a post is deleted, at the start of ct_delete_object().
844 + *
845 + * @since 1.0.0
846 + *
847 + * @see ct_delete_object()
848 + *
849 + * @param int $object_id Object ID.
850 + */
851 + do_action( 'ct_before_delete_object', $object_id );
852 + do_action( 'before_delete_object', $object_id );
853 +
854 + if( $ct_table->meta ) {
855 + ct_delete_object_meta( $object_id, '_wp_trash_meta_status' );
856 + ct_delete_object_meta( $object_id, '_wp_trash_meta_time' );
857 +
858 + $object_meta_ids = $wpdb->get_col( $wpdb->prepare( "SELECT {$ct_table->meta->db->primary_key} FROM {$ct_table->meta->db->table_name} WHERE {$ct_table->db->primary_key} = %d ", $object_id ));
859 +
860 + foreach ( $object_meta_ids as $mid ) {
861 + ct_delete_metadata_by_mid( 'post', $mid );
862 + }
863 + }
864 +
865 + /**
866 + * Fires immediately before a post is deleted from the database.
867 + *
868 + * @since 1.0.0
869 + *
870 + * @param int $object_id Object ID.
871 + */
872 + do_action( 'ct_delete_object', $object_id );
873 + do_action( 'delete_object', $object_id );
874 +
875 + $result = $ct_table->db->delete( $object_id );
876 +
877 + if ( ! $result ) {
878 + return false;
879 + }
880 +
881 + /**
882 + * Fires immediately after a post is deleted from the database.
883 + *
884 + * @since 1.0.0
885 + *
886 + * @param int $object_id Object ID.
887 + */
888 + do_action( 'ct_deleted_object', $object_id );
889 + do_action( 'deleted_object', $object_id );
890 +
891 + ct_clean_object_cache( $ct_object );
892 +
893 + /**
894 + * Fires after a post is deleted, at the conclusion of ct_delete_object().
895 + *
896 + * @since 1.0.0
897 + *
898 + * @see ct_delete_object()
899 + *
900 + * @param int $object_id Object ID.
901 + */
902 + do_action( 'ct_after_delete_object', $object_id );
903 + do_action( 'after_delete_object', $object_id );
904 +
905 + return $ct_object;
906 +}
907 +
908 +//
909 +// Object meta functions
910 +//
911 +
912 +/**
913 + * Add meta data field to an object.
914 + *
915 + * Object meta data is called "Custom Fields" on the Administration Screen.
916 + *
917 + * @since 1.0.0
918 + *
919 + * @param int $object_id Object ID.
920 + * @param string $meta_key Metadata name.
921 + * @param mixed $meta_value Metadata value. Must be serializable if non-scalar.
922 + * @param bool $unique Optional. Whether the same key should not be added. Default false.
923 + * @return int|false Meta ID on success, false on failure.
924 + */
925 +function ct_add_object_meta( $object_id, $meta_key, $meta_value, $unique = false ) {
926 +
927 + global $wpdb, $ct_table;
928 +
929 + // Bail if CT_Table not supports meta data
930 + if( ! $ct_table->meta ) {
931 + return false;
932 + }
933 +
934 + $object_id = absint( $object_id );
935 + if ( ! $object_id ) {
936 + return false;
937 + }
938 +
939 + $primary_key = $ct_table->db->primary_key;
940 + $meta_primary_key = $ct_table->meta->db->primary_key;
941 + $meta_table_name = $ct_table->meta->db->table_name;
942 +
943 + // expected_slashed ($meta_key)
944 + $meta_key = wp_unslash($meta_key);
945 + $meta_value = wp_unslash($meta_value);
946 + $meta_value = sanitize_meta( $meta_key, $meta_value, $ct_table->name );
947 +
948 + /**
949 + * Filters whether to add metadata of a specific type.
950 + *
951 + * The dynamic portion of the hook, `$meta_type`, refers to the meta
952 + * object type (comment, post, or user). Returning a non-null value
953 + * will effectively short-circuit the function.
954 + *
955 + * @since 1.0.0
956 + *
957 + * @param null|bool $check Whether to allow adding metadata for the given type.
958 + * @param int $object_id Object ID.
959 + * @param string $meta_key Meta key.
960 + * @param mixed $meta_value Meta value. Must be serializable if non-scalar.
961 + * @param bool $unique Whether the specified meta key should be unique
962 + * for the object. Optional. Default false.
963 + */
964 + $check = apply_filters( "ct_add_{$ct_table->name}_metadata", null, $object_id, $meta_key, $meta_value, $unique );
965 + // Backward compatibility
966 + $check = apply_filters( "add_{$ct_table->name}_metadata", $check, $object_id, $meta_key, $meta_value, $unique );
967 + if ( null !== $check )
968 + return $check;
969 +
970 + if ( $unique && $wpdb->get_var( $wpdb->prepare(
971 + "SELECT COUNT(*) FROM $meta_table_name WHERE meta_key = %s AND $primary_key = %d",
972 + $meta_key, $object_id ) ) )
973 + return false;
974 +
975 + $_meta_value = $meta_value;
976 + $meta_value = maybe_serialize( $meta_value );
977 +
978 + /**
979 + * Fires immediately before meta of a specific type is added.
980 + *
981 + * The dynamic portion of the hook, `$meta_type`, refers to the meta
982 + * object type (comment, post, or user).
983 + *
984 + * @since 1.0.0
985 + *
986 + * @param int $object_id Object ID.
987 + * @param string $meta_key Meta key.
988 + * @param mixed $meta_value Meta value.
989 + */
990 + do_action( "ct_add_{$ct_table->name}_meta", $object_id, $meta_key, $_meta_value );
991 + // Backward compatibility
992 + do_action( "add_{$ct_table->name}_meta", $object_id, $meta_key, $_meta_value );
993 +
994 + $result = $wpdb->insert( $meta_table_name, array(
995 + $primary_key => $object_id,
996 + 'meta_key' => $meta_key,
997 + 'meta_value' => $meta_value
998 + ) );
999 +
1000 + if ( ! $result )
1001 + return false;
1002 +
1003 + $mid = (int) $wpdb->insert_id;
1004 +
1005 + wp_cache_delete( $object_id, $ct_table->meta->name );
1006 +
1007 + /**
1008 + * Fires immediately after meta of a specific type is added.
1009 + *
1010 + * The dynamic portion of the hook, `$meta_type`, refers to the meta
1011 + * object type (comment, post, or user).
1012 + *
1013 + * @since 1.0.0
1014 + *
1015 + * @param int $mid The meta ID after successful update.
1016 + * @param int $object_id Object ID.
1017 + * @param string $meta_key Meta key.
1018 + * @param mixed $meta_value Meta value.
1019 + */
1020 + do_action( "ct_added_{$ct_table->name}_meta", $mid, $object_id, $meta_key, $_meta_value );
1021 + // Backward compatibility
1022 + do_action( "added_{$ct_table->name}_meta", $mid, $object_id, $meta_key, $_meta_value );
1023 +
1024 + return $mid;
1025 +}
1026 +
1027 +/**
1028 + * Remove metadata matching criteria from a post.
1029 + *
1030 + * You can match based on the key, or key and value. Removing based on key and
1031 + * value, will keep from removing duplicate metadata with the same key. It also
1032 + * allows removing all metadata matching key, if needed.
1033 + *
1034 + * @since 1.0.0
1035 + *
1036 + * @param int $object_id Post ID.
1037 + * @param string $meta_key Metadata name.
1038 + * @param mixed $meta_value Optional. Metadata value. Must be serializable if
1039 + * non-scalar. Default empty.
1040 + * @return bool True on success, false on failure.
1041 + */
1042 +function ct_delete_object_meta( $object_id, $meta_key, $meta_value = '' ) {
1043 +
1044 + global $wpdb, $ct_table;
1045 +
1046 + // Bail if CT_Table not supports meta data
1047 + if( ! $ct_table->meta ) {
1048 + return false;
1049 + }
1050 +
1051 + $object_id = absint( $object_id );
1052 + if ( ! $object_id ) {
1053 + return false;
1054 + }
1055 +
1056 + $primary_key = $ct_table->db->primary_key;
1057 + $meta_primary_key = $ct_table->meta->db->primary_key;
1058 + $meta_table_name = $ct_table->meta->db->table_name;
1059 +
1060 + // expected_slashed ($meta_key)
1061 + $meta_key = wp_unslash($meta_key);
1062 + $meta_value = wp_unslash($meta_value);
1063 + $delete_all = false;
1064 +
1065 + /**
1066 + * Filters whether to delete metadata of a specific type.
1067 + *
1068 + * The dynamic portion of the hook, `$meta_type`, refers to the meta
1069 + * object type (comment, post, or user). Returning a non-null value
1070 + * will effectively short-circuit the function.
1071 + *
1072 + * @since 1.0.0
1073 + *
1074 + * @param null|bool $delete Whether to allow metadata deletion of the given type.
1075 + * @param int $object_id Object ID.
1076 + * @param string $meta_key Meta key.
1077 + * @param mixed $meta_value Meta value. Must be serializable if non-scalar.
1078 + * @param bool $delete_all Whether to delete the matching metadata entries
1079 + * for all objects, ignoring the specified $object_id.
1080 + * Default false.
1081 + */
1082 + $check = apply_filters( "ct_delete_{$ct_table->name}_metadata", null, $object_id, $meta_key, $meta_value, $delete_all );
1083 + // Backward compatibility
1084 + $check = apply_filters( "delete_{$ct_table->name}_metadata", $check, $object_id, $meta_key, $meta_value, $delete_all );
1085 + if ( null !== $check )
1086 + return (bool) $check;
1087 +
1088 + $_meta_value = $meta_value;
1089 + $meta_value = maybe_serialize( $meta_value );
1090 +
1091 + $query = $wpdb->prepare( "SELECT $meta_primary_key FROM $meta_table_name WHERE meta_key = %s", $meta_key );
1092 +
1093 + if ( !$delete_all )
1094 + $query .= $wpdb->prepare(" AND $primary_key = %d", $object_id );
1095 +
1096 + if ( '' !== $meta_value && null !== $meta_value && false !== $meta_value )
1097 + $query .= $wpdb->prepare(" AND meta_value = %s", $meta_value );
1098 +
1099 + $meta_ids = $wpdb->get_col( $query );
1100 + if ( !count( $meta_ids ) )
1101 + return false;
1102 +
1103 + if ( $delete_all ) {
1104 + $value_clause = '';
1105 + if ( '' !== $meta_value && null !== $meta_value && false !== $meta_value ) {
1106 + $value_clause = $wpdb->prepare( " AND meta_value = %s", $meta_value );
1107 + }
1108 +
1109 + $object_ids = $wpdb->get_col( $wpdb->prepare( "SELECT $meta_primary_key FROM $meta_table_name WHERE meta_key = %s $value_clause", $meta_key ) );
1110 + }
1111 +
1112 + /**
1113 + * Fires immediately before deleting metadata of a specific type.
1114 + *
1115 + * The dynamic portion of the hook, `$meta_type`, refers to the meta
1116 + * object type (comment, post, or user).
1117 + *
1118 + * @since 1.0.0
1119 + *
1120 + * @param array $meta_ids An array of metadata entry IDs to delete.
1121 + * @param int $object_id Object ID.
1122 + * @param string $meta_key Meta key.
1123 + * @param mixed $meta_value Meta value.
1124 + */
1125 + do_action( "ct_delete_{$ct_table->name}_meta", $meta_ids, $object_id, $meta_key, $_meta_value );
1126 + // Backward compatibility
1127 + do_action( "delete_{$ct_table->name}_meta", $meta_ids, $object_id, $meta_key, $_meta_value );
1128 +
1129 + $query = "DELETE FROM $meta_table_name WHERE $meta_primary_key IN( " . implode( ',', $meta_ids ) . " )";
1130 +
1131 + $count = $wpdb->query($query);
1132 +
1133 + if ( !$count )
1134 + return false;
1135 +
1136 + if ( $delete_all ) {
1137 + foreach ( (array) $object_ids as $o_id ) {
1138 + wp_cache_delete( $o_id, $ct_table->meta->name );
1139 + }
1140 + } else {
1141 + wp_cache_delete( $object_id, $ct_table->meta->name );
1142 + }
1143 +
1144 + /**
1145 + * Fires immediately after deleting metadata of a specific type.
1146 + *
1147 + * The dynamic portion of the hook name, `$meta_type`, refers to the meta
1148 + * object type (comment, post, or user).
1149 + *
1150 + * @since 1.0.0
1151 + *
1152 + * @param array $meta_ids An array of deleted metadata entry IDs.
1153 + * @param int $object_id Object ID.
1154 + * @param string $meta_key Meta key.
1155 + * @param mixed $meta_value Meta value.
1156 + */
1157 + do_action( "ct_deleted_{$ct_table->name}_meta", $meta_ids, $object_id, $meta_key, $_meta_value );
1158 + // Backward compatibility
1159 + do_action( "deleted_{$ct_table->name}_meta", $meta_ids, $object_id, $meta_key, $_meta_value );
1160 +
1161 + return true;
1162 +}
1163 +
1164 +/**
1165 + * Retrieve object meta field for an object.
1166 + *
1167 + * @since 1.0.0
1168 + *
1169 + * @param int $object_id Post ID.
1170 + * @param string $meta_key Optional. The meta key to retrieve. By default, returns
1171 + * data for all keys. Default empty.
1172 + * @param bool $single Optional. Whether to return a single value. Default false.
1173 + * @return mixed Will be an array if $single is false. Will be value of meta data
1174 + * field if $single is true.
1175 + */
1176 +function ct_get_object_meta( $object_id, $meta_key = '', $single = false ) {
1177 +
1178 + global $wpdb, $ct_table;
1179 +
1180 + // Bail if CT_Table not supports meta data
1181 + if( ! $ct_table->meta ) {
1182 + return false;
1183 + }
1184 +
1185 + $object_id = absint( $object_id );
1186 + if ( ! $object_id ) {
1187 + return false;
1188 + }
1189 +
1190 + /**
1191 + * Filters whether to retrieve metadata of a specific type.
1192 + *
1193 + * The dynamic portion of the hook, `$meta_type`, refers to the meta
1194 + * object type (comment, post, or user). Returning a non-null value
1195 + * will effectively short-circuit the function.
1196 + *
1197 + * @since 1.0.0
1198 + *
1199 + * @param null|array|string $value The value get_metadata() should return - a single metadata value,
1200 + * or an array of values.
1201 + * @param int $object_id Object ID.
1202 + * @param string $meta_key Meta key.
1203 + * @param bool $single Whether to return only the first value of the specified $meta_key.
1204 + */
1205 + $check = apply_filters( "ct_get_{$ct_table->name}_metadata", null, $object_id, $meta_key, $single );
1206 + // Backward compatibility
1207 + $check = apply_filters( "get_{$ct_table->name}_metadata", $check, $object_id, $meta_key, $single );
1208 + if ( null !== $check ) {
1209 + if ( $single && is_array( $check ) )
1210 + return $check[0];
1211 + else
1212 + return $check;
1213 + }
1214 +
1215 + $meta_cache = wp_cache_get( $object_id, $ct_table->meta->name );
1216 +
1217 + if ( !$meta_cache ) {
1218 + $meta_cache = ct_update_meta_cache( $ct_table->meta->name, array( $object_id ) );
1219 + $meta_cache = $meta_cache[$object_id];
1220 + }
1221 +
1222 + if ( ! $meta_key ) {
1223 + return $meta_cache;
1224 + }
1225 +
1226 + if ( isset($meta_cache[$meta_key]) ) {
1227 + if ( $single )
1228 + return maybe_unserialize( $meta_cache[$meta_key][0] );
1229 + else
1230 + return array_map('maybe_unserialize', $meta_cache[$meta_key]);
1231 + }
1232 +
1233 + if ( $single )
1234 + return '';
1235 + else
1236 + return array();
1237 +}
1238 +
1239 +/**
1240 + * Update post meta field based on post ID.
1241 + *
1242 + * Use the $prev_value parameter to differentiate between meta fields with the
1243 + * same key and post ID.
1244 + *
1245 + * If the meta field for the post does not exist, it will be added.
1246 + *
1247 + * @since 1.0.0
1248 + *
1249 + * @param int $object_id Object ID.
1250 + * @param string $meta_key Metadata key.
1251 + * @param mixed $meta_value Metadata value. Must be serializable if non-scalar.
1252 + * @param mixed $prev_value Optional. Previous value to check before removing.
1253 + * Default empty.
1254 + * @return int|bool Meta ID if the key didn't exist, true on successful update,
1255 + * false on failure.
1256 + */
1257 +function ct_update_object_meta( $object_id, $meta_key, $meta_value, $prev_value = '' ) {
1258 +
1259 + global $wpdb, $ct_table;
1260 +
1261 + if( ! is_a( $ct_table, 'CT_Table' ) ) {
1262 + return false;
1263 + }
1264 +
1265 + // Bail if CT_Table not supports meta data
1266 + if( ! $ct_table->meta ) {
1267 + return false;
1268 + }
1269 +
1270 + $object_id = absint( $object_id );
1271 + if ( ! $object_id ) {
1272 + return false;
1273 + }
1274 +
1275 + $primary_key = $ct_table->db->primary_key;
1276 + $meta_primary_key = $ct_table->meta->db->primary_key;
1277 + $meta_table_name = $ct_table->meta->db->table_name;
1278 +
1279 + // Keep original values
1280 + $raw_meta_key = $meta_key;
1281 + $passed_value = $meta_value;
1282 +
1283 + // Sanitize vars
1284 + $meta_key = wp_unslash( $meta_key );
1285 + $meta_value = wp_unslash( $meta_value );
1286 + $meta_value = sanitize_meta( $meta_key, $meta_value, $ct_table->name );
1287 +
1288 + /**
1289 + * Filters whether to update metadata of a specific type.
1290 + *
1291 + * The dynamic portion of the hook, `$ct_table->name`, refers to the meta object type.
1292 + * Returning a non-null value will effectively short-circuit the function.
1293 + *
1294 + * @since 1.0.0
1295 + *
1296 + * @param null|bool $check Whether to allow updating metadata for the given type.
1297 + * @param int $object_id Object ID.
1298 + * @param string $meta_key Meta key.
1299 + * @param mixed $meta_value Meta value. Must be serializable if non-scalar.
1300 + * @param mixed $prev_value Optional. If specified, only update existing
1301 + * metadata entries with the specified value.
1302 + * Otherwise, update all entries.
1303 + */
1304 + $check = apply_filters( "ct_update_{$ct_table->name}_metadata", null, $object_id, $meta_key, $meta_value, $prev_value );
1305 + // backward compatibility
1306 + $check = apply_filters( "update_{$ct_table->name}_metadata", $check, $object_id, $meta_key, $meta_value, $prev_value );
1307 + if ( null !== $check )
1308 + return (bool) $check;
1309 +
1310 + // Compare existing value to new value if no prev value given and the key exists only once.
1311 + if ( empty($prev_value) ) {
1312 + $old_value = ct_get_object_meta( $object_id, $meta_key );
1313 + if ( count( $old_value ) == 1 ) {
1314 + if ( $old_value[0] === $meta_value )
1315 + return false;
1316 + }
1317 + }
1318 +
1319 + $meta_ids = $wpdb->get_col( $wpdb->prepare( "SELECT {$meta_primary_key} FROM $meta_table_name WHERE meta_key = %s AND $primary_key = %d", $meta_key, $object_id ) );
1320 + if ( empty( $meta_ids ) ) {
1321 + return ct_add_object_meta( $object_id, $raw_meta_key, $passed_value );
1322 + }
1323 +
1324 + $_meta_value = $meta_value;
1325 + $meta_value = maybe_serialize( $meta_value );
1326 +
1327 + $data = compact( 'meta_value' );
1328 + $where = array(
1329 + $primary_key => $object_id,
1330 + 'meta_key' => $meta_key
1331 + );
1332 +
1333 + if ( ! empty( $prev_value ) ) {
1334 + $prev_value = maybe_serialize( $prev_value );
1335 + $where['meta_value'] = $prev_value;
1336 + }
1337 +
1338 + foreach ( $meta_ids as $meta_id ) {
1339 + /**
1340 + * Fires immediately before updating metadata of a specific type.
1341 + *
1342 + * The dynamic portion of the hook, `$meta_type`, refers to the meta
1343 + * object type (comment, post, or user).
1344 + *
1345 + * @since 1.0.0
1346 + *
1347 + * @param int $meta_id ID of the metadata entry to update.
1348 + * @param int $object_id Object ID.
1349 + * @param string $meta_key Meta key.
1350 + * @param mixed $meta_value Meta value.
1351 + */
1352 + do_action( "ct_update_{$ct_table->name}_meta", $meta_id, $object_id, $meta_key, $_meta_value );
1353 + // Backward compatibility
1354 + do_action( "update_{$ct_table->name}_meta", $meta_id, $object_id, $meta_key, $_meta_value );
1355 + }
1356 +
1357 + $result = $ct_table->meta->db->update( $data, $where );
1358 +
1359 + if ( ! $result )
1360 + return false;
1361 +
1362 + wp_cache_delete( $object_id, $ct_table->meta->name );
1363 +
1364 + foreach ( $meta_ids as $meta_id ) {
1365 + /**
1366 + * Fires immediately after updating metadata of a specific type.
1367 + *
1368 + * The dynamic portion of the hook, `$meta_type`, refers to the meta
1369 + * object type (comment, post, or user).
1370 + *
1371 + * @since 1.0.0
1372 + *
1373 + * @param int $meta_id ID of updated metadata entry.
1374 + * @param int $object_id Object ID.
1375 + * @param string $meta_key Meta key.
1376 + * @param mixed $meta_value Meta value.
1377 + */
1378 + do_action( "ct_updated_{$ct_table->name}_meta", $meta_id, $object_id, $meta_key, $_meta_value );
1379 + // Backward compatibility
1380 + do_action( "updated_{$ct_table->name}_meta", $meta_id, $object_id, $meta_key, $_meta_value );
1381 + }
1382 +
1383 + return true;
1384 +}
1385 +
1386 +/**
1387 + * Update the metadata cache for the specified objects.
1388 + *
1389 + * @since 1.0.0
1390 + *
1391 + * @global wpdb $wpdb WordPress database abstraction object.
1392 + *
1393 + * @param string $meta_type Type of object metadata is for (e.g., comment, post, or user)
1394 + * @param int|array $object_ids Array or comma delimited list of object IDs to update cache for
1395 + * @return array|false Metadata cache for the specified objects, or false on failure.
1396 + */
1397 +function ct_update_meta_cache( $meta_type, $object_ids) {
1398 +
1399 + global $wpdb, $ct_table;
1400 +
1401 + if ( ! $meta_type || ! $object_ids ) {
1402 + return false;
1403 + }
1404 +
1405 + // Bail if CT_Table not supports meta data
1406 + if( ! $ct_table->meta ) {
1407 + return false;
1408 + }
1409 +
1410 + // Setup vars
1411 + $primary_key = $ct_table->db->primary_key;
1412 + $meta_primary_key = $ct_table->meta->db->primary_key;
1413 + $meta_table_name = $ct_table->meta->db->table_name;
1414 +
1415 + if ( !is_array($object_ids) ) {
1416 + $object_ids = preg_replace('|[^0-9,]|', '', $object_ids);
1417 + $object_ids = explode(',', $object_ids);
1418 + }
1419 +
1420 + $object_ids = array_map('intval', $object_ids);
1421 +
1422 + $cache_key = $meta_table_name;
1423 + $ids = array();
1424 + $cache = array();
1425 + foreach ( $object_ids as $id ) {
1426 + $cached_object = wp_cache_get( $id, $cache_key );
1427 + if ( false === $cached_object )
1428 + $ids[] = $id;
1429 + else
1430 + $cache[$id] = $cached_object;
1431 + }
1432 +
1433 + if ( empty( $ids ) )
1434 + return $cache;
1435 +
1436 + // Get meta info
1437 + $id_list = join( ',', $ids );
1438 +
1439 + $meta_list = $wpdb->get_results( "SELECT {$primary_key}, meta_key, meta_value FROM {$meta_table_name} WHERE {$primary_key} IN ($id_list) ORDER BY {$meta_primary_key} ASC", ARRAY_A );
1440 +
1441 + if ( !empty($meta_list) ) {
1442 + foreach ( $meta_list as $metarow) {
1443 + $mpid = intval($metarow[$primary_key]);
1444 + $mkey = $metarow['meta_key'];
1445 + $mval = $metarow['meta_value'];
1446 +
1447 + // Force subkeys to be array type:
1448 + if ( !isset($cache[$mpid]) || !is_array($cache[$mpid]) )
1449 + $cache[$mpid] = array();
1450 + if ( !isset($cache[$mpid][$mkey]) || !is_array($cache[$mpid][$mkey]) )
1451 + $cache[$mpid][$mkey] = array();
1452 +
1453 + // Add a value to the current pid/key:
1454 + $cache[$mpid][$mkey][] = $mval;
1455 + }
1456 + }
1457 +
1458 + foreach ( $ids as $id ) {
1459 + if ( ! isset($cache[$id]) )
1460 + $cache[$id] = array();
1461 + wp_cache_add( $id, $cache[$id], $cache_key );
1462 + }
1463 +
1464 + return $cache;
1465 +}
1466 +
1467 +/**
1468 + * Get meta data by meta ID
1469 + *
1470 + * @since 3.3.0
1471 + *
1472 + * @global wpdb $wpdb WordPress database abstraction object.
1473 + *
1474 + * @param string $meta_type Type of object metadata is for (e.g., comment, post, term, or user).
1475 + * @param int $meta_id ID for a specific meta row
1476 + * @return object|false Meta object or false.
1477 + */
1478 +function ct_get_metadata_by_mid( $meta_type, $meta_id ) {
1479 +
1480 + global $wpdb, $ct_table;
1481 +
1482 + if ( ! $meta_type || ! is_numeric( $meta_id ) || floor( $meta_id ) != $meta_id ) {
1483 + return false;
1484 + }
1485 +
1486 + $meta_id = intval( $meta_id );
1487 +
1488 + if ( $meta_id <= 0 ) {
1489 + return false;
1490 + }
1491 +
1492 + // Bail if CT_Table not supports meta data
1493 + if( ! $ct_table->meta ) {
1494 + return false;
1495 + }
1496 +
1497 + // Setup vars
1498 + $meta_primary_key = $ct_table->meta->db->primary_key;
1499 + $meta_table_name = $ct_table->meta->db->table_name;
1500 +
1501 + $meta = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $meta_table_name WHERE $meta_primary_key = %d", $meta_id ) );
1502 +
1503 + if ( empty( $meta ) )
1504 + return false;
1505 +
1506 + if ( isset( $meta->meta_value ) )
1507 + $meta->meta_value = maybe_unserialize( $meta->meta_value );
1508 +
1509 + return $meta;
1510 +
1511 +}
1512 +
1513 +/**
1514 + * Delete meta data by meta ID
1515 + *
1516 + * @since 1.0.0
1517 + *
1518 + * @global wpdb $wpdb WordPress database abstraction object.
1519 + *
1520 + * @param string $meta_type Type of object metadata is for (e.g., comment, post, term, or user).
1521 + * @param int $meta_id ID for a specific meta row
1522 + * @return bool True on successful delete, false on failure.
1523 + */
1524 +function ct_delete_metadata_by_mid( $meta_type, $meta_id ) {
1525 +
1526 + global $wpdb, $ct_table;
1527 +
1528 + // Make sure everything is valid.
1529 + if ( ! $meta_type || ! is_numeric( $meta_id ) || floor( $meta_id ) != $meta_id ) {
1530 + return false;
1531 + }
1532 +
1533 + $meta_id = intval( $meta_id );
1534 +
1535 + if ( $meta_id <= 0 ) {
1536 + return false;
1537 + }
1538 +
1539 + // Bail if CT_Table not supports meta data
1540 + if( ! $ct_table->meta ) {
1541 + return false;
1542 + }
1543 +
1544 + // Setup vars
1545 + $meta_primary_key = $ct_table->meta->db->primary_key;
1546 + $meta_table_name = $ct_table->meta->db->table_name;
1547 +
1548 + // Fetch the meta and go on if it's found.
1549 + if ( $meta = ct_get_metadata_by_mid( $meta_type, $meta_id ) ) {
1550 + $object_id = $meta->{$meta_primary_key};
1551 +
1552 + /** This action is documented in wp-includes/meta.php */
1553 + do_action( "ct_delete_{$meta_type}_meta", (array) $meta_id, $object_id, $meta->meta_key, $meta->meta_value );
1554 + // Backward compatibility
1555 + do_action( "delete_{$meta_type}_meta", (array) $meta_id, $object_id, $meta->meta_key, $meta->meta_value );
1556 +
1557 + // Run the query, will return true if deleted, false otherwise
1558 + $result = (bool) $wpdb->delete( $meta_table_name, array( $meta_primary_key => $meta_id ) );
1559 +
1560 + // Clear the caches.
1561 + wp_cache_delete($object_id, $meta_type . '_meta');
1562 +
1563 + /** This action is documented in wp-includes/meta.php */
1564 + do_action( "ct_deleted_{$meta_type}_meta", (array) $meta_id, $object_id, $meta->meta_key, $meta->meta_value );
1565 + // Backward compatibility
1566 + do_action( "deleted_{$meta_type}_meta", (array) $meta_id, $object_id, $meta->meta_key, $meta->meta_value );
1567 +
1568 + return $result;
1569 +
1570 + }
1571 +
1572 + // Meta id was not found.
1573 + return false;
1574 +}
1575 +
1576 +/**
1577 + * Get meta data for the given object ID.
1578 + *
1579 + * @since 1.0.0
1580 + *
1581 + * @global wpdb $wpdb WordPress database abstraction object.
1582 + *
1583 + * @param int $object_id
1584 + * @return mixed
1585 + */
1586 +function ct_has_meta( $object_id ) {
1587 +
1588 + global $wpdb, $ct_table;
1589 +
1590 + $primary_key = $ct_table->db->primary_key;
1591 + $meta_primary_key = $ct_table->meta->db->primary_key;
1592 + $meta_table_name = $ct_table->meta->db->table_name;
1593 +
1594 + return $wpdb->get_results( $wpdb->prepare(
1595 + "SELECT meta_key, meta_value, meta_id, {$primary_key}
1596 + FROM {$meta_table_name} WHERE {$primary_key} = %d
1597 + ORDER BY meta_key,meta_id",
1598 + $object_id ), ARRAY_A );
1599 +}
1600 +
1601 +/**
1602 + * Prints the form in the Custom Fields meta box.
1603 + *
1604 + * @since 1.0.0
1605 + *
1606 + * @global wpdb $wpdb WordPress database abstraction object.
1607 + *
1608 + * @param stdClass $object Optional. The post being edited.
1609 + */
1610 +function ct_meta_form( $object = null ) {
1611 +
1612 + global $wpdb, $ct_table;
1613 +
1614 + $primary_key = $ct_table->db->primary_key;
1615 + $object = ct_get_object( $object );
1616 +
1617 + /**
1618 + * Filters values for the meta key dropdown in the Custom Fields meta box.
1619 + *
1620 + * Returning a non-null value will effectively short-circuit and avoid a
1621 + * potentially expensive query against postmeta.
1622 + *
1623 + * @since 1.0.0
1624 + *
1625 + * @param array|null $keys Pre-defined meta keys to be used in place of a postmeta query. Default null.
1626 + * @param stdClass $object The current object.
1627 + */
1628 + $keys = apply_filters( "{$ct_table->name}_meta_form_keys", null, $object );
1629 +
1630 + if ( null === $keys ) {
1631 + /**
1632 + * Filters the number of custom fields to retrieve for the drop-down
1633 + * in the Custom Fields meta box.
1634 + *
1635 + * @since 1.0.0
1636 + *
1637 + * @param int $limit Number of custom fields to retrieve. Default 30.
1638 + */
1639 + $limit = apply_filters( "{$ct_table->name}_meta_form_limit", 30 );
1640 + $sql = "SELECT DISTINCT meta_key
1641 + FROM {$ct_table->meta->db->table_name}
1642 + WHERE meta_key NOT BETWEEN '_' AND '_z'
1643 + HAVING meta_key NOT LIKE %s
1644 + ORDER BY meta_key
1645 + LIMIT %d";
1646 + $keys = $wpdb->get_col( $wpdb->prepare( $sql, $wpdb->esc_like( '_' ) . '%', $limit ) );
1647 + }
1648 +
1649 + if ( $keys ) {
1650 + natcasesort( $keys );
1651 + $meta_key_input_id = 'metakeyselect';
1652 + } else {
1653 + $meta_key_input_id = 'metakeyinput';
1654 + }
1655 + ?>
1656 + <p><strong><?php esc_html_e( 'Add New Custom Field:' ) ?></strong></p>
1657 + <table id="newmeta">
1658 + <thead>
1659 + <tr>
1660 + <th class="left"><label for="<?php echo esc_attr( $meta_key_input_id ); ?>"><?php echo esc_html( _x( 'Name', 'meta name' ) ); ?></label></th>
1661 + <th><label for="metavalue"><?php esc_html_e( 'Value' ) ?></label></th>
1662 + </tr>
1663 + </thead>
1664 +
1665 + <tbody>
1666 + <tr>
1667 + <td id="newmetaleft" class="left">
1668 + <?php if ( $keys ) { ?>
1669 + <select id="metakeyselect" name="metakeyselect">
1670 + <option value="#NONE#"><?php esc_html_e( '&mdash; Select &mdash;' ); ?></option>
1671 + <?php
1672 +
1673 + foreach ( $keys as $key ) {
1674 + if ( is_protected_meta( $key, 'post' ) || ! current_user_can( $ct_table->cap->add_post_meta, $object->$primary_key, $key ) )
1675 + continue;
1676 + echo "\n<option value='" . esc_attr($key) . "'>" . esc_html($key) . "</option>";
1677 + }
1678 + ?>
1679 + </select>
1680 + <input class="hide-if-js" type="text" id="metakeyinput" name="metakeyinput" value="" />
1681 + <a href="#postcustomstuff" class="hide-if-no-js" onclick="jQuery('#metakeyinput, #metakeyselect, #enternew, #cancelnew').toggle();return false;">
1682 + <span id="enternew"><?php esc_html_e('Enter new'); ?></span>
1683 + <span id="cancelnew" class="hidden"><?php esc_html_e('Cancel'); ?></span></a>
1684 + <?php } else { ?>
1685 + <input type="text" id="metakeyinput" name="metakeyinput" value="" />
1686 + <?php } ?>
1687 + </td>
1688 + <td><textarea id="metavalue" name="metavalue" rows="2" cols="25"></textarea></td>
1689 + </tr>
1690 +
1691 + <tr><td colspan="2">
1692 + <div class="submit">
1693 + <?php submit_button( __( 'Add Custom Field' ), '', 'addmeta', false, array( 'id' => 'newmeta-submit', 'data-wp-lists' => 'add:the-list:newmeta' ) ); ?>
1694 + </div>
1695 + <?php wp_nonce_field( 'add-meta', '_ajax_nonce-add-meta', false ); ?>
1696 + </td></tr>
1697 + </tbody>
1698 + </table>
1699 + <?php
1700 +
1701 +}
1702 +
1703 +// ---------------------------------------
1704 +// Helpers
1705 +// ---------------------------------------
1706 +
1707 +/**
1708 + * Helper function to get the list link of an given object type name
1709 + *
1710 + * @param string $name
1711 + * @return string $url
1712 + */
1713 +function ct_get_list_link( $name ) {
1714 + global $ct_registered_tables;
1715 +
1716 + // Check if table exists
1717 + if( ! isset( $ct_registered_tables[$name] ) ) {
1718 + return '';
1719 + }
1720 +
1721 + // Check if table has list view
1722 + if( ! isset( $ct_registered_tables[$name]->views->list ) ) {
1723 + return '';
1724 + }
1725 +
1726 + return $ct_registered_tables[$name]->views->list->get_link();
1727 +}
1728 +
1729 +/**
1730 + * Helper function to get the edit link of an given object type name and object id
1731 + *
1732 + * @param string $name
1733 + * @param int $object_id
1734 + *
1735 + * @return string
1736 + */
1737 +function ct_get_edit_link( $name, $object_id = 0 ) {
1738 + global $ct_registered_tables;
1739 +
1740 + // Check if table exists
1741 + if( ! isset( $ct_registered_tables[$name] ) || $object_id === 0 ) {
1742 + return '';
1743 + }
1744 +
1745 + // Check if table has edit view
1746 + if( ! isset( $ct_registered_tables[$name]->views->edit ) ) {
1747 + return '';
1748 + }
1749 +
1750 + // Check if user has edit permissions
1751 + if( ! current_user_can( $ct_registered_tables[$name]->cap->edit_item, $object_id ) ) {
1752 + return '';
1753 + }
1754 +
1755 + $primary_key = $ct_registered_tables[$name]->db->primary_key;
1756 +
1757 + // Edit link + object ID
1758 + return add_query_arg( array( $primary_key => $object_id ), $ct_registered_tables[$name]->views->edit->get_link() );
1759 +}
1760 +
1761 +/**
1762 + * Helper function to get the delete link of an given object type name and object id
1763 + *
1764 + * @param string $name
1765 + * @param int $object_id
1766 + *
1767 + * @return string
1768 + */
1769 +function ct_get_delete_link( $name, $object_id = 0 ) {
1770 + global $ct_registered_tables;
1771 +
1772 + // Check if table exists
1773 + if( ! isset( $ct_registered_tables[$name] ) || $object_id === 0 ) {
1774 + return '';
1775 + }
1776 +
1777 + // Check if table has list view
1778 + if( ! isset( $ct_registered_tables[$name]->views->list ) ) {
1779 + return '';
1780 + }
1781 +
1782 + // Check if user has delete permissions
1783 + if( ! current_user_can( $ct_registered_tables[$name]->cap->delete_item, $object_id ) ) {
1784 + return '';
1785 + }
1786 +
1787 + $primary_key = $ct_registered_tables[$name]->db->primary_key;
1788 +
1789 + // List link + object ID + action delete
1790 + $url = $ct_registered_tables[$name]->views->list->get_link();
1791 + $url = add_query_arg( array( $primary_key => $object_id ), $url );
1792 + $url = add_query_arg( array( 'ct-action' => 'delete' ), $url );
1793 + $url = add_query_arg( '_wpnonce', wp_create_nonce( 'ct_delete_' . $object_id ), $url );
1794 +
1795 + return $url;
1796 +}
1797 +
1798 +/**
1799 + * Helper function to get the tables registered in a group of custom tables
1800 + *
1801 + * @param string $group
1802 + *
1803 + * @return array
1804 + */
1805 +function ct_get_tables_in_group( $group ) {
1806 +
1807 + global $ct_tables_groups, $wpdb;
1808 +
1809 + if( ! is_array( $ct_tables_groups ) ) {
1810 + $ct_tables_groups = array();
1811 + }
1812 +
1813 + if( isset( $ct_tables_groups[$group] ) ) {
1814 + return $ct_tables_groups[$group];
1815 + }
1816 +
1817 + $ct_tables_groups[$group] = $wpdb->get_col( $wpdb->prepare(
1818 + "SHOW TABLES LIKE %s",
1819 + "%" . $wpdb->esc_like( $group ) . "%"
1820 + ) );
1821 +
1822 + // Ensure that group tables is an array
1823 + if( ! is_array( $ct_tables_groups[$group] ) ) {
1824 + $ct_tables_groups[$group] = array();
1825 + }
1826 +
1827 + return $ct_tables_groups[$group];
1828 +
1829 +}
1830 +
1831 +/**
1832 + * Add the table to the group of tables
1833 + *
1834 + * @param string $group
1835 + * @param string $table
1836 + */
1837 +function ct_add_table_to_group( $group, $table ) {
1838 +
1839 + global $ct_tables_groups;
1840 +
1841 + if( ! is_array( $ct_tables_groups ) ) {
1842 + $ct_tables_groups = array();
1843 + }
1844 +
1845 + if( ! isset( $ct_tables_groups[$group] ) ) {
1846 + $ct_tables_groups[$group] = array();
1847 + }
1848 +
1849 + $ct_tables_groups[$group][] = $table;
1850 +
1851 +}
1852 +
1853 +/**
1854 + * Reset the tables in a group
1855 + *
1856 + * @param string $group
1857 + */
1858 +function ct_reset_tables_in_group( $group ) {
1859 +
1860 + global $ct_tables_groups;
1861 +
1862 + if( ! is_array( $ct_tables_groups ) ) {
1863 + $ct_tables_groups = array();
1864 + }
1865 +
1866 + if( isset( $ct_tables_groups[$group] ) ) {
1867 + unset( $ct_tables_groups[$group] );
1868 + }
1869 +
1870 +}