From fda46f9920b991520d0a20c0d0adf5c6e90f1f29 Mon Sep 17 00:00:00 2001 From: Joel Thiessen <444632+joelclimbsthings@users.noreply.github.com> Date: Tue, 9 Feb 2021 18:35:47 -0800 Subject: [PATCH] Adding Navigation Favorites REST API Endpoints (https://github.com/woocommerce/woocommerce-admin/pull/6282) --- plugins/woocommerce-admin/src/API/Init.php | 1 + .../src/API/NavigationFavorites.php | 327 ++++++++++++++++++ .../src/Features/Navigation/Favorites.php | 42 ++- 3 files changed, 360 insertions(+), 10 deletions(-) create mode 100644 plugins/woocommerce-admin/src/API/NavigationFavorites.php diff --git a/plugins/woocommerce-admin/src/API/Init.php b/plugins/woocommerce-admin/src/API/Init.php index b74386bb780..ed2b5617841 100644 --- a/plugins/woocommerce-admin/src/API/Init.php +++ b/plugins/woocommerce-admin/src/API/Init.php @@ -97,6 +97,7 @@ class Init { 'Automattic\WooCommerce\Admin\API\OnboardingProfile', 'Automattic\WooCommerce\Admin\API\OnboardingTasks', 'Automattic\WooCommerce\Admin\API\OnboardingThemes', + 'Automattic\WooCommerce\Admin\API\NavigationFavorites', ); // The performance indicators controller must be registered last, after other /stats endpoints have been registered. diff --git a/plugins/woocommerce-admin/src/API/NavigationFavorites.php b/plugins/woocommerce-admin/src/API/NavigationFavorites.php new file mode 100644 index 00000000000..4f4b7bc364a --- /dev/null +++ b/plugins/woocommerce-admin/src/API/NavigationFavorites.php @@ -0,0 +1,327 @@ + 400, + 'woocommerce_favorites_already_exists' => 409, + 'woocommerce_favorites_does_not_exist' => 404, + 'woocommerce_favorites_invalid_user' => 400, + 'woocommerce_favorites_unauthenticated' => 401, + ); + + /** + * Register the routes + */ + public function register_routes() { + register_rest_route( + $this->namespace, + '/' . $this->rest_base, + array( + array( + 'methods' => \WP_REST_Server::READABLE, + 'callback' => array( $this, 'get_items' ), + 'permission_callback' => array( $this, 'get_items_permissions_check' ), + 'args' => array( + 'user_id' => array( + 'required' => true, + 'validate_callback' => function( $param, $request, $key ) { + return is_numeric( $param ); + }, + ), + ), + ), + array( + 'methods' => \WP_REST_Server::CREATABLE, + 'callback' => array( $this, 'add_item' ), + 'permission_callback' => array( $this, 'add_item_permissions_check' ), + 'args' => array( + 'item_id' => array( + 'required' => true, + ), + 'user_id' => array( + 'required' => true, + 'validate_callback' => function( $param, $request, $key ) { + return is_numeric( $param ); + }, + ), + ), + ), + array( + 'methods' => \WP_REST_Server::DELETABLE, + 'callback' => array( $this, 'delete_item' ), + 'permission_callback' => array( $this, 'delete_item_permissions_check' ), + 'args' => array( + 'item_id' => array( + 'required' => true, + ), + 'user_id' => array( + 'required' => true, + 'validate_callback' => function( $param, $request, $key ) { + return is_numeric( $param ); + }, + ), + ), + ), + 'schema' => array( $this, 'get_public_item_schema' ), + ) + ); + + register_rest_route( + $this->namespace, + '/' . $this->rest_base . '/me', + array( + array( + 'methods' => \WP_REST_Server::READABLE, + 'callback' => array( $this, 'get_items_by_current_user' ), + 'permission_callback' => array( $this, 'current_user_permissions_check' ), + ), + array( + 'methods' => \WP_REST_Server::CREATABLE, + 'callback' => array( $this, 'add_item_by_current_user' ), + 'permission_callback' => array( $this, 'current_user_permissions_check' ), + 'args' => array( + 'item_id' => array( + 'required' => true, + ), + ), + ), + array( + 'methods' => \WP_REST_Server::DELETABLE, + 'callback' => array( $this, 'delete_item_by_current_user' ), + 'permission_callback' => array( $this, 'current_user_permissions_check' ), + 'args' => array( + 'item_id' => array( + 'required' => true, + ), + ), + ), + 'schema' => array( $this, 'get_public_item_schema' ), + ) + ); + + } + + /** + * Get all favorites. + * + * @param WP_REST_Request $request Request data. + * @return WP_REST_Response + */ + public function get_items( $request ) { + $user_id = $request->get_param( 'user_id' ); + + $response = Favorites::get_all( $user_id ); + + if ( is_wp_error( $response ) || ! $response ) { + return rest_ensure_response( $this->prepare_error( $response ) ); + } + + return rest_ensure_response( + array_map( 'stripslashes', $response ) + ); + } + + /** + * Get all favorites of current user. + * + * @param WP_REST_Request $request Request data. + * @return WP_REST_Response + */ + public function get_items_by_current_user( $request ) { + $current_user = get_current_user_id(); + + if ( ! $current_user ) { + return $this->prepare_error( + new \WP_Error( + 'woocommerce_favorites_unauthenticated', + __( 'You must be authenticated to use this endpoint', 'woocommerce-admin' ) + ) + ); + } + + $request->set_param( 'user_id', $current_user ); + + return $this->get_items( $request ); + } + + /** + * Add a favorite. + * + * @param WP_REST_Request $request Request data. + * @return WP_REST_Response + */ + public function add_item( $request ) { + $user_id = $request->get_param( 'user_id' ); + $fav_id = $request->get_param( 'item_id' ); + $user = get_userdata( $user_id ); + + if ( false === $user ) { + return $this->prepare_error( + new \WP_Error( + 'woocommerce_favorites_invalid_user', + __( 'Invalid user_id provided', 'woocommerce-admin' ) + ) + ); + } + + $response = Favorites::add_item( $fav_id, $user_id ); + + if ( is_wp_error( $response ) || ! $response ) { + return rest_ensure_response( $this->prepare_error( $response ) ); + } + + return rest_ensure_response( Favorites::get_all( $user_id ) ); + } + + /** + * Add a favorite for current user. + * + * @param WP_REST_Request $request Request data. + * @return WP_REST_Response + */ + public function add_item_by_current_user( $request ) { + $current_user = get_current_user_id(); + + if ( ! $current_user ) { + return $this->prepare_error( + new \WP_Error( + 'woocommerce_favorites_unauthenticated', + __( 'You must be authenticated to use this endpoint', 'woocommerce-admin' ) + ) + ); + } + + $request->set_param( 'user_id', get_current_user_id() ); + return $this->add_item( $request ); + } + + /** + * Delete a favorite. + * + * @param WP_REST_Request $request Request data. + * @return WP_REST_Response + */ + public function delete_item( $request ) { + $user_id = $request->get_param( 'user_id' ); + $fav_id = $request->get_param( 'item_id' ); + + $response = Favorites::remove_item( $fav_id, $user_id ); + + if ( is_wp_error( $response ) || ! $response ) { + return rest_ensure_response( $this->prepare_error( $response ) ); + } + + return rest_ensure_response( Favorites::get_all( $user_id ) ); + } + + /** + * Delete a favorite for current user. + * + * @param WP_REST_Request $request Request data. + * @return WP_REST_Response + */ + public function delete_item_by_current_user( $request ) { + $current_user = get_current_user_id(); + + if ( ! $current_user ) { + return $this->prepare_error( + new \WP_Error( + 'woocommerce_favorites_unauthenticated', + __( 'You must be authenticated to use this endpoint', 'woocommerce-admin' ) + ) + ); + } + + $request->set_param( 'user_id', $current_user ); + + return $this->delete_item( $request ); + } + + + /** + * Check whether a given request has permission to create favorites. + * + * @param WP_REST_Request $request Full details about the request. + * @return WP_Error|boolean + */ + public function add_item_permissions_check( $request ) { + return current_user_can( 'edit_users' ); + } + + /** + * Check whether a given request has permission to delete notes. + * + * @param WP_REST_Request $request Full details about the request. + * @return WP_Error|boolean + */ + public function delete_item_permissions_check( $request ) { + return current_user_can( 'edit_users' ); + } + + /** + * Always allow for operations that only impact current user + * + * @param WP_REST_Request $request Full details about the request. + * @return WP_Error|boolean + */ + public function current_user_permissions_check( $request ) { + return true; + } + + /** + * Accept an instance of WP_Error and add the appropriate data for REST transit. + * + * @param WP_Error $error Error to prepare. + * @return WP_Error + */ + protected function prepare_error( $error ) { + if ( ! is_wp_error( $error ) ) { + return $error; + } + + $error->add_data( + array( + 'status' => $this->error_to_status_map[ $error->get_error_code() ] ?? 500, + ) + ); + + return $error; + } + +} diff --git a/plugins/woocommerce-admin/src/Features/Navigation/Favorites.php b/plugins/woocommerce-admin/src/Features/Navigation/Favorites.php index 7808f78c740..7e6f247d9f7 100644 --- a/plugins/woocommerce-admin/src/Features/Navigation/Favorites.php +++ b/plugins/woocommerce-admin/src/Features/Navigation/Favorites.php @@ -46,23 +46,32 @@ class Favorites { * * @param string $item_id Identifier of item to add. * @param string|number $user_id Identifier of user to add to. + * @return WP_Error|Boolean Throws exception if item already exists. */ public static function add_item( $item_id, $user_id = null ) { - $user = $user_id ? $user_id : get_current_user_id(); + $user = $user_id ?? get_current_user_id(); if ( ! $user || ! $item_id ) { - return; + return new \WP_Error( + 'woocommerce_favorites_invalid_request', + __( 'Sorry, invalid request', 'woocommerce-admin' ) + ); } $all_favorites = self::get_all( $user ); if ( in_array( $item_id, $all_favorites, true ) ) { - return; + return new \WP_Error( + 'woocommerce_favorites_already_exists', + __( 'Favorite already exists', 'woocommerce-admin' ) + ); } $all_favorites[] = $item_id; self::set_meta_value( $user, $all_favorites ); + + return true; } /** @@ -70,35 +79,48 @@ class Favorites { * * @param string $item_id Identifier of item to remove. * @param string|number $user_id Identifier of user to remove from. + * @return \WP_Error|Boolean Throws exception if item does not exist. */ public static function remove_item( $item_id, $user_id = null ) { - $user = $user_id ? $user_id : get_current_user_id(); + $user = $user_id ?? get_current_user_id(); if ( ! $user || ! $item_id ) { - return; + return new \WP_Error( + 'woocommerce_favorites_invalid_request', + __( 'Sorry, invalid request', 'woocommerce-admin' ) + ); } $all_favorites = self::get_all( $user ); if ( ! in_array( $item_id, $all_favorites, true ) ) { - return; + return new \WP_Error( + 'woocommerce_favorites_does_not_exist', + __( 'Favorite item not found', 'woocommerce-admin' ) + ); } - $remaining = array_diff( $all_favorites, [ $item_id ] ); + $remaining = array_values( array_diff( $all_favorites, [ $item_id ] ) ); - self::set_meta_value( $user, array_values( $remaining ) ); + self::set_meta_value( $user, $remaining ); + + return true; } /** * Get all registered favorites. * * @param string|number $user_id Identifier of user to query. + * @return WP_Error|Array */ public static function get_all( $user_id = null ) { - $user = $user_id ? $user_id : get_current_user_id(); + $user = $user_id ?? get_current_user_id(); if ( ! $user ) { - return; + return new \WP_Error( + 'woocommerce_favorites_invalid_request', + __( 'Sorry, invalid request', 'woocommerce-admin' ) + ); } $response = Loader::get_user_data_field( $user, self::META_NAME );