PluginProbe
GamiPress – Gamification plugin to reward points, badges & ranks in WordPress, now with AI / 7.9.8
GamiPress – Gamification plugin to reward points, badges & ranks in WordPress, now with AI v7.9.8
8.0.4 8.0.3 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 All 46 releases
gamipress / libraries / ct / includes / functions.php

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

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