268 // Note that the UUID format will be validated in the setup_theme() method. |
268 // Note that the UUID format will be validated in the setup_theme() method. |
269 if ( ! isset( $args['changeset_uuid'] ) ) { |
269 if ( ! isset( $args['changeset_uuid'] ) ) { |
270 $args['changeset_uuid'] = wp_generate_uuid4(); |
270 $args['changeset_uuid'] = wp_generate_uuid4(); |
271 } |
271 } |
272 |
272 |
273 // The theme and messenger_channel should be supplied via $args, but they are also looked at in the $_REQUEST global here for back-compat. |
273 // The theme and messenger_channel should be supplied via $args, |
|
274 // but they are also looked at in the $_REQUEST global here for back-compat. |
274 if ( ! isset( $args['theme'] ) ) { |
275 if ( ! isset( $args['theme'] ) ) { |
275 if ( isset( $_REQUEST['customize_theme'] ) ) { |
276 if ( isset( $_REQUEST['customize_theme'] ) ) { |
276 $args['theme'] = wp_unslash( $_REQUEST['customize_theme'] ); |
277 $args['theme'] = wp_unslash( $_REQUEST['customize_theme'] ); |
277 } elseif ( isset( $_REQUEST['theme'] ) ) { // Deprecated. |
278 } elseif ( isset( $_REQUEST['theme'] ) ) { // Deprecated. |
278 $args['theme'] = wp_unslash( $_REQUEST['theme'] ); |
279 $args['theme'] = wp_unslash( $_REQUEST['theme'] ); |
291 if ( isset( $args[ $key ] ) ) { |
292 if ( isset( $args[ $key ] ) ) { |
292 $this->$key = (bool) $args[ $key ]; |
293 $this->$key = (bool) $args[ $key ]; |
293 } |
294 } |
294 } |
295 } |
295 |
296 |
296 require_once( ABSPATH . WPINC . '/class-wp-customize-setting.php' ); |
297 require_once ABSPATH . WPINC . '/class-wp-customize-setting.php'; |
297 require_once( ABSPATH . WPINC . '/class-wp-customize-panel.php' ); |
298 require_once ABSPATH . WPINC . '/class-wp-customize-panel.php'; |
298 require_once( ABSPATH . WPINC . '/class-wp-customize-section.php' ); |
299 require_once ABSPATH . WPINC . '/class-wp-customize-section.php'; |
299 require_once( ABSPATH . WPINC . '/class-wp-customize-control.php' ); |
300 require_once ABSPATH . WPINC . '/class-wp-customize-control.php'; |
300 |
301 |
301 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-color-control.php' ); |
302 require_once ABSPATH . WPINC . '/customize/class-wp-customize-color-control.php'; |
302 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-media-control.php' ); |
303 require_once ABSPATH . WPINC . '/customize/class-wp-customize-media-control.php'; |
303 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-upload-control.php' ); |
304 require_once ABSPATH . WPINC . '/customize/class-wp-customize-upload-control.php'; |
304 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-image-control.php' ); |
305 require_once ABSPATH . WPINC . '/customize/class-wp-customize-image-control.php'; |
305 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-background-image-control.php' ); |
306 require_once ABSPATH . WPINC . '/customize/class-wp-customize-background-image-control.php'; |
306 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-background-position-control.php' ); |
307 require_once ABSPATH . WPINC . '/customize/class-wp-customize-background-position-control.php'; |
307 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-cropped-image-control.php' ); |
308 require_once ABSPATH . WPINC . '/customize/class-wp-customize-cropped-image-control.php'; |
308 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-site-icon-control.php' ); |
309 require_once ABSPATH . WPINC . '/customize/class-wp-customize-site-icon-control.php'; |
309 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-header-image-control.php' ); |
310 require_once ABSPATH . WPINC . '/customize/class-wp-customize-header-image-control.php'; |
310 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-theme-control.php' ); |
311 require_once ABSPATH . WPINC . '/customize/class-wp-customize-theme-control.php'; |
311 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-code-editor-control.php' ); |
312 require_once ABSPATH . WPINC . '/customize/class-wp-customize-code-editor-control.php'; |
312 require_once( ABSPATH . WPINC . '/customize/class-wp-widget-area-customize-control.php' ); |
313 require_once ABSPATH . WPINC . '/customize/class-wp-widget-area-customize-control.php'; |
313 require_once( ABSPATH . WPINC . '/customize/class-wp-widget-form-customize-control.php' ); |
314 require_once ABSPATH . WPINC . '/customize/class-wp-widget-form-customize-control.php'; |
314 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-control.php' ); |
315 require_once ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-control.php'; |
315 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-item-control.php' ); |
316 require_once ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-item-control.php'; |
316 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-location-control.php' ); |
317 require_once ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-location-control.php'; |
317 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-name-control.php' ); |
318 require_once ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-name-control.php'; |
318 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-locations-control.php' ); |
319 require_once ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-locations-control.php'; |
319 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-auto-add-control.php' ); |
320 require_once ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-auto-add-control.php'; |
320 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-new-menu-control.php' ); // @todo Remove in a future release. See #42364. |
321 |
321 |
322 require_once ABSPATH . WPINC . '/customize/class-wp-customize-nav-menus-panel.php'; |
322 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-nav-menus-panel.php' ); |
323 |
323 |
324 require_once ABSPATH . WPINC . '/customize/class-wp-customize-themes-panel.php'; |
324 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-themes-panel.php' ); |
325 require_once ABSPATH . WPINC . '/customize/class-wp-customize-themes-section.php'; |
325 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-themes-section.php' ); |
326 require_once ABSPATH . WPINC . '/customize/class-wp-customize-sidebar-section.php'; |
326 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-sidebar-section.php' ); |
327 require_once ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-section.php'; |
327 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-section.php' ); |
328 |
328 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-new-menu-section.php' ); // @todo Remove in a future release. See #42364. |
329 require_once ABSPATH . WPINC . '/customize/class-wp-customize-custom-css-setting.php'; |
329 |
330 require_once ABSPATH . WPINC . '/customize/class-wp-customize-filter-setting.php'; |
330 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-custom-css-setting.php' ); |
331 require_once ABSPATH . WPINC . '/customize/class-wp-customize-header-image-setting.php'; |
331 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-filter-setting.php' ); |
332 require_once ABSPATH . WPINC . '/customize/class-wp-customize-background-image-setting.php'; |
332 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-header-image-setting.php' ); |
333 require_once ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-item-setting.php'; |
333 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-background-image-setting.php' ); |
334 require_once ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-setting.php'; |
334 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-item-setting.php' ); |
|
335 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-setting.php' ); |
|
336 |
335 |
337 /** |
336 /** |
338 * Filters the core Customizer components to load. |
337 * Filters the core Customizer components to load. |
339 * |
338 * |
340 * This allows Core components to be excluded from being instantiated by |
339 * This allows Core components to be excluded from being instantiated by |
349 * @param string[] $components Array of core components to load. |
348 * @param string[] $components Array of core components to load. |
350 * @param WP_Customize_Manager $this WP_Customize_Manager instance. |
349 * @param WP_Customize_Manager $this WP_Customize_Manager instance. |
351 */ |
350 */ |
352 $components = apply_filters( 'customize_loaded_components', $this->components, $this ); |
351 $components = apply_filters( 'customize_loaded_components', $this->components, $this ); |
353 |
352 |
354 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-selective-refresh.php' ); |
353 require_once ABSPATH . WPINC . '/customize/class-wp-customize-selective-refresh.php'; |
355 $this->selective_refresh = new WP_Customize_Selective_Refresh( $this ); |
354 $this->selective_refresh = new WP_Customize_Selective_Refresh( $this ); |
356 |
355 |
357 if ( in_array( 'widgets', $components, true ) ) { |
356 if ( in_array( 'widgets', $components, true ) ) { |
358 require_once( ABSPATH . WPINC . '/class-wp-customize-widgets.php' ); |
357 require_once ABSPATH . WPINC . '/class-wp-customize-widgets.php'; |
359 $this->widgets = new WP_Customize_Widgets( $this ); |
358 $this->widgets = new WP_Customize_Widgets( $this ); |
360 } |
359 } |
361 |
360 |
362 if ( in_array( 'nav_menus', $components, true ) ) { |
361 if ( in_array( 'nav_menus', $components, true ) ) { |
363 require_once( ABSPATH . WPINC . '/class-wp-customize-nav-menus.php' ); |
362 require_once ABSPATH . WPINC . '/class-wp-customize-nav-menus.php'; |
364 $this->nav_menus = new WP_Customize_Nav_Menus( $this ); |
363 $this->nav_menus = new WP_Customize_Nav_Menus( $this ); |
365 } |
364 } |
366 |
365 |
367 add_action( 'setup_theme', array( $this, 'setup_theme' ) ); |
366 add_action( 'setup_theme', array( $this, 'setup_theme' ) ); |
368 add_action( 'wp_loaded', array( $this, 'wp_loaded' ) ); |
367 add_action( 'wp_loaded', array( $this, 'wp_loaded' ) ); |
383 add_filter( 'heartbeat_received', array( $this, 'check_changeset_lock_with_heartbeat' ), 10, 3 ); |
382 add_filter( 'heartbeat_received', array( $this, 'check_changeset_lock_with_heartbeat' ), 10, 3 ); |
384 add_action( 'wp_ajax_customize_override_changeset_lock', array( $this, 'handle_override_changeset_lock_request' ) ); |
383 add_action( 'wp_ajax_customize_override_changeset_lock', array( $this, 'handle_override_changeset_lock_request' ) ); |
385 add_action( 'wp_ajax_customize_dismiss_autosave_or_lock', array( $this, 'handle_dismiss_autosave_or_lock_request' ) ); |
384 add_action( 'wp_ajax_customize_dismiss_autosave_or_lock', array( $this, 'handle_dismiss_autosave_or_lock_request' ) ); |
386 |
385 |
387 add_action( 'customize_register', array( $this, 'register_controls' ) ); |
386 add_action( 'customize_register', array( $this, 'register_controls' ) ); |
388 add_action( 'customize_register', array( $this, 'register_dynamic_settings' ), 11 ); // allow code to create settings first |
387 add_action( 'customize_register', array( $this, 'register_dynamic_settings' ), 11 ); // Allow code to create settings first. |
389 add_action( 'customize_controls_init', array( $this, 'prepare_controls' ) ); |
388 add_action( 'customize_controls_init', array( $this, 'prepare_controls' ) ); |
390 add_action( 'customize_controls_enqueue_scripts', array( $this, 'enqueue_control_scripts' ) ); |
389 add_action( 'customize_controls_enqueue_scripts', array( $this, 'enqueue_control_scripts' ) ); |
391 |
390 |
392 // Render Common, Panel, Section, and Control templates. |
391 // Render Common, Panel, Section, and Control templates. |
393 add_action( 'customize_controls_print_footer_scripts', array( $this, 'render_panel_templates' ), 1 ); |
392 add_action( 'customize_controls_print_footer_scripts', array( $this, 'render_panel_templates' ), 1 ); |
563 |
562 |
564 if ( $this->is_theme_active() ) { |
563 if ( $this->is_theme_active() ) { |
565 // Once the theme is loaded, we'll validate it. |
564 // Once the theme is loaded, we'll validate it. |
566 add_action( 'after_setup_theme', array( $this, 'after_setup_theme' ) ); |
565 add_action( 'after_setup_theme', array( $this, 'after_setup_theme' ) ); |
567 } else { |
566 } else { |
568 // If the requested theme is not the active theme and the user doesn't have the |
567 // If the requested theme is not the active theme and the user doesn't have |
569 // switch_themes cap, bail. |
568 // the switch_themes cap, bail. |
570 if ( ! current_user_can( 'switch_themes' ) ) { |
569 if ( ! current_user_can( 'switch_themes' ) ) { |
571 $this->wp_die( -1, __( 'Sorry, you are not allowed to edit theme options on this site.' ) ); |
570 $this->wp_die( -1, __( 'Sorry, you are not allowed to edit theme options on this site.' ) ); |
572 } |
571 } |
573 |
572 |
574 // If the theme has errors while loading, bail. |
573 // If the theme has errors while loading, bail. |
888 * @since 3.4.0 |
892 * @since 3.4.0 |
889 * |
893 * |
890 * @return bool |
894 * @return bool |
891 */ |
895 */ |
892 public function is_theme_active() { |
896 public function is_theme_active() { |
893 return $this->get_stylesheet() == $this->original_stylesheet; |
897 return $this->get_stylesheet() === $this->original_stylesheet; |
894 } |
898 } |
895 |
899 |
896 /** |
900 /** |
897 * Register styles/scripts and initialize the preview of each setting |
901 * Register styles/scripts and initialize the preview of each setting |
898 * |
902 * |
899 * @since 3.4.0 |
903 * @since 3.4.0 |
900 */ |
904 */ |
901 public function wp_loaded() { |
905 public function wp_loaded() { |
902 |
906 |
903 // Unconditionally register core types for panels, sections, and controls in case plugin unhooks all customize_register actions. |
907 // Unconditionally register core types for panels, sections, and controls |
|
908 // in case plugin unhooks all customize_register actions. |
904 $this->register_panel_type( 'WP_Customize_Panel' ); |
909 $this->register_panel_type( 'WP_Customize_Panel' ); |
905 $this->register_panel_type( 'WP_Customize_Themes_Panel' ); |
910 $this->register_panel_type( 'WP_Customize_Themes_Panel' ); |
906 $this->register_section_type( 'WP_Customize_Section' ); |
911 $this->register_section_type( 'WP_Customize_Section' ); |
907 $this->register_section_type( 'WP_Customize_Sidebar_Section' ); |
912 $this->register_section_type( 'WP_Customize_Sidebar_Section' ); |
908 $this->register_section_type( 'WP_Customize_Themes_Section' ); |
913 $this->register_section_type( 'WP_Customize_Themes_Section' ); |
1114 } |
1119 } |
1115 } elseif ( 'customize_changeset' !== $changeset_post->post_type ) { |
1120 } elseif ( 'customize_changeset' !== $changeset_post->post_type ) { |
1116 return new WP_Error( 'wrong_post_type' ); |
1121 return new WP_Error( 'wrong_post_type' ); |
1117 } |
1122 } |
1118 $changeset_data = json_decode( $changeset_post->post_content, true ); |
1123 $changeset_data = json_decode( $changeset_post->post_content, true ); |
1119 if ( function_exists( 'json_last_error' ) && json_last_error() ) { |
1124 $last_error = json_last_error(); |
1120 return new WP_Error( 'json_parse_error', '', json_last_error() ); |
1125 if ( $last_error ) { |
|
1126 return new WP_Error( 'json_parse_error', '', $last_error ); |
1121 } |
1127 } |
1122 if ( ! is_array( $changeset_data ) ) { |
1128 if ( ! is_array( $changeset_data ) ) { |
1123 return new WP_Error( 'expected_array' ); |
1129 return new WP_Error( 'expected_array' ); |
1124 } |
1130 } |
1125 return $changeset_data; |
1131 return $changeset_data; |
1373 array( |
1379 array( |
1374 'post_status' => 'auto-draft', // So attachment will be garbage collected in a week if changeset is never published. |
1380 'post_status' => 'auto-draft', // So attachment will be garbage collected in a week if changeset is never published. |
1375 ) |
1381 ) |
1376 ); |
1382 ); |
1377 |
1383 |
1378 // In PHP < 5.6 filesize() returns 0 for the temp files unless we clear the file status cache. |
|
1379 // Technically, PHP < 5.6.0 || < 5.5.13 || < 5.4.29 but no need to be so targeted. |
|
1380 // See https://bugs.php.net/bug.php?id=65701 |
|
1381 if ( version_compare( PHP_VERSION, '5.6', '<' ) ) { |
|
1382 clearstatcache(); |
|
1383 } |
|
1384 |
|
1385 $attachment_id = media_handle_sideload( $file_array, 0, null, $attachment_post_data ); |
1384 $attachment_id = media_handle_sideload( $file_array, 0, null, $attachment_post_data ); |
1386 if ( is_wp_error( $attachment_id ) ) { |
1385 if ( is_wp_error( $attachment_id ) ) { |
1387 continue; |
1386 continue; |
1388 } |
1387 } |
1389 update_post_meta( $attachment_id, '_starter_content_theme', $this->get_stylesheet() ); |
1388 update_post_meta( $attachment_id, '_starter_content_theme', $this->get_stylesheet() ); |
1521 } |
1520 } |
1522 } |
1521 } |
1523 |
1522 |
1524 // Options. |
1523 // Options. |
1525 foreach ( $options as $name => $value ) { |
1524 foreach ( $options as $name => $value ) { |
1526 if ( preg_match( '/^{{(?P<symbol>.+)}}$/', $value, $matches ) ) { |
1525 |
|
1526 // Serialize the value to check for post symbols. |
|
1527 $value = maybe_serialize( $value ); |
|
1528 |
|
1529 if ( is_serialized( $value ) ) { |
|
1530 if ( preg_match( '/s:\d+:"{{(?P<symbol>.+)}}"/', $value, $matches ) ) { |
|
1531 if ( isset( $posts[ $matches['symbol'] ] ) ) { |
|
1532 $symbol_match = $posts[ $matches['symbol'] ]['ID']; |
|
1533 } elseif ( isset( $attachment_ids[ $matches['symbol'] ] ) ) { |
|
1534 $symbol_match = $attachment_ids[ $matches['symbol'] ]; |
|
1535 } |
|
1536 |
|
1537 // If we have any symbol matches, update the values. |
|
1538 if ( isset( $symbol_match ) ) { |
|
1539 // Replace found string matches with post IDs. |
|
1540 $value = str_replace( $matches[0], "i:{$symbol_match}", $value ); |
|
1541 } else { |
|
1542 continue; |
|
1543 } |
|
1544 } |
|
1545 } elseif ( preg_match( '/^{{(?P<symbol>.+)}}$/', $value, $matches ) ) { |
1527 if ( isset( $posts[ $matches['symbol'] ] ) ) { |
1546 if ( isset( $posts[ $matches['symbol'] ] ) ) { |
1528 $value = $posts[ $matches['symbol'] ]['ID']; |
1547 $value = $posts[ $matches['symbol'] ]['ID']; |
1529 } elseif ( isset( $attachment_ids[ $matches['symbol'] ] ) ) { |
1548 } elseif ( isset( $attachment_ids[ $matches['symbol'] ] ) ) { |
1530 $value = $attachment_ids[ $matches['symbol'] ]; |
1549 $value = $attachment_ids[ $matches['symbol'] ]; |
1531 } else { |
1550 } else { |
1532 continue; |
1551 continue; |
1533 } |
1552 } |
1534 } |
1553 } |
1535 |
1554 |
|
1555 // Unserialize values after checking for post symbols, so they can be properly referenced. |
|
1556 $value = maybe_unserialize( $value ); |
|
1557 |
1536 if ( empty( $changeset_data[ $name ] ) || ! empty( $changeset_data[ $name ]['starter_content'] ) ) { |
1558 if ( empty( $changeset_data[ $name ] ) || ! empty( $changeset_data[ $name ]['starter_content'] ) ) { |
1537 $this->set_post_value( $name, $value ); |
1559 $this->set_post_value( $name, $value ); |
1538 $this->pending_starter_content_settings_ids[] = $name; |
1560 $this->pending_starter_content_settings_ids[] = $name; |
1539 } |
1561 } |
1540 } |
1562 } |
1541 |
1563 |
1542 // Theme mods. |
1564 // Theme mods. |
1543 foreach ( $theme_mods as $name => $value ) { |
1565 foreach ( $theme_mods as $name => $value ) { |
1544 if ( preg_match( '/^{{(?P<symbol>.+)}}$/', $value, $matches ) ) { |
1566 |
|
1567 // Serialize the value to check for post symbols. |
|
1568 $value = maybe_serialize( $value ); |
|
1569 |
|
1570 // Check if value was serialized. |
|
1571 if ( is_serialized( $value ) ) { |
|
1572 if ( preg_match( '/s:\d+:"{{(?P<symbol>.+)}}"/', $value, $matches ) ) { |
|
1573 if ( isset( $posts[ $matches['symbol'] ] ) ) { |
|
1574 $symbol_match = $posts[ $matches['symbol'] ]['ID']; |
|
1575 } elseif ( isset( $attachment_ids[ $matches['symbol'] ] ) ) { |
|
1576 $symbol_match = $attachment_ids[ $matches['symbol'] ]; |
|
1577 } |
|
1578 |
|
1579 // If we have any symbol matches, update the values. |
|
1580 if ( isset( $symbol_match ) ) { |
|
1581 // Replace found string matches with post IDs. |
|
1582 $value = str_replace( $matches[0], "i:{$symbol_match}", $value ); |
|
1583 } else { |
|
1584 continue; |
|
1585 } |
|
1586 } |
|
1587 } elseif ( preg_match( '/^{{(?P<symbol>.+)}}$/', $value, $matches ) ) { |
1545 if ( isset( $posts[ $matches['symbol'] ] ) ) { |
1588 if ( isset( $posts[ $matches['symbol'] ] ) ) { |
1546 $value = $posts[ $matches['symbol'] ]['ID']; |
1589 $value = $posts[ $matches['symbol'] ]['ID']; |
1547 } elseif ( isset( $attachment_ids[ $matches['symbol'] ] ) ) { |
1590 } elseif ( isset( $attachment_ids[ $matches['symbol'] ] ) ) { |
1548 $value = $attachment_ids[ $matches['symbol'] ]; |
1591 $value = $attachment_ids[ $matches['symbol'] ]; |
1549 } else { |
1592 } else { |
1550 continue; |
1593 continue; |
1551 } |
1594 } |
1552 } |
1595 } |
|
1596 |
|
1597 // Unserialize values after checking for post symbols, so they can be properly referenced. |
|
1598 $value = maybe_unserialize( $value ); |
1553 |
1599 |
1554 // Handle header image as special case since setting has a legacy format. |
1600 // Handle header image as special case since setting has a legacy format. |
1555 if ( 'header_image' === $name ) { |
1601 if ( 'header_image' === $name ) { |
1556 $name = 'header_image_data'; |
1602 $name = 'header_image_data'; |
1557 $metadata = wp_get_attachment_metadata( $value ); |
1603 $metadata = wp_get_attachment_metadata( $value ); |
1598 if ( empty( $attachments ) ) { |
1644 if ( empty( $attachments ) ) { |
1599 return $prepared_attachments; |
1645 return $prepared_attachments; |
1600 } |
1646 } |
1601 |
1647 |
1602 // Such is The WordPress Way. |
1648 // Such is The WordPress Way. |
1603 require_once( ABSPATH . 'wp-admin/includes/file.php' ); |
1649 require_once ABSPATH . 'wp-admin/includes/file.php'; |
1604 require_once( ABSPATH . 'wp-admin/includes/media.php' ); |
1650 require_once ABSPATH . 'wp-admin/includes/media.php'; |
1605 require_once( ABSPATH . 'wp-admin/includes/image.php' ); |
1651 require_once ABSPATH . 'wp-admin/includes/image.php'; |
1606 |
1652 |
1607 foreach ( $attachments as $symbol => $attachment ) { |
1653 foreach ( $attachments as $symbol => $attachment ) { |
1608 |
1654 |
1609 // A file is required and URLs to files are not currently allowed. |
1655 // A file is required and URLs to files are not currently allowed. |
1610 if ( empty( $attachment['file'] ) || preg_match( '#^https?://$#', $attachment['file'] ) ) { |
1656 if ( empty( $attachment['file'] ) || preg_match( '#^https?://$#', $attachment['file'] ) ) { |
2727 * A changeset update is transactional when a status is supplied in the request. |
2781 * A changeset update is transactional when a status is supplied in the request. |
2728 */ |
2782 */ |
2729 if ( $update_transactionally && $invalid_setting_count > 0 ) { |
2783 if ( $update_transactionally && $invalid_setting_count > 0 ) { |
2730 $response = array( |
2784 $response = array( |
2731 'setting_validities' => $setting_validities, |
2785 'setting_validities' => $setting_validities, |
2732 /* translators: %s: number of invalid settings */ |
2786 /* translators: %s: Number of invalid settings. */ |
2733 'message' => sprintf( _n( 'Unable to save due to %s invalid setting.', 'Unable to save due to %s invalid settings.', $invalid_setting_count ), number_format_i18n( $invalid_setting_count ) ), |
2787 'message' => sprintf( _n( 'Unable to save due to %s invalid setting.', 'Unable to save due to %s invalid settings.', $invalid_setting_count ), number_format_i18n( $invalid_setting_count ) ), |
2734 ); |
2788 ); |
2735 return new WP_Error( 'transaction_fail', '', $response ); |
2789 return new WP_Error( 'transaction_fail', '', $response ); |
2736 } |
2790 } |
2737 |
2791 |
2841 update_option( 'theme_switched_via_customizer', true ); |
2895 update_option( 'theme_switched_via_customizer', true ); |
2842 $this->start_previewing_theme(); |
2896 $this->start_previewing_theme(); |
2843 } |
2897 } |
2844 |
2898 |
2845 // Gather the data for wp_insert_post()/wp_update_post(). |
2899 // Gather the data for wp_insert_post()/wp_update_post(). |
2846 $json_options = 0; |
2900 $post_array = array( |
2847 if ( defined( 'JSON_UNESCAPED_SLASHES' ) ) { |
2901 // JSON_UNESCAPED_SLASHES is only to improve readability as slashes needn't be escaped in storage. |
2848 $json_options |= JSON_UNESCAPED_SLASHES; // Introduced in PHP 5.4. This is only to improve readability as slashes needn't be escaped in storage. |
2902 'post_content' => wp_json_encode( $data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT ), |
2849 } |
|
2850 $json_options |= JSON_PRETTY_PRINT; // Also introduced in PHP 5.4, but WP defines constant for back compat. See WP Trac #30139. |
|
2851 $post_array = array( |
|
2852 'post_content' => wp_json_encode( $data, $json_options ), |
|
2853 ); |
2903 ); |
2854 if ( $args['title'] ) { |
2904 if ( $args['title'] ) { |
2855 $post_array['post_title'] = $args['title']; |
2905 $post_array['post_title'] = $args['title']; |
2856 } |
2906 } |
2857 if ( $changeset_post_id ) { |
2907 if ( $changeset_post_id ) { |
2884 |
2934 |
2885 $this->store_changeset_revision = $allow_revision; |
2935 $this->store_changeset_revision = $allow_revision; |
2886 add_filter( 'wp_save_post_revision_post_has_changed', array( $this, '_filter_revision_post_has_changed' ), 5, 3 ); |
2936 add_filter( 'wp_save_post_revision_post_has_changed', array( $this, '_filter_revision_post_has_changed' ), 5, 3 ); |
2887 |
2937 |
2888 /* |
2938 /* |
2889 * Update the changeset post. The publish_customize_changeset action |
2939 * Update the changeset post. The publish_customize_changeset action will cause the settings in the |
2890 * will cause the settings in the changeset to be saved via |
2940 * changeset to be saved via WP_Customize_Setting::save(). Updating a post with publish status will |
2891 * WP_Customize_Setting::save(). |
2941 * trigger WP_Customize_Manager::publish_changeset_values(). |
2892 */ |
2942 */ |
2893 |
2943 add_filter( 'wp_insert_post_data', array( $this, 'preserve_insert_changeset_post_content' ), 5, 3 ); |
2894 // Prevent content filters from corrupting JSON in post_content. |
|
2895 $has_kses = ( false !== has_filter( 'content_save_pre', 'wp_filter_post_kses' ) ); |
|
2896 if ( $has_kses ) { |
|
2897 kses_remove_filters(); |
|
2898 } |
|
2899 $has_targeted_link_rel_filters = ( false !== has_filter( 'content_save_pre', 'wp_targeted_link_rel' ) ); |
|
2900 if ( $has_targeted_link_rel_filters ) { |
|
2901 wp_remove_targeted_link_rel_filters(); |
|
2902 } |
|
2903 |
|
2904 // Note that updating a post with publish status will trigger WP_Customize_Manager::publish_changeset_values(). |
|
2905 if ( $changeset_post_id ) { |
2944 if ( $changeset_post_id ) { |
2906 if ( $args['autosave'] && 'auto-draft' !== get_post_status( $changeset_post_id ) ) { |
2945 if ( $args['autosave'] && 'auto-draft' !== get_post_status( $changeset_post_id ) ) { |
2907 // See _wp_translate_postdata() for why this is required as it will use the edit_post meta capability. |
2946 // See _wp_translate_postdata() for why this is required as it will use the edit_post meta capability. |
2908 add_filter( 'map_meta_cap', array( $this, 'grant_edit_post_capability_for_changeset' ), 10, 4 ); |
2947 add_filter( 'map_meta_cap', array( $this, 'grant_edit_post_capability_for_changeset' ), 10, 4 ); |
|
2948 |
2909 $post_array['post_ID'] = $post_array['ID']; |
2949 $post_array['post_ID'] = $post_array['ID']; |
2910 $post_array['post_type'] = 'customize_changeset'; |
2950 $post_array['post_type'] = 'customize_changeset'; |
2911 $r = wp_create_post_autosave( wp_slash( $post_array ) ); |
2951 |
|
2952 $r = wp_create_post_autosave( wp_slash( $post_array ) ); |
|
2953 |
2912 remove_filter( 'map_meta_cap', array( $this, 'grant_edit_post_capability_for_changeset' ), 10 ); |
2954 remove_filter( 'map_meta_cap', array( $this, 'grant_edit_post_capability_for_changeset' ), 10 ); |
2913 } else { |
2955 } else { |
2914 $post_array['edit_date'] = true; // Prevent date clearing. |
2956 $post_array['edit_date'] = true; // Prevent date clearing. |
2915 $r = wp_update_post( wp_slash( $post_array ), true ); |
2957 |
|
2958 $r = wp_update_post( wp_slash( $post_array ), true ); |
2916 |
2959 |
2917 // Delete autosave revision for user when the changeset is updated. |
2960 // Delete autosave revision for user when the changeset is updated. |
2918 if ( ! empty( $args['user_id'] ) ) { |
2961 if ( ! empty( $args['user_id'] ) ) { |
2919 $autosave_draft = wp_get_post_autosave( $changeset_post_id, $args['user_id'] ); |
2962 $autosave_draft = wp_get_post_autosave( $changeset_post_id, $args['user_id'] ); |
2920 if ( $autosave_draft ) { |
2963 if ( $autosave_draft ) { |
2926 $r = wp_insert_post( wp_slash( $post_array ), true ); |
2969 $r = wp_insert_post( wp_slash( $post_array ), true ); |
2927 if ( ! is_wp_error( $r ) ) { |
2970 if ( ! is_wp_error( $r ) ) { |
2928 $this->_changeset_post_id = $r; // Update cached post ID for the loaded changeset. |
2971 $this->_changeset_post_id = $r; // Update cached post ID for the loaded changeset. |
2929 } |
2972 } |
2930 } |
2973 } |
2931 |
2974 remove_filter( 'wp_insert_post_data', array( $this, 'preserve_insert_changeset_post_content' ), 5 ); |
2932 // Restore removed content filters. |
|
2933 if ( $has_kses ) { |
|
2934 kses_init_filters(); |
|
2935 } |
|
2936 if ( $has_targeted_link_rel_filters ) { |
|
2937 wp_init_targeted_link_rel_filters(); |
|
2938 } |
|
2939 |
2975 |
2940 $this->_changeset_data = null; // Reset so WP_Customize_Manager::changeset_data() will re-populate with updated contents. |
2976 $this->_changeset_data = null; // Reset so WP_Customize_Manager::changeset_data() will re-populate with updated contents. |
2941 |
2977 |
2942 remove_filter( 'wp_save_post_revision_post_has_changed', array( $this, '_filter_revision_post_has_changed' ) ); |
2978 remove_filter( 'wp_save_post_revision_post_has_changed', array( $this, '_filter_revision_post_has_changed' ) ); |
2943 |
2979 |
2949 $response['changeset_post_save_failure'] = $r->get_error_code(); |
2985 $response['changeset_post_save_failure'] = $r->get_error_code(); |
2950 return new WP_Error( 'changeset_post_save_failure', '', $response ); |
2986 return new WP_Error( 'changeset_post_save_failure', '', $response ); |
2951 } |
2987 } |
2952 |
2988 |
2953 return $response; |
2989 return $response; |
|
2990 } |
|
2991 |
|
2992 /** |
|
2993 * Preserve the initial JSON post_content passed to save into the post. |
|
2994 * |
|
2995 * This is needed to prevent KSES and other {@see 'content_save_pre'} filters |
|
2996 * from corrupting JSON data. |
|
2997 * |
|
2998 * Note that WP_Customize_Manager::validate_setting_values() have already |
|
2999 * run on the setting values being serialized as JSON into the post content |
|
3000 * so it is pre-sanitized. |
|
3001 * |
|
3002 * Also, the sanitization logic is re-run through the respective |
|
3003 * WP_Customize_Setting::sanitize() method when being read out of the |
|
3004 * changeset, via WP_Customize_Manager::post_value(), and this sanitized |
|
3005 * value will also be sent into WP_Customize_Setting::update() for |
|
3006 * persisting to the DB. |
|
3007 * |
|
3008 * Multiple users can collaborate on a single changeset, where one user may |
|
3009 * have the unfiltered_html capability but another may not. A user with |
|
3010 * unfiltered_html may add a script tag to some field which needs to be kept |
|
3011 * intact even when another user updates the changeset to modify another field |
|
3012 * when they do not have unfiltered_html. |
|
3013 * |
|
3014 * @since 5.4.1 |
|
3015 * |
|
3016 * @param array $data An array of slashed and processed post data. |
|
3017 * @param array $postarr An array of sanitized (and slashed) but otherwise unmodified post data. |
|
3018 * @param array $unsanitized_postarr An array of slashed yet *unsanitized* and unprocessed post data as originally passed to wp_insert_post(). |
|
3019 * @return array Filtered post data. |
|
3020 */ |
|
3021 public function preserve_insert_changeset_post_content( $data, $postarr, $unsanitized_postarr ) { |
|
3022 if ( |
|
3023 isset( $data['post_type'] ) && |
|
3024 isset( $unsanitized_postarr['post_content'] ) && |
|
3025 'customize_changeset' === $data['post_type'] || |
|
3026 ( |
|
3027 'revision' === $data['post_type'] && |
|
3028 ! empty( $data['post_parent'] ) && |
|
3029 'customize_changeset' === get_post_type( $data['post_parent'] ) |
|
3030 ) |
|
3031 ) { |
|
3032 $data['post_content'] = $unsanitized_postarr['post_content']; |
|
3033 } |
|
3034 return $data; |
2954 } |
3035 } |
2955 |
3036 |
2956 /** |
3037 /** |
2957 * Trash or delete a changeset post. |
3038 * Trash or delete a changeset post. |
2958 * |
3039 * |
3109 * `_wp_translate_postdata()` which in turn will check if a user can 'edit_post', but the |
3205 * `_wp_translate_postdata()` which in turn will check if a user can 'edit_post', but the |
3110 * the caps for the customize_changeset post type are all mapping to the meta capability. |
3206 * the caps for the customize_changeset post type are all mapping to the meta capability. |
3111 * This should be able to be removed once #40922 is addressed in core. |
3207 * This should be able to be removed once #40922 is addressed in core. |
3112 * |
3208 * |
3113 * @since 4.9.0 |
3209 * @since 4.9.0 |
|
3210 * |
3114 * @link https://core.trac.wordpress.org/ticket/40922 |
3211 * @link https://core.trac.wordpress.org/ticket/40922 |
3115 * @see WP_Customize_Manager::save_changeset_post() |
3212 * @see WP_Customize_Manager::save_changeset_post() |
3116 * @see _wp_translate_postdata() |
3213 * @see _wp_translate_postdata() |
3117 * |
3214 * |
3118 * @param string[] $caps Array of the user's capabilities. |
3215 * @param string[] $caps Array of the user's capabilities. |
3119 * @param string $cap Capability name. |
3216 * @param string $cap Capability name. |
3120 * @param int $user_id The user ID. |
3217 * @param int $user_id The user ID. |
3121 * @param array $args Adds the context to the cap. Typically the object ID. |
3218 * @param array $args Adds the context to the cap. Typically the object ID. |
3122 * @return array Capabilities. |
3219 * @return array Capabilities. |
3123 */ |
3220 */ |
3124 public function grant_edit_post_capability_for_changeset( $caps, $cap, $user_id, $args ) { |
3221 public function grant_edit_post_capability_for_changeset( $caps, $cap, $user_id, $args ) { |
3125 if ( 'edit_post' === $cap && ! empty( $args[0] ) && 'customize_changeset' === get_post_type( $args[0] ) ) { |
3222 if ( 'edit_post' === $cap && ! empty( $args[0] ) && 'customize_changeset' === get_post_type( $args[0] ) ) { |
3126 $post_type_obj = get_post_type_object( 'customize_changeset' ); |
3223 $post_type_obj = get_post_type_object( 'customize_changeset' ); |
3127 $caps = map_meta_cap( $post_type_obj->cap->$cap, $user_id ); |
3224 $caps = map_meta_cap( $post_type_obj->cap->$cap, $user_id ); |
3637 * Add a customize setting. |
3734 * Add a customize setting. |
3638 * |
3735 * |
3639 * @since 3.4.0 |
3736 * @since 3.4.0 |
3640 * @since 4.5.0 Return added WP_Customize_Setting instance. |
3737 * @since 4.5.0 Return added WP_Customize_Setting instance. |
3641 * |
3738 * |
|
3739 * @see WP_Customize_Setting::__construct() |
|
3740 * @link https://developer.wordpress.org/themes/customize-api |
|
3741 * |
3642 * @param WP_Customize_Setting|string $id Customize Setting object, or ID. |
3742 * @param WP_Customize_Setting|string $id Customize Setting object, or ID. |
3643 * @param array $args { |
3743 * @param array $args Optional. Array of properties for the new Setting object. |
3644 * Optional. Array of properties for the new WP_Customize_Setting. Default empty array. |
3744 * See WP_Customize_Setting::__construct() for information |
3645 * |
3745 * on accepted arguments. Default empty array. |
3646 * @type string $type Type of the setting. Default 'theme_mod'. |
3746 * @return WP_Customize_Setting The instance of the setting that was added. |
3647 * @type string $capability Capability required for the setting. Default 'edit_theme_options' |
|
3648 * @type string|array $theme_supports Theme features required to support the panel. Default is none. |
|
3649 * @type string $default Default value for the setting. Default is empty string. |
|
3650 * @type string $transport Options for rendering the live preview of changes in Customizer. |
|
3651 * Using 'refresh' makes the change visible by reloading the whole preview. |
|
3652 * Using 'postMessage' allows a custom JavaScript to handle live changes. |
|
3653 * @link https://developer.wordpress.org/themes/customize-api |
|
3654 * Default is 'refresh' |
|
3655 * @type callable $validate_callback Server-side validation callback for the setting's value. |
|
3656 * @type callable $sanitize_callback Callback to filter a Customize setting value in un-slashed form. |
|
3657 * @type callable $sanitize_js_callback Callback to convert a Customize PHP setting value to a value that is |
|
3658 * JSON serializable. |
|
3659 * @type bool $dirty Whether or not the setting is initially dirty when created. |
|
3660 * } |
|
3661 * @return WP_Customize_Setting The instance of the setting that was added. |
|
3662 */ |
3747 */ |
3663 public function add_setting( $id, $args = array() ) { |
3748 public function add_setting( $id, $args = array() ) { |
3664 if ( $id instanceof WP_Customize_Setting ) { |
3749 if ( $id instanceof WP_Customize_Setting ) { |
3665 $setting = $id; |
3750 $setting = $id; |
3666 } else { |
3751 } else { |
3769 * Add a customize panel. |
3856 * Add a customize panel. |
3770 * |
3857 * |
3771 * @since 4.0.0 |
3858 * @since 4.0.0 |
3772 * @since 4.5.0 Return added WP_Customize_Panel instance. |
3859 * @since 4.5.0 Return added WP_Customize_Panel instance. |
3773 * |
3860 * |
3774 * @param WP_Customize_Panel|string $id Customize Panel object, or Panel ID. |
3861 * @see WP_Customize_Panel::__construct() |
3775 * @param array $args { |
3862 * |
3776 * Optional. Array of properties for the new Panel object. Default empty array. |
3863 * @param WP_Customize_Panel|string $id Customize Panel object, or ID. |
3777 * @type int $priority Priority of the panel, defining the display order of panels and sections. |
3864 * @param array $args Optional. Array of properties for the new Panel object. |
3778 * Default 160. |
3865 * See WP_Customize_Panel::__construct() for information |
3779 * @type string $capability Capability required for the panel. Default `edit_theme_options` |
3866 * on accepted arguments. Default empty array. |
3780 * @type string|array $theme_supports Theme features required to support the panel. |
3867 * @return WP_Customize_Panel The instance of the panel that was added. |
3781 * @type string $title Title of the panel to show in UI. |
|
3782 * @type string $description Description to show in the UI. |
|
3783 * @type string $type Type of the panel. |
|
3784 * @type callable $active_callback Active callback. |
|
3785 * } |
|
3786 * @return WP_Customize_Panel The instance of the panel that was added. |
|
3787 */ |
3868 */ |
3788 public function add_panel( $id, $args = array() ) { |
3869 public function add_panel( $id, $args = array() ) { |
3789 if ( $id instanceof WP_Customize_Panel ) { |
3870 if ( $id instanceof WP_Customize_Panel ) { |
3790 $panel = $id; |
3871 $panel = $id; |
3791 } else { |
3872 } else { |
3811 } |
3892 } |
3812 |
3893 |
3813 /** |
3894 /** |
3814 * Remove a customize panel. |
3895 * Remove a customize panel. |
3815 * |
3896 * |
|
3897 * Note that removing the panel doesn't destroy the WP_Customize_Panel instance or remove its filters. |
|
3898 * |
3816 * @since 4.0.0 |
3899 * @since 4.0.0 |
3817 * |
3900 * |
3818 * @param string $id Panel ID to remove. |
3901 * @param string $id Panel ID to remove. |
3819 */ |
3902 */ |
3820 public function remove_panel( $id ) { |
3903 public function remove_panel( $id ) { |
3821 // Removing core components this way is _doing_it_wrong(). |
3904 // Removing core components this way is _doing_it_wrong(). |
3822 if ( in_array( $id, $this->components, true ) ) { |
3905 if ( in_array( $id, $this->components, true ) ) { |
3823 /* translators: 1: panel id, 2: link to 'customize_loaded_components' filter reference */ |
|
3824 $message = sprintf( |
3906 $message = sprintf( |
|
3907 /* translators: 1: Panel ID, 2: Link to 'customize_loaded_components' filter reference. */ |
3825 __( 'Removing %1$s manually will cause PHP warnings. Use the %2$s filter instead.' ), |
3908 __( 'Removing %1$s manually will cause PHP warnings. Use the %2$s filter instead.' ), |
3826 $id, |
3909 $id, |
3827 '<a href="' . esc_url( 'https://developer.wordpress.org/reference/hooks/customize_loaded_components/' ) . '"><code>customize_loaded_components</code></a>' |
3910 sprintf( |
|
3911 '<a href="%1$s">%2$s</a>', |
|
3912 esc_url( 'https://developer.wordpress.org/reference/hooks/customize_loaded_components/' ), |
|
3913 '<code>customize_loaded_components</code>' |
|
3914 ) |
3828 ); |
3915 ); |
3829 |
3916 |
3830 _doing_it_wrong( __METHOD__, $message, '4.5.0' ); |
3917 _doing_it_wrong( __METHOD__, $message, '4.5.0' ); |
3831 } |
3918 } |
3832 unset( $this->panels[ $id ] ); |
3919 unset( $this->panels[ $id ] ); |
3863 * Add a customize section. |
3950 * Add a customize section. |
3864 * |
3951 * |
3865 * @since 3.4.0 |
3952 * @since 3.4.0 |
3866 * @since 4.5.0 Return added WP_Customize_Section instance. |
3953 * @since 4.5.0 Return added WP_Customize_Section instance. |
3867 * |
3954 * |
3868 * @param WP_Customize_Section|string $id Customize Section object, or Section ID. |
3955 * @see WP_Customize_Section::__construct() |
3869 * @param array $args { |
3956 * |
3870 * Optional. Array of properties for the new Section object. Default empty array. |
3957 * @param WP_Customize_Section|string $id Customize Section object, or ID. |
3871 * @type int $priority Priority of the section, defining the display order of panels and sections. |
3958 * @param array $args Optional. Array of properties for the new Section object. |
3872 * Default 160. |
3959 * See WP_Customize_Section::__construct() for information |
3873 * @type string $panel The panel this section belongs to (if any). Default empty. |
3960 * on accepted arguments. Default empty array. |
3874 * @type string $capability Capability required for the section. Default 'edit_theme_options' |
3961 * @return WP_Customize_Section The instance of the section that was added. |
3875 * @type string|array $theme_supports Theme features required to support the section. |
|
3876 * @type string $title Title of the section to show in UI. |
|
3877 * @type string $description Description to show in the UI. |
|
3878 * @type string $type Type of the section. |
|
3879 * @type callable $active_callback Active callback. |
|
3880 * @type bool $description_hidden Hide the description behind a help icon, instead of inline above the first control. Default false. |
|
3881 * } |
|
3882 * @return WP_Customize_Section The instance of the section that was added. |
|
3883 */ |
3962 */ |
3884 public function add_section( $id, $args = array() ) { |
3963 public function add_section( $id, $args = array() ) { |
3885 if ( $id instanceof WP_Customize_Section ) { |
3964 if ( $id instanceof WP_Customize_Section ) { |
3886 $section = $id; |
3965 $section = $id; |
3887 } else { |
3966 } else { |
3948 * Add a customize control. |
4029 * Add a customize control. |
3949 * |
4030 * |
3950 * @since 3.4.0 |
4031 * @since 3.4.0 |
3951 * @since 4.5.0 Return added WP_Customize_Control instance. |
4032 * @since 4.5.0 Return added WP_Customize_Control instance. |
3952 * |
4033 * |
|
4034 * @see WP_Customize_Control::__construct() |
|
4035 * |
3953 * @param WP_Customize_Control|string $id Customize Control object, or ID. |
4036 * @param WP_Customize_Control|string $id Customize Control object, or ID. |
3954 * @param array $args { |
4037 * @param array $args Optional. Array of properties for the new Control object. |
3955 * Optional. Array of properties for the new Control object. Default empty array. |
4038 * See WP_Customize_Control::__construct() for information |
3956 * |
4039 * on accepted arguments. Default empty array. |
3957 * @type array $settings All settings tied to the control. If undefined, defaults to `$setting`. |
4040 * @return WP_Customize_Control The instance of the control that was added. |
3958 * IDs in the array correspond to the ID of a registered `WP_Customize_Setting`. |
|
3959 * @type string $setting The primary setting for the control (if there is one). Default is 'default'. |
|
3960 * @type string $capability Capability required to use this control. Normally derived from `$settings`. |
|
3961 * @type int $priority Order priority to load the control. Default 10. |
|
3962 * @type string $section The section this control belongs to. Default empty. |
|
3963 * @type string $label Label for the control. Default empty. |
|
3964 * @type string $description Description for the control. Default empty. |
|
3965 * @type array $choices List of choices for 'radio' or 'select' type controls, where values |
|
3966 * are the keys, and labels are the values. Default empty array. |
|
3967 * @type array $input_attrs List of custom input attributes for control output, where attribute |
|
3968 * names are the keys and values are the values. Default empty array. |
|
3969 * @type bool $allow_addition Show UI for adding new content, currently only used for the |
|
3970 * dropdown-pages control. Default false. |
|
3971 * @type string $type The type of the control. Default 'text'. |
|
3972 * @type callback $active_callback Active callback. |
|
3973 * } |
|
3974 * @return WP_Customize_Control The instance of the control that was added. |
|
3975 */ |
4041 */ |
3976 public function add_control( $id, $args = array() ) { |
4042 public function add_control( $id, $args = array() ) { |
3977 if ( $id instanceof WP_Customize_Control ) { |
4043 if ( $id instanceof WP_Customize_Control ) { |
3978 $control = $id; |
4044 $control = $id; |
3979 } else { |
4045 } else { |
4465 * |
4533 * |
4466 * @return string The template string for the document title. |
4534 * @return string The template string for the document title. |
4467 */ |
4535 */ |
4468 public function get_document_title_template() { |
4536 public function get_document_title_template() { |
4469 if ( $this->is_theme_active() ) { |
4537 if ( $this->is_theme_active() ) { |
4470 /* translators: %s: document title from the preview */ |
4538 /* translators: %s: Document title from the preview. */ |
4471 $document_title_tmpl = __( 'Customize: %s' ); |
4539 $document_title_tmpl = __( 'Customize: %s' ); |
4472 } else { |
4540 } else { |
4473 /* translators: %s: document title from the preview */ |
4541 /* translators: %s: Document title from the preview. */ |
4474 $document_title_tmpl = __( 'Live Preview: %s' ); |
4542 $document_title_tmpl = __( 'Live Preview: %s' ); |
4475 } |
4543 } |
4476 $document_title_tmpl = html_entity_decode( $document_title_tmpl, ENT_QUOTES, 'UTF-8' ); // Because exported to JS and assigned to document.title. |
4544 $document_title_tmpl = html_entity_decode( $document_title_tmpl, ENT_QUOTES, 'UTF-8' ); // Because exported to JS and assigned to document.title. |
4477 return $document_title_tmpl; |
4545 return $document_title_tmpl; |
4478 } |
4546 } |
4610 * @since 4.4.0 |
4698 * @since 4.4.0 |
4611 * |
4699 * |
4612 * @param array $autofocus { |
4700 * @param array $autofocus { |
4613 * Mapping of 'panel', 'section', 'control' to the ID which should be autofocused. |
4701 * Mapping of 'panel', 'section', 'control' to the ID which should be autofocused. |
4614 * |
4702 * |
4615 * @type string [$control] ID for control to be autofocused. |
4703 * @type string $control ID for control to be autofocused. |
4616 * @type string [$section] ID for section to be autofocused. |
4704 * @type string $section ID for section to be autofocused. |
4617 * @type string [$panel] ID for panel to be autofocused. |
4705 * @type string $panel ID for panel to be autofocused. |
4618 * } |
4706 * } |
4619 */ |
4707 */ |
4620 public function set_autofocus( $autofocus ) { |
4708 public function set_autofocus( $autofocus ) { |
4621 $this->autofocus = array_filter( wp_array_slice_assoc( $autofocus, array( 'panel', 'section', 'control' ) ), 'is_string' ); |
4709 $this->autofocus = array_filter( wp_array_slice_assoc( $autofocus, array( 'panel', 'section', 'control' ) ), 'is_string' ); |
4622 } |
4710 } |
4807 'autofocus' => $this->get_autofocus(), |
4895 'autofocus' => $this->get_autofocus(), |
4808 'documentTitleTmpl' => $this->get_document_title_template(), |
4896 'documentTitleTmpl' => $this->get_document_title_template(), |
4809 'previewableDevices' => $this->get_previewable_devices(), |
4897 'previewableDevices' => $this->get_previewable_devices(), |
4810 'l10n' => array( |
4898 'l10n' => array( |
4811 'confirmDeleteTheme' => __( 'Are you sure you want to delete this theme?' ), |
4899 'confirmDeleteTheme' => __( 'Are you sure you want to delete this theme?' ), |
4812 /* translators: %d: number of theme search results, which cannot currently consider singular vs. plural forms */ |
4900 /* translators: %d: Number of theme search results, which cannot currently consider singular vs. plural forms. */ |
4813 'themeSearchResults' => __( '%d themes found' ), |
4901 'themeSearchResults' => __( '%d themes found' ), |
4814 /* translators: %d: number of themes being displayed, which cannot currently consider singular vs. plural forms */ |
4902 /* translators: %d: Number of themes being displayed, which cannot currently consider singular vs. plural forms. */ |
4815 'announceThemeCount' => __( 'Displaying %d themes' ), |
4903 'announceThemeCount' => __( 'Displaying %d themes' ), |
4816 /* translators: %s: theme name */ |
4904 /* translators: %s: Theme name. */ |
4817 'announceThemeDetails' => __( 'Showing details for theme: %s' ), |
4905 'announceThemeDetails' => __( 'Showing details for theme: %s' ), |
4818 ), |
4906 ), |
4819 ); |
4907 ); |
4820 |
4908 |
4821 // Temporarily disable installation in Customizer. See #42184. |
4909 // Temporarily disable installation in Customizer. See #42184. |
5096 'custom_logo', |
5184 'custom_logo', |
5097 array( |
5185 array( |
5098 'label' => __( 'Logo' ), |
5186 'label' => __( 'Logo' ), |
5099 'section' => 'title_tagline', |
5187 'section' => 'title_tagline', |
5100 'priority' => 8, |
5188 'priority' => 8, |
5101 'height' => $custom_logo_args[0]['height'], |
5189 'height' => isset( $custom_logo_args[0]['height'] ) ? $custom_logo_args[0]['height'] : null, |
5102 'width' => $custom_logo_args[0]['width'], |
5190 'width' => isset( $custom_logo_args[0]['width'] ) ? $custom_logo_args[0]['width'] : null, |
5103 'flex_height' => $custom_logo_args[0]['flex-height'], |
5191 'flex_height' => isset( $custom_logo_args[0]['flex-height'] ) ? $custom_logo_args[0]['flex-height'] : null, |
5104 'flex_width' => $custom_logo_args[0]['flex-width'], |
5192 'flex_width' => isset( $custom_logo_args[0]['flex-width'] ) ? $custom_logo_args[0]['flex-width'] : null, |
5105 'button_labels' => array( |
5193 'button_labels' => array( |
5106 'select' => __( 'Select logo' ), |
5194 'select' => __( 'Select logo' ), |
5107 'change' => __( 'Change logo' ), |
5195 'change' => __( 'Change logo' ), |
5108 'remove' => __( 'Remove' ), |
5196 'remove' => __( 'Remove' ), |
5109 'default' => __( 'Default' ), |
5197 'default' => __( 'Default' ), |
5202 |
5290 |
5203 $width = absint( get_theme_support( 'custom-header', 'width' ) ); |
5291 $width = absint( get_theme_support( 'custom-header', 'width' ) ); |
5204 $height = absint( get_theme_support( 'custom-header', 'height' ) ); |
5292 $height = absint( get_theme_support( 'custom-header', 'height' ) ); |
5205 if ( $width && $height ) { |
5293 if ( $width && $height ) { |
5206 $control_description = sprintf( |
5294 $control_description = sprintf( |
5207 /* translators: 1: .mp4, 2: header size in pixels */ |
5295 /* translators: 1: .mp4, 2: Header size in pixels. */ |
5208 __( 'Upload your video in %1$s format and minimize its file size for best results. Your theme recommends dimensions of %2$s pixels.' ), |
5296 __( 'Upload your video in %1$s format and minimize its file size for best results. Your theme recommends dimensions of %2$s pixels.' ), |
5209 '<code>.mp4</code>', |
5297 '<code>.mp4</code>', |
5210 sprintf( '<strong>%s × %s</strong>', $width, $height ) |
5298 sprintf( '<strong>%s × %s</strong>', $width, $height ) |
5211 ); |
5299 ); |
5212 } elseif ( $width ) { |
5300 } elseif ( $width ) { |
5213 $control_description = sprintf( |
5301 $control_description = sprintf( |
5214 /* translators: 1: .mp4, 2: header width in pixels */ |
5302 /* translators: 1: .mp4, 2: Header width in pixels. */ |
5215 __( 'Upload your video in %1$s format and minimize its file size for best results. Your theme recommends a width of %2$s pixels.' ), |
5303 __( 'Upload your video in %1$s format and minimize its file size for best results. Your theme recommends a width of %2$s pixels.' ), |
5216 '<code>.mp4</code>', |
5304 '<code>.mp4</code>', |
5217 sprintf( '<strong>%s</strong>', $width ) |
5305 sprintf( '<strong>%s</strong>', $width ) |
5218 ); |
5306 ); |
5219 } else { |
5307 } else { |
5220 $control_description = sprintf( |
5308 $control_description = sprintf( |
5221 /* translators: 1: .mp4, 2: header height in pixels */ |
5309 /* translators: 1: .mp4, 2: Header height in pixels. */ |
5222 __( 'Upload your video in %1$s format and minimize its file size for best results. Your theme recommends a height of %2$s pixels.' ), |
5310 __( 'Upload your video in %1$s format and minimize its file size for best results. Your theme recommends a height of %2$s pixels.' ), |
5223 '<code>.mp4</code>', |
5311 '<code>.mp4</code>', |
5224 sprintf( '<strong>%s</strong>', $height ) |
5312 sprintf( '<strong>%s</strong>', $height ) |
5225 ); |
5313 ); |
5226 } |
5314 } |
5566 $section_description .= __( 'Add your own CSS code here to customize the appearance and layout of your site.' ); |
5654 $section_description .= __( 'Add your own CSS code here to customize the appearance and layout of your site.' ); |
5567 $section_description .= sprintf( |
5655 $section_description .= sprintf( |
5568 ' <a href="%1$s" class="external-link" target="_blank">%2$s<span class="screen-reader-text"> %3$s</span></a>', |
5656 ' <a href="%1$s" class="external-link" target="_blank">%2$s<span class="screen-reader-text"> %3$s</span></a>', |
5569 esc_url( __( 'https://codex.wordpress.org/CSS' ) ), |
5657 esc_url( __( 'https://codex.wordpress.org/CSS' ) ), |
5570 __( 'Learn more about CSS' ), |
5658 __( 'Learn more about CSS' ), |
5571 /* translators: accessibility text */ |
5659 /* translators: Accessibility text. */ |
5572 __( '(opens in a new tab)' ) |
5660 __( '(opens in a new tab)' ) |
5573 ); |
5661 ); |
5574 $section_description .= '</p>'; |
5662 $section_description .= '</p>'; |
5575 |
5663 |
5576 $section_description .= '<p id="editor-keyboard-trap-help-1">' . __( 'When using a keyboard to navigate:' ) . '</p>'; |
5664 $section_description .= '<p id="editor-keyboard-trap-help-1">' . __( 'When using a keyboard to navigate:' ) . '</p>'; |
5577 $section_description .= '<ul>'; |
5665 $section_description .= '<ul>'; |
5578 $section_description .= '<li id="editor-keyboard-trap-help-2">' . __( 'In the editing area, the Tab key enters a tab character.' ) . '</li>'; |
5666 $section_description .= '<li id="editor-keyboard-trap-help-2">' . __( 'In the editing area, the Tab key enters a tab character.' ) . '</li>'; |
5579 $section_description .= '<li id="editor-keyboard-trap-help-3">' . __( 'To move away from this area, press the Esc key followed by the Tab key.' ) . '</li>'; |
5667 $section_description .= '<li id="editor-keyboard-trap-help-3">' . __( 'To move away from this area, press the Esc key followed by the Tab key.' ) . '</li>'; |
5580 $section_description .= '<li id="editor-keyboard-trap-help-4">' . __( 'Screen reader users: when in forms mode, you may need to press the escape key twice.' ) . '</li>'; |
5668 $section_description .= '<li id="editor-keyboard-trap-help-4">' . __( 'Screen reader users: when in forms mode, you may need to press the Esc key twice.' ) . '</li>'; |
5581 $section_description .= '</ul>'; |
5669 $section_description .= '</ul>'; |
5582 |
5670 |
5583 if ( 'false' !== wp_get_current_user()->syntax_highlighting ) { |
5671 if ( 'false' !== wp_get_current_user()->syntax_highlighting ) { |
5584 $section_description .= '<p>'; |
5672 $section_description .= '<p>'; |
5585 $section_description .= sprintf( |
5673 $section_description .= sprintf( |
5586 /* translators: 1: link to user profile, 2: additional link attributes, 3: accessibility text */ |
5674 /* translators: 1: Link to user profile, 2: Additional link attributes, 3: Accessibility text. */ |
5587 __( '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.' ), |
5675 __( '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.' ), |
5588 esc_url( get_edit_profile_url() ), |
5676 esc_url( get_edit_profile_url() ), |
5589 'class="external-link" target="_blank"', |
5677 'class="external-link" target="_blank"', |
5590 sprintf( |
5678 sprintf( |
5591 '<span class="screen-reader-text"> %s</span>', |
5679 '<span class="screen-reader-text"> %s</span>', |
5592 /* translators: accessibility text */ |
5680 /* translators: Accessibility text. */ |
5593 __( '(opens in a new tab)' ) |
5681 __( '(opens in a new tab)' ) |
5594 ) |
5682 ) |
5595 ); |
5683 ); |
5596 $section_description .= '</p>'; |
5684 $section_description .= '</p>'; |
5597 } |
5685 } |
5797 |
5885 |
5798 // Set active based on customized theme. |
5886 // Set active based on customized theme. |
5799 $theme->active = ( isset( $_POST['customized_theme'] ) && $_POST['customized_theme'] === $theme->slug ); |
5887 $theme->active = ( isset( $_POST['customized_theme'] ) && $_POST['customized_theme'] === $theme->slug ); |
5800 |
5888 |
5801 // Map available theme properties to installed theme properties. |
5889 // Map available theme properties to installed theme properties. |
5802 $theme->id = $theme->slug; |
5890 $theme->id = $theme->slug; |
5803 $theme->screenshot = array( $theme->screenshot_url ); |
5891 $theme->screenshot = array( $theme->screenshot_url ); |
5804 $theme->authorAndUri = wp_kses( $theme->author['display_name'], $themes_allowedtags ); |
5892 $theme->authorAndUri = wp_kses( $theme->author['display_name'], $themes_allowedtags ); |
|
5893 $theme->compatibleWP = is_wp_version_compatible( $theme->requires ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName |
|
5894 $theme->compatiblePHP = is_php_version_compatible( $theme->requires_php ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName |
5805 |
5895 |
5806 if ( isset( $theme->parent ) ) { |
5896 if ( isset( $theme->parent ) ) { |
5807 $theme->parent = $theme->parent['slug']; |
5897 $theme->parent = $theme->parent['slug']; |
5808 } else { |
5898 } else { |
5809 $theme->parent = false; |
5899 $theme->parent = false; |
5864 /** |
5954 /** |
5865 * Callback for validating a background setting value. |
5955 * Callback for validating a background setting value. |
5866 * |
5956 * |
5867 * @since 4.7.0 |
5957 * @since 4.7.0 |
5868 * |
5958 * |
5869 * @param string $value Repeat value. |
5959 * @param string $value Repeat value. |
5870 * @param WP_Customize_Setting $setting Setting. |
5960 * @param WP_Customize_Setting $setting Setting. |
5871 * @return string|WP_Error Background value or validation error. |
5961 * @return string|WP_Error Background value or validation error. |
5872 */ |
5962 */ |
5873 public function _sanitize_background_setting( $value, $setting ) { |
5963 public function _sanitize_background_setting( $value, $setting ) { |
5874 if ( 'background_repeat' === $setting->id ) { |
5964 if ( 'background_repeat' === $setting->id ) { |
5875 if ( ! in_array( $value, array( 'repeat-x', 'repeat-y', 'repeat', 'no-repeat' ) ) ) { |
5965 if ( ! in_array( $value, array( 'repeat-x', 'repeat-y', 'repeat', 'no-repeat' ), true ) ) { |
5876 return new WP_Error( 'invalid_value', __( 'Invalid value for background repeat.' ) ); |
5966 return new WP_Error( 'invalid_value', __( 'Invalid value for background repeat.' ) ); |
5877 } |
5967 } |
5878 } elseif ( 'background_attachment' === $setting->id ) { |
5968 } elseif ( 'background_attachment' === $setting->id ) { |
5879 if ( ! in_array( $value, array( 'fixed', 'scroll' ) ) ) { |
5969 if ( ! in_array( $value, array( 'fixed', 'scroll' ), true ) ) { |
5880 return new WP_Error( 'invalid_value', __( 'Invalid value for background attachment.' ) ); |
5970 return new WP_Error( 'invalid_value', __( 'Invalid value for background attachment.' ) ); |
5881 } |
5971 } |
5882 } elseif ( 'background_position_x' === $setting->id ) { |
5972 } elseif ( 'background_position_x' === $setting->id ) { |
5883 if ( ! in_array( $value, array( 'left', 'center', 'right' ), true ) ) { |
5973 if ( ! in_array( $value, array( 'left', 'center', 'right' ), true ) ) { |
5884 return new WP_Error( 'invalid_value', __( 'Invalid value for background position X.' ) ); |
5974 return new WP_Error( 'invalid_value', __( 'Invalid value for background position X.' ) ); |
5927 * Ensures that the selected video is less than 8MB and provides an error message. |
6017 * Ensures that the selected video is less than 8MB and provides an error message. |
5928 * |
6018 * |
5929 * @since 4.7.0 |
6019 * @since 4.7.0 |
5930 * |
6020 * |
5931 * @param WP_Error $validity |
6021 * @param WP_Error $validity |
5932 * @param mixed $value |
6022 * @param mixed $value |
5933 * @return mixed |
6023 * @return mixed |
5934 */ |
6024 */ |
5935 public function _validate_header_video( $validity, $value ) { |
6025 public function _validate_header_video( $validity, $value ) { |
5936 $video = get_attached_file( absint( $value ) ); |
6026 $video = get_attached_file( absint( $value ) ); |
5937 if ( $video ) { |
6027 if ( $video ) { |
5938 $size = filesize( $video ); |
6028 $size = filesize( $video ); |
5939 if ( 8 < $size / pow( 1024, 2 ) ) { // Check whether the size is larger than 8MB. |
6029 if ( $size > 8 * MB_IN_BYTES ) { |
5940 $validity->add( |
6030 $validity->add( |
5941 'size_too_large', |
6031 'size_too_large', |
5942 __( '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.' ) |
6032 __( '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.' ) |
5943 ); |
6033 ); |
5944 } |
6034 } |