WordPress.org

Make WordPress Core

Ticket #33901: 33901.2.diff

File 33901.2.diff, 16.5 KB (added by westonruter, 4 years ago)

Added unit tests: https://github.com/xwp/wordpress-develop/compare/abe29ce...51e3f16

  • src/wp-admin/includes/widgets.php

    diff --git src/wp-admin/includes/widgets.php src/wp-admin/includes/widgets.php
    index 356970f..19fd390 100644
    function wp_widget_control( $sidebar_args ) { 
    181181        $multi_number = isset($sidebar_args['_multi_num']) ? $sidebar_args['_multi_num'] : '';
    182182        $add_new = isset($sidebar_args['_add']) ? $sidebar_args['_add'] : '';
    183183
     184        $before_form = isset( $sidebar_args['before_form'] ) ? $sidebar_args['before_form'] : '<form method="post">';
     185        $after_form = isset( $sidebar_args['after_form'] ) ? $sidebar_args['after_form'] : '</form>';
     186        $before_widget_content = isset( $sidebar_args['before_widget_content'] ) ? $sidebar_args['before_widget_content'] : '<div class="widget-content">';
     187        $after_widget_content = isset( $sidebar_args['after_widget_content'] ) ? $sidebar_args['after_widget_content'] : '</div>';
     188
    184189        $query_arg = array( 'editwidget' => $widget['id'] );
    185190        if ( $add_new ) {
    186191                $query_arg['addnew'] = 1;
    function wp_widget_control( $sidebar_args ) { 
    225230        </div>
    226231
    227232        <div class="widget-inside">
    228         <form method="post">
    229         <div class="widget-content">
    230 <?php
    231         if ( isset($control['callback']) )
     233        <?php echo $before_form; ?>
     234        <?php echo $before_widget_content; ?>
     235        <?php
     236        if ( isset( $control['callback'] ) ) {
    232237                $has_form = call_user_func_array( $control['callback'], $control['params'] );
    233         else
    234                 echo "\t\t<p>" . __('There are no options for this widget.') . "</p>\n"; ?>
    235         </div>
     238        } else {
     239                echo "\t\t<p>" . __('There are no options for this widget.') . "</p>\n";
     240        }
     241        ?>
     242        <?php echo $after_widget_content; ?>
    236243        <input type="hidden" name="widget-id" class="widget-id" value="<?php echo esc_attr($id_format); ?>" />
    237244        <input type="hidden" name="id_base" class="id_base" value="<?php echo esc_attr($id_base); ?>" />
    238245        <input type="hidden" name="widget-width" class="widget-width" value="<?php if (isset( $control['width'] )) echo esc_attr($control['width']); ?>" />
    function wp_widget_control( $sidebar_args ) { 
    252259                </div>
    253260                <br class="clear" />
    254261        </div>
    255         </form>
     262        <?php echo $after_form; ?>
    256263        </div>
    257264
    258265        <div class="widget-description">
  • src/wp-admin/js/customize-widgets.js

    diff --git src/wp-admin/js/customize-widgets.js src/wp-admin/js/customize-widgets.js
    index 6a640e0..8edc02c 100644
     
    417417                /**
    418418                 * @since 4.1.0
    419419                 */
    420                 initialize: function ( id, options ) {
     420                initialize: function( id, options ) {
    421421                        var control = this;
    422                         api.Control.prototype.initialize.call( control, id, options );
    423                         control.expanded = new api.Value();
     422
     423                        control.widgetControlEmbedded = false;
     424                        control.widgetContentEmbedded = false;
     425                        control.expanded = new api.Value( false );
    424426                        control.expandedArgumentsQueue = [];
    425                         control.expanded.bind( function ( expanded ) {
     427                        control.expanded.bind( function( expanded ) {
    426428                                var args = control.expandedArgumentsQueue.shift();
    427429                                args = $.extend( {}, control.defaultExpandedArguments, args );
    428430                                control.onChangeExpanded( expanded, args );
    429431                        });
    430                         control.expanded.set( false );
     432
     433                        api.Control.prototype.initialize.call( control, id, options );
    431434                },
    432435
    433436                /**
    434                  * Set up the control
     437                 * Set up the control.
     438                 *
     439                 * @since 3.9.0
    435440                 */
    436441                ready: function() {
    437                         this._setupModel();
    438                         this._setupWideWidget();
    439                         this._setupControlToggle();
    440                         this._setupWidgetTitle();
    441                         this._setupReorderUI();
    442                         this._setupHighlightEffects();
    443                         this._setupUpdateUI();
    444                         this._setupRemoveUI();
     442                        var control = this;
     443
     444                        /*
     445                         * Embed a placeholder once the section is expanded. The full widget
     446                         * form content will be embedded once the control itself is expanded,
     447                         * and at this point the widget-added event will be triggered.
     448                         */
     449                        if ( ! control.section() ) {
     450                                control.embedWidgetControl();
     451                        } else {
     452                                api.section( control.section(), function( section ) {
     453                                        var onExpanded = function( isExpanded ) {
     454                                                if ( isExpanded ) {
     455                                                        control.embedWidgetControl();
     456                                                        section.expanded.unbind( onExpanded );
     457                                                }
     458                                        };
     459                                        if ( section.expanded() ) {
     460                                                onExpanded( true );
     461                                        } else {
     462                                                section.expanded.bind( onExpanded );
     463                                        }
     464                                } );
     465                        }
     466                },
     467
     468                /**
     469                 * Embed the .widget element inside the li container.
     470                 *
     471                 * @since 4.4.0
     472                 */
     473                embedWidgetControl: function() {
     474                        var control = this, widgetControl;
     475
     476                        if ( control.widgetControlEmbedded ) {
     477                                return;
     478                        }
     479                        control.widgetControlEmbedded = true;
     480
     481                        widgetControl = $( control.params.widget_control );
     482                        control.container.append( widgetControl );
     483
     484                        control._setupModel();
     485                        control._setupWideWidget();
     486                        control._setupControlToggle();
     487
     488                        control._setupWidgetTitle();
     489                        control._setupReorderUI();
     490                        control._setupHighlightEffects();
     491                        control._setupUpdateUI();
     492                        control._setupRemoveUI();
     493                },
     494
     495                /**
     496                 * Embed the actual widget form inside of .widget-content and finally trigger the widget-added event.
     497                 *
     498                 * @since 4.4.0
     499                 */
     500                embedWidgetContent: function() {
     501                        var control = this, widgetContent;
     502
     503                        control.embedWidgetControl();
     504                        if ( control.widgetContentEmbedded ) {
     505                                return;
     506                        }
     507                        control.widgetContentEmbedded = true;
     508
     509                        widgetContent = $( control.params.widget_content );
     510                        control.container.find( '.widget-content:first' ).append( widgetContent );
    445511
    446512                        /*
    447513                         * Trigger widget-added event so that plugins can attach any event
    448514                         * listeners and dynamic UI elements.
    449515                         */
    450                         $( document ).trigger( 'widget-added', [ this.container.find( '.widget:first' ) ] );
     516                        $( document ).trigger( 'widget-added', [ control.container.find( '.widget:first' ) ] );
     517
    451518                },
    452519
    453520                /**
     
    10081075                        var self = this, instanceOverride, completeCallback, $widgetRoot, $widgetContent,
    10091076                                updateNumber, params, data, $inputs, processing, jqxhr, isChanged;
    10101077
     1078                        // The updateWidget logic requires that the form fields to be fully present.
     1079                        self.embedWidgetContent();
     1080
    10111081                        args = $.extend( {
    10121082                                instance: null,
    10131083                                complete: null,
     
    12551325                onChangeExpanded: function ( expanded, args ) {
    12561326                        var self = this, $widget, $inside, complete, prevComplete;
    12571327
     1328                        self.embedWidgetControl(); // Make sure the outer form is embedded so that the expanded state can be set in the UI.
     1329                        if ( expanded ) {
     1330                                self.embedWidgetContent();
     1331                        }
     1332
    12581333                        // If the expanded state is unchanged only manipulate container expanded states
    12591334                        if ( args.unchanged ) {
    12601335                                if ( expanded ) {
  • src/wp-includes/class-wp-customize-control.php

    diff --git src/wp-includes/class-wp-customize-control.php src/wp-includes/class-wp-customize-control.php
    index f105ab1..b6ffa1a 100644
    class WP_Widget_Form_Customize_Control extends WP_Customize_Control { 
    14871487        public $height;
    14881488        public $is_wide = false;
    14891489
     1490        /**
     1491         * Gather control params for exporting to JavaScript.
     1492         *
     1493         * @global array $wp_registered_widgets
     1494         */
    14901495        public function to_json() {
     1496                global $wp_registered_widgets;
     1497
    14911498                parent::to_json();
    14921499                $exported_properties = array( 'widget_id', 'widget_id_base', 'sidebar_id', 'width', 'height', 'is_wide' );
    14931500                foreach ( $exported_properties as $key ) {
    14941501                        $this->json[ $key ] = $this->$key;
    14951502                }
    1496         }
    14971503
    1498         /**
    1499          *
    1500          * @global array $wp_registered_widgets
    1501          */
    1502         public function render_content() {
    1503                 global $wp_registered_widgets;
     1504                // Get the widget_control and widget_content.
    15041505                require_once ABSPATH . '/wp-admin/includes/widgets.php';
    15051506
    15061507                $widget = $wp_registered_widgets[ $this->widget_id ];
    class WP_Widget_Form_Customize_Control extends WP_Customize_Control { 
    15141515                );
    15151516
    15161517                $args = wp_list_widget_controls_dynamic_sidebar( array( 0 => $args, 1 => $widget['params'][0] ) );
    1517                 echo $this->manager->widgets->get_widget_control( $args );
     1518                $widget_control_parts = $this->manager->widgets->get_widget_control_parts( $args );
     1519
     1520                $this->json['widget_control'] = $widget_control_parts['control'];
     1521                $this->json['widget_content'] = $widget_control_parts['content'];
    15181522        }
    15191523
    15201524        /**
     1525         * Override render_content to be no-op since content is exported via to_json for deferred embedding.
     1526         */
     1527        public function render_content() {}
     1528
     1529        /**
    15211530         * Whether the current widget is rendered on the page.
    15221531         *
    15231532         * @since 4.0.0
  • src/wp-includes/class-wp-customize-widgets.php

    diff --git src/wp-includes/class-wp-customize-widgets.php src/wp-includes/class-wp-customize-widgets.php
    index 8569228..a003393 100644
    final class WP_Customize_Widgets { 
    898898         * @return string Widget control form HTML markup.
    899899         */
    900900        public function get_widget_control( $args ) {
     901                $args[0]['before_form'] = '<div class="form">';
     902                $args[0]['after_form'] = '</div><!-- .form -->';
     903                $args[0]['before_widget_content'] = '<div class="widget-content">';
     904                $args[0]['after_widget_content'] = '</div><!-- .widget-content -->';
    901905                ob_start();
    902 
    903906                call_user_func_array( 'wp_widget_control', $args );
    904                 $replacements = array(
    905                         '<form method="post">' => '<div class="form">',
    906                         '</form>' => '</div><!-- .form -->',
    907                 );
    908 
    909907                $control_tpl = ob_get_clean();
     908                return $control_tpl;
     909        }
    910910
    911                 $control_tpl = str_replace( array_keys( $replacements ), array_values( $replacements ), $control_tpl );
     911        /**
     912         * Get the widget control markup parts.
     913         *
     914         * @since 4.4.0
     915         * @access public
     916         *
     917         * @param array $args Widget control arguments.
     918         * @return array {
     919         *     @type string $control  Markup for widget control wrapping form.
     920         *     @type string $content  The contents of the widget form itself.
     921         * }
     922         */
     923        public function get_widget_control_parts( $args ) {
     924                $args[0]['before_widget_content'] = '<div class="widget-content">';
     925                $args[0]['after_widget_content'] = '</div><!-- .widget-content -->';
     926                $control_markup = $this->get_widget_control( $args );
     927
     928                $content_start_pos = strpos( $control_markup, $args[0]['before_widget_content'] );
     929                $content_end_pos = strrpos( $control_markup, $args[0]['after_widget_content'] );
     930
     931                $control = substr( $control_markup, 0, $content_start_pos + strlen( $args[0]['before_widget_content'] ) );
     932                $control .= substr( $control_markup, $content_end_pos );
     933                $content = trim( substr(
     934                        $control_markup,
     935                        $content_start_pos + strlen( $args[0]['before_widget_content'] ),
     936                        $content_end_pos - $content_start_pos - strlen( $args[0]['before_widget_content'] )
     937                ) );
    912938
    913                 return $control_tpl;
     939                return compact( 'control', 'content' );
    914940        }
    915941
    916942        /**
  • tests/phpunit/tests/customize/widgets.php

    diff --git tests/phpunit/tests/customize/widgets.php tests/phpunit/tests/customize/widgets.php
    index 9f30b94..50aab18 100644
    class Tests_WP_Customize_Widgets extends WP_UnitTestCase { 
    195195                $unsanitized_from_js = $this->manager->widgets->sanitize_widget_instance( $sanitized_for_js );
    196196                $this->assertEquals( $unsanitized_from_js, $new_categories_instance );
    197197        }
     198
     199        /**
     200         * Get the widget control args for tests.
     201         *
     202         * @return array
     203         */
     204        function get_test_widget_control_args() {
     205                global $wp_registered_widgets;
     206                require_once ABSPATH . '/wp-admin/includes/widgets.php';
     207                $widget_id = 'search-2';
     208                $widget = $wp_registered_widgets[ $widget_id ];
     209                $args = array(
     210                        'widget_id' => $widget['id'],
     211                        'widget_name' => $widget['name'],
     212                );
     213                $args = wp_list_widget_controls_dynamic_sidebar( array( 0 => $args, 1 => $widget['params'][0] ) );
     214                return $args;
     215        }
     216
     217        /**
     218         * @see WP_Customize_Widgets::get_widget_control()
     219         */
     220        function test_get_widget_control() {
     221                $this->do_customize_boot_actions();
     222                $widget_control = $this->manager->widgets->get_widget_control( $this->get_test_widget_control_args() );
     223
     224                $this->assertContains( '<div class="form">', $widget_control );
     225                $this->assertContains( '<div class="widget-content">', $widget_control );
     226                $this->assertContains( '<input type="hidden" name="id_base" class="id_base" value="search"', $widget_control );
     227                $this->assertContains( '<input class="widefat"', $widget_control );
     228        }
     229
     230        /**
     231         * @see WP_Customize_Widgets::get_widget_control_parts()
     232         */
     233        function test_get_widget_control_parts() {
     234                $this->do_customize_boot_actions();
     235                $widget_control_parts = $this->manager->widgets->get_widget_control_parts( $this->get_test_widget_control_args() );
     236                $this->assertArrayHasKey( 'content', $widget_control_parts );
     237                $this->assertArrayHasKey( 'control', $widget_control_parts );
     238
     239                $this->assertContains( '<div class="form">', $widget_control_parts['control'] );
     240                $this->assertContains( '<div class="widget-content">', $widget_control_parts['control'] );
     241                $this->assertContains( '<input type="hidden" name="id_base" class="id_base" value="search"', $widget_control_parts['control'] );
     242                $this->assertNotContains( '<input class="widefat"', $widget_control_parts['control'] );
     243                $this->assertContains( '<input class="widefat"', $widget_control_parts['content'] );
     244        }
     245
     246        /**
     247         * @see WP_Widget_Form_Customize_Control::json()
     248         */
     249        function test_wp_widget_form_customize_control_json() {
     250                $this->do_customize_boot_actions();
     251                $control = $this->manager->get_control( 'widget_search[2]' );
     252                $params = $control->json();
     253
     254                $this->assertEquals( 'widget_form', $params['type'] );
     255                $this->assertRegExp( '#^<li[^>]+>\s+</li>$#', $params['content'] );
     256                $this->assertRegExp( '#^<div[^>]*class=\'widget\'[^>]*#s', $params['widget_control'] );
     257                $this->assertContains( '<div class="widget-content"></div>', $params['widget_control'] );
     258                $this->assertNotContains( '<input class="widefat"', $params['widget_control'] );
     259                $this->assertContains( '<input class="widefat"', $params['widget_content'] );
     260                $this->assertEquals( 'search-2', $params['widget_id'] );
     261                $this->assertEquals( 'search', $params['widget_id_base'] );
     262                $this->assertArrayHasKey( 'sidebar_id', $params );
     263                $this->assertArrayHasKey( 'width', $params );
     264                $this->assertArrayHasKey( 'height', $params );
     265                $this->assertInternalType( 'bool', $params['is_wide'] );
     266
     267        }
    198268}
  • tests/phpunit/tests/widgets.php

    diff --git tests/phpunit/tests/widgets.php tests/phpunit/tests/widgets.php
    index 443d3f3..c0dfe08 100644
    class Tests_Widgets extends WP_UnitTestCase { 
    293293                $this->assertArrayNotHasKey( 2, $option_value );
    294294        }
    295295
     296        /**
     297         * @see wp_widget_control()
     298         */
     299        function test_wp_widget_control() {
     300                global $wp_registered_widgets;
     301
     302                wp_widgets_init();
     303                require_once ABSPATH . '/wp-admin/includes/widgets.php';
     304                $widget_id = 'search-2';
     305                $widget = $wp_registered_widgets[ $widget_id ];
     306                $params = array(
     307                        'widget_id' => $widget['id'],
     308                        'widget_name' => $widget['name'],
     309                );
     310                $args = wp_list_widget_controls_dynamic_sidebar( array( 0 => $params, 1 => $widget['params'][0] ) );
     311
     312                ob_start();
     313                call_user_func_array( 'wp_widget_control', $args );
     314                $control = ob_get_clean();
     315                $this->assertNotEmpty( $control );
     316
     317                $this->assertContains( '<div class="widget-top">', $control );
     318                $this->assertContains( '<div class="widget-title-action">', $control );
     319                $this->assertContains( '<div class="widget-title">', $control );
     320                $this->assertContains( '<form method="post">', $control );
     321                $this->assertContains( '<div class="widget-content">', $control );
     322                $this->assertContains( '<input class="widefat"', $control );
     323                $this->assertContains( '<input type="hidden" name="id_base" class="id_base" value="search"', $control );
     324                $this->assertContains( '<div class="widget-control-actions">', $control );
     325                $this->assertContains( '<div class="alignleft">', $control );
     326                $this->assertContains( 'widget-control-remove', $control );
     327                $this->assertContains( 'widget-control-close', $control );
     328                $this->assertContains( '<div class="alignright">', $control );
     329                $this->assertContains( '<input type="submit" name="savewidget"', $control );
     330
     331                $param_overrides = array(
     332                        'before_form' => '<!-- before_form -->',
     333                        'after_form' => '<!-- after_form -->',
     334                        'before_widget_content' => '<!-- before_widget_content -->',
     335                        'after_widget_content' => '<!-- after_widget_content -->',
     336                );
     337                $params = array_merge( $params, $param_overrides );
     338                $args = wp_list_widget_controls_dynamic_sidebar( array( 0 => $params, 1 => $widget['params'][0] ) );
     339
     340                ob_start();
     341                call_user_func_array( 'wp_widget_control', $args );
     342                $control = ob_get_clean();
     343                $this->assertNotEmpty( $control );
     344                $this->assertNotContains( '<form method="post">', $control );
     345                $this->assertNotContains( '<div class="widget-content">', $control );
     346
     347                foreach ( $param_overrides as $contained ) {
     348                        $this->assertContains( $contained, $control );
     349                }
     350        }
     351
    296352}