Make WordPress Core

Ticket #31885: test-select-multiple-widget.php

File test-select-multiple-widget.php, 9.0 KB (added by westonruter, 10 years ago)

Demo plugin: Test select[multiple] sync behavior in Widget Customizer https://gist.github.com/westonruter/2e56e08881ae296237ca

Line 
1<?php
2/*
3Plugin Name: Test select[multiple] sync behavior in Widget Customizer
4Description: Demonstration of Core issue #31885 via a widget undo/redo feature.
5Plugin URI: https://core.trac.wordpress.org/ticket/31885
6Author: Weston Ruter, David Lonjon, XWP
7Author URI: https://xwp.co/
8GitHub Plugin URI: https://gist.github.com/westonruter/2e56e08881ae296237ca
9
10Copyright 2015 XWP.co Pty Ltd
11
12This program is free software; you can redistribute it and/or modify
13it under the terms of the GNU General Public License as published by
14the Free Software Foundation; version 2 of the License (GPL v2) only.
15
16This program is distributed in the hope that it will be useful,
17but WITHOUT ANY WARRANTY; without even the implied warranty of
18MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19GNU General Public License for more details.
20
21You should have received a copy of the GNU General Public License
22along with this program; if not, write to the Free Software
23Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
24*/
25
26class Test_Select_Multiple_Widget extends WP_Widget {
27
28        public $select_options = array(
29                'A' => 1,
30                'B' => 2,
31                'C' => 3,
32                'D' => 4,
33        );
34
35        /**
36         * Register widget with WordPress.
37         */
38        function __construct() {
39                parent::__construct(
40                        'test_select_multiple', // Base ID
41                        __( 'Test select[multiple]' ) // Name
42                );
43
44                add_action( 'customize_controls_print_footer_scripts', array( $this, 'print_customize_footer_script' ) );
45        }
46
47        /**
48         * @param array $instance
49         * @return array
50         */
51        function get_instance( $instance = array() ) {
52                return array_merge(
53                        array(
54                                'title' => '',
55                                'select' => array(),
56                        ),
57                        $instance
58                );
59        }
60
61        /**
62         * Front-end display of widget.
63         *
64         * @see WP_Widget::widget()
65         *
66         * @param array $args     Widget arguments.
67         * @param array $instance Saved values from database.
68         */
69        public function widget( $args, $instance ) {
70                $instance = $this->get_instance( $instance );
71                echo $args['before_widget'];
72                echo $args['before_title'];
73                echo esc_html( apply_filters( 'widget_title', $instance['title'] ) );
74                echo $args['after_title'];
75                var_dump( $instance['select'] );
76                echo $args['after_widget'];
77        }
78
79        /**
80         * Back-end widget form.
81         *
82         * @see WP_Widget::form()
83         *
84         * @param array $instance Previously saved values from database.
85         * @return null
86         */
87        public function form( $instance ) {
88                $instance = $this->get_instance( $instance );
89
90                printf( '<p><label for="%s">Title:</label> <input type="text" id="%s" name="%s" value="%s"></p>',
91                        esc_attr( $this->get_field_id( 'title' ) ),
92                        esc_attr( $this->get_field_id( 'title' ) ),
93                        esc_attr( $this->get_field_name( 'title' ) ),
94                        esc_attr( $instance['title'] )
95                );
96                printf( '<p><label for="%s">Select:</label><br>', esc_attr( $this->get_field_id( 'select' ) ) );
97                printf( '<select class="widefat" id="%s" name="%s[]" multiple>', esc_attr( $this->get_field_id( 'select' ) ), esc_attr( $this->get_field_name( 'select' ) ) );
98                foreach ( $this->select_options as $text => $value ) {
99                        printf(
100                                '<option %s value="%s">%s</option>',
101                                selected( in_array( $value, $instance['select'] ), true, false ),
102                                esc_attr( $value ),
103                                esc_html( $text )
104                        );
105                }
106                echo '</select>';
107                echo '</p>';
108
109                return null;
110        }
111
112        /**
113         * Sanitize widget form values as they are saved.
114         *
115         * @see WP_Widget::update()
116         *
117         * @param array $new_instance Values just sent to be saved.
118         * @param array $old_instance Previously saved values from database.
119         *
120         * @return array Updated safe values to be saved.
121         */
122        public function update( $new_instance, $old_instance ) {
123                $instance = $this->get_instance( $new_instance );
124                if ( isset( $new_instance['title'] ) ) {
125                        $instance['title'] = sanitize_text_field( $new_instance['title'] );
126                }
127                if ( isset( $new_instance['select'] ) && is_array( $new_instance['select'] ) ) {
128                        $instance['select'] = array_intersect( $new_instance['select'], $this->select_options );
129                }
130                return $instance;
131        }
132
133        /**
134         *
135         */
136        public function print_customize_footer_script() {
137                wp_print_scripts( array( 'customize-controls', 'customize-widgets', 'wp-util' ) );
138                ?>
139                <style>
140                        .widget-history-control-actions a {
141                                font-size: 16px;
142                        }
143                        .widget-history-control-actions a.disabled {
144                                color: #DDD;
145                                cursor: default;
146                        }
147                </style>
148                <script type="text/html" id="tmpl-widget-history-control-actions">
149                        <span class="widget-history-control-actions">
150                                |
151                                <a class="widget-control-undo dashicons dashicons-undo disabled" href="#undo" title="<?php esc_attr_e( 'Undo' ) ?>"></a>
152                                <a class="widget-control-redo dashicons dashicons-redo disabled" href="#redo" title="<?php esc_attr_e( 'Redo' ) ?>"></a>
153                        </span>
154                </script>
155
156                <script type="text/javascript">
157                var TestSelectMultipleWidget = (function ( $ ) {
158                        var self = {};
159                        self.widgetHistoryControlActionsTpl = wp.template( 'widget-history-control-actions' );
160
161                        /**
162                         * @param {jQuery} widget
163                         * @return string
164                         */
165                        self.getWidgetControlId = function ( widget ) {
166                                var controlId, idBase, widgetNumber;
167                                idBase = widget.find( 'input[name=id_base]' ).val();
168                                widgetNumber = widget.find( 'input[name=widget_number]' ).val();
169                                controlId = 'widget_' + idBase + '[' + widgetNumber + ']';
170                                return controlId;
171                        };
172
173                        self.settingHistories = {};
174
175                        /**
176                         * @param {wp.customize.Control} control
177                         * @return {object}
178                         */
179                        self.getSettingHistory = function ( control ) {
180                                var settingHistory = self.settingHistories[ control.id ];
181                                if ( ! settingHistory ) {
182                                        throw new Error( 'No settingHistory for ' + control.id );
183                                }
184                                return settingHistory;
185                        };
186
187                        /**
188                         *
189                         * @param {wp.customize.Control} control
190                         */
191                        self.updateUiState = function ( control ) {
192                                var settingHistory = self.getSettingHistory( control );
193                                control.container.find( '.widget-control-undo' ).toggleClass( 'disabled', settingHistory.offset >= settingHistory.stack.length - 1 );
194                                control.container.find( '.widget-control-redo' ).toggleClass( 'disabled', settingHistory.offset <= 0 );
195                        };
196
197                        /**
198                         *
199                         * @param {wp.customize.Control} control
200                         */
201                        self.undoSettingChange = function ( control ) {
202                                var settingHistory = self.getSettingHistory( control );
203                                if ( settingHistory.offset + 1 < settingHistory.stack.length ) {
204                                        settingHistory.ignoreChanges = true;
205                                        settingHistory.offset += 1;
206                                        control.setting( $.extend( {}, settingHistory.stack[ settingHistory.offset ] ) );
207                                        settingHistory.ignoreChanges = false;
208                                }
209                                self.updateUiState( control );
210                        };
211
212                        /**
213                         *
214                         * @param {wp.customize.Control} control
215                         */
216                        self.redoSettingChange = function ( control ) {
217                                var settingHistory = self.getSettingHistory( control );
218                                if ( settingHistory.offset > 0 ) {
219                                        settingHistory.ignoreChanges = true;
220                                        settingHistory.offset -= 1;
221                                        control.setting( $.extend( {}, settingHistory.stack[ settingHistory.offset ] ) );
222                                        settingHistory.ignoreChanges = false;
223                                }
224                                self.updateUiState( control );
225                        };
226
227                        /**
228                         * @param {wp.customize.Control} control
229                         * @param {array} instance
230                         */
231                        self.onChangeSetting = function ( control, instance ) {
232                                var settingHistory = self.getSettingHistory( control );
233                                if ( ! settingHistory.ignoreChanges ) {
234                                        settingHistory.stack.splice( 0, settingHistory.offset, instance );
235                                        settingHistory.offset = 0;
236                                        self.updateUiState( control );
237                                }
238                        };
239
240                        /**
241                         *
242                         * @param {wp.customize.Control} control
243                         */
244                        self.addWidgetHistory = function ( control ) {
245                                var historyControlActions = $( self.widgetHistoryControlActionsTpl() )
246                                        widgetControlActionsLeft = control.container.find( '.widget-control-actions .alignleft' );
247
248                                self.settingHistories[ control.id ] = {
249                                        offset: 0,
250                                        stack: [ control.setting() ]
251                                };
252
253                                control.setting.bind( function ( to ) {
254                                        self.onChangeSetting( control, to );
255                                } );
256
257                                historyControlActions.find( '.widget-control-undo' ).on( 'click', function ( e ) {
258                                        e.preventDefault();
259                                        self.undoSettingChange( control );
260                                } );
261                                historyControlActions.find( '.widget-control-redo' ).on( 'click', function ( e ) {
262                                        e.preventDefault();
263                                        self.redoSettingChange( control );
264                                } );
265
266                                // Add the undo and redo links
267                                widgetControlActionsLeft.append( historyControlActions );
268
269                                // Remove the redundant Close link
270                                widgetControlActionsLeft.find( '.widget-control-close' ).each( function () {
271                                        if ( this.previousSibling && 3 === this.previousSibling.nodeType ) {
272                                                // Remove the ' | '
273                                                this.previousSibling.parentNode.removeChild( this.previousSibling );
274                                        }
275                                        $( this ).hide();
276                                } );
277                        };
278
279                        /**
280                         *
281                         * @param {jQuery.Event} e
282                         * @param {jQuery} widget
283                         */
284                        self.onWidgetAdded = function ( e, widget ) {
285                                var controlId = self.getWidgetControlId( widget );
286                                wp.customize.control( controlId, function ( control ) {
287                                        self.addWidgetHistory( control );
288                                } );
289                        };
290
291                        $( document ).on( 'widget-added', _.bind( self.onWidgetAdded, self ) );
292
293                        return self;
294                }( jQuery ));
295                </script>
296
297                <?php
298        }
299}
300
301add_action( 'widgets_init', function () {
302        register_widget( 'Test_Select_Multiple_Widget' );
303} );