Ticket #40527: 40527.diff
File 40527.diff, 20.5 KB (added by , 7 years ago) |
---|
-
src/wp-includes/class-wp-customize-changeset.php
1 <?php 2 /** 3 * Class file for WP_Customize_Changeset 4 * 5 * @package WordPress 6 * @subpackage Customize 7 * @since 4.9.0 8 */ 9 10 /** 11 * Representation of a Customize Changeset. 12 */ 13 class WP_Customize_Changeset { 14 /** 15 * Changeset UUID, the post_name for the customize_changeset post containing the customized state. 16 * 17 * @since 4.9.0 18 * @var string 19 */ 20 protected $uuid; 21 22 /** 23 * Changeset post ID. 24 * 25 * @since 4.9.0 26 * @var int 27 */ 28 protected $post_id; 29 30 /** 31 * Retrieve a WP_Customize_Changeset instance from a changeset UUID. 32 * 33 * Defers to {@see WP_Customize_Changeset::from_post()} if a post for the UUID exists. 34 * 35 * @since 4.9.0 36 * 37 * @param string $uuid UUID. 38 * @return WP_Customize_Changeset Changeset instance. 39 */ 40 public static function from_uuid( $uuid ) { 41 $cache_group = 'customize_changeset_post'; 42 43 $post_id = wp_cache_get( $uuid, $cache_group ); 44 45 if ( $post_id && 'customize_changeset' === get_post_type( $post_id ) ) { 46 return WP_Customize_Changeset::from_post( $post_id ); 47 } 48 49 // The full post object is being retrieved so it's cached. 50 $query = new WP_Query( array( 51 'post_type' => 'customize_changeset', 52 'post_status' => get_post_stati(), 53 'name' => $uuid, 54 'posts_per_page' => 1, 55 'no_found_rows' => true, 56 'cache_results' => true, 57 'update_post_meta_cache' => false, 58 'update_post_term_cache' => false, 59 'lazy_load_term_meta' => false, 60 ) ); 61 62 if ( empty( $query->posts ) ) { 63 $instance = new WP_Customize_Changeset(); 64 $instance->set_uuid( $uuid ); 65 return $instance; 66 } 67 68 $post_id = $query->posts[0]->ID; 69 wp_cache_set( $uuid, $post_id, $cache_group ); 70 71 return WP_Customize_Changeset::from_post( $post_id ); 72 } 73 74 /** 75 * Retrieve a WP_Customize_Changeset instance from a post. 76 * 77 * @since 4.9.0 78 * 79 * @param int|WP_Post $post Post ID or object. 80 * @return WP_Customize_Changeset Changeset instance. 81 */ 82 public static function from_post( $post ) { 83 $instance = new WP_Customize_Changeset(); 84 85 $post = get_post( $post ); 86 87 // @todo Return null instead if post doesn't exist? 88 if ( $post instanceof WP_Post ) { 89 $instance->parse_post( $post ); 90 } 91 92 return $instance; 93 } 94 95 /** 96 * Populate instance properties from a post object. 97 * 98 * @since 4.9.0 99 * 100 * @param WP_Post $post Post object. 101 */ 102 public function parse_post( $post ) { 103 $this->post_id = $post->ID; 104 $this->uuid = $post->post_name; 105 } 106 107 /** 108 * Set the instance UUID. 109 * 110 * @param string $uuid UUID. 111 */ 112 public function set_uuid( $uuid ) { 113 $this->uuid = $uuid; 114 } 115 116 /** 117 * Get the changeset UUID. 118 * 119 * @since 4.9.0 120 * 121 * @return string UUID. 122 */ 123 public function get_uuid() { 124 return $this->uuid; 125 } 126 127 /** 128 * Get the changeset post ID. 129 * 130 * @return int 131 */ 132 public function get_post_id() { 133 return $this->post_id; 134 } 135 136 /** 137 * Get the data stored in the changeset post, if one exists. 138 * 139 * @since 4.9.0 140 * 141 * @return array|WP_Error Changeset data or WP_Error on error. 142 */ 143 public function get_post_data() { 144 if ( ! $this->post_id ) { 145 return new WP_Error( 'empty_post_id' ); 146 } 147 148 $post = get_post( $this->post_id ); 149 150 if ( ! $post ) { 151 return new WP_Error( 'missing_post' ); 152 } 153 154 if ( 'customize_changeset' !== get_post_type( $post ) ) { 155 return new WP_Error( 'wrong_post_type' ); 156 } 157 158 $data = json_decode( $post->post_content, true ); 159 160 if ( function_exists( 'json_last_error' ) && json_last_error() ) { 161 return new WP_Error( 'json_parse_error', '', json_last_error() ); 162 } 163 164 if ( ! is_array( $data ) ) { 165 return new WP_Error( 'expected_array' ); 166 } 167 168 return $data; 169 } 170 171 /** 172 * Get the changeset data. 173 * 174 * @since 4.9.0 175 * 176 * @return array Changeset data. 177 */ 178 public function get_data() { 179 $data = $this->get_post_data(); 180 return ( is_wp_error( $data ) ) ? array() : $data; 181 } 182 183 /** 184 * Save the changeset post. 185 * 186 * @since 4.9.0 187 * 188 * @param array $args { 189 * Array of arguments that make up the changeset. 190 * 191 * @type array $data Changeset data. 192 * @type string $date_gmt Changeset date in GMT. Optional. 193 * @type string $status Changeset status. Optional. 194 * @type string $title Changeset title. Optional. 195 * } 196 * @return array|WP_Error Array on success or WP_Error. 197 */ 198 public function save( $args ) { 199 $args = wp_parse_args( $args, array( 200 'data' => array(), 201 'date_gmt' => null, 202 'status' => null, 203 'title' => null, 204 ) ); 205 206 $json_options = 0; 207 208 if ( defined( 'JSON_UNESCAPED_SLASHES' ) ) { 209 // Introduced in PHP 5.4. This is only to improve readability as slashes needn't be escaped in storage. 210 $json_options |= JSON_UNESCAPED_SLASHES; 211 } 212 213 // Also introduced in PHP 5.4, but WP defines constant for back compat. See WP Trac #30139. 214 $json_options |= JSON_PRETTY_PRINT; 215 216 $post_array = array( 217 'post_content' => wp_json_encode( $args['data'], $json_options ), 218 ); 219 220 if ( $args['title'] ) { 221 $post_array['post_title'] = $args['title']; 222 } 223 224 // @todo What if there is no UUID or post ID? 225 if ( $this->post_id ) { 226 $post_array['ID'] = $this->post_id; 227 } else { 228 $post_array['post_type'] = 'customize_changeset'; 229 $post_array['post_name'] = $this->uuid; 230 $post_array['post_status'] = 'auto-draft'; 231 } 232 233 if ( $args['status'] ) { 234 $post_array['post_status'] = $args['status']; 235 } 236 237 // Reset post date to now if we are publishing, otherwise pass post_date_gmt and translate for post_date. 238 if ( 'publish' === $args['status'] ) { 239 $post_array['post_date_gmt'] = '0000-00-00 00:00:00'; 240 $post_array['post_date'] = '0000-00-00 00:00:00'; 241 } elseif ( $args['date_gmt'] ) { 242 $post_array['post_date_gmt'] = $args['date_gmt']; 243 $post_array['post_date'] = get_date_from_gmt( $args['date_gmt'] ); 244 } elseif ( $this->post_id && 'auto-draft' === get_post_status( $this->post_id ) ) { 245 /* 246 * Keep bumping the date for the auto-draft whenever it is modified; 247 * this extends its life, preserving it from garbage-collection via 248 * wp_delete_auto_drafts(). 249 */ 250 $post_array['post_date'] = current_time( 'mysql' ); 251 $post_array['post_date_gmt'] = ''; 252 } 253 254 /* 255 * Update the changeset post. The 'publish_customize_changeset' action 256 * will cause the settings in the changeset to be saved via 257 * WP_Customize_Setting::save(). 258 */ 259 $has_kses = ( false !== has_filter( 'content_save_pre', 'wp_filter_post_kses' ) ); 260 if ( $has_kses ) { 261 // Prevent KSES from corrupting JSON in post_content. 262 kses_remove_filters(); 263 } 264 265 // Note that updating a post with publish status will trigger WP_Customize_Manager::publish_changeset_values(). 266 if ( $this->post_id ) { 267 // Prevent date clearing. 268 $post_array['edit_date'] = true; 269 270 $result = wp_update_post( wp_slash( $post_array ), true ); 271 } else { 272 $result = wp_insert_post( wp_slash( $post_array ), true ); 273 274 if ( is_numeric( $result ) ) { 275 $this->post_id = (int) $result; 276 } 277 } 278 279 if ( $has_kses ) { 280 kses_init_filters(); 281 } 282 283 return $result; 284 } 285 286 /** 287 * Publish the changeset values. 288 * 289 * @since 4.9.0 290 * 291 * @param WP_Customize_Manager $wp_customize Customize manager instance that 292 * should publish the changeset. 293 * @return bool|WP_Error True or a WP_Error. 294 */ 295 public function publish( $wp_customize ) { 296 $result = $wp_customize->_publish_changeset_values( $this->post_id ); 297 298 if ( true === $result ) { 299 /* 300 * Trash the changeset post if revisions are not enabled. 301 * 302 * Unpublished changesets by default get garbage collected due to 303 * their auto-draft status. When a changeset post is published, 304 * however, it would no longer get cleaned out. Ths is a problem 305 * when the changeset posts are never displayed anywhere, since they 306 * would just be endlessly piling up. So here we use the revisions 307 * feature to indicate whether or not a published changeset should 308 * get trashed and thus garbage collected. 309 */ 310 if ( ! wp_revisions_enabled( get_post( $this->post_id ) ) ) { 311 $this->trash(); 312 } 313 } 314 315 return $result; 316 } 317 318 /** 319 * Trash or delete the changeset post. 320 * 321 * The following re-formulates the logic from wp_trash_post() as done in 322 * wp_publish_post(). The reason for bypassing wp_trash_post() is that it 323 * will mutate the the post_content and the post_name when they should be 324 * untouched. 325 * 326 * @global wpdb $wpdb WordPress database abstraction object. 327 * 328 * @return mixed The trashed post as an array or an empty value on failure. 329 */ 330 public function trash() { 331 global $wpdb; 332 333 if ( ! EMPTY_TRASH_DAYS ) { 334 return wp_delete_post( (int) $this->post_id, true ); 335 } 336 337 if ( $this->post_id ) { 338 $post = get_post( $this->post_id ); 339 } 340 341 342 if ( empty( $post ) ) { 343 return $post; 344 } 345 346 if ( get_post_status( $post ) === 'trash' ) { 347 return false; 348 } 349 350 $post_id = $post->ID; 351 352 /** This action is documented in wp-includes/post.php */ 353 do_action( 'wp_trash_post', $post_id ); 354 355 add_post_meta( $post_id, '_wp_trash_meta_status', $post->post_status ); 356 add_post_meta( $post_id, '_wp_trash_meta_time', time() ); 357 358 $old_status = $post->post_status; 359 $new_status = 'trash'; 360 $wpdb->update( $wpdb->posts, array( 'post_status' => $new_status ), array( 'ID' => $post->ID ) ); 361 clean_post_cache( $post->ID ); 362 363 $post->post_status = $new_status; 364 wp_transition_post_status( $new_status, $old_status, $post ); 365 366 /** This action is documented in wp-includes/post.php */ 367 do_action( 'edit_post', $post->ID, $post ); 368 369 /** This action is documented in wp-includes/post.php */ 370 do_action( "save_post_{$post->post_type}", $post->ID, $post, true ); 371 372 /** This action is documented in wp-includes/post.php */ 373 do_action( 'save_post', $post->ID, $post, true ); 374 375 /** This action is documented in wp-includes/post.php */ 376 do_action( 'wp_insert_post', $post->ID, $post, true ); 377 378 wp_trash_post_comments( $post_id ); 379 380 /** This action is documented in wp-includes/post.php */ 381 do_action( 'trashed_post', $post_id ); 382 383 return $post->to_array(); 384 } 385 } -
src/wp-includes/class-wp-customize-manager.php
796 796 * @return int|null Returns post ID on success and null on failure. 797 797 */ 798 798 public function find_changeset_post_id( $uuid ) { 799 $cache_group = 'customize_changeset_post'; 800 $changeset_post_id = wp_cache_get( $uuid, $cache_group ); 801 if ( $changeset_post_id && 'customize_changeset' === get_post_type( $changeset_post_id ) ) { 802 return $changeset_post_id; 803 } 804 805 $changeset_post_query = new WP_Query( array( 806 'post_type' => 'customize_changeset', 807 'post_status' => get_post_stati(), 808 'name' => $uuid, 809 'posts_per_page' => 1, 810 'no_found_rows' => true, 811 'cache_results' => true, 812 'update_post_meta_cache' => false, 813 'update_post_term_cache' => false, 814 'lazy_load_term_meta' => false, 815 ) ); 816 if ( ! empty( $changeset_post_query->posts ) ) { 817 // Note: 'fields'=>'ids' is not being used in order to cache the post object as it will be needed. 818 $changeset_post_id = $changeset_post_query->posts[0]->ID; 819 wp_cache_set( $this->_changeset_uuid, $changeset_post_id, $cache_group ); 820 return $changeset_post_id; 821 } 822 823 return null; 799 $changeset = WP_Customize_Changeset::from_uuid( $uuid ); 800 return $changeset->get_post_id(); 824 801 } 825 802 826 803 /** … … 853 830 * @return array|WP_Error Changeset data or WP_Error on error. 854 831 */ 855 832 protected function get_changeset_post_data( $post_id ) { 856 if ( ! $post_id ) { 857 return new WP_Error( 'empty_post_id' ); 858 } 859 $changeset_post = get_post( $post_id ); 860 if ( ! $changeset_post ) { 861 return new WP_Error( 'missing_post' ); 862 } 863 if ( 'customize_changeset' !== $changeset_post->post_type ) { 864 return new WP_Error( 'wrong_post_type' ); 865 } 866 $changeset_data = json_decode( $changeset_post->post_content, true ); 867 if ( function_exists( 'json_last_error' ) && json_last_error() ) { 868 return new WP_Error( 'json_parse_error', '', json_last_error() ); 869 } 870 if ( ! is_array( $changeset_data ) ) { 871 return new WP_Error( 'expected_array' ); 872 } 873 return $changeset_data; 833 $changeset = WP_Customize_Changeset::from_post( $post_id ); 834 return $changeset->get_post_data(); 874 835 } 875 836 876 837 /** … … 2230 2191 ); 2231 2192 2232 2193 $changeset_post_id = $this->changeset_post_id(); 2194 2195 // @todo Retrieving by UUID here because it's possible with retrieving 2196 // by post ID for save() to be called while the instance doesn't have a 2197 // post ID or UUID. See comment in save(). 2198 $changeset = WP_Customize_Changeset::from_uuid( $this->changeset_uuid() ); 2199 2233 2200 $existing_changeset_data = array(); 2234 2201 if ( $changeset_post_id ) { 2235 2202 $existing_status = get_post_status( $changeset_post_id ); … … 2462 2429 $this->start_previewing_theme(); 2463 2430 } 2464 2431 2465 // Gather the data for wp_insert_post()/wp_update_post().2466 $json_options = 0;2467 if ( defined( 'JSON_UNESCAPED_SLASHES' ) ) {2468 $json_options |= JSON_UNESCAPED_SLASHES; // Introduced in PHP 5.4. This is only to improve readability as slashes needn't be escaped in storage.2469 }2470 $json_options |= JSON_PRETTY_PRINT; // Also introduced in PHP 5.4, but WP defines constant for back compat. See WP Trac #30139.2471 $post_array = array(2472 'post_content' => wp_json_encode( $data, $json_options ),2473 );2474 if ( $args['title'] ) {2475 $post_array['post_title'] = $args['title'];2476 }2477 if ( $changeset_post_id ) {2478 $post_array['ID'] = $changeset_post_id;2479 } else {2480 $post_array['post_type'] = 'customize_changeset';2481 $post_array['post_name'] = $this->changeset_uuid();2482 $post_array['post_status'] = 'auto-draft';2483 }2484 if ( $args['status'] ) {2485 $post_array['post_status'] = $args['status'];2486 }2487 2488 // Reset post date to now if we are publishing, otherwise pass post_date_gmt and translate for post_date.2489 if ( 'publish' === $args['status'] ) {2490 $post_array['post_date_gmt'] = '0000-00-00 00:00:00';2491 $post_array['post_date'] = '0000-00-00 00:00:00';2492 } elseif ( $args['date_gmt'] ) {2493 $post_array['post_date_gmt'] = $args['date_gmt'];2494 $post_array['post_date'] = get_date_from_gmt( $args['date_gmt'] );2495 } elseif ( $changeset_post_id && 'auto-draft' === get_post_status( $changeset_post_id ) ) {2496 /*2497 * Keep bumping the date for the auto-draft whenever it is modified;2498 * this extends its life, preserving it from garbage-collection via2499 * wp_delete_auto_drafts().2500 */2501 $post_array['post_date'] = current_time( 'mysql' );2502 $post_array['post_date_gmt'] = '';2503 }2504 2505 2432 $this->store_changeset_revision = $allow_revision; 2506 2433 add_filter( 'wp_save_post_revision_post_has_changed', array( $this, '_filter_revision_post_has_changed' ), 5, 3 ); 2507 2434 2508 // Update the changeset post. The publish_customize_changeset action will cause the settings in the changeset to be saved via WP_Customize_Setting::save(). 2509 $has_kses = ( false !== has_filter( 'content_save_pre', 'wp_filter_post_kses' ) ); 2510 if ( $has_kses ) { 2511 kses_remove_filters(); // Prevent KSES from corrupting JSON in post_content. 2435 $r = $changeset->save( array( 2436 'data' => $data, 2437 'date_gmt' => $args['date_gmt'], 2438 'status' => $args['status'], 2439 'title' => $args['title'], 2440 ) ); 2441 2442 if ( ! is_wp_error( $r ) ) { 2443 $this->_changeset_post_id = $r; // Update cached post ID for the loaded changeset. 2512 2444 } 2513 2445 2514 // Note that updating a post with publish status will trigger WP_Customize_Manager::publish_changeset_values().2515 if ( $changeset_post_id ) {2516 $post_array['edit_date'] = true; // Prevent date clearing.2517 $r = wp_update_post( wp_slash( $post_array ), true );2518 } else {2519 $r = wp_insert_post( wp_slash( $post_array ), true );2520 if ( ! is_wp_error( $r ) ) {2521 $this->_changeset_post_id = $r; // Update cached post ID for the loaded changeset.2522 }2523 }2524 if ( $has_kses ) {2525 kses_init_filters();2526 }2527 2446 $this->_changeset_data = null; // Reset so WP_Customize_Manager::changeset_data() will re-populate with updated contents. 2528 2447 2529 2448 remove_filter( 'wp_save_post_revision_post_has_changed', array( $this, '_filter_revision_post_has_changed' ) ); -
src/wp-includes/theme.php
2836 2836 * @since 4.7.0 2837 2837 * @access private 2838 2838 * 2839 * @global wpdb $wpdb WordPress database abstraction object.2840 2839 * @global WP_Customize_Manager $wp_customize Customizer instance. 2841 2840 * 2842 2841 * @param string $new_status New post status. … … 2844 2843 * @param WP_Post $changeset_post Changeset post object. 2845 2844 */ 2846 2845 function _wp_customize_publish_changeset( $new_status, $old_status, $changeset_post ) { 2847 global $wp_customize , $wpdb;2846 global $wp_customize; 2848 2847 2849 2848 $is_publishing_changeset = ( 2850 2849 'customize_changeset' === $changeset_post->post_type … … 2857 2856 return; 2858 2857 } 2859 2858 2859 $changeset = WP_Customize_Changeset::from_post( $changeset_post ); 2860 2861 if ( ! $changeset ) { 2862 return; 2863 } 2864 2860 2865 if ( empty( $wp_customize ) ) { 2861 2866 require_once ABSPATH . WPINC . '/class-wp-customize-manager.php'; 2862 2867 $wp_customize = new WP_Customize_Manager( array( … … 2886 2891 /** This filter is documented in /wp-includes/class-wp-customize-manager.php */ 2887 2892 do_action( 'customize_register', $wp_customize ); 2888 2893 } 2889 $wp_customize->_publish_changeset_values( $changeset_post->ID ) ;2890 2894 2891 /* 2892 * Trash the changeset post if revisions are not enabled. Unpublished 2893 * changesets by default get garbage collected due to the auto-draft status. 2894 * When a changeset post is published, however, it would no longer get cleaned 2895 * out. Ths is a problem when the changeset posts are never displayed anywhere, 2896 * since they would just be endlessly piling up. So here we use the revisions 2897 * feature to indicate whether or not a published changeset should get trashed 2898 * and thus garbage collected. 2899 */ 2900 if ( ! wp_revisions_enabled( $changeset_post ) ) { 2901 $post = $changeset_post; 2902 $post_id = $changeset_post->ID; 2903 2904 /* 2905 * The following re-formulates the logic from wp_trash_post() as done in 2906 * wp_publish_post(). The reason for bypassing wp_trash_post() is that it 2907 * will mutate the the post_content and the post_name when they should be 2908 * untouched. 2909 */ 2910 if ( ! EMPTY_TRASH_DAYS ) { 2911 wp_delete_post( $post_id, true ); 2912 } else { 2913 /** This action is documented in wp-includes/post.php */ 2914 do_action( 'wp_trash_post', $post_id ); 2915 2916 add_post_meta( $post_id, '_wp_trash_meta_status', $post->post_status ); 2917 add_post_meta( $post_id, '_wp_trash_meta_time', time() ); 2918 2919 $old_status = $post->post_status; 2920 $new_status = 'trash'; 2921 $wpdb->update( $wpdb->posts, array( 'post_status' => $new_status ), array( 'ID' => $post->ID ) ); 2922 clean_post_cache( $post->ID ); 2923 2924 $post->post_status = $new_status; 2925 wp_transition_post_status( $new_status, $old_status, $post ); 2926 2927 /** This action is documented in wp-includes/post.php */ 2928 do_action( 'edit_post', $post->ID, $post ); 2929 2930 /** This action is documented in wp-includes/post.php */ 2931 do_action( "save_post_{$post->post_type}", $post->ID, $post, true ); 2932 2933 /** This action is documented in wp-includes/post.php */ 2934 do_action( 'save_post', $post->ID, $post, true ); 2935 2936 /** This action is documented in wp-includes/post.php */ 2937 do_action( 'wp_insert_post', $post->ID, $post, true ); 2938 2939 /** This action is documented in wp-includes/post.php */ 2940 do_action( 'trashed_post', $post_id ); 2941 } 2942 } 2895 $changeset->publish( $wp_customize ); 2943 2896 } 2944 2897 2945 2898 /** -
src/wp-settings.php
218 218 require( ABSPATH . WPINC . '/class-wp-widget-factory.php' ); 219 219 require( ABSPATH . WPINC . '/nav-menu.php' ); 220 220 require( ABSPATH . WPINC . '/nav-menu-template.php' ); 221 require( ABSPATH . WPINC . '/class-wp-customize-changeset.php' ); 221 222 require( ABSPATH . WPINC . '/admin-bar.php' ); 222 223 require( ABSPATH . WPINC . '/rest-api.php' ); 223 224 require( ABSPATH . WPINC . '/rest-api/class-wp-rest-server.php' );