Changeset 41597 for trunk/src/wp-includes/class-wp-customize-manager.php
- Timestamp:
- 09/26/2017 07:37:02 AM (7 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/wp-includes/class-wp-customize-manager.php
r41586 r41597 175 175 176 176 /** 177 * Whether settings should be previewed.177 * Whether the autosave revision of the changeset should should be loaded. 178 178 * 179 179 * @since 4.9.0 180 180 * @var bool 181 181 */ 182 protected $settings_previewed; 182 protected $autosaved = false; 183 184 /** 185 * Whether the changeset branching is allowed. 186 * 187 * @since 4.9.0 188 * @var bool 189 */ 190 protected $branching = true; 191 192 /** 193 * Whether settings should be previewed. 194 * 195 * @since 4.9.0 196 * @var bool 197 */ 198 protected $settings_previewed = true; 199 200 /** 201 * Whether a starter content changeset was saved. 202 * 203 * @since 4.9.0 204 * @var bool 205 */ 206 protected $saved_starter_content_changeset = false; 183 207 184 208 /** … … 222 246 * Args. 223 247 * 224 * @type string $changeset_uuid Changeset UUID, the post_name for the customize_changeset post containing the customized state. Defaults to new UUID. 225 * @type string $theme Theme to be previewed (for theme switch). Defaults to customize_theme or theme query params. 226 * @type string $messenger_channel Messenger channel. Defaults to customize_messenger_channel query param. 227 * @type bool $settings_previewed If settings should be previewed. Defaults to true. 248 * @type null|string|false $changeset_uuid Changeset UUID, the `post_name` for the customize_changeset post containing the customized state. 249 * Defaults to `null` resulting in a UUID to be immediately generated. If `false` is provided, then 250 * then the changeset UUID will be determined during `after_setup_theme`: when the 251 * `customize_changeset_branching` filter returns false, then the default UUID will be that 252 * of the most recent `customize_changeset` post that has a status other than 'auto-draft', 253 * 'publish', or 'trash'. Otherwise, if changeset branching is enabled, then a random UUID will be used. 254 * @type string $theme Theme to be previewed (for theme switch). Defaults to customize_theme or theme query params. 255 * @type string $messenger_channel Messenger channel. Defaults to customize_messenger_channel query param. 256 * @type bool $settings_previewed If settings should be previewed. Defaults to true. 257 * @type bool $branching If changeset branching is allowed; otherwise, changesets are linear. Defaults to true. 258 * @type bool $autosaved If data from a changeset's autosaved revision should be loaded if it exists. Defaults to false. 228 259 * } 229 260 */ … … 231 262 232 263 $args = array_merge( 233 array_fill_keys( array( 'changeset_uuid', 'theme', 'messenger_channel', 'settings_previewed' ), null ),264 array_fill_keys( array( 'changeset_uuid', 'theme', 'messenger_channel', 'settings_previewed', 'autosaved', 'branching' ), null ), 234 265 $args 235 266 ); … … 252 283 } 253 284 254 if ( ! isset( $args['settings_previewed'] ) ) {255 $args['settings_previewed'] = true;256 }257 258 285 $this->original_stylesheet = get_stylesheet(); 259 286 $this->theme = wp_get_theme( 0 === validate_file( $args['theme'] ) ? $args['theme'] : null ); 260 287 $this->messenger_channel = $args['messenger_channel']; 261 $this->settings_previewed = ! empty( $args['settings_previewed'] );262 288 $this->_changeset_uuid = $args['changeset_uuid']; 289 290 foreach ( array( 'settings_previewed', 'autosaved', 'branching' ) as $key ) { 291 if ( isset( $args[ $key ] ) ) { 292 $this->$key = (bool) $args[ $key ]; 293 } 294 } 263 295 264 296 require_once( ABSPATH . WPINC . '/class-wp-customize-setting.php' ); … … 344 376 add_action( 'wp_ajax_customize_save', array( $this, 'save' ) ); 345 377 add_action( 'wp_ajax_customize_refresh_nonces', array( $this, 'refresh_nonces' ) ); 378 add_action( 'wp_ajax_dismiss_customize_changeset_autosave', array( $this, 'handle_dismiss_changeset_autosave_request' ) ); 346 379 347 380 add_action( 'customize_register', array( $this, 'register_controls' ) ); … … 475 508 } 476 509 477 if ( ! wp_is_uuid( $this->_changeset_uuid ) ) { 510 // If a changeset was provided is invalid. 511 if ( isset( $this->_changeset_uuid ) && false !== $this->_changeset_uuid && ! wp_is_uuid( $this->_changeset_uuid ) ) { 478 512 $this->wp_die( -1, __( 'Invalid changeset UUID' ) ); 479 513 } … … 536 570 } 537 571 572 // Make sure changeset UUID is established immediately after the theme is loaded. 573 add_action( 'after_setup_theme', array( $this, 'establish_loaded_changeset' ), 5 ); 574 538 575 /* 539 576 * Import theme starter content for fresh installations when landing in the customizer. … … 546 583 547 584 $this->start_previewing_theme(); 585 } 586 587 /** 588 * Establish the loaded changeset. 589 * 590 * This method runs right at after_setup_theme and applies the 'customize_changeset_branching' filter to determine 591 * whether concurrent changesets are allowed. Then if the Customizer is not initialized with a `changeset_uuid` param, 592 * this method will determine which UUID should be used. If changeset branching is disabled, then the most saved 593 * changeset will be loaded by default. Otherwise, if there are no existing saved changesets or if changeset branching is 594 * enabled, then a new UUID will be generated. 595 * 596 * @since 4.9.0 597 */ 598 public function establish_loaded_changeset() { 599 600 /** 601 * Filters whether or not changeset branching is allowed. 602 * 603 * By default in core, when changeset branching is not allowed, changesets will operate 604 * linearly in that only one saved changeset will exist at a time (with a 'draft' or 605 * 'future' status). This makes the Customizer operate in a way that is similar to going to 606 * "edit" to one existing post: all users will be making changes to the same post, and autosave 607 * revisions will be made for that post. 608 * 609 * By contrast, when changeset branching is allowed, then the model is like users going 610 * to "add new" for a page and each user makes changes independently of each other since 611 * they are all operating on their own separate pages, each getting their own separate 612 * initial auto-drafts and then once initially saved, autosave revisions on top of that 613 * user's specific post. 614 * 615 * Since linear changesets are deemed to be more suitable for the majority of WordPress users, 616 * they are the default. For WordPress sites that have heavy site management in the Customizer 617 * by multiple users then branching changesets should be enabled by means of this filter. 618 * 619 * @since 4.9.0 620 * 621 * @param bool $allow_branching Whether branching is allowed. If `false`, the default, 622 * then only one saved changeset exists at a time. 623 * @param WP_Customize_Manager $wp_customize Manager instance. 624 */ 625 $this->branching = apply_filters( 'customize_changeset_branching', $this->branching, $this ); 626 627 if ( empty( $this->_changeset_uuid ) ) { 628 $changeset_uuid = null; 629 630 if ( ! $this->branching ) { 631 $unpublished_changeset_posts = $this->get_changeset_posts( array( 632 'post_status' => array_diff( get_post_stati(), array( 'auto-draft', 'publish', 'trash', 'inherit', 'private' ) ), 633 'exclude_restore_dismissed' => false, 634 'posts_per_page' => 1, 635 'order' => 'DESC', 636 'orderby' => 'date', 637 ) ); 638 $unpublished_changeset_post = array_shift( $unpublished_changeset_posts ); 639 if ( ! empty( $unpublished_changeset_post ) && wp_is_uuid( $unpublished_changeset_post->post_name ) ) { 640 $changeset_uuid = $unpublished_changeset_post->post_name; 641 } 642 } 643 644 // If no changeset UUID has been set yet, then generate a new one. 645 if ( empty( $changeset_uuid ) ) { 646 $changeset_uuid = wp_generate_uuid4(); 647 } 648 649 $this->_changeset_uuid = $changeset_uuid; 650 } 548 651 } 549 652 … … 653 756 * 654 757 * @since 4.7.0 655 * 758 * @since 4.9.0 An exception is thrown if the changeset UUID has not been established yet. 759 * @see WP_Customize_Manager::establish_loaded_changeset() 760 * 761 * @throws Exception When the UUID has not been set yet. 656 762 * @return string UUID. 657 763 */ 658 764 public function changeset_uuid() { 765 if ( empty( $this->_changeset_uuid ) ) { 766 throw new Exception( 'Changeset UUID has not been set.' ); // @todo Replace this with a call to `WP_Customize_Manager::establish_loaded_changeset()` during 4.9-beta2. 767 } 659 768 return $this->_changeset_uuid; 660 769 } … … 826 935 827 936 /** 937 * Get changeset posts. 938 * 939 * @since 4.9.0 940 * 941 * @param array $args { 942 * Args to pass into `get_posts()` to query changesets. 943 * 944 * @type int $posts_per_page Number of posts to return. Defaults to -1 (all posts). 945 * @type int $author Post author. Defaults to current user. 946 * @type string $post_status Status of changeset. Defaults to 'auto-draft'. 947 * @type bool $exclude_restore_dismissed Whether to exclude changeset auto-drafts that have been dismissed. Defaults to true. 948 * } 949 * @return WP_Post[] Auto-draft changesets. 950 */ 951 protected function get_changeset_posts( $args = array() ) { 952 $default_args = array( 953 'exclude_restore_dismissed' => true, 954 'posts_per_page' => -1, 955 'post_type' => 'customize_changeset', 956 'post_status' => 'auto-draft', 957 'order' => 'DESC', 958 'orderby' => 'date', 959 'no_found_rows' => true, 960 'cache_results' => true, 961 'update_post_meta_cache' => false, 962 'update_post_term_cache' => false, 963 'lazy_load_term_meta' => false, 964 ); 965 if ( get_current_user_id() ) { 966 $default_args['author'] = get_current_user_id(); 967 } 968 $args = array_merge( $default_args, $args ); 969 970 if ( ! empty( $args['exclude_restore_dismissed'] ) ) { 971 unset( $args['exclude_restore_dismissed'] ); 972 $args['meta_query'] = array( 973 array( 974 'key' => '_customize_restore_dismissed', 975 'compare' => 'NOT EXISTS', 976 ), 977 ); 978 } 979 980 return get_posts( $args ); 981 } 982 983 /** 828 984 * Get the changeset post id for the loaded changeset. 829 985 * … … 834 990 public function changeset_post_id() { 835 991 if ( ! isset( $this->_changeset_post_id ) ) { 836 $post_id = $this->find_changeset_post_id( $this-> _changeset_uuid);992 $post_id = $this->find_changeset_post_id( $this->changeset_uuid() ); 837 993 if ( ! $post_id ) { 838 994 $post_id = false; … … 862 1018 return new WP_Error( 'missing_post' ); 863 1019 } 864 if ( 'customize_changeset' !== $changeset_post->post_type ) { 1020 if ( 'revision' === $changeset_post->post_type ) { 1021 if ( 'customize_changeset' !== get_post_type( $changeset_post->post_parent ) ) { 1022 return new WP_Error( 'wrong_post_type' ); 1023 } 1024 } elseif ( 'customize_changeset' !== $changeset_post->post_type ) { 865 1025 return new WP_Error( 'wrong_post_type' ); 866 1026 } … … 879 1039 * 880 1040 * @since 4.7.0 1041 * @since 4.9.0 This will return the changeset's data with a user's autosave revision merged on top, if one exists and $autosaved is true. 881 1042 * 882 1043 * @return array Changeset data. … … 890 1051 $this->_changeset_data = array(); 891 1052 } else { 892 $data = $this->get_changeset_post_data( $changeset_post_id ); 893 if ( ! is_wp_error( $data ) ) { 894 $this->_changeset_data = $data; 895 } else { 896 $this->_changeset_data = array(); 1053 if ( $this->autosaved ) { 1054 $autosave_post = wp_get_post_autosave( $changeset_post_id ); 1055 if ( $autosave_post ) { 1056 $data = $this->get_changeset_post_data( $autosave_post->ID ); 1057 if ( ! is_wp_error( $data ) ) { 1058 $this->_changeset_data = $data; 1059 } 1060 } 1061 } 1062 1063 // Load data from the changeset if it was not loaded from an autosave. 1064 if ( ! isset( $this->_changeset_data ) ) { 1065 $data = $this->get_changeset_post_data( $changeset_post_id ); 1066 if ( ! is_wp_error( $data ) ) { 1067 $this->_changeset_data = $data; 1068 } else { 1069 $this->_changeset_data = array(); 1070 } 897 1071 } 898 1072 } … … 1375 1549 'starter_content' => true, 1376 1550 ) ); 1551 $this->saved_starter_content_changeset = true; 1377 1552 1378 1553 $this->pending_starter_content_settings_ids = array(); … … 1797 1972 $settings = array( 1798 1973 'changeset' => array( 1799 'uuid' => $this->_changeset_uuid, 1974 'uuid' => $this->changeset_uuid(), 1975 'autosaved' => $this->autosaved, 1800 1976 ), 1801 1977 'timeouts' => array( … … 2079 2255 2080 2256 $changeset_post_id = $this->changeset_post_id(); 2081 if ( empty( $changeset_post_id ) ) { 2257 $is_new_changeset = empty( $changeset_post_id ); 2258 if ( $is_new_changeset ) { 2082 2259 if ( ! current_user_can( get_post_type_object( 'customize_changeset' )->cap->create_posts ) ) { 2083 2260 wp_send_json_error( 'cannot_create_changeset_post' ); … … 2145 2322 } 2146 2323 2324 $autosave = ! empty( $_POST['customize_changeset_autosave'] ); 2325 if ( $autosave && ! defined( 'DOING_AUTOSAVE' ) ) { // Back-compat. 2326 define( 'DOING_AUTOSAVE', true ); 2327 } 2328 2147 2329 $r = $this->save_changeset_post( array( 2148 2330 'status' => $changeset_status, … … 2150 2332 'date_gmt' => $changeset_date_gmt, 2151 2333 'data' => $input_changeset_data, 2334 'autosave' => $autosave, 2152 2335 ) ); 2153 2336 if ( is_wp_error( $r ) ) { … … 2163 2346 } else { 2164 2347 $response = $r; 2348 2349 // Dismiss all other auto-draft changeset posts for this user (they serve like autosave revisions), as there should only be one. 2350 if ( $is_new_changeset ) { 2351 $changeset_autodraft_posts = $this->get_changeset_posts( array( 2352 'post_status' => 'auto-draft', 2353 'exclude_restore_dismissed' => true, 2354 'posts_per_page' => -1, 2355 ) ); 2356 foreach ( $changeset_autodraft_posts as $autosave_autodraft_post ) { 2357 if ( $autosave_autodraft_post->ID !== $this->changeset_post_id() ) { 2358 update_post_meta( $autosave_autodraft_post->ID, '_customize_restore_dismissed', true ); 2359 } 2360 } 2361 } 2165 2362 2166 2363 // Note that if the changeset status was publish, then it will get set to trash if revisions are not supported. … … 2213 2410 * @type int $user_id ID for user who is saving the changeset. Optional, defaults to the current user ID. 2214 2411 * @type bool $starter_content Whether the data is starter content. If false (default), then $starter_content will be cleared for any $data being saved. 2412 * @type bool $autosave Whether this is a request to create an autosave revision. 2215 2413 * } 2216 2414 * … … 2227 2425 'user_id' => get_current_user_id(), 2228 2426 'starter_content' => false, 2427 'autosave' => false, 2229 2428 ), 2230 2429 $args … … 2276 2475 if ( ! empty( $is_future_dated ) && 'publish' === $args['status'] ) { 2277 2476 $args['status'] = 'future'; 2477 } 2478 2479 // Validate autosave param. See _wp_post_revision_fields() for why these fields are disallowed. 2480 if ( $args['autosave'] ) { 2481 if ( $args['date_gmt'] ) { 2482 return new WP_Error( 'illegal_autosave_with_date_gmt' ); 2483 } elseif ( $args['status'] ) { 2484 return new WP_Error( 'illegal_autosave_with_status' ); 2485 } elseif ( $args['user_id'] && get_current_user_id() !== $args['user_id'] ) { 2486 return new WP_Error( 'illegal_autosave_with_non_current_user' ); 2487 } 2278 2488 } 2279 2489 … … 2520 2730 // Note that updating a post with publish status will trigger WP_Customize_Manager::publish_changeset_values(). 2521 2731 if ( $changeset_post_id ) { 2522 $post_array['edit_date'] = true; // Prevent date clearing. 2523 $r = wp_update_post( wp_slash( $post_array ), true ); 2732 if ( $args['autosave'] && 'auto-draft' !== get_post_status( $changeset_post_id ) ) { 2733 // See _wp_translate_postdata() for why this is required as it will use the edit_post meta capability. 2734 add_filter( 'map_meta_cap', array( $this, 'grant_edit_post_capability_for_changeset' ), 10, 4 ); 2735 $post_array['post_ID'] = $post_array['ID']; 2736 $post_array['post_type'] = 'customize_changeset'; 2737 $r = wp_create_post_autosave( wp_slash( $post_array ) ); 2738 remove_filter( 'map_meta_cap', array( $this, 'grant_edit_post_capability_for_changeset' ), 10 ); 2739 } else { 2740 $post_array['edit_date'] = true; // Prevent date clearing. 2741 $r = wp_update_post( wp_slash( $post_array ), true ); 2742 2743 // Delete autosave revision when the changeset is updated. 2744 $autosave_draft = wp_get_post_autosave( $changeset_post_id ); 2745 if ( $autosave_draft ) { 2746 wp_delete_post( $autosave_draft->ID, true ); 2747 } 2748 } 2524 2749 } else { 2525 2750 $r = wp_insert_post( wp_slash( $post_array ), true ); … … 2545 2770 2546 2771 return $response; 2772 } 2773 2774 /** 2775 * Re-map 'edit_post' meta cap for a customize_changeset post to be the same as 'customize' maps. 2776 * 2777 * There is essentially a "meta meta" cap in play here, where 'edit_post' meta cap maps to 2778 * the 'customize' meta cap which then maps to 'edit_theme_options'. This is currently 2779 * required in core for `wp_create_post_autosave()` because it will call 2780 * `_wp_translate_postdata()` which in turn will check if a user can 'edit_post', but the 2781 * the caps for the customize_changeset post type are all mapping to the meta capability. 2782 * This should be able to be removed once #40922 is addressed in core. 2783 * 2784 * @since 4.9.0 2785 * @link https://core.trac.wordpress.org/ticket/40922 2786 * @see WP_Customize_Manager::save_changeset_post() 2787 * @see _wp_translate_postdata() 2788 * 2789 * @param array $caps Returns the user's actual capabilities. 2790 * @param string $cap Capability name. 2791 * @param int $user_id The user ID. 2792 * @param array $args Adds the context to the cap. Typically the object ID. 2793 * @return array Capabilities. 2794 */ 2795 public function grant_edit_post_capability_for_changeset( $caps, $cap, $user_id, $args ) { 2796 if ( 'edit_post' === $cap && ! empty( $args[0] ) && 'customize_changeset' === get_post_type( $args[0] ) ) { 2797 $post_type_obj = get_post_type_object( 'customize_changeset' ); 2798 $caps = map_meta_cap( $post_type_obj->cap->$cap, $user_id ); 2799 } 2800 return $caps; 2547 2801 } 2548 2802 … … 2787 3041 2788 3042 /** 3043 * Delete a given auto-draft changeset or the autosave revision for a given changeset. 3044 * 3045 * @since 4.9.0 3046 */ 3047 public function handle_dismiss_changeset_autosave_request() { 3048 if ( ! $this->is_preview() ) { 3049 wp_send_json_error( 'not_preview', 400 ); 3050 } 3051 3052 if ( ! check_ajax_referer( 'dismiss_customize_changeset_autosave', 'nonce', false ) ) { 3053 wp_send_json_error( 'invalid_nonce', 403 ); 3054 } 3055 3056 $changeset_post_id = $this->changeset_post_id(); 3057 if ( empty( $changeset_post_id ) ) { 3058 wp_send_json_error( 'missing_changeset', 404 ); 3059 } 3060 3061 if ( 'auto-draft' === get_post_status( $changeset_post_id ) ) { 3062 if ( ! update_post_meta( $changeset_post_id, '_customize_restore_dismissed', true ) ) { 3063 wp_send_json_error( 'auto_draft_dismissal_failure', 500 ); 3064 } else { 3065 wp_send_json_success( 'auto_draft_dismissed' ); 3066 } 3067 } else { 3068 $revision = wp_get_post_autosave( $changeset_post_id ); 3069 3070 if ( $revision ) { 3071 if ( ! current_user_can( get_post_type_object( 'customize_changeset' )->cap->delete_post, $changeset_post_id ) ) { 3072 wp_send_json_error( 'cannot_delete_autosave_revision', 403 ); 3073 } 3074 3075 if ( ! wp_delete_post( $revision->ID, true ) ) { 3076 wp_send_json_error( 'autosave_revision_deletion_failure', 500 ); 3077 } else { 3078 wp_send_json_success( 'autosave_revision_deleted' ); 3079 } 3080 } else { 3081 wp_send_json_error( 'no_autosave_to_delete', 404 ); 3082 } 3083 } 3084 wp_send_json_error( 'unknown_error', 500 ); 3085 } 3086 3087 /** 2789 3088 * Add a customize setting. 2790 3089 * … … 3528 3827 'save' => wp_create_nonce( 'save-customize_' . $this->get_stylesheet() ), 3529 3828 'preview' => wp_create_nonce( 'preview-customize_' . $this->get_stylesheet() ), 3829 'dismiss_autosave' => wp_create_nonce( 'dismiss_customize_changeset_autosave' ), 3530 3830 ); 3531 3831 … … 3564 3864 } 3565 3865 3866 $autosave_revision_post = null; 3867 $autosave_autodraft_post = null; 3868 $changeset_post_id = $this->changeset_post_id(); 3869 if ( ! $this->saved_starter_content_changeset && ! $this->autosaved ) { 3870 if ( $changeset_post_id ) { 3871 $autosave_revision_post = wp_get_post_autosave( $changeset_post_id ); 3872 } else { 3873 $autosave_autodraft_posts = $this->get_changeset_posts( array( 3874 'posts_per_page' => 1, 3875 'post_status' => 'auto-draft', 3876 'exclude_restore_dismissed' => true, 3877 ) ); 3878 if ( ! empty( $autosave_autodraft_posts ) ) { 3879 $autosave_autodraft_post = array_shift( $autosave_autodraft_posts ); 3880 } 3881 } 3882 } 3883 3566 3884 // Prepare Customizer settings to pass to JavaScript. 3567 3885 $settings = array( 3568 3886 'changeset' => array( 3569 3887 'uuid' => $this->changeset_uuid(), 3570 'status' => $this->changeset_post_id() ? get_post_status( $this->changeset_post_id() ) : '', 3888 'branching' => $this->branching, 3889 'autosaved' => $this->autosaved, 3890 'hasAutosaveRevision' => ! empty( $autosave_revision_post ), 3891 'latestAutoDraftUuid' => $autosave_autodraft_post ? $autosave_autodraft_post->post_name : null, 3892 'status' => $changeset_post_id ? get_post_status( $changeset_post_id ) : '', 3571 3893 ), 3572 3894 'timeouts' => array(
Note: See TracChangeset
for help on using the changeset viewer.