Skip to content

Instantly share code, notes, and snippets.

@westonruter
Last active August 29, 2015 14:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save westonruter/2e56e08881ae296237ca to your computer and use it in GitHub Desktop.
Save westonruter/2e56e08881ae296237ca to your computer and use it in GitHub Desktop.
<?php
/*
Plugin Name: Test select[multiple] sync behavior in Widget Customizer
Description: Demonstration of Core issue #31885 via a widget undo/redo feature.
Plugin URI: https://core.trac.wordpress.org/ticket/31885
Author: Weston Ruter, David Lonjon, XWP
Author URI: https://xwp.co/
GitHub Plugin URI: https://gist.github.com/westonruter/2e56e08881ae296237ca
Copyright 2015 XWP.co Pty Ltd
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License (GPL v2) only.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
class Test_Select_Multiple_Widget extends WP_Widget {
public $select_options = array(
'A' => 1,
'B' => 2,
'C' => 3,
'D' => 4,
);
/**
* Register widget with WordPress.
*/
function __construct() {
parent::__construct(
'test_select_multiple', // Base ID
__( 'Test select[multiple]' ) // Name
);
add_action( 'customize_controls_print_footer_scripts', array( $this, 'print_customize_footer_script' ) );
}
/**
* @param array $instance
* @return array
*/
function get_instance( $instance = array() ) {
return array_merge(
array(
'title' => '',
'select' => array(),
),
$instance
);
}
/**
* Front-end display of widget.
*
* @see WP_Widget::widget()
*
* @param array $args Widget arguments.
* @param array $instance Saved values from database.
*/
public function widget( $args, $instance ) {
$instance = $this->get_instance( $instance );
echo $args['before_widget'];
echo $args['before_title'];
echo esc_html( apply_filters( 'widget_title', $instance['title'] ) );
echo $args['after_title'];
var_dump( $instance['select'] );
echo $args['after_widget'];
}
/**
* Back-end widget form.
*
* @see WP_Widget::form()
*
* @param array $instance Previously saved values from database.
* @return null
*/
public function form( $instance ) {
$instance = $this->get_instance( $instance );
printf( '<p><label for="%s">Title:</label> <input type="text" id="%s" name="%s" value="%s"></p>',
esc_attr( $this->get_field_id( 'title' ) ),
esc_attr( $this->get_field_id( 'title' ) ),
esc_attr( $this->get_field_name( 'title' ) ),
esc_attr( $instance['title'] )
);
printf( '<p><label for="%s">Select:</label><br>', esc_attr( $this->get_field_id( 'select' ) ) );
printf( '<select class="widefat" id="%s" name="%s[]" multiple>', esc_attr( $this->get_field_id( 'select' ) ), esc_attr( $this->get_field_name( 'select' ) ) );
foreach ( $this->select_options as $text => $value ) {
printf(
'<option %s value="%s">%s</option>',
selected( in_array( $value, $instance['select'] ), true, false ),
esc_attr( $value ),
esc_html( $text )
);
}
echo '</select>';
echo '</p>';
return null;
}
/**
* Sanitize widget form values as they are saved.
*
* @see WP_Widget::update()
*
* @param array $new_instance Values just sent to be saved.
* @param array $old_instance Previously saved values from database.
*
* @return array Updated safe values to be saved.
*/
public function update( $new_instance, $old_instance ) {
$instance = $this->get_instance( $new_instance );
if ( isset( $new_instance['title'] ) ) {
$instance['title'] = sanitize_text_field( $new_instance['title'] );
}
if ( isset( $new_instance['select'] ) && is_array( $new_instance['select'] ) ) {
$instance['select'] = array_intersect( $new_instance['select'], $this->select_options );
}
return $instance;
}
/**
*
*/
public function print_customize_footer_script() {
wp_print_scripts( array( 'customize-controls', 'customize-widgets', 'wp-util' ) );
?>
<style>
.widget-history-control-actions a {
font-size: 16px;
}
.widget-history-control-actions a.disabled {
color: #DDD;
cursor: default;
}
</style>
<script type="text/html" id="tmpl-widget-history-control-actions">
<span class="widget-history-control-actions">
|
<a class="widget-control-undo dashicons dashicons-undo disabled" href="#undo" title="<?php esc_attr_e( 'Undo' ) ?>"></a>
<a class="widget-control-redo dashicons dashicons-redo disabled" href="#redo" title="<?php esc_attr_e( 'Redo' ) ?>"></a>
</span>
</script>
<script type="text/javascript">
var TestSelectMultipleWidget = (function ( $ ) {
var self = {};
self.widgetHistoryControlActionsTpl = wp.template( 'widget-history-control-actions' );
/**
* @param {jQuery} widget
* @return string
*/
self.getWidgetControlId = function ( widget ) {
var controlId, idBase, widgetNumber;
idBase = widget.find( 'input[name=id_base]' ).val();
widgetNumber = widget.find( 'input[name=widget_number]' ).val();
controlId = 'widget_' + idBase + '[' + widgetNumber + ']';
return controlId;
};
self.settingHistories = {};
/**
* @param {wp.customize.Control} control
* @return {object}
*/
self.getSettingHistory = function ( control ) {
var settingHistory = self.settingHistories[ control.id ];
if ( ! settingHistory ) {
throw new Error( 'No settingHistory for ' + control.id );
}
return settingHistory;
};
/**
*
* @param {wp.customize.Control} control
*/
self.updateUiState = function ( control ) {
var settingHistory = self.getSettingHistory( control );
control.container.find( '.widget-control-undo' ).toggleClass( 'disabled', settingHistory.offset >= settingHistory.stack.length - 1 );
control.container.find( '.widget-control-redo' ).toggleClass( 'disabled', settingHistory.offset <= 0 );
};
/**
*
* @param {wp.customize.Control} control
*/
self.undoSettingChange = function ( control ) {
var settingHistory = self.getSettingHistory( control );
if ( settingHistory.offset + 1 < settingHistory.stack.length ) {
settingHistory.ignoreChanges = true;
settingHistory.offset += 1;
control.setting( $.extend( {}, settingHistory.stack[ settingHistory.offset ] ) );
settingHistory.ignoreChanges = false;
}
self.updateUiState( control );
};
/**
*
* @param {wp.customize.Control} control
*/
self.redoSettingChange = function ( control ) {
var settingHistory = self.getSettingHistory( control );
if ( settingHistory.offset > 0 ) {
settingHistory.ignoreChanges = true;
settingHistory.offset -= 1;
control.setting( $.extend( {}, settingHistory.stack[ settingHistory.offset ] ) );
settingHistory.ignoreChanges = false;
}
self.updateUiState( control );
};
/**
* @param {wp.customize.Control} control
* @param {array} instance
*/
self.onChangeSetting = function ( control, instance ) {
var settingHistory = self.getSettingHistory( control );
if ( ! settingHistory.ignoreChanges ) {
settingHistory.stack.splice( 0, settingHistory.offset, instance );
settingHistory.offset = 0;
self.updateUiState( control );
}
};
/**
*
* @param {wp.customize.Control} control
*/
self.addWidgetHistory = function ( control ) {
var historyControlActions = $( $.trim( self.widgetHistoryControlActionsTpl() ) ),
widgetControlActionsLeft = control.container.find( '.widget-control-actions .alignleft' );
self.settingHistories[ control.id ] = {
offset: 0,
stack: [ control.setting() ]
};
control.setting.bind( function ( to ) {
self.onChangeSetting( control, to );
} );
historyControlActions.find( '.widget-control-undo' ).on( 'click', function ( e ) {
e.preventDefault();
self.undoSettingChange( control );
} );
historyControlActions.find( '.widget-control-redo' ).on( 'click', function ( e ) {
e.preventDefault();
self.redoSettingChange( control );
} );
// Add the undo and redo links
widgetControlActionsLeft.append( historyControlActions );
// Remove the redundant Close link
widgetControlActionsLeft.find( '.widget-control-close' ).each( function () {
if ( this.previousSibling && 3 === this.previousSibling.nodeType ) {
// Remove the ' | '
this.previousSibling.parentNode.removeChild( this.previousSibling );
}
$( this ).hide();
} );
};
/**
*
* @param {jQuery.Event} e
* @param {jQuery} widget
*/
self.onWidgetAdded = function ( e, widget ) {
var controlId = self.getWidgetControlId( widget );
wp.customize.control( controlId, function ( control ) {
self.addWidgetHistory( control );
} );
};
$( document ).on( 'widget-added', _.bind( self.onWidgetAdded, self ) );
return self;
}( jQuery ));
</script>
<?php
}
}
add_action( 'widgets_init', function () {
register_widget( 'Test_Select_Multiple_Widget' );
} );
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment