<?php if( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly if( ! class_exists('acf_form_customizer') ) : class acf_form_customizer { /* * __construct * * This function will setup the class functionality * * @type function * @date 5/03/2014 * @since 5.0.0 * * @param n/a * @return n/a */ function __construct() { // vars $this->preview_values = array(); $this->preview_fields = array(); $this->preview_errors = array(); // actions add_action('customize_controls_init', array($this, 'customize_controls_init')); add_action('customize_preview_init', array($this, 'customize_preview_init'), 1, 1); add_action('customize_save', array($this, 'customize_save'), 1, 1); // save add_filter('widget_update_callback', array($this, 'save_widget'), 10, 4); } /* * admin_enqueue_scripts * * This action is run after post query but before any admin script / head actions. * It is a good place to register all actions. * * @type action (admin_enqueue_scripts) * @date 26/01/13 * @since 3.6.0 * * @param N/A * @return N/A */ function customize_controls_init() { // load acf scripts acf_enqueue_scripts(array( 'context' => 'customize_controls' )); // actions add_action('acf/input/admin_footer', array($this, 'admin_footer'), 1); } /* * save_widget * * This function will hook into the widget update filter and save ACF data * * @type function * @date 27/05/2015 * @since 5.2.3 * * @param $instance (array) widget settings * @param $new_instance (array) widget settings * @param $old_instance (array) widget settings * @param $widget (object) widget info * @return $instance */ function save_widget( $instance, $new_instance, $old_instance, $widget ) { // bail ealry if not valid (customize + acf values + nonce) if( !isset($_POST['wp_customize']) || !isset($new_instance['acf']) || !acf_verify_nonce('widget') ) return $instance; // vars $data = array( 'post_id' => "widget_{$widget->id}", 'values' => array(), 'fields' => array() ); // append values $data['values'] = $new_instance['acf']; // append fields (name => key relationship) - used later in 'acf/get_field_reference' for customizer previews foreach( $data['values'] as $k => $v ) { // get field $field = acf_get_field( $k ); // continue if no field if( !$field ) continue; // update $data['fields'][ $field['name'] ] = $field['key']; } // append data to instance $instance['acf'] = $data; // return return $instance; } /* * settings * * This function will return an array of cutomizer settings that include ACF data * similar to `$customizer->settings();` * * @type function * @date 22/03/2016 * @since 5.3.2 * * @param $customizer (object) * @return $value (mixed) */ function settings( $customizer ) { // vars $data = array(); $settings = $customizer->settings(); // bail ealry if no settings if( empty($settings) ) return false; // loop over settings foreach( $settings as $setting ) { // vars $id = $setting->id; // verify settings type if( substr($id, 0, 6) == 'widget' || substr($id, 0, 7) == 'nav_menu' ) { // allow } else { continue; } // get value $value = $setting->post_value(); // bail early if no acf if( !is_array($value) || !isset($value['acf']) ) continue; // set data $setting->acf = $value['acf']; // append $data[] = $setting; } // bail ealry if no settings if( empty($data) ) return false; // return return $data; } /* * customize_preview_init * * This function is called when customizer preview is initialized * * @type function * @date 22/03/2016 * @since 5.3.2 * * @param $customizer (object) * @return n/a */ function customize_preview_init( $customizer ) { // get customizer settings (widgets) $settings = $this->settings( $customizer ); // bail ealry if no settings if( empty($settings) ) return; // append values foreach( $settings as $setting ) { // get acf data $data = $setting->acf; // append acf_value to preview_values $this->preview_values[ $data['post_id'] ] = $data['values']; $this->preview_fields[ $data['post_id'] ] = $data['fields']; } // bail ealry if no preview_values if( empty($this->preview_values) ) return; // add filters add_filter('acf/pre_load_value', array($this, 'pre_load_value'), 10, 3); add_filter('acf/pre_load_reference', array($this, 'pre_load_reference'), 10, 3); } /** * pre_load_value * * Used to inject preview value * * @date 2/2/18 * @since 5.6.5 * * @param type $var Description. Default. * @return type Description. */ function pre_load_value( $value, $post_id, $field ) { // check if( isset($this->preview_values[ $post_id ][ $field['key'] ]) ) { return $this->preview_values[ $post_id ][ $field['key'] ]; } // return return $value; } /** * pre_load_reference * * Used to inject preview value * * @date 2/2/18 * @since 5.6.5 * * @param type $var Description. Default. * @return type Description. */ function pre_load_reference( $field_key, $field_name, $post_id ) { // check if( isset($this->preview_fields[ $post_id ][ $field_name ]) ) { return $this->preview_fields[ $post_id ][ $field_name ]; } // return return $field_key; } /* * customize_save * * This function is called when customizer saves a widget. * Normally, the widget_update_callback filter would be used, but the customizer disables this and runs a custom action * class-customizer-settings.php will save the widget data via the function set_root_value which uses update_option * * @type function * @date 22/03/2016 * @since 5.3.2 * * @param $customizer (object) * @return n/a */ function customize_save( $customizer ) { // get customizer settings (widgets) $settings = $this->settings( $customizer ); // bail ealry if no settings if( empty($settings) ) return; // append values foreach( $settings as $setting ) { // get acf data $data = $setting->acf; // save acf data acf_save_post( $data['post_id'], $data['values'] ); // remove [acf] data from saved widget array $id_data = $setting->id_data(); add_filter('pre_update_option_' . $id_data['base'], array($this, 'pre_update_option'), 10, 3); } } /* * pre_update_option * * this function will remove the [acf] data from widget insance * * @type function * @date 22/03/2016 * @since 5.3.2 * * @param $post_id (int) * @return $post_id (int) */ function pre_update_option( $value, $option, $old_value ) { // bail ealry if no value if( empty($value) ) return $value; // loop over widgets // WP saves all widgets (of the same type) as an array of widgets foreach( $value as $i => $widget ) { // bail ealry if no acf if( !isset($widget['acf']) ) continue; // remove widget unset($value[ $i ]['acf']); } // return return $value; } /* * admin_footer * * This function will add some custom HTML to the footer of the edit page * * @type function * @date 11/06/2014 * @since 5.0.0 * * @param n/a * @return n/a */ function admin_footer() { ?> <script type="text/javascript"> (function($) { // customizer saves widget on any input change, so unload is not needed acf.unload.active = 0; // hack customizer function to remove bug caused by WYSIWYG field using aunique ID // customizer compares returned AJAX HTML with the HTML of the widget form. // the _getInputsSignature() function is used to generate a string based of input name + id. // because ACF generates a unique ID on the WYSIWYG field, this string will not match causing the preview function to bail. // an attempt was made to remove the WYSIWYG unique ID, but this caused multiple issues in the wp-admin and altimately doesn't make sense with the tinymce rule that all editors must have a unique ID. // source: wp-admin/js/customize-widgets.js // vars var WidgetControl = wp.customize.Widgets.WidgetControl.prototype; // backup functions WidgetControl.__getInputsSignature = WidgetControl._getInputsSignature; WidgetControl.__setInputState = WidgetControl._setInputState; // modify __getInputsSignature WidgetControl._getInputsSignature = function( inputs ) { // vars var signature = this.__getInputsSignature( inputs ); safe = []; // split signature = signature.split(';'); // loop for( var i in signature ) { // vars var bit = signature[i]; // bail ealry if acf is found if( bit.indexOf('acf') !== -1 ) continue; // append safe.push( bit ); } // update signature = safe.join(';'); // return return signature; }; // modify _setInputState // this function deosn't seem to run on widget title/content, only custom fields // either way, this function is not needed and will break ACF fields WidgetControl._setInputState = function( input, state ) { return true; }; })(jQuery); </script> <?php } } new acf_form_customizer(); endif; ?>