Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add user preference api #1948

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions gutenberg.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
require_once dirname( __FILE__ ) . '/lib/i18n.php';
require_once dirname( __FILE__ ) . '/lib/parser.php';
require_once dirname( __FILE__ ) . '/lib/register.php';
require_once dirname( __FILE__ ) . '/lib/class-gutenberg-user-preferences.php';
require_once dirname( __FILE__ ) . '/lib/api.php';

// Register server-side code for individual blocks.
require_once dirname( __FILE__ ) . '/lib/blocks/latest-posts.php';
Expand Down
21 changes: 21 additions & 0 deletions lib/api.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php
/**
* API Endpoints.
*
* @package gutenberg
*/

if ( ! defined( 'ABSPATH' ) ) {
die( 'Silence is golden.' );
}

/**
* Callback for registering gutenberg API routes.
*
* @return void
*/
function gutenberg_register_routes() {
Gutenberg_User_Preferences::register_routes();
}

add_action( 'rest_api_init', 'gutenberg_register_routes' );
128 changes: 128 additions & 0 deletions lib/class-gutenberg-user-preferences.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
<?php
/**
* Preference handling.
*
* @package gutenberg
*/

if ( ! defined( 'ABSPATH' ) ) {
die( 'Silence is golden.' );
}

/**
* User preference management.
*/
class Gutenberg_User_Preferences {
/**
* Used to validate preference names are correct.
*
* @var Array $valid_preferences List of valid preference names.
*/
public static $valid_preferences = array(
'block_usage',
'layout_config',
);

/**
* Validates that a preference name is valid, as part of a REST API request.
*
* @param string $preference_name Preference name.
* @return bool If the preference name is valid preference.
*/
public static function is_valid_preference_name( $preference_name ) {
return in_array( $preference_name, self::$valid_preferences );
}

/**
* Validates that all preference names in the request are valid.
*
* @param String $param Preference name.
* @param WP_REST_Request $request The REST request.
* @param String $key The parameter key for the value being validated.
* @return Bool If all keys are valid preference names.
*/
public static function validate_preferences( $param, $request, $key ) {
foreach ( $param as $preference_name => $value ) {
if ( ! self::is_valid_preference_name( $preference_name ) ) {
return false;
}
}
return true;
}

/**
* Checks that the user has the needd permissions to store and read preferences.
*
* @return bool
*/
public static function check_permissions() {
$user_id = get_current_user_id();
return user_can( $user_id, 'edit_posts' );
}

/**
* Gets all preferences for a user.
*
* @param WP_REST_Request $request The REST request.
* @return mixed Stored preference values indexed by preference name.
*/
public static function get_preferences( $request ) {
$user_id = get_current_user_id();
$preferences = array();
foreach ( self::$valid_preferences as $preference_name ) {
$preferences[ $preference_name ] = get_user_meta( $user_id, 'gutenberg_' . $preference_name );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens when a preference is unset? It should be excluded from this array.

}
return $preferences;
}

/**
* Sets preferences for a user.
*
* Expects an array of preferences to store, indexed by the preference name.
*
* @param WP_REST_Request $request The REST request.
* @return bool If the store was successful.
*/
public static function set_preferences( $request ) {
$params = $request->get_params();
$user_id = get_current_user_id();

foreach ( $params['preferences'] as $preference_name => $value ) {
if ( ! self::is_valid_preference_name( $preference_name ) ) {
return false;
}
}

foreach ( $params['preferences'] as $preference_name => $value ) {
update_user_meta( $user_id, 'gutenberg_' . $preference_name, $value );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If null is passed here, then the preference should be deleted instead.

}

return true;
}

/**
* Registers preferences API routes.
*
* @return void
*/
public static function register_routes() {
register_rest_route( 'gutenberg/v1', '/user-preferences', array(
array(
'methods' => WP_REST_Server::READABLE,
'callback' => 'Gutenberg_User_Preferences::get_preferences',
'permission_callback' => 'Gutenberg_User_Preferences::check_permissions',
),
array(
'methods' => WP_REST_Server::EDITABLE,
'callback' => 'Gutenberg_User_Preferences::set_preferences',
'permission_callback' => 'Gutenberg_User_Preferences::check_permissions',
'args' => array(
'preferences' => array(
'required' => true,
'validate_callback' => 'Gutenberg_User_Preferences::validate_preferences',
),
),
),
) );
}
}
146 changes: 146 additions & 0 deletions phpunit/class-user-preferences-api-test.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
<?php
/**
* User preferences API Tests
*
* @package Gutenberg
*/

/**
* Test Gutenberg_User_Preferences
*/
class User_Preferences_API_Test extends WP_Test_REST_Controller_Testcase {
protected static $user;
protected static $editor;
public static function wpSetUpBeforeClass( $factory ) {
self::$editor = $factory->user->create( array(
'role' => 'editor',
'user_email' => 'editor@example.com',
) );
self::$user = $factory->user->create( array(
'role' => 'subscriber',
'user_email' => 'user@example.com',
) );
}

public static function wpTearDownAfterClass() {
self::delete_user( self::$user );
self::delete_user( self::$editor );
}

public function test_register_routes() {
$routes = $this->server->get_routes();
$this->assertArrayHasKey( '/gutenberg/v1/user-preferences', $routes );
$this->assertCount( 2, $routes['/gutenberg/v1/user-preferences'] );
}

public function test_get_item() {
wp_set_current_user( self::$editor );

$request = new WP_REST_Request( 'GET', '/gutenberg/v1/user-preferences' );
$response = $this->server->dispatch( $request );

$this->assertEquals( 200, $response->get_status() );

// Each valid preference should be present.
foreach ( Gutenberg_User_Preferences::$valid_preferences as $preference ) {
$this->assertArrayHasKey( $preference, $response->data );
}
}

public function test_get_item_without_permission() {
wp_set_current_user( self::$user );

$request = new WP_REST_Request( 'GET', '/gutenberg/v1/user-preferences' );
$response = $this->server->dispatch( $request );

$this->assertEquals( 403, $response->get_status() );
}

/**
* Should fail with invalid preferences
*/
function test_set_item_with_invalid_preferences() {
wp_set_current_user( self::$editor );

$params = array(
'preferences' => array(
'bad_pref_name' => 42,
),
);

$request = new WP_REST_Request( 'POST', '/gutenberg/v1/user-preferences' );
$request->add_header( 'content-type', 'application/x-www-form-urlencoded' );
$request->set_body_params( $params );

$response = $this->server->dispatch( $request );
$this->assertEquals( 'rest_invalid_param', $response->data['code'] );
$this->assertEquals( 400, $response->get_status() );
}

/**
* Should set an individual preference
*/
function test_update_item() {
wp_set_current_user( self::$editor );

$params = array(
'preferences' => array(
'block_usage' => array( 'core/text' ),
),
);

$request = new WP_REST_Request( 'POST', '/gutenberg/v1/user-preferences' );
$request->add_header( 'content-type', 'application/x-www-form-urlencoded' );
$request->set_body_params( $params );
$response = $this->server->dispatch( $request );

$this->assertEquals( 200, $response->get_status() );

$request = new WP_REST_Request( 'GET', '/gutenberg/v1/user-preferences' );
$response = $this->server->dispatch( $request );

$this->assertEquals( array( 'core/text' ), $response->data['block_usage'][0] );

}

/**
* Should set multiple preferences
*/
function test_update_item_multiple_preferences() {
wp_set_current_user( self::$editor );

$params = array(
'preferences' => array(
'block_usage' => array( 'core/text' ),
'layout_config' => array(
'things' => 'awesome',
),
),
);

$request = new WP_REST_Request( 'POST', '/gutenberg/v1/user-preferences' );
$request->add_header( 'content-type', 'application/x-www-form-urlencoded' );
$request->set_body_params( $params );
$response = $this->server->dispatch( $request );

$this->assertEquals( 200, $response->get_status() );

$request = new WP_REST_Request( 'GET', '/gutenberg/v1/user-preferences' );
$response = $this->server->dispatch( $request );

$this->assertEquals( array( 'core/text' ), $response->data['block_usage'][0] );
$this->assertEquals( array(
'things' => 'awesome',
), $response->data['layout_config'][0] );

}

/** API does not implement these. */
public function test_get_items() {}
public function test_create_item() {}
public function test_delete_item() {}
public function test_prepare_item() {}
public function test_context_param() {}
public function test_get_item_schema() {}

}