From 70e9d283b6448825df65b211cc62a47234fa32d8 Mon Sep 17 00:00:00 2001 From: Paul Kilmurray Date: Sat, 1 Jul 2023 20:52:58 +0100 Subject: [PATCH] Add login template and refactor --- includes/API.php | 88 ++++- includes/API/{ => Abstracts}/Controller.php | 10 +- .../API/Abstracts/WC_Rest_API_Modifier.php | 6 +- includes/API/Auth.php | 241 +++---------- includes/API/Customers.php | 2 +- includes/API/Orders.php | 2 +- includes/API/Product_Variations.php | 2 +- includes/API/Products.php | 2 +- includes/API/Settings.php | 314 ++--------------- includes/API/Settings_Controller.php | 74 ---- includes/API/Stores.php | 2 +- includes/Admin/Settings.php | 49 ++- includes/Gateways.php | 6 +- includes/Services/Auth.php | 206 +++++++++++ includes/Services/README.md | 3 + includes/Services/Settings.php | 319 ++++++++++++++++++ includes/Services/Stores.php | 7 + includes/Templates/Frontend.php | 66 ++-- includes/wcpos-functions.php | 28 +- packages/analytics/package.json | 8 +- packages/eslint/package.json | 2 +- packages/settings/package.json | 8 +- .../src/screens/general/barcode-select.tsx | 32 +- yarn.lock | 252 +++++++------- 24 files changed, 905 insertions(+), 824 deletions(-) rename includes/API/{ => Abstracts}/Controller.php (88%) delete mode 100644 includes/API/Settings_Controller.php create mode 100644 includes/Services/Auth.php create mode 100644 includes/Services/README.md create mode 100644 includes/Services/Settings.php create mode 100644 includes/Services/Stores.php diff --git a/includes/API.php b/includes/API.php index 4d9a46c..fb63901 100644 --- a/includes/API.php +++ b/includes/API.php @@ -10,6 +10,7 @@ namespace WCPOS\WooCommercePOS; +use WCPOS\WooCommercePOS\Services\Auth; use WP_HTTP_Response; use WP_REST_Request; use WP_REST_Response; @@ -23,10 +24,26 @@ class API { * @var array */ protected $controllers = array(); - private $wc_rest_api_handler; + /** + * @var + */ + protected $wc_rest_api_handler; + + /** + * @var + */ + protected $auth_service; + + /** + * @var bool + */ + protected $is_auth_checked = false; + + + public function __construct() { + $this->auth_service = new Auth(); - public function __construct() { // Init and register routes for the WCPOS REST API $this->controllers = array( 'auth' => new API\Auth(), @@ -45,6 +62,7 @@ public function __construct() { // Adds authentication to for JWT bearer tokens add_filter( 'determine_current_user', array( $this, 'determine_current_user' ) ); + add_filter( 'rest_authentication_errors', array( $this, 'rest_authentication_errors' ), 50, 1 ); // Adds uuid for the WordPress install add_filter( 'rest_index', array( $this, 'rest_index' ), 10, 1 ); @@ -107,27 +125,62 @@ public function rest_pre_serve_request( bool $served, WP_HTTP_Response $result, * @return false|int|void */ public function determine_current_user( $user_id ) { + $this->is_auth_checked = true; if ( ! empty( $user_id ) ) { return $user_id; } - // extract Bearer token from Authorization Header - list($token) = sscanf( $this->get_auth_header(), 'Bearer %s' ); - - if ( $token ) { - $decoded_token = $this->controllers['auth']->validate_token( $token, false ); - - if ( empty( $decoded_token ) || is_wp_error( $decoded_token ) ) { - return $user_id; - } - $user = ! empty( $decoded_token->data->user->id ) ? $decoded_token->data->user->id : $user_id; - - return absint( $user ); - } - - return $user_id; + return $this->authenticate( $user_id ); } + /** + * It's possible that the determine_current_user filter above is not called + * https://github.com/woocommerce/woocommerce/issues/26847 + * + * We need to make sure our + */ + public function rest_authentication_errors( $errors ) { + // Pass through other errors + if ( ! empty( $error ) ) { + return $error; + } + + // check if determine_current_user has been called + if ( ! $this->is_auth_checked ) { + // Authentication hasn't occurred during `determine_current_user`, so check auth. + $user_id = $this->authenticate( false ); + if ( $user_id ) { + wp_set_current_user( $user_id ); + return true; + } + } + + return $errors; + } + + /** + * @param int|false $user_id User ID if one has been determined, false otherwise. + * + * @return integer + */ + private function authenticate( $user_id ) { + // extract Bearer token from Authorization Header + list($token) = sscanf( $this->get_auth_header(), 'Bearer %s' ); + + if ( $token ) { + $decoded_token = $this->auth_service->validate_token( $token ); + + if ( empty( $decoded_token ) || is_wp_error( $decoded_token ) ) { + return $user_id; + } + $user = ! empty( $decoded_token->data->user->id ) ? $decoded_token->data->user->id : $user_id; + + return absint( $user ); + } + + return $user_id; + } + /** * @return false|string */ @@ -147,6 +200,7 @@ public function get_auth_header() { * Add uuid to the WP REST API index. * * @param WP_REST_Response $response Response data + * * @return WP_REST_Response */ public function rest_index( WP_REST_Response $response ): WP_REST_Response { diff --git a/includes/API/Controller.php b/includes/API/Abstracts/Controller.php similarity index 88% rename from includes/API/Controller.php rename to includes/API/Abstracts/Controller.php index a01868f..6be5dde 100644 --- a/includes/API/Controller.php +++ b/includes/API/Abstracts/Controller.php @@ -12,13 +12,13 @@ * @see https://developer.wordpress.org/rest-api/extending-the-rest-api/controller-classes/ */ -namespace WCPOS\WooCommercePOS\API; - -use const WCPOS\WooCommercePOS\SHORT_NAME; +namespace WCPOS\WooCommercePOS\API\Abstracts; use WP_REST_Controller; +use function defined; +use const WCPOS\WooCommercePOS\SHORT_NAME; -if ( ! \defined( 'ABSPATH' ) ) { +if ( ! defined( 'ABSPATH' ) ) { exit; } @@ -26,8 +26,6 @@ * Abstract Rest Controller Class. * * @extends WP_REST_Controller - * - * @version 2.6.0 */ abstract class Controller extends WP_REST_Controller { /** diff --git a/includes/API/Abstracts/WC_Rest_API_Modifier.php b/includes/API/Abstracts/WC_Rest_API_Modifier.php index bd3ab48..81e200a 100644 --- a/includes/API/Abstracts/WC_Rest_API_Modifier.php +++ b/includes/API/Abstracts/WC_Rest_API_Modifier.php @@ -55,13 +55,13 @@ protected function parse_meta_data( WC_Data $object ): array { * This is just a helper function to try and alert us to these large responses * * @param WP_REST_Response $response - * @param WC_Data $object + * @param int $id */ - protected function log_large_rest_response( WP_REST_Response $response, WC_Data $object ) { + protected function log_large_rest_response( WP_REST_Response $response, int $id ) { $response_size = strlen( serialize( $response->data ) ); $max_response_size = 100000; if ( $response_size > $max_response_size ) { - Logger::log( "Object ID {$object->get_id()} has a response size of {$response_size} bytes, exceeding the limit of {$max_response_size} bytes." ); + Logger::log( "ID {$id} has a response size of {$response_size} bytes, exceeding the limit of {$max_response_size} bytes." ); } } } diff --git a/includes/API/Auth.php b/includes/API/Auth.php index 384fb46..822d46a 100644 --- a/includes/API/Auth.php +++ b/includes/API/Auth.php @@ -10,18 +10,15 @@ namespace WCPOS\WooCommercePOS\API; -use Exception; -use Firebase\JWT\JWT as FirebaseJWT; -use Firebase\JWT\Key as FirebaseKey; -use Ramsey\Uuid\Uuid; use WP_Error; -use WP_HTTP_Response; use WP_REST_Request; use WP_REST_Response; use WP_REST_Server; -use WP_User; +use function is_wp_error; +use function rest_ensure_response; +use function wp_authenticate; -class Auth extends Controller { +class Auth extends Abstracts\Controller { /** * Route base. * @@ -29,102 +26,36 @@ class Auth extends Controller { */ protected $rest_base = 'jwt'; + /** + * + */ + protected $auth_service; + /** * Stores constructor. */ public function __construct() { + $this->auth_service = new \WCPOS\WooCommercePOS\Services\Auth(); } - /** - * Validate JWT Token. - * - * @TODO - flesh out this API, see https://github.com/wp-graphql/wp-graphql-jwt-authentication for inspiration - * @TODO - validate, refresh and revoke should be GET with auth header tokens - * - * @param null $token - * @param bool $output - * - * @return array|object|WP_Error - */ - public function validate_token( $token = null, $output = true ) { - try { - $decoded_token = FirebaseJWT::decode( $token, new FirebaseKey( $this->get_secret_key(), 'HS256' ) ); - // The Token is decoded now validate the iss - if ( get_bloginfo( 'url' ) != $decoded_token->iss ) { - // The iss do not match, return error - return new WP_Error( - 'jwt_auth_bad_iss', - 'The iss do not match with this server', - array( 'status' => 403 ) - ); - } - // So far so good, validate the user id in the token - if ( ! isset( $decoded_token->data->user->id ) ) { - // No user id in the token, abort!! - return new WP_Error( - 'jwt_auth_bad_request', - 'User ID not found in the token', - array( - 'status' => 403, - ) - ); - } - /** Everything looks good return the decoded token if the $output is false */ - if ( ! $output ) { - return $decoded_token; - } - - // If the output is true return an answer to the request to show it - return array( - 'code' => 'jwt_auth_valid_token', - 'data' => array( - 'status' => 200, - ), - ); - } catch ( Exception $e ) { - // Something is wrong trying to decode the token, send back the error - return new WP_Error( - 'jwt_auth_invalid_token', - $e->getMessage(), - array( - 'status' => 403, - ) - ); - } - } - - /** - * - * @return string - */ - public function get_secret_key(): string { - $secret_key = get_option( 'woocommerce_pos_secret_key' ); - if ( false === $secret_key || empty( $secret_key ) ) { - $secret_key = wp_generate_password( 64, true, true ); - update_option( 'woocommerce_pos_secret_key', $secret_key ); - } - return $secret_key; - } - - /** * */ public function register_routes(): void { - // Validate JWT token - register_rest_route( - $this->namespace, - '/' . $this->rest_base . '/authorize', - array( - 'methods' => WP_REST_Server::READABLE, - 'callback' => array( $this, 'generate_token' ), - 'permission_callback' => function ( WP_REST_Request $request ) { - $authorization = $request->get_header( 'authorization' ); - - return ! \is_null( $authorization ); - }, - ) - ); + // Generate JWT token +// register_rest_route( +// $this->namespace, +// '/' . $this->rest_base . '/authorize', +// array( +// 'methods' => WP_REST_Server::READABLE, +// 'callback' => array( $this, 'generate_token' ), +// 'permission_callback' => function ( WP_REST_Request $request ) { +// $authorization = $request->get_header( 'authorization' ); +// +// return ! is_null( $authorization ); +// }, +// ) +// ); // Validate JWT token register_rest_route( @@ -181,27 +112,16 @@ public function register_routes(): void { /** * Get the user and password in the request body and generate a JWT. + * @NOTE - not allowing REST Auth at the moment * * @param WP_REST_Request $request - * - * @return WP_Error|WP_HTTP_Response|WP_REST_Response + * @return WP_Error|WP_REST_Response */ public function generate_token( WP_REST_Request $request ) { $token = str_replace( 'Basic ', '', $request->get_header( 'authorization' ) ); $decoded = base64_decode( $token, true ); list($username, $password) = explode( ':', $decoded ); - // First thing, check the secret key if not exist return a error - if ( ! $this->get_secret_key() ) { - return new WP_Error( - '[woocommerce_pos] jwt_auth_bad_config', - __( 'JWT is not configured properly, please contact the admin', 'woocommerce-pos' ), - array( - 'status' => 403, - ) - ); - } - /** Try to authenticate the user with the passed credentials*/ $user = wp_authenticate( $username, $password ); @@ -210,7 +130,7 @@ public function generate_token( WP_REST_Request $request ) { $error_code = $user->get_error_code(); return new WP_Error( - '[woocommerce_pos] ' . $error_code, + 'woocommerce_pos_' . $error_code, $user->get_error_message( $error_code ), array( 'status' => 403, @@ -218,69 +138,7 @@ public function generate_token( WP_REST_Request $request ) { ); } - /** - * @TODO - * I'm not sure if this is the best way, but I need the user to be logged in for subsequent processing - */ - wp_set_current_user( $user->ID ); - - /** Valid credentials, the user exists create the according Token */ - $issued_at = time(); - - /** - * Filters the JWT issued at time. - * - * @param {string} $issued_at - * - * @returns {string} Issued at time - * - * @since 1.0.0 - * - * @hook woocommerce_pos_jwt_auth_not_before - */ - $not_before = apply_filters( 'woocommerce_pos_jwt_auth_not_before', $issued_at, $issued_at ); - /** - * Filters the JWT expire time. - * - * @param {string} $issued_at - * - * @returns {string} Expire time - * - * @since 1.0.0 - * - * @hook woocommerce_pos_jwt_auth_expire - */ - $expire = apply_filters( 'woocommerce_pos_jwt_auth_expire', $issued_at + ( DAY_IN_SECONDS * 7 ), $issued_at ); - - $token = array( - 'iss' => get_bloginfo( 'url' ), - 'iat' => $issued_at, - 'nbf' => $not_before, - 'exp' => $expire, - 'data' => array( - 'user' => array( - 'id' => $user->data->ID, - ), - ), - ); - - /** - * Let the user modify the token data before the sign. - * - * @param {string} $token - * @param {WP_User} $user - * - * @returns {string} Token - * - * @since 1.0.0 - * - * @hook woocommerce_pos_jwt_auth_token_before_sign - */ - $token = FirebaseJWT::encode( apply_filters( 'woocommerce_pos_jwt_auth_token_before_sign', $token, $user ), $this->get_secret_key(), 'HS256' ); - - /** The token is signed, now create the object with no sensible user data to the client*/ - $data = $this->get_user_data( $user ); - $data['jwt'] = $token; + $data = $this->auth_service->generate_token( $user ); /** * Let the user modify the data before sending it back @@ -299,6 +157,18 @@ public function generate_token( WP_REST_Request $request ) { return rest_ensure_response( $data ); } + /** + * Validate JWT Token. + * + * @param WP_REST_Request $request + * @return WP_REST_Response + */ + public function validate_token( WP_REST_Request $request ): WP_REST_Response { + $token = $request->get_param( 'jwt' ); + $result = $this->auth_service->validate_token( $token ); + return rest_ensure_response( $result ); + } + /** * Refresh JWT Token. */ @@ -310,35 +180,4 @@ public function refresh_token(): void { */ public function revoke_token(): void { } - - /** - * - * @param WP_User $user - * - * @return array - */ - private function get_user_data( WP_User $user ): array { - $uuid = get_user_meta( $user->ID, '_woocommerce_pos_uuid', true ); - if ( ! $uuid ) { - $uuid = Uuid::uuid4()->toString(); - update_user_meta( $user->ID, '_woocommerce_pos_uuid', $uuid ); - } - - $store_settings = new Stores(); - - $data = array( - 'uuid' => $uuid, - 'id' => $user->ID, - 'username' => $user->user_login, - 'email' => $user->user_email, - 'first_name' => $user->user_firstname, - 'last_name' => $user->user_lastname, - 'nice_name' => $user->user_nicename, - 'display_name' => $user->display_name, - 'avatar_url' => get_avatar_url( $user->ID ), - 'stores' => $store_settings->get_stores(), - ); - - return $data; - } } diff --git a/includes/API/Customers.php b/includes/API/Customers.php index 498eb2b..61de913 100644 --- a/includes/API/Customers.php +++ b/includes/API/Customers.php @@ -120,7 +120,7 @@ public function customer_response( WP_REST_Response $response, WP_User $user_dat // Set any changes to the response data $response->set_data( $data ); - $this->log_large_rest_response( $response, $product ); + // $this->log_large_rest_response( $response, $user_data->ID ); return $response; } diff --git a/includes/API/Orders.php b/includes/API/Orders.php index a159ee2..49028c2 100644 --- a/includes/API/Orders.php +++ b/includes/API/Orders.php @@ -243,7 +243,7 @@ public function order_response( WP_REST_Response $response, WC_Order $order, WP_ $data['meta_data'] = $this->parse_meta_data( $order ); $response->set_data( $data ); - $this->log_large_rest_response( $response, $order ); + // $this->log_large_rest_response( $response, $order->get_id() ); return $response; } diff --git a/includes/API/Product_Variations.php b/includes/API/Product_Variations.php index c73283b..42ac4d1 100644 --- a/includes/API/Product_Variations.php +++ b/includes/API/Product_Variations.php @@ -56,7 +56,7 @@ public function product_response( WP_REST_Response $response, WC_Data $product, $data['meta_data'] = $this->parse_meta_data( $product ); $response->set_data( $data ); - $this->log_large_rest_response( $response, $product ); + // $this->log_large_rest_response( $response, $product->get_id() ); return $response; } diff --git a/includes/API/Products.php b/includes/API/Products.php index aced384..0ae5b89 100644 --- a/includes/API/Products.php +++ b/includes/API/Products.php @@ -194,7 +194,7 @@ public function product_response( WP_REST_Response $response, WC_Product $produc // Set any changes to the response data $response->set_data( $data ); - $this->log_large_rest_response( $response, $product ); + // $this->log_large_rest_response( $response, $product->get_id() ); return $response; } diff --git a/includes/API/Settings.php b/includes/API/Settings.php index 3862a32..95a89bd 100644 --- a/includes/API/Settings.php +++ b/includes/API/Settings.php @@ -3,7 +3,6 @@ namespace WCPOS\WooCommercePOS\API; use Closure; -use WC_Payment_Gateways; use WP_Error; use WP_REST_Request; use WP_REST_Server; @@ -13,74 +12,9 @@ use function is_integer; use function is_string; -class Settings extends Settings_Controller { +class Settings extends Abstracts\Controller { - /** - * Prefix for the $wpdb->options table. - * - * @var string - */ - protected static $db_prefix = 'woocommerce_pos_settings_'; - - protected static $default_settings = array( - 'general' => array( - 'pos_only_products' => false, - 'decimal_qty' => false, - 'force_ssl' => true, - 'default_customer' => 0, - 'default_customer_is_cashier' => false, - 'barcode_field' => '_sku', - 'generate_username' => true, - ), - 'checkout' => array( - 'order_status' => 'wc-completed', - 'admin_emails' => true, - 'customer_emails' => true, - ), - 'payment_gateways' => array( - 'default_gateway' => 'pos_cash', - 'gateways' => array( - 'pos_cash' => array( - 'order' => 0, - 'enabled' => true, - ), - 'pos_card' => array( - 'order' => 1, - 'enabled' => true, - ), - ), - ), - 'license' => array(), - ); - /** - * @var array - */ - private static $caps = array( - 'wcpos' => array( - 'access_woocommerce_pos', // pos frontend - 'manage_woocommerce_pos', // pos admin - ), - 'wc' => array( - 'create_users', - 'edit_others_products', - 'edit_others_shop_orders', - 'edit_product', - 'edit_published_products', - 'edit_shop_orders', - 'edit_users', - 'list_users', - 'manage_product_terms', - 'publish_shop_orders', - 'read_private_products', - 'read_private_shop_coupons', - 'read_private_shop_orders', - ), - 'wp' => array( - 'read', // wp-admin access - ), - ); - /** * Route base. * @@ -88,13 +22,27 @@ class Settings extends Settings_Controller { */ protected $rest_base = 'settings'; + /** + * + */ + protected $settings_service; + /** * Settings constructor. */ public function __construct() { + $this->settings_service = new \WCPOS\WooCommercePOS\Services\Settings(); add_filter( 'option_woocommerce_pos_settings_payment_gateways', array( $this, 'payment_gateways_settings' ) ); } + /** + * BACKWARD COMPATIBILITY: Remove this method in the future + */ +// public function get_settings( $id ) { +// $settings = $this->settings_service->get_settings( $id ); +// return $settings; +// } + /** * @return void */ @@ -304,142 +252,16 @@ public function get_checkout_endpoint_args(): array { ); } - /** - * - */ - public function get_license_settings() { - $settings = array(); - - if ( class_exists( '\WCPOS\WooCommercePOSPro' ) ) { - $settings = get_option( self::$db_prefix . 'license', array() ); - } - - $license_settings = array_replace_recursive( - self::$default_settings['license'], - $settings - ); - - /** - * Filters the license settings. - * - * @param {array} $settings - * - * @returns {array} $settings - * - * @since 1.0.0 - * - * @hook woocommerce_pos_license_settings - */ - return apply_filters( 'woocommerce_pos_license_settings', $license_settings ); - } - - /** - * @return array - */ - public function get_barcodes( WP_REST_Request $request ): array { - global $wpdb; - - // Get 'search' parameter from request - $search = $request->get_param( 'search' ); - - // maybe add custom barcode field - $custom_field = woocommerce_pos_get_settings( 'general', 'barcode_field' ); - - // Prepare the basic query - $result = $wpdb->get_col( - " - SELECT DISTINCT(pm.meta_key) - FROM $wpdb->postmeta AS pm - JOIN $wpdb->posts AS p - ON p.ID = pm.post_id - WHERE p.post_type IN ('product', 'product_variation') - ORDER BY pm.meta_key - " - ); - - if ( ! empty( $custom_field ) ) { - $result[] = $custom_field; - } - - // Filter the results - if ( ! empty( $search ) ) { - $result = array_filter( $result, function( $item ) use ( $search ) { - return stripos( $item, $search ) !== false; - }); - } - - sort( $result ); - - return array_unique( $result ); - } - - /** - * @return array - */ - public function get_order_statuses() { - $order_statuses = wc_get_order_statuses(); - $order_statuses = array_map( 'wc_get_order_status_name', $order_statuses ); - - return $order_statuses; - } - - /** - * - */ - public function get_payment_gateways_settings() { - // Note: I need to re-init the gateways here to pass the tests, but it seems to work fine in the app. - WC_Payment_Gateways::instance()->init(); - $installed_gateways = WC_Payment_Gateways::instance()->payment_gateways(); - $gateways_settings = array_replace_recursive( - self::$default_settings['payment_gateways'], - get_option( self::$db_prefix . 'payment_gateways', array() ) - ); - // NOTE - gateways can be installed and uninstalled, so we need to assume the settings data is stale - $response = array( - 'default_gateway' => $gateways_settings['default_gateway'], - 'gateways' => array(), - ); - - // loop through installed gateways and merge with saved settings - foreach ( $installed_gateways as $id => $gateway ) { - // sanity check for gateway class - if ( ! is_a( $gateway, 'WC_Payment_Gateway' ) || 'pre_install_woocommerce_payments_promotion' === $id ) { - continue; - } - $response['gateways'][ $id ] = array_replace_recursive( - array( - 'id' => $gateway->id, - 'title' => $gateway->title, - 'description' => $gateway->description, - 'enabled' => false, - 'order' => 999, - ), - isset( $gateways_settings['gateways'][ $id ] ) ? $gateways_settings['gateways'][ $id ] : array() - ); - } - - /** - * Filters the payment gateways settings. - * - * @param {array} $settings - * - * @returns {array} $settings - * - * @since 1.0.0 - * - * @hook woocommerce_pos_payment_gateways_settings - */ - return apply_filters( 'woocommerce_pos_payment_gateways_settings', $response ); - } /** * @param WP_REST_Request $request * @return array|WP_Error */ public function update_payment_gateways_settings( WP_REST_Request $request ) { - $settings = array_replace_recursive( $this->get_payment_gateways_settings(), $request->get_json_params() ); - return $this->save_settings( 'payment_gateways', $settings ); + $old_settings = $this->settings_service->get_payment_gateways_settings(); + $updated_settings = array_replace_recursive( $old_settings, $request->get_json_params() ); + return $this->settings_service->save_settings( 'payment_gateways', $updated_settings ); } /** @@ -451,31 +273,10 @@ public function update_payment_gateways_settings( WP_REST_Request $request ) { */ public function update_general_settings( WP_REST_Request $request ) { $settings = array_replace_recursive( $this->get_general_settings(), $request->get_json_params() ); - return $this->save_settings( 'general', $settings ); + return $this->settings_service->save_settings( 'general', $settings ); } - /** - * @return array - */ - public function get_general_settings(): array { - $general_settings = array_replace_recursive( - self::$default_settings['general'], - get_option( self::$db_prefix . 'general', array() ) - ); - /** - * Filters the general settings. - * - * @param {array} $settings - * - * @returns {array} $settings - * - * @since 1.0.0 - * - * @hook woocommerce_pos_general_settings - */ - return apply_filters( 'woocommerce_pos_general_settings', $general_settings ); - } /** * @param WP_REST_Request $request @@ -484,38 +285,17 @@ public function get_general_settings(): array { */ public function update_checkout_settings( WP_REST_Request $request ) { $settings = array_replace_recursive( $this->get_checkout_settings(), $request->get_json_params() ); - return $this->save_settings( 'checkout', $settings ); + return $this->settings_service->save_settings( 'checkout', $settings ); } - /** - * @return array - */ - public function get_checkout_settings(): array { - $checkout_settings = array_replace_recursive( - self::$default_settings['checkout'], - get_option( self::$db_prefix . 'checkout', array() ) - ); - /** - * Filters the checkout settings. - * - * @param {array} $settings - * - * @returns {array} $settings - * - * @since 1.0.0 - * - * @hook woocommerce_pos_checkout_settings - */ - return apply_filters( 'woocommerce_pos_checkout_settings', $checkout_settings ); - } /** * @param WP_REST_Request $request * - * @return array|WP_Error + * @return array */ - public function update_access_settings( WP_REST_Request $request ) { + public function update_access_settings( WP_REST_Request $request ): array { global $wp_roles; $data = $request->get_json_params(); @@ -551,59 +331,17 @@ public function update_access_settings( WP_REST_Request $request ) { } } - return $this->get_access_settings(); + return $this->settings_service->get_access_settings(); } - /** - * - */ - public function get_access_settings(): array { - global $wp_roles; - $role_caps = array(); - - $roles = $wp_roles->roles; - if ( $roles ) { - foreach ( $roles as $slug => $role ) { - $role_caps[ $slug ] = array( - 'name' => $role['name'], - 'capabilities' => array( - 'wcpos' => array_intersect_key( - array_merge( array_fill_keys( self::$caps['wcpos'], false ), $role['capabilities'] ), - array_flip( self::$caps['wcpos'] ) - ), - 'wc' => array_intersect_key( - array_merge( array_fill_keys( self::$caps['wc'], false ), $role['capabilities'] ), - array_flip( self::$caps['wc'] ) - ), - 'wp' => array_intersect_key( - array_merge( array_fill_keys( self::$caps['wp'], false ), $role['capabilities'] ), - array_flip( self::$caps['wp'] ) - ), - ), - ); - } - } - /** - * Filters the access settings. - * - * @param {array} $settings - * - * @returns {array} $settings - * - * @since 1.0.0 - * - * @hook woocommerce_pos_access_settings - */ - return apply_filters( 'woocommerce_pos_access_settings', $role_caps ); - } /** * @TODO - who can read settings? * * @return bool */ - public function read_permission_check() { + public function read_permission_check(): bool { // return current_user_can( 'manage_woocommerce_pos' ); return true; } @@ -611,14 +349,14 @@ public function read_permission_check() { /** * @return bool */ - public function update_permission_check() { + public function update_permission_check(): bool { return current_user_can( 'manage_woocommerce_pos' ); } /** * @return bool */ - public function access_permission_check() { + public function access_permission_check(): bool { return current_user_can( 'promote_users' ); } diff --git a/includes/API/Settings_Controller.php b/includes/API/Settings_Controller.php deleted file mode 100644 index 5cfeae7..0000000 --- a/includes/API/Settings_Controller.php +++ /dev/null @@ -1,74 +0,0 @@ - - * - * @class WCPOS_REST_Controller - * - * @see https://developer.wordpress.org/rest-api/extending-the-rest-api/controller-classes/ - */ - -namespace WCPOS\WooCommercePOS\API; - -use WP_Error; - -abstract class Settings_Controller extends Controller { - /** - * Prefix for the $wpdb->options table. - * Empty here because I extend this Controller in the Pro plugin - * - * @var string - */ - protected static $db_prefix = ''; - protected static $default_settings = array(); - - /** - * @param string $id - * @return array|mixed|WP_Error|null - */ - public function get_settings( string $id ) { - $method_name = 'get_' . $id . '_settings'; - - if ( method_exists( $this, $method_name ) ) { - return $this->$method_name(); - } - - return new WP_Error( - 'woocommerce_pos_settings_error', - /* translators: %s: Settings group id, ie: 'general' or 'checkout' */ - sprintf( __( 'Settings with id %s not found', 'woocommerce-pos' ), $id ), - array( 'status' => 400 ) - ); - } - - /** - * @param string $id - * @param array $settings - * @return array|mixed|WP_Error|null - */ - protected function save_settings( string $id, array $settings ) { - $success = update_option( - static::$db_prefix . $id, - array_merge( - $settings, - array( 'date_modified_gmt' => current_time( 'mysql', true ) ) - ), - false - ); - - if ( $success ) { - return $this->get_settings( $id ); - } - - return new WP_Error( - 'woocommerce_pos_settings_error', - /* translators: %s: Settings group id, ie: 'general' or 'checkout' */ - sprintf( __( 'Can not save settings with id %s', 'woocommerce-pos' ), $id ), - array( 'status' => 400 ) - ); - } -} diff --git a/includes/API/Stores.php b/includes/API/Stores.php index ef06907..1d539c4 100644 --- a/includes/API/Stores.php +++ b/includes/API/Stores.php @@ -4,7 +4,7 @@ use WC_Admin_Settings; -class Stores extends Controller { +class Stores extends Abstracts\Controller { /** * Stores constructor. */ diff --git a/includes/Admin/Settings.php b/includes/Admin/Settings.php index 3c11405..f3627f5 100644 --- a/includes/Admin/Settings.php +++ b/includes/Admin/Settings.php @@ -16,7 +16,13 @@ class Settings { + /** + * @var + */ + protected $settings_service; + public function __construct() { + $this->settings_service = new \WCPOS\WooCommercePOS\Services\Settings(); add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_assets' ) ); } @@ -55,30 +61,6 @@ public function enqueue_assets() { VERSION ); - - /** - * unpkg.com is unreliable, so we're packaging these dependencies - */ -// wp_enqueue_script( -// PLUGIN_NAME . '-react-query', -// 'https://unpkg.com/@tanstack/react-query@4/build/umd/index.production.js', -// array( -// 'react', -// 'react-dom', -// ), -// VERSION -// ); -// -// wp_enqueue_script( -// PLUGIN_NAME . '-react-beautiful-dnd', -// 'https://unpkg.com/react-beautiful-dnd@13.1.1/dist/react-beautiful-dnd.js', -// array( -// 'react', -// 'react-dom', -// ), -// VERSION -// ); - wp_enqueue_script( PLUGIN_NAME . '-transifex', 'https://cdn.jsdelivr.net/npm/@transifex/native/dist/browser.native.min.js', @@ -103,7 +85,10 @@ public function enqueue_assets() { true ); - if ( $is_development ) { + // Add inline script + wp_add_inline_script( PLUGIN_NAME . '-settings', $this->inline_script(), 'before' ); + + if ( $is_development ) { wp_enqueue_script( 'webpack-live-reload', 'http://localhost:35729/livereload.js', @@ -116,13 +101,25 @@ public function enqueue_assets() { do_action( 'woocommerce_pos_admin_settings_enqueue_assets' ); } + /** + * @return string + */ + private function inline_script(): string { + $barcodes = array_values( $this->settings_service->get_barcodes() ); + $order_statuses = $this->settings_service->get_order_statuses(); + return sprintf( 'var wcpos = wcpos || {}; wcpos.settings = { + barcodes: %s, + order_statuses: %s + }', json_encode( $barcodes ), json_encode( $order_statuses ) ); + } + /** * Delete settings in WP options table * * @param $id * @return bool */ - public static function delete_settings($id ) { + public static function delete_settings( $id ): bool { return delete_option( 'woocommerce_pos_' . $id ); } diff --git a/includes/Gateways.php b/includes/Gateways.php index d9cd1b2..e24e5ff 100644 --- a/includes/Gateways.php +++ b/includes/Gateways.php @@ -10,8 +10,6 @@ namespace WCPOS\WooCommercePOS; -use WCPOS\WooCommercePOS\API\Settings; - class Gateways { @@ -62,8 +60,8 @@ public function available_payment_gateways( ?array $gateways ): array { } // use POS settings - $api = new Settings(); - $settings = $api->get_payment_gateways_settings(); + $settings_service = new Services\Settings(); + $settings = $settings_service->get_payment_gateways_settings(); // Get all payment gateways $all_gateways = WC()->payment_gateways->payment_gateways; diff --git a/includes/Services/Auth.php b/includes/Services/Auth.php new file mode 100644 index 0000000..d654d4f --- /dev/null +++ b/includes/Services/Auth.php @@ -0,0 +1,206 @@ +stores_service = new Stores(); + } + + /** + * Generate a secret key if it doesn't exist, or return the existing one + * + * @return string + */ + public function get_secret_key(): string { + $secret_key = get_option( 'woocommerce_pos_secret_key' ); + if ( false === $secret_key || empty( $secret_key ) ) { + $secret_key = wp_generate_password( 64, true, true ); + update_option( 'woocommerce_pos_secret_key', $secret_key ); + } + return $secret_key; + } + + /** + * Validate the provided JWT token + * + * @param null $token + * + * @return object|WP_Error + */ + public function validate_token( $token = null ) { + try { + $decoded_token = FirebaseJWT::decode( $token, new FirebaseKey( $this->get_secret_key(), 'HS256' ) ); + + // The Token is decoded now validate the iss + if ( get_bloginfo( 'url' ) != $decoded_token->iss ) { + // The iss do not match, return error + return new WP_Error( + 'woocommmerce_pos_auth_bad_iss', + 'The iss do not match with this server', + array( 'status' => 403 ) + ); + } + + // So far so good, validate the user id in the token + if ( ! isset( $decoded_token->data->user->id ) ) { + // No user id in the token, abort!! + return new WP_Error( + 'woocommmerce_pos_auth_bad_request', + 'User ID not found in the token', + array( + 'status' => 403, + ) + ); + } + + /** Everything looks good return the decoded token */ + return $decoded_token; + } catch ( Exception $e ) { + // Something is wrong trying to decode the token, send back the error + return new WP_Error( + 'woocommmerce_pos_auth_invalid_token', + $e->getMessage(), + array( + 'status' => 403, + ) + ); + } + } + + /** + * Generate a JWT token for the provided user + * + * @param WP_User $user + * + * @return string|WP_Error + */ + /** + * Generate a JWT token for the provided user + * + * @param WP_User $user + * + * @return string|WP_Error + */ + public function generate_token( WP_User $user ) { + // First thing, check the secret key if not exist return a error + if ( ! $this->get_secret_key() ) { + return new WP_Error( + 'woocommerce_pos_jwt_auth_bad_config', + __( 'JWT is not configured properly, please contact the admin', 'woocommerce-pos' ), + array( + 'status' => 403, + ) + ); + } + + /** Valid credentials, the user exists create the according Token */ + $issued_at = time(); + + /** + * Filters the JWT issued at time. + * + * @param {string} $issued_at + * @returns {string} Issued at time + * @since 1.0.0 + * @hook woocommerce_pos_jwt_auth_not_before + */ + $not_before = apply_filters( 'woocommerce_pos_jwt_auth_not_before', $issued_at, $issued_at ); + + /** + * Filters the JWT expire time. + * + * @param {string} $issued_at + * @returns {string} Expire time + * @since 1.0.0 + * @hook woocommerce_pos_jwt_auth_expire + */ + $expire = apply_filters( 'woocommerce_pos_jwt_auth_expire', $issued_at + ( DAY_IN_SECONDS * 7 ), $issued_at ); + + $token = array( + 'iss' => get_bloginfo( 'url' ), + 'iat' => $issued_at, + 'nbf' => $not_before, + 'exp' => $expire, + 'data' => array( + 'user' => array( + 'id' => $user->data->ID, + ), + ), + ); + + /** + * Let the user modify the token data before the sign. + * + * @param {string} $token + * @param {WP_User} $user + * @returns {string} Token + * @since 1.0.0 + * @hook woocommerce_pos_jwt_auth_token_before_sign + */ + $token = FirebaseJWT::encode( apply_filters( 'woocommerce_pos_jwt_auth_token_before_sign', $token, $user ), $this->get_secret_key(), 'HS256' ); + + return $token; + } + + + /** + * Get user's data + * + * @param WP_User $user + * + * @return array + */ + public function get_user_data( WP_User $user ): array { + $uuid = get_user_meta( $user->ID, '_woocommerce_pos_uuid', true ); + if ( ! $uuid ) { + $uuid = Uuid::uuid4()->toString(); + update_user_meta( $user->ID, '_woocommerce_pos_uuid', $uuid ); + } + + $store_settings = new Stores(); + + $data = array( + 'uuid' => $uuid, + 'id' => $user->ID, + 'jwt' => $this->generate_token( $user ), + 'username' => $user->user_login, + 'email' => $user->user_email, + 'first_name' => $user->user_firstname, + 'last_name' => $user->user_lastname, + 'nice_name' => $user->user_nicename, + 'display_name' => $user->display_name, + 'avatar_url' => get_avatar_url( $user->ID ), + 'stores' => $this->stores_service->get_stores(), + ); + + return $data; + } + + /** + * Revoke JWT Token + */ + public function revoke_token(): void { + // Implementation + } + + /** + * Refresh JWT Token. + */ + public function refresh_token(): void { + // Implementation + } +} diff --git a/includes/Services/README.md b/includes/Services/README.md new file mode 100644 index 0000000..656e7d3 --- /dev/null +++ b/includes/Services/README.md @@ -0,0 +1,3 @@ +## Services + +Shared service classes, eg: between Frontend, Admin and API. Also note that the Pro plugin may use these Services. \ No newline at end of file diff --git a/includes/Services/Settings.php b/includes/Services/Settings.php new file mode 100644 index 0000000..cf16b44 --- /dev/null +++ b/includes/Services/Settings.php @@ -0,0 +1,319 @@ +options table. + * + * @var string + */ + protected static $db_prefix = 'woocommerce_pos_settings_'; + + /** + * @var array + */ + protected static $default_settings = array( + 'general' => array( + 'pos_only_products' => false, + 'decimal_qty' => false, + 'force_ssl' => true, + 'default_customer' => 0, + 'default_customer_is_cashier' => false, + 'barcode_field' => '_sku', + 'generate_username' => true, + ), + 'checkout' => array( + 'order_status' => 'wc-completed', + 'admin_emails' => true, + 'customer_emails' => true, + ), + 'payment_gateways' => array( + 'default_gateway' => 'pos_cash', + 'gateways' => array( + 'pos_cash' => array( + 'order' => 0, + 'enabled' => true, + ), + 'pos_card' => array( + 'order' => 1, + 'enabled' => true, + ), + ), + ), + 'license' => array(), + ); + + /** + * @var array + */ + private static $caps = array( + 'wcpos' => array( + 'access_woocommerce_pos', // pos frontend + 'manage_woocommerce_pos', // pos admin + ), + 'wc' => array( + 'create_users', + 'edit_others_products', + 'edit_others_shop_orders', + 'edit_product', + 'edit_published_products', + 'edit_shop_orders', + 'edit_users', + 'list_users', + 'manage_product_terms', + 'publish_shop_orders', + 'read_private_products', + 'read_private_shop_coupons', + 'read_private_shop_orders', + ), + 'wp' => array( + 'read', // wp-admin access + ), + ); + + /** + * @param string $id + * @return array|mixed|WP_Error|null + */ + public function get_settings( string $id ) { + $method_name = 'get_' . $id . '_settings'; + + if ( method_exists( $this, $method_name ) ) { + return $this->$method_name(); + } + + return new WP_Error( + 'woocommerce_pos_settings_error', + /* translators: %s: Settings group id, ie: 'general' or 'checkout' */ + sprintf( __( 'Settings with id %s not found', 'woocommerce-pos' ), $id ), + array( 'status' => 400 ) + ); + } + + /** + * @param string $id + * @param array $settings + * @return array|mixed|WP_Error|null + */ + public function save_settings( string $id, array $settings ) { + $success = update_option( + static::$db_prefix . $id, + array_merge( + $settings, + array( 'date_modified_gmt' => current_time( 'mysql', true ) ) + ), + false + ); + + if ( $success ) { + return $this->get_settings( $id ); + } + + return new WP_Error( + 'woocommerce_pos_settings_error', + /* translators: %s: Settings group id, ie: 'general' or 'checkout' */ + sprintf( __( 'Can not save settings with id %s', 'woocommerce-pos' ), $id ), + array( 'status' => 400 ) + ); + } + + /** + * @return array + */ + public function get_general_settings(): array { + $general_settings = array_replace_recursive( + self::$default_settings['general'], + get_option( self::$db_prefix . 'general', array() ) + ); + + /** + * Filters the general settings. + * + * @param {array} $settings + * @returns {array} $settings + * @since 1.0.0 + * @hook woocommerce_pos_general_settings + */ + return apply_filters( 'woocommerce_pos_general_settings', $general_settings ); + } + + /** + * @return array + */ + public function get_checkout_settings(): array { + $checkout_settings = array_replace_recursive( + self::$default_settings['checkout'], + get_option( self::$db_prefix . 'checkout', array() ) + ); + + /** + * Filters the checkout settings. + * + * @param {array} $settings + * @returns {array} $settings + * @since 1.0.0 + * @hook woocommerce_pos_checkout_settings + */ + return apply_filters( 'woocommerce_pos_checkout_settings', $checkout_settings ); + } + + /** + * + */ + public function get_access_settings(): array { + global $wp_roles; + $role_caps = array(); + + $roles = $wp_roles->roles; + if ( $roles ) { + foreach ( $roles as $slug => $role ) { + $role_caps[ $slug ] = array( + 'name' => $role['name'], + 'capabilities' => array( + 'wcpos' => array_intersect_key( + array_merge( array_fill_keys( self::$caps['wcpos'], false ), $role['capabilities'] ), + array_flip( self::$caps['wcpos'] ) + ), + 'wc' => array_intersect_key( + array_merge( array_fill_keys( self::$caps['wc'], false ), $role['capabilities'] ), + array_flip( self::$caps['wc'] ) + ), + 'wp' => array_intersect_key( + array_merge( array_fill_keys( self::$caps['wp'], false ), $role['capabilities'] ), + array_flip( self::$caps['wp'] ) + ), + ), + ); + } + } + + /** + * Filters the access settings. + * + * @param {array} $settings + * @returns {array} $settings + * @since 1.0.0 + * @hook woocommerce_pos_access_settings + */ + return apply_filters( 'woocommerce_pos_access_settings', $role_caps ); + } + + /** + * + */ + public function get_license_settings() { + $settings = array(); + + if ( class_exists( '\WCPOS\WooCommercePOSPro' ) ) { + $settings = get_option( self::$db_prefix . 'license', array() ); + } + + $license_settings = array_replace_recursive( + self::$default_settings['license'], + $settings + ); + + /** + * Filters the license settings. + * + * @param {array} $settings + * @returns {array} $settings + * @since 1.0.0 + * @hook woocommerce_pos_license_settings + */ + return apply_filters( 'woocommerce_pos_license_settings', $license_settings ); + } + + /** + * @return array + */ + public function get_barcodes(): array { + global $wpdb; + + // maybe add custom barcode field + $custom_field = woocommerce_pos_get_settings( 'general', 'barcode_field' ); + + // Prepare the basic query + $result = $wpdb->get_col( + " + SELECT DISTINCT(pm.meta_key) + FROM $wpdb->postmeta AS pm + JOIN $wpdb->posts AS p + ON p.ID = pm.post_id + WHERE p.post_type IN ('product', 'product_variation') + ORDER BY pm.meta_key + " + ); + + if ( ! empty( $custom_field ) ) { + $result[] = $custom_field; + } + + sort( $result ); + + return array_unique( $result ); + } + + /** + * @return array + */ + public function get_order_statuses(): array { + $order_statuses = wc_get_order_statuses(); + $order_statuses = array_map( 'wc_get_order_status_name', $order_statuses ); + + return $order_statuses; + } + + /** + * + */ + public function get_payment_gateways_settings() { + // Note: I need to re-init the gateways here to pass the tests, but it seems to work fine in the app. + WC_Payment_Gateways::instance()->init(); + $installed_gateways = WC_Payment_Gateways::instance()->payment_gateways(); + $gateways_settings = array_replace_recursive( + self::$default_settings['payment_gateways'], + get_option( self::$db_prefix . 'payment_gateways', array() ) + ); + + // NOTE - gateways can be installed and uninstalled, so we need to assume the settings data is stale + $response = array( + 'default_gateway' => $gateways_settings['default_gateway'], + 'gateways' => array(), + ); + + // loop through installed gateways and merge with saved settings + foreach ( $installed_gateways as $id => $gateway ) { + // sanity check for gateway class + if ( ! is_a( $gateway, 'WC_Payment_Gateway' ) || 'pre_install_woocommerce_payments_promotion' === $id ) { + continue; + } + $response['gateways'][ $id ] = array_replace_recursive( + array( + 'id' => $gateway->id, + 'title' => $gateway->title, + 'description' => $gateway->description, + 'enabled' => false, + 'order' => 999, + ), + isset( $gateways_settings['gateways'][ $id ] ) ? $gateways_settings['gateways'][ $id ] : array() + ); + } + + /** + * Filters the payment gateways settings. + * + * @param {array} $settings + * @returns {array} $settings + * @since 1.0.0 + * @hook woocommerce_pos_payment_gateways_settings + */ + return apply_filters( 'woocommerce_pos_payment_gateways_settings', $response ); + } +} diff --git a/includes/Services/Stores.php b/includes/Services/Stores.php new file mode 100644 index 0000000..66e6cd4 --- /dev/null +++ b/includes/Services/Stores.php @@ -0,0 +1,7 @@ +auth_service = new Auth(); } @@ -46,11 +53,18 @@ public function get_template(): void { // last chance before template is rendered do_action( 'woocommerce_pos_template_redirect' ); + // if modal login + if ( isset( $_GET['modal'] ) && $_GET['modal'] === '1' ) { + $this->modal_login(); + exit; + } + // add head & footer actions add_action( 'woocommerce_pos_head', array( $this, 'head' ) ); add_action( 'woocommerce_pos_footer', array( $this, 'footer' ) ); include woocommerce_pos_locate_template( 'pos.php' ); + exit; } /** @@ -91,27 +105,6 @@ public function footer(): void { update_user_meta( $user->ID, '_woocommerce_pos_uuid', $user_uuid ); } -// $issued_at = time(); -// $not_before = $issued_at; -// $expire = $issued_at + ( 60 * 60 * 24 * 7 ); // 7 days -// $token = array( -// 'iss' => get_bloginfo( 'url' ), -// 'iat' => $issued_at, -// 'nbf' => $not_before, -// 'exp' => $expire, -// 'data' => array( -// 'user' => array( -// 'id' => $user->ID, -// ), -// ), -// ); -// $secret_key = get_option( 'woocommerce_pos_secret_key' ); -// if ( false === $secret_key || empty( $secret_key ) ) { -// $secret_key = wp_generate_password( 64, true, true ); -// update_option( 'woocommerce_pos_secret_key', $secret_key ); -// } -// $jwt = FirebaseJWT::encode( $token, $secret_key, 'HS256' ); - $vars = array( 'version' => VERSION, 'manifest' => $github_url . 'metadata.json', @@ -222,4 +215,33 @@ private function no_cache(): void { do_action( 'litespeed_control_set_nocache', 'nocache WoCommerce POS web application' ); } + + private function modal_login() { + $user = wp_get_current_user(); + $user_data = $this->auth_service->get_user_data( $user ); + $credentials = wp_json_encode( $user_data ); + + echo "" . "\n"; + } } diff --git a/includes/wcpos-functions.php b/includes/wcpos-functions.php index 06d578f..16ce0d4 100644 --- a/includes/wcpos-functions.php +++ b/includes/wcpos-functions.php @@ -23,7 +23,7 @@ use const WCPOS\WooCommercePOS\SHORT_NAME; use const WCPOS\WooCommercePOS\VERSION; -if ( ! \function_exists( 'woocommerce_pos_url' ) ) { +if ( ! function_exists( 'woocommerce_pos_url' ) ) { function woocommerce_pos_url( $page = '' ): string { $slug = Permalink::get_slug(); $scheme = woocommerce_pos_get_settings( 'general', 'force_ssl' ) ? 'https' : null; @@ -36,7 +36,7 @@ function woocommerce_pos_url( $page = '' ): string { * getallheaders() is an alias of apache_response_headers() * This function provides compatibility for nginx servers */ -if ( ! \function_exists( 'getallheaders' ) ) { +if ( ! function_exists( 'getallheaders' ) ) { function getallheaders(): array { $headers = array(); foreach ( $_SERVER as $name => $value ) { @@ -57,7 +57,7 @@ function getallheaders(): array { * * @return bool */ -if ( ! \function_exists( 'woocommerce_pos_request' ) ) { +if ( ! function_exists( 'woocommerce_pos_request' ) ) { function woocommerce_pos_request( $type = 'all' ): bool { // check query_vars, eg: ?wcpos=1 or /pos rewrite rule if ( 'all' == $type || 'query_var' == $type ) { @@ -80,9 +80,9 @@ function woocommerce_pos_request( $type = 'all' ): bool { } -if ( ! \function_exists( 'woocommerce_pos_admin_request' ) ) { +if ( ! function_exists( 'woocommerce_pos_admin_request' ) ) { function woocommerce_pos_admin_request() { - if ( \function_exists( 'getallheaders' ) + if ( function_exists( 'getallheaders' ) && $headers = getallheaders() && isset( $headers['X-WC-POS-ADMIN'] ) ) { @@ -105,10 +105,10 @@ function woocommerce_pos_admin_request() { * * @return mixed */ -if ( ! \function_exists( 'woocommerce_pos_get_settings' ) ) { +if ( ! function_exists( 'woocommerce_pos_get_settings' ) ) { function woocommerce_pos_get_settings( $id, $key = null ) { - $api = new Settings(); - $settings = $api->get_settings( $id ); + $settings_service = new \WCPOS\WooCommercePOS\Services\Settings(); + $settings = $settings_service->get_settings( $id ); if ( is_wp_error( $settings ) ) { return $settings; @@ -140,11 +140,11 @@ function woocommerce_pos_get_settings( $id, $key = null ) { * * @return mixed */ -if ( ! \function_exists( 'woocommerce_pos_json_encode' ) ) { +if ( ! function_exists( 'woocommerce_pos_json_encode' ) ) { function woocommerce_pos_json_encode( $data ) { $args = array( $data, JSON_FORCE_OBJECT ); - return \call_user_func_array( 'json_encode', $args ); + return call_user_func_array( 'json_encode', $args ); } } @@ -155,7 +155,7 @@ function woocommerce_pos_json_encode( $data ) { * * @return mixed|void */ -if ( ! \function_exists( 'woocommerce_pos_locate_template' ) ) { +if ( ! function_exists( 'woocommerce_pos_locate_template' ) ) { function woocommerce_pos_locate_template( $path = '' ) { $template = locate_template(array( 'woocommerce-pos/' . $path, @@ -190,21 +190,21 @@ function woocommerce_pos_locate_template( $path = '' ) { * * @return mixed */ -if ( ! \function_exists( 'woocommerce_pos_trim_html_string' ) ) { +if ( ! function_exists( 'woocommerce_pos_trim_html_string' ) ) { function woocommerce_pos_trim_html_string( $str ): string { return preg_replace( '/^\s+|\n|\r|\s+$/m', '', $str ); } } -if ( ! \function_exists( 'woocommerce_pos_doc_url' ) ) { +if ( ! function_exists( 'woocommerce_pos_doc_url' ) ) { function woocommerce_pos_doc_url( $page ): string { return 'http://docs.wcpos.com/v/' . VERSION . '/en/' . $page; } } -if ( ! \function_exists( 'woocommerce_pos_faq_url' ) ) { +if ( ! function_exists( 'woocommerce_pos_faq_url' ) ) { function woocommerce_pos_faq_url( $page ): string { return 'http://faq.wcpos.com/v/' . VERSION . '/en/' . $page; } diff --git a/packages/analytics/package.json b/packages/analytics/package.json index 9bd0143..bb0a520 100644 --- a/packages/analytics/package.json +++ b/packages/analytics/package.json @@ -10,7 +10,7 @@ "@transifex/react": "^5.3.0", "@woocommerce/components": "^12.0.0", "@wordpress/api-fetch": "6.33.0", - "@wordpress/components": "^25.1.3", + "@wordpress/components": "^25.2.0", "@wordpress/element": "5.13.0", "@wordpress/hooks": "^3.36.0", "@wordpress/url": "3.37.0", @@ -60,9 +60,9 @@ "sass-loader": "13.3.2", "style-loader": "3.3.3", "terser-webpack-plugin": "5.3.9", - "ts-loader": "^9.4.3", - "typescript": "5.1.5", - "webpack": "5.88.0", + "ts-loader": "^9.4.4", + "typescript": "5.1.6", + "webpack": "5.88.1", "webpack-bundle-analyzer": "^4.9.0", "webpack-cli": "5.1.4", "webpack-livereload-plugin": "3.0.2", diff --git a/packages/eslint/package.json b/packages/eslint/package.json index 15db659..5855621 100644 --- a/packages/eslint/package.json +++ b/packages/eslint/package.json @@ -27,6 +27,6 @@ "eslint-plugin-react-hooks": "4.6.0", "eslint-plugin-react-native": "4.0.0", "prettier": "2.8.8", - "typescript": "^5.1.5" + "typescript": "^5.1.6" } } diff --git a/packages/settings/package.json b/packages/settings/package.json index 1034674..14412f3 100644 --- a/packages/settings/package.json +++ b/packages/settings/package.json @@ -11,7 +11,7 @@ "@transifex/native": "^5.3.0", "@transifex/react": "^5.3.0", "@wordpress/api-fetch": "6.33.0", - "@wordpress/components": "^25.1.3", + "@wordpress/components": "^25.2.0", "@wordpress/element": "5.13.0", "@wordpress/url": "3.37.0", "classnames": "2.3.2", @@ -62,9 +62,9 @@ "style-loader": "3.3.3", "tailwindcss": "3.3.2", "terser-webpack-plugin": "5.3.9", - "ts-loader": "^9.4.3", - "typescript": "5.1.5", - "webpack": "5.88.0", + "ts-loader": "^9.4.4", + "typescript": "5.1.6", + "webpack": "5.88.1", "webpack-bundle-analyzer": "^4.9.0", "webpack-cli": "5.1.4", "webpack-livereload-plugin": "3.0.2", diff --git a/packages/settings/src/screens/general/barcode-select.tsx b/packages/settings/src/screens/general/barcode-select.tsx index ee19c51..74299af 100644 --- a/packages/settings/src/screens/general/barcode-select.tsx +++ b/packages/settings/src/screens/general/barcode-select.tsx @@ -1,8 +1,5 @@ import * as React from 'react'; -import { useQuery } from '@tanstack/react-query'; -import apiFetch from '@wordpress/api-fetch'; - import Combobox from '../../components/combobox'; import useNotices from '../../hooks/use-notices'; @@ -13,38 +10,16 @@ interface BarcodeSelectProps { const BarcodeSelect = ({ selected, onSelect }: BarcodeSelectProps) => { const [query, setQuery] = React.useState(''); - const { setNotice } = useNotices(); - - const { data, isFetching } = useQuery({ - queryKey: ['barcodes', query], - queryFn: async () => { - const response = await apiFetch({ - path: `wcpos/v1/settings/general/barcodes?wcpos=1&search=${encodeURIComponent(query)}`, - method: 'GET', - }).catch((err) => { - console.error(err); - return err; - }); - - // if we have an error response, set the notice - if (response?.code && response?.message) { - setNotice({ type: 'error', message: response?.message }); - } - - // convert to array - return Object.values(response); - }, - placeholderData: [], - }); + const barcodes = window?.wcpos?.settings?.barcodes; const options = React.useMemo(() => { - return (data || []).map((option) => { + return (barcodes || []).map((option) => { return { value: option, label: option, }; }); - }, [data]); + }, [barcodes, query]); return ( { onSelect(value); }} onSearch={(value) => setQuery(value)} - loading={isFetching} /> ); }; diff --git a/yarn.lock b/yarn.lock index 8e55181..4168d50 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5083,7 +5083,7 @@ __metadata: "@wcpos/eslint-config": "*" "@woocommerce/components": ^12.0.0 "@wordpress/api-fetch": 6.33.0 - "@wordpress/components": ^25.1.3 + "@wordpress/components": ^25.2.0 "@wordpress/element": 5.13.0 "@wordpress/env": 8.2.0 "@wordpress/hooks": ^3.36.0 @@ -5113,9 +5113,9 @@ __metadata: sass-loader: 13.3.2 style-loader: 3.3.3 terser-webpack-plugin: 5.3.9 - ts-loader: ^9.4.3 - typescript: 5.1.5 - webpack: 5.88.0 + ts-loader: ^9.4.4 + typescript: 5.1.6 + webpack: 5.88.1 webpack-bundle-analyzer: ^4.9.0 webpack-cli: 5.1.4 webpack-livereload-plugin: 3.0.2 @@ -5152,7 +5152,7 @@ __metadata: eslint-plugin-react-hooks: 4.6.0 eslint-plugin-react-native: 4.0.0 prettier: 2.8.8 - typescript: ^5.1.5 + typescript: ^5.1.6 languageName: unknown linkType: soft @@ -5184,7 +5184,7 @@ __metadata: "@typescript-eslint/parser": 5.60.1 "@wcpos/eslint-config": "*" "@wordpress/api-fetch": 6.33.0 - "@wordpress/components": ^25.1.3 + "@wordpress/components": ^25.2.0 "@wordpress/element": 5.13.0 "@wordpress/env": 8.2.0 "@wordpress/url": 3.37.0 @@ -5215,9 +5215,9 @@ __metadata: style-loader: 3.3.3 tailwindcss: 3.3.2 terser-webpack-plugin: 5.3.9 - ts-loader: ^9.4.3 - typescript: 5.1.5 - webpack: 5.88.0 + ts-loader: ^9.4.4 + typescript: 5.1.6 + webpack: 5.88.1 webpack-bundle-analyzer: ^4.9.0 webpack-cli: 5.1.4 webpack-livereload-plugin: 3.0.2 @@ -5614,7 +5614,7 @@ __metadata: languageName: node linkType: hard -"@wordpress/a11y@npm:^3.35.1": +"@wordpress/a11y@npm:^3.36.0": version: 3.36.0 resolution: "@wordpress/a11y@npm:3.36.0" dependencies: @@ -6034,9 +6034,9 @@ __metadata: languageName: node linkType: hard -"@wordpress/components@npm:^25.1.3": - version: 25.1.3 - resolution: "@wordpress/components@npm:25.1.3" +"@wordpress/components@npm:^25.2.0": + version: 25.2.0 + resolution: "@wordpress/components@npm:25.2.0" dependencies: "@ariakit/react": ^0.2.10 "@babel/runtime": ^7.16.0 @@ -6049,23 +6049,23 @@ __metadata: "@floating-ui/react-dom": 1.0.0 "@radix-ui/react-dropdown-menu": ^2.0.4 "@use-gesture/react": ^10.2.24 - "@wordpress/a11y": ^3.35.1 - "@wordpress/compose": ^6.12.1 - "@wordpress/date": ^4.35.1 - "@wordpress/deprecated": ^3.35.1 - "@wordpress/dom": ^3.35.1 - "@wordpress/element": ^5.12.1 - "@wordpress/escape-html": ^2.35.1 - "@wordpress/hooks": ^3.35.1 - "@wordpress/html-entities": ^3.35.1 - "@wordpress/i18n": ^4.35.1 - "@wordpress/icons": ^9.26.2 - "@wordpress/is-shallow-equal": ^4.35.1 - "@wordpress/keycodes": ^3.35.1 - "@wordpress/primitives": ^3.33.1 - "@wordpress/private-apis": ^0.17.1 - "@wordpress/rich-text": ^6.12.1 - "@wordpress/warning": ^2.35.1 + "@wordpress/a11y": ^3.36.0 + "@wordpress/compose": ^6.13.0 + "@wordpress/date": ^4.36.0 + "@wordpress/deprecated": ^3.36.0 + "@wordpress/dom": ^3.36.0 + "@wordpress/element": ^5.13.0 + "@wordpress/escape-html": ^2.36.0 + "@wordpress/hooks": ^3.36.0 + "@wordpress/html-entities": ^3.36.0 + "@wordpress/i18n": ^4.36.0 + "@wordpress/icons": ^9.27.0 + "@wordpress/is-shallow-equal": ^4.36.0 + "@wordpress/keycodes": ^3.36.0 + "@wordpress/primitives": ^3.34.0 + "@wordpress/private-apis": ^0.18.0 + "@wordpress/rich-text": ^6.13.0 + "@wordpress/warning": ^2.36.0 change-case: ^4.1.2 classnames: ^2.3.1 colord: ^2.7.0 @@ -6074,7 +6074,7 @@ __metadata: dom-scroll-into-view: ^1.2.1 downshift: ^6.0.15 fast-deep-equal: ^3.1.3 - framer-motion: ~10.11.6 + framer-motion: ^10.11.6 gradient-parser: ^0.1.5 highlight-words-core: ^1.2.2 is-plain-object: ^5.0.0 @@ -6090,7 +6090,7 @@ __metadata: peerDependencies: react: ^18.0.0 react-dom: ^18.0.0 - checksum: 5434777eba5e1a3179f99887410e98654283844961d7577c00c7008a573b1ab50e6c3f80b4d8f8f387b9cee0e90e29ac59f16befc06239e98a99833b0116b859 + checksum: 3d88d37842001ffdec297458d2621c6cf789b7e25e7b64b80c4ad79fd314e7a29d3b5216c9c5caf22962d2571b852dd8e0194c5cc18321029b2eace5afbf0172 languageName: node linkType: hard @@ -6159,25 +6159,25 @@ __metadata: languageName: node linkType: hard -"@wordpress/compose@npm:^6.12.1": - version: 6.12.1 - resolution: "@wordpress/compose@npm:6.12.1" +"@wordpress/compose@npm:^6.13.0": + version: 6.13.0 + resolution: "@wordpress/compose@npm:6.13.0" dependencies: "@babel/runtime": ^7.16.0 "@types/mousetrap": ^1.6.8 - "@wordpress/deprecated": ^3.35.1 - "@wordpress/dom": ^3.35.1 - "@wordpress/element": ^5.12.1 - "@wordpress/is-shallow-equal": ^4.35.1 - "@wordpress/keycodes": ^3.35.1 - "@wordpress/priority-queue": ^2.35.1 + "@wordpress/deprecated": ^3.36.0 + "@wordpress/dom": ^3.36.0 + "@wordpress/element": ^5.13.0 + "@wordpress/is-shallow-equal": ^4.36.0 + "@wordpress/keycodes": ^3.36.0 + "@wordpress/priority-queue": ^2.36.0 change-case: ^4.1.2 clipboard: ^2.0.8 mousetrap: ^1.6.5 use-memo-one: ^1.1.1 peerDependencies: react: ^18.0.0 - checksum: a1d5351c70a63219e7eda5a3453b0602de4c3501b09e3915259830d64ab9db78123c1f5130646fc9c16802cb11bbd4b77738fdb0af9d111fc06950aacb038349 + checksum: 85fb2d9049cd5b3087b86d54901486354e110ba59573ecb0a8b77078c37663ef2840c47477a551ff137db1817cc98fb7fe02e62bcc89a4f4de68ae49b2f10320 languageName: node linkType: hard @@ -6366,18 +6366,18 @@ __metadata: languageName: node linkType: hard -"@wordpress/data@npm:^9.5.1": - version: 9.5.1 - resolution: "@wordpress/data@npm:9.5.1" +"@wordpress/data@npm:^9.6.0": + version: 9.6.0 + resolution: "@wordpress/data@npm:9.6.0" dependencies: "@babel/runtime": ^7.16.0 - "@wordpress/compose": ^6.12.1 - "@wordpress/deprecated": ^3.35.1 - "@wordpress/element": ^5.12.1 - "@wordpress/is-shallow-equal": ^4.35.1 - "@wordpress/priority-queue": ^2.35.1 - "@wordpress/private-apis": ^0.17.1 - "@wordpress/redux-routine": ^4.35.1 + "@wordpress/compose": ^6.13.0 + "@wordpress/deprecated": ^3.36.0 + "@wordpress/element": ^5.13.0 + "@wordpress/is-shallow-equal": ^4.36.0 + "@wordpress/priority-queue": ^2.36.0 + "@wordpress/private-apis": ^0.18.0 + "@wordpress/redux-routine": ^4.36.0 deepmerge: ^4.3.0 equivalent-key-map: ^0.2.2 is-plain-object: ^5.0.0 @@ -6387,7 +6387,7 @@ __metadata: use-memo-one: ^1.1.1 peerDependencies: react: ^18.0.0 - checksum: 3acd4e59c33941d440f99ae654e235553c7b16b91737e56ca4120b7c56ed303fbe6b5640be371135b6170f97f5e4cac8e2ed912e8b6f17413ead46820a00b85c + checksum: 5123da380f81ab94e46545cd8cec309bd0a8477448c32da7454b9667868fb199a35e4261e6e225003ab0a30fa35ec4ee723bdf4f2b77015496774466d997021d languageName: node linkType: hard @@ -6403,7 +6403,7 @@ __metadata: languageName: node linkType: hard -"@wordpress/date@npm:^4.35.1": +"@wordpress/date@npm:^4.36.0": version: 4.36.0 resolution: "@wordpress/date@npm:4.36.0" dependencies: @@ -6445,7 +6445,7 @@ __metadata: languageName: node linkType: hard -"@wordpress/deprecated@npm:^3.35.1, @wordpress/deprecated@npm:^3.36.0": +"@wordpress/deprecated@npm:^3.36.0": version: 3.36.0 resolution: "@wordpress/deprecated@npm:3.36.0" dependencies: @@ -6503,7 +6503,7 @@ __metadata: languageName: node linkType: hard -"@wordpress/dom@npm:^3.35.1": +"@wordpress/dom@npm:^3.36.0": version: 3.36.0 resolution: "@wordpress/dom@npm:3.36.0" dependencies: @@ -6513,7 +6513,7 @@ __metadata: languageName: node linkType: hard -"@wordpress/element@npm:5.13.0, @wordpress/element@npm:^5.12.1, @wordpress/element@npm:^5.13.0": +"@wordpress/element@npm:5.13.0, @wordpress/element@npm:^5.13.0": version: 5.13.0 resolution: "@wordpress/element@npm:5.13.0" dependencies: @@ -6666,7 +6666,7 @@ __metadata: languageName: node linkType: hard -"@wordpress/escape-html@npm:^2.35.1, @wordpress/escape-html@npm:^2.36.0": +"@wordpress/escape-html@npm:^2.36.0": version: 2.36.0 resolution: "@wordpress/escape-html@npm:2.36.0" dependencies: @@ -6702,7 +6702,7 @@ __metadata: languageName: node linkType: hard -"@wordpress/hooks@npm:^3.35.1, @wordpress/hooks@npm:^3.36.0": +"@wordpress/hooks@npm:^3.36.0": version: 3.36.0 resolution: "@wordpress/hooks@npm:3.36.0" dependencies: @@ -6720,7 +6720,7 @@ __metadata: languageName: node linkType: hard -"@wordpress/html-entities@npm:^3.35.1": +"@wordpress/html-entities@npm:^3.36.0": version: 3.36.0 resolution: "@wordpress/html-entities@npm:3.36.0" dependencies: @@ -6778,7 +6778,7 @@ __metadata: languageName: node linkType: hard -"@wordpress/i18n@npm:^4.35.1, @wordpress/i18n@npm:^4.36.0": +"@wordpress/i18n@npm:^4.36.0": version: 4.36.0 resolution: "@wordpress/i18n@npm:4.36.0" dependencies: @@ -6816,14 +6816,14 @@ __metadata: languageName: node linkType: hard -"@wordpress/icons@npm:^9.26.2": - version: 9.26.2 - resolution: "@wordpress/icons@npm:9.26.2" +"@wordpress/icons@npm:^9.27.0": + version: 9.27.0 + resolution: "@wordpress/icons@npm:9.27.0" dependencies: "@babel/runtime": ^7.16.0 - "@wordpress/element": ^5.12.1 - "@wordpress/primitives": ^3.33.1 - checksum: f57491abf9c0fede1a89665755d146b85c7c3388a73fabb0ec4a33606e974cc6468ecdffc6cde773b1534294df717d1f42984ddb95bdd80c2bd450f229a12d10 + "@wordpress/element": ^5.13.0 + "@wordpress/primitives": ^3.34.0 + checksum: da2c6d81d8bc8405dc9bd5981053bc8be1c37a84e1f57b2f53422b6001e6908fd94730f6c9929d0da97a075b495628926acfdb97bae51ad9b8a55a13a8a747e3 languageName: node linkType: hard @@ -6854,7 +6854,7 @@ __metadata: languageName: node linkType: hard -"@wordpress/is-shallow-equal@npm:^4.35.1": +"@wordpress/is-shallow-equal@npm:^4.36.0": version: 4.36.0 resolution: "@wordpress/is-shallow-equal@npm:4.36.0" dependencies: @@ -6911,7 +6911,7 @@ __metadata: languageName: node linkType: hard -"@wordpress/keycodes@npm:^3.35.1": +"@wordpress/keycodes@npm:^3.36.0": version: 3.36.0 resolution: "@wordpress/keycodes@npm:3.36.0" dependencies: @@ -6957,7 +6957,7 @@ __metadata: languageName: node linkType: hard -"@wordpress/primitives@npm:^3.33.1": +"@wordpress/primitives@npm:^3.34.0": version: 3.34.0 resolution: "@wordpress/primitives@npm:3.34.0" dependencies: @@ -6997,7 +6997,7 @@ __metadata: languageName: node linkType: hard -"@wordpress/priority-queue@npm:^2.35.1": +"@wordpress/priority-queue@npm:^2.36.0": version: 2.36.0 resolution: "@wordpress/priority-queue@npm:2.36.0" dependencies: @@ -7025,12 +7025,12 @@ __metadata: languageName: node linkType: hard -"@wordpress/private-apis@npm:^0.17.1": - version: 0.17.1 - resolution: "@wordpress/private-apis@npm:0.17.1" +"@wordpress/private-apis@npm:^0.18.0": + version: 0.18.0 + resolution: "@wordpress/private-apis@npm:0.18.0" dependencies: "@babel/runtime": ^7.16.0 - checksum: 309a2ff713ef8615ea3082ab8f79f416dce6973cee5a3fe9c4046243542f7027fbd95f544ada0454cb16706aaa2c571fdb961812011c1cf025d4b0a23d4902e7 + checksum: 34be3dec8871dbbc95a6c5e82ef816425808dc0461fd026ded1a491ddd8c3b21259f7c5b4f11aac8742039f14104fae8f063b260a014bce731c000f5f8f2218d languageName: node linkType: hard @@ -7074,7 +7074,7 @@ __metadata: languageName: node linkType: hard -"@wordpress/redux-routine@npm:^4.35.1": +"@wordpress/redux-routine@npm:^4.36.0": version: 4.36.0 resolution: "@wordpress/redux-routine@npm:4.36.0" dependencies: @@ -7130,24 +7130,24 @@ __metadata: languageName: node linkType: hard -"@wordpress/rich-text@npm:^6.12.1": - version: 6.12.1 - resolution: "@wordpress/rich-text@npm:6.12.1" +"@wordpress/rich-text@npm:^6.13.0": + version: 6.13.0 + resolution: "@wordpress/rich-text@npm:6.13.0" dependencies: "@babel/runtime": ^7.16.0 - "@wordpress/a11y": ^3.35.1 - "@wordpress/compose": ^6.12.1 - "@wordpress/data": ^9.5.1 - "@wordpress/deprecated": ^3.35.1 - "@wordpress/element": ^5.12.1 - "@wordpress/escape-html": ^2.35.1 - "@wordpress/i18n": ^4.35.1 - "@wordpress/keycodes": ^3.35.1 + "@wordpress/a11y": ^3.36.0 + "@wordpress/compose": ^6.13.0 + "@wordpress/data": ^9.6.0 + "@wordpress/deprecated": ^3.36.0 + "@wordpress/element": ^5.13.0 + "@wordpress/escape-html": ^2.36.0 + "@wordpress/i18n": ^4.36.0 + "@wordpress/keycodes": ^3.36.0 memize: ^2.1.0 rememo: ^4.0.2 peerDependencies: react: ^18.0.0 - checksum: 721a58f484c7531aa4c3e31457a3c8da71ad0014df47d3bb10f1df363f0ae9512f6ed7f25fca7d83506b720434055ea50f11616532efd4c1e1a2ab596d35033f + checksum: e98f2e1c927a5099b21349e63a70e81f9d98e9e1a2400ec0df012b513ab9b92dd5c2ca70981eb53b34099ee0e31f631d29644989e86276739712c5c9ca4bfaf5 languageName: node linkType: hard @@ -7252,7 +7252,7 @@ __metadata: languageName: node linkType: hard -"@wordpress/warning@npm:^2.35.1": +"@wordpress/warning@npm:^2.36.0": version: 2.36.0 resolution: "@wordpress/warning@npm:2.36.0" checksum: d3b7b5018fe6b3072d549673cdcd464eef2b86fc6c06595e7e665f6f3449e4282e30cbff6eaf1c61c05bd6fa266f2a4f94c251e208b28d1062d25753c31aa20e @@ -10999,6 +10999,27 @@ __metadata: languageName: node linkType: hard +"framer-motion@npm:^10.11.6": + version: 10.12.18 + resolution: "framer-motion@npm:10.12.18" + dependencies: + "@emotion/is-prop-valid": ^0.8.2 + tslib: ^2.4.0 + peerDependencies: + react: ^18.0.0 + react-dom: ^18.0.0 + dependenciesMeta: + "@emotion/is-prop-valid": + optional: true + peerDependenciesMeta: + react: + optional: true + react-dom: + optional: true + checksum: 71539be1175de80d2266702069bb3c4f0150eb3e28f0e32307e10c217410d5ee350b025d48a86c3144caf2d0e0e1dd95e33ed886a14466bc1e763e52d4cd6157 + languageName: node + linkType: hard + "framer-motion@npm:^6.2.8": version: 6.5.1 resolution: "framer-motion@npm:6.5.1" @@ -11020,27 +11041,6 @@ __metadata: languageName: node linkType: hard -"framer-motion@npm:~10.11.6": - version: 10.11.6 - resolution: "framer-motion@npm:10.11.6" - dependencies: - "@emotion/is-prop-valid": ^0.8.2 - tslib: ^2.4.0 - peerDependencies: - react: ^18.0.0 - react-dom: ^18.0.0 - dependenciesMeta: - "@emotion/is-prop-valid": - optional: true - peerDependenciesMeta: - react: - optional: true - react-dom: - optional: true - checksum: 5f6ebb5b3b8aed29b336a5a3fbe608ae9827820514093dc447a7c33de40e61fd832e249f89ea443d8c130865652b60ccfb12be08749b4676c3badc16f3387970 - languageName: node - linkType: hard - "framesync@npm:6.0.1": version: 6.0.1 resolution: "framesync@npm:6.0.1" @@ -18117,9 +18117,9 @@ __metadata: languageName: node linkType: hard -"ts-loader@npm:^9.4.3": - version: 9.4.3 - resolution: "ts-loader@npm:9.4.3" +"ts-loader@npm:^9.4.4": + version: 9.4.4 + resolution: "ts-loader@npm:9.4.4" dependencies: chalk: ^4.1.0 enhanced-resolve: ^5.0.0 @@ -18128,7 +18128,7 @@ __metadata: peerDependencies: typescript: "*" webpack: ^5.0.0 - checksum: 139ed53bc60717d0ca231cdffbdef7566b9feda11c72fecc697983113f1266ccca2e1cdf191f841a43afa6b87d6afe57a0caf4feecf02f30845aa7ac6f2411a4 + checksum: 8e5e6b839b0edfa40d2156c880d88ccab58226894ea5978221bc48c7db3215e2e856bfd0093f148e925a2befc42d6c94cafa9a994a7da274541efaa916012b63 languageName: node linkType: hard @@ -18248,23 +18248,23 @@ __metadata: languageName: node linkType: hard -"typescript@npm:5.1.5, typescript@npm:^5.1.5": - version: 5.1.5 - resolution: "typescript@npm:5.1.5" +"typescript@npm:5.1.6, typescript@npm:^5.1.6": + version: 5.1.6 + resolution: "typescript@npm:5.1.6" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: 0eef8699e05ae767096924dbed633c340b4d36e953bb8ed87fb12e9dd9dcea5055ceac7182c614a556dbd346a8a82df799d330e1e286ae66e17c84e1710f6a6f + checksum: b2f2c35096035fe1f5facd1e38922ccb8558996331405eb00a5111cc948b2e733163cc22fab5db46992aba7dd520fff637f2c1df4996ff0e134e77d3249a7350 languageName: node linkType: hard -"typescript@patch:typescript@5.1.5#~builtin, typescript@patch:typescript@^5.1.5#~builtin": - version: 5.1.5 - resolution: "typescript@patch:typescript@npm%3A5.1.5#~builtin::version=5.1.5&hash=7ad353" +"typescript@patch:typescript@5.1.6#~builtin, typescript@patch:typescript@^5.1.6#~builtin": + version: 5.1.6 + resolution: "typescript@patch:typescript@npm%3A5.1.6#~builtin::version=5.1.6&hash=7ad353" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: 33020c886b1aa2e948b557aad4986cf6448b30c58915b12cac873bd35dc2260d93f71af8a661d2c9f352b5d099d9df13a59688e222e79276099b9c5d86d847be + checksum: 21e88b0a0c0226f9cb9fd25b9626fb05b4c0f3fddac521844a13e1f30beb8f14e90bd409a9ac43c812c5946d714d6e0dee12d5d02dfc1c562c5aacfa1f49b606 languageName: node linkType: hard @@ -18783,9 +18783,9 @@ __metadata: languageName: node linkType: hard -"webpack@npm:5.88.0": - version: 5.88.0 - resolution: "webpack@npm:5.88.0" +"webpack@npm:5.88.1": + version: 5.88.1 + resolution: "webpack@npm:5.88.1" dependencies: "@types/eslint-scope": ^3.7.3 "@types/estree": ^1.0.0 @@ -18816,7 +18816,7 @@ __metadata: optional: true bin: webpack: bin/webpack.js - checksum: 9fd1568b34ec2e99ba97c8509a15ab2576ec80c396e7015551ec814b24cfc11de173acba3e114dafe95f1a6d460781b09d6201e6a1fb15110e1d01a09f61a283 + checksum: 726e7e05ab2e7c142609a673dd6aa1a711ed97f349418a2a393d650c5ddad172d191257f60e1e37f6b2a77261571c202aabd5ce9240791a686774f0801cf5ec2 languageName: node linkType: hard