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 3 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/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
89 changes: 89 additions & 0 deletions lib/api.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
<?php
/**
* API Endpoints.
*
* @package gutenberg
*/

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

/**
* Validates that a preference name is valid, as part of a REST API request.
*
* @param string $preference_name Preference name.
* @param WP_REST_Request $request The REST request.
* @param string $request The parameter key for the value being validated.
* @return bool If the preference name is valid.
*/
function gutenberg_validate_preference_name( $preference_name, $request, $key ) {
Copy link
Member

Choose a reason for hiding this comment

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

Do we need to wrap all of this logic in functions? Why not just a single static class that also contains a register_routes method?

Copy link
Member Author

Choose a reason for hiding this comment

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

We don't need to, but merging these and the Gutenberg_User_Preferences would couple it to the API, as it would become dependent on the API request. But perhaps that's not a problem, I'm still getting used to our coding styles and usually I'd have a class that handled the preferences and could be reused outside of the API. I'll change this round.

return Gutenberg_User_Preferences::is_valid_preference_name( $preference_name );
}

/**
* Callback for getting a specific user preference.
*
* @param WP_REST_Request $request The REST request.
* @return mixed The value of the preference.
*/
function gutenburg_get_user_preference( $request ) {
$params = $request->get_url_params();
return Gutenberg_User_Preferences::get_preference( get_current_user_id(), $params['preference'] );
}

/**
* Callback for getting all preferences for a user.
*
* @param WP_REST_Request $request The REST request.
* @return array Associative array of user preferences.
*/
function gutenburg_get_user_preferences( $request ) {
return Gutenberg_User_Preferences::get_preferences( get_current_user_id() );
}

/**
* Callback for setting a user preference.
*
* @param WP_REST_Request $request The REST request.
* @return bool If the preference was set successfully.
*/
function gutenburg_set_user_preference( $request ) {
$params = $request->get_params();
return Gutenberg_User_Preferences::set_preference( get_current_user_id(), $params['preference'], $params['value'] );
}

/**
* Callback for registering gutenberg API routes.
*
* @return null
*/
function gutenburg_register_routes() {
register_rest_route( 'gutenburg/v1', '/user-preferences', array(
'methods' => WP_REST_Server::READABLE,
'callback' => 'gutenburg_get_user_preferences',
Copy link
Member

Choose a reason for hiding this comment

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

We will want a permission_callback here and with other endpoints to make sure the user is authenticated (and has whatever capability we deem necessary to use the Gutenberg interface, probably edit_posts). We should be able to re-use the same permission callback for all of these routes..

) );
register_rest_route( 'gutenburg/v1', '/user-preferences/(?P<preference>[a-z_]+)', array(
'methods' => WP_REST_Server::READABLE,
'callback' => 'gutenburg_get_user_preference',
'args' => array(
'preference' => array(
'validate_callback' => 'gutenberg_validate_preference_name',
),
),
) );
register_rest_route( 'gutenburg/v1', '/user-preferences/(?P<preference>[a-z_]+)', array(
'methods' => WP_REST_Server::EDITABLE,
'callback' => 'gutenburg_set_user_preference',
'args' => array(
'value' => array(
'required' => true,
),
'preference' => array(
'validate_callback' => 'gutenberg_validate_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.

Let's combine these two endpoints using the same route ('/user-preferences/(?P<preference>[a-z_]+)') into a single register_rest_route call. See: https://core.trac.wordpress.org/browser/tags/4.8/src/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php#L90

Copy link
Member

Choose a reason for hiding this comment

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

Actually, now that I think about it, I don't think there are many use cases for getting a single preference. Setting a single preference and setting multiple preferences can be done the same way:

POST /gutenberg/v1/user-preferences
{
    "pref1": "value1",
    "pref2: "value2" // if needed, and so on
}

or the value can be set to null to delete them.

Copy link
Member Author

Choose a reason for hiding this comment

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

Agreed. Will change this.

}

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

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

/***
* User preference management.
*/
class Gutenberg_User_Preferences {
const 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 );
Copy link
Member

Choose a reason for hiding this comment

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

We can add a filter here to allow plugins to register their own user preferences. Not something that needs to be done for this PR.

}

/**
* Gets a single preference for a user.
*
* @param int $user_id User ID.
* @param string $preference_name Preference name.
* @return mixed Stored preference value.
*/
public static function get_preference( $user_id, $preference_name ) {
if ( ! self::is_valid_preference_name( $preference_name ) ) {
return false;
}
return get_user_meta( $user_id, 'gutenburg_' . $preference_name );
}

/**
* Gets all preferences for a user.
*
* @param int $user_id User ID.
* @return mixed Stored preference values indexed by preference name.
*/
public static function get_preferences( $user_id ) {
$preferences = array();
foreach ( self::VALID_PREFERENCES as $preference_name ) {
$preferences[ $preference_name ] = get_user_meta( $user_id, 'gutenburg_' . $preference_name );
}
return $preferences;
}

/**
* Sets a single preference for a user.
*
* @param int $user_id User ID.
* @param string $preference_name Preference name.
* @param mixed Preference value to store.
* @return bool If the store was successful.
*/
public static function set_preference( $user_id, $preference_name, $value ) {
if ( ! self::is_valid_preference_name( $preference_name ) ) {
return false;
}
update_user_meta( $user_id, 'gutenburg_' . $preference_name, $value );
return true;
}
}