/** * Handle customize_save WP Ajax request to save/update a changeset. * * @since 3.4.0 * @since 4.7.0 The semantics of this method have changed to update a changeset, optionally to also change the status and other attributes. */ public function save() { if (!is_user_logged_in()) { wp_send_json_error('unauthenticated'); } if (!$this->is_preview()) { wp_send_json_error('not_preview'); } $action = 'save-customize_' . $this->get_stylesheet(); if (!check_ajax_referer($action, 'nonce', false)) { wp_send_json_error('invalid_nonce'); } $changeset_post_id = $this->changeset_post_id(); if (empty($changeset_post_id)) { if (!current_user_can(get_post_type_object('customize_changeset')->cap->create_posts)) { wp_send_json_error('cannot_create_changeset_post'); } } else { if (!current_user_can(get_post_type_object('customize_changeset')->cap->edit_post, $changeset_post_id)) { wp_send_json_error('cannot_edit_changeset_post'); } } if (!empty($_POST['customize_changeset_data'])) { $input_changeset_data = json_decode(wp_unslash($_POST['customize_changeset_data']), true); if (!is_array($input_changeset_data)) { wp_send_json_error('invalid_customize_changeset_data'); } } else { $input_changeset_data = array(); } // Validate title. $changeset_title = null; if (isset($_POST['customize_changeset_title'])) { $changeset_title = sanitize_text_field(wp_unslash($_POST['customize_changeset_title'])); } // Validate changeset status param. $is_publish = null; $changeset_status = null; if (isset($_POST['customize_changeset_status'])) { $changeset_status = wp_unslash($_POST['customize_changeset_status']); if (!get_post_status_object($changeset_status) || !in_array($changeset_status, array('draft', 'pending', 'publish', 'future'), true)) { wp_send_json_error('bad_customize_changeset_status', 400); } $is_publish = 'publish' === $changeset_status || 'future' === $changeset_status; if ($is_publish && !current_user_can(get_post_type_object('customize_changeset')->cap->publish_posts)) { wp_send_json_error('changeset_publish_unauthorized', 403); } } /* * Validate changeset date param. Date is assumed to be in local time for * the WP if in MySQL format (YYYY-MM-DD HH:MM:SS). Otherwise, the date * is parsed with strtotime() so that ISO date format may be supplied * or a string like "+10 minutes". */ $changeset_date_gmt = null; if (isset($_POST['customize_changeset_date'])) { $changeset_date = wp_unslash($_POST['customize_changeset_date']); if (preg_match('/^\\d\\d\\d\\d-\\d\\d-\\d\\d \\d\\d:\\d\\d:\\d\\d$/', $changeset_date)) { $mm = substr($changeset_date, 5, 2); $jj = substr($changeset_date, 8, 2); $aa = substr($changeset_date, 0, 4); $valid_date = wp_checkdate($mm, $jj, $aa, $changeset_date); if (!$valid_date) { wp_send_json_error('bad_customize_changeset_date', 400); } $changeset_date_gmt = get_gmt_from_date($changeset_date); } else { $timestamp = strtotime($changeset_date); if (!$timestamp) { wp_send_json_error('bad_customize_changeset_date', 400); } $changeset_date_gmt = gmdate('Y-m-d H:i:s', $timestamp); } } $r = $this->save_changeset_post(array('status' => $changeset_status, 'title' => $changeset_title, 'date_gmt' => $changeset_date_gmt, 'data' => $input_changeset_data)); if (is_wp_error($r)) { $response = array('message' => $r->get_error_message(), 'code' => $r->get_error_code()); if (is_array($r->get_error_data())) { $response = array_merge($response, $r->get_error_data()); } else { $response['data'] = $r->get_error_data(); } } else { $response = $r; // Note that if the changeset status was publish, then it will get set to trash if revisions are not supported. $response['changeset_status'] = get_post_status($this->changeset_post_id()); if ($is_publish && 'trash' === $response['changeset_status']) { $response['changeset_status'] = 'publish'; } if ('publish' === $response['changeset_status']) { $response['next_changeset_uuid'] = wp_generate_uuid4(); } } if (isset($response['setting_validities'])) { $response['setting_validities'] = array_map(array($this, 'prepare_setting_validity_for_js'), $response['setting_validities']); } /** * Filters response data for a successful customize_save Ajax request. * * This filter does not apply if there was a nonce or authentication failure. * * @since 4.2.0 * * @param array $response Additional information passed back to the 'saved' * event on `wp.customize`. * @param WP_Customize_Manager $this WP_Customize_Manager instance. */ $response = apply_filters('customize_save_response', $response, $this); if (is_wp_error($r)) { wp_send_json_error($response); } else { wp_send_json_success($response); } }
/** * @ticket 30937 * @covers wp_admin_bar_customize_menu() */ public function test_customize_link() { global $wp_customize; require_once ABSPATH . WPINC . '/class-wp-customize-manager.php'; $uuid = wp_generate_uuid4(); $this->go_to(home_url("/?customize_changeset_uuid={$uuid}")); wp_set_current_user(self::$admin_id); $this->factory()->post->create(array('post_type' => 'customize_changeset', 'post_status' => 'auto-draft', 'post_name' => $uuid)); $wp_customize = new WP_Customize_Manager(array('changeset_uuid' => $uuid)); $wp_customize->start_previewing_theme(); set_current_screen('front'); $wp_admin_bar = $this->get_standard_admin_bar(); $node = $wp_admin_bar->get_node('customize'); $this->assertNotEmpty($node); $parsed_url = wp_parse_url($node->href); $query_params = array(); wp_parse_str($parsed_url['query'], $query_params); $this->assertEquals($uuid, $query_params['changeset_uuid']); $this->assertNotContains('changeset_uuid', $query_params['url']); }
/** * Tests wp_generate_uuid4(). * * @covers wp_generate_uuid4() * @ticket 38164 */ function test_wp_generate_uuid4() { $uuids = array(); for ($i = 0; $i < 20; $i += 1) { $uuid = wp_generate_uuid4(); $this->assertRegExp('/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/', $uuid); $uuids[] = $uuid; } $unique_uuids = array_unique($uuids); $this->assertEquals($uuids, $unique_uuids); }
/** * Test WP_Customize_Manager::unsanitized_post_values(). * * @ticket 30937 * @covers WP_Customize_Manager::unsanitized_post_values() */ function test_unsanitized_post_values_with_changeset_and_stashed_theme_mods() { wp_set_current_user(self::$admin_user_id); $preview_theme = $this->get_inactive_core_theme(); $stashed_theme_mods = array($preview_theme => array('background_color' => array('value' => '#000000'))); $stashed_theme_mods[get_stylesheet()] = array('background_color' => array('value' => '#FFFFFF')); update_option('customize_stashed_theme_mods', $stashed_theme_mods); $post_values = array('blogdescription' => 'Post Input Tagline'); $_POST['customized'] = wp_slash(wp_json_encode($post_values)); $uuid = wp_generate_uuid4(); $changeset_data = array('blogname' => array('value' => 'Changeset Title'), 'blogdescription' => array('value' => 'Changeset Tagline')); $this->factory()->post->create(array('post_type' => 'customize_changeset', 'post_status' => 'auto-draft', 'post_name' => $uuid, 'post_content' => wp_json_encode($changeset_data))); $manager = new WP_Customize_Manager(array('changeset_uuid' => $uuid)); $this->assertTrue($manager->is_theme_active()); $this->assertArrayNotHasKey('background_color', $manager->unsanitized_post_values()); $this->assertEquals(array('blogname' => 'Changeset Title', 'blogdescription' => 'Post Input Tagline'), $manager->unsanitized_post_values()); $this->assertEquals(array('blogdescription' => 'Post Input Tagline'), $manager->unsanitized_post_values(array('exclude_changeset' => true))); $manager->set_post_value('blogdescription', 'Post Override Tagline'); $this->assertEquals(array('blogname' => 'Changeset Title', 'blogdescription' => 'Post Override Tagline'), $manager->unsanitized_post_values()); $this->assertEquals(array('blogname' => 'Changeset Title', 'blogdescription' => 'Changeset Tagline'), $manager->unsanitized_post_values(array('exclude_post_data' => true))); $this->assertEmpty($manager->unsanitized_post_values(array('exclude_post_data' => true, 'exclude_changeset' => true))); // Test unstashing theme mods. $manager = new WP_Customize_Manager(array('changeset_uuid' => $uuid, 'theme' => $preview_theme)); $this->assertFalse($manager->is_theme_active()); $values = $manager->unsanitized_post_values(array('exclude_post_data' => true, 'exclude_changeset' => true)); $this->assertNotEmpty($values); $this->assertArrayHasKey('background_color', $values); $this->assertEquals('#000000', $values['background_color']); $values = $manager->unsanitized_post_values(array('exclude_post_data' => false, 'exclude_changeset' => false)); $this->assertArrayHasKey('background_color', $values); $this->assertArrayHasKey('blogname', $values); $this->assertArrayHasKey('blogdescription', $values); }
/** * Test WP_Customize_Manager::save(). * * @ticket 38943 * @covers WP_Customize_Manager::save() */ function test_success_save_post_date() { $uuid = wp_generate_uuid4(); $post_id = $this->factory()->post->create(array('post_name' => $uuid, 'post_title' => 'Original', 'post_type' => 'customize_changeset', 'post_status' => 'auto-draft', 'post_content' => wp_json_encode(array('blogname' => array('value' => 'New Site Title'))))); $wp_customize = $this->set_up_valid_state($uuid); // Success future schedule date. $future_date = gmdate('Y') + 1 . '-01-01 00:00:00'; $_POST['customize_changeset_status'] = 'future'; $_POST['customize_changeset_title'] = 'Future date'; $_POST['customize_changeset_date'] = $future_date; $this->make_ajax_call('customize_save'); $this->assertTrue($this->_last_response_parsed['success']); $changeset_post_schedule = get_post($post_id); $this->assertEquals($future_date, $changeset_post_schedule->post_date); // Success future changeset change to draft keeping existing date. unset($_POST['customize_changeset_date']); $_POST['customize_changeset_status'] = 'draft'; $this->make_ajax_call('customize_save'); $this->assertTrue($this->_last_response_parsed['success']); $changeset_post_draft = get_post($post_id); $this->assertEquals($future_date, $changeset_post_draft->post_date); // Success if date is not passed with schedule changeset and stored changeset have future date. $_POST['customize_changeset_status'] = 'future'; $this->make_ajax_call('customize_save'); $this->assertTrue($this->_last_response_parsed['success']); $changeset_post_schedule = get_post($post_id); $this->assertEquals($future_date, $changeset_post_schedule->post_date); // Success if draft with past date. $now = current_time('mysql'); wp_update_post(array('ID' => $post_id, 'post_status' => 'draft', 'post_date' => $now, 'post_date_gmt' => get_gmt_from_date($now))); // Fail if future request and existing date is past. $_POST['customize_changeset_status'] = 'future'; unset($_POST['customize_changeset_date']); $this->make_ajax_call('customize_save'); $this->assertFalse($this->_last_response_parsed['success']); $this->assertEquals('not_future_date', $this->_last_response_parsed['data']['code']); // Success publish changeset reset date to current. wp_update_post(array('ID' => $post_id, 'post_status' => 'future', 'post_date' => $future_date, 'post_date_gmt' => get_gmt_from_date($future_date))); unset($_POST['customize_changeset_date']); $_POST['customize_changeset_status'] = 'publish'; $this->make_ajax_call('customize_save'); $this->assertTrue($this->_last_response_parsed['success']); $changeset_post_publish = get_post($post_id); $this->assertNotEquals($future_date, $changeset_post_publish->post_date); }
/** * Test ensuring that the post_name (UUID) is preserved when wp_insert_post()/wp_update_post() is called. * * @see _wp_customize_changeset_filter_insert_post_data() * @ticket 30937 */ function test_wp_insert_post_for_customize_changeset_should_not_drop_post_name() { $this->assertEquals(10, has_filter('wp_insert_post_data', '_wp_customize_changeset_filter_insert_post_data')); $changeset_data = array('blogname' => array('value' => 'Hello World')); wp_set_current_user($this->factory()->user->create(array('role' => 'contributor'))); $uuid = wp_generate_uuid4(); $post_id = wp_insert_post(array('post_type' => 'customize_changeset', 'post_name' => strtoupper($uuid), 'post_content' => wp_json_encode($changeset_data))); $this->assertEquals($uuid, get_post($post_id)->post_name, 'Expected lower-case UUID4 to be inserted.'); $this->assertEquals($changeset_data, json_decode(get_post($post_id)->post_content, true)); $changeset_data['blogname']['value'] = 'Hola Mundo'; wp_update_post(array('ID' => $post_id, 'post_status' => 'draft', 'post_content' => wp_json_encode($changeset_data))); $this->assertEquals($uuid, get_post($post_id)->post_name, 'Expected post_name to not have been dropped for drafts.'); $this->assertEquals($changeset_data, json_decode(get_post($post_id)->post_content, true)); $changeset_data['blogname']['value'] = 'Hallo Welt'; wp_update_post(array('ID' => $post_id, 'post_status' => 'pending', 'post_content' => wp_json_encode($changeset_data))); $this->assertEquals($uuid, get_post($post_id)->post_name, 'Expected post_name to not have been dropped for pending.'); $this->assertEquals($changeset_data, json_decode(get_post($post_id)->post_content, true)); }
/** * Test WP_Customize_Manager::save(). * * @ticket 30937 * @covers WP_Customize_Manager::save() */ function test_save_success_publish_edit() { $uuid = wp_generate_uuid4(); $post_id = $this->factory()->post->create(array('post_name' => $uuid, 'post_title' => 'Original', 'post_type' => 'customize_changeset', 'post_status' => 'auto-draft', 'post_content' => wp_json_encode(array('blogname' => array('value' => 'New Site Title'))))); $wp_customize = $this->set_up_valid_state($uuid); // Successful future. $_POST['customize_changeset_status'] = 'publish'; $_POST['customize_changeset_title'] = 'Published'; $this->make_ajax_call('customize_save'); $this->assertTrue($this->_last_response_parsed['success']); $this->assertInternalType('array', $this->_last_response_parsed['data']); $this->assertEquals('publish', $this->_last_response_parsed['data']['changeset_status']); $this->assertArrayHasKey('next_changeset_uuid', $this->_last_response_parsed['data']); $this->assertEquals('New Site Title', get_option('blogname')); $this->assertEquals('Published', get_post($post_id)->post_title); }