/** * Test override post date if empty. * * @covers CustomizeSnapshots\Customize_Snapshot_Manager::override_post_date_default_data() */ public function test_override_post_date_default_data() { $post_id = $this->factory()->post->create(); $post = get_post($post_id); $post->post_date = $post->post_date_gmt = $post->post_modified = $post->post_modified_gmt = '0000-00-00 00:00:00'; $this->manager->override_post_date_default_data($post); $this->assertNotEquals($post->post_date, '0000-00-00 00:00:00'); $this->assertNotEquals($post->post_date_gmt, '0000-00-00 00:00:00'); $this->assertNotEquals($post->post_modified, '0000-00-00 00:00:00'); $this->assertNotEquals($post->post_modified_gmt, '0000-00-00 00:00:00'); }
/** * Initial loader. * * @access public * * @throws Exception If the UUID is invalid. * * @param Customize_Snapshot_Manager $snapshot_manager Customize snapshot bootstrap instance. * @param string $uuid Snapshot unique identifier. */ public function __construct(Customize_Snapshot_Manager $snapshot_manager, $uuid) { $this->snapshot_manager = $snapshot_manager; $this->data = array(); if (!Customize_Snapshot_Manager::is_valid_uuid($uuid)) { throw new Exception(__('You\'ve entered an invalid snapshot UUID.', 'customize-snapshots')); } $this->uuid = $uuid; $post = $this->post(); if ($post) { $this->data = $this->snapshot_manager->post_type->get_post_content($post); } }
/** * Test create item. */ function test_create_item() { wp_set_current_user($this->factory()->user->create(array('role' => 'administrator'))); $request = new \WP_REST_Request('POST', '/wp/v2/customize_snapshots'); $request->set_param('content', array('blogname' => array('value' => 'test'))); $request->set_param('slug', Customize_Snapshot_Manager::generate_uuid()); $response = $this->server->dispatch($request); $this->assertErrorResponse('rest_cannot_create', $response); }
/** * Helper function to make the Ajax call directy to `Customize_Snapshot_Manager::save_snapshot`. * * @see Customize_Snapshot_Manager::save_snapshot() */ function make_save_snapshot_ajax_call() { try { ini_set('implicit_flush', false); ob_start(); $manager = new Customize_Snapshot_Manager($this->plugin); $manager->publish_snapshot_with_customize_save_after(); $buffer = ob_get_clean(); if (!empty($buffer)) { $this->_last_response = $buffer; } } catch (\WPAjaxDieContinueException $e) { unset($e); } }
/** * Initiate the plugin resources. * * Priority is 8 because \CustomizeWidgetsPlus\Widget_Posts::init() happens * at priority 9, and this calls \CustomizeWidgetsPlus\Widget_Posts::add_customize_hooks(). * So we need priority 8 so that the Customizer will be initialized before Widget Posts * initializes. * * @action after_setup_theme, 8 */ public function init() { $this->customize_snapshot_manager = new Customize_Snapshot_Manager($this); $this->customize_snapshot_manager->init(); }
/** * 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; }
/** * Test that the snapshot object is passed as the second filter param. * * @see Customize_Snapshot::save() */ function test_filter_customize_snapshot_save() { $manager = new Customize_Snapshot_Manager($this->plugin); $manager->ensure_customize_manager(); $manager->init(); $snapshot = new Customize_Snapshot($manager, self::UUID); $that = $this; // For PHP 5.3. add_filter('customize_snapshot_save', function ($data, $test_snapshot) use($that) { $that->filtered_snapshot = $test_snapshot; return $data; }, 10, 2); $snapshot->save(array('uuid' => self::UUID, 'data' => array('foo' => array('value' => 'bar')))); $this->assertEquals($snapshot, $this->filtered_snapshot); }
/** * Snapshot publish. * * @see Post_Type::save() */ function test_publish_snapshot() { $admin_user_id = $this->factory()->user->create(array('role' => 'administrator')); wp_set_current_user($admin_user_id); $post_type = get_plugin_instance()->customize_snapshot_manager->post_type; $post_type->register(); $tag_line = 'Snapshot blog'; $data = array('blogdescription' => array('value' => $tag_line), 'foo' => array('value' => 'bar'), 'baz' => array('value' => null)); $validated_content = array('blogdescription' => array('value' => $tag_line), 'foo' => array('value' => 'bar', 'publish_error' => 'unrecognized_setting'), 'baz' => array('value' => null, 'publish_error' => 'null_value')); /* * Ensure that directly updating a post succeeds with invalid settings * works because the post is a draft. Note that if using * Customize_Snapshot::set() this would fail because it does validation. */ $post_id = $post_type->save(array('uuid' => Customize_Snapshot_Manager::generate_uuid(), 'data' => $data, 'status' => 'draft')); wp_update_post(array('ID' => $post_id, 'post_status' => 'draft')); $content = $post_type->get_post_content(get_post($post_id)); $this->assertEquals($data, $content); /* * Ensure that attempting to publish a snapshot with invalid settings * will get the publish_errors added as well as kick it back to pending. */ remove_all_filters('redirect_post_location'); $post_id = $post_type->save(array('uuid' => Customize_Snapshot_Manager::generate_uuid(), 'data' => $data, 'status' => 'draft')); wp_publish_post($post_id); $snapshot_post = get_post($post_id); $content = $post_type->get_post_content($snapshot_post); $this->assertEquals('pending', $snapshot_post->post_status); $this->assertEquals($validated_content, $content); $this->assertContains('snapshot_error_on_publish=1', apply_filters('redirect_post_location', get_edit_post_link($snapshot_post->ID), $snapshot_post->ID)); /* * Remove invalid settings and now attempt publish. */ remove_all_filters('redirect_post_location'); unset($data['foo']); unset($data['baz']); $post_id = $post_type->save(array('uuid' => Customize_Snapshot_Manager::generate_uuid(), 'data' => $data, 'status' => 'draft')); wp_publish_post($post_id); $snapshot_post = get_post($post_id); $content = $post_type->get_post_content($snapshot_post); $this->assertEquals('publish', $snapshot_post->post_status); $this->assertEquals($data, $content); $this->assertEquals($tag_line, get_bloginfo('description')); $this->assertNotContains('snapshot_error_on_publish=1', apply_filters('redirect_post_location', get_edit_post_link($snapshot_post->ID), $snapshot_post->ID)); }
/** * Persist the data in the snapshot post content. * * @param array $args Args. * @return int|\WP_Error Post ID for snapshot or WP_Error instance. */ public function save(array $args) { // @todo Add support for $args['post_id']. if (empty($args['uuid']) || !Customize_Snapshot_Manager::is_valid_uuid($args['uuid'])) { return new \WP_Error('missing_valid_uuid'); } $post_arr = array('post_name' => $args['uuid'], 'post_title' => $args['uuid'], 'post_type' => static::SLUG, 'meta_input' => array('_snapshot_version' => $this->snapshot_manager->plugin->version)); if (!empty($args['status'])) { if (isset($args['post_date'], $args['edit_date'], $args['post_date_gmt'])) { $post_arr['post_date'] = $args['post_date']; $post_arr['edit_date'] = $args['edit_date']; $post_arr['post_date_gmt'] = $args['post_date_gmt']; } if (!get_post_status_object($args['status'])) { return new \WP_Error('bad_status'); } $post_arr['post_status'] = $args['status']; } $post_id = $this->find_post($args['uuid']); $is_update = !empty($post_id); if ($post_id) { $post_arr['ID'] = $post_id; } if (isset($args['data'])) { if (!is_array($args['data'])) { return new \WP_Error('missing_data'); } foreach ($args['data'] as $setting_id => $setting_params) { if (!is_array($setting_params)) { return new \WP_Error('bad_setting_params'); } if (!array_key_exists('value', $setting_params)) { return new \WP_Error('missing_value_param'); } } $post_arr['post_content'] = Customize_Snapshot_Manager::encode_json($args['data']); } elseif (!$is_update) { $post_arr['post_content'] = Customize_Snapshot_Manager::encode_json(array()); } if (!empty($args['theme'])) { $post_arr['meta_input']['_snapshot_theme'] = $args['theme']; } if (!empty($args['author'])) { $post_arr['post_author'] = $args['author']; } if (!empty($args['date_gmt'])) { $post_arr['post_date_gmt'] = $args['date_gmt']; } $this->suspend_kses(); if ($is_update) { $r = wp_update_post(wp_slash($post_arr), true); } else { $r = wp_insert_post(wp_slash($post_arr), true); } $this->restore_kses(); return $r; }