Advertisement
Guest User

Untitled

a guest
Mar 25th, 2019
140
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 36.54 KB | None | 0 0
  1. <?php
  2. /*
  3. Script Name:  Custom Metaboxes and Fields
  4. Contributors: 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)
  9. Description:  This will create metaboxes with custom fields that will blow your mind.
  10. Version:      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
  38. spl_autoload_register('cmb_Meta_Box::autoload_helpers');
  39.  
  40. $meta_boxes = array();
  41. $meta_boxes = apply_filters( 'cmb_meta_boxes', $meta_boxes );
  42. foreach ( $meta_boxes as $meta_box ) {
  43.     $my_box = new cmb_Meta_Box( $meta_box );
  44. }
  45.  
  46. define( 'CMB_META_BOX_URL', cmb_Meta_Box::get_meta_box_url() );
  47.  
  48. /**
  49.  * Create meta boxes
  50.  */
  51. class 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'        => '[ ↑ ]&nbsp;',
  287.             'down_arrow'      => '&nbsp;[ ↓ ]',
  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
  1046. add_action( 'wp_ajax_cmb_oembed_handler', array( 'cmb_Meta_Box_ajax', 'oembed_handler' ) );
  1047. add_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.  */
  1056. function 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.  */
  1068. function 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.  */
  1085. function 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.  */
  1096. function 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.  */
  1108. function 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.  */
  1134. function 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.  */
  1146. function 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! //
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement