The name "post_value" is a carry-over from when the customized state was
exclusively sourced from $_POST['customized'].
public set_post_value ( string $setting_id, mixed $value ) | ||
$setting_id | string | ID for the WP_Customize_Setting instance. |
$value | mixed | Post value. |
function set_customized_post_data( $customized ) { $_POST['customized'] = wp_slash( wp_json_encode( $customized ) ); if ( $this->manager ) { foreach ( $customized as $id => $value ) { $this->manager->set_post_value( $id, $value ); } } }
/** * Test protected update() method via the save() method, for deleted menu. * * @see WP_Customize_Nav_Menu_Item_Setting::update() */ function test_save_deleted() { do_action('customize_register', $this->wp_customize); $menu_id = wp_create_nav_menu('Primary'); $post_id = $this->factory->post->create(array('post_title' => 'Hello World')); $item_ids = array(); for ($i = 0; $i < 5; $i += 1) { $item_id = wp_update_nav_menu_item($menu_id, 0, array('menu-item-type' => 'post_type', 'menu-item-object' => 'post', 'menu-item-object-id' => $post_id, 'menu-item-title' => "Item {$i}", 'menu-item-status' => 'publish', 'menu-item-position' => $i + 1)); $item_ids[] = $item_id; } $delete_item_id = $item_ids[2]; $setting_id = "nav_menu_item[{$delete_item_id}]"; $setting = new WP_Customize_Nav_Menu_Item_Setting($this->wp_customize, $setting_id); $this->wp_customize->set_post_value($setting_id, false); $current_items = wp_get_nav_menu_items($menu_id); $this->assertContains($delete_item_id, wp_list_pluck($current_items, 'db_id')); $setting->save(); $preview_items = wp_get_nav_menu_items($menu_id); $this->assertNotEquals(count($current_items), count($preview_items)); $this->assertContains($delete_item_id, wp_list_pluck($current_items, 'db_id')); // Verify the Ajax responses is being amended. $save_response = apply_filters('customize_save_response', array()); $this->assertArrayHasKey('nav_menu_item_updates', $save_response); $update_result = array_shift($save_response['nav_menu_item_updates']); $this->assertArrayHasKey('post_id', $update_result); $this->assertArrayHasKey('previous_post_id', $update_result); $this->assertArrayHasKey('error', $update_result); $this->assertArrayHasKey('status', $update_result); $this->assertEquals($delete_item_id, $update_result['post_id']); $this->assertNull($update_result['previous_post_id']); $this->assertNull($update_result['error']); $this->assertEquals('deleted', $update_result['status']); }
/** * Test save_nav_menus_created_posts. * * @covers WP_Customize_Nav_Menus::save_nav_menus_created_posts() */ function test_save_nav_menus_created_posts() { $menus = new WP_Customize_Nav_Menus($this->wp_customize); do_action('customize_register', $this->wp_customize); $post_ids = $this->factory()->post->create_many(3, array('post_status' => 'auto-draft', 'post_type' => 'post', 'post_name' => 'auto-draft')); $pre_published_post_id = $this->factory()->post->create(array('post_status' => 'publish')); $setting_id = 'nav_menus_created_posts'; $this->wp_customize->set_post_value($setting_id, array_merge($post_ids, array($pre_published_post_id))); $setting = $this->wp_customize->get_setting($setting_id); $this->assertInstanceOf('WP_Customize_Filter_Setting', $setting); $this->assertEquals(array($menus, 'sanitize_nav_menus_created_posts'), $setting->sanitize_callback); $this->assertEquals($post_ids, $setting->post_value()); foreach ($post_ids as $post_id) { $this->assertEquals('auto-draft', get_post_status($post_id)); } $save_action_count = did_action('customize_save_nav_menus_created_posts'); $setting->save(); $this->assertEquals($save_action_count + 1, did_action('customize_save_nav_menus_created_posts')); foreach ($post_ids as $post_id) { $this->assertEquals('publish', get_post_status($post_id)); } // Ensure that unique slugs were assigned. $posts = array_map('get_post', $post_ids); $post_names = wp_list_pluck($posts, 'post_name'); $this->assertEqualSets($post_names, array_unique($post_names)); }
/** * Test protected update() method via the save() method, for deleted menu. * * @see WP_Customize_Nav_Menu_Setting::update() */ function test_save_deleted() { do_action('customize_register', $this->wp_customize); $menu_name = 'Lorem Ipsum'; $menu_id = wp_create_nav_menu($menu_name); $setting_id = "nav_menu[{$menu_id}]"; $setting = new WP_Customize_Nav_Menu_Setting($this->wp_customize, $setting_id); $nav_menu_options = $this->get_nav_menu_items_option(); $nav_menu_options['auto_add'][] = $menu_id; update_option('nav_menu_options', $nav_menu_options); $menu = wp_get_nav_menu_object($menu_id); $this->assertEquals($menu_name, $menu->name); $this->wp_customize->set_post_value($setting_id, false); $setting->save(); $this->assertFalse(wp_get_nav_menu_object($menu_id)); $save_response = apply_filters('customize_save_response', array()); $this->assertArrayHasKey('nav_menu_updates', $save_response); $update_result = array_shift($save_response['nav_menu_updates']); $this->assertArrayHasKey('term_id', $update_result); $this->assertArrayHasKey('previous_term_id', $update_result); $this->assertArrayHasKey('error', $update_result); $this->assertArrayHasKey('status', $update_result); $this->assertArrayHasKey('saved_value', $update_result); $this->assertNull($update_result['saved_value']); $this->assertEquals($menu_id, $update_result['term_id']); $this->assertNull($update_result['previous_term_id']); $this->assertNull($update_result['error']); $this->assertEquals('deleted', $update_result['status']); $nav_menu_options = $this->get_nav_menu_items_option(); $this->assertNotContains($menu_id, $nav_menu_options['auto_add']); }
/** * Test save_nav_menus_created_posts. * * @covers WP_Customize_Nav_Menus::save_nav_menus_created_posts() */ function test_save_nav_menus_created_posts() { $menus = new WP_Customize_Nav_Menus($this->wp_customize); do_action('customize_register', $this->wp_customize); $post_ids = array(); for ($i = 0; $i < 3; $i += 1) { $r = $menus->insert_auto_draft_post(array('post_title' => 'Auto Draft ' . $i, 'post_type' => 'post', 'post_name' => 'auto-draft-' . $i)); $this->assertInstanceOf('WP_Post', $r); $post_ids[] = $r->ID; } $pre_published_post_id = $this->factory()->post->create(array('post_status' => 'publish')); $setting_id = 'nav_menus_created_posts'; $this->wp_customize->set_post_value($setting_id, array_merge($post_ids, array($pre_published_post_id))); $setting = $this->wp_customize->get_setting($setting_id); $this->assertInstanceOf('WP_Customize_Filter_Setting', $setting); $this->assertEquals(array($menus, 'sanitize_nav_menus_created_posts'), $setting->sanitize_callback); $this->assertEquals($post_ids, $setting->post_value()); foreach ($post_ids as $post_id) { $this->assertEquals('auto-draft', get_post_status($post_id)); $this->assertEmpty(get_post($post_id)->post_name); $this->assertNotEmpty(get_post_meta($post_id, '_customize_draft_post_name', true)); } $save_action_count = did_action('customize_save_nav_menus_created_posts'); $setting->save(); $this->assertEquals($save_action_count + 1, did_action('customize_save_nav_menus_created_posts')); foreach ($post_ids as $post_id) { $this->assertEquals('publish', get_post_status($post_id)); $this->assertRegExp('/^auto-draft-\\d+$/', get_post($post_id)->post_name); $this->assertEmpty(get_post_meta($post_id, '_customize_draft_post_name', true)); } // Ensure that unique slugs were assigned. $posts = array_map('get_post', $post_ids); $post_names = wp_list_pluck($posts, 'post_name'); $this->assertEqualSets($post_names, array_unique($post_names)); }
/** * Test customize_preview_settings() method. * * @see WP_Customize_Manager::customize_preview_settings() */ function test_customize_preview_settings() { wp_set_current_user( self::factory()->user->create( array( 'role' => 'administrator' ) ) ); $this->manager->register_controls(); $this->manager->prepare_controls(); $this->manager->set_post_value( 'foo', 'bar' ); $_POST['customize_messenger_channel'] = 'preview-0'; ob_start(); $this->manager->customize_preview_settings(); $content = ob_get_clean(); $this->assertEquals( 1, preg_match( '/var _wpCustomizeSettings = ({.+});/', $content, $matches ) ); $settings = json_decode( $matches[1], true ); $this->assertArrayHasKey( 'theme', $settings ); $this->assertArrayHasKey( 'url', $settings ); $this->assertArrayHasKey( 'channel', $settings ); $this->assertArrayHasKey( 'activePanels', $settings ); $this->assertArrayHasKey( 'activeSections', $settings ); $this->assertArrayHasKey( 'activeControls', $settings ); $this->assertArrayHasKey( 'nonce', $settings ); $this->assertArrayHasKey( '_dirty', $settings ); $this->assertArrayHasKey( 'preview', $settings['nonce'] ); $this->assertEquals( array( 'foo' ), $settings['_dirty'] ); }
/** * Test WP_Customize_Nav_Menu_Item_Setting::value_as_wp_post_nav_menu_item(). * * @see WP_Customize_Nav_Menu_Item_Setting::value_as_wp_post_nav_menu_item() */ function test_value_as_wp_post_nav_menu_item() { $post_id = self::factory()->post->create(); $setting = new WP_Customize_Nav_Menu_Item_Setting($this->wp_customize, 'nav_menu_item[123]'); $post_value = array('object_id' => $post_id, 'object' => 'post', 'menu_item_parent' => 0, 'position' => 2, 'type' => 'custom_type', 'title' => 'Hello \\o/ o\'o World', 'url' => '', 'target' => '', 'attr_title' => '">att \\o/ o\'o empted <b>baddie</b>', 'description' => 'Attempted \\o/ o\'o <b>markup</b>', 'classes' => '', 'xfn' => '', 'status' => 'publish', 'original_title' => '', 'nav_menu_term_id' => 0, '_invalid' => false); $this->wp_customize->set_post_value($setting->id, $post_value); $setting->preview(); $nav_menu_item = $setting->value_as_wp_post_nav_menu_item(); $this->assertEquals('Custom Link', $nav_menu_item->type_label); add_filter('wp_setup_nav_menu_item', array($this, 'filter_type_label')); $nav_menu_item = $setting->value_as_wp_post_nav_menu_item(); $this->assertEquals('Custom Label', $nav_menu_item->type_label); $this->assertObjectNotHasAttribute('nav_menu_term_id', $nav_menu_item); $this->assertObjectNotHasAttribute('status', $nav_menu_item); $this->assertEquals('publish', $nav_menu_item->post_status); $this->assertEquals('nav_menu_item', $nav_menu_item->post_type); $this->assertObjectNotHasAttribute('position', $nav_menu_item); $this->assertEquals($post_value['position'], $nav_menu_item->menu_order); $this->assertEquals($post_value['title'], $nav_menu_item->post_title); $this->assertEquals(123, $nav_menu_item->ID); $this->assertEquals(123, $nav_menu_item->db_id); $this->assertEquals(wp_get_current_user()->ID, $nav_menu_item->post_author); $this->assertObjectHasAttribute('type_label', $nav_menu_item); $expected = apply_filters('nav_menu_attr_title', wp_unslash(apply_filters('excerpt_save_pre', wp_slash($post_value['attr_title'])))); $this->assertEquals($expected, $nav_menu_item->attr_title); $this->assertEquals('Attempted \\o/ o’o markup', $nav_menu_item->description); }
/** * @see \WP_Customize_Setting::save() */ function test_customize_widgets_save() { $initial_search_widgets = get_option('widget_search'); $widget_id = 'search-2'; $setting_id = 'widget_search[2]'; $override_widget_data = array('title' => 'Buscar'); $sanitized_widget_instance = $this->customize_manager->widgets->sanitize_widget_js_instance($override_widget_data); $this->customize_manager->set_post_value($setting_id, $sanitized_widget_instance); $widget_post = $this->widget_posts->get_widget_post($widget_id); $this->assertEquals($initial_search_widgets[2], $this->widget_posts->get_widget_instance_data($widget_post)); do_action('customize_save', $this->customize_manager); foreach ($this->customize_manager->settings() as $setting) { /** @var \WP_Customize_Setting $setting */ $setting->save(); } do_action('customize_save_after', $this->customize_manager); $saved_data = $this->widget_posts->get_widget_instance_data($this->widget_posts->get_widget_post($widget_id)); $this->assertEquals($override_widget_data, $saved_data, 'Overridden widget data should be saved.'); $this->assertEquals($initial_search_widgets[3], $this->widget_posts->get_widget_instance_data($this->widget_posts->get_widget_post('search-3')), 'Untouched widget instance should remain intact.'); }
/** * Test WP_Customize_Nav_Menu_Item_Setting::value_as_wp_post_nav_menu_item() where title is empty. * * @ticket 38015 * @see WP_Customize_Nav_Menu_Item_Setting::value_as_wp_post_nav_menu_item() */ function test_value_as_wp_post_nav_menu_item_with_empty_title() { $original_title = 'The Original Title'; $post_id = self::factory()->post->create(array('post_title' => $original_title)); $setting = new WP_Customize_Nav_Menu_Item_Setting($this->wp_customize, 'nav_menu_item[123]'); $post_value = array_merge($setting->default, array('object_id' => $post_id, 'object' => 'post', 'type' => 'post_type', 'status' => 'publish', 'nav_menu_term_id' => 0)); $this->wp_customize->set_post_value($setting->id, $post_value); $setting->preview(); $nav_menu_item = $setting->value_as_wp_post_nav_menu_item(); $this->assertEquals($original_title, $nav_menu_item->title); }
/** * Ensure that WP_Customize_Setting::value() can return a previewed value for aggregated multidimensionals. * * @ticket 37294 */ public function test_multidimensional_value_when_previewed() { WP_Customize_Setting::reset_aggregated_multidimensionals(); $initial_value = 456; set_theme_mod('nav_menu_locations', array('primary' => $initial_value)); $setting_id = 'nav_menu_locations[primary]'; $setting = new WP_Customize_Setting($this->manager, $setting_id); $this->assertEquals($initial_value, $setting->value()); $override_value = -123456; $this->manager->set_post_value($setting_id, $override_value); $setting->preview(); $this->assertEquals($override_value, $setting->value()); }
/** * Filter for heartbeat data sent from client. * * @filter heartbeat_received * * @param array $response Data to send back to client. * @param array $data Data sent from client. * @param string $screen_id Current screen. * @return array */ public function filter_heartbeat_received($response, $data, $screen_id) { $is_customize_concurrency = 'customize' === $screen_id && isset($data[self::SLUG]['last_update_timestamp_cursor']) && intval($data[self::SLUG]['last_update_timestamp_cursor']) > time() - $this->config('lock_window_seconds'); if (!$is_customize_concurrency) { return $response; } if (empty($this->customize_manager)) { require_once ABSPATH . WPINC . '/class-wp-customize-manager.php'; $GLOBALS['wp_customize'] = new \WP_Customize_Manager(); $this->customize_manager = $GLOBALS['wp_customize']; } do_action('customize_register', $this->customize_manager); $next_update_timestamp_cursor = time(); $last_update_timestamp_cursor = absint($data[self::SLUG]['last_update_timestamp_cursor']); $query_vars = array('post_type' => self::POST_TYPE, 'date_query' => array(array('after' => gmdate('c', $last_update_timestamp_cursor), 'inclusive' => true, 'column' => 'post_date_gmt')), 'posts_per_page' => 50); $query = new \WP_Query($query_vars); $setting_updates = array(); foreach ($query->posts as $post) { if (intval($post->post_author) === intval(get_current_user_id())) { continue; } $setting_id = $post->post_name; $setting = $this->get_setting($setting_id); $value = json_decode($post->post_content_filtered, true); if (!$this->customize_manager->get_setting($setting_id)) { $this->customize_manager->add_setting($setting); $this->customize_manager->set_post_value($setting_id, $value); $setting->preview(); } /* * @todo This filter is prevented from applying due to optimized-widget-registration. * Make sure we account for why. Heartbeat may need to send the active widgets. */ if (!preg_match('/sidebars_widgets\\]?\\[/', $setting_id)) { $value = apply_filters("customize_sanitize_js_{$setting_id}", $value, $setting); } $args = array('post_id' => $post->ID, 'post_status' => $post->post_status, 'post_author' => $this->get_preview_user_data($post->post_author), 'post_date' => strtotime($post->post_date_gmt), 'value' => $value, 'transport' => $setting->transport); $setting_updates[$post->post_name] = $args; } /** * Filters which settings have been updated. * * @param array $setting_updates Sanitized post values. * @return array */ $setting_updates = apply_filters('customize_concurrency_settings', $setting_updates); $response[self::SLUG] = array('next_update_timestamp_cursor' => $next_update_timestamp_cursor, 'old_last_update_timestamp_cursor' => $last_update_timestamp_cursor, 'setting_updates' => $setting_updates); return $response; }
/** * Test update filter on WP_Customize_Custom_CSS_Setting. * * @covers WP_Customize_Custom_CSS_Setting::update() */ function test_update_filter() { $original_css = 'body { color:red; }'; $post_id = $this->factory()->post->create(array('post_title' => $this->setting->stylesheet, 'post_name' => $this->setting->stylesheet, 'post_content' => $original_css, 'post_status' => 'publish', 'post_type' => 'custom_css')); $overridden_css = 'body { color:green; }'; $this->wp_customize->set_post_value($this->setting->id, $overridden_css); $post = get_post($post_id); $original_title = $post->post_title; add_filter('update_custom_css_data', array($this, 'filter_update_custom_css_data'), 10, 3); $this->setting->save(); $post = get_post($post_id); $this->assertEquals($original_title, $post->post_title); $this->assertContains($overridden_css, $post->post_content); $this->assertContains('/* filtered post_content */', $post->post_content); $this->assertContains('/* filtered post_content_filtered */', $post->post_content_filtered); }
/** * @see WP_Customize_Widget_Setting::update() */ function test_save() { $widget_data = $this->get_sample_widget_instance_data('archives'); $args = $this->customize_manager->widgets->get_setting_args($widget_data['setting_id']); $setting = new WP_Customize_Widget_Setting($this->customize_manager, $widget_data['setting_id'], $args); $value = $setting->value(); $override_title = 'BAR UPDATED VALUE'; $this->assertNotEquals($value['title'], $override_title); $value['title'] = $override_title; $this->customize_manager->set_post_value($setting->id, $this->customize_manager->widgets->sanitize_widget_js_instance($value)); $setting->save(); $saved_value = $setting->value(); $this->assertEquals($override_title, $saved_value['title']); $instances = get_option('widget_archives'); $this->assertEquals($saved_value, $instances[$widget_data['number']]); }
/** * Test WP_Customize_Manager::set_post_value(). * * @see WP_Customize_Manager::set_post_value() */ function test_set_post_value() { $this->manager->add_setting('foo', array('sanitize_callback' => array($this, 'sanitize_foo_for_test_set_post_value'))); $setting = $this->manager->get_setting('foo'); $this->assertEmpty($this->captured_customize_post_value_set_actions); add_action('customize_post_value_set', array($this, 'capture_customize_post_value_set_actions'), 10, 3); add_action('customize_post_value_set_foo', array($this, 'capture_customize_post_value_set_actions'), 10, 2); $this->manager->set_post_value($setting->id, '123abc'); $this->assertCount(2, $this->captured_customize_post_value_set_actions); $this->assertEquals('customize_post_value_set_foo', $this->captured_customize_post_value_set_actions[0]['action']); $this->assertEquals('customize_post_value_set', $this->captured_customize_post_value_set_actions[1]['action']); $this->assertEquals(array('123abc', $this->manager), $this->captured_customize_post_value_set_actions[0]['args']); $this->assertEquals(array($setting->id, '123abc', $this->manager), $this->captured_customize_post_value_set_actions[1]['args']); $unsanitized = $this->manager->unsanitized_post_values(); $this->assertArrayHasKey($setting->id, $unsanitized); $this->assertEquals('123abc', $unsanitized[$setting->id]); $this->assertEquals(123, $setting->post_value()); }
/** * Ensure that previewing a setting is disabled when the current blog is switched. * * @ticket 31428 * @group multisite */ function test_previewing_with_switch_to_blog() { if (!is_multisite()) { $this->markTestSkipped('Cannot test WP_Customize_Setting::is_current_blog_previewed() with switch_to_blog() if not on multisite.'); } $type = 'option'; $name = 'blogdescription'; $post_value = rand_str(); $this->manager->set_post_value($name, $post_value); $setting = new WP_Customize_Setting($this->manager, $name, compact('type')); $this->assertFalse($setting->is_current_blog_previewed()); $setting->preview(); $this->assertTrue($setting->is_current_blog_previewed()); $blog_id = $this->factory->blog->create(); switch_to_blog($blog_id); $this->assertFalse($setting->is_current_blog_previewed()); $this->assertNotEquals($post_value, $setting->value()); $this->assertNotEquals($post_value, get_option($name)); restore_current_blog(); }
/** * @ticket 38164 */ function test_dropdown_pages() { do_action('customize_register', $this->wp_customize); $this->assertInstanceOf('WP_Customize_Nav_Menus', $this->wp_customize->nav_menus); $nav_menus_created_posts_setting = $this->wp_customize->get_setting('nav_menus_created_posts'); $this->assertInstanceOf('WP_Customize_Filter_Setting', $nav_menus_created_posts_setting); $page_on_front_control = $this->wp_customize->get_control('page_on_front'); // Ensure the add-new-toggle is absent if allow_addition param is not set. $page_on_front_control->allow_addition = false; ob_start(); $page_on_front_control->maybe_render(); $content = ob_get_clean(); $this->assertNotContains('add-new-toggle', $content); // Ensure the add-new-toggle is absent if allow_addition param is set. $page_on_front_control->allow_addition = true; ob_start(); $page_on_front_control->maybe_render(); $content = ob_get_clean(); $this->assertContains('add-new-toggle', $content); // Ensure that dropdown-pages delect is rendered even if there are no pages published (yet). foreach (get_pages() as $page) { wp_delete_post($page->ID); } $page_on_front_control->allow_addition = true; ob_start(); $page_on_front_control->maybe_render(); $content = ob_get_clean(); $this->assertContains('<option value="0">', $content, 'Dropdown-pages renders select even without any pages published.'); // Ensure that auto-draft pages are included if they are among the nav_menus_created_posts. $auto_draft_page_id = $this->factory()->post->create(array('post_type' => 'page', 'post_status' => 'auto-draft', 'post_title' => 'Auto Draft Page')); $this->factory()->post->create(array('post_type' => 'page', 'post_status' => 'auto-draft', 'post_title' => 'Orphan Auto Draft Page')); $auto_draft_post_id = $this->factory()->post->create(array('post_type' => 'post', 'post_status' => 'auto-draft', 'post_title' => 'Auto Draft Post')); $this->wp_customize->set_post_value($nav_menus_created_posts_setting->id, array($auto_draft_page_id, $auto_draft_post_id)); $nav_menus_created_posts_setting->preview(); ob_start(); $page_on_front_control->maybe_render(); $content = ob_get_clean(); $this->assertContains(sprintf('<option value="%d">Auto Draft Page</option>', $auto_draft_page_id), $content); $this->assertNotContains('Auto Draft Post', $content); $this->assertNotContains('Orphan Auto Draft Page', $content); }
/** * Testing the results of various searches * * @dataProvider data_ajax_search_available_items_results * * @param array $post_args POST args. * @param array $expected_results Expected results. */ function test_ajax_search_available_items_results($post_args, $expected_results) { do_action('customize_register', $this->wp_customize); self::factory()->post->create_many(5, array('post_title' => 'Test Post')); $included_auto_draft_post = $this->wp_customize->nav_menus->insert_auto_draft_post(array('post_title' => 'Test Included Auto Draft', 'post_type' => 'post')); $excluded_auto_draft_post = $this->wp_customize->nav_menus->insert_auto_draft_post(array('post_title' => 'Excluded Auto Draft', 'post_type' => 'post')); $this->wp_customize->set_post_value('nav_menus_created_posts', array($included_auto_draft_post->ID, $excluded_auto_draft_post->ID)); $this->wp_customize->get_setting('nav_menus_created_posts')->preview(); $_POST = array_merge(array('action' => 'search-available-menu-items-customizer', 'customize-menus-nonce' => wp_create_nonce('customize-menus')), $post_args); $this->make_ajax_call('search-available-menu-items-customizer'); $response = json_decode($this->_last_response, true); if (isset($post_args['search']) && 'test' === $post_args['search']) { $this->assertsame(true, $response['success']); $this->assertSame(6, count($response['data']['items'])); $item_ids = wp_list_pluck($response['data']['items'], 'id'); $this->assertContains('post-' . $included_auto_draft_post->ID, $item_ids); $this->assertNotContains('post-' . $excluded_auto_draft_post->ID, $item_ids); } else { $this->assertSame($expected_results, $response); } }
/** * @ticket 33499 */ function test_option_autoloading() { global $wpdb; wp_set_current_user(self::factory()->user->create(array('role' => 'administrator'))); $name = 'autoloaded1'; $setting = new WP_Customize_Setting($this->manager, $name, array('type' => 'option')); $value = 'value1'; $this->manager->set_post_value($setting->id, $value); $setting->save(); $autoload = $wpdb->get_var($wpdb->prepare("SELECT autoload FROM {$wpdb->options} WHERE option_name = %s", $setting->id)); $this->assertEquals('yes', $autoload); $this->assertEquals($value, get_option($name)); $name = 'autoloaded2'; $setting = new WP_Customize_Setting($this->manager, $name, array('type' => 'option', 'autoload' => true)); $value = 'value2'; $this->manager->set_post_value($setting->id, $value); $setting->save(); $autoload = $wpdb->get_var($wpdb->prepare("SELECT autoload FROM {$wpdb->options} WHERE option_name = %s", $setting->id)); $this->assertEquals('yes', $autoload); $this->assertEquals($value, get_option($name)); $name = 'not-autoloaded1'; $setting = new WP_Customize_Setting($this->manager, $name, array('type' => 'option', 'autoload' => false)); $value = 'value3'; $this->manager->set_post_value($setting->id, $value); $setting->save(); $autoload = $wpdb->get_var($wpdb->prepare("SELECT autoload FROM {$wpdb->options} WHERE option_name = %s", $setting->id)); $this->assertEquals('no', $autoload); $this->assertEquals($value, get_option($name)); $id_base = 'multi-not-autoloaded'; $setting1 = new WP_Customize_Setting($this->manager, $id_base . '[foo]', array('type' => 'option')); $setting2 = new WP_Customize_Setting($this->manager, $id_base . '[bar]', array('type' => 'option', 'autoload' => false)); $this->manager->set_post_value($setting1->id, 'value1'); $this->manager->set_post_value($setting2->id, 'value2'); $setting1->save(); $autoload = $wpdb->get_var($wpdb->prepare("SELECT autoload FROM {$wpdb->options} WHERE option_name = %s", $id_base)); $this->assertEquals('no', $autoload, 'Even though setting1 did not indicate autoload (thus normally true), since another multidimensional option setting of the base did say autoload=false, it should be autoload=no'); }
/** * Find and invoke the widget update and control callbacks. * * Requires that $_POST be populated with the instance data. * * @since 3.9.0 * @access public * * @global array $wp_registered_widget_updates * @global array $wp_registered_widget_controls * * @param string $widget_id Widget ID. * @return WP_Error|array Array containing the updated widget information. * A WP_Error object, otherwise. */ public function call_widget_update($widget_id) { global $wp_registered_widget_updates, $wp_registered_widget_controls; $setting_id = $this->get_setting_id($widget_id); /* * Make sure that other setting changes have previewed since this widget * may depend on them (e.g. Menus being present for Custom Menu widget). */ if (!did_action('customize_preview_init')) { foreach ($this->manager->settings() as $setting) { if ($setting->id !== $setting_id) { $setting->preview(); } } } $this->start_capturing_option_updates(); $parsed_id = $this->parse_widget_id($widget_id); $option_name = 'widget_' . $parsed_id['id_base']; /* * If a previously-sanitized instance is provided, populate the input vars * with its values so that the widget update callback will read this instance */ $added_input_vars = array(); if (!empty($_POST['sanitized_widget_setting'])) { $sanitized_widget_setting = json_decode($this->get_post_value('sanitized_widget_setting'), true); if (false === $sanitized_widget_setting) { $this->stop_capturing_option_updates(); return new WP_Error('widget_setting_malformed'); } $instance = $this->sanitize_widget_instance($sanitized_widget_setting); if (is_null($instance)) { $this->stop_capturing_option_updates(); return new WP_Error('widget_setting_unsanitized'); } if (!is_null($parsed_id['number'])) { $value = array(); $value[$parsed_id['number']] = $instance; $key = 'widget-' . $parsed_id['id_base']; $_REQUEST[$key] = $_POST[$key] = wp_slash($value); $added_input_vars[] = $key; } else { foreach ($instance as $key => $value) { $_REQUEST[$key] = $_POST[$key] = wp_slash($value); $added_input_vars[] = $key; } } } // Invoke the widget update callback. foreach ((array) $wp_registered_widget_updates as $name => $control) { if ($name === $parsed_id['id_base'] && is_callable($control['callback'])) { ob_start(); call_user_func_array($control['callback'], $control['params']); ob_end_clean(); break; } } // Clean up any input vars that were manually added foreach ($added_input_vars as $key) { unset($_POST[$key]); unset($_REQUEST[$key]); } // Make sure the expected option was updated. if (0 !== $this->count_captured_options()) { if ($this->count_captured_options() > 1) { $this->stop_capturing_option_updates(); return new WP_Error('widget_setting_too_many_options'); } $updated_option_name = key($this->get_captured_options()); if ($updated_option_name !== $option_name) { $this->stop_capturing_option_updates(); return new WP_Error('widget_setting_unexpected_option'); } } // Obtain the widget instance. $option = $this->get_captured_option($option_name); if (null !== $parsed_id['number']) { $instance = $option[$parsed_id['number']]; } else { $instance = $option; } /* * Override the incoming $_POST['customized'] for a newly-created widget's * setting with the new $instance so that the preview filter currently * in place from WP_Customize_Setting::preview() will use this value * instead of the default widget instance value (an empty array). */ $this->manager->set_post_value($setting_id, $instance); // Obtain the widget control with the updated instance in place. ob_start(); $form = $wp_registered_widget_controls[$widget_id]; if ($form) { call_user_func_array($form['callback'], $form['params']); } $form = ob_get_clean(); $this->stop_capturing_option_updates(); return compact('instance', 'form'); }
/** * @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(); }
/** * Early at the customize_save action, iterate over all settings and check for any that are invalid. * * If any of the settings are invalid, short-circuit the WP_Customize_Manager::save() call with a * call to wp_send_json_error() sending back the invalid_settings. * * @access public * @action customize_save * * @param \WP_Customize_Manager $wp_customize Customizer manager. */ public function validate_settings(\WP_Customize_Manager $wp_customize) { global $wp_registered_widget_updates; $sanitized_value = null; /* * Check to see if any of the registered settings are invalid, and for * those that are invalid, build an array of the invalid messages. */ $unsanitized_post_values = $wp_customize->unsanitized_post_values(); foreach ($unsanitized_post_values as $setting_id => $unsanitized_value) { $sanitized_value = null; $setting = $wp_customize->get_setting($setting_id); if (!$setting) { continue; } if (is_null($unsanitized_value)) { continue; } $parsed_widget_id = $wp_customize->widgets->parse_widget_setting_id($setting_id); $is_empty_widget_instance = !is_wp_error($parsed_widget_id) && is_array($unsanitized_value) && empty($unsanitized_value) && !empty($wp_customize->widgets); if ($is_empty_widget_instance) { $instance = null; foreach ((array) $wp_registered_widget_updates as $name => $control) { $is_wp_widget = $name === $parsed_widget_id['id_base'] && is_callable($control['callback']) && is_array($control['callback']) && $control['callback'][0] instanceof \WP_Widget; if ($is_wp_widget) { // Note that error suppression is needed because a widget update() callback may have default values. // @todo All Core widgets should have proper defaults if the incoming array is empty. $instance = @call_user_func(array($control['callback'][0], 'update'), array(), array()); $sanitized_value = $wp_customize->widgets->sanitize_widget_js_instance($instance); if (!is_null($sanitized_value) && !is_wp_error($sanitized_value)) { $wp_customize->set_post_value($setting_id, $sanitized_value); } break; } } } if (!isset($sanitized_value)) { $sanitized_value = $setting->sanitize($unsanitized_value); } if (is_null($sanitized_value)) { $sanitized_value = new \WP_Error('invalid_value', __('Invalid value.', 'customize-setting-validation')); } if (is_wp_error($sanitized_value)) { $this->invalid_settings[$setting_id] = $sanitized_value->get_error_message(); } } $invalid_count = count($this->invalid_settings); // No invalid settings, do not short-circuit. if (0 === $invalid_count) { return; } $response = array('message' => sprintf(_n('There is %d invalid setting.', 'There are %d invalid settings.', $invalid_count, 'customize-setting-validation'), $invalid_count), 'invalid_settings' => $this->invalid_settings); /** This filter is documented in wp-includes/class-wp-customize-manager.php */ $response = apply_filters('customize_save_response', $response, $wp_customize); /* * This assumes that the method is being called in the context of * WP_Customize_Manager::save(), which calls wp_json_send_success() * at the end. */ wp_send_json_error($response); }
/** * Test WP_Customize_Manager::save(). * * @ticket 30937 * @covers WP_Customize_Manager::save() */ function test_save_failures() { global $wp_customize; $wp_customize = new WP_Customize_Manager(); $wp_customize->register_controls(); add_filter('user_has_cap', array($this, 'filter_user_has_cap')); // Unauthenticated. wp_set_current_user(0); $this->make_ajax_call('customize_save'); $this->assertFalse($this->_last_response_parsed['success']); $this->assertEquals('unauthenticated', $this->_last_response_parsed['data']); // Unauthorized. wp_set_current_user(self::$subscriber_user_id); $nonce = wp_create_nonce('save-customize_' . $wp_customize->get_stylesheet()); $_POST['nonce'] = $_GET['nonce'] = $_REQUEST['nonce'] = $nonce; $exception = null; try { ob_start(); $wp_customize->setup_theme(); } catch (WPAjaxDieContinueException $e) { $exception = $e; } $this->assertNotEmpty($e); $this->assertEquals(-1, $e->getMessage()); // Not called setup_theme. wp_set_current_user(self::$admin_user_id); $nonce = wp_create_nonce('save-customize_' . $wp_customize->get_stylesheet()); $_POST['nonce'] = $_GET['nonce'] = $_REQUEST['nonce'] = $nonce; $this->make_ajax_call('customize_save'); $this->assertFalse($this->_last_response_parsed['success']); $this->assertEquals('not_preview', $this->_last_response_parsed['data']); // Bad nonce. $_POST['nonce'] = $_GET['nonce'] = $_REQUEST['nonce'] = 'bad'; $wp_customize->setup_theme(); $this->make_ajax_call('customize_save'); $this->assertFalse($this->_last_response_parsed['success']); $this->assertEquals('invalid_nonce', $this->_last_response_parsed['data']); // User cannot create. $nonce = wp_create_nonce('save-customize_' . $wp_customize->get_stylesheet()); $_POST['nonce'] = $_GET['nonce'] = $_REQUEST['nonce'] = $nonce; $post_type_obj = get_post_type_object('customize_changeset'); $post_type_obj->cap->create_posts = 'create_customize_changesets'; $this->make_ajax_call('customize_save'); $this->assertFalse($this->_last_response_parsed['success']); $this->assertEquals('cannot_create_changeset_post', $this->_last_response_parsed['data']); $this->overridden_caps[$post_type_obj->cap->create_posts] = true; $this->make_ajax_call('customize_save'); $this->assertTrue($this->_last_response_parsed['success']); $post_type_obj->cap->create_posts = 'customize'; // Restore. // Changeset already published. $wp_customize->set_post_value('blogname', 'Hello'); $wp_customize->save_changeset_post(array('status' => 'publish')); $this->make_ajax_call('customize_save'); $this->assertFalse($this->_last_response_parsed['success']); $this->assertEquals('changeset_already_published', $this->_last_response_parsed['data']['code']); wp_update_post(array('ID' => $wp_customize->changeset_post_id(), 'post_status' => 'auto-draft')); // User cannot edit. $post_type_obj = get_post_type_object('customize_changeset'); $post_type_obj->cap->edit_post = 'edit_customize_changesets'; $this->make_ajax_call('customize_save'); $this->assertFalse($this->_last_response_parsed['success']); $this->assertEquals('cannot_edit_changeset_post', $this->_last_response_parsed['data']); $this->overridden_caps[$post_type_obj->cap->edit_post] = true; $this->make_ajax_call('customize_save'); $this->assertTrue($this->_last_response_parsed['success']); $post_type_obj->cap->edit_post = 'customize'; // Restore. // Bad customize_changeset_data. $_POST['customize_changeset_data'] = '[MALFORMED]'; $this->make_ajax_call('customize_save'); $this->assertFalse($this->_last_response_parsed['success']); $this->assertEquals('invalid_customize_changeset_data', $this->_last_response_parsed['data']); // Bad customize_changeset_status. $_POST['customize_changeset_data'] = '{}'; $_POST['customize_changeset_status'] = 'unrecognized'; $this->make_ajax_call('customize_save'); $this->assertFalse($this->_last_response_parsed['success']); $this->assertEquals('bad_customize_changeset_status', $this->_last_response_parsed['data']); // Disallowed publish posts if not allowed. $post_type_obj = get_post_type_object('customize_changeset'); $post_type_obj->cap->publish_posts = 'publish_customize_changesets'; $_POST['customize_changeset_status'] = 'publish'; $this->make_ajax_call('customize_save'); $this->assertFalse($this->_last_response_parsed['success']); $this->assertEquals('changeset_publish_unauthorized', $this->_last_response_parsed['data']); $_POST['customize_changeset_status'] = 'future'; $this->make_ajax_call('customize_save'); $this->assertFalse($this->_last_response_parsed['success']); $this->assertEquals('changeset_publish_unauthorized', $this->_last_response_parsed['data']); $post_type_obj->cap->publish_posts = 'customize'; // Restore. // Validate date. $_POST['customize_changeset_status'] = 'draft'; $_POST['customize_changeset_date'] = 'BAD DATE'; $this->make_ajax_call('customize_save'); $this->assertFalse($this->_last_response_parsed['success']); $this->assertEquals('bad_customize_changeset_date', $this->_last_response_parsed['data']); $_POST['customize_changeset_date'] = '2010-01-01 00:00:00'; $this->make_ajax_call('customize_save'); $this->assertFalse($this->_last_response_parsed['success']); $this->assertEquals('not_future_date', $this->_last_response_parsed['data']['code']); $_POST['customize_changeset_date'] = gmdate('Y') + 1 . '-01-01 00:00:00'; $this->make_ajax_call('customize_save'); $this->assertTrue($this->_last_response_parsed['success']); $_POST['customize_changeset_status'] = 'future'; $_POST['customize_changeset_date'] = '+10 minutes'; $this->make_ajax_call('customize_save'); $this->assertTrue($this->_last_response_parsed['success']); $this->assertEquals('future', get_post_status($wp_customize->changeset_post_id())); wp_update_post(array('ID' => $wp_customize->changeset_post_id(), 'post_status' => 'auto-draft')); }
/** * 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; }