18 * Serves as a factory for Customize Controls and Settings, and |
18 * Serves as a factory for Customize Controls and Settings, and |
19 * instantiates default Customize Controls and Settings. |
19 * instantiates default Customize Controls and Settings. |
20 * |
20 * |
21 * @since 3.4.0 |
21 * @since 3.4.0 |
22 */ |
22 */ |
|
23 #[AllowDynamicProperties] |
23 final class WP_Customize_Manager { |
24 final class WP_Customize_Manager { |
24 /** |
25 /** |
25 * An instance of the theme being previewed. |
26 * An instance of the theme being previewed. |
26 * |
27 * |
27 * @since 3.4.0 |
28 * @since 3.4.0 |
268 // Note that the UUID format will be validated in the setup_theme() method. |
269 // Note that the UUID format will be validated in the setup_theme() method. |
269 if ( ! isset( $args['changeset_uuid'] ) ) { |
270 if ( ! isset( $args['changeset_uuid'] ) ) { |
270 $args['changeset_uuid'] = wp_generate_uuid4(); |
271 $args['changeset_uuid'] = wp_generate_uuid4(); |
271 } |
272 } |
272 |
273 |
273 // The theme and messenger_channel should be supplied via $args, |
274 /* |
274 // but they are also looked at in the $_REQUEST global here for back-compat. |
275 * The theme and messenger_channel should be supplied via $args, |
|
276 * but they are also looked at in the $_REQUEST global here for back-compat. |
|
277 */ |
275 if ( ! isset( $args['theme'] ) ) { |
278 if ( ! isset( $args['theme'] ) ) { |
276 if ( isset( $_REQUEST['customize_theme'] ) ) { |
279 if ( isset( $_REQUEST['customize_theme'] ) ) { |
277 $args['theme'] = wp_unslash( $_REQUEST['customize_theme'] ); |
280 $args['theme'] = wp_unslash( $_REQUEST['customize_theme'] ); |
278 } elseif ( isset( $_REQUEST['theme'] ) ) { // Deprecated. |
281 } elseif ( isset( $_REQUEST['theme'] ) ) { // Deprecated. |
279 $args['theme'] = wp_unslash( $_REQUEST['theme'] ); |
282 $args['theme'] = wp_unslash( $_REQUEST['theme'] ); |
280 } |
283 } |
281 } |
284 } |
282 if ( ! isset( $args['messenger_channel'] ) && isset( $_REQUEST['customize_messenger_channel'] ) ) { |
285 if ( ! isset( $args['messenger_channel'] ) && isset( $_REQUEST['customize_messenger_channel'] ) ) { |
283 $args['messenger_channel'] = sanitize_key( wp_unslash( $_REQUEST['customize_messenger_channel'] ) ); |
286 $args['messenger_channel'] = sanitize_key( wp_unslash( $_REQUEST['customize_messenger_channel'] ) ); |
|
287 } |
|
288 |
|
289 // Do not load 'widgets' component if a block theme is activated. |
|
290 if ( ! wp_is_block_theme() ) { |
|
291 $this->components[] = 'widgets'; |
284 } |
292 } |
285 |
293 |
286 $this->original_stylesheet = get_stylesheet(); |
294 $this->original_stylesheet = get_stylesheet(); |
287 $this->theme = wp_get_theme( 0 === validate_file( $args['theme'] ) ? $args['theme'] : null ); |
295 $this->theme = wp_get_theme( 0 === validate_file( $args['theme'] ) ? $args['theme'] : null ); |
288 $this->messenger_channel = $args['messenger_channel']; |
296 $this->messenger_channel = $args['messenger_channel']; |
459 'channel' => $this->messenger_channel, |
467 'channel' => $this->messenger_channel, |
460 'url' => wp_customize_url(), |
468 'url' => wp_customize_url(), |
461 ), |
469 ), |
462 'error' => $ajax_message, |
470 'error' => $ajax_message, |
463 ); |
471 ); |
|
472 $message .= ob_get_clean(); |
|
473 ob_start(); |
464 ?> |
474 ?> |
465 <script> |
475 <script> |
466 ( function( api, settings ) { |
476 ( function( api, settings ) { |
467 var preview = new api.Messenger( settings.messengerArgs ); |
477 var preview = new api.Messenger( settings.messengerArgs ); |
468 preview.send( 'iframe-loading-error', settings.error ); |
478 preview.send( 'iframe-loading-error', settings.error ); |
469 } )( wp.customize, <?php echo wp_json_encode( $settings ); ?> ); |
479 } )( wp.customize, <?php echo wp_json_encode( $settings ); ?> ); |
470 </script> |
480 </script> |
471 <?php |
481 <?php |
472 $message .= ob_get_clean(); |
482 $message .= wp_get_inline_script_tag( wp_remove_surrounding_empty_script_tags( ob_get_clean() ) ); |
473 } |
483 } |
474 |
484 |
475 wp_die( $message ); |
485 wp_die( $message ); |
476 } |
486 } |
477 |
487 |
562 |
572 |
563 if ( $this->is_theme_active() ) { |
573 if ( $this->is_theme_active() ) { |
564 // Once the theme is loaded, we'll validate it. |
574 // Once the theme is loaded, we'll validate it. |
565 add_action( 'after_setup_theme', array( $this, 'after_setup_theme' ) ); |
575 add_action( 'after_setup_theme', array( $this, 'after_setup_theme' ) ); |
566 } else { |
576 } else { |
567 // If the requested theme is not the active theme and the user doesn't have |
577 /* |
568 // the switch_themes cap, bail. |
578 * If the requested theme is not the active theme and the user doesn't have |
|
579 * the switch_themes cap, bail. |
|
580 */ |
569 if ( ! current_user_can( 'switch_themes' ) ) { |
581 if ( ! current_user_can( 'switch_themes' ) ) { |
570 $this->wp_die( -1, __( 'Sorry, you are not allowed to edit theme options on this site.' ) ); |
582 $this->wp_die( -1, __( 'Sorry, you are not allowed to edit theme options on this site.' ) ); |
571 } |
583 } |
572 |
584 |
573 // If the theme has errors while loading, bail. |
585 // If the theme has errors while loading, bail. |
902 * |
914 * |
903 * @since 3.4.0 |
915 * @since 3.4.0 |
904 */ |
916 */ |
905 public function wp_loaded() { |
917 public function wp_loaded() { |
906 |
918 |
907 // Unconditionally register core types for panels, sections, and controls |
919 /* |
908 // in case plugin unhooks all customize_register actions. |
920 * Unconditionally register core types for panels, sections, and controls |
|
921 * in case plugin unhooks all customize_register actions. |
|
922 */ |
909 $this->register_panel_type( 'WP_Customize_Panel' ); |
923 $this->register_panel_type( 'WP_Customize_Panel' ); |
910 $this->register_panel_type( 'WP_Customize_Themes_Panel' ); |
924 $this->register_panel_type( 'WP_Customize_Themes_Panel' ); |
911 $this->register_section_type( 'WP_Customize_Section' ); |
925 $this->register_section_type( 'WP_Customize_Section' ); |
912 $this->register_section_type( 'WP_Customize_Sidebar_Section' ); |
926 $this->register_section_type( 'WP_Customize_Sidebar_Section' ); |
913 $this->register_section_type( 'WP_Customize_Themes_Section' ); |
927 $this->register_section_type( 'WP_Customize_Themes_Section' ); |
1470 } |
1484 } |
1471 } |
1485 } |
1472 |
1486 |
1473 if ( ! $nav_menu_term_id ) { |
1487 if ( ! $nav_menu_term_id ) { |
1474 while ( isset( $changeset_data[ sprintf( 'nav_menu[%d]', $placeholder_id ) ] ) ) { |
1488 while ( isset( $changeset_data[ sprintf( 'nav_menu[%d]', $placeholder_id ) ] ) ) { |
1475 $placeholder_id--; |
1489 --$placeholder_id; |
1476 } |
1490 } |
1477 $nav_menu_term_id = $placeholder_id; |
1491 $nav_menu_term_id = $placeholder_id; |
1478 $nav_menu_setting_id = sprintf( 'nav_menu[%d]', $placeholder_id ); |
1492 $nav_menu_setting_id = sprintf( 'nav_menu[%d]', $placeholder_id ); |
1479 } |
1493 } |
1480 |
1494 |
1900 * not send no-cache headers by default. |
1914 * not send no-cache headers by default. |
1901 */ |
1915 */ |
1902 if ( ! headers_sent() ) { |
1916 if ( ! headers_sent() ) { |
1903 nocache_headers(); |
1917 nocache_headers(); |
1904 header( 'X-Robots: noindex, nofollow, noarchive' ); |
1918 header( 'X-Robots: noindex, nofollow, noarchive' ); |
|
1919 header( 'X-Robots-Tag: noindex, nofollow, noarchive' ); |
1905 } |
1920 } |
1906 add_filter( 'wp_robots', 'wp_robots_no_robots' ); |
1921 add_filter( 'wp_robots', 'wp_robots_no_robots' ); |
1907 add_filter( 'wp_headers', array( $this, 'filter_iframe_security_headers' ) ); |
1922 add_filter( 'wp_headers', array( $this, 'filter_iframe_security_headers' ) ); |
1908 |
1923 |
1909 /* |
1924 /* |
2073 */ |
2088 */ |
2074 public function remove_frameless_preview_messenger_channel() { |
2089 public function remove_frameless_preview_messenger_channel() { |
2075 if ( ! $this->messenger_channel ) { |
2090 if ( ! $this->messenger_channel ) { |
2076 return; |
2091 return; |
2077 } |
2092 } |
|
2093 ob_start(); |
2078 ?> |
2094 ?> |
2079 <script> |
2095 <script> |
2080 ( function() { |
2096 ( function() { |
2081 var urlParser, oldQueryParams, newQueryParams, i; |
|
2082 if ( parent !== window ) { |
2097 if ( parent !== window ) { |
2083 return; |
2098 return; |
2084 } |
2099 } |
2085 urlParser = document.createElement( 'a' ); |
2100 const url = new URL( location.href ); |
2086 urlParser.href = location.href; |
2101 if ( url.searchParams.has( 'customize_messenger_channel' ) ) { |
2087 oldQueryParams = urlParser.search.substr( 1 ).split( /&/ ); |
2102 url.searchParams.delete( 'customize_messenger_channel' ); |
2088 newQueryParams = []; |
2103 location.replace( url ); |
2089 for ( i = 0; i < oldQueryParams.length; i += 1 ) { |
|
2090 if ( ! /^customize_messenger_channel=/.test( oldQueryParams[ i ] ) ) { |
|
2091 newQueryParams.push( oldQueryParams[ i ] ); |
|
2092 } |
|
2093 } |
|
2094 urlParser.search = newQueryParams.join( '&' ); |
|
2095 if ( urlParser.search !== location.search ) { |
|
2096 location.replace( urlParser.href ); |
|
2097 } |
2104 } |
2098 } )(); |
2105 } )(); |
2099 </script> |
2106 </script> |
2100 <?php |
2107 <?php |
|
2108 wp_print_inline_script_tag( wp_remove_surrounding_empty_script_tags( ob_get_clean() ) ); |
2101 } |
2109 } |
2102 |
2110 |
2103 /** |
2111 /** |
2104 * Prints JavaScript settings for preview frame. |
2112 * Prints JavaScript settings for preview frame. |
2105 * |
2113 * |
2109 $post_values = $this->unsanitized_post_values( array( 'exclude_changeset' => true ) ); |
2117 $post_values = $this->unsanitized_post_values( array( 'exclude_changeset' => true ) ); |
2110 $setting_validities = $this->validate_setting_values( $post_values ); |
2118 $setting_validities = $this->validate_setting_values( $post_values ); |
2111 $exported_setting_validities = array_map( array( $this, 'prepare_setting_validity_for_js' ), $setting_validities ); |
2119 $exported_setting_validities = array_map( array( $this, 'prepare_setting_validity_for_js' ), $setting_validities ); |
2112 |
2120 |
2113 // Note that the REQUEST_URI is not passed into home_url() since this breaks subdirectory installations. |
2121 // Note that the REQUEST_URI is not passed into home_url() since this breaks subdirectory installations. |
2114 $self_url = empty( $_SERVER['REQUEST_URI'] ) ? home_url( '/' ) : esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) ); |
2122 $self_url = empty( $_SERVER['REQUEST_URI'] ) ? home_url( '/' ) : sanitize_url( wp_unslash( $_SERVER['REQUEST_URI'] ) ); |
2115 $state_query_params = array( |
2123 $state_query_params = array( |
2116 'customize_theme', |
2124 'customize_theme', |
2117 'customize_changeset_uuid', |
2125 'customize_changeset_uuid', |
2118 'customize_messenger_channel', |
2126 'customize_messenger_channel', |
2119 ); |
2127 ); |
2131 $host .= ':' . $parsed['port']; |
2139 $host .= ':' . $parsed['port']; |
2132 } |
2140 } |
2133 $allowed_hosts[] = $host; |
2141 $allowed_hosts[] = $host; |
2134 } |
2142 } |
2135 |
2143 |
2136 $switched_locale = switch_to_locale( get_user_locale() ); |
2144 $switched_locale = switch_to_user_locale( get_current_user_id() ); |
2137 $l10n = array( |
2145 $l10n = array( |
2138 'shiftClickToEdit' => __( 'Shift-click to edit this element.' ), |
2146 'shiftClickToEdit' => __( 'Shift-click to edit this element.' ), |
2139 'linkUnpreviewable' => __( 'This link is not live-previewable.' ), |
2147 'linkUnpreviewable' => __( 'This link is not live-previewable.' ), |
2140 'formUnpreviewable' => __( 'This form is not live-previewable.' ), |
2148 'formUnpreviewable' => __( 'This form is not live-previewable.' ), |
2141 ); |
2149 ); |
2156 'stylesheet' => $this->get_stylesheet(), |
2164 'stylesheet' => $this->get_stylesheet(), |
2157 'active' => $this->is_theme_active(), |
2165 'active' => $this->is_theme_active(), |
2158 ), |
2166 ), |
2159 'url' => array( |
2167 'url' => array( |
2160 'self' => $self_url, |
2168 'self' => $self_url, |
2161 'allowed' => array_map( 'esc_url_raw', $this->get_allowed_urls() ), |
2169 'allowed' => array_map( 'sanitize_url', $this->get_allowed_urls() ), |
2162 'allowedHosts' => array_unique( $allowed_hosts ), |
2170 'allowedHosts' => array_unique( $allowed_hosts ), |
2163 'isCrossDomain' => $this->is_cross_domain(), |
2171 'isCrossDomain' => $this->is_cross_domain(), |
2164 ), |
2172 ), |
2165 'channel' => $this->messenger_channel, |
2173 'channel' => $this->messenger_channel, |
2166 'activePanels' => array(), |
2174 'activePanels' => array(), |
3072 |
3082 |
3073 if ( 'trash' === get_post_status( $post ) ) { |
3083 if ( 'trash' === get_post_status( $post ) ) { |
3074 return false; |
3084 return false; |
3075 } |
3085 } |
3076 |
3086 |
|
3087 $previous_status = $post->post_status; |
|
3088 |
3077 /** This filter is documented in wp-includes/post.php */ |
3089 /** This filter is documented in wp-includes/post.php */ |
3078 $check = apply_filters( 'pre_trash_post', null, $post ); |
3090 $check = apply_filters( 'pre_trash_post', null, $post, $previous_status ); |
3079 if ( null !== $check ) { |
3091 if ( null !== $check ) { |
3080 return $check; |
3092 return $check; |
3081 } |
3093 } |
3082 |
3094 |
3083 /** This action is documented in wp-includes/post.php */ |
3095 /** This action is documented in wp-includes/post.php */ |
3084 do_action( 'wp_trash_post', $post_id ); |
3096 do_action( 'wp_trash_post', $post_id, $previous_status ); |
3085 |
3097 |
3086 add_post_meta( $post_id, '_wp_trash_meta_status', $post->post_status ); |
3098 add_post_meta( $post_id, '_wp_trash_meta_status', $previous_status ); |
3087 add_post_meta( $post_id, '_wp_trash_meta_time', time() ); |
3099 add_post_meta( $post_id, '_wp_trash_meta_time', time() ); |
3088 |
3100 |
3089 $old_status = $post->post_status; |
|
3090 $new_status = 'trash'; |
3101 $new_status = 'trash'; |
3091 $wpdb->update( $wpdb->posts, array( 'post_status' => $new_status ), array( 'ID' => $post->ID ) ); |
3102 $wpdb->update( $wpdb->posts, array( 'post_status' => $new_status ), array( 'ID' => $post->ID ) ); |
3092 clean_post_cache( $post->ID ); |
3103 clean_post_cache( $post->ID ); |
3093 |
3104 |
3094 $post->post_status = $new_status; |
3105 $post->post_status = $new_status; |
3095 wp_transition_post_status( $new_status, $old_status, $post ); |
3106 wp_transition_post_status( $new_status, $previous_status, $post ); |
3096 |
3107 |
3097 /** This action is documented in wp-includes/post.php */ |
3108 /** This action is documented in wp-includes/post.php */ |
3098 do_action( "edit_post_{$post->post_type}", $post->ID, $post ); |
3109 do_action( "edit_post_{$post->post_type}", $post->ID, $post ); |
3099 |
3110 |
3100 /** This action is documented in wp-includes/post.php */ |
3111 /** This action is documented in wp-includes/post.php */ |
3112 wp_after_insert_post( get_post( $post_id ), true, $post ); |
3123 wp_after_insert_post( get_post( $post_id ), true, $post ); |
3113 |
3124 |
3114 wp_trash_post_comments( $post_id ); |
3125 wp_trash_post_comments( $post_id ); |
3115 |
3126 |
3116 /** This action is documented in wp-includes/post.php */ |
3127 /** This action is documented in wp-includes/post.php */ |
3117 do_action( 'trashed_post', $post_id ); |
3128 do_action( 'trashed_post', $post_id, $previous_status ); |
3118 |
3129 |
3119 return $post; |
3130 return $post; |
3120 } |
3131 } |
3121 |
3132 |
3122 /** |
3133 /** |
3425 * Note that this will not be called while a changeset post remains in auto-draft status. |
3436 * Note that this will not be called while a changeset post remains in auto-draft status. |
3426 * |
3437 * |
3427 * @since 4.7.0 |
3438 * @since 4.7.0 |
3428 * |
3439 * |
3429 * @param bool $post_has_changed Whether the post has changed. |
3440 * @param bool $post_has_changed Whether the post has changed. |
3430 * @param WP_Post $last_revision The last revision post object. |
3441 * @param WP_Post $latest_revision The latest revision post object. |
3431 * @param WP_Post $post The post object. |
3442 * @param WP_Post $post The post object. |
3432 * @return bool Whether a revision should be made. |
3443 * @return bool Whether a revision should be made. |
3433 */ |
3444 */ |
3434 public function _filter_revision_post_has_changed( $post_has_changed, $last_revision, $post ) { |
3445 public function _filter_revision_post_has_changed( $post_has_changed, $latest_revision, $post ) { |
3435 unset( $last_revision ); |
3446 unset( $latest_revision ); |
3436 if ( 'customize_changeset' === $post->post_type ) { |
3447 if ( 'customize_changeset' === $post->post_type ) { |
3437 $post_has_changed = $this->store_changeset_revision; |
3448 $post_has_changed = $this->store_changeset_revision; |
3438 } |
3449 } |
3439 return $post_has_changed; |
3450 return $post_has_changed; |
3440 } |
3451 } |
3607 * restore them when a changeset is published, but they had been locked out from including |
3618 * restore them when a changeset is published, but they had been locked out from including |
3608 * their changes in the changeset. |
3619 * their changes in the changeset. |
3609 */ |
3620 */ |
3610 $revisions = wp_get_post_revisions( $changeset_post_id, array( 'check_enabled' => false ) ); |
3621 $revisions = wp_get_post_revisions( $changeset_post_id, array( 'check_enabled' => false ) ); |
3611 foreach ( $revisions as $revision ) { |
3622 foreach ( $revisions as $revision ) { |
3612 if ( false !== strpos( $revision->post_name, "{$changeset_post_id}-autosave" ) ) { |
3623 if ( str_contains( $revision->post_name, "{$changeset_post_id}-autosave" ) ) { |
3613 $wpdb->update( |
3624 $wpdb->update( |
3614 $wpdb->posts, |
3625 $wpdb->posts, |
3615 array( |
3626 array( |
3616 'post_status' => 'auto-draft', |
3627 'post_status' => 'auto-draft', |
3617 'post_type' => 'customize_changeset', |
3628 'post_type' => 'customize_changeset', |
4282 |
4293 |
4283 <script type="text/html" id="tmpl-customize-notification"> |
4294 <script type="text/html" id="tmpl-customize-notification"> |
4284 <li class="notice notice-{{ data.type || 'info' }} {{ data.alt ? 'notice-alt' : '' }} {{ data.dismissible ? 'is-dismissible' : '' }} {{ data.containerClasses || '' }}" data-code="{{ data.code }}" data-type="{{ data.type }}"> |
4295 <li class="notice notice-{{ data.type || 'info' }} {{ data.alt ? 'notice-alt' : '' }} {{ data.dismissible ? 'is-dismissible' : '' }} {{ data.containerClasses || '' }}" data-code="{{ data.code }}" data-type="{{ data.type }}"> |
4285 <div class="notification-message">{{{ data.message || data.code }}}</div> |
4296 <div class="notification-message">{{{ data.message || data.code }}}</div> |
4286 <# if ( data.dismissible ) { #> |
4297 <# if ( data.dismissible ) { #> |
4287 <button type="button" class="notice-dismiss"><span class="screen-reader-text"><?php _e( 'Dismiss' ); ?></span></button> |
4298 <button type="button" class="notice-dismiss"><span class="screen-reader-text"> |
|
4299 <?php |
|
4300 /* translators: Hidden accessibility text. */ |
|
4301 _e( 'Dismiss' ); |
|
4302 ?> |
|
4303 </span></button> |
4288 <# } #> |
4304 <# } #> |
4289 </li> |
4305 </li> |
4290 </script> |
4306 </script> |
4291 |
4307 |
4292 <script type="text/html" id="tmpl-customize-changeset-locked-notification"> |
4308 <script type="text/html" id="tmpl-customize-changeset-locked-notification"> |
4349 <?php esc_html_e( 'Share Preview Link' ); ?> |
4365 <?php esc_html_e( 'Share Preview Link' ); ?> |
4350 </p> |
4366 </p> |
4351 <p class="description customize-control-description"><?php esc_html_e( 'See how changes would look live on your website, and share the preview with people who can\'t access the Customizer.' ); ?></p> |
4367 <p class="description customize-control-description"><?php esc_html_e( 'See how changes would look live on your website, and share the preview with people who can\'t access the Customizer.' ); ?></p> |
4352 <div class="customize-control-notifications-container"></div> |
4368 <div class="customize-control-notifications-container"></div> |
4353 <div class="preview-link-wrapper"> |
4369 <div class="preview-link-wrapper"> |
4354 <label for="{{ elementPrefix }}customize-preview-link-input" class="screen-reader-text"><?php esc_html_e( 'Preview Link' ); ?></label> |
4370 <label for="{{ elementPrefix }}customize-preview-link-input" class="screen-reader-text"> |
|
4371 <?php |
|
4372 /* translators: Hidden accessibility text. */ |
|
4373 esc_html_e( 'Preview Link' ); |
|
4374 ?> |
|
4375 </label> |
4355 <a href="" target=""> |
4376 <a href="" target=""> |
4356 <span class="preview-control-element" data-component="url"></span> |
4377 <span class="preview-control-element" data-component="url"></span> |
4357 <span class="screen-reader-text"><?php _e( '(opens in a new tab)' ); ?></span> |
4378 <span class="screen-reader-text"> |
|
4379 <?php |
|
4380 /* translators: Hidden accessibility text. */ |
|
4381 _e( '(opens in a new tab)' ); |
|
4382 ?> |
|
4383 </span> |
4358 </a> |
4384 </a> |
4359 <input id="{{ elementPrefix }}customize-preview-link-input" readonly tabindex="-1" class="preview-control-element" data-component="input"> |
4385 <input id="{{ elementPrefix }}customize-preview-link-input" readonly tabindex="-1" class="preview-control-element" data-component="input"> |
4360 <button class="customize-copy-preview-link preview-control-element button button-secondary" data-component="button" data-copy-text="<?php esc_attr_e( 'Copy' ); ?>" data-copied-text="<?php esc_attr_e( 'Copied' ); ?>" ><?php esc_html_e( 'Copy' ); ?></button> |
4386 <button class="customize-copy-preview-link preview-control-element button button-secondary" data-component="button" data-copy-text="<?php esc_attr_e( 'Copy' ); ?>" data-copied-text="<?php esc_attr_e( 'Copied' ); ?>" ><?php esc_html_e( 'Copy' ); ?></button> |
4361 </div> |
4387 </div> |
4362 </script> |
4388 </script> |
4572 * @since 4.4.0 |
4598 * @since 4.4.0 |
4573 * |
4599 * |
4574 * @param string $preview_url URL to be previewed. |
4600 * @param string $preview_url URL to be previewed. |
4575 */ |
4601 */ |
4576 public function set_preview_url( $preview_url ) { |
4602 public function set_preview_url( $preview_url ) { |
4577 $preview_url = esc_url_raw( $preview_url ); |
4603 $preview_url = sanitize_url( $preview_url ); |
4578 $this->preview_url = wp_validate_redirect( $preview_url, home_url( '/' ) ); |
4604 $this->preview_url = wp_validate_redirect( $preview_url, home_url( '/' ) ); |
4579 } |
4605 } |
4580 |
4606 |
4581 /** |
4607 /** |
4582 * Gets the initial URL to be previewed. |
4608 * Gets the initial URL to be previewed. |
4660 * @since 4.4.0 |
4686 * @since 4.4.0 |
4661 * |
4687 * |
4662 * @param string $return_url URL for return link. |
4688 * @param string $return_url URL for return link. |
4663 */ |
4689 */ |
4664 public function set_return_url( $return_url ) { |
4690 public function set_return_url( $return_url ) { |
4665 $return_url = esc_url_raw( $return_url ); |
4691 $return_url = sanitize_url( $return_url ); |
4666 $return_url = remove_query_arg( wp_removable_query_args(), $return_url ); |
4692 $return_url = remove_query_arg( wp_removable_query_args(), $return_url ); |
4667 $return_url = wp_validate_redirect( $return_url ); |
4693 $return_url = wp_validate_redirect( $return_url ); |
4668 $this->return_url = $return_url; |
4694 $this->return_url = $return_url; |
4669 } |
4695 } |
4670 |
4696 |
4683 $referer = wp_get_referer(); |
4709 $referer = wp_get_referer(); |
4684 $excluded_referer_basenames = array( 'customize.php', 'wp-login.php' ); |
4710 $excluded_referer_basenames = array( 'customize.php', 'wp-login.php' ); |
4685 |
4711 |
4686 if ( $this->return_url ) { |
4712 if ( $this->return_url ) { |
4687 $return_url = $this->return_url; |
4713 $return_url = $this->return_url; |
|
4714 |
|
4715 $return_url_basename = wp_basename( parse_url( $this->return_url, PHP_URL_PATH ) ); |
|
4716 $return_url_query = parse_url( $this->return_url, PHP_URL_QUERY ); |
|
4717 |
|
4718 if ( 'themes.php' === $return_url_basename && $return_url_query ) { |
|
4719 parse_str( $return_url_query, $query_vars ); |
|
4720 |
|
4721 /* |
|
4722 * If the return URL is a page added by a theme to the Appearance menu via add_submenu_page(), |
|
4723 * verify that it belongs to the active theme, otherwise fall back to the Themes screen. |
|
4724 */ |
|
4725 if ( isset( $query_vars['page'] ) && ! isset( $_registered_pages[ "appearance_page_{$query_vars['page']}" ] ) ) { |
|
4726 $return_url = admin_url( 'themes.php' ); |
|
4727 } |
|
4728 } |
4688 } elseif ( $referer && ! in_array( wp_basename( parse_url( $referer, PHP_URL_PATH ) ), $excluded_referer_basenames, true ) ) { |
4729 } elseif ( $referer && ! in_array( wp_basename( parse_url( $referer, PHP_URL_PATH ) ), $excluded_referer_basenames, true ) ) { |
4689 $return_url = $referer; |
4730 $return_url = $referer; |
4690 } elseif ( $this->preview_url ) { |
4731 } elseif ( $this->preview_url ) { |
4691 $return_url = $this->preview_url; |
4732 $return_url = $this->preview_url; |
4692 } else { |
4733 } else { |
4693 $return_url = home_url( '/' ); |
4734 $return_url = home_url( '/' ); |
4694 } |
|
4695 |
|
4696 $return_url_basename = wp_basename( parse_url( $this->return_url, PHP_URL_PATH ) ); |
|
4697 $return_url_query = parse_url( $this->return_url, PHP_URL_QUERY ); |
|
4698 |
|
4699 if ( 'themes.php' === $return_url_basename && $return_url_query ) { |
|
4700 parse_str( $return_url_query, $query_vars ); |
|
4701 |
|
4702 /* |
|
4703 * If the return URL is a page added by a theme to the Appearance menu via add_submenu_page(), |
|
4704 * verify that it belongs to the active theme, otherwise fall back to the Themes screen. |
|
4705 */ |
|
4706 if ( isset( $query_vars['page'] ) && ! isset( $_registered_pages[ "appearance_page_{$query_vars['page']}" ] ) ) { |
|
4707 $return_url = admin_url( 'themes.php' ); |
|
4708 } |
|
4709 } |
4735 } |
4710 |
4736 |
4711 return $return_url; |
4737 return $return_url; |
4712 } |
4738 } |
4713 |
4739 |
4892 'stylesheet' => $this->get_stylesheet(), |
4918 'stylesheet' => $this->get_stylesheet(), |
4893 'active' => $this->is_theme_active(), |
4919 'active' => $this->is_theme_active(), |
4894 '_canInstall' => current_user_can( 'install_themes' ), |
4920 '_canInstall' => current_user_can( 'install_themes' ), |
4895 ), |
4921 ), |
4896 'url' => array( |
4922 'url' => array( |
4897 'preview' => esc_url_raw( $this->get_preview_url() ), |
4923 'preview' => sanitize_url( $this->get_preview_url() ), |
4898 'return' => esc_url_raw( $this->get_return_url() ), |
4924 'return' => sanitize_url( $this->get_return_url() ), |
4899 'parent' => esc_url_raw( admin_url() ), |
4925 'parent' => sanitize_url( admin_url() ), |
4900 'activated' => esc_url_raw( home_url( '/' ) ), |
4926 'activated' => sanitize_url( home_url( '/' ) ), |
4901 'ajax' => esc_url_raw( admin_url( 'admin-ajax.php', 'relative' ) ), |
4927 'ajax' => sanitize_url( admin_url( 'admin-ajax.php', 'relative' ) ), |
4902 'allowed' => array_map( 'esc_url_raw', $this->get_allowed_urls() ), |
4928 'allowed' => array_map( 'sanitize_url', $this->get_allowed_urls() ), |
4903 'isCrossDomain' => $this->is_cross_domain(), |
4929 'isCrossDomain' => $this->is_cross_domain(), |
4904 'home' => esc_url_raw( home_url( '/' ) ), |
4930 'home' => sanitize_url( home_url( '/' ) ), |
4905 'login' => esc_url_raw( $login_url ), |
4931 'login' => sanitize_url( $login_url ), |
4906 ), |
4932 ), |
4907 'browser' => array( |
4933 'browser' => array( |
4908 'mobile' => wp_is_mobile(), |
4934 'mobile' => wp_is_mobile(), |
4909 'ios' => $this->is_ios(), |
4935 'ios' => $this->is_ios(), |
4910 ), |
4936 ), |
5173 $this, |
5201 $this, |
5174 'site_icon', |
5202 'site_icon', |
5175 array( |
5203 array( |
5176 'label' => __( 'Site Icon' ), |
5204 'label' => __( 'Site Icon' ), |
5177 'description' => sprintf( |
5205 'description' => sprintf( |
5178 '<p>' . __( 'Site Icons are what you see in browser tabs, bookmark bars, and within the WordPress mobile apps. Upload one here!' ) . '</p>' . |
5206 /* translators: %s: Site Icon size in pixels. */ |
5179 /* translators: %s: Site icon size in pixels. */ |
5207 '<p>' . __( 'The Site Icon is what you see in browser tabs, bookmark bars, and within the WordPress mobile apps. It should be square and at least %s pixels.' ) . '</p>', |
5180 '<p>' . __( 'Site Icons should be square and at least %s pixels.' ) . '</p>', |
5208 '<code>512 × 512</code>' |
5181 '<strong>512 × 512</strong>' |
|
5182 ), |
5209 ), |
5183 'section' => 'title_tagline', |
5210 'section' => 'title_tagline', |
5184 'priority' => 60, |
5211 'priority' => 60, |
5185 'height' => 512, |
5212 'height' => 512, |
5186 'width' => 512, |
5213 'width' => 512, |
5251 'sanitize_callback' => array( $this, '_sanitize_header_textcolor' ), |
5278 'sanitize_callback' => array( $this, '_sanitize_header_textcolor' ), |
5252 'sanitize_js_callback' => 'maybe_hash_hex_color', |
5279 'sanitize_js_callback' => 'maybe_hash_hex_color', |
5253 ) |
5280 ) |
5254 ); |
5281 ); |
5255 |
5282 |
5256 // Input type: checkbox. |
5283 // Input type: checkbox, with custom value. |
5257 // With custom value. |
|
5258 $this->add_control( |
5284 $this->add_control( |
5259 'display_header_text', |
5285 'display_header_text', |
5260 array( |
5286 array( |
5261 'settings' => 'header_textcolor', |
5287 'settings' => 'header_textcolor', |
5262 'label' => __( 'Display Site Title and Tagline' ), |
5288 'label' => __( 'Display Site Title and Tagline' ), |
5275 'section' => 'colors', |
5301 'section' => 'colors', |
5276 ) |
5302 ) |
5277 ) |
5303 ) |
5278 ); |
5304 ); |
5279 |
5305 |
5280 // Input type: color. |
5306 // Input type: color, with sanitize_callback. |
5281 // With sanitize_callback. |
|
5282 $this->add_setting( |
5307 $this->add_setting( |
5283 'background_color', |
5308 'background_color', |
5284 array( |
5309 array( |
5285 'default' => get_theme_support( 'custom-background', 'default-color' ), |
5310 'default' => get_theme_support( 'custom-background', 'default-color' ), |
5286 'theme_supports' => 'custom-background', |
5311 'theme_supports' => 'custom-background', |
5584 'section' => 'background_image', |
5609 'section' => 'background_image', |
5585 'type' => 'checkbox', |
5610 'type' => 'checkbox', |
5586 ) |
5611 ) |
5587 ); |
5612 ); |
5588 |
5613 |
5589 // If the theme is using the default background callback, we can update |
5614 /* |
5590 // the background CSS using postMessage. |
5615 * If the theme is using the default background callback, we can update |
|
5616 * the background CSS using postMessage. |
|
5617 */ |
5591 if ( get_theme_support( 'custom-background', 'wp-head-callback' ) === '_custom_background_cb' ) { |
5618 if ( get_theme_support( 'custom-background', 'wp-head-callback' ) === '_custom_background_cb' ) { |
5592 foreach ( array( 'color', 'image', 'preset', 'position_x', 'position_y', 'size', 'repeat', 'attachment' ) as $prop ) { |
5619 foreach ( array( 'color', 'image', 'preset', 'position_x', 'position_y', 'size', 'repeat', 'attachment' ) as $prop ) { |
5593 $this->get_setting( 'background_' . $prop )->transport = 'postMessage'; |
5620 $this->get_setting( 'background_' . $prop )->transport = 'postMessage'; |
5594 } |
5621 } |
5595 } |
5622 } |
5671 /* Custom CSS */ |
5698 /* Custom CSS */ |
5672 $section_description = '<p>'; |
5699 $section_description = '<p>'; |
5673 $section_description .= __( 'Add your own CSS code here to customize the appearance and layout of your site.' ); |
5700 $section_description .= __( 'Add your own CSS code here to customize the appearance and layout of your site.' ); |
5674 $section_description .= sprintf( |
5701 $section_description .= sprintf( |
5675 ' <a href="%1$s" class="external-link" target="_blank">%2$s<span class="screen-reader-text"> %3$s</span></a>', |
5702 ' <a href="%1$s" class="external-link" target="_blank">%2$s<span class="screen-reader-text"> %3$s</span></a>', |
5676 esc_url( __( 'https://codex.wordpress.org/CSS' ) ), |
5703 esc_url( __( 'https://developer.wordpress.org/advanced-administration/wordpress/css/' ) ), |
5677 __( 'Learn more about CSS' ), |
5704 __( 'Learn more about CSS' ), |
5678 /* translators: Accessibility text. */ |
5705 /* translators: Hidden accessibility text. */ |
5679 __( '(opens in a new tab)' ) |
5706 __( '(opens in a new tab)' ) |
5680 ); |
5707 ); |
5681 $section_description .= '</p>'; |
5708 $section_description .= '</p>'; |
5682 |
5709 |
5683 $section_description .= '<p id="editor-keyboard-trap-help-1">' . __( 'When using a keyboard to navigate:' ) . '</p>'; |
5710 $section_description .= '<p id="editor-keyboard-trap-help-1">' . __( 'When using a keyboard to navigate:' ) . '</p>'; |
5694 __( 'The edit field automatically highlights code syntax. You can disable this in your <a href="%1$s" %2$s>user profile%3$s</a> to work in plain text mode.' ), |
5721 __( 'The edit field automatically highlights code syntax. You can disable this in your <a href="%1$s" %2$s>user profile%3$s</a> to work in plain text mode.' ), |
5695 esc_url( get_edit_profile_url() ), |
5722 esc_url( get_edit_profile_url() ), |
5696 'class="external-link" target="_blank"', |
5723 'class="external-link" target="_blank"', |
5697 sprintf( |
5724 sprintf( |
5698 '<span class="screen-reader-text"> %s</span>', |
5725 '<span class="screen-reader-text"> %s</span>', |
5699 /* translators: Accessibility text. */ |
5726 /* translators: Hidden accessibility text. */ |
5700 __( '(opens in a new tab)' ) |
5727 __( '(opens in a new tab)' ) |
5701 ) |
5728 ) |
5702 ); |
5729 ); |
5703 $section_description .= '</p>'; |
5730 $section_description .= '</p>'; |
5704 } |
5731 } |
6004 } elseif ( 'background_preset' === $setting->id ) { |
6039 } elseif ( 'background_preset' === $setting->id ) { |
6005 if ( ! in_array( $value, array( 'default', 'fill', 'fit', 'repeat', 'custom' ), true ) ) { |
6040 if ( ! in_array( $value, array( 'default', 'fill', 'fit', 'repeat', 'custom' ), true ) ) { |
6006 return new WP_Error( 'invalid_value', __( 'Invalid value for background size.' ) ); |
6041 return new WP_Error( 'invalid_value', __( 'Invalid value for background size.' ) ); |
6007 } |
6042 } |
6008 } elseif ( 'background_image' === $setting->id || 'background_image_thumb' === $setting->id ) { |
6043 } elseif ( 'background_image' === $setting->id || 'background_image_thumb' === $setting->id ) { |
6009 $value = empty( $value ) ? '' : esc_url_raw( $value ); |
6044 $value = empty( $value ) ? '' : sanitize_url( $value ); |
6010 } else { |
6045 } else { |
6011 return new WP_Error( 'unrecognized_setting', __( 'Unrecognized background setting.' ) ); |
6046 return new WP_Error( 'unrecognized_setting', __( 'Unrecognized background setting.' ) ); |
6012 } |
6047 } |
6013 return $value; |
6048 return $value; |
6014 } |
6049 } |
6050 $validity->add( |
6085 $validity->add( |
6051 'size_too_large', |
6086 'size_too_large', |
6052 __( 'This video file is too large to use as a header video. Try a shorter video or optimize the compression settings and re-upload a file that is less than 8MB. Or, upload your video to YouTube and link it with the option below.' ) |
6087 __( 'This video file is too large to use as a header video. Try a shorter video or optimize the compression settings and re-upload a file that is less than 8MB. Or, upload your video to YouTube and link it with the option below.' ) |
6053 ); |
6088 ); |
6054 } |
6089 } |
6055 if ( '.mp4' !== substr( $video, -4 ) && '.mov' !== substr( $video, -4 ) ) { // Check for .mp4 or .mov format, which (assuming h.264 encoding) are the only cross-browser-supported formats. |
6090 if ( ! str_ends_with( $video, '.mp4' ) && ! str_ends_with( $video, '.mov' ) ) { // Check for .mp4 or .mov format, which (assuming h.264 encoding) are the only cross-browser-supported formats. |
6056 $validity->add( |
6091 $validity->add( |
6057 'invalid_file_type', |
6092 'invalid_file_type', |
6058 sprintf( |
6093 sprintf( |
6059 /* translators: 1: .mp4, 2: .mov */ |
6094 /* translators: 1: .mp4, 2: .mov */ |
6060 __( 'Only %1$s or %2$s files may be used for header video. Please convert your video file and try again, or, upload your video to YouTube and link it with the option below.' ), |
6095 __( 'Only %1$s or %2$s files may be used for header video. Please convert your video file and try again, or, upload your video to YouTube and link it with the option below.' ), |
6077 * @param WP_Error $validity |
6112 * @param WP_Error $validity |
6078 * @param mixed $value |
6113 * @param mixed $value |
6079 * @return mixed |
6114 * @return mixed |
6080 */ |
6115 */ |
6081 public function _validate_external_header_video( $validity, $value ) { |
6116 public function _validate_external_header_video( $validity, $value ) { |
6082 $video = esc_url_raw( $value ); |
6117 $video = sanitize_url( $value ); |
6083 if ( $video ) { |
6118 if ( $video ) { |
6084 if ( ! preg_match( '#^https?://(?:www\.)?(?:youtube\.com/watch|youtu\.be/)#', $video ) ) { |
6119 if ( ! preg_match( '#^https?://(?:www\.)?(?:youtube\.com/watch|youtu\.be/)#', $video ) ) { |
6085 $validity->add( 'invalid_url', __( 'Please enter a valid YouTube URL.' ) ); |
6120 $validity->add( 'invalid_url', __( 'Please enter a valid YouTube URL.' ) ); |
6086 } |
6121 } |
6087 } |
6122 } |
6095 * |
6130 * |
6096 * @param string $value URL. |
6131 * @param string $value URL. |
6097 * @return string Sanitized URL. |
6132 * @return string Sanitized URL. |
6098 */ |
6133 */ |
6099 public function _sanitize_external_header_video( $value ) { |
6134 public function _sanitize_external_header_video( $value ) { |
6100 return esc_url_raw( trim( $value ) ); |
6135 return sanitize_url( trim( $value ) ); |
6101 } |
6136 } |
6102 |
6137 |
6103 /** |
6138 /** |
6104 * Callback for rendering the custom logo, used in the custom_logo partial. |
6139 * Callback for rendering the custom logo, used in the custom_logo partial. |
6105 * |
6140 * |