PluginProbe
GamiPress – Gamification plugin to reward points, badges & ranks in WordPress, now with AI / 8.0.1
GamiPress – Gamification plugin to reward points, badges & ranks in WordPress, now with AI v8.0.1
8.0.1 8.0.2 8.0.0 7.9.9.7 7.9.9.6 7.9.9.5 7.9.9.4 7.9.9.3 7.9.9.2 7.9.9.1 7.9.9 7.9.8 7.9.7 7.9.6 7.9.5 7.9.4 7.9.3 7.9.2 7.9.1 7.9.0 7.8.9 7.8.8 7.8.7 7.8.6 7.8.5 All 44 releases
gamipress / libraries / ct / includes / functions.php

functions.php in GamiPress – Gamification plugin to reward points, badges & ranks in WordPress, now with AI 8.0.1, at libraries/ct/includes/functions.php

1,870 lines 60.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
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 }