WordPress.org

Make WordPress Core

Ticket #39693: 39693.4.diff

File 39693.4.diff, 17.4 KB (added by obenland, 3 months ago)
  • src/wp-includes/theme.php

     
    679679function switch_theme( $stylesheet ) { 
    680680        global $wp_theme_directories, $wp_customize, $sidebars_widgets; 
    681681 
    682         $_sidebars_widgets = null; 
    683         if ( 'wp_ajax_customize_save' === current_action() ) { 
    684                 $_sidebars_widgets = $wp_customize->post_value( $wp_customize->get_setting( 'old_sidebars_widgets_data' ) ); 
    685         } elseif ( is_array( $sidebars_widgets ) ) { 
    686                 $_sidebars_widgets = $sidebars_widgets; 
    687         } 
    688  
    689         if ( is_array( $_sidebars_widgets ) ) { 
    690                 set_theme_mod( 'sidebars_widgets', array( 'time' => time(), 'data' => $_sidebars_widgets ) ); 
    691         } 
    692  
    693682        $nav_menu_locations = get_theme_mod( 'nav_menu_locations' ); 
    694683        add_option( 'theme_switch_menu_locations', $nav_menu_locations ); 
    695684 
     
    716705 
    717706        update_option( 'current_theme', $new_name ); 
    718707 
     708        $_sidebars_widgets = null; 
     709        if ( 'wp_ajax_customize_save' === current_action() ) { 
     710                $_sidebars_widgets = $wp_customize->post_value( $wp_customize->get_setting( 'old_sidebars_widgets_data' ) ); 
     711        } elseif ( is_array( $sidebars_widgets ) ) { 
     712                $_sidebars_widgets = $sidebars_widgets; 
     713        } 
     714 
     715        if ( is_array( $_sidebars_widgets ) ) { 
     716                set_theme_mod( 'sidebars_widgets', array( 'time' => time(), 'data' => $_sidebars_widgets ) ); 
     717        } 
     718 
    719719        // Migrate from the old mods_{name} option to theme_mods_{slug}. 
    720720        if ( is_admin() && false === get_option( 'theme_mods_' . $stylesheet ) ) { 
    721721                $default_theme_mods = (array) get_option( 'mods_' . $new_name ); 
  • src/wp-includes/widgets.php

     
    11071107 * 
    11081108 * @param string|bool $theme_changed Whether the theme was changed as a boolean. A value 
    11091109 *                                   of 'customize' defers updates for the Customizer. 
    1110  * @return array|void 
     1110 * @return array 
    11111111 */ 
    11121112function retrieve_widgets( $theme_changed = false ) { 
    11131113        global $wp_registered_sidebars, $sidebars_widgets, $wp_registered_widgets; 
    11141114 
    1115         $registered_sidebar_keys = array_keys( $wp_registered_sidebars ); 
    1116         $orphaned = 0; 
     1115        $registered_sidebars_keys = array_keys( $wp_registered_sidebars ); 
     1116        $registered_widgets_ids   = array_keys( $wp_registered_widgets ); 
     1117        $old_sidebars_widgets     = get_theme_mod( 'sidebars_widgets' ); 
    11171118 
    1118         $old_sidebars_widgets = get_theme_mod( 'sidebars_widgets' ); 
    11191119        if ( is_array( $old_sidebars_widgets ) ) { 
    11201120                // time() that sidebars were stored is in $old_sidebars_widgets['time'] 
    1121                 $_sidebars_widgets = $old_sidebars_widgets['data']; 
     1121                $sidebars_widgets = $old_sidebars_widgets['data']; 
    11221122 
    11231123                if ( 'customize' !== $theme_changed ) { 
    11241124                        remove_theme_mod( 'sidebars_widgets' ); 
    11251125                } 
     1126        } else { 
     1127                if ( empty( $sidebars_widgets ) ) { 
     1128                        return array(); 
     1129                } 
    11261130 
    1127                 foreach ( $_sidebars_widgets as $sidebar => $widgets ) { 
    1128                         if ( 'wp_inactive_widgets' === $sidebar || 'orphaned_widgets' === substr( $sidebar, 0, 16 ) ) { 
    1129                                 continue; 
    1130                         } 
     1131                unset( $sidebars_widgets['array_version'] ); 
    11311132 
    1132                         if ( !in_array( $sidebar, $registered_sidebar_keys ) ) { 
    1133                                 $_sidebars_widgets['orphaned_widgets_' . ++$orphaned] = $widgets; 
    1134                                 unset( $_sidebars_widgets[$sidebar] ); 
    1135                         } 
     1133                $sidebars_widgets_keys = array_keys( $sidebars_widgets ); 
     1134                sort( $sidebars_widgets_keys ); 
     1135                sort( $registered_sidebars_keys ); 
     1136 
     1137                if ( $sidebars_widgets_keys == $registered_sidebars_keys ) { 
     1138                        $sidebars_widgets = _wp_remove_unregistered_widgets( $sidebars_widgets, $registered_widgets_ids ); 
     1139 
     1140                        return $sidebars_widgets; 
    11361141                } 
    1137         } else { 
    1138                 if ( empty( $sidebars_widgets ) ) 
    1139                         return; 
     1142        } 
    11401143 
    1141                 unset( $sidebars_widgets['array_version'] ); 
     1144        // Discard invalid, theme-specific widgets from sidebars. 
     1145        $sidebars_widgets = _wp_remove_unregistered_widgets( $sidebars_widgets, $registered_widgets_ids ); 
     1146        $sidebars_widgets = _wp_map_sidebars( $sidebars_widgets ); 
    11421147 
    1143                 $old = array_keys($sidebars_widgets); 
    1144                 sort($old); 
    1145                 sort($registered_sidebar_keys); 
    1146  
    1147                 if ( $old == $registered_sidebar_keys ) 
    1148                         return; 
    1149  
    1150                 $_sidebars_widgets = array( 
    1151                         'wp_inactive_widgets' => !empty( $sidebars_widgets['wp_inactive_widgets'] ) ? $sidebars_widgets['wp_inactive_widgets'] : array() 
    1152                 ); 
    1153  
    1154                 unset( $sidebars_widgets['wp_inactive_widgets'] ); 
    1155  
    1156                 foreach ( $wp_registered_sidebars as $id => $settings ) { 
    1157                         if ( $theme_changed ) { 
    1158                                 $_sidebars_widgets[$id] = array_shift( $sidebars_widgets ); 
    1159                         } else { 
    1160                                 // no theme change, grab only sidebars that are currently registered 
    1161                                 if ( isset( $sidebars_widgets[$id] ) ) { 
    1162                                         $_sidebars_widgets[$id] = $sidebars_widgets[$id]; 
    1163                                         unset( $sidebars_widgets[$id] ); 
    1164                                 } 
    1165                         } 
     1148        // Find hidden/lost multi-widget instances. 
     1149        $shown_widgets = call_user_func_array( 'array_merge', $sidebars_widgets ); 
     1150        $lost_widgets  = array_diff( $registered_widgets_ids, $shown_widgets ); 
     1151 
     1152        foreach ( $lost_widgets as $key => $widget_id ) { 
     1153                $number = preg_replace( '/.+?-([0-9]+)$/', '$1', $widget_id ); 
     1154 
     1155                if ( (int) $number < 2 ) { 
     1156                        unset( $lost_widgets[ $key ] ); 
    11661157                } 
     1158        } 
     1159        $sidebars_widgets['wp_inactive_widgets'] = array_merge( $lost_widgets, (array) $sidebars_widgets['wp_inactive_widgets'] ); 
     1160 
     1161        if ( 'customize' !== $theme_changed ) { 
     1162                wp_set_sidebars_widgets( $sidebars_widgets ); 
     1163        } 
     1164 
     1165        return $sidebars_widgets; 
     1166} 
    11671167 
    1168                 foreach ( $sidebars_widgets as $val ) { 
    1169                         if ( is_array($val) && ! empty( $val ) ) 
    1170                                 $_sidebars_widgets['orphaned_widgets_' . ++$orphaned] = $val; 
     1168/** 
     1169 * Compares a list of sidebars with their widgets against a whitelist. 
     1170 * 
     1171 * @since 4.9.0 
     1172 * 
     1173 * @param array $old_sidebars_widgets List of sidebars and their widget instance IDs. 
     1174 * @return array Mapped sidebars widgets. 
     1175 */ 
     1176function _wp_map_sidebars( $old_sidebars_widgets ) { 
     1177        global $wp_registered_sidebars; 
     1178 
     1179        $new_sidebars_widgets = array(); 
     1180 
     1181        // Short-circuit if there are no sidebars to map. 
     1182        if ( ! is_array( $old_sidebars_widgets ) || empty( $old_sidebars_widgets ) ) { 
     1183                return $new_sidebars_widgets; 
     1184        } 
     1185 
     1186        foreach ( $old_sidebars_widgets as $sidebar => $widgets ) { 
     1187                if ( 'wp_inactive_widgets' === $sidebar || 'orphaned_widgets' === substr( $sidebar, 0, 16 ) ) { 
     1188                        $new_sidebars_widgets[ $sidebar ] = (array) $widgets; 
     1189                        unset( $old_sidebars_widgets[ $sidebar ] ); 
    11711190                } 
    11721191        } 
    11731192 
    1174         // discard invalid, theme-specific widgets from sidebars 
    1175         $shown_widgets = array(); 
     1193        // If old and new theme have just one sidebar, map it and we're done. 
     1194        if ( 1 === count( $old_sidebars_widgets ) && 1 === count( $wp_registered_sidebars ) ) { 
     1195                $new_sidebars_widgets[ key( $wp_registered_sidebars ) ] = array_pop( $old_sidebars_widgets ); 
     1196 
     1197                return $new_sidebars_widgets; 
     1198        } 
    11761199 
    1177         foreach ( $_sidebars_widgets as $sidebar => $widgets ) { 
    1178                 if ( !is_array($widgets) ) 
    1179                         continue; 
     1200        // Map locations with the same slug. 
     1201        $old_sidebars = array_keys( $old_sidebars_widgets ); 
    11801202 
    1181                 $_widgets = array(); 
    1182                 foreach ( $widgets as $widget ) { 
    1183                         if ( isset($wp_registered_widgets[$widget]) ) 
    1184                                 $_widgets[] = $widget; 
     1203        foreach ( $wp_registered_sidebars as $sidebar => $name ) { 
     1204                if ( in_array( $sidebar, $old_sidebars, true ) ) { 
     1205                        $new_sidebars_widgets[ $sidebar ] = $old_sidebars_widgets[ $sidebar ]; 
     1206                        unset( $old_sidebars_widgets[ $sidebar ] ); 
     1207                } else { 
     1208                        $new_sidebars_widgets[ $sidebar ] = array(); 
    11851209                } 
     1210        } 
    11861211 
    1187                 $_sidebars_widgets[$sidebar] = $_widgets; 
    1188                 $shown_widgets = array_merge($shown_widgets, $_widgets); 
     1212        // If there are no old sidebars left, then we're done. 
     1213        if ( empty( $old_sidebars_widgets ) ) { 
     1214                return $new_sidebars_widgets; 
    11891215        } 
    11901216 
    1191         $sidebars_widgets = $_sidebars_widgets; 
    1192         unset($_sidebars_widgets, $_widgets); 
     1217        /* 
     1218         * If old and new theme both have sidebars that contain phrases 
     1219         * from within the same group, make an educated guess and map it. 
     1220         */ 
     1221        $common_slug_groups = array( 
     1222                array( 'sidebar', 'primary', 'main', 'right' ), 
     1223                array( 'second', 'left' ), 
     1224                array( 'footer', 'bottom' ), 
     1225                array( 'header', 'top' ), 
     1226        ); 
     1227 
     1228        // Go through each group... 
     1229        foreach ( $common_slug_groups as $slug_group ) { 
     1230 
     1231                // ...and see if any of these slugs... 
     1232                foreach ( $slug_group as $slug ) { 
     1233 
     1234                        // ...and any of the new sidebars... 
     1235                        foreach ( $wp_registered_sidebars as $new_sidebar => $args ) { 
     1236 
     1237                                // ...actually match! 
     1238                                if ( false === stripos( $new_sidebar, $slug ) && false === stripos( $slug, $new_sidebar ) ) { 
     1239                                        continue; 
     1240                                } 
    11931241 
    1194         // find hidden/lost multi-widget instances 
    1195         $lost_widgets = array(); 
    1196         foreach ( $wp_registered_widgets as $key => $val ) { 
    1197                 if ( in_array($key, $shown_widgets, true) ) 
    1198                         continue; 
     1242                                // Then see if any of the old sidebars... 
     1243                                foreach ( $old_sidebars_widgets as $sidebar => $widgets ) { 
    11991244 
    1200                 $number = preg_replace('/.+?-([0-9]+)$/', '$1', $key); 
     1245                                        // ...and any slug in the same group... 
     1246                                        foreach ( $slug_group as $slug ) { 
    12011247 
    1202                 if ( 2 > (int) $number ) 
    1203                         continue; 
     1248                                                // ... have a match as well. 
     1249                                                if ( false === stripos( $sidebar, $slug ) && false === stripos( $slug, $sidebar ) ) { 
     1250                                                        continue; 
     1251                                                } 
     1252 
     1253                                                // Make sure this sidebar wasn't mapped and removed previously. 
     1254                                                if ( ! empty( $old_sidebars_widgets[ $sidebar ] ) ) { 
     1255 
     1256                                                        // We have a match that can be mapped! 
     1257                                                        $new_sidebars_widgets[ $new_sidebar ] = array_merge( $new_sidebars_widgets[ $new_sidebar ], $old_sidebars_widgets[ $sidebar ] ); 
     1258 
     1259                                                        // Remove the mapped sidebar so it can't be mapped again. 
     1260                                                        unset( $old_sidebars_widgets[ $sidebar ] ); 
     1261 
     1262                                                        // Go back and check the next new sidebar. 
     1263                                                        continue 3; 
     1264                                                } 
     1265                                        } // endforeach ( $slug_group as $slug ) 
     1266                                } // endforeach ( $old_sidebars_widgets as $sidebar => $menu_id ) 
     1267                        } // endforeach foreach ( $wp_registered_sidebars as $new_sidebar => $name ) 
     1268                } // endforeach ( $slug_group as $slug ) 
     1269        } // endforeach ( $common_slug_groups as $slug_group ) 
    12041270 
    1205                 $lost_widgets[] = $key; 
     1271        $orphaned = 0; 
     1272        foreach ( $old_sidebars_widgets as $widgets ) { 
     1273                if ( is_array( $widgets ) && ! empty( $widgets ) ) { 
     1274                        $new_sidebars_widgets[ 'orphaned_widgets_' . ++$orphaned ] = $widgets; 
     1275                } 
    12061276        } 
    12071277 
    1208         $sidebars_widgets['wp_inactive_widgets'] = array_merge($lost_widgets, (array) $sidebars_widgets['wp_inactive_widgets']); 
    1209         if ( 'customize' !== $theme_changed ) { 
    1210                 wp_set_sidebars_widgets( $sidebars_widgets ); 
     1278        return $new_sidebars_widgets; 
     1279} 
     1280 
     1281/** 
     1282 * Compares a list of sidebars with their widgets against a whitelist. 
     1283 * 
     1284 * @since 4.9.0 
     1285 * 
     1286 * @param array $sidebars_widgets List of sidebars and their widget instance IDs. 
     1287 * @param array $whitelist        List of widget IDs to compare against. 
     1288 * @return array Sidebars with whitelisted widgets. 
     1289 */ 
     1290function _wp_remove_unregistered_widgets( $sidebars_widgets, $whitelist ) { 
     1291        foreach ( $sidebars_widgets as $sidebar => $widgets ) { 
     1292                if ( is_array( $widgets ) ) { 
     1293                        $sidebars_widgets[ $sidebar ] = array_intersect( $widgets, $whitelist ); 
     1294                } 
    12111295        } 
    12121296 
    12131297        return $sidebars_widgets; 
  • tests/phpunit/tests/widgets.php

     
    715715 
    716716                $_wp_sidebars_widgets = array(); 
    717717                $this->assertInternalType( 'array', $result ); 
    718                 $this->assertNotEmpty( $result ); 
     718                $this->assertEquals( $result, $sidebars_widgets ); 
    719719 
    720720                foreach ( $sidebars_widgets as $widgets ) { 
    721721                        $this->assertInternalType( 'array', $widgets ); 
     
    764764                $result = retrieve_widgets( true ); 
    765765 
    766766                // $sidebars_widgets matches registered sidebars. 
    767                 $this->assertNull( $result ); 
     767                $this->assertInternalType( 'array', $result ); 
     768                $this->assertEquals( $result, $sidebars_widgets ); 
    768769 
    769770                foreach ( $sidebars_widgets as $widgets ) { 
    770771                        $this->assertInternalType( 'array', $widgets ); 
     
    773774                $this->assertContains( 'tag_cloud-1', $sidebars_widgets['sidebar-1'] ); 
    774775                $this->assertContains( 'text-1', $sidebars_widgets['sidebar-2'] ); 
    775776 
    776                 // No widget validity check when $sidebars_widgets matches registered sidebars. 
    777                 $this->assertContains( 'custom_widget-1', $sidebars_widgets['sidebar-3'] ); 
     777                // Invalid widget removed, even when $sidebars_widgets matches registered sidebars. 
     778                $this->assertEmpty( $sidebars_widgets['sidebar-3'] ); 
    778779 
    779780                // No lost widgets when $sidebars_widgets matches registered sidebars. 
    780781                $this->assertEmpty( $sidebars_widgets['wp_inactive_widgets'] ); 
     
    803804 
    804805                $_wp_sidebars_widgets = array(); 
    805806                $this->assertInternalType( 'array', $result ); 
    806                 $this->assertNotEmpty( $result ); 
     807                $this->assertEquals( $result, $sidebars_widgets ); 
    807808 
    808809                foreach ( $sidebars_widgets as $widgets ) { 
    809810                        $this->assertInternalType( 'array', $widgets ); 
     
    846847 
    847848                $_wp_sidebars_widgets = array(); 
    848849                $this->assertInternalType( 'array', $result ); 
    849                 $this->assertNotEmpty( $result ); 
     850                $this->assertEquals( $result, $sidebars_widgets ); 
    850851 
    851852                foreach ( $sidebars_widgets as $widgets ) { 
    852853                        $this->assertInternalType( 'array', $widgets ); 
    853854                } 
    854855 
    855                 /* 
    856                  * Only returns intersection of registered sidebars and saved sidebars, 
    857                  * so neither fantasy-sidebar nor sidebar-3 will make the cut. 
    858                  */ 
     856                // This sidebar is not registered anymore. 
    859857                $this->assertArrayNotHasKey( 'fantasy', $sidebars_widgets ); 
    860                 $this->assertArrayNotHasKey( 'sidebar-3', $sidebars_widgets ); 
     858                $this->assertArrayHasKey( 'sidebar-3', $sidebars_widgets ); 
    861859 
    862860                // archives-2 ends up as an orphan because of the above behavior. 
    863861                $this->assertContains( 'archives-2', $sidebars_widgets['orphaned_widgets_1'] ); 
     
    904902 
    905903                $_wp_sidebars_widgets = array(); 
    906904                $this->assertInternalType( 'array', $result ); 
    907                 $this->assertNotEmpty( $result ); 
     905                $this->assertEquals( $result, $sidebars_widgets ); 
    908906 
    909907                foreach ( $sidebars_widgets as $widgets ) { 
    910908                        $this->assertInternalType( 'array', $widgets ); 
     
    929927                // Sidebar_widgets option was not updated. 
    930928                $this->assertNotEquals( $sidebars_widgets, wp_get_sidebars_widgets() ); 
    931929        } 
     930 
     931        /** 
     932         * 
     933         * 
     934         * @covers _wp_remove_unregistered_widgets() 
     935         */ 
     936        function test__wp_remove_unregistered_widgets() { 
     937                $widgets = array( 
     938                        'sidebar-1' => array( 'tag_cloud-1' ), 
     939                        'sidebar-2' => array( 'text-1' ), 
     940                        'fantasy'   => array( 'archives-2' ), 
     941                        'wp_inactive_widgets' => array(), 
     942                        'array_version' => 3, 
     943                ); 
     944 
     945                $whitelist = array( 'tag_cloud-1', 'text-1' ); 
     946 
     947                $filtered_widgets = _wp_remove_unregistered_widgets( $widgets, $whitelist ); 
     948 
     949                $this->assertInternalType( 'array', $filtered_widgets ); 
     950                $this->assertArrayHasKey( 'fantasy', $filtered_widgets ); 
     951                $this->assertEmpty( $filtered_widgets['fantasy'] ); 
     952                $this->assertArrayHasKey( 'array_version', $filtered_widgets ); 
     953                $this->assertEquals( 3, $filtered_widgets['array_version'] ); 
     954                $this->assertInternalType( 'integer', $filtered_widgets['array_version'] ); 
     955        } 
     956 
     957        /** 
     958         * _wp_map_sidebars Tests. 
     959         */ 
     960 
     961        /** 
     962         * Two themes with one sidebar each should just map, switching to a theme not previously-active. 
     963         * 
     964         * @covers _wp_map_sidebars() 
     965         */ 
     966        function test_one_sidebar_each() { 
     967                $this->register_sidebars( array( 'primary' ) ); 
     968                $prev_theme_sidebars = array( 
     969                        'unique-slug' => 1, 
     970                ); 
     971 
     972                $new_next_theme_sidebars = _wp_map_sidebars( $prev_theme_sidebars ); 
     973 
     974                $expected_sidebars = array( 
     975                        'primary' => 1, 
     976                ); 
     977                $this->assertEquals( $expected_sidebars, $new_next_theme_sidebars ); 
     978        } 
     979 
     980        /** 
     981         * Sidebars with the same name should map, switching to a theme not previously-active. 
     982         * 
     983         * @covers _wp_map_sidebars() 
     984         */ 
     985        function test_sidebars_with_same_slug() { 
     986                $this->register_sidebars( array( 'primary', 'secondary' ) ); 
     987                $prev_theme_sidebars = array( 
     988                        'primary' => 1, 
     989                        'secondary' => 2, 
     990                ); 
     991 
     992                $new_next_theme_sidebars = _wp_map_sidebars( $prev_theme_sidebars ); 
     993 
     994                $this->assertEquals( $prev_theme_sidebars, $new_next_theme_sidebars ); 
     995        } 
     996 
     997        /** 
     998         * Make educated guesses on theme sidebars. 
     999         * 
     1000         * @covers _wp_map_sidebars() 
     1001         */ 
     1002        function test_sidebar_guessing() { 
     1003                $this->register_sidebars( array( 'primary', 'secondary' ) ); 
     1004 
     1005                $prev_theme_sidebars = array( 
     1006                        'header' => array(), 
     1007                        'footer' => array(), 
     1008                ); 
     1009 
     1010                $new_next_theme_sidebars = _wp_map_sidebars( $prev_theme_sidebars ); 
     1011 
     1012                $expected_sidebars = array( 
     1013                        'primary' => array(), 
     1014                        'secondary' => array(), 
     1015                ); 
     1016                $this->assertEquals( $expected_sidebars, $new_next_theme_sidebars ); 
     1017        } 
     1018 
     1019        /** 
     1020         * Make sure two sidebars that fall in the same group don't get the same menu assigned. 
     1021         * 
     1022         * @covers _wp_map_sidebars() 
     1023         */ 
     1024        function test_sidebar_guessing_one_menu_per_group() { 
     1025                $this->register_sidebars( array( 'primary' ) ); 
     1026                $prev_theme_sidebars = array( 
     1027                        'top-menu' => array(), 
     1028                        'secondary' => array(), 
     1029                ); 
     1030 
     1031                $new_next_theme_sidebars = _wp_map_sidebars( $prev_theme_sidebars ); 
     1032 
     1033                $expected_sidebars = array( 
     1034                        'main' => array(), 
     1035                ); 
     1036                $this->assertEqualSets( $expected_sidebars, $new_next_theme_sidebars ); 
     1037        } 
     1038 
     1039        /** 
     1040         * Make sure two sidebars that fall in the same group get menus assigned from the same group. 
     1041         * 
     1042         * @covers _wp_map_sidebars() 
     1043         */ 
     1044        function test_sidebar_guessing_one_menu_per_sidebar() { 
     1045                $this->register_sidebars( array( 'primary', 'main' ) ); 
     1046 
     1047                $prev_theme_sidebars = array( 
     1048                        'navigation-menu' => array(), 
     1049                        'top-menu' => array(), 
     1050                ); 
     1051 
     1052                $new_next_theme_sidebars = _wp_map_sidebars( $prev_theme_sidebars ); 
     1053 
     1054                $expected_sidebars = array( 
     1055                        'main' => array(), 
     1056                        'primary' => array(), 
     1057                ); 
     1058                $this->assertEquals( $expected_sidebars, $new_next_theme_sidebars ); 
     1059        } 
    9321060}