/** * @ticket 34738 * @see WP_Customize_Widgets::call_widget_update() */ function test_call_widget_update() { $widget_number = 2; $widget_id = "search-{$widget_number}"; $setting_id = "widget_search[{$widget_number}]"; $instance = array('title' => 'Buscar'); $_POST = wp_slash(array('action' => 'update-widget', 'wp_customize' => 'on', 'nonce' => wp_create_nonce('update-widget'), 'theme' => $this->manager->get_stylesheet(), 'customized' => '{}', 'widget-search' => array(2 => $instance), 'widget-id' => $widget_id, 'id_base' => 'search', 'widget-width' => '250', 'widget-height' => '200', 'widget_number' => strval($widget_number), 'multi_number' => '', 'add_new' => '')); $this->do_customize_boot_actions(); $this->assertArrayNotHasKey($setting_id, $this->manager->unsanitized_post_values()); $result = $this->manager->widgets->call_widget_update($widget_id); $this->assertInternalType('array', $result); $this->assertArrayHasKey('instance', $result); $this->assertArrayHasKey('form', $result); $this->assertEquals($instance, $result['instance']); $this->assertContains(sprintf('value="%s"', esc_attr($instance['title'])), $result['form']); $post_values = $this->manager->unsanitized_post_values(); $this->assertArrayHasKey($setting_id, $post_values); $post_value = $post_values[$setting_id]; $this->assertInternalType('array', $post_value); $this->assertArrayHasKey('title', $post_value); $this->assertArrayHasKey('encoded_serialized_instance', $post_value); $this->assertArrayHasKey('instance_hash_key', $post_value); $this->assertArrayHasKey('is_widget_customizer_js_value', $post_value); $this->assertEquals($post_value, $this->manager->widgets->sanitize_widget_js_instance($instance)); }
/** * Is previewing another theme. * * @return bool Whether theme is active. */ public function is_theme_active() { if (empty($this->customize_manager)) { return true; } return $this->customize_manager->get_stylesheet() === $this->original_stylesheet; }
/** * Export data from PHP to JS. * * @since 4.3.0 * @access public */ public function export_preview_data() { // Why not wp_localize_script? Because we're not localizing, and it forces values into strings. $exports = array('renderQueryVar' => self::RENDER_QUERY_VAR, 'renderNonceValue' => wp_create_nonce(self::RENDER_AJAX_ACTION), 'renderNoncePostKey' => self::RENDER_NONCE_POST_KEY, 'requestUri' => '/', 'theme' => array('stylesheet' => $this->manager->get_stylesheet(), 'active' => $this->manager->is_theme_active()), 'previewCustomizeNonce' => wp_create_nonce('preview-customize_' . $this->manager->get_stylesheet()), 'navMenuInstanceArgs' => $this->preview_nav_menu_instance_args); if (!empty($_SERVER['REQUEST_URI'])) { $exports['requestUri'] = esc_url_raw(home_url(wp_unslash($_SERVER['REQUEST_URI']))); } printf('<script>var _wpCustomizePreviewNavMenusExports = %s;</script>', wp_json_encode($exports)); }
/** * Test crud methods on WP_Customize_Custom_CSS_Setting. * * @covers wp_get_custom_css() * @covers WP_Customize_Custom_CSS_Setting::value() * @covers WP_Customize_Custom_CSS_Setting::preview() * @covers WP_Customize_Custom_CSS_Setting::update() */ function test_crud() { $this->setting->default = '/* Hello World */'; $this->assertEquals($this->setting->default, $this->setting->value()); $this->assertNull(wp_get_custom_css_post()); $this->assertNull(wp_get_custom_css_post($this->setting->stylesheet)); $this->assertNull(wp_get_custom_css_post('twentyten')); $original_css = 'body { color: black; }'; $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')); $twentyten_css = 'body { color: red; }'; $twentyten_post_id = $this->factory()->post->create(array('post_title' => 'twentyten', 'post_name' => 'twentyten', 'post_content' => $twentyten_css, 'post_status' => 'publish', 'post_type' => 'custom_css')); $twentyten_setting = new WP_Customize_Custom_CSS_Setting($this->wp_customize, 'custom_css[twentyten]'); $this->assertEquals($post_id, wp_get_custom_css_post()->ID); $this->assertEquals($post_id, wp_get_custom_css_post($this->setting->stylesheet)->ID); $this->assertEquals($twentyten_post_id, wp_get_custom_css_post('twentyten')->ID); $this->assertEquals($original_css, wp_get_custom_css($this->setting->stylesheet)); $this->assertEquals($original_css, $this->setting->value()); $this->assertEquals($twentyten_css, wp_get_custom_css('twentyten')); $this->assertEquals($twentyten_css, $twentyten_setting->value()); $updated_css = 'body { color: blue; }'; $this->wp_customize->set_post_value($this->setting->id, $updated_css); $saved = $this->setting->save(); $this->assertTrue(false !== $saved); $this->assertEquals($updated_css, $this->setting->value()); $this->assertEquals($updated_css, wp_get_custom_css($this->setting->stylesheet)); $this->assertEquals($updated_css, get_post($post_id)->post_content); $previewed_css = 'body { color: red; }'; $this->wp_customize->set_post_value($this->setting->id, $previewed_css); $this->setting->preview(); $this->assertEquals($previewed_css, $this->setting->value()); $this->assertEquals($previewed_css, wp_get_custom_css($this->setting->stylesheet)); // Make sure that wp_update_custom_css_post() works as expected for updates. $r = wp_update_custom_css_post('body { color:red; }', array('stylesheet' => $this->setting->stylesheet, 'preprocessed' => "body\n\tcolor:red;")); $this->assertInstanceOf('WP_Post', $r); $this->assertEquals($post_id, $r->ID); $this->assertEquals('body { color:red; }', get_post($r)->post_content); $this->assertEquals("body\n\tcolor:red;", get_post($r)->post_content_filtered); $r = wp_update_custom_css_post('body { content: "\\o/"; }'); $this->assertEquals($this->wp_customize->get_stylesheet(), get_post($r)->post_name); $this->assertEquals('body { content: "\\o/"; }', get_post($r)->post_content); $this->assertEquals('', get_post($r)->post_content_filtered); // Make sure that wp_update_custom_css_post() works as expected for insertion. $r = wp_update_custom_css_post('body { background:black; }', array('stylesheet' => 'other')); $this->assertInstanceOf('WP_Post', $r); $this->assertEquals('other', get_post($r)->post_name); $this->assertEquals('body { background:black; }', get_post($r)->post_content); $this->assertEquals('publish', get_post($r)->post_status); // Test deletion. wp_delete_post($post_id); $this->assertNull(wp_get_custom_css_post()); $this->assertNull(wp_get_custom_css_post(get_stylesheet())); $this->assertEquals($previewed_css, wp_get_custom_css(get_stylesheet()), 'Previewed value remains in spite of deleted post.'); wp_delete_post($twentyten_post_id); $this->assertNull(wp_get_custom_css_post('twentyten')); $this->assertEquals('', wp_get_custom_css('twentyten')); }
/** * Test publish snapshot with customize_save_after. * * @covers CustomizeSnapshots\Customize_Snapshot_Manager::publish_snapshot_with_customize_save_after() */ function test_publish_snapshot_with_customize_save_after() { wp_set_current_user($this->user_id); $this->do_customize_boot_actions(true); $_POST = array('nonce' => wp_create_nonce('save-customize_' . $this->wp_customize->get_stylesheet()), 'customize_snapshot_uuid' => self::UUID, 'customized' => '{"foo":"foo_default","bar":"bar_default"}'); $_REQUEST['action'] = 'customize_save'; $_REQUEST['customize_snapshot_uuid'] = self::UUID; $manager = new Customize_Snapshot_Manager($this->plugin); $manager->init(); $this->assertEmpty($manager->snapshot()->post()); $manager->publish_snapshot_with_customize_save_after(); $this->assertNotEmpty($manager->snapshot()->post()); $this->markTestIncomplete('Need to test when snapshot->save() returns errors, and when snapshot post save fails.'); }
/** * Do logic for customized_settings_previewed Ajax request. * * @param array $params { * Params as passed by ajax_customize_settings_previewed(). * * @type string $nonce The preview-customize_{stylesheet} nonce. * @type int $last_update_timestamp_cursor Last previewed timestamp. * } * @throws Exception User is not logged in, if they can't customize, or if a param is missing. * @return array */ public function request_customize_settings_previewed(array $params) { if (!is_user_logged_in()) { throw new Exception('not_logged_in', 403); } if (!current_user_can('customize')) { throw new Exception('unauthorized', 403); } if (empty($this->customize_manager)) { throw new Exception('customize_off', 400); } $action = 'preview-customize_' . $this->customize_manager->get_stylesheet(); if (!wp_verify_nonce($params['nonce'], $action)) { throw new Exception('bad_nonce', 403); } $customized = $this->customize_manager->unsanitized_post_values(); if (empty($customized)) { throw new Exception('customized_empty', 400); } if (empty($params['last_update_timestamp_cursor'])) { throw new Exception('missing_last_update_timestamp_cursor', 400); } /** * Filters which settings have been previewed. * * @param array $customized Unsanitized post values. * @return array */ $customized = apply_filters('customize_concurrency_settings', $customized); $settings = array(); foreach (array_keys($customized) as $setting_id) { $setting = $this->customize_manager->get_setting($setting_id); if (!$setting) { throw new Exception("unknown_setting: {$setting_id}", 404); } $setting->preview(); $sanitized_value = $setting->post_value(); $settings[$setting_id] = $sanitized_value; } $previewed_settings = array(); foreach ($settings as $setting_id => $sanitized_value) { $results = $this->save_previewed_setting($setting_id, array('sanitized_value' => $sanitized_value, 'preview_timestamp_cursor' => $params['last_update_timestamp_cursor'])); $previewed_settings[$setting_id] = $results; } return array('previewed_settings' => $previewed_settings); }
/** * Set up widget addition previews. * * Since the widgets get registered on 'widgets_init' before the customizer * settings are set up on 'customize_register', we have to filter the options * similarly to how the setting previewer will filter the options later. * * @since 3.9.0 * * @access public */ public function setup_widget_addition_previews() { $is_customize_preview = false; if (!empty($this->manager) && !is_admin() && 'on' === $this->get_post_value('wp_customize')) { $is_customize_preview = check_ajax_referer('preview-customize_' . $this->manager->get_stylesheet(), 'nonce', false); } $is_ajax_widget_update = false; if ($this->manager->doing_ajax() && 'update-widget' === $this->get_post_value('action')) { $is_ajax_widget_update = check_ajax_referer('update-widget', 'nonce', false); } $is_ajax_customize_save = false; if ($this->manager->doing_ajax() && 'customize_save' === $this->get_post_value('action')) { $is_ajax_customize_save = check_ajax_referer('save-customize_' . $this->manager->get_stylesheet(), 'nonce', false); } $is_valid_request = $is_ajax_widget_update || $is_customize_preview || $is_ajax_customize_save; if (!$is_valid_request) { return; } // Input from customizer preview. if (isset($_POST['customized'])) { $this->_customized = json_decode($this->get_post_value('customized'), true); } else { // Input from ajax widget update request. $this->_customized = array(); $id_base = $this->get_post_value('id_base'); $widget_number = $this->get_post_value('widget_number', false); $option_name = 'widget_' . $id_base; $this->_customized[$option_name] = array(); if (preg_match('/^[0-9]+$/', $widget_number)) { $option_name .= '[' . $widget_number . ']'; $this->_customized[$option_name][$widget_number] = array(); } } $function = array($this, 'prepreview_added_sidebars_widgets'); $hook = 'option_sidebars_widgets'; add_filter($hook, $function); $this->_prepreview_added_filters[] = compact('hook', 'function'); $hook = 'default_option_sidebars_widgets'; add_filter($hook, $function); $this->_prepreview_added_filters[] = compact('hook', 'function'); $function = array($this, 'prepreview_added_widget_instance'); foreach ($this->_customized as $setting_id => $value) { if (preg_match('/^(widget_.+?)(?:\\[(\\d+)\\])?$/', $setting_id, $matches)) { $option = $matches[1]; $hook = sprintf('option_%s', $option); if (!has_filter($hook, $function)) { add_filter($hook, $function); $this->_prepreview_added_filters[] = compact('hook', 'function'); } $hook = sprintf('default_option_%s', $option); if (!has_filter($hook, $function)) { add_filter($hook, $function); $this->_prepreview_added_filters[] = compact('hook', 'function'); } /* * Make sure the option is registered so that the update_option() * won't fail due to the filters providing a default value, which * causes the update_option() to get confused. */ add_option($option, array()); } } }
/** * Handle Ajax request to return the settings partial value. * * @since 4.5.0 * @access public */ public function handle_render_partials_request() { if (!$this->is_render_partials_request()) { return; } $this->manager->remove_preview_signature(); if (!check_ajax_referer('preview-customize_' . $this->manager->get_stylesheet(), 'nonce', false)) { status_header(403); wp_send_json_error('nonce_check_fail'); } else { if (!current_user_can('customize') || !is_customize_preview()) { status_header(403); wp_send_json_error('expected_customize_preview'); } else { if (!isset($_POST['partials'])) { status_header(400); wp_send_json_error('missing_partials'); } } } $partials = json_decode(wp_unslash($_POST['partials']), true); if (!is_array($partials)) { wp_send_json_error('malformed_partials'); } $this->add_dynamic_partials(array_keys($partials)); /** * Do setup before rendering each partial. * * Plugins may do things like wp_enqueue_scripts() to gather a list of * the scripts and styles which may get enqueued in the response. * * @todo Eventually this should automatically by default do wp_enqueue_scripts(). See below. * * @since 4.5.0 * * @param WP_Customize_Selective_Refresh $this Selective refresh component. */ do_action('customize_render_partials_before', $this); set_error_handler(array($this, 'handle_error'), error_reporting()); $contents = array(); foreach ($partials as $partial_id => $container_contexts) { $this->current_partial_id = $partial_id; if (!is_array($container_contexts)) { wp_send_json_error('malformed_container_contexts'); } $partial = $this->get_partial($partial_id); if (!$partial) { $contents[$partial_id] = null; continue; } $contents[$partial_id] = array(); if (empty($container_contexts)) { // Since there are no container contexts, render just once. $contents[$partial_id][] = $partial->render(null); } else { foreach ($container_contexts as $container_context) { $contents[$partial_id][] = $partial->render($container_context); } } } $this->current_partial_id = null; restore_error_handler(); $response = array('contents' => $contents); if (defined('WP_DEBUG_DISPLAY') && WP_DEBUG_DISPLAY) { $response['errors'] = $this->triggered_errors; } /** * Filter the response from rendering the partials. * * This is similar to the <code>infinite_scroll_results</code> filter in Jetpack, * and the <code>The_Neverending_Home_Page::filter_infinite_scroll_results()</code> * function which will amend the response with scripts and styles that are enqueued * so that the client can inject these new dependencies into the document. * * @todo This method should eventually go ahead and include the enqueued scripts and styles by default. Beware of any sripts that do document.write(). * * @since 4.5.0 * * @param array $response { * Response. * * @type array $contents Associative array mapping a partial ID its corresponding array of contents for the containers requested. * @type array [$errors] List of errors triggered during rendering of partials, if WP_DEBUG_DISPLAY is enabled. * } * * @param WP_Customize_Selective_Refresh $this Selective refresh component. */ $response = apply_filters('customize_render_partials_response', $response, $this); wp_send_json_success($response); }
/** * Test saving changesets with varying users and capabilities. * * @ticket 38705 * @covers WP_Customize_Manager::save_changeset_post() */ function test_save_changeset_post_with_varying_users() { global $wp_customize; add_theme_support('custom-background'); wp_set_current_user(self::$admin_user_id); $other_admin_user_id = self::factory()->user->create(array('role' => 'administrator')); $uuid = wp_generate_uuid4(); $wp_customize = $this->create_test_manager($uuid); $r = $wp_customize->save_changeset_post(array('status' => 'auto-draft', 'data' => array('blogname' => array('value' => 'Admin 1 Title'), 'scratchpad' => array('value' => 'Admin 1 Scratch'), 'background_color' => array('value' => '#000000')))); $this->assertInternalType('array', $r); $this->assertEquals(array_fill_keys(array('blogname', 'scratchpad', 'background_color'), true), $r['setting_validities']); $post_id = $wp_customize->find_changeset_post_id($uuid); $data = json_decode(get_post($post_id)->post_content, true); $this->assertEquals(self::$admin_user_id, $data['blogname']['user_id']); $this->assertEquals(self::$admin_user_id, $data['scratchpad']['user_id']); $this->assertEquals(self::$admin_user_id, $data[$this->manager->get_stylesheet() . '::background_color']['user_id']); // Attempt to save just one setting under a different user. wp_set_current_user($other_admin_user_id); $wp_customize = $this->create_test_manager($uuid); $r = $wp_customize->save_changeset_post(array('status' => 'auto-draft', 'data' => array('blogname' => array('value' => 'Admin 2 Title'), 'background_color' => array('value' => '#FFFFFF')))); $this->assertInternalType('array', $r); $this->assertEquals(array_fill_keys(array('blogname', 'background_color'), true), $r['setting_validities']); $data = json_decode(get_post($post_id)->post_content, true); $this->assertEquals('Admin 2 Title', $data['blogname']['value']); $this->assertEquals($other_admin_user_id, $data['blogname']['user_id']); $this->assertEquals('Admin 1 Scratch', $data['scratchpad']['value']); $this->assertEquals(self::$admin_user_id, $data['scratchpad']['user_id']); $this->assertEquals('#FFFFFF', $data[$this->manager->get_stylesheet() . '::background_color']['value']); $this->assertEquals($other_admin_user_id, $data[$this->manager->get_stylesheet() . '::background_color']['user_id']); // Attempt to save now as under-privileged user. $wp_customize = $this->create_test_manager($uuid); $r = $wp_customize->save_changeset_post(array('status' => 'auto-draft', 'data' => array('blogname' => array('value' => 'Admin 2 Title'), 'scratchpad' => array('value' => 'Subscriber Scratch')), 'user_id' => self::$subscriber_user_id)); $this->assertInternalType('array', $r); $this->assertEquals(array_fill_keys(array('scratchpad', 'blogname'), true), $r['setting_validities']); $data = json_decode(get_post($post_id)->post_content, true); $this->assertEquals($other_admin_user_id, $data['blogname']['user_id'], 'Expected setting to be untouched.'); $this->assertEquals(self::$subscriber_user_id, $data['scratchpad']['user_id']); $this->assertEquals($other_admin_user_id, $data[$this->manager->get_stylesheet() . '::background_color']['user_id']); // Manually update the changeset so that the user_id context is not included. $data = json_decode(get_post($post_id)->post_content, true); $data['blogdescription']['value'] = 'Programmatically-supplied Tagline'; wp_update_post(wp_slash(array('ID' => $post_id, 'post_content' => wp_json_encode($data)))); // Ensure the modifying user set as the current user when each is saved, simulating WP Cron envronment. wp_set_current_user(0); $save_counts = array(); foreach (array_keys($data) as $setting_id) { $setting_id = preg_replace('/^.+::/', '', $setting_id); $save_counts[$setting_id] = did_action(sprintf('customize_save_%s', $setting_id)); } $this->filtered_setting_current_user_ids = array(); foreach ($wp_customize->settings() as $setting) { add_filter(sprintf('customize_sanitize_%s', $setting->id), array($this, 'filter_customize_setting_to_log_current_user'), 10, 2); } wp_update_post(array('ID' => $post_id, 'post_status' => 'publish')); foreach (array_keys($data) as $setting_id) { $setting_id = preg_replace('/^.+::/', '', $setting_id); $this->assertEquals($save_counts[$setting_id] + 1, did_action(sprintf('customize_save_%s', $setting_id)), $setting_id); } $this->assertEqualSets(array('blogname', 'blogdescription', 'background_color', 'scratchpad'), array_keys($this->filtered_setting_current_user_ids)); $this->assertEquals($other_admin_user_id, $this->filtered_setting_current_user_ids['blogname']); $this->assertEquals(0, $this->filtered_setting_current_user_ids['blogdescription']); $this->assertEquals(self::$subscriber_user_id, $this->filtered_setting_current_user_ids['scratchpad']); $this->assertEquals($other_admin_user_id, $this->filtered_setting_current_user_ids['background_color']); $this->assertEquals('Subscriber Scratch', get_option('scratchpad')); }
/** * Add Custom CSS section and controls. * * @param WP_Customize_Manager $wp_customize WP_Customize_Manager instance. */ public static function customize_register($wp_customize) { /** * SETTINGS. */ $wp_customize->add_setting('jetpack_custom_css[preprocessor]', array('default' => '', 'transport' => 'postMessage', 'sanitize_callback' => array(__CLASS__, 'sanitize_preprocessor'))); $wp_customize->add_setting('jetpack_custom_css[replace]', array('default' => false, 'transport' => 'refresh')); $wp_customize->add_setting('jetpack_custom_css[content_width]', array('default' => '', 'transport' => 'refresh', 'sanitize_callback' => array(__CLASS__, 'intval_base10'))); // Add custom sanitization to the core css customizer setting. foreach ($wp_customize->settings() as $setting) { if ($setting instanceof WP_Customize_Custom_CSS_Setting) { add_filter("customize_sanitize_{$setting->id}", array(__CLASS__, 'sanitize_css_callback'), 10, 2); } } /** * CONTROLS. */ // Overwrite the Core Control. $core_custom_css = $wp_customize->get_control('custom_css'); if ($core_custom_css) { $wp_customize->remove_control('custom_css'); $core_custom_css->type = 'jetpackCss'; $wp_customize->add_control($core_custom_css); } $wp_customize->selective_refresh->add_partial('custom_css', array('type' => 'custom_css', 'selector' => '#wp-custom-css', 'container_inclusive' => false, 'fallback_refresh' => false, 'settings' => array('custom_css[' . $wp_customize->get_stylesheet() . ']', 'jetpack_custom_css[preprocessor]'), 'render_callback' => array(__CLASS__, 'echo_custom_css_partial'))); $wp_customize->add_control('wpcom_custom_css_content_width_control', array('type' => 'text', 'label' => __('Media Width', 'jetpack'), 'section' => 'custom_css', 'settings' => 'jetpack_custom_css[content_width]')); $wp_customize->add_control('jetpack_css_mode_control', array('type' => 'checkbox', 'label' => __('Don\'t use the theme\'s original CSS.', 'jetpack'), 'section' => 'custom_css', 'settings' => 'jetpack_custom_css[replace]')); /** * An action to grab on to if another Jetpack Module would like to add its own controls. * * @module custom-css * * @since 4.4.2 * * @param $wp_customize The WP_Customize object. */ do_action('jetpack_custom_css_customizer_controls', $wp_customize); /** This filter is documented in modules/custom-css/custom-css.php */ $preprocessors = apply_filters('jetpack_custom_css_preprocessors', array()); if (!empty($preprocessors)) { $preprocessor_choices = array('' => __('None', 'jetpack')); foreach ($preprocessors as $preprocessor_key => $processor) { $preprocessor_choices[$preprocessor_key] = $processor['name']; } $wp_customize->add_control('jetpack_css_preprocessors_control', array('type' => 'select', 'choices' => $preprocessor_choices, 'label' => __('Preprocessor', 'jetpack'), 'section' => 'custom_css', 'settings' => 'jetpack_custom_css[preprocessor]')); } }
/** * 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; }