/** * Verify sanitization of external header video URL will trim the whitespaces in the beginning and end of the URL. * * @ticket 39125 */ function test_sanitize_external_header_video_trim() { $this->manager->register_controls(); $setting = $this->manager->get_setting('external_header_video'); $video_url = 'https://www.youtube.com/watch?v=KiS8rZBeIO0'; $whitespaces = array(' ', "\t", "\n", "\r", "\f", "\v"); foreach ($whitespaces as $whitespace) { $sanitized = $setting->sanitize($whitespace . $video_url . $whitespace); $this->assertEquals($video_url, $sanitized); } }
/** * Test customize_pane_settings() method. * * @see WP_Customize_Manager::customize_pane_settings() */ function test_customize_pane_settings() { wp_set_current_user($this->factory->user->create(array('role' => 'administrator'))); $this->manager->register_controls(); $this->manager->prepare_controls(); $autofocus = array('control' => 'blogname'); $this->manager->set_autofocus($autofocus); ob_start(); $this->manager->customize_pane_settings(); $content = ob_get_clean(); $this->assertContains('var _wpCustomizeSettings =', $content); $this->assertContains('"blogname"', $content); $this->assertContains('_wpCustomizeSettings.controls', $content); $this->assertContains('_wpCustomizeSettings.settings', $content); $this->assertContains('</script>', $content); $this->assertNotEmpty(preg_match('#var _wpCustomizeSettings\\s*=\\s*({.*?});\\s*\\n#', $content, $matches)); $json = $matches[1]; $data = json_decode($json, true); $this->assertNotEmpty($data); $this->assertEqualSets(array('theme', 'url', 'browser', 'panels', 'sections', 'nonce', 'autofocus', 'documentTitleTmpl'), array_keys($data)); $this->assertEquals($autofocus, $data['autofocus']); $this->assertArrayHasKey('save', $data['nonce']); $this->assertArrayHasKey('preview', $data['nonce']); }
/** * @ticket 30225 * @ticket 34594 */ function test_prepare_controls_stable_sorting() { $manager = new WP_Customize_Manager(); $manager->register_controls(); $section_id = 'foo-section'; wp_set_current_user( self::factory()->user->create( array( 'role' => 'administrator' ) ) ); $manager->add_section( $section_id, array( 'title' => 'Section', 'priority' => 1, ) ); $added_control_ids = array(); $count = 9; for ( $i = 0; $i < $count; $i += 1 ) { $id = 'sort-test-' . $i; $added_control_ids[] = $id; $manager->add_setting( $id ); $control = new WP_Customize_Control( $manager, $id, array( 'section' => $section_id, 'priority' => 1, 'setting' => $id, ) ); $manager->add_control( $control ); } $manager->prepare_controls(); $sorted_control_ids = wp_list_pluck( $manager->get_section( $section_id )->controls, 'id' ); $this->assertEquals( $added_control_ids, $sorted_control_ids ); }
/** * Publish snapshot changes when snapshot post is being published. * * The logic in here is the inverse of to publish_snapshot_with_customize_save_after. * * The meat of the logic that manipulates the post_content and validates the settings * needs to be done in wp_insert_post_data filter in like a * filter_insert_post_data_to_validate_published_snapshot method? This would * have the benefit of reducing one wp_insert_post() call. * * @todo Consider using wp_insert_post_data to prevent double calls to wp_insert_post(). * @see Customize_Snapshot_Manager::publish_snapshot_with_customize_save_after() * * @param string $new_status New status. * @param string $old_status Old status. * @param \WP_Post $post Post object. * @return bool Whether the settings were saved. */ public function save_settings_with_publish_snapshot($new_status, $old_status, $post) { // Abort if not transitioning a snapshot post to publish from a non-publish status. if (Post_Type::SLUG !== $post->post_type || 'publish' !== $new_status || $new_status === $old_status) { return false; } $this->ensure_customize_manager(); if ($this->doing_customize_save_ajax()) { // Short circuit because customize_save ajax call is changing status. return false; } if (!did_action('customize_register')) { /* * When running from CLI or Cron, we have to remove the action because * it will get added with a default priority of 10, after themes and plugins * have already done add_action( 'customize_register' ), resulting in them * being called first at the priority 10. So we manually call the * prerequisite function WP_Customize_Manager::register_controls() and * remove it from being called when the customize_register action fires. */ remove_action('customize_register', array($this->customize_manager, 'register_controls')); $this->customize_manager->register_controls(); /* * Unfortunate hack to prevent \WP_Customize_Widgets::customize_register() * from calling preview() on settings. This needs to be cleaned up in core. * It is important for previewing to be prevented because if an option has * a filter it will short-circuit when an update is attempted since it * detects that there is no change to be put into the DB. * See: https://github.com/xwp/wordpress-develop/blob/e8c58c47db1421a1d0b2afa9ad4b9eb9e1e338e0/src/wp-includes/class-wp-customize-widgets.php#L208-L217 */ if (!defined('DOING_AJAX')) { define('DOING_AJAX', true); } $_REQUEST['action'] = 'customize_save'; /** This action is documented in wp-includes/class-wp-customize-manager.php */ do_action('customize_register', $this->customize_manager); // undefine( 'DOING_AJAX' )... just kidding. This is the end of the unfortunate hack and it should be fixed in Core. unset($_REQUEST['action']); } $snapshot_content = $this->post_type->get_post_content($post); if (method_exists($this->customize_manager, 'validate_setting_values')) { /** This action is documented in wp-includes/class-wp-customize-manager.php */ do_action('customize_save_validation_before', $this->customize_manager); } $setting_ids = array_keys($snapshot_content); $this->customize_manager->add_dynamic_settings($setting_ids); /** This action is documented in wp-includes/class-wp-customize-manager.php */ do_action('customize_save', $this->customize_manager); /** * Settings to save. * * @var \WP_Customize_Setting[] */ $settings = array(); $publish_error_count = 0; foreach ($snapshot_content as $setting_id => &$setting_params) { // Missing value error. if (!isset($setting_params['value']) || is_null($setting_params['value'])) { if (!is_array($setting_params)) { if (!empty($setting_params)) { $setting_params = array('value' => $setting_params); } else { $setting_params = array(); } } $setting_params['publish_error'] = 'null_value'; $publish_error_count += 1; continue; } // Unrecognized setting error. $this->customize_manager->set_post_value($setting_id, $setting_params['value']); $setting = $this->customize_manager->get_setting($setting_id); if (!$setting instanceof \WP_Customize_Setting) { $setting_params['publish_error'] = 'unrecognized_setting'; $publish_error_count += 1; continue; } // Validate setting value. if (method_exists($setting, 'validate')) { $validity = $setting->validate($setting_params['value']); if (is_wp_error($validity)) { $setting_params['publish_error'] = $validity->get_error_code(); $publish_error_count += 1; continue; } } // Validate sanitized setting value. $sanitized_value = $setting->sanitize($setting_params['value']); if (is_null($sanitized_value) || is_wp_error($sanitized_value)) { $setting_params['publish_error'] = is_wp_error($sanitized_value) ? $sanitized_value->get_error_code() : 'invalid_value'; $publish_error_count += 1; continue; } $settings[] = $setting; unset($setting_params['publish_error']); } // Handle error scenarios. if ($publish_error_count > 0) { $update_setting_args = array('ID' => $post->ID, 'post_content' => Customize_Snapshot_Manager::encode_json($snapshot_content), 'post_status' => 'pending'); wp_update_post(wp_slash($update_setting_args)); update_post_meta($post->ID, 'snapshot_error_on_publish', $publish_error_count); add_filter('redirect_post_location', function ($location) { $location = add_query_arg('snapshot_error_on_publish', '1', $location); return $location; }); return false; } /* * Change all setting capabilities temporarily to 'exist' to allow them to * be saved regardless of current user, such as when WP-Cron is publishing * the snapshot post if it was scheduled. It is safe to do this because * a setting can only be written into a snapshot by users who have the * capability, so after it has been added to a snapshot it is good to commit. */ $existing_caps = wp_list_pluck($settings, 'capability'); foreach ($settings as $setting) { $setting->capability = 'exist'; } // Persist the settings in the DB. foreach ($settings as $setting) { $setting->save(); } // Restore setting capabilities. foreach ($existing_caps as $setting_id => $existing_cap) { $settings[$setting_id]->capability = $existing_cap; } /** This action is documented in wp-includes/class-wp-customize-manager.php */ do_action('customize_save_after', $this->customize_manager); // Remove any previous error on setting. delete_post_meta($post->ID, 'snapshot_error_on_publish'); return true; }
/** * Publish a snapshot's changes. * * @param string $new_status New post status. * @param string $old_status Old post status. * @param WP_Post $changeset_post Changeset post object. */ function _wp_customize_publish_changeset($new_status, $old_status, $changeset_post) { global $wp_customize, $wpdb; $is_publishing_changeset = 'customize_changeset' === $changeset_post->post_type && 'publish' === $new_status && 'publish' !== $old_status; if (!$is_publishing_changeset) { return; } if (empty($wp_customize)) { require_once ABSPATH . WPINC . '/class-wp-customize-manager.php'; $wp_customize = new WP_Customize_Manager(array('changeset_uuid' => $changeset_post->post_name)); } if (!did_action('customize_register')) { /* * When running from CLI or Cron, the customize_register action will need * to be triggered in order for core, themes, and plugins to register their * settings. Normally core will add_action( 'customize_register' ) at * priority 10 to register the core settings, and if any themes/plugins * also add_action( 'customize_register' ) at the same priority, they * will have a $wp_customize with those settings registered since they * call add_action() afterward, normally. However, when manually doing * the customize_register action after the setup_theme, then the order * will be reversed for two actions added at priority 10, resulting in * the core settings no longer being available as expected to themes/plugins. * So the following manually calls the method that registers the core * settings up front before doing the action. */ remove_action('customize_register', array($wp_customize, 'register_controls')); $wp_customize->register_controls(); /** This filter is documented in /wp-includes/class-wp-customize-manager.php */ do_action('customize_register', $wp_customize); } $wp_customize->_publish_changeset_values($changeset_post->ID); /* * Trash the changeset post if revisions are not enabled. Unpublished * changesets by default get garbage collected due to the auto-draft status. * When a changeset post is published, however, it would no longer get cleaned * out. Ths is a problem when the changeset posts are never displayed anywhere, * since they would just be endlessly piling up. So here we use the revisions * feature to indicate whether or not a published changeset should get trashed * and thus garbage collected. */ if (!wp_revisions_enabled($changeset_post)) { $post = $changeset_post; $post_id = $changeset_post->ID; /* * The following re-formulates the logic from wp_trash_post() as done in * wp_publish_post(). The reason for bypassing wp_trash_post() is that it * will mutate the the post_content and the post_name when they should be * untouched. */ if (!EMPTY_TRASH_DAYS) { wp_delete_post($post_id, true); } else { /** This action is documented in wp-includes/post.php */ do_action('wp_trash_post', $post_id); add_post_meta($post_id, '_wp_trash_meta_status', $post->post_status); add_post_meta($post_id, '_wp_trash_meta_time', time()); $old_status = $post->post_status; $new_status = 'trash'; $wpdb->update($wpdb->posts, array('post_status' => $new_status), array('ID' => $post->ID)); clean_post_cache($post->ID); $post->post_status = $new_status; wp_transition_post_status($new_status, $old_status, $post); /** This action is documented in wp-includes/post.php */ do_action('edit_post', $post->ID, $post); /** This action is documented in wp-includes/post.php */ do_action("save_post_{$post->post_type}", $post->ID, $post, true); /** This action is documented in wp-includes/post.php */ do_action('save_post', $post->ID, $post, true); /** This action is documented in wp-includes/post.php */ do_action('wp_insert_post', $post->ID, $post, true); /** This action is documented in wp-includes/post.php */ do_action('trashed_post', $post_id); } } }
/** * @group site_icon * @ticket 38377 */ function test_customize_preview_wp_site_icon_dirty() { global $wp_customize; wp_set_current_user($this->factory()->user->create(array('role' => 'administrator'))); require_once ABSPATH . WPINC . '/class-wp-customize-manager.php'; $wp_customize = new WP_Customize_Manager(); $wp_customize->register_controls(); $wp_customize->start_previewing_theme(); $attachment_id = $this->_insert_attachment(); $wp_customize->set_post_value('site_icon', $attachment_id); $wp_customize->get_setting('site_icon')->preview(); $output = array(sprintf('<link rel="icon" href="%s" sizes="32x32" />', esc_url(wp_get_attachment_image_url($attachment_id, 32))), sprintf('<link rel="icon" href="%s" sizes="192x192" />', esc_url(wp_get_attachment_image_url($attachment_id, 192))), sprintf('<link rel="apple-touch-icon-precomposed" href="%s" />', esc_url(wp_get_attachment_image_url($attachment_id, 180))), sprintf('<meta name="msapplication-TileImage" content="%s" />', esc_url(wp_get_attachment_image_url($attachment_id, 270))), ''); $output = implode("\n", $output); $this->expectOutputString($output); wp_site_icon(); }
/** * Set up valid user state. * * @param string $uuid Changeset UUID. * @return WP_Customize_Manager */ protected function set_up_valid_state($uuid = null) { global $wp_customize; wp_set_current_user(self::$admin_user_id); $wp_customize = new WP_Customize_Manager(array('changeset_uuid' => $uuid)); $wp_customize->register_controls(); $nonce = wp_create_nonce('save-customize_' . $wp_customize->get_stylesheet()); $_POST['nonce'] = $_GET['nonce'] = $_REQUEST['nonce'] = $nonce; $wp_customize->setup_theme(); return $wp_customize; }