· 6 years ago · Mar 25, 2019, 05:12 PM
1<?php
2/*
3Script Name: Custom Metaboxes and Fields
4Contributors: WebDevStudios (@webdevstudios / webdevstudios.com)
5 Justin Sternberg (@jtsternberg / dsgnwrks.pro)
6 Jared Atchison (@jaredatch / jaredatchison.com)
7 Bill Erickson (@billerickson / billerickson.net)
8 Andrew Norcross (@norcross / andrewnorcross.com)
9Description: This will create metaboxes with custom fields that will blow your mind.
10Version: 1.2.0
11*/
12
13/**
14 * Released under the GPL license
15 * http://www.opensource.org/licenses/gpl-license.php
16 *
17 * This is an add-on for WordPress
18 * http://wordpress.org/
19 *
20 * **********************************************************************
21 * This program is free software; you can redistribute it and/or modify
22 * it under the terms of the GNU General Public License as published by
23 * the Free Software Foundation; either version 2 of the License, or
24 * (at your option) any later version.
25 *
26 * This program is distributed in the hope that it will be useful,
27 * but WITHOUT ANY WARRANTY; without even the implied warranty of
28 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29 * GNU General Public License for more details.
30 * **********************************************************************
31 */
32
33/************************************************************************
34 You should not edit the code below or things might explode!
35*************************************************************************/
36
37// Autoload helper classes
38spl_autoload_register('cmb_Meta_Box::autoload_helpers');
39
40$meta_boxes = array();
41$meta_boxes = apply_filters( 'cmb_meta_boxes', $meta_boxes );
42foreach ( $meta_boxes as $meta_box ) {
43 $my_box = new cmb_Meta_Box( $meta_box );
44}
45
46define( 'CMB_META_BOX_URL', cmb_Meta_Box::get_meta_box_url() );
47
48/**
49 * Create meta boxes
50 */
51class cmb_Meta_Box {
52
53 /**
54 * Current version number
55 * @var string
56 * @since 1.0.0
57 */
58 const CMB_VERSION = '1.2.0';
59
60 /**
61 * Metabox Config array
62 * @var array
63 * @since 0.9.0
64 */
65 protected $_meta_box;
66
67 /**
68 * Metabox Defaults
69 * @var array
70 * @since 1.0.1
71 */
72 protected static $mb_defaults = array(
73 'id' => '',
74 'title' => '',
75 'type' => '',
76 'pages' => array(), // Post type
77 'context' => 'normal',
78 'priority' => 'high',
79 'show_names' => true, // Show field names on the left
80 'show_on' => array( 'key' => false, 'value' => false ), // Specific post IDs or page templates to display this metabox
81 'cmb_styles' => true, // Include cmb bundled stylesheet
82 'fields' => array(),
83 );
84
85 /**
86 * Metabox Form ID
87 * @var string
88 * @since 0.9.4
89 */
90 protected $form_id = 'post';
91
92 /**
93 * Current field config array
94 * @var array
95 * @since 1.0.0
96 */
97 public static $field = array();
98
99 /**
100 * Object ID for metabox meta retrieving/saving
101 * @var int
102 * @since 1.0.0
103 */
104 protected static $object_id = 0;
105
106 /**
107 * Type of object being saved. (e.g., post, user, or comment)
108 * @var string
109 * @since 1.0.0
110 */
111 protected static $object_type = '';
112
113 /**
114 * Whether scripts/styles have been enqueued yet
115 * @var bool
116 * @since 1.0.0
117 */
118 protected static $is_enqueued = false;
119
120 /**
121 * Whether CMB nonce has been added to the page. (oly add once)
122 * @var bool
123 * @since 1.2.0
124 */
125 protected static $nonce_added = false;
126
127 /**
128 * Type of object specified by the metabox Config
129 * @var string
130 * @since 1.0.0
131 */
132 protected static $mb_object_type = 'post';
133
134 /**
135 * Array of all options from manage-options metaboxes
136 * @var array
137 * @since 1.0.0
138 */
139 protected static $options = array();
140
141 /**
142 * List of fields that are changed/updated on save
143 * @var array
144 * @since 1.1.0
145 */
146 protected static $updated = array();
147
148 /**
149 * Get started
150 */
151 function __construct( $meta_box ) {
152
153 $meta_box = self::set_mb_defaults( $meta_box );
154
155 $allow_frontend = apply_filters( 'cmb_allow_frontend', true, $meta_box );
156
157 if ( ! is_admin() && ! $allow_frontend )
158 return;
159
160 $this->_meta_box = $meta_box;
161
162 self::set_mb_type( $meta_box );
163
164 $types = wp_list_pluck( $meta_box['fields'], 'type' );
165 $upload = in_array( 'file', $types ) || in_array( 'file_list', $types );
166
167 global $pagenow;
168
169 $show_filters = 'cmb_Meta_Box_Show_Filters';
170 foreach ( get_class_methods( $show_filters ) as $filter ) {
171 add_filter( 'cmb_show_on', array( $show_filters, $filter ), 10, 2 );
172 }
173
174 // register our scripts and styles for cmb
175 add_action( 'admin_enqueue_scripts', array( $this, 'register_scripts' ), 8 );
176
177 if ( self::get_object_type() == 'post' ) {
178 add_action( 'admin_menu', array( $this, 'add_metaboxes' ) );
179 add_action( 'add_attachment', array( $this, 'save_post' ) );
180 add_action( 'edit_attachment', array( $this, 'save_post' ) );
181 add_action( 'save_post', array( $this, 'save_post' ), 10, 2 );
182 add_action( 'admin_enqueue_scripts', array( $this, 'do_scripts' ) );
183
184 if ( $upload && in_array( $pagenow, array( 'page.php', 'page-new.php', 'post.php', 'post-new.php' ) ) ) {
185 add_action( 'admin_head', array( $this, 'add_post_enctype' ) );
186 }
187
188 }
189 if ( self::get_object_type() == 'user' ) {
190
191 $priority = 10;
192 if ( isset( $meta_box['priority'] ) ) {
193 if ( is_numeric( $meta_box['priority'] ) )
194 $priority = $meta_box['priority'];
195 elseif ( $meta_box['priority'] == 'high' )
196 $priority = 5;
197 elseif ( $meta_box['priority'] == 'low' )
198 $priority = 20;
199 }
200 add_action( 'show_user_profile', array( $this, 'user_metabox' ), $priority );
201 add_action( 'edit_user_profile', array( $this, 'user_metabox' ), $priority );
202
203 add_action( 'personal_options_update', array( $this, 'save_user' ) );
204 add_action( 'edit_user_profile_update', array( $this, 'save_user' ) );
205 if ( $upload && in_array( $pagenow, array( 'profile.php', 'user-edit.php' ) ) ) {
206 $this->form_id = 'your-profile';
207 add_action( 'admin_head', array( $this, 'add_post_enctype' ) );
208 }
209 }
210
211 }
212
213 /**
214 * Autoloads files with classes when needed
215 * @since 1.0.0
216 * @param string $class_name Name of the class being requested
217 */
218 public static function autoload_helpers( $class_name ) {
219 if ( class_exists( $class_name, false ) )
220 return;
221
222 // for PHP versions < 5.3
223 $dir = dirname( __FILE__ );
224
225 $file = "$dir/helpers/$class_name.php";
226 if ( file_exists( $file ) )
227 @include( $file );
228 }
229
230 /**
231 * Registers scripts and styles for CMB
232 * @since 1.0.0
233 */
234 public function register_scripts() {
235
236 // Should only be run once
237 if ( self::$is_enqueued )
238 return;
239
240 global $wp_version;
241 // Only use minified files if SCRIPT_DEBUG is off
242 $min = defined('SCRIPT_DEBUG') && SCRIPT_DEBUG ? '' : '.min';
243
244 // scripts required for cmb
245 $scripts = array( 'jquery', 'jquery-ui-core', 'cmb-datepicker', /*'media-upload', */'cmb-timepicker' );
246 // styles required for cmb
247 $styles = array();
248
249 // if we're 3.5 or later, user wp-color-picker
250 if ( 3.5 <= $wp_version ) {
251 $scripts[] = 'wp-color-picker';
252 $styles[] = 'wp-color-picker';
253 if ( ! is_admin() ) {
254 // we need to register colorpicker on the front-end
255 wp_register_script( 'iris', admin_url( 'js/iris.min.js' ), array( 'jquery-ui-draggable', 'jquery-ui-slider', 'jquery-touch-punch' ), self::CMB_VERSION );
256 wp_register_script( 'wp-color-picker', admin_url( 'js/color-picker.min.js' ), array( 'iris' ), self::CMB_VERSION );
257 wp_localize_script( 'wp-color-picker', 'wpColorPickerL10n', array(
258 'clear' => __( 'Clear' ),
259 'defaultString' => __( 'Default' ),
260 'pick' => __( 'Select Color' ),
261 'current' => __( 'Current Color' ),
262 ) );
263 }
264 } else {
265 // otherwise use the older 'farbtastic'
266 $scripts[] = 'farbtastic';
267 $styles[] = 'farbtastic';
268 }
269 wp_register_script( 'cmb-datepicker', CMB_META_BOX_URL . 'js/jquery.datePicker.min.js' );
270 wp_register_script( 'cmb-timepicker', CMB_META_BOX_URL . 'js/jquery.timePicker.min.js' );
271 wp_register_script( 'cmb-scripts', CMB_META_BOX_URL .'js/cmb'. $min .'.js', $scripts, self::CMB_VERSION );
272
273 wp_enqueue_media();
274
275 wp_localize_script( 'cmb-scripts', 'cmb_l10', apply_filters( 'cmb_localized_data', array(
276 'ajax_nonce' => wp_create_nonce( 'ajax_nonce' ),
277 'script_debug' => defined('SCRIPT_DEBUG') && SCRIPT_DEBUG,
278 'new_admin_style' => version_compare( $wp_version, '3.7', '>' ),
279 'object_type' => self::get_object_type(),
280 'upload_file' => __('Use this file' , 'virtue-toolkit'),
281 'remove_image' => __('Remove Image' , 'virtue-toolkit'),
282 'remove_file' => __('Remove' , 'virtue-toolkit'),
283 'file' => __('File:' , 'virtue-toolkit'),
284 'download' => __('Download' , 'virtue-toolkit'),
285 'ajaxurl' => admin_url( '/admin-ajax.php' ),
286 'up_arrow' => '[ ↑ ] ',
287 'down_arrow' => ' [ ↓ ]',
288 'check_toggle' => __( 'Select / Deselect All', 'virtue-toolkit' ),
289 ) ) );
290
291 wp_register_style( 'cmb-styles', CMB_META_BOX_URL . 'style'. $min .'.css', $styles );
292
293 // Ok, we've enqueued our scripts/styles
294 self::$is_enqueued = true;
295 }
296
297 /**
298 * Enqueues scripts and styles for CMB
299 * @since 1.0.0
300 */
301 public function do_scripts( $hook ) {
302 // only enqueue our scripts/styles on the proper pages
303 if ( $hook == 'post.php' || $hook == 'post-new.php' || $hook == 'page-new.php' || $hook == 'page.php' ) {
304 wp_enqueue_script( 'cmb-scripts' );
305
306 // default is to show cmb styles on post pages
307 if ( $this->_meta_box['cmb_styles'] )
308 wp_enqueue_style( 'cmb-styles' );
309 }
310 }
311
312 /**
313 * Add encoding attribute
314 */
315 public function add_post_enctype() {
316 echo '
317 <script type="text/javascript">
318 jQuery(document).ready(function(){
319 jQuery("#'. $this->form_id .'").attr("enctype", "multipart/form-data");
320 jQuery("#'. $this->form_id .'").attr("encoding", "multipart/form-data");
321 });
322 </script>';
323 }
324
325 /**
326 * Add metaboxes (to 'post' object type)
327 */
328 public function add_metaboxes() {
329
330 foreach ( $this->_meta_box['pages'] as $page ) {
331 if ( apply_filters( 'cmb_show_on', true, $this->_meta_box ) )
332 add_meta_box( $this->_meta_box['id'], $this->_meta_box['title'], array( $this, 'post_metabox' ), $page, $this->_meta_box['context'], $this->_meta_box['priority']) ;
333 }
334 }
335
336 /**
337 * Display metaboxes for a post object
338 * @since 1.0.0
339 */
340 public function post_metabox() {
341 if ( ! $this->_meta_box )
342 return;
343
344 self::show_form( $this->_meta_box, get_the_ID(), 'post' );
345
346 }
347
348 /**
349 * Display metaboxes for a user object
350 * @since 1.0.0
351 */
352 public function user_metabox() {
353 if ( ! $this->_meta_box )
354 return;
355
356 if ( 'user' != self::set_mb_type( $this->_meta_box ) )
357 return;
358
359 if ( ! apply_filters( 'cmb_show_on', true, $this->_meta_box ) )
360 return;
361
362 wp_enqueue_script( 'cmb-scripts' );
363
364 // default is to NOT show cmb styles on user profile page
365 if ( $this->_meta_box['cmb_styles'] != false )
366 wp_enqueue_style( 'cmb-styles' );
367
368 self::show_form( $this->_meta_box );
369
370 }
371
372 /**
373 * Loops through and displays fields
374 * @since 1.0.0
375 * @param array $meta_box Metabox config array
376 * @param int $object_id Object ID
377 * @param string $object_type Type of object being saved. (e.g., post, user, or comment)
378 */
379 public static function show_form( $meta_box, $object_id = 0, $object_type = '' ) {
380 $meta_box = self::set_mb_defaults( $meta_box );
381 // Set/get type
382 $object_type = self::set_object_type( $object_type ? $object_type : self::set_mb_type( $meta_box ) );
383 // Set/get ID
384 $object_id = self::set_object_id( $object_id ? $object_id : self::get_object_id() );
385
386 // Add nonce only once per page.
387 if ( ! self::$nonce_added ) {
388 wp_nonce_field( self::nonce(), 'wp_meta_box_nonce', false, true );
389 self::$nonce_added = true;
390 }
391
392 // Use nonce for verification
393 echo "\n<!-- Begin CMB Fields -->\n";
394 do_action( 'cmb_before_table', $meta_box, $object_id, $object_type );
395 echo '<table class="form-table cmb_metabox">';
396
397 foreach ( $meta_box['fields'] as $field_args ) {
398
399 $field_args['context'] = $meta_box['context'];
400
401 if ( 'group' == $field_args['type'] ) {
402
403 if ( ! isset( $field_args['show_names'] ) ) {
404 $field_args['show_names'] = $meta_box['show_names'];
405 }
406 self::render_group( $field_args );
407 } else {
408
409 $field_args['show_names'] = $meta_box['show_names'];
410 // Render default fields
411 $field = new cmb_Meta_Box_field( $field_args );
412 $field->render_field();
413 }
414 }
415 echo '</table>';
416 do_action( 'cmb_after_table', $meta_box, $object_id, $object_type );
417 echo "\n<!-- End CMB Fields -->\n";
418
419 }
420
421 /**
422 * Render a repeatable group
423 */
424 public static function render_group( $args ) {
425 if ( ! isset( $args['id'], $args['fields'] ) || ! is_array( $args['fields'] ) )
426 return;
427
428 $args['count'] = 0;
429 $field_group = new cmb_Meta_Box_field( $args );
430 $desc = $field_group->args( 'description' );
431 $label = $field_group->args( 'name' );
432 $sortable = $field_group->options( 'sortable' ) ? ' sortable' : '';
433 $group_val = (array) $field_group->value();
434 $nrows = count( $group_val );
435 $remove_disabled = $nrows <= 1 ? 'disabled="disabled" ' : '';
436
437 echo '<tr><td colspan="2"><table id="', $field_group->id(), '_repeat" class="repeatable-group'. $sortable .'" style="width:100%;">';
438 if ( $desc || $label ) {
439 echo '<tr><th>';
440 if ( $label )
441 echo '<h2 class="cmb-group-name">'. $label .'</h2>';
442 if ( $desc )
443 echo '<p class="cmb_metabox_description">'. $desc .'</p>';
444 echo '</th></tr>';
445 }
446
447 if ( ! empty( $group_val ) ) {
448
449 foreach ( $group_val as $iterator => $field_id ) {
450 self::render_group_row( $field_group, $remove_disabled );
451 }
452 } else {
453 self::render_group_row( $field_group, $remove_disabled );
454 }
455
456 echo '<tr><td><p class="add-row"><button data-selector="', $field_group->id() ,'_repeat" data-grouptitle="', $field_group->options( 'group_title' ) ,'" class="add-group-row button">'. $field_group->options( 'add_button' ) .'</button></p></td></tr>';
457
458 echo '</table></td></tr>';
459
460 }
461
462 public static function render_group_row( $field_group, $remove_disabled ) {
463
464 echo '
465 <tr class="repeatable-grouping" data-iterator="'. $field_group->count() .'">
466 <td>
467 <table class="cmb-nested-table" style="width: 100%;">';
468 if ( $field_group->options( 'group_title' ) ) {
469 echo '
470 <tr class="cmb-group-title">
471 <th colspan="2">
472 ', sprintf( '<h4>%1$s</h4>', $field_group->replace_hash( $field_group->options( 'group_title' ) ) ), '
473 <th>
474 </tr>
475 ';
476 }
477 // Render repeatable group fields
478 foreach ( array_values( $field_group->args( 'fields' ) ) as $field_args ) {
479 $field_args['show_names'] = $field_group->args( 'show_names' );
480 $field_args['context'] = $field_group->args( 'context' );
481 $field = new cmb_Meta_Box_field( $field_args, $field_group );
482 $field->render_field();
483 }
484 echo '
485 <tr>
486 <td class="remove-row" colspan="2">
487 <button '. $remove_disabled .'data-selector="'. $field_group->id() .'_repeat" class="button remove-group-row alignright">'. $field_group->options( 'remove_button' ) .'</button>
488 </td>
489 </tr>
490 </table>
491 </td>
492 </tr>
493 ';
494
495 $field_group->args['count']++;
496 }
497
498 /**
499 * Save data from metabox
500 */
501 public function save_post( $post_id, $post = false ) {
502
503 $post_type = $post ? $post->post_type : get_post_type( $post_id );
504
505 // check permissions
506 if (
507 // check nonce
508 ! isset( $_POST['wp_meta_box_nonce'] )
509 || ! wp_verify_nonce( $_POST['wp_meta_box_nonce'], self::nonce() )
510 // check if autosave
511 || defined('DOING_AUTOSAVE' ) && DOING_AUTOSAVE
512 // check user editing permissions
513 || ( 'page' == $_POST['post_type'] && ! current_user_can( 'edit_page', $post_id ) )
514 || ! current_user_can( 'edit_post', $post_id )
515 // get the metabox post_types & compare it to this post_type
516 || ! in_array( $post_type, $this->_meta_box['pages'] )
517 )
518 return $post_id;
519
520 self::save_fields( $this->_meta_box, $post_id, 'post' );
521 }
522
523 /**
524 * Save data from metabox
525 */
526 public function save_user( $user_id ) {
527
528 // check permissions
529 // @todo more hardening?
530 if (
531 // check nonce
532 ! isset( $_POST['wp_meta_box_nonce'] )
533 || ! wp_verify_nonce( $_POST['wp_meta_box_nonce'], self::nonce() )
534 )
535 return $user_id;
536
537 self::save_fields( $this->_meta_box, $user_id, 'user' );
538 }
539
540 /**
541 * Loops through and saves field data
542 * @since 1.0.0
543 * @param array $meta_box Metabox config array
544 * @param int $object_id Object ID
545 * @param string $object_type Type of object being saved. (e.g., post, user, or comment)
546 */
547 public static function save_fields( $meta_box, $object_id, $object_type = '' ) {
548 $meta_box = self::set_mb_defaults( $meta_box );
549
550 $meta_box['show_on'] = empty( $meta_box['show_on'] ) ? array( 'key' => false, 'value' => false ) : $meta_box['show_on'];
551
552 self::set_object_id( $object_id );
553 // Set/get type
554 $object_type = self::set_object_type( $object_type ? $object_type : self::set_mb_type( $meta_box ) );
555
556 if ( ! apply_filters( 'cmb_show_on', true, $meta_box ) )
557 return;
558
559 // save field ids of those that are updated
560 self::$updated = array();
561
562 foreach ( $meta_box['fields'] as $field_args ) {
563
564 if ( 'group' == $field_args['type'] ) {
565 self::save_group( $field_args );
566 } else {
567 // Save default fields
568 $field = new cmb_Meta_Box_field( $field_args );
569 self::save_field( self::sanitize_field( $field ), $field );
570 }
571
572 }
573
574 // If options page, save the updated options
575 if ( $object_type == 'options-page' )
576 self::save_option( $object_id );
577
578 do_action( "cmb_save_{$object_type}_fields", $object_id, $meta_box['id'], self::$updated, $meta_box );
579
580 }
581
582 /**
583 * Save a repeatable group
584 */
585 public static function save_group( $args ) {
586 if ( ! isset( $args['id'], $args['fields'], $_POST[ $args['id'] ] ) || ! is_array( $args['fields'] ) )
587 return;
588
589 $field_group = new cmb_Meta_Box_field( $args );
590 $base_id = $field_group->id();
591 $old = $field_group->get_data();
592 $group_vals = $_POST[ $base_id ];
593 $saved = array();
594 $is_updated = false;
595 $field_group->index = 0;
596
597 // $group_vals[0]['color'] = '333';
598 foreach ( array_values( $field_group->fields() ) as $field_args ) {
599 $field = new cmb_Meta_Box_field( $field_args, $field_group );
600 $sub_id = $field->id( true );
601
602 foreach ( (array) $group_vals as $field_group->index => $post_vals ) {
603
604 // Get value
605 $new_val = isset( $group_vals[ $field_group->index ][ $sub_id ] )
606 ? $group_vals[ $field_group->index ][ $sub_id ]
607 : false;
608
609 // Sanitize
610 $new_val = self::sanitize_field( $field, $new_val, $field_group->index );
611
612 if ( 'file' == $field->type() && is_array( $new_val ) ) {
613 // Add image ID to the array stack
614 $saved[ $field_group->index ][ $new_val['field_id'] ] = $new_val['attach_id'];
615 // Reset var to url string
616 $new_val = $new_val['url'];
617 }
618
619 // Get old value
620 $old_val = is_array( $old ) && isset( $old[ $field_group->index ][ $sub_id ] )
621 ? $old[ $field_group->index ][ $sub_id ]
622 : false;
623
624 $is_updated = ( ! empty( $new_val ) && $new_val != $old_val );
625 $is_removed = ( empty( $new_val ) && ! empty( $old_val ) );
626 // Compare values and add to `$updated` array
627 if ( $is_updated || $is_removed )
628 self::$updated[] = $base_id .'::'. $field_group->index .'::'. $sub_id;
629
630 // Add to `$saved` array
631 $saved[ $field_group->index ][ $sub_id ] = $new_val;
632
633 }
634 $saved[ $field_group->index ] = array_filter( $saved[ $field_group->index ] );
635 }
636 $saved = array_filter( $saved );
637
638 $field_group->update_data( $saved, true );
639 }
640
641 public static function sanitize_field( $field, $new_value = null ) {
642
643 $new_value = null !== $new_value
644 ? $new_value
645 : ( isset( $_POST[ $field->id( true ) ] ) ? $_POST[ $field->id( true ) ] : null );
646
647 if ( $field->args( 'repeatable' ) && is_array( $new_value ) ) {
648 // Remove empties
649 $new_value = array_filter( $new_value );
650 }
651
652 // Check if this metabox field has a registered validation callback, or perform default sanitization
653 return $field->sanitization_cb( $new_value );
654 }
655
656 public static function save_field( $new_value, $field ) {
657 $name = $field->id();
658 $old = $field->get_data();
659
660 // if ( $field->args( 'multiple' ) && ! $field->args( 'repeatable' ) && ! $field->group ) {
661 // $field->remove_data();
662 // if ( ! empty( $new_value ) ) {
663 // foreach ( $new_value as $add_new ) {
664 // self::$updated[] = $name;
665 // $field->update_data( $add_new, $name, false );
666 // }
667 // }
668 // } else
669 if ( ! empty( $new_value ) && $new_value != $old ) {
670 self::$updated[] = $name;
671 return $field->update_data( $new_value );
672 } elseif ( empty( $new_value ) ) {
673 if ( ! empty( $old ) )
674 self::$updated[] = $name;
675 return $field->remove_data();
676 }
677 }
678
679 /**
680 * Get object id from global space if no id is provided
681 * @since 1.0.0
682 * @param integer $object_id Object ID
683 * @return integer $object_id Object ID
684 */
685 public static function get_object_id( $object_id = 0 ) {
686
687 if ( $object_id )
688 return $object_id;
689
690 if ( self::$object_id )
691 return self::$object_id;
692
693 // Try to get our object ID from the global space
694 switch ( self::get_object_type() ) {
695 case 'user':
696 $object_id = isset( $GLOBALS['user_ID'] ) ? $GLOBALS['user_ID'] : $object_id;
697 $object_id = isset( $_REQUEST['user_id'] ) ? $_REQUEST['user_id'] : $object_id;
698 break;
699
700 default:
701 $object_id = isset( $GLOBALS['post']->ID ) ? $GLOBALS['post']->ID : $object_id;
702 $object_id = isset( $_REQUEST['post'] ) ? $_REQUEST['post'] : $object_id;
703 break;
704 }
705
706 // reset to id or 0
707 self::set_object_id( $object_id ? $object_id : 0 );
708
709 return self::$object_id;
710 }
711
712 /**
713 * Explicitly Set object id
714 * @since 1.0.0
715 * @param integer $object_id Object ID
716 * @return integer $object_id Object ID
717 */
718 public static function set_object_id( $object_id ) {
719 return self::$object_id = $object_id;
720 }
721
722 /**
723 * Sets the $object_type based on metabox settings
724 * @since 1.0.0
725 * @param array|string $meta_box Metabox config array or explicit setting
726 * @return string Object type
727 */
728 public static function set_mb_type( $meta_box ) {
729
730 if ( is_string( $meta_box ) ) {
731 self::$mb_object_type = $meta_box;
732 return self::get_mb_type();
733 }
734
735 if ( ! isset( $meta_box['pages'] ) )
736 return self::get_mb_type();
737
738 $type = false;
739 // check if 'pages' is a string
740 if ( self::is_options_page_mb( $meta_box ) )
741 $type = 'options-page';
742 // check if 'pages' is a string
743 elseif ( is_string( $meta_box['pages'] ) )
744 $type = $meta_box['pages'];
745 // if it's an array of one, extract it
746 elseif ( is_array( $meta_box['pages'] ) && count( $meta_box['pages'] === 1 ) )
747 $type = is_string( end( $meta_box['pages'] ) ) ? end( $meta_box['pages'] ) : false;
748
749 if ( !$type )
750 return self::get_mb_type();
751
752 // Get our object type
753 if ( 'user' == $type )
754 self::$mb_object_type = 'user';
755 elseif ( 'comment' == $type )
756 self::$mb_object_type = 'comment';
757 elseif ( 'options-page' == $type )
758 self::$mb_object_type = 'options-page';
759 else
760 self::$mb_object_type = 'post';
761
762 return self::get_mb_type();
763 }
764
765 /**
766 * Determines if metabox is for an options page
767 * @since 1.0.1
768 * @param array $meta_box Metabox config array
769 * @return boolean True/False
770 */
771 public static function is_options_page_mb( $meta_box ) {
772 return ( isset( $meta_box['show_on']['key'] ) && 'options-page' === $meta_box['show_on']['key'] );
773 }
774
775 /**
776 * Returns the object type
777 * @since 1.0.0
778 * @return string Object type
779 */
780 public static function get_object_type() {
781 if ( self::$object_type )
782 return self::$object_type;
783
784 global $pagenow;
785
786 if (
787 $pagenow == 'user-edit.php'
788 || $pagenow == 'profile.php'
789 )
790 self::set_object_type( 'user' );
791
792 elseif (
793 $pagenow == 'edit-comments.php'
794 || $pagenow == 'comment.php'
795 )
796 self::set_object_type( 'comment' );
797 else
798 self::set_object_type( 'post' );
799
800 return self::$object_type;
801 }
802
803 /**
804 * Sets the object type
805 * @since 1.0.0
806 * @return string Object type
807 */
808 public static function set_object_type( $object_type ) {
809 return self::$object_type = $object_type;
810 }
811
812 /**
813 * Returns the object type
814 * @since 1.0.0
815 * @return string Object type
816 */
817 public static function get_mb_type() {
818 return self::$mb_object_type;
819 }
820
821 /**
822 * Returns the nonce value for wp_meta_box_nonce
823 * @since 1.0.0
824 * @return string Nonce value
825 */
826 public static function nonce() {
827 return basename( __FILE__ );
828 }
829
830 /**
831 * Defines the url which is used to load local resources.
832 * This may need to be filtered for local Window installations.
833 * If resources do not load, please check the wiki for details.
834 * @since 1.0.1
835 * @return string URL to CMB resources
836 */
837 public static function get_meta_box_url() {
838
839 if ( strtoupper( substr( PHP_OS, 0, 3 ) ) === 'WIN' ) {
840 // Windows
841 $content_dir = str_replace( '/', DIRECTORY_SEPARATOR, WP_CONTENT_DIR );
842 $content_url = str_replace( $content_dir, WP_CONTENT_URL, dirname(__FILE__) );
843 $cmb_url = str_replace( DIRECTORY_SEPARATOR, '/', $content_url );
844
845 } else {
846 $cmb_url = str_replace(
847 array(WP_CONTENT_DIR, WP_PLUGIN_DIR),
848 array(WP_CONTENT_URL, WP_PLUGIN_URL),
849 dirname( __FILE__ )
850 );
851 }
852 $cmb_url = set_url_scheme( $cmb_url );
853
854 return trailingslashit( apply_filters('cmb_meta_box_url', $cmb_url ) );
855 }
856
857 /**
858 * Fills in empty metabox parameters with defaults
859 * @since 1.0.1
860 * @param array $meta_box Metabox config array
861 * @return array Modified Metabox config array
862 */
863 public static function set_mb_defaults( $meta_box ) {
864 return wp_parse_args( $meta_box, self::$mb_defaults );
865 }
866
867 /**
868 * Removes an option from an option array
869 * @since 1.0.1
870 * @param string $option_key Option key
871 * @param string $field_id Option array field key
872 * @return array Modified options
873 */
874 public static function remove_option( $option_key, $field_id ) {
875
876 self::$options[ $option_key ] = ! isset( self::$options[ $option_key ] ) || empty( self::$options[ $option_key ] ) ? self::_get_option( $option_key ) : self::$options[ $option_key ];
877
878 if ( isset( self::$options[ $option_key ][ $field_id ] ) )
879 unset( self::$options[ $option_key ][ $field_id ] );
880
881 return self::$options[ $option_key ];
882 }
883
884 /**
885 * Retrieves an option from an option array
886 * @since 1.0.1
887 * @param string $option_key Option key
888 * @param string $field_id Option array field key
889 * @return array Options array or specific field
890 */
891 public static function get_option( $option_key, $field_id = '' ) {
892
893 self::$options[ $option_key ] = ! isset( self::$options[ $option_key ] ) || empty( self::$options[ $option_key ] ) ? self::_get_option( $option_key ) : self::$options[ $option_key ];
894
895 if ( $field_id ) {
896 return isset( self::$options[ $option_key ][ $field_id ] ) ? self::$options[ $option_key ][ $field_id ] : false;
897 }
898
899 return self::$options[ $option_key ];
900 }
901
902 /**
903 * Updates Option data
904 * @since 1.0.1
905 * @param string $option_key Option key
906 * @param string $field_id Option array field key
907 * @param mixed $value Value to update data with
908 * @param bool $single Whether data should be an array
909 * @return array Modified options
910 */
911 public static function update_option( $option_key, $field_id, $value, $single = true ) {
912
913 if ( ! $single ) {
914 // If multiple, add to array
915 self::$options[ $option_key ][ $field_id ][] = $value;
916 } else {
917 self::$options[ $option_key ][ $field_id ] = $value;
918 }
919
920 return self::$options[ $option_key ];
921 }
922
923 /**
924 * Retrieve option value based on name of option.
925 * @uses apply_filters() Calls 'cmb_override_option_get_$option_key' hook to allow
926 * overwriting the option value to be retrieved.
927 *
928 * @since 1.0.1
929 * @param string $option Name of option to retrieve. Expected to not be SQL-escaped.
930 * @param mixed $default Optional. Default value to return if the option does not exist.
931 * @return mixed Value set for the option.
932 */
933 public static function _get_option( $option_key, $default = false ) {
934
935 $test_get = apply_filters( "cmb_override_option_get_$option_key", 'cmb_no_override_option_get', $default );
936
937 if ( $test_get !== 'cmb_no_override_option_get' )
938 return $test_get;
939
940 // If no override, get the option
941 return get_option( $option_key, $default );
942 }
943
944 /**
945 * Saves the option array
946 * Needs to be run after finished using remove/update_option
947 * @uses apply_filters() Calls 'cmb_override_option_save_$option_key' hook to allow
948 * overwriting the option value to be stored.
949 *
950 * @since 1.0.1
951 * @param string $option_key Option key
952 * @return boolean Success/Failure
953 */
954 public static function save_option( $option_key ) {
955
956 $to_save = self::get_option( $option_key );
957
958 $test_save = apply_filters( "cmb_override_option_save_$option_key", 'cmb_no_override_option_save', $to_save );
959
960 if ( $test_save !== 'cmb_no_override_option_save' )
961 return $test_save;
962
963 // If no override, update the option
964 return update_option( $option_key, $to_save );
965 }
966
967 /**
968 * Utility method that returns a timezone string representing the default timezone for the site.
969 *
970 * Roughly copied from WordPress, as get_option('timezone_string') will return
971 * and empty string if no value has beens set on the options page.
972 * A timezone string is required by the wp_timezone_choice() used by the
973 * select_timezone field.
974 *
975 * @since 1.0.0
976 * @return string Timezone string
977 */
978 public static function timezone_string() {
979 $current_offset = get_option( 'gmt_offset' );
980 $tzstring = get_option( 'timezone_string' );
981
982 if ( empty( $tzstring ) ) { // Create a UTC+- zone if no timezone string exists
983 if ( 0 == $current_offset )
984 $tzstring = 'UTC+0';
985 elseif ( $current_offset < 0 )
986 $tzstring = 'UTC' . $current_offset;
987 else
988 $tzstring = 'UTC+' . $current_offset;
989 }
990
991 return $tzstring;
992 }
993
994 /**
995 * Utility method that returns time string offset by timezone
996 * @since 1.0.0
997 * @param string $tzstring Time string
998 * @return string Offset time string
999 */
1000 public static function timezone_offset( $tzstring ) {
1001 if ( ! empty( $tzstring ) && is_string( $tzstring ) ) {
1002 if ( substr( $tzstring, 0, 3 ) === 'UTC' ) {
1003 $tzstring = str_replace( array( ':15',':30',':45' ), array( '.25','.5','.75' ), $tzstring );
1004 return intval( floatval( substr( $tzstring, 3 ) ) * HOUR_IN_SECONDS );
1005 }
1006
1007 $date_time_zone_selected = new DateTimeZone( $tzstring );
1008 $tz_offset = timezone_offset_get( $date_time_zone_selected, date_create() );
1009
1010 return $tz_offset;
1011 }
1012
1013 return 0;
1014 }
1015
1016 /**
1017 * Utility method that attempts to get an attachment's ID by it's url
1018 * @since 1.0.0
1019 * @param string $img_url Attachment url
1020 * @return mixed Attachment ID or false
1021 */
1022 public static function image_id_from_url( $img_url ) {
1023 global $wpdb;
1024
1025 $img_url = esc_url_raw( $img_url );
1026 // Get just the file name
1027 if ( false !== strpos( $img_url, '/' ) ) {
1028 $explode = explode( '/', $img_url );
1029 $img_url = end( $explode );
1030 }
1031
1032 // And search for a fuzzy match of the file name
1033 $attachment = $wpdb->get_col( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE guid LIKE '%%%s%%' LIMIT 1;", $img_url ) );
1034
1035 // If we found an attachement ID, return it
1036 if ( !empty( $attachment ) && is_array( $attachment ) )
1037 return $attachment[0];
1038
1039 // No luck
1040 return false;
1041 }
1042
1043}
1044
1045// Handle oembed Ajax
1046add_action( 'wp_ajax_cmb_oembed_handler', array( 'cmb_Meta_Box_ajax', 'oembed_handler' ) );
1047add_action( 'wp_ajax_nopriv_cmb_oembed_handler', array( 'cmb_Meta_Box_ajax', 'oembed_handler' ) );
1048
1049/**
1050 * A helper function to get an option from a CMB options array
1051 * @since 1.0.1
1052 * @param string $option_key Option key
1053 * @param string $field_id Option array field key
1054 * @return array Options array or specific field
1055 */
1056function cmb_get_option( $option_key, $field_id = '' ) {
1057 return cmb_Meta_Box::get_option( $option_key, $field_id );
1058}
1059
1060/**
1061 * Get a CMB field object.
1062 * @since 1.1.0
1063 * @param array $field_args Field arguments
1064 * @param int $object_id Object ID
1065 * @param string $object_type Type of object being saved. (e.g., post, user, or comment)
1066 * @return object cmb_Meta_Box_field object
1067 */
1068function cmb_get_field( $field_args, $object_id = 0, $object_type = 'post' ) {
1069 // Default to the loop post ID
1070 $object_id = $object_id ? $object_id : get_the_ID();
1071 cmb_Meta_Box::set_object_id( $object_id );
1072 cmb_Meta_Box::set_object_type( $object_type );
1073 // Send back field object
1074 return new cmb_Meta_Box_field( $field_args );
1075}
1076
1077/**
1078 * Get a field's value.
1079 * @since 1.1.0
1080 * @param array $field_args Field arguments
1081 * @param int $object_id Object ID
1082 * @param string $object_type Type of object being saved. (e.g., post, user, comment, or options-page)
1083 * @return mixed Maybe escaped value
1084 */
1085function cmb_get_field_value( $field_args, $object_id = 0, $object_type = 'post' ) {
1086 $field = cmb_get_field( $field_args, $object_id, $object_type );
1087 return $field->escaped_value();
1088}
1089
1090/**
1091 * Loop and output multiple metaboxes
1092 * @since 1.0.0
1093 * @param array $meta_boxes Metaboxes config array
1094 * @param int $object_id Object ID
1095 */
1096function cmb_print_metaboxes( $meta_boxes, $object_id ) {
1097 foreach ( (array) $meta_boxes as $meta_box ) {
1098 cmb_print_metabox( $meta_box, $object_id );
1099 }
1100}
1101
1102/**
1103 * Output a metabox
1104 * @since 1.0.0
1105 * @param array $meta_box Metabox config array
1106 * @param int $object_id Object ID
1107 */
1108function cmb_print_metabox( $meta_box, $object_id ) {
1109 $cmb = new cmb_Meta_Box( $meta_box );
1110 if ( $cmb ) {
1111
1112 cmb_Meta_Box::set_object_id( $object_id );
1113
1114 if ( ! wp_script_is( 'cmb-scripts', 'registered' ) )
1115 $cmb->register_scripts();
1116
1117 wp_enqueue_script( 'cmb-scripts' );
1118
1119 // default is to show cmb styles
1120 if ( $meta_box['cmb_styles'] != false )
1121 wp_enqueue_style( 'cmb-styles' );
1122
1123 cmb_Meta_Box::show_form( $meta_box );
1124 }
1125
1126}
1127
1128/**
1129 * Saves a particular metabox's fields
1130 * @since 1.0.0
1131 * @param array $meta_box Metabox config array
1132 * @param int $object_id Object ID
1133 */
1134function cmb_save_metabox_fields( $meta_box, $object_id ) {
1135 cmb_Meta_Box::save_fields( $meta_box, $object_id );
1136}
1137
1138/**
1139 * Display a metabox form & save it on submission
1140 * @since 1.0.0
1141 * @param array $meta_box Metabox config array
1142 * @param int $object_id Object ID
1143 * @param boolean $return Whether to return or echo form
1144 * @return string CMB html form markup
1145 */
1146function cmb_metabox_form( $meta_box, $object_id, $echo = true ) {
1147
1148 $meta_box = cmb_Meta_Box::set_mb_defaults( $meta_box );
1149
1150 // Make sure form should be shown
1151 if ( ! apply_filters( 'cmb_show_on', true, $meta_box ) )
1152 return '';
1153
1154 // Make sure that our object type is explicitly set by the metabox config
1155 cmb_Meta_Box::set_object_type( cmb_Meta_Box::set_mb_type( $meta_box ) );
1156
1157 // Save the metabox if it's been submitted
1158 // check permissions
1159 // @todo more hardening?
1160 if (
1161 // check nonce
1162 isset( $_POST['submit-cmb'], $_POST['object_id'], $_POST['wp_meta_box_nonce'] )
1163 && wp_verify_nonce( $_POST['wp_meta_box_nonce'], cmb_Meta_Box::nonce() )
1164 && $_POST['object_id'] == $object_id
1165 )
1166 cmb_save_metabox_fields( $meta_box, $object_id );
1167
1168 // Show specific metabox form
1169
1170 // Get cmb form
1171 ob_start();
1172 cmb_print_metabox( $meta_box, $object_id );
1173 $form = ob_get_contents();
1174 ob_end_clean();
1175
1176 $form_format = apply_filters( 'cmb_frontend_form_format', '<form class="cmb-form" method="post" id="%s" enctype="multipart/form-data" encoding="multipart/form-data"><input type="hidden" name="object_id" value="%s">%s<input type="submit" name="submit-cmb" value="%s" class="button-primary"></form>', $object_id, $meta_box, $form );
1177
1178 $form = sprintf( $form_format, $meta_box['id'], $object_id, $form, __( 'Save' ) );
1179
1180 if ( $echo )
1181 echo $form;
1182
1183 return $form;
1184}
1185
1186// End. That's it, folks! //