Ticket #39693: 39693.diff
File 39693.diff, 16.8 KB (added by , 8 years ago) |
---|
-
src/wp-includes/widgets.php
1107 1107 * 1108 1108 * @param string|bool $theme_changed Whether the theme was changed as a boolean. A value 1109 1109 * of 'customize' defers updates for the Customizer. 1110 * @return array |void1110 * @return array 1111 1111 */ 1112 1112 function retrieve_widgets( $theme_changed = false ) { 1113 1113 global $wp_registered_sidebars, $sidebars_widgets, $wp_registered_widgets; 1114 1114 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' ); 1117 1118 1118 $old_sidebars_widgets = get_theme_mod( 'sidebars_widgets' );1119 1119 if ( is_array( $old_sidebars_widgets ) ) { 1120 1120 // 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']; 1122 1122 1123 1123 if ( 'customize' !== $theme_changed ) { 1124 1124 remove_theme_mod( 'sidebars_widgets' ); 1125 1125 } 1126 } else { 1127 if ( empty( $sidebars_widgets ) ) { 1128 return array(); 1129 } 1126 1130 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'] ); 1131 1132 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 return _wp_remove_unregistered_widgets( $sidebars_widgets, $registered_widgets_ids ); 1136 1139 } 1137 } else { 1138 if ( empty( $sidebars_widgets ) ) 1139 return; 1140 } 1140 1141 1141 unset( $sidebars_widgets['array_version'] ); 1142 // Discard invalid, theme-specific widgets from sidebars. 1143 $sidebars_widgets = _wp_remove_unregistered_widgets( $sidebars_widgets, $registered_widgets_ids ); 1144 $sidebars_widgets = _wp_map_sidebars( $sidebars_widgets ); 1142 1145 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 } 1146 // Find hidden/lost multi-widget instances. 1147 $shown_widgets = call_user_func_array( 'array_merge', array_filter( $sidebars_widgets ) ); 1148 $lost_widgets = array_diff( $registered_widgets_ids, $shown_widgets ); 1149 1150 foreach ( $lost_widgets as $key => $widget_id ) { 1151 $number = preg_replace( '/.+?-([0-9]+)$/', '$1', $widget_id ); 1152 1153 if ( (int) $number < 2 ) { 1154 unset( $lost_widgets[ $key ] ); 1166 1155 } 1156 } 1157 $sidebars_widgets['wp_inactive_widgets'] = array_merge( $lost_widgets, (array) $sidebars_widgets['wp_inactive_widgets'] ); 1158 1159 if ( 'customize' !== $theme_changed ) { 1160 wp_set_sidebars_widgets( $sidebars_widgets ); 1161 } 1162 1163 return $sidebars_widgets; 1164 } 1165 1166 /** 1167 * Compares a list of sidebars with their widgets against a whitelist. 1168 * 1169 * @since 4.9.0 1170 * 1171 * @param array $old_sidebars_widgets List of sidebars and their widget instance IDs. 1172 * @return array Mapped sidebars widgets. 1173 */ 1174 function _wp_map_sidebars( $old_sidebars_widgets ) { 1175 global $wp_registered_sidebars; 1176 1177 $new_sidebars_widgets = array(); 1178 1179 // Short-circuit if there are no sidebars to map. 1180 if ( empty( $old_sidebars_widgets ) ) { 1181 return $new_sidebars_widgets; 1182 } 1167 1183 1168 foreach ( $sidebars_widgets as $val ) { 1169 if ( is_array($val) && ! empty( $val ) ) 1170 $_sidebars_widgets['orphaned_widgets_' . ++$orphaned] = $val; 1184 foreach ( $old_sidebars_widgets as $sidebar => $widgets ) { 1185 if ( 'wp_inactive_widgets' === $sidebar || 'orphaned_widgets' === substr( $sidebar, 0, 16 ) ) { 1186 $new_sidebars_widgets[ $sidebar ] = $widgets; 1187 unset( $old_sidebars_widgets[ $sidebar ] ); 1171 1188 } 1172 1189 } 1173 1190 1174 // discard invalid, theme-specific widgets from sidebars 1175 $shown_widgets = array(); 1191 // If old and new theme have just one sidebar, map it and we're done. 1192 if ( 1 === count( $old_sidebars_widgets ) && 1 === count( $wp_registered_sidebars ) ) { 1193 $new_sidebars_widgets[ key( $wp_registered_sidebars ) ] = array_pop( $old_sidebars_widgets ); 1194 1195 return $new_sidebars_widgets; 1196 } 1176 1197 1177 foreach ( $_sidebars_widgets as $sidebar => $widgets ) { 1178 if ( !is_array($widgets) ) 1179 continue; 1198 // Map locations with the same slug. 1199 $old_sidebars = array_keys( $old_sidebars_widgets ); 1180 1200 1181 $_widgets = array(); 1182 foreach ( $widgets as $widget ) { 1183 if ( isset($wp_registered_widgets[$widget]) ) 1184 $_widgets[] = $widget; 1201 foreach ( $wp_registered_sidebars as $sidebar => $name ) { 1202 if ( in_array( $sidebar, $old_sidebars, true ) ) { 1203 $new_sidebars_widgets[ $sidebar ] = $old_sidebars_widgets[ $sidebar ]; 1204 unset( $old_sidebars_widgets[ $sidebar ] ); 1205 } else { 1206 $new_sidebars_widgets[ $sidebar ] = array(); 1185 1207 } 1208 } 1186 1209 1187 $_sidebars_widgets[$sidebar] = $_widgets; 1188 $shown_widgets = array_merge($shown_widgets, $_widgets); 1210 // If there are no old sidebars left, then we're done. 1211 if ( empty( $old_sidebars_widgets ) ) { 1212 return $new_sidebars_widgets; 1189 1213 } 1190 1214 1191 $sidebars_widgets = $_sidebars_widgets; 1192 unset($_sidebars_widgets, $_widgets); 1215 /* 1216 * If old and new theme both have sidebars that contain phrases 1217 * from within the same group, make an educated guess and map it. 1218 */ 1219 $common_slug_groups = array( 1220 array( 'sidebar', 'primary', 'main', 'right' ), 1221 array( 'second', 'left' ), 1222 array( 'footer', 'bottom' ), 1223 array( 'header', 'top' ), 1224 ); 1225 1226 // Go through each group... 1227 foreach ( $common_slug_groups as $slug_group ) { 1228 1229 // ...and see if any of these slugs... 1230 foreach ( $slug_group as $slug ) { 1231 1232 // ...and any of the new sidebars... 1233 foreach ( $wp_registered_sidebars as $new_sidebar => $args ) { 1234 1235 // ...actually match! 1236 if ( false === stripos( $new_sidebar, $slug ) && false === stripos( $slug, $new_sidebar ) ) { 1237 continue; 1238 } 1193 1239 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; 1240 // Then see if any of the old sidebars... 1241 foreach ( $old_sidebars_widgets as $sidebar => $widgets ) { 1199 1242 1200 $number = preg_replace('/.+?-([0-9]+)$/', '$1', $key); 1243 // ...and any slug in the same group... 1244 foreach ( $slug_group as $slug ) { 1201 1245 1202 if ( 2 > (int) $number ) 1203 continue; 1246 // ... have a match as well. 1247 if ( false === stripos( $sidebar, $slug ) && false === stripos( $slug, $sidebar ) ) { 1248 continue; 1249 } 1250 1251 // Make sure this sidebar wasn't mapped and removed previously. 1252 if ( ! empty( $old_sidebars_widgets[ $sidebar ] ) ) { 1253 1254 // We have a match that can be mapped! 1255 $new_sidebars_widgets[ $new_sidebar ] = $old_sidebars_widgets[ $sidebar ]; 1256 1257 // Remove the mapped sidebar so it can't be mapped again. 1258 unset( $old_sidebars_widgets[ $sidebar ] ); 1259 1260 // Go back and check the next new sidebar. 1261 continue 3; 1262 } 1263 } // endforeach ( $slug_group as $slug ) 1264 } // endforeach ( $old_sidebars_widgets as $sidebar => $menu_id ) 1265 } // endforeach foreach ( $wp_registered_sidebars as $new_sidebar => $name ) 1266 } // endforeach ( $slug_group as $slug ) 1267 } // endforeach ( $common_slug_groups as $slug_group ) 1204 1268 1205 $lost_widgets[] = $key; 1269 $orphaned = 0; 1270 foreach ( $old_sidebars_widgets as $widgets ) { 1271 if ( is_array( $widgets ) && ! empty( $widgets ) ) { 1272 $new_sidebars_widgets[ 'orphaned_widgets_' . ++$orphaned ] = $widgets; 1273 } 1206 1274 } 1207 1275 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 ); 1276 return $new_sidebars_widgets; 1277 } 1278 1279 /** 1280 * Compares a list of sidebars with their widgets against a whitelist. 1281 * 1282 * @since 4.9.0 1283 * 1284 * @param array $sidebars_widgets List of sidebars and their widget instance IDs. 1285 * @param array $whitelist List of widget IDs to compare against. 1286 * @return array Sidebars with whitelisted widgets. 1287 */ 1288 function _wp_remove_unregistered_widgets( $sidebars_widgets, $whitelist ) { 1289 foreach ( $sidebars_widgets as $sidebar => $widgets ) { 1290 if ( is_array( $widgets ) ) { 1291 $sidebars_widgets[ $sidebar ] = array_intersect( $widgets, $whitelist ); 1292 } 1211 1293 } 1212 1294 1213 1295 return $sidebars_widgets; -
tests/phpunit/tests/widgets.php
678 678 679 679 } 680 680 681 function test_retrieve_widgets_with_theme_mod() { 682 global $sidebars_widgets, $_wp_sidebars_widgets; 683 684 wp_widgets_init(); 685 686 register_sidebar( array( 687 'name' => 'Primary Sidebar', 688 'id' => 'sidebar-1', 689 ) ); 690 register_sidebar( array( 691 'name' => 'Content Sidebar', 692 'id' => 'sidebar-2', 693 ) ); 694 register_sidebar( array( 695 'name' => 'Footer Widget Area', 696 'id' => 'sidebar-3', 697 ) ); 698 register_sidebar( array( 'id' => 'wp_inactive_widgets' ) ); 699 700 set_theme_mod( 'sidebars_widgets', array( 701 'time' => time(), 702 'data' => array( 703 'sidebar-1' => array( 'tag_cloud-1' ), 704 'sidebar-2' => array( 'text-1' ), 705 'sidebar-3' => array( 'unregistered_widget-1' ), 706 'fantasy-sidebar' => array( 'archives-2' ), 707 'wp_inactive_widgets' => array(), 708 ), 709 ) ); 710 711 $result = retrieve_widgets( true ); 712 713 $_wp_sidebars_widgets = array(); 714 $this->assertInternalType( 'array', $result ); 715 $this->assertEquals( $result, $sidebars_widgets ); 716 $this->assertContains( 'tag_cloud-1', $sidebars_widgets['sidebar-1'] ); 717 $this->assertContains( 'text-1', $sidebars_widgets['sidebar-2'] ); 718 $this->assertContains( 'archives-2', $sidebars_widgets['orphaned_widgets_1'] ); 719 720 // Unregistered widget should be filtered out. 721 $this->assertEmpty( $sidebars_widgets['sidebar-3'] ); 722 723 // 6 default widgets - 1 active text widget = 5. 724 $this->assertCount( 5, $sidebars_widgets['wp_inactive_widgets'] ); 725 726 // Theme mode with previous widgets was removed. 727 $this->assertFalse( get_theme_mod( 'sidebars_widgets' ) ); 728 729 // Sidebar_widgets option was updated. 730 $this->assertEquals( $sidebars_widgets, wp_get_sidebars_widgets() ); 731 } 732 733 function test_retrieve_widgets_with_sidebars_widgets_matching_registered_sidebars() { 734 global $sidebars_widgets; 735 736 wp_widgets_init(); 737 738 register_sidebar( array( 739 'name' => 'Primary Sidebar', 740 'id' => 'sidebar-1', 741 ) ); 742 register_sidebar( array( 743 'name' => 'Content Sidebar', 744 'id' => 'sidebar-2', 745 ) ); 746 register_sidebar( array( 747 'name' => 'Footer Widget Area', 748 'id' => 'sidebar-3', 749 ) ); 750 register_sidebar( array( 'id' => 'wp_inactive_widgets' ) ); 751 752 $sidebars_widgets = array( 753 'sidebar-1' => array( 'tag_cloud-1' ), 754 'sidebar-2' => array( 'text-1' ), 755 'sidebar-3' => array( 'custom_widget-1' ), 756 'wp_inactive_widgets' => array(), 757 ); 758 759 $result = retrieve_widgets( true ); 760 761 // $sidebars_widgets matches registered sidebars. 762 $this->assertInternalType( 'array', $result ); 763 $this->assertEquals( $result, $sidebars_widgets ); 764 $this->assertContains( 'tag_cloud-1', $sidebars_widgets['sidebar-1'] ); 765 $this->assertContains( 'text-1', $sidebars_widgets['sidebar-2'] ); 766 767 // Invalid widget removed, even when $sidebars_widgets matches registered sidebars. 768 $this->assertEmpty( $sidebars_widgets['sidebar-3'] ); 769 770 // No lost widgets when $sidebars_widgets matches registered sidebars. 771 $this->assertEmpty( $sidebars_widgets['wp_inactive_widgets'] ); 772 } 773 774 function test_retrieve_widgets_with_sidebars_widgets_not_matching_registered_sidebars() { 775 global $sidebars_widgets, $_wp_sidebars_widgets; 776 777 wp_widgets_init(); 778 779 register_sidebar( array( 780 'name' => 'Primary Sidebar', 781 'id' => 'sidebar-1', 782 ) ); 783 register_sidebar( array( 784 'name' => 'Content Sidebar', 785 'id' => 'sidebar-2', 786 ) ); 787 register_sidebar( array( 788 'name' => 'Footer Widget Area', 789 'id' => 'sidebar-3', 790 ) ); 791 register_sidebar( array( 'id' => 'wp_inactive_widgets' ) ); 792 793 $sidebars_widgets = array( 794 'sidebar-1' => array( 'tag_cloud-1' ), 795 'sidebar-2' => array( 'text-1' ), 796 'fantasy-sidebar' => array( 'unregistered_widget-1' ), 797 'wp_inactive_widgets' => array(), 798 ); 799 800 // Theme changed. 801 $result = retrieve_widgets( true ); 802 803 $_wp_sidebars_widgets = array(); 804 $this->assertInternalType( 'array', $result ); 805 $this->assertEquals( $result, $sidebars_widgets ); 806 807 // Current theme doesn't have a fantasy-sidebar. 808 $this->assertArrayNotHasKey( 'fantasy-sidebar', $sidebars_widgets ); 809 $this->assertArrayHasKey( 'sidebar-3', $sidebars_widgets ); 810 811 $this->assertContains( 'tag_cloud-1', $sidebars_widgets['sidebar-1'] ); 812 $this->assertContains( 'text-1', $sidebars_widgets['sidebar-2'] ); 813 814 // We should not have orphaned widgets, because widget was not registered. 815 $this->assertArrayNotHasKey( 'orphaned_widgets_1', $sidebars_widgets ); 816 817 // 6 default widgets. 818 $this->assertCount( 6, $sidebars_widgets['wp_inactive_widgets'] ); 819 820 // Sidebar_widgets option was updated. 821 $this->assertEquals( $sidebars_widgets, wp_get_sidebars_widgets() ); 822 823 // Reset. 824 $sidebars_widgets = array( 825 'sidebar-1' => array( 'tag_cloud-1' ), 826 'sidebar-2' => array( 'text-1' ), 827 'fantasy-sidebar' => array( 'archives-2' ), 828 'wp_inactive_widgets' => array(), 829 ); 830 831 // Theme did not change. 832 $result = retrieve_widgets(); 833 834 $_wp_sidebars_widgets = array(); 835 $this->assertInternalType( 'array', $result ); 836 $this->assertEquals( $result, $sidebars_widgets ); 837 838 // This sidebar is not registered anymore. 839 $this->assertArrayNotHasKey( 'fantasy-sidebar', $sidebars_widgets ); 840 $this->assertArrayHasKey( 'sidebar-3', $sidebars_widgets ); 841 842 // archives-2 ends up as an orphan because of the above behavior. 843 $this->assertContains( 'archives-2', $sidebars_widgets['orphaned_widgets_1'] ); 844 $this->assertContains( 'tag_cloud-1', $sidebars_widgets['sidebar-1'] ); 845 $this->assertContains( 'text-1', $sidebars_widgets['sidebar-2'] ); 846 847 // 6 default widgets - 1 active text widget = 5. 848 $this->assertCount( 5, $sidebars_widgets['wp_inactive_widgets'] ); 849 850 // Sidebar_widgets option was updated. 851 $this->assertEquals( $sidebars_widgets, wp_get_sidebars_widgets() ); 852 } 853 854 function test_retrieve_widgets_for_customizer() { 855 global $sidebars_widgets, $_wp_sidebars_widgets; 856 857 wp_widgets_init(); 858 859 register_sidebar( array( 860 'name' => 'Primary Sidebar', 861 'id' => 'sidebar-1', 862 ) ); 863 register_sidebar( array( 864 'name' => 'Content Sidebar', 865 'id' => 'sidebar-2', 866 ) ); 867 register_sidebar( array( 868 'name' => 'Footer Widget Area', 869 'id' => 'sidebar-3', 870 ) ); 871 register_sidebar( array( 'id' => 'wp_inactive_widgets' ) ); 872 873 $old_sidebars_widgets = array( 874 'time' => time(), 875 'data' => array( 876 'sidebar-1' => array( 'tag_cloud-1' ), 877 'sidebar-2' => array( 'text-1' ), 878 'sidebar-3' => array( 'unregistered_widget-1' ), 879 'fantasy-sidebar' => array( 'archives-2' ), 880 'wp_inactive_widgets' => array(), 881 ), 882 ); 883 set_theme_mod( 'sidebars_widgets', $old_sidebars_widgets ); 884 885 $result = retrieve_widgets( 'customize' ); 886 887 $_wp_sidebars_widgets = array(); 888 $this->assertInternalType( 'array', $result ); 889 $this->assertEquals( $result, $sidebars_widgets ); 890 $this->assertContains( 'tag_cloud-1', $sidebars_widgets['sidebar-1'] ); 891 $this->assertContains( 'text-1', $sidebars_widgets['sidebar-2'] ); 892 $this->assertContains( 'archives-2', $sidebars_widgets['orphaned_widgets_1'] ); 893 $this->assertEmpty( $sidebars_widgets['sidebar-3'] ); 894 $this->assertCount( 5, $sidebars_widgets['wp_inactive_widgets'] ); 895 896 // Theme mod with previous widgets was not removed. 897 $this->assertEqualSets( $old_sidebars_widgets, get_theme_mod( 'sidebars_widgets' ) ); 898 899 // Sidebar_widgets option was not updated. 900 $this->assertNotEquals( $sidebars_widgets, wp_get_sidebars_widgets() ); 901 } 681 902 }