diff --git a/.github/workflows/deploy-stagging.yml b/.github/workflows/deploy-stagging.yml index a65086dee..4fc5e2698 100644 --- a/.github/workflows/deploy-stagging.yml +++ b/.github/workflows/deploy-stagging.yml @@ -8,6 +8,7 @@ on: jobs: deploy: runs-on: ubuntu-latest + if: ${{ github.repository_owner == 'Codeinwp' }} # Disable on forks env: SSH_USERNAME: ${{ secrets.SSH_USERNAME }} SSH_KEY: ${{ secrets.SSH_KEY }} diff --git a/.gitignore b/.gitignore index 4ba8de367..01f932c40 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,5 @@ todo.md artifacts *.results.json trace.json -license.json \ No newline at end of file +license.json +.fleet diff --git a/.wp-env.override.json b/.wp-env.override.json index 530d913bd..2bbde377f 100644 --- a/.wp-env.override.json +++ b/.wp-env.override.json @@ -6,22 +6,23 @@ "config": { "WP_DEBUG": true, "WP_DEBUG_DISPLAY": true, - "FS_METHOD": "direct" + "FS_METHOD": "direct", + "WP_DEFAULT_THEME": "twentytwentythree" }, "env": { - "development": { - "themes": [ - "./one-theme" - ] - }, "tests": { + "config": { + "WP_DEBUG": false, + "WP_DEBUG_DISPLAY": false + }, "plugins": [ "." ], "mappings": { - "wp-content/mu-plugins": "./node_modules/@wordpress/e2e-tests/mu-plugins", - "wp-content/plugins/gutenberg-test-plugins": "./node_modules/@wordpress/e2e-tests/plugins" - } + "wp-content/mu-plugins": "./node_modules/@wordpress/e2e-tests/mu-plugins", + "wp-content/plugins/gutenberg-test-plugins": "./node_modules/@wordpress/e2e-tests/plugins", + "wp-content/themes/gutenberg-test-themes/twentytwentythree": "https://downloads.wordpress.org/theme/twentytwentythree.1.0.zip" + } } } -} \ No newline at end of file +} diff --git a/assets/images/guide/welcome-ai.png b/assets/images/guide/welcome-ai.png new file mode 100644 index 000000000..379e2f29b Binary files /dev/null and b/assets/images/guide/welcome-ai.png differ diff --git a/blocks.json b/blocks.json index e740e559d..4d6889618 100644 --- a/blocks.json +++ b/blocks.json @@ -99,6 +99,12 @@ "form-file": { "block": "blocks/blocks/form/file/block.json" }, + "form-hidden-field": { + "block": "blocks/blocks/form/hidden-field/block.json" + }, + "form-stripe-field": { + "block": "blocks/blocks/form/stripe-field/block.json" + }, "google-map": { "block": "blocks/blocks/google-map/block.json", "assets": { @@ -272,5 +278,8 @@ "product-upsells": { "isPro": true, "block": "pro/woocommerce/upsells/block.json" + }, + "content-generator": { + "block": "blocks/blocks/content-generator/block.json" } } diff --git a/composer.json b/composer.json index 20f58a572..815dbcca6 100644 --- a/composer.json +++ b/composer.json @@ -62,6 +62,6 @@ "tubalmartin/cssmin": "^4.1", "wptt/webfont-loader": "^1.1", "sabberworm/php-css-parser": "^8.4", - "stripe/stripe-php": "^10.4" + "stripe/stripe-php": "^12.1" } } diff --git a/composer.lock b/composer.lock index 0a6e9dec9..9aea7916e 100644 --- a/composer.lock +++ b/composer.lock @@ -4,20 +4,20 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "b9df017d36e6719340b1cdb8437f0859", + "content-hash": "006c7c213e5951781d71f00b7b99b212", "packages": [ { "name": "codeinwp/themeisle-sdk", - "version": "3.3.1", + "version": "3.3.3", "source": { "type": "git", "url": "https://github.com/Codeinwp/themeisle-sdk.git", - "reference": "efb66935e69935b21ad99b0e55484e611ce4549d" + "reference": "4f7e367b6a33b41ced763e261e7a3dc3342f6330" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Codeinwp/themeisle-sdk/zipball/efb66935e69935b21ad99b0e55484e611ce4549d", - "reference": "efb66935e69935b21ad99b0e55484e611ce4549d", + "url": "https://api.github.com/repos/Codeinwp/themeisle-sdk/zipball/4f7e367b6a33b41ced763e261e7a3dc3342f6330", + "reference": "4f7e367b6a33b41ced763e261e7a3dc3342f6330", "shasum": "" }, "require-dev": { @@ -42,9 +42,9 @@ ], "support": { "issues": "https://github.com/Codeinwp/themeisle-sdk/issues", - "source": "https://github.com/Codeinwp/themeisle-sdk/tree/v3.3.1" + "source": "https://github.com/Codeinwp/themeisle-sdk/tree/v3.3.3" }, - "time": "2023-06-21T06:55:46+00:00" + "time": "2023-08-22T07:22:05+00:00" }, { "name": "masterminds/html5", @@ -168,16 +168,16 @@ }, { "name": "stripe/stripe-php", - "version": "v10.19.0", + "version": "v12.1.0", "source": { "type": "git", "url": "https://github.com/stripe/stripe-php.git", - "reference": "9ea3ba13791217bd697e896bb839d905d170cba6" + "reference": "8ef18513811e3ad0cac50f699deac4032409ae07" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/stripe/stripe-php/zipball/9ea3ba13791217bd697e896bb839d905d170cba6", - "reference": "9ea3ba13791217bd697e896bb839d905d170cba6", + "url": "https://api.github.com/repos/stripe/stripe-php/zipball/8ef18513811e3ad0cac50f699deac4032409ae07", + "reference": "8ef18513811e3ad0cac50f699deac4032409ae07", "shasum": "" }, "require": { @@ -223,9 +223,9 @@ ], "support": { "issues": "https://github.com/stripe/stripe-php/issues", - "source": "https://github.com/stripe/stripe-php/tree/v10.19.0" + "source": "https://github.com/stripe/stripe-php/tree/v12.1.0" }, - "time": "2023-07-27T23:18:52+00:00" + "time": "2023-08-31T20:23:14+00:00" }, { "name": "tubalmartin/cssmin", @@ -945,16 +945,16 @@ }, { "name": "phpstan/phpstan", - "version": "1.10.26", + "version": "1.10.32", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "5d660cbb7e1b89253a47147ae44044f49832351f" + "reference": "c47e47d3ab03137c0e121e77c4d2cb58672f6d44" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/5d660cbb7e1b89253a47147ae44044f49832351f", - "reference": "5d660cbb7e1b89253a47147ae44044f49832351f", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/c47e47d3ab03137c0e121e77c4d2cb58672f6d44", + "reference": "c47e47d3ab03137c0e121e77c4d2cb58672f6d44", "shasum": "" }, "require": { @@ -1003,7 +1003,7 @@ "type": "tidelift" } ], - "time": "2023-07-19T12:44:37+00:00" + "time": "2023-08-24T21:54:50+00:00" }, { "name": "phpunit/php-code-coverage", diff --git a/inc/class-main.php b/inc/class-main.php index 02cc5ab8a..f2518a4eb 100644 --- a/inc/class-main.php +++ b/inc/class-main.php @@ -74,6 +74,7 @@ public function autoload_classes() { '\ThemeIsle\GutenbergBlocks\Integration\Form_Providers', '\ThemeIsle\GutenbergBlocks\Integration\Form_Email', '\ThemeIsle\GutenbergBlocks\Server\Form_Server', + '\ThemeIsle\GutenbergBlocks\Server\Prompt_Server', ); $classnames = apply_filters( 'otter_blocks_autoloader', $classnames ); diff --git a/inc/class-pro.php b/inc/class-pro.php index 04e0ee9f8..3b1e24e32 100644 --- a/inc/class-pro.php +++ b/inc/class-pro.php @@ -159,6 +159,10 @@ public static function should_show_upsell() { * @access public */ public function should_show_dashboard_upsell() { + if ( defined( 'OTTER_PRO_VERSION' ) ) { + return; + } + $show_upsell = false; $installed = get_option( 'otter_blocks_install' ); @@ -398,6 +402,11 @@ public function reset_dashboard_notice() { * @access public */ public function add_pro_link( $links ) { + + if ( defined( 'OTTER_PRO_VERSION' ) ) { + return $links; + } + $links[] = sprintf( '%s', esc_url_raw( tsdk_utmify( self::get_url(), 'pluginspage', 'action' ) ), diff --git a/inc/class-registration.php b/inc/class-registration.php index 5451e5924..7e9796229 100644 --- a/inc/class-registration.php +++ b/inc/class-registration.php @@ -498,16 +498,17 @@ function() { 'root' => esc_url_raw( rest_url() ), 'nonce' => wp_create_nonce( 'wp_rest' ), 'messages' => array( - 'submission' => __( 'Form submission from', 'otter-blocks' ), - 'captcha-not-loaded' => __( 'Captcha is not loaded. Please check your browser plugins to allow the Google reCaptcha.', 'otter-blocks' ), - 'check-captcha' => __( 'Please check the captcha.', 'otter-blocks' ), - 'invalid-email' => __( 'The email address is invalid!', 'otter-blocks' ), - 'already-registered' => __( 'The email was already registered!', 'otter-blocks' ), - 'try-again' => __( 'Error. Something is wrong with the server! Try again later.', 'otter-blocks' ), - 'privacy' => __( 'I have read and agreed the privacy statement.', 'otter-blocks' ), - 'too-many-files' => __( 'Too many files loaded. Maximum is: ', 'otter-blocks' ), - 'big-file' => __( 'File size is to big. The limit is: ', 'otter-blocks' ), - 'invalid-file' => __( 'Invalid files type. The submitted files could not be processed.', 'otter-blocks' ), + 'submission' => __( 'Form submission from', 'otter-blocks' ), + 'captcha-not-loaded' => __( 'Captcha is not loaded. Please check your browser plugins to allow the Google reCaptcha.', 'otter-blocks' ), + 'check-captcha' => __( 'Please check the captcha.', 'otter-blocks' ), + 'invalid-email' => __( 'The email address is invalid!', 'otter-blocks' ), + 'already-registered' => __( 'The email was already registered!', 'otter-blocks' ), + 'try-again' => __( 'Error. Something is wrong with the server! Try again later.', 'otter-blocks' ), + 'privacy' => __( 'I have read and agreed the privacy statement.', 'otter-blocks' ), + 'too-many-files' => __( 'Too many files loaded. Maximum is: ', 'otter-blocks' ), + 'big-file' => __( 'File size is to big. The limit is: ', 'otter-blocks' ), + 'invalid-file' => __( 'Invalid files type. The submitted files could not be processed.', 'otter-blocks' ), + 'confirmingSubmission' => __( 'Confirming submission', 'otter-blocks' ), ), ) ); diff --git a/inc/css/blocks/class-shared-css.php b/inc/css/blocks/class-shared-css.php index c368a9983..aa4dbc417 100644 --- a/inc/css/blocks/class-shared-css.php +++ b/inc/css/blocks/class-shared-css.php @@ -275,6 +275,14 @@ public static function section_shared() { 'value' => 'boxShadowColor', 'default' => '#000', 'format' => function( $value, $attrs ) { + if ( ! isset( $attrs['boxShadowColorOpacity'] ) ) { + return $value; + } + + if ( 100 === $attrs['boxShadowColorOpacity'] ) { + return $value; + } + $opacity = ( isset( $attrs['boxShadowColorOpacity'] ) ? $attrs['boxShadowColorOpacity'] : 50 ) / 100; return Base_CSS::hex2rgba( $value, $opacity ); }, diff --git a/inc/integrations/api/form-request-data.php b/inc/integrations/api/form-request-data.php index 2431cb99b..8c8039488 100644 --- a/inc/integrations/api/form-request-data.php +++ b/inc/integrations/api/form-request-data.php @@ -7,8 +7,6 @@ namespace ThemeIsle\GutenbergBlocks\Integration; -use ArrayAccess; - /** * Class Form_Data_Request * @@ -43,7 +41,7 @@ class Form_Data_Request { /** * Form fields options. * - * @var array + * @var array * @since 2.2.3 */ protected $form_fields_options = array(); @@ -104,26 +102,65 @@ class Form_Data_Request { */ protected $warning_codes = array(); + /** + * Saving mode of the form data. + * + * @var string $saving_mode Saving mode. + * @since 2.4 + */ + protected $saving_mode = 'permanent'; + + /** + * The form metadata generated through the request. Use prefix 'frontend_' to make the value visible in the frontend. + * + * @var array + * @since 2.4 + */ + public $metadata = array(); + /** * Constructor. * * @access public - * @param \WP_REST_Request|mixed $request Request Data. + * @param \WP_REST_Request $request Request Data. * @since 2.0.3 */ public function __construct( $request = null ) { - if ( ! is_a( $request, 'WP_REST_Request' ) ) { + if ( ! isset( $request ) || ! is_a( $request, 'WP_REST_Request' ) ) { return; } $this->request = $request; - $form_data = $request->get_param( 'form_data' ); - $form_data = json_decode( $form_data, true ); + if ( ! empty( $request->get_param( 'form_data' ) ) ) { + $form_data = $request->get_param( 'form_data' ); + $form_data = json_decode( $form_data, true ); + $this->request_data = $this->sanitize_request_data( $form_data ); + } else { + $body = json_decode( $request->get_body(), true ); + if ( null !== $body ) { + $this->request_data = $this->sanitize_request_data( $body ); + } else { + $this->error_code = Form_Data_Response::ERROR_MALFORMED_REQUEST; + } + } - $this->request_data = $this->sanitize_request_data( $form_data ); $this->form_options = new Form_Settings_Data( array() ); + + if ( ! empty( $request->get_header( 'O-Form-Save-Mode' ) ) ) { + $this->set_saving_mode( $request->get_header( 'O-Form-Save-Mode' ) ); + } + } + + /** + * Set the form data. + * + * @param mixed $form_request_data The form data. + * @return void + */ + private function set_data_from_request( $form_request_data ) { + $this->request_data = $form_request_data; } /** @@ -140,34 +177,34 @@ public function set_form_options( $form_options ) { /** * Get the value of the field from the request. * - * @param string $field_name The name of the field. + * @param string $key The name of the field. * @return mixed * @since 2.0.3 */ - public function get( $field_name ) { - return $this->is_set( $field_name ) ? $this->request_data[ $field_name ] : null; + public function get_root_data( $key ) { + return $this->is_root_data_set( $key ) ? $this->request_data[ $key ] : null; } /** - * Get the field value. + * Get the item from the payload. * - * @param string $field_name The name of the field. + * @param string $key The name of the item. * @return mixed|null * @since 2.0.3 */ - public function get_payload_field( $field_name ) { - return $this->payload_has_field( $field_name ) ? $this->request_data['payload'][ $field_name ] : null; + public function get_data_from_payload( $key ) { + return $this->payload_has( $key ) ? $this->request_data['payload'][ $key ] : null; } /** - * Check if the payload has the field. + * Check if the payload has the data item set. * - * @param string $field_name The name of the field. + * @param string $key The name of the item. * @return bool * @since 2.0.3 */ - public function payload_has_field( $field_name ) { - return $this->has_payload() && isset( $this->request_data['payload'][ $field_name ] ); + public function payload_has( $key ) { + return $this->has_payload() && isset( $this->request_data['payload'][ $key ] ); } /** @@ -192,27 +229,29 @@ public function change_provider( $provider ) { } /** - * Check if the value of the field is set. + * Check if the root data of the request is set. + * The root data is the top level structure of the request. * - * @param string $field_name The name of the field. + * @param string $key The name of the field. * @return boolean * @since 2.0.0 */ - public function is_set( $field_name ) { + public function is_root_data_set( $key ) { // TODO: we can do a more refined verification like checking for empty strings or arrays. - return isset( $this->request_data[ $field_name ] ); + return isset( $this->request_data[ $key ] ); } /** - * Check if the given fields are set. + * Check if the root data of the request has the given fields. + * The root data is the top level structure of the request. * - * @param array $fields_name The name of the fields. + * @param array $keys The name of the fields. * @return boolean * @since 2.0.0 */ - public function are_fields_set( $fields_name ) { - foreach ( $fields_name as $field_name ) { - if ( ! isset( $this->request_data[ $field_name ] ) ) { + public function are_root_data_set( $keys ) { + foreach ( $keys as $key ) { + if ( ! $this->is_root_data_set( $key ) ) { return false; } } @@ -220,61 +259,21 @@ public function are_fields_set( $fields_name ) { } /** - * Check if the given fields are set. + * Check if the given data fields are set in the payload. * - * @param array $fields_name The name of the fields. + * @param array $keys The name of the fields. * @return boolean * @since 2.0.3 */ - public function are_payload_fields_set( $fields_name ) { - foreach ( $fields_name as $field_name ) { - if ( ! isset( $this->request_data['payload'][ $field_name ] ) || '' === $this->request_data['payload'][ $field_name ] ) { - return false; - } - } - return true; - } - - /** - * Check if the payload has the given fields. - * - * @param array $fields_name The name of the fields. - * @return bool - * @since 2.0.3 - */ - public function payload_has_fields( $fields_name ) { - foreach ( $fields_name as $field_name ) { - if ( ! $this->payload_has_field( $field_name ) ) { + public function are_payload_data_set( $keys ) { + foreach ( $keys as $key ) { + if ( ! isset( $this->request_data['payload'][ $key ] ) || '' === $this->request_data['payload'][ $key ] ) { return false; } } return true; } - /** - * Check if the field has one of the given values. - * - * @param string $field_name The name of the field. - * @param array $values The desired values of the field. - * @return boolean - * @since 2.0.0 - */ - public function field_has( $field_name, $values ) { - return in_array( $this->get( $field_name ), $values, true ); - } - - /** - * Check if a field has the given values. - * - * @param string $field_name The field name. - * @param array $values The values. - * @return bool - * @since 2.0.3 - */ - public function payload_field_has( $field_name, $values ) { - return in_array( $this->get_payload_field( $field_name ), $values, true ); - } - /** * Sanitize the request data. * @@ -315,8 +314,8 @@ public static function sanitize_array_map_deep( $array ) { * @return mixed Form input data. * @since 2.0.0 */ - public function get_form_inputs() { - return $this->get_payload_field( 'formInputsData' ); + public function get_fields() { + return $this->get_data_from_payload( 'formInputsData' ); } /** @@ -325,7 +324,7 @@ public function get_form_inputs() { * @return Form_Settings_Data|null * @since 2.0.0 */ - public function get_form_options() { + public function get_wp_options() { return $this->form_options; } @@ -372,12 +371,14 @@ public function can_keep_uploaded_files() { /** * Set if we should keep the uploaded files. * - * @param bool $keep_uploaded_files True if we should keep the uploaded files. + * @param bool|mixed $keep_uploaded_files True if we should keep the uploaded files. * @return void * @since 2.2.3 */ public function set_keep_uploaded_files( $keep_uploaded_files ) { - $this->keep_uploaded_files = $keep_uploaded_files; + if ( is_bool( $keep_uploaded_files ) ) { + $this->keep_uploaded_files = (bool) $keep_uploaded_files; + } } /** @@ -403,12 +404,14 @@ public function get_files_loaded_to_media_library() { /** * Set the files loaded to media library. * - * @param array $files_loaded_to_media_library The files loaded to media library. + * @param array|mixed $files_loaded_to_media_library The files loaded to media library. * @return void * @since 2.2.3 */ public function set_files_loaded_to_media_library( $files_loaded_to_media_library ) { - $this->files_loaded_to_media_library = $files_loaded_to_media_library; + if ( is_array( $files_loaded_to_media_library ) ) { + $this->files_loaded_to_media_library = $files_loaded_to_media_library; + } } /** @@ -431,16 +434,6 @@ public function get_error_code() { return $this->error_code; } - /** - * Get the error details. - * - * @return string - * @since 2.2.3 - */ - public function get_error_details() { - return $this->error_details; - } - /** * Set the error. * @@ -460,11 +453,11 @@ public function set_error( $error_code, $error_details = null ) { * @return mixed|string * @since 2.2.3 */ - public function get_email_from_form_input() { - $inputs = $this->get_form_inputs(); + public function get_first_email_from_input_fields() { + $inputs = $this->get_fields(); if ( is_array( $inputs ) ) { foreach ( $inputs as $input_field ) { - if ( 'email' == $input_field['type'] ) { + if ( ! empty( $input_field['type'] ) && 'email' == $input_field['type'] ) { return $input_field['value']; } } @@ -475,11 +468,11 @@ public function get_email_from_form_input() { /** * Add a field option. * - * @param Form_Field_Option_Data $field_option The field option. + * @param Form_Field_WP_Option_Data $field_option The field option. * @return void */ - public function add_field_option( $field_option ) { - if ( $field_option instanceof Form_Field_Option_Data ) { + public function add_field_wp_option( $field_option ) { + if ( $field_option instanceof Form_Field_WP_Option_Data ) { if ( empty( $this->form_fields_options ) ) { $this->form_fields_options = array(); } @@ -488,24 +481,12 @@ public function add_field_option( $field_option ) { } } - /** - * Remove a field option. - * - * @param string $field_option_name The field option name. - * @return void - */ - public function remove_field_option( $field_option_name ) { - if ( isset( $this->form_fields_options[ $field_option_name ] ) ) { - unset( $this->form_fields_options[ $field_option_name ] ); - } - } - /** * Get the field options. * - * @return array + * @return array */ - public function get_field_options() { + public function get_wp_fields_options() { return $this->form_fields_options; } @@ -513,7 +494,7 @@ public function get_field_options() { * Get the field option. * * @param string $field_option_name The field option name. - * @return Form_Field_Option_Data|null + * @return Form_Field_WP_Option_Data|null */ public function get_field_option( $field_option_name ) { if ( isset( $this->form_fields_options[ $field_option_name ] ) ) { @@ -585,7 +566,7 @@ public function has_warning_codes( $codes ) { * @return mixed|string|null */ public function get_form_option_id() { - return $this->get_payload_field( 'formOption' ); + return $this->get_data_from_payload( 'formOption' ); } /** @@ -596,4 +577,135 @@ public function get_form_option_id() { public function get_request() { return $this->request; } + + /** + * Get the saving mode. + * + * @return string|null + */ + public function get_saving_mode() { + return $this->saving_mode; + } + + /** + * Check if we are saving temporary data. + * + * @return bool + */ + public function is_temporary() { + return 'temporary' === $this->saving_mode; + } + + /** + * Check if we are saving duplicate data. + * + * @return bool + */ + public function is_duplicate() { + return 'duplicate' === $this->saving_mode; + } + + /** + * Set the saving mode. + * + * @param string $saving_mode The saving mode. + * @return void + */ + public function set_saving_mode( $saving_mode ) { + if ( empty( $saving_mode ) ) { + return; + } + + $this->saving_mode = $saving_mode; + } + + /** + * Mark as duplicate. + * + * @return void + */ + public function mark_as_duplicate() { + $this->set_saving_mode( 'duplicate' ); + } + + /** + * Mark as temporary data. + * + * @return void + */ + public function mark_as_temporary() { + $this->set_saving_mode( 'temporary' ); + } + + /** + * Dump the data. Can be used to reconstruct the object. + * + * @return array The data to dump. + */ + public function dump_data() { + return array( + 'form_data' => $this->request_data, + 'uploaded_files_path' => $this->uploaded_files_path, + 'files_loaded_to_media_library' => $this->files_loaded_to_media_library, + 'keep_uploaded_files' => $this->keep_uploaded_files, + 'metadata' => $this->metadata, + ); + } + + /** + * Create a new instance from dumped data. + * + * @param array $dumped_data The dumped data. + * @return Form_Data_Request + */ + public static function create_from_dump( $dumped_data ) { + + $form = new self( null ); + + if ( ! empty( $dumped_data['form_data'] ) ) { + $form->set_data_from_request( $dumped_data['form_data'] ); + } + + if ( ! empty( $dumped_data['uploaded_files_path'] ) ) { + $form->set_uploaded_files_path( $dumped_data['uploaded_files_path'] ); + } + + if ( ! empty( $dumped_data['files_loaded_to_media_library'] ) ) { + $form->set_files_loaded_to_media_library( $dumped_data['files_loaded_to_media_library'] ); + } + + if ( ! empty( $dumped_data['keep_uploaded_files'] ) ) { + $form->set_keep_uploaded_files( $dumped_data['keep_uploaded_files'] ); + } + + if ( ! empty( $dumped_data['metadata'] ) ) { + $form->metadata = $dumped_data['metadata']; + } + + return $form; + } + + /** + * Append the frontend metadata to the response. + * + * @param Form_Data_Response $response The response. + * @return void + */ + public function append_metadata( $response ) { + foreach ( $this->metadata as $key => $value ) { + if ( 0 === strpos( $key, 'frontend_' ) ) { + $response->add_response_field( $key, $value ); + } + } + } + + /** + * Check if field is present in the metadata. + * + * @param string $key The key. + * @return bool + */ + public function has_metadata( $key ) { + return isset( $this->metadata[ $key ] ); + } } diff --git a/inc/integrations/api/form-response-data.php b/inc/integrations/api/form-response-data.php index c2be56dae..0d93c372d 100644 --- a/inc/integrations/api/form-response-data.php +++ b/inc/integrations/api/form-response-data.php @@ -30,6 +30,7 @@ class Form_Data_Response { const ERROR_MISSING_FILE_FIELD_OPTION = '16'; const ERROR_AUTORESPONDER_MISSING_EMAIL_FIELD = '17'; const ERROR_AUTORESPONDER_COULD_NOT_SEND = '18'; + const ERROR_MALFORMED_REQUEST = '19'; // Request validation errors. const ERROR_MISSING_DATA = '101'; @@ -44,6 +45,7 @@ class Form_Data_Response { const ERROR_BOT_DETECTED = '110'; const ERROR_FILES_METADATA_FORMAT = '111'; const ERROR_FILE_MISSING_BINARY = '112'; + const ERROR_MISSING_DUMP_DATA = '113'; @@ -57,6 +59,12 @@ class Form_Data_Response { const ERROR_PROVIDER_INVALID_EMAIL = '207'; const ERROR_PROVIDER_DUPLICATED_EMAIL = '208'; const ERROR_PROVIDER_CREDENTIAL_ERROR = '209'; + const ERROR_WEBHOOK_COULD_NOT_TRIGGER = '210'; + const ERROR_RUNTIME_STRIPE_SESSION_VALIDATION = '300'; + const ERROR_STRIPE_CHECKOUT_SESSION_CREATION = '301'; + const ERROR_STRIPE_CHECKOUT_SESSION_NOT_FOUND = '302'; + const ERROR_STRIPE_PAYMENT_UNPAID = '303'; + const ERROR_STRIPE_METADATA_RECORD_NOT_FOUND = '304'; /** @@ -76,6 +84,13 @@ class Form_Data_Response { */ protected $is_credential_error = false; + /** + * The REST response. + * + * @var WP_REST_Response|WP_Error|WP_HTTP_Response|null + */ + public $rest_response = null; + /** * Constructor. * @@ -151,7 +166,7 @@ public function set_reasons( $reasons ) { /** * Set success message. - * + * * @param string $message The message. * @since 2.4 */ @@ -177,12 +192,25 @@ public function is_success() { * @since 2.0.3 */ public function build_response() { - // TODO: We can to addition operation when returning the response. + if ( isset( $this->rest_response ) ) { + $this->rest_response->set_data( $this->response ); + return $this->rest_response; + } + $this->process_error_code(); return rest_ensure_response( $this->response ); } + /** + * Create the REST response. + * + * @return void + */ + public function make_internal_response() { + $this->rest_response = $this->build_response(); + } + /** * Mark response as success. * @@ -356,6 +384,14 @@ public static function get_error_code_message( $error_code ) { self::ERROR_AUTORESPONDER_MISSING_EMAIL_FIELD => __( 'The email field is missing from the Form Block with Autoresponder activated.', 'otter-blocks' ), self::ERROR_AUTORESPONDER_COULD_NOT_SEND => __( 'The email from Autoresponder could not be sent.', 'otter-blocks' ), self::ERROR_FILE_MISSING_BINARY => __( 'The file data is missing.', 'otter-blocks' ), + self::ERROR_MALFORMED_REQUEST => __( 'The request is malformed.', 'otter-blocks' ), + self::ERROR_WEBHOOK_COULD_NOT_TRIGGER => __( 'The webhook could not be triggered.', 'otter-blocks' ), + self::ERROR_MISSING_DUMP_DATA => __( 'The form dump data is missing.', 'otter-blocks' ), + self::ERROR_STRIPE_CHECKOUT_SESSION_CREATION => __( 'The Stripe Checkout session could not be created.', 'otter-blocks' ), + self::ERROR_STRIPE_CHECKOUT_SESSION_NOT_FOUND => __( 'The Stripe Checkout session was not found.', 'otter-blocks' ), + self::ERROR_STRIPE_PAYMENT_UNPAID => __( 'The payment was not completed.', 'otter-blocks' ), + self::ERROR_STRIPE_METADATA_RECORD_NOT_FOUND => __( 'The metadata submission record was not found.', 'otter-blocks' ), + self::ERROR_RUNTIME_STRIPE_SESSION_VALIDATION => __( 'The payment has been processed. You will be contacted by the support team.', 'otter-blocks' ), ); if ( ! isset( $error_messages[ $error_code ] ) ) { diff --git a/inc/integrations/class-form-email.php b/inc/integrations/class-form-email.php index cad215e9e..c73c06776 100644 --- a/inc/integrations/class-form-email.php +++ b/inc/integrations/class-form-email.php @@ -118,7 +118,7 @@ public function build_head() { * @since 2.0.3 */ public function build_body( $form_data ) { - $email_form_content = $form_data->get_form_inputs(); + $email_form_content = $form_data->get_fields(); $content = ''; $attachment_links = ''; @@ -282,7 +282,7 @@ public function build_test_email( $form_data ) { esc_html__( 'Mail From: ', 'otter-blocks' ), sanitize_email( get_site_option( 'admin_email' ) ), esc_html( __( 'This a test email. If you receive this email, your SMTP set-up is working for sending emails via Form Block.', 'otter-blocks' ) ), - $form_data->get_payload_field( 'site' ) + $form_data->get_data_from_payload( 'site' ) ); } diff --git a/inc/integrations/class-form-field-option-data.php b/inc/integrations/class-form-field-wp-option-data.php similarity index 70% rename from inc/integrations/class-form-field-option-data.php rename to inc/integrations/class-form-field-wp-option-data.php index 552f6fe8a..1994d0a94 100644 --- a/inc/integrations/class-form-field-option-data.php +++ b/inc/integrations/class-form-field-wp-option-data.php @@ -13,7 +13,7 @@ * @package ThemeIsle\GutenbergBlocks\Integration * @since 2.2.3 */ -class Form_Field_Option_Data { +class Form_Field_WP_Option_Data { /** * The name of the field option. @@ -36,17 +36,22 @@ class Form_Field_Option_Data { */ protected $options = array(); + /** + * The stripe data of the field option. + * + * @var array + */ + protected $stripe_product_info = array(); + /** * Form_Field_Option_Data constructor. * * @param string $field_option_name The name of the field option. * @param string $field_option_type The type of the field option. - * @param array $options The options of the field option. */ - public function __construct( $field_option_name = '', $field_option_type = '', $options = array() ) { + public function __construct( $field_option_name = '', $field_option_type = '' ) { $this->field_option_name = $field_option_name; $this->field_option_type = $field_option_type; - $this->options = $options; } /** @@ -92,13 +97,17 @@ public function get_option( $option_name ) { } /** - * Set the option of the field option. + * Get the stripe data of the field option. * - * @param string $option_name The name of the option. - * @param mixed $option_value The value of the option. + * @return array The stripe data of the field option: + * [ + * 'product' => (string) The ID of the product, + * 'price' => (string) The price ID of the product, + * 'quantity' => (int) The quantity of the product to order + * ] */ - public function set_option( $option_name, $option_value ) { - $this->options[ $option_name ] = $option_value; + public function get_stripe_product_info() { + return $this->stripe_product_info; } /** @@ -128,6 +137,24 @@ public function set_name( $field_option_name ) { $this->field_option_name = $field_option_name; } + /** + * Set the stripe product data of the field option. + * + * @param array $stripe_product_info The stripe product data of the field option. + * @return void + */ + public function set_stripe_product_info( $stripe_product_info ) { + if ( ! is_array( $stripe_product_info ) ) { + return; + } + + if ( ! isset( $stripe_product_info['product'] ) || ! isset( $stripe_product_info['price'] ) ) { + return; + } + + $this->stripe_product_info = $stripe_product_info; + } + /** * Check if the field option has the option. * @@ -147,6 +174,8 @@ public function has_options() { return ! empty( $this->options ); } + + /** * Check if the field option has type. * @@ -164,4 +193,13 @@ public function has_type() { public function has_name() { return ! empty( $this->field_option_name ); } + + /** + * Check if the field option has stripe product data. + * + * @return bool + */ + public function has_stripe_product_info() { + return ! empty( $this->stripe_product_info ); + } } diff --git a/inc/integrations/class-form-providers.php b/inc/integrations/class-form-providers.php index 6e76c8306..c36a4eda1 100644 --- a/inc/integrations/class-form-providers.php +++ b/inc/integrations/class-form-providers.php @@ -92,7 +92,7 @@ public function register_providers( $new_providers ) { * @since 2.0.3 */ public function select_provider_from_form_options( $form_request ) { - $form_options = $form_request->get_form_options(); + $form_options = $form_request->get_wp_options(); if ( $form_options->has_provider() && $form_options->has_credentials() ) { return $this->get_provider_handlers( $form_options->get_provider() ); } diff --git a/inc/integrations/class-form-settings-data.php b/inc/integrations/class-form-settings-data.php index 21c27cb1e..edd1c62b3 100644 --- a/inc/integrations/class-form-settings-data.php +++ b/inc/integrations/class-form-settings-data.php @@ -121,6 +121,20 @@ class Form_Settings_Data { */ private $submissions_save_location = ''; + /** + * The webhook ID. + * + * @var string + */ + private $webhook_id = ''; + + /** + * The required fields. + * + * @var array + */ + private $required_fields = array(); + /** * The default constructor. * @@ -241,6 +255,12 @@ public static function get_form_setting_from_wordpress_options( $form_option ) { $integration->set_submissions_save_location( 'database-email' ); } $integration->set_meta( $form ); + if ( isset( $form['webhookId'] ) ) { + $integration->set_webhook_id( $form['webhookId'] ); + } + if ( isset( $form['requiredFields'] ) && is_array( $form['requiredFields'] ) ) { + $integration->set_required_fields( $form['requiredFields'] ); + } } } return $integration; @@ -644,6 +664,15 @@ public function get_autoresponder() { return $this->autoresponder; } + /** + * Get the webhook id. + * + * @return string + */ + public function get_webhook_id() { + return $this->webhook_id; + } + /** * Set the autoresponder. * @@ -675,4 +704,47 @@ public function set_submissions_save_location( $submissions_save_location ) { $this->submissions_save_location = $submissions_save_location; return $this; } + + /** + * Set the webhook ID. + * + * @param string $webhook_id The webhook ID. + * @return $this + */ + private function set_webhook_id( $webhook_id ) { + if ( ! empty( $webhook_id ) ) { + $this->webhook_id = $webhook_id; + } + return $this; + } + + /** + * Set the required fields. + * + * @param array $required_fields The required fields. + * @return $this + */ + public function set_required_fields( $required_fields ) { + + $this->required_fields = $required_fields; + return $this; + } + + /** + * Get the required fields. + * + * @return array + */ + public function get_required_fields() { + return $this->required_fields; + } + + /** + * Check if the form has required fields. + * + * @return bool + */ + public function has_required_fields() { + return ! empty( $this->required_fields ); + } } diff --git a/inc/integrations/class-form-utils.php b/inc/integrations/class-form-utils.php index 86a1f6094..611308b9c 100644 --- a/inc/integrations/class-form-utils.php +++ b/inc/integrations/class-form-utils.php @@ -40,9 +40,14 @@ public static function generate_test_email() { 'eight', 'nine', 'ten', + 'eleven', + 'twelve', + 'thirteen', + 'fourteen', + 'fifteen', ); - $name_1 = $words[ wp_rand( 0, count( $words ) ) ]; + $name_1 = $words[ wp_rand( 0, count( $words ) - 1 ) ]; $name_2 = $words[ wp_rand( 2, count( $words ) ) - 1 ]; return "Otter-Form-successfully-connected.delete-on-confirmation.$name_1.$name_2@otter-blocks.com"; diff --git a/inc/integrations/interfaces/interface-form-subscribe-service.php b/inc/integrations/interfaces/interface-form-subscribe-service.php index 8f3b482e9..d14785f0c 100644 --- a/inc/integrations/interfaces/interface-form-subscribe-service.php +++ b/inc/integrations/interfaces/interface-form-subscribe-service.php @@ -51,10 +51,10 @@ public function extract_data_from_integration( $wp_options_form ); public static function validate_api_key( $api_key ); /** - * Test if the service is set up by registering a random email address on the contact list. + * Make a request that add the email to the contact list. * - * @return mixed - * @since 2.0.3 + * @param string $email The email address. + * @return array|\WP_Error The response from Mailchimp. */ - public function test_subscription(); + public function make_subscribe_request( $email ); } diff --git a/inc/integrations/providers/class-mailchimp.php b/inc/integrations/providers/class-mailchimp.php index 80ffea446..a03b3849e 100644 --- a/inc/integrations/providers/class-mailchimp.php +++ b/inc/integrations/providers/class-mailchimp.php @@ -111,7 +111,7 @@ function( $item ) { * @param string $email The email address. * @return array|\WP_Error The response from Mailchimp. */ - private function make_subscribe_request( $email ) { + public function make_subscribe_request( $email ) { $user_status = $this->get_new_user_status_mailchimp( $this->list_id ); $url = 'https://' . $this->server_name . '.api.mailchimp.com/3.0/lists/' . $this->list_id . '/members/' . md5( strtolower( $email ) ); @@ -141,7 +141,7 @@ private function make_subscribe_request( $email ) { */ public function subscribe( $form_data ) { - $email = $form_data->get_email_from_form_input(); + $email = $form_data->get_first_email_from_input_fields(); $response = $this->make_subscribe_request( $email ); $body = json_decode( wp_remote_retrieve_body( $response ), true ); @@ -161,23 +161,6 @@ public function subscribe( $form_data ) { return $form_data; } - /** - * Test the subscription by registering a random generated email. - * - * @return Form_Data_Request - * @since 2.0.3 - */ - public function test_subscription() { - $req = new Form_Data_Request(); - $response = $this->make_subscribe_request( Form_Utils::generate_test_email() ); - - if ( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) { - $req->set_error( Form_Data_Response::get_error_code_message( Form_Data_Response::ERROR_PROVIDER_SUBSCRIBE_ERROR ) ); - } - - return $req; - } - /** * Set the API Key. * @@ -282,8 +265,8 @@ private function get_new_user_status_mailchimp( $list_id ) { * @since 2.0.3 */ public function get_information_from_provider( $request ) { - if ( $request->is_set( 'action' ) ) { - if ( $request->get( 'action' ) == 'listId' ) { + if ( $request->is_root_data_set( 'action' ) ) { + if ( $request->get_root_data( 'action' ) == 'listId' ) { return $this->get_lists(); } } diff --git a/inc/integrations/providers/class-sendinblue.php b/inc/integrations/providers/class-sendinblue.php index 63f8a4cd3..38c5cca4a 100644 --- a/inc/integrations/providers/class-sendinblue.php +++ b/inc/integrations/providers/class-sendinblue.php @@ -133,7 +133,7 @@ public function subscribe( $form_data ) { return $form_data; } - $email = $form_data->get_email_from_form_input(); + $email = $form_data->get_first_email_from_input_fields(); $response = $this->make_subscribe_request( $email ); $body = json_decode( wp_remote_retrieve_body( $response ), true ); @@ -154,22 +154,6 @@ public function subscribe( $form_data ) { return $form_data; } - /** - * Test the subscription by registering a random generated email. - * - * @return Form_Data_Request - */ - public function test_subscription() { - $req = new Form_Data_Request(); - $response = $this->make_subscribe_request( Form_Utils::generate_test_email() ); - - if ( is_wp_error( $response ) || 400 === wp_remote_retrieve_response_code( $response ) ) { - $req->set_error( Form_Data_Response::get_error_code_message( Form_Data_Response::ERROR_PROVIDER_SUBSCRIBE_ERROR ) ); - } - - return $req; - } - /** * Set the API Key * @@ -236,8 +220,8 @@ public function set_list_id( $list_id ) { * @since 2.0.3 */ public function get_information_from_provider( $request ) { - if ( $request->is_set( 'action' ) ) { - if ( $request->get( 'action' ) == 'listId' ) { + if ( $request->is_root_data_set( 'action' ) ) { + if ( $request->get_root_data( 'action' ) == 'listId' ) { return $this->get_lists(); } } diff --git a/inc/plugins/class-dashboard.php b/inc/plugins/class-dashboard.php index 8ee25a375..6284f49eb 100644 --- a/inc/plugins/class-dashboard.php +++ b/inc/plugins/class-dashboard.php @@ -28,6 +28,11 @@ public function init() { add_action( 'admin_menu', array( $this, 'register_menu_page' ) ); add_action( 'admin_init', array( $this, 'maybe_redirect' ) ); add_action( 'admin_notices', array( $this, 'maybe_add_otter_banner' ), 30 ); + + $form_options = get_option( 'themeisle_blocks_form_emails' ); + if ( ! empty( $form_options ) ) { + add_action( 'wp_dashboard_setup', array( $this, 'form_submissions_widget' ) ); + } } /** @@ -271,6 +276,321 @@ private function the_otter_banner() { 'otter_form_record', + 'posts_per_page' => 5, + ); + + if ( 'all' !== $posts_filter ) { + $query_args['post_status'] = $posts_filter; + } + + $query = new \WP_Query( $query_args ); + + + $records_count = wp_count_posts( 'otter_form_record' ); + + $count = $records_count->read + $records_count->unread; + + if ( 'read' === $posts_filter ) { + $count = $records_count->read; + } elseif ( 'unread' === $posts_filter ) { + $count = $records_count->unread; + } + + if ( $query->have_posts() ) { + + while ( $query->have_posts() ) { + $query->the_post(); + + $meta = get_post_meta( get_the_ID(), 'otter_form_record_meta', true ); + + $title = null; + $date = null; + + if ( isset( $meta['post_id']['value'] ) ) { + $date = get_the_date( 'F j, H:i', $meta['post_id']['value'] ); + } + + if ( isset( $meta['inputs'] ) && is_array( $meta['inputs'] ) ) { + // Find the first email field and use that as the title. + foreach ( $meta['inputs'] as $input ) { + if ( isset( $input['type'] ) && 'email' === $input['type'] && ! empty( $input['value'] ) ) { + $title = $input['value']; + break; + } + } + } + + + if ( ! $title ) { + + if ( isset( $meta['post_id']['value'] ) ) { + $title = __( 'Submission', 'otter-blocks' ) . ' #' . get_the_ID(); + } else { + $title = __( 'No title', 'otter-blocks' ); + } + } + + $entries[] = array( + 'title' => $title, + 'date' => $date, + ); + } + } + } + + ?> + + + + +
+ + +
+
+ Otter Logo +
+ +
+

+ +
+
+ + +
+
+
+ + + + : + + + + +
+ + +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+ +
+ +
+
+ + +
+ array( 'type' => 'string', ), + 'webhookId' => array( + 'type' => 'string', + ), + 'requiredFields' => array( + 'type' => 'array', + 'items' => array( + 'type' => 'string', + ), + ), ), ), ), @@ -433,6 +450,18 @@ function ( $item ) { $item['options']['maxFilesNumber'] = sanitize_text_field( $item['options']['maxFilesNumber'] ); } + if ( isset( $item['stripe']['product'] ) ) { + $item['stripe']['product'] = sanitize_text_field( $item['stripe']['product'] ); + } + + if ( isset( $item['stripe']['price'] ) ) { + $item['stripe']['price'] = sanitize_text_field( $item['stripe']['price'] ); + } + + if ( isset( $item['stripe']['quantity'] ) && ! is_int( $item['stripe']['quantity'] ) ) { + $item['stripe']['quantity'] = sanitize_text_field( $item['stripe']['quantity'] ); + } + return $item; }, $array @@ -471,6 +500,22 @@ function ( $item ) { ), 'default' => array(), ), + 'stripe' => array( + 'type' => 'object', + 'properties' => array( + 'product' => array( + 'type' => 'string', + ), + 'price' => array( + 'type' => 'string', + ), + 'quantity' => array( + 'type' => 'number', + 'default' => 1, + ), + ), + + ), ), ), ), @@ -511,6 +556,141 @@ function ( $item ) { ), ) ); + + register_setting( + 'themeisle_blocks_settings', + 'themeisle_webhooks_options', + array( + 'type' => 'array', + 'description' => __( 'Otter Registered Webhooks.', 'otter-blocks' ), + 'sanitize_callback' => function ( $array ) { + return array_map( + function ( $item ) { + if ( isset( $item['id'] ) ) { + $item['id'] = sanitize_text_field( $item['id'] ); + } + if ( isset( $item['url'] ) ) { + $item['url'] = esc_url_raw( $item['url'] ); + } + if ( isset( $item['name'] ) ) { + $item['name'] = sanitize_text_field( $item['name'] ); + } + if ( isset( $item['method'] ) ) { + $item['method'] = sanitize_text_field( $item['method'] ); + } + if ( isset( $item['headers'] ) && is_array( $item['headers'] ) ) { + foreach ( $item['headers'] as &$header ) { + $header['key'] = sanitize_text_field( $header['key'] ); + $header['value'] = sanitize_text_field( $header['value'] ); + } + } else { + $item['headers'] = array(); + } + return $item; + }, + $array + ); + }, + 'show_in_rest' => array( + 'schema' => array( + 'type' => 'array', + 'items' => array( + 'type' => 'object', + 'properties' => array( + 'id' => array( + 'type' => 'string', + ), + 'url' => array( + 'type' => 'string', + ), + 'headers' => array( + 'type' => 'array', + 'items' => array( + 'type' => 'object', + 'properties' => array( + 'key' => array( + 'type' => 'string', + ), + 'value' => array( + 'type' => 'string', + ), + ), + ), + ), + 'name' => array( + 'type' => 'string', + ), + 'method' => array( + 'type' => 'string', + ), + ), + ), + ), + ), + ) + ); + + register_setting( + 'themeisle_blocks_settings', + 'themeisle_open_ai_api_key', + array( + 'type' => 'string', + 'description' => __( 'The OpenAI API Key required for usage of Otter AI features.', 'otter-blocks' ), + 'sanitize_callback' => 'sanitize_text_field', + 'show_in_rest' => true, + 'default' => '', + ) + ); + + register_setting( + 'themeisle_blocks_settings', + 'themeisle_otter_ai_usage', + array( + 'type' => 'object', + 'description' => __( 'Usage of Otter AI features.', 'otter-blocks' ), + 'show_in_test' => array( + 'schema' => array( + 'type' => 'object', + 'properties' => array( + 'usage_count' => array( + 'type' => 'array', + 'items' => array( + 'type' => 'object', + 'properties' => array( + 'key' => array( + 'type' => 'string', + ), + 'value' => array( + 'type' => 'string', + ), + ), + ), + 'default' => array(), + ), + 'prompts' => array( + 'type' => 'array', + 'items' => array( + 'type' => 'object', + 'properties' => array( + 'key' => array( + 'type' => 'string', + ), + 'values' => array( + 'type' => 'array', + 'items' => array( + 'type' => 'string', + ), + ), + ), + ), + 'default' => array(), + ), + ), + ), + ), + 'default' => array(), + ) + ); } /** diff --git a/inc/render/class-form-multiple-choice.php b/inc/render/class-form-multiple-choice.php index 70a96299c..46809f755 100644 --- a/inc/render/class-form-multiple-choice.php +++ b/inc/render/class-form-multiple-choice.php @@ -32,11 +32,12 @@ public function render( $attributes ) { $options_array = explode( "\n", $options ); $is_required = isset( $attributes['isRequired'] ) ? boolval( $attributes['isRequired'] ) : false; $has_multiple_selection = isset( $attributes['multipleSelection'] ) ? boolval( $attributes['multipleSelection'] ) : false; + $mapped_name = isset( $attributes['mappedName'] ) ? $attributes['mappedName'] : $id; $output = '
'; if ( 'select' === $field_type ) { - $output .= $this->render_select_field( $label, $options_array, $id, $has_multiple_selection, $is_required ); + $output .= $this->render_select_field( $label, $options_array, $id, $mapped_name, $has_multiple_selection, $is_required ); } else { $output .= ''; @@ -50,7 +51,7 @@ public function render( $attributes ) { $field_value = implode( '_', explode( ' ', sanitize_title( $field_label ) ) ); $field_id = 'field-' . $field_value; - $output .= $this->render_field( $field_type, $field_label, $field_value, $id, $field_id, $is_required ); + $output .= $this->render_field( $field_type, $field_label, $field_value, $mapped_name, $field_id, $is_required ); } $output .= '
'; @@ -90,13 +91,14 @@ public function render_field( $type, $label, $value, $name, $id, $is_required = * @param string $label The label of the field. * @param array $options_array The options of the field. * @param string $id The id of the field. + * @param string $name The name of the field. * @param bool $is_multiple The multiple status of the field. * @param bool $is_required The required status of the field. * @return string */ - public function render_select_field( $label, $options_array, $id, $is_multiple, $is_required ) { + public function render_select_field( $label, $options_array, $id, $name, $is_multiple, $is_required ) { $output = ''; - $output .= ''; foreach ( $options_array as $field_label ) { @@ -114,7 +116,7 @@ public function render_select_field( $label, $options_array, $id, $is_multiple, /** * Render the required sign. - * + * * @param bool $is_required The required status of the field. * @return string */ diff --git a/inc/render/class-review-block.php b/inc/render/class-review-block.php index b12b07ce2..f682983e7 100644 --- a/inc/render/class-review-block.php +++ b/inc/render/class-review-block.php @@ -193,7 +193,7 @@ function() use ( $attributes, $post_id ) { foreach ( $attributes['links'] as $link ) { $rel = ( isset( $link['isSponsored'] ) && true === $link['isSponsored'] ) ? 'sponsored' : 'nofollow'; - $html .= ' ' . esc_html( $link['label'] ) . ''; + $html .= ' ' . esc_html( $link['label'] ) . ''; } $html .= ' '; $html .= ' '; diff --git a/inc/server/class-dashboard-server.php b/inc/server/class-dashboard-server.php index d3ea489c7..a6798a7c1 100644 --- a/inc/server/class-dashboard-server.php +++ b/inc/server/class-dashboard-server.php @@ -38,6 +38,7 @@ class Dashboard_Server { */ public function init() { add_action( 'rest_api_init', array( $this, 'register_routes' ) ); + add_action( 'after_switch_theme', array( $this, 'regenerate_styles_on_theme_change' ) ); } /** @@ -65,7 +66,7 @@ public function register_routes() { * Regenerate styles. * * @param \WP_REST_Request $request The request. - * + * * @return \WP_REST_Response * @since 2.0.9 * @access public @@ -74,6 +75,15 @@ public function rest_regenerate_styles( \WP_REST_Request $request ) { return self::regenerate_styles(); } + /** + * Regenerate styles on theme change. + * + * @since 2.3 + */ + public function regenerate_styles_on_theme_change() { + self::regenerate_styles(); + } + /** * Function to delete Otter generated styles. * diff --git a/inc/server/class-form-server.php b/inc/server/class-form-server.php index a5f351685..434765282 100644 --- a/inc/server/class-form-server.php +++ b/inc/server/class-form-server.php @@ -11,12 +11,13 @@ use ThemeIsle\GutenbergBlocks\Integration\Form_Data_Request; use ThemeIsle\GutenbergBlocks\Integration\Form_Data_Response; use ThemeIsle\GutenbergBlocks\Integration\Form_Email; -use ThemeIsle\GutenbergBlocks\Integration\Form_Field_Option_Data; +use ThemeIsle\GutenbergBlocks\Integration\Form_Field_WP_Option_Data; use ThemeIsle\GutenbergBlocks\Integration\Form_Providers; use ThemeIsle\GutenbergBlocks\Integration\Form_Settings_Data; use ThemeIsle\GutenbergBlocks\Integration\Form_Utils; use ThemeIsle\GutenbergBlocks\Integration\Mailchimp_Integration; use ThemeIsle\GutenbergBlocks\Integration\Sendinblue_Integration; +use ThemeIsle\GutenbergBlocks\Plugins\Stripe_API; use ThemeIsle\GutenbergBlocks\Pro; use WP_Error; use WP_HTTP_Response; @@ -146,6 +147,9 @@ public function init() { */ add_action( 'otter_form_after_submit', array( $this, 'after_submit' ) ); add_action( 'otter_form_after_submit', array( $this, 'send_error_email_to_admin' ), 999 ); + + add_action( 'otter_form_on_submission_confirmed', array( $this, 'apply_main_provider' ) ); + add_filter( 'otter_form_session_confirmation', array( $this, 'verify_confirmation_session' ) ); } /** @@ -176,6 +180,36 @@ public function register_routes() { ), ) ); + register_rest_route( + $namespace, + '/form/confirm', + array( + array( + 'methods' => WP_REST_Server::READABLE, + 'callback' => array( $this, 'confirm_form_submission' ), + 'permission_callback' => function ( $request ) { + $session = $request->get_param( 'stripe_checkout' ); + + if ( apply_filters( 'otter_form_session_confirmation', $session ) ) { + return __return_true(); + } + return false; + }, + 'args' => array( + 'record_id' => array( + 'validate_callback' => function( $param, $request, $key ) { + return is_numeric( $param ); + }, + ), + 'session' => array( + 'validate_callback' => function( $param, $request, $key ) { + return is_string( $param ); + }, + ), + ), + ), + ) + ); register_rest_route( $namespace, '/form/editor', @@ -199,22 +233,26 @@ public function register_routes() { * @since 2.0.3 */ public function editor( $request ) { - $data = new Form_Data_Request( json_decode( $request->get_body(), true ) ); + $data = new Form_Data_Request( $request ); $res = new Form_Data_Response(); - $form_options = Form_Settings_Data::get_form_setting_from_wordpress_options( $data->get_payload_field( 'formOption' ) ); + $form_options = Form_Settings_Data::get_form_setting_from_wordpress_options( $data->get_data_from_payload( 'formOption' ) ); $data->set_form_options( $form_options ); // Select the handler functions based on the provider. $provider = $form_options->get_provider(); - if ( $data->payload_has_field( 'provider' ) ) { - $provider = $data->get_payload_field( 'provider' ); + if ( $data->payload_has( 'provider' ) ) { + $provider = $data->get_data_from_payload( 'provider' ); } $provider_handlers = Form_Providers::$instance->get_provider_handlers( $provider, 'editor' ); - if ( $provider_handlers && Form_Providers::provider_has_handler( $provider_handlers, $data->get( 'handler' ) ) ) { + if ( $data->has_error() ) { + return $res->set_code( $data->get_error_code() )->build_response(); + } + + if ( $provider_handlers && Form_Providers::provider_has_handler( $provider_handlers, $data->get_root_data( 'handler' ) ) ) { // Send the data to the provider. - return $provider_handlers[ $data->get( 'handler' ) ]( $data ); + return $provider_handlers[ $data->get_root_data( 'handler' ) ]( $data ); } else { $res->set_error( __( 'The email service provider was not registered!', 'otter-blocks' ) ); } @@ -235,14 +273,16 @@ public function frontend( $request ) { $res = new Form_Data_Response(); $form_data = new Form_Data_Request( $request ); + // Validate the form data. + $form_data = apply_filters( 'otter_form_validate_form', $form_data ); + + $form_options = Form_Settings_Data::get_form_setting_from_wordpress_options( $form_data->get_data_from_payload( 'formOption' ) ); + $form_data->set_form_options( $form_options ); + $form_data = self::pull_fields_options_for_form( $form_data ); + try { - // Validate the form data. - $form_data = apply_filters( 'otter_form_data_validation', $form_data ); - $form_options = Form_Settings_Data::get_form_setting_from_wordpress_options( $form_data->get_payload_field( 'formOption' ) ); - $form_data->set_form_options( $form_options ); - $form_data = $this->pull_fields_options_for_form( $form_data ); // Check bot validation. $form_data = apply_filters( 'otter_form_anti_spam_validation', $form_data ); @@ -264,16 +304,7 @@ public function frontend( $request ) { $res->add_response_field( 'redirectLink', $form_options->get_redirect_link() ); } - // Select the submit function based on the provider. - $provider_handlers = apply_filters( 'otter_select_form_provider', $form_data ); - - if ( $provider_handlers && Form_Providers::provider_has_handler( $provider_handlers, $form_data->get( 'handler' ) ) ) { - - // Send the data to the provider handler. - $form_data = $provider_handlers[ $form_data->get( 'handler' ) ]( $form_data ); - } else { - $res->set_code( Form_Data_Response::ERROR_PROVIDER_NOT_REGISTERED ); - } + $this->apply_main_provider( $form_data ); // Send the data to the provider. do_action( 'otter_form_after_submit', $form_data ); @@ -297,10 +328,58 @@ public function frontend( $request ) { $form_data->set_error( Form_Data_Response::ERROR_RUNTIME_ERROR, $e->getMessage() ); $this->send_error_email( $form_data ); } finally { + $form_data->append_metadata( $res ); return $res->build_response(); } } + /** + * Confirm form submission. + * + * @param WP_REST_Request $request Form request. + * @return WP_Error|WP_HTTP_Response|WP_REST_Response + */ + public function confirm_form_submission( $request ) { + + $response = new Form_Data_Response(); + + try { + $response = apply_filters( 'otter_form_record_confirm', $response, $request ); + } catch ( Exception $e ) { + $response->set_code( Form_Data_Response::ERROR_RUNTIME_STRIPE_SESSION_VALIDATION ); + $response->add_reason( $e->getMessage() ); + } finally { + return $response->build_response(); + } + } + + /** + * Apply the main provider. + * + * @param Form_Data_Request|null $form_data Form data. + * @return void + */ + public function apply_main_provider( $form_data ) { + + if ( ! isset( $form_data ) ) { + return; + } + + if ( $form_data->has_error() || $form_data->is_temporary() ) { + return; + } + + $provider_handlers = apply_filters( 'otter_select_form_provider', $form_data ); + + if ( $provider_handlers && Form_Providers::provider_has_handler( $provider_handlers, $form_data->get_root_data( 'handler' ) ) ) { + + // Send the data to the provider handler. + $provider_handlers[ $form_data->get_root_data( 'handler' ) ]( $form_data ); + } else { + $form_data->set_error( Form_Data_Response::ERROR_PROVIDER_NOT_REGISTERED ); + } + } + /** * Send Email using SMTP. * @@ -319,7 +398,7 @@ public function send_default_email( $form_data ) { } try { - $form_options = $form_data->get_form_options(); + $form_options = $form_data->get_wp_options(); $can_send_email = substr( $form_options->get_submissions_save_location(), -strlen( 'email' ) ) === 'email'; @@ -336,8 +415,8 @@ public function send_default_email( $form_data ) { $to = sanitize_email( get_site_option( 'admin_email' ) ); // Check if we need to send it to another user email. - if ( $form_data->payload_has_field( 'formOption' ) ) { - $option_name = $form_data->get_payload_field( 'formOption' ); + if ( $form_data->payload_has( 'formOption' ) ) { + $option_name = $form_data->get_data_from_payload( 'formOption' ); $form_emails = get_option( 'themeisle_blocks_form_emails' ); foreach ( $form_emails as $form ) { @@ -463,10 +542,10 @@ public function change_provider_based_on_consent( $form_data ) { // If there is no consent, change the service to send only an email. if ( - 'submit-subscribe' === $form_data->get_form_options()->get_action() && + 'submit-subscribe' === $form_data->get_wp_options()->get_action() && ( - ! $form_data->payload_has_field( 'consent' ) || - ! $form_data->get_payload_field( 'consent' ) + ! $form_data->payload_has( 'consent' ) || + ! $form_data->get_data_from_payload( 'consent' ) ) ) { $form_data->change_provider( 'default' ); @@ -493,9 +572,9 @@ public function after_submit( $form_data ) { // Send also an email to the form editor/owner with the data alongside the subscription. if ( - 'submit-subscribe' === $form_data->get_form_options()->get_action() && - $form_data->get_form_options()->has_provider() && - 'default' !== $form_data->get_form_options()->get_provider() + 'submit-subscribe' === $form_data->get_wp_options()->get_action() && + $form_data->get_wp_options()->has_provider() && + 'default' !== $form_data->get_wp_options()->get_provider() ) { $this->send_default_email( $form_data ); } @@ -518,13 +597,13 @@ public function anti_spam_validation( $form_data ) { } if ( - $form_data->payload_has_field( 'antiSpamTime' ) && - is_numeric( $form_data->get_payload_field( 'antiSpamTime' ) ) && - $form_data->payload_has_field( 'antiSpamHoneyPot' ) + $form_data->payload_has( 'antiSpamTime' ) && + is_numeric( $form_data->get_data_from_payload( 'antiSpamTime' ) ) && + $form_data->payload_has( 'antiSpamHoneyPot' ) ) { if ( - $form_data->get_payload_field( 'antiSpamTime' ) >= self::ANTI_SPAM_TIMEOUT && - '' === $form_data->get_payload_field( 'antiSpamHoneyPot' ) + $form_data->get_data_from_payload( 'antiSpamTime' ) >= self::ANTI_SPAM_TIMEOUT && + '' === $form_data->get_data_from_payload( 'antiSpamHoneyPot' ) ) { return $form_data; } @@ -566,8 +645,8 @@ public static function send_test_email( $form_data ) { $email_body = Form_Email::instance()->build_test_email( $form_data ); // Sent the form date to the admin site as a default behaviour. $to = sanitize_email( get_site_option( 'admin_email' ) ); - if ( $form_data->payload_has_field( 'to' ) && '' !== $form_data->get_payload_field( 'to' ) ) { - $to = $form_data->get_payload_field( 'to' ); + if ( $form_data->payload_has( 'to' ) && '' !== $form_data->get_data_from_payload( 'to' ) ) { + $to = $form_data->get_data_from_payload( 'to' ); } $headers = array( 'Content-Type: text/html; charset=UTF-8', 'From: ' . get_bloginfo( 'name', 'display' ) . '<' . $to . '>' ); // phpcs:ignore WordPressVIPMinimum.Functions.RestrictedFunctions.wp_mail_wp_mail @@ -594,7 +673,7 @@ public function get_integration_data( $form_data ) { try { $service = null; - switch ( $form_data->get_payload_field( 'provider' ) ) { + switch ( $form_data->get_data_from_payload( 'provider' ) ) { case 'mailchimp': $service = new Mailchimp_Integration(); break; @@ -606,9 +685,9 @@ public function get_integration_data( $form_data ) { } if ( isset( $service ) ) { - $valid_api_key = $service::validate_api_key( $form_data->get_payload_field( 'apiKey' ) ); + $valid_api_key = $service::validate_api_key( $form_data->get_data_from_payload( 'apiKey' ) ); if ( $valid_api_key['valid'] ) { - $service->set_api_key( $form_data->get_payload_field( 'apiKey' ) ); + $service->set_api_key( $form_data->get_data_from_payload( 'apiKey' ) ); $res->set_response( $service->get_information_from_provider( $form_data ) ); } else { $res->set_code( $valid_api_key['code'] ); @@ -631,7 +710,7 @@ public function get_integration_data( $form_data ) { */ public function test_subscription_service( $form_data ) { $res = new Form_Data_Response(); - $form_options = Form_Settings_Data::get_form_setting_from_wordpress_options( $form_data->get_payload_field( 'formOption' ) ); + $form_options = Form_Settings_Data::get_form_setting_from_wordpress_options( $form_data->get_data_from_payload( 'formOption' ) ); try { $service = null; @@ -651,7 +730,13 @@ public function test_subscription_service( $form_data ) { if ( $valid_api_key['valid'] ) { if ( $form_options->has_list_id() ) { $service->set_api_key( $form_options->get_api_key() )->set_list_id( $form_options->get_list_id() ); - $res = $service->test_subscription(); + $response = $service->make_subscribe_request( Form_Utils::generate_test_email() ); + + if ( is_wp_error( $response ) ) { + $res->set_error( Form_Data_Response::ERROR_RUNTIME_ERROR, $response->get_error_message() ); + } else { + $res->mark_as_success(); + } } else { $res->set_error( __( 'Contact list ID is missing!', 'otter-blocks' ) ); } @@ -679,12 +764,12 @@ public function subscribe_to_service( $form_data ) { return $form_data; } - if ( $form_data->has_error() ) { + if ( $form_data->has_error() || $form_data->is_temporary() ) { return $form_data; } // Get the first email from form. - $email = $form_data->get_email_from_form_input(); + $email = $form_data->get_first_email_from_input_fields(); if ( '' === $email ) { $form_data->set_error( Form_Data_Response::ERROR_MISSING_EMAIL, __( 'Marketing Integration is active, but there is no Email field in the form. Please check your Form block settings in the page.', 'otter-blocks' ) ); @@ -692,16 +777,16 @@ public function subscribe_to_service( $form_data ) { } if ( - 'submit-subscribe' === $form_data->get_form_options()->get_action() && - $form_data->payload_has_field( 'consent' ) && - ! $form_data->get_payload_field( 'consent' ) + 'submit-subscribe' === $form_data->get_wp_options()->get_action() && + $form_data->payload_has( 'consent' ) && + ! $form_data->get_data_from_payload( 'consent' ) ) { return $form_data; } try { // Get the api credentials from the Form block. - $wp_options_form = $form_data->get_form_options(); + $wp_options_form = $form_data->get_wp_options(); $error_code = $wp_options_form->check_data(); @@ -746,14 +831,14 @@ public function subscribe_to_service( $form_data ) { */ public function has_required_data( $form_data ) { - $main_fields_set = $form_data->are_fields_set( + $main_fields_set = $form_data->are_root_data_set( array( 'handler', 'payload', ) ); - $required_payload_fields = $form_data->are_payload_fields_set( + $required_payload_fields = $form_data->are_payload_data_set( array( 'nonceValue', 'postUrl', @@ -762,7 +847,7 @@ public function has_required_data( $form_data ) { ) ); - $is_nonce_valid = wp_verify_nonce( $form_data->get_payload_field( 'nonceValue' ), 'form-verification' ); + $is_nonce_valid = wp_verify_nonce( $form_data->get_data_from_payload( 'nonceValue' ), 'form-verification' ); return $main_fields_set && $required_payload_fields && $is_nonce_valid; } @@ -813,24 +898,24 @@ public function check_form_captcha( $form_data ) { return $form_data; } - $form_options = $form_data->get_form_options(); + $form_options = $form_data->get_wp_options(); if ( $form_options->form_has_captcha() && ( - ! $form_data->payload_has_field( 'token' ) || - '' === $form_data->get_payload_field( 'token' ) + ! $form_data->payload_has( 'token' ) || + '' === $form_data->get_data_from_payload( 'token' ) ) ) { $form_data->set_error( Form_Data_Response::ERROR_MISSING_CAPTCHA ); } - if ( $form_data->payload_has_field( 'token' ) ) { + if ( $form_data->payload_has( 'token' ) ) { $secret = get_option( 'themeisle_google_captcha_api_secret_key' ); $resp = wp_remote_post( apply_filters( 'otter_blocks_recaptcha_verify_url', 'https://www.google.com/recaptcha/api/siteverify' ), array( - 'body' => 'secret=' . $secret . '&response=' . $form_data->get_payload_field( 'token' ), + 'body' => 'secret=' . $secret . '&response=' . $form_data->get_data_from_payload( 'token' ), 'headers' => [ 'Content-Type' => 'application/x-www-form-urlencoded', ], @@ -878,9 +963,9 @@ public function build_email_error_content( $content ) { * @since 2.0.3 */ public function get_email_from_form_input( Form_Data_Request $data ) { - $inputs = $data->get_payload_field( 'formInputsData' ); + $inputs = $data->get_data_from_payload( 'formInputsData' ); if ( is_array( $inputs ) ) { - foreach ( $data->get_payload_field( 'formInputsData' ) as $input_field ) { + foreach ( $inputs as $input_field ) { if ( isset( $input_field['type'] ) && 'email' == $input_field['type'] ) { return $input_field['value']; } @@ -906,7 +991,7 @@ public function check_form_files( $form_data ) { return $form_data; } - $inputs = $form_data->get_form_inputs(); + $inputs = $form_data->get_fields(); foreach ( $inputs as $input ) { if ( Form_Utils::is_file_field( $input ) && ! Form_Utils::is_file_field_valid( $input ) ) { @@ -924,7 +1009,7 @@ public function check_form_files( $form_data ) { * @param Form_Data_Request $form_data The form data. * @since 2.2.3 */ - public function pull_fields_options_for_form( $form_data ) { + public static function pull_fields_options_for_form( $form_data ) { if ( ! ( $form_data instanceof Form_Data_Request ) || $form_data->has_error() ) { return $form_data; } @@ -935,22 +1020,55 @@ public function pull_fields_options_for_form( $form_data ) { return $form_data; } - foreach ( $form_data->get_form_inputs() as $input ) { + foreach ( $form_data->get_fields() as $input ) { if ( isset( $input['metadata']['fieldOptionName'] ) ) { $field_name = $input['metadata']['fieldOptionName']; foreach ( $global_fields_options as $field ) { if ( isset( $field['fieldOptionName'] ) && $field['fieldOptionName'] === $field_name ) { - $new_field = new Form_Field_Option_Data( $field_name, $field['fieldOptionType'], $field['options'] ); - $form_data->add_field_option( $new_field ); + $new_field = new Form_Field_WP_Option_Data( $field_name, $field['fieldOptionType'] ); + if ( isset( $field['options'] ) ) { + $new_field->set_options( $field['options'] ); + } + if ( isset( $field['stripe'] ) ) { + $new_field->set_stripe_product_info( $field['stripe'] ); + } + $form_data->add_field_wp_option( $new_field ); break; } } } } + $required_fields = $form_data->get_wp_options()->get_required_fields(); + + foreach ( $required_fields as $required_field ) { + foreach ( $global_fields_options as $field ) { + if ( isset( $field['fieldOptionName'] ) && $field['fieldOptionName'] === $required_field ) { + $new_field = new Form_Field_WP_Option_Data( $required_field, $field['fieldOptionType'] ); + if ( isset( $field['options'] ) ) { + $new_field->set_options( $field['options'] ); + } + if ( isset( $field['stripe'] ) ) { + $new_field->set_stripe_product_info( $field['stripe'] ); + } + $form_data->add_field_wp_option( $new_field ); + break; + } + } + } + return $form_data; } + /** + * Verify the given confirmation session. + * + * @param string $session The session id. + * @return bool + */ + public function verify_confirmation_session( $session ) { + return ! empty( $session ) && is_string( $session ); + } /** * The instance method for the static class. * Defines and returns the instance of the static class. diff --git a/inc/server/class-prompt-server.php b/inc/server/class-prompt-server.php new file mode 100644 index 000000000..25198c8d9 --- /dev/null +++ b/inc/server/class-prompt-server.php @@ -0,0 +1,397 @@ +namespace . $this->version; + + register_rest_route( + $namespace, + '/prompt', + array( + array( + 'methods' => \WP_REST_Server::READABLE, + 'callback' => array( $this, 'get_prompts' ), + 'permission_callback' => function () { + return current_user_can( 'edit_posts' ); + }, + ), + ) + ); + + register_rest_route( + $namespace, + '/generate', + array( + array( + 'methods' => \WP_REST_Server::CREATABLE, + 'callback' => array( $this, 'forward_prompt' ), + 'permission_callback' => function () { + return current_user_can( 'edit_posts' ); + }, + ), + ) + ); + } + + /** + * Forward the prompt to OpenAI API. + * + * @param \WP_REST_Request $request Request object. + * @return \WP_Error|\WP_HTTP_Response|\WP_REST_Response + */ + public function forward_prompt( $request ) { + $open_ai_endpoint = 'https://api.openai.com/v1/chat/completions'; + + // Get the body from request and decode it. + $body = $request->get_body(); + $body = json_decode( $body, true ); + + $api_key = get_option( 'themeisle_open_ai_api_key' ); + + // Extract the data from keys that start with 'otter_'. + $otter_data = array_filter( + $body, + function ( $key ) { + return 0 === strpos( $key, 'otter_' ); + }, + ARRAY_FILTER_USE_KEY + ); + + // Remove the values which keys start with 'otter_'. + $body = array_diff_key( $body, $otter_data ); + + $response = wp_remote_post( + $open_ai_endpoint, + array( + 'method' => 'POST', + 'headers' => array( + 'Authorization' => 'Bearer ' . $api_key, + 'Content-Type' => 'application/json', + ), + 'body' => wp_json_encode( $body ), + 'timeout' => 2 * MINUTE_IN_SECONDS, + ) + ); + + if ( is_wp_error( $response ) ) { + return $response; + } + + $body = wp_remote_retrieve_body( $response ); + $body = json_decode( $body, true ); + + if ( json_last_error() !== JSON_ERROR_NONE ) { + return new \WP_Error( 'rest_invalid_json', __( 'Could not parse the response from OpenAI. Try again.', 'otter-blocks' ), array( 'status' => 400 ) ); + } + + return new \WP_REST_Response( $body, wp_remote_retrieve_response_code( $response ) ); + } + + /** + * Get prompts. + * + * @param \WP_REST_Request $request Request object. + * @return \WP_Error|\WP_HTTP_Response|\WP_REST_Response + */ + public function get_prompts( $request ) { + $response = array( + 'prompts' => array(), + 'code' => '0', + 'error' => '', + ); + + // Get the saved prompts. + $prompts = get_transient( $this->transient_prompts ); + + if ( false === $prompts ) { + /** + * If we don't have the prompts saved, we need to retrieve them from the server. Once retrieved, we save them in a transient and return them. + */ + $response = $this->retrieve_prompts_from_server(); + } + + if ( '0' === $response['code'] ) { + if ( $request->get_param( 'name' ) !== null ) { + $prompts = ! empty( $prompts ) ? $prompts : $response['prompts']; + + // Prompt can be filtered by name. By filtering by name, we can get only the prompt we need and save some bandwidth. + $single_prompt = array_values( + array_filter( + $prompts, + function ( $prompt ) use ( $request ) { + return $prompt['otter_name'] === $request->get_param( 'name' ); + } + ) + ); + + if ( empty( $single_prompt ) ) { + $response['prompts'] = $prompts; + $response['code'] = '1'; + $response['error'] = __( 'Something went wrong when preparing the data for this feature.', 'otter-blocks' ); + } else { + $response['prompts'] = $single_prompt; + } + } else { + $response['prompts'] = $prompts; + } + } + + + return rest_ensure_response( $response ); + } + + /** + * + * Retrieve prompts from server. + * + * @return array + */ + public function retrieve_prompts_from_server() { + + if ( false !== get_transient( $this->timeout_transient ) ) { + return array( + 'response' => array(), + 'code' => '3', + 'error' => __( 'Timeout is active. Please try again in', 'otter-blocks' ) . 5 . __( 'minutes.', 'otter-blocks' ), + ); + } + + $url = add_query_arg( + array( + 'site_url' => get_site_url(), + 'license_id' => apply_filters( 'product_otter_license_key', 'free' ), + 'cache' => gmdate( 'u' ), + 'isValid' => boolval( get_option( 'themeisle_open_ai_api_key', false ) ) ? 'true' : 'false', + ), + 'https://api.themeisle.com/templates-cloud/otter-prompts' + ); + + $response = ''; + + if ( function_exists( 'vip_safe_wp_remote_get' ) ) { + $response = vip_safe_wp_remote_get( esc_url_raw( $url ) ); + } else { + $response = wp_remote_get( esc_url_raw( $url ) ); // phpcs:ignore WordPressVIPMinimum.Functions.RestrictedFunctions.wp_remote_get_wp_remote_get + } + + $response = wp_remote_retrieve_body( $response ); + $response = json_decode( $response, true ); + + if ( ! is_array( $response ) || 0 === count( $response ) || ! $this->check_prompt_structure( $response ) ) { + set_transient( $this->timeout_transient, '1', 5 * MINUTE_IN_SECONDS ); + return array( + 'response' => array(), + 'code' => '2', + 'error' => __( 'Invalid data from central server. Please try again in', 'otter-blocks' ) . 5 . __( 'minutes.', 'otter-blocks' ), + ); + } + + set_transient( $this->transient_prompts, $response, WEEK_IN_SECONDS ); + + return array( + 'prompts' => $response, + 'code' => '0', + 'error' => '', + ); + } + + /** + * Check if the prompt structure is valid. + * + * @param mixed $response Response from the server. + * @return bool + */ + public function check_prompt_structure( $response ) { + if ( ! isset( $response ) ) { + return false; + } + + if ( ! is_array( $response ) ) { + return false; + } + + if ( 0 === count( $response ) ) { + return false; + } + + return true; + } + + /** + * Record prompt usage. + * + * @param array $otter_metadata The metadata from the prompt usage request. + * @return void + * @phpstan-ignore-next-line + */ + private function record_prompt_usage( $otter_metadata ) { + if ( ! isset( $otter_metadata['otter_used_action'] ) || ! isset( $otter_metadata['otter_user_content'] ) ) { + return; + } + + $action = $otter_metadata['otter_used_action']; + $user_content = $otter_metadata['otter_user_content']; + + $usage = get_option( 'themeisle_otter_ai_usage' ); + + if ( ! is_array( $usage ) ) { + $usage = array( + 'usage_count' => array(), + 'prompts' => array(), + ); + } + + if ( ! is_array( $usage['usage_count'] ) ) { + $usage['usage_count'] = array(); + } + + if ( ! is_array( $usage['prompts'] ) ) { + $usage['prompts'] = array(); + } + + $is_missing = true; + + foreach ( $usage['usage_count'] as &$u ) { + if ( isset( $u['key'] ) && $u['key'] === $action ) { + $u['value']++; + $is_missing = false; + } + } + + unset( $u ); + + if ( $is_missing ) { + $usage['usage_count'][] = array( + 'key' => $action, + 'value' => 1, + ); + } + + $is_missing = true; + + foreach ( $usage['prompts'] as &$u ) { + if ( isset( $u['key'] ) && $u['key'] === $action ) { + $u['values'][] = $user_content; + $is_missing = false; + + // Keep only the last 10 prompts. + if ( count( $u['values'] ) > 10 ) { + array_shift( $u['values'] ); + } + } + } + + unset( $u ); + + if ( $is_missing ) { + $usage['prompts'][] = array( + 'key' => $action, + 'values' => array( $user_content ), + ); + } + + update_option( 'themeisle_otter_ai_usage', $usage ); + } + + + /** + * The instance method for the static class. + * Defines and returns the instance of the static class. + * + * @static + * @since 1.7.0 + * @access public + * @return Prompt_Server + */ + public static function instance() { + if ( is_null( self::$instance ) ) { + self::$instance = new self(); + self::$instance->init(); + } + + return self::$instance; + } + + /** + * Throw error on object clone + * + * The whole idea of the singleton design pattern is that there is a single + * object therefore, we don't want the object to be cloned. + * + * @access public + * @since 1.7.0 + * @return void + */ + public function __clone() { + // Cloning instances of the class is forbidden. + _doing_it_wrong( __FUNCTION__, esc_html__( 'Cheatin’ huh?', 'otter-blocks' ), '1.0.0' ); + } + + /** + * Disable unserializing of the class + * + * @access public + * @since 1.7.0 + * @return void + */ + public function __wakeup() { + // Unserializing instances of the class is forbidden. + _doing_it_wrong( __FUNCTION__, esc_html__( 'Cheatin’ huh?', 'otter-blocks' ), '1.0.0' ); + } +} diff --git a/package-lock.json b/package-lock.json index 37201ddf3..e8b809d83 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,10 +6,10 @@ "packages": { "": { "name": "otter-blocks", - "version": "2.3.2", + "version": "2.3.4", "license": "GPL-2.0+", "dependencies": { - "@wordpress/icons": "^9.29.0", + "@wordpress/icons": "^9.32.0", "array-move": "^3.0.1", "classnames": "^2.3.1", "currency-symbol-map": "^5.0.1", @@ -33,16 +33,16 @@ "@types/wordpress__block-editor": "^11.5.1", "@types/wordpress__components": "^23.0.1", "@typescript-eslint/parser": "^6.3.0", - "@wordpress/block-editor": "^12.6.0", - "@wordpress/components": "^25.5.0", + "@wordpress/block-editor": "^12.9.0", + "@wordpress/components": "^25.7.0", "@wordpress/compose": "^6.15.0", "@wordpress/data": "^9.7.0", - "@wordpress/dom-ready": "^3.38.0", + "@wordpress/dom-ready": "^3.41.0", "@wordpress/e2e-test-utils": "^10.4.0", - "@wordpress/e2e-test-utils-playwright": "^0.2.0", + "@wordpress/e2e-test-utils-playwright": "^0.9.0", "@wordpress/e2e-tests": "^7.9.0", - "@wordpress/element": "^5.14.0", - "@wordpress/env": "^8.4.0", + "@wordpress/element": "^5.18.0", + "@wordpress/env": "^8.7.0", "@wordpress/scripts": "^26.9.0", "conventional-changelog-simple-preset": "^1.0.20", "eslint-config-wordpress": "^2.0.0", @@ -60,10 +60,10 @@ "semantic-release": "^19.0.5", "semantic-release-slack-bot": "^3.5.2", "simple-statistics": "^7.8.3", - "typescript": "^5.1.6" + "typescript": "^5.2.2" }, "optionalDependencies": { - "fsevents": "^2.3.2" + "fsevents": "^2.3.3" } }, "node_modules/@adobe/css-tools": { @@ -2595,33 +2595,43 @@ ] }, "node_modules/@floating-ui/core": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.3.1.tgz", - "integrity": "sha512-Bu+AMaXNjrpjh41znzHqaz3r2Nr8hHuHZT6V2LBKMhyMl0FgKA62PNYbqnfgmzOhoWZj70Zecisbo4H1rotP5g==", - "dev": true + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.4.1.tgz", + "integrity": "sha512-jk3WqquEJRlcyu7997NtR5PibI+y5bi+LS3hPmguVClypenMsCY3CBa3LAQnozRCtCrYWSEtAdiskpamuJRFOQ==", + "dev": true, + "dependencies": { + "@floating-ui/utils": "^0.1.1" + } }, "node_modules/@floating-ui/dom": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.4.4.tgz", - "integrity": "sha512-21hhDEPOiWkGp0Ys4Wi6Neriah7HweToKra626CIK712B5m9qkdz54OP9gVldUg+URnBTpv/j/bi/skmGdstXQ==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.5.1.tgz", + "integrity": "sha512-KwvVcPSXg6mQygvA1TjbN/gh///36kKtllIF8SUm0qpFj8+rvYrpvlYdL1JoA71SHpDqgSSdGOSoQ0Mp3uY5aw==", "dev": true, "dependencies": { - "@floating-ui/core": "^1.3.1" + "@floating-ui/core": "^1.4.1", + "@floating-ui/utils": "^0.1.1" } }, "node_modules/@floating-ui/react-dom": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-1.0.0.tgz", - "integrity": "sha512-uiOalFKPG937UCLm42RxjESTWUVpbbatvlphQAU6bsv+ence6IoVG8JOUZcy8eW81NkU+Idiwvx10WFLmR4MIg==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.2.tgz", + "integrity": "sha512-5qhlDvjaLmAst/rKb3VdlCinwTF4EYMiVxuuc/HVUjs46W0zgtbMmAZ1UTsDrRTxRmUEzl92mOtWbeeXL26lSQ==", "dev": true, "dependencies": { - "@floating-ui/dom": "^1.0.0" + "@floating-ui/dom": "^1.5.1" }, "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, + "node_modules/@floating-ui/utils": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.1.1.tgz", + "integrity": "sha512-m0G6wlnhm/AX0H12IOWtK8gASEMffnX08RtKkCgTdHb9JpHKGloI7icFfLg9ZmQeavcvR0PKmzxClyuFPSjKWw==", + "dev": true + }, "node_modules/@hapi/hoek": { "version": "9.3.0", "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", @@ -3405,6 +3415,20 @@ "fsevents": "2.3.2" } }, + "node_modules/@playwright/test/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/@pmmmwh/react-refresh-webpack-plugin": { "version": "0.5.10", "resolved": "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.10.tgz", @@ -3683,6 +3707,146 @@ "react": "^16.8 || ^17.0 || ^18.0" } }, + "node_modules/@radix-ui/react-dialog": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.0.0.tgz", + "integrity": "sha512-Yn9YU+QlHYLWwV1XfKiqnGVpWYWk6MeBVM6x/bcoyPvxgjQGoeT35482viLPctTMWoMw0PoHgqfSox7Ig+957Q==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.0", + "@radix-ui/react-compose-refs": "1.0.0", + "@radix-ui/react-context": "1.0.0", + "@radix-ui/react-dismissable-layer": "1.0.0", + "@radix-ui/react-focus-guards": "1.0.0", + "@radix-ui/react-focus-scope": "1.0.0", + "@radix-ui/react-id": "1.0.0", + "@radix-ui/react-portal": "1.0.0", + "@radix-ui/react-presence": "1.0.0", + "@radix-ui/react-primitive": "1.0.0", + "@radix-ui/react-slot": "1.0.0", + "@radix-ui/react-use-controllable-state": "1.0.0", + "aria-hidden": "^1.1.1", + "react-remove-scroll": "2.5.4" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.0.0.tgz", + "integrity": "sha512-n7kDRfx+LB1zLueRDvZ1Pd0bxdJWDUZNQ/GWoxDn2prnuJKRdxsjulejX/ePkOsLi2tTm6P24mDqlMSgQpsT6g==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.0", + "@radix-ui/react-compose-refs": "1.0.0", + "@radix-ui/react-primitive": "1.0.0", + "@radix-ui/react-use-callback-ref": "1.0.0", + "@radix-ui/react-use-escape-keydown": "1.0.0" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-focus-scope": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.0.0.tgz", + "integrity": "sha512-C4SWtsULLGf/2L4oGeIHlvWQx7Rf+7cX/vKOAD2dXW0A1b5QXwi3wWeaEgW+wn+SEVrraMUk05vLU9fZZz5HbQ==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.0", + "@radix-ui/react-primitive": "1.0.0", + "@radix-ui/react-use-callback-ref": "1.0.0" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-portal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.0.0.tgz", + "integrity": "sha512-a8qyFO/Xb99d8wQdu4o7qnigNjTPG123uADNecz0eX4usnQEj7o+cG4ZX4zkqq98NYekT7UoEQIjxBNWIFuqTA==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-primitive": "1.0.0" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-primitive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-1.0.0.tgz", + "integrity": "sha512-EyXe6mnRlHZ8b6f4ilTDrXmkLShICIuOTTj0GX4w1rp+wSxf3+TD05u1UOITC8VsJ2a9nwHvdXtOXEOl0Cw/zQ==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-slot": "1.0.0" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-slot": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.0.tgz", + "integrity": "sha512-3mrKauI/tWXo1Ll+gN5dHcxDPdm/Df1ufcDLCecn+pnCIVcdWE7CujXo8QaXOWRJyZyQWWbpB8eFwHzWXlv5mQ==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.0" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-use-escape-keydown": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.0.0.tgz", + "integrity": "sha512-JwfBCUIfhXRxKExgIqGa4CQsiMemo1Xt0W/B4ei3fpzpvPENKpMKQ8mZSB6Acj3ebrAEgi2xiQvcI1PAAodvyg==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-use-callback-ref": "1.0.0" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/react-remove-scroll": { + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.5.4.tgz", + "integrity": "sha512-xGVKJJr0SJGQVirVFAUZ2k1QLyO6m+2fy0l8Qawbp5Jgrv3DeLalrfMNBFSlmz5kriGGzsVBtGVnf4pTKIhhWA==", + "dev": true, + "dependencies": { + "react-remove-scroll-bar": "^2.3.3", + "react-style-singleton": "^2.2.1", + "tslib": "^2.1.0", + "use-callback-ref": "^1.3.0", + "use-sidecar": "^1.1.2" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-direction": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.0.0.tgz", @@ -6012,37 +6176,37 @@ } }, "node_modules/@wordpress/a11y": { - "version": "3.39.0", - "resolved": "https://registry.npmjs.org/@wordpress/a11y/-/a11y-3.39.0.tgz", - "integrity": "sha512-UbPf1BEES25aLZiS+SxpavRhRJqM6vSE5I87fQSFING8JmsyuBsGnVCWXUwhMsyueXcwC6XrHLZJMx2onY175w==", + "version": "3.41.0", + "resolved": "https://registry.npmjs.org/@wordpress/a11y/-/a11y-3.41.0.tgz", + "integrity": "sha512-T+7rHdX4k72ty3MdtySuL41d4wrIXRKdM1Xhjr89G/OXyetsY0nb4n6jUsFGnf69iq7gFSgVjEVogbOc/ffbTA==", "dev": true, "dependencies": { "@babel/runtime": "^7.16.0", - "@wordpress/dom-ready": "^3.39.0", - "@wordpress/i18n": "^4.39.0" + "@wordpress/dom-ready": "^3.41.0", + "@wordpress/i18n": "^4.41.0" }, "engines": { "node": ">=12" } }, "node_modules/@wordpress/api-fetch": { - "version": "6.35.0", - "resolved": "https://registry.npmjs.org/@wordpress/api-fetch/-/api-fetch-6.35.0.tgz", - "integrity": "sha512-pQkH9BRhrWgDki9Bj9Cwyj89kXcXrDjWWJKUq2viuuyK/QEGLthKFOWkSQWT5v0QHDL81B0jWkr0eSG80Mtxtw==", + "version": "6.38.0", + "resolved": "https://registry.npmjs.org/@wordpress/api-fetch/-/api-fetch-6.38.0.tgz", + "integrity": "sha512-EY5+9hxUDFOKCrIBFokUFuF2bPnWjtOlc8yQcB1SmJv5JULdFZF+pgAKXqTPFwWR8wcNjv2hypemV8j82Rq4MA==", "dev": true, "dependencies": { "@babel/runtime": "^7.16.0", - "@wordpress/i18n": "^4.38.0", - "@wordpress/url": "^3.39.0" + "@wordpress/i18n": "^4.41.0", + "@wordpress/url": "^3.42.0" }, "engines": { "node": ">=12" } }, "node_modules/@wordpress/autop": { - "version": "3.38.0", - "resolved": "https://registry.npmjs.org/@wordpress/autop/-/autop-3.38.0.tgz", - "integrity": "sha512-s33r5YAK2QguM/HfvhF8yT7e9AgvTBloF1scqZnbtiehU7PhJHgiDCL9nCQ1lpNHsSuKizk2fvIjYaCdfFI0sA==", + "version": "3.41.0", + "resolved": "https://registry.npmjs.org/@wordpress/autop/-/autop-3.41.0.tgz", + "integrity": "sha512-oaGwox3PKg6/a4nFul7js4k/wXazgtL0t6v8Epg3IotMUFXBN3rCAqH/zmCl/669LA9gG0TaT0VoqjF3iFuLtQ==", "dev": true, "dependencies": { "@babel/runtime": "^7.16.0" @@ -6093,9 +6257,9 @@ "dev": true }, "node_modules/@wordpress/blob": { - "version": "3.38.0", - "resolved": "https://registry.npmjs.org/@wordpress/blob/-/blob-3.38.0.tgz", - "integrity": "sha512-zvkRbbEScM1RIPLVA27ydyDjPX50FWdro8Zy0yQ2BtZ+heH6khsCB48yKPDRhl0GoifdVJIiEcLtpb4yHMzZ2g==", + "version": "3.41.0", + "resolved": "https://registry.npmjs.org/@wordpress/blob/-/blob-3.41.0.tgz", + "integrity": "sha512-osBcD1IpN/YT96ArGqf7Cdon/Z8gHMC8sWPUAQ1OPLNrxlFLomaCPOoC9UeAgbEh338epoYfqmi7rTwUMCBS3Q==", "dev": true, "dependencies": { "@babel/runtime": "^7.16.0" @@ -6105,44 +6269,45 @@ } }, "node_modules/@wordpress/block-editor": { - "version": "12.6.0", - "resolved": "https://registry.npmjs.org/@wordpress/block-editor/-/block-editor-12.6.0.tgz", - "integrity": "sha512-B76qxMdrQgfjsnTjez9tZ3OsyeQL0rfABktDVkmCTL9O8u5dPb+ArtGucmElFwUKuNibv54zwyUADbKSa+RUFw==", + "version": "12.9.0", + "resolved": "https://registry.npmjs.org/@wordpress/block-editor/-/block-editor-12.9.0.tgz", + "integrity": "sha512-x8fUoP2T6PtuEXm62VGkJSLokDU8EKmh+5sFeVIj2Vbh7av4i0+OWzf/LhP4rCi1bRujZSk8Gn1uzRfJglNMHQ==", "dev": true, "dependencies": { "@babel/runtime": "^7.16.0", "@emotion/react": "^11.7.1", "@emotion/styled": "^11.6.0", "@react-spring/web": "^9.4.5", - "@wordpress/a11y": "^3.38.0", - "@wordpress/api-fetch": "^6.35.0", - "@wordpress/blob": "^3.38.0", - "@wordpress/blocks": "^12.15.0", - "@wordpress/components": "^25.4.0", - "@wordpress/compose": "^6.15.0", - "@wordpress/data": "^9.8.0", - "@wordpress/date": "^4.38.0", - "@wordpress/deprecated": "^3.38.0", - "@wordpress/dom": "^3.38.0", - "@wordpress/element": "^5.15.0", - "@wordpress/escape-html": "^2.38.0", - "@wordpress/hooks": "^3.38.0", - "@wordpress/html-entities": "^3.38.0", - "@wordpress/i18n": "^4.38.0", - "@wordpress/icons": "^9.29.0", - "@wordpress/is-shallow-equal": "^4.38.0", - "@wordpress/keyboard-shortcuts": "^4.15.0", - "@wordpress/keycodes": "^3.38.0", - "@wordpress/notices": "^4.6.0", - "@wordpress/preferences": "^3.15.0", - "@wordpress/private-apis": "^0.20.0", - "@wordpress/rich-text": "^6.15.0", - "@wordpress/shortcode": "^3.38.0", - "@wordpress/style-engine": "^1.21.0", - "@wordpress/token-list": "^2.38.0", - "@wordpress/url": "^3.39.0", - "@wordpress/warning": "^2.38.0", - "@wordpress/wordcount": "^3.38.0", + "@wordpress/a11y": "^3.41.0", + "@wordpress/api-fetch": "^6.38.0", + "@wordpress/blob": "^3.41.0", + "@wordpress/blocks": "^12.18.0", + "@wordpress/commands": "^0.12.0", + "@wordpress/components": "^25.7.0", + "@wordpress/compose": "^6.18.0", + "@wordpress/data": "^9.11.0", + "@wordpress/date": "^4.41.0", + "@wordpress/deprecated": "^3.41.0", + "@wordpress/dom": "^3.41.0", + "@wordpress/element": "^5.18.0", + "@wordpress/escape-html": "^2.41.0", + "@wordpress/hooks": "^3.41.0", + "@wordpress/html-entities": "^3.41.0", + "@wordpress/i18n": "^4.41.0", + "@wordpress/icons": "^9.32.0", + "@wordpress/is-shallow-equal": "^4.41.0", + "@wordpress/keyboard-shortcuts": "^4.18.0", + "@wordpress/keycodes": "^3.41.0", + "@wordpress/notices": "^4.9.0", + "@wordpress/preferences": "^3.18.0", + "@wordpress/private-apis": "^0.23.0", + "@wordpress/rich-text": "^6.18.0", + "@wordpress/shortcode": "^3.41.0", + "@wordpress/style-engine": "^1.24.0", + "@wordpress/token-list": "^2.41.0", + "@wordpress/url": "^3.42.0", + "@wordpress/warning": "^2.41.0", + "@wordpress/wordcount": "^3.41.0", "change-case": "^4.1.2", "classnames": "^2.3.1", "colord": "^2.7.0", @@ -6154,7 +6319,7 @@ "react-autosize-textarea": "^7.1.0", "react-easy-crop": "^4.5.1", "rememo": "^4.0.2", - "remove-accents": "^0.4.2", + "remove-accents": "^0.5.0", "traverse": "^0.6.6" }, "engines": { @@ -6166,9 +6331,9 @@ } }, "node_modules/@wordpress/block-serialization-default-parser": { - "version": "4.38.0", - "resolved": "https://registry.npmjs.org/@wordpress/block-serialization-default-parser/-/block-serialization-default-parser-4.38.0.tgz", - "integrity": "sha512-IE/34Auhr3iHsKolxVa1BHKJPyg7lCwjZdlIpjOrtjN6DpjRdn0uIFpz5b7gZJIyQLvgE0E1sc2+BWtYXCu4qg==", + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@wordpress/block-serialization-default-parser/-/block-serialization-default-parser-4.41.0.tgz", + "integrity": "sha512-/bHeVewOO2GSm3y6fSoOlt0ZNweP2jua8G+CTZG3XKrwX1eTXWFQtJTJQPUuerXeWEqMTD58lILxneqyw28hcg==", "dev": true, "dependencies": { "@babel/runtime": "^7.16.0" @@ -6178,26 +6343,26 @@ } }, "node_modules/@wordpress/blocks": { - "version": "12.15.0", - "resolved": "https://registry.npmjs.org/@wordpress/blocks/-/blocks-12.15.0.tgz", - "integrity": "sha512-mw+JvwwUVaTW3CEQH2C/ibC0grZ0bZ/Y2vNcgn81asQokIaNl+/jRc6EJRLs3uAymYksF8sKsH/RDPlXmLmBcw==", + "version": "12.18.0", + "resolved": "https://registry.npmjs.org/@wordpress/blocks/-/blocks-12.18.0.tgz", + "integrity": "sha512-JS2g4k95GPNdO2hOLBbppz3kTBUdKIoFXJ5w5tn11TGYep4dmEHh8ugGCW9XweOCkz3g4HDT+vansRAz4JF1yQ==", "dev": true, "dependencies": { "@babel/runtime": "^7.16.0", - "@wordpress/autop": "^3.38.0", - "@wordpress/blob": "^3.38.0", - "@wordpress/block-serialization-default-parser": "^4.38.0", - "@wordpress/compose": "^6.15.0", - "@wordpress/data": "^9.8.0", - "@wordpress/deprecated": "^3.38.0", - "@wordpress/dom": "^3.38.0", - "@wordpress/element": "^5.15.0", - "@wordpress/hooks": "^3.38.0", - "@wordpress/html-entities": "^3.38.0", - "@wordpress/i18n": "^4.38.0", - "@wordpress/is-shallow-equal": "^4.38.0", - "@wordpress/private-apis": "^0.20.0", - "@wordpress/shortcode": "^3.38.0", + "@wordpress/autop": "^3.41.0", + "@wordpress/blob": "^3.41.0", + "@wordpress/block-serialization-default-parser": "^4.41.0", + "@wordpress/compose": "^6.18.0", + "@wordpress/data": "^9.11.0", + "@wordpress/deprecated": "^3.41.0", + "@wordpress/dom": "^3.41.0", + "@wordpress/element": "^5.18.0", + "@wordpress/hooks": "^3.41.0", + "@wordpress/html-entities": "^3.41.0", + "@wordpress/i18n": "^4.41.0", + "@wordpress/is-shallow-equal": "^4.41.0", + "@wordpress/private-apis": "^0.23.0", + "@wordpress/shortcode": "^3.41.0", "change-case": "^4.1.2", "colord": "^2.7.0", "deepmerge": "^4.3.0", @@ -6206,7 +6371,7 @@ "is-plain-object": "^5.0.0", "memize": "^2.1.0", "rememo": "^4.0.2", - "remove-accents": "^0.4.2", + "remove-accents": "^0.5.0", "showdown": "^1.9.1", "simple-html-tokenizer": "^0.5.7", "uuid": "^8.3.0" @@ -6227,10 +6392,36 @@ "node": ">=14" } }, + "node_modules/@wordpress/commands": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@wordpress/commands/-/commands-0.12.0.tgz", + "integrity": "sha512-yeFZ2BCDh9i8+XhcwAjwREXE8WUl9hv4YL5/b2Dh6eUqld/TGH4lUtxIHMxsnlzLqR29ZRaChobU44ji4rZf9g==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.16.0", + "@wordpress/components": "^25.7.0", + "@wordpress/data": "^9.11.0", + "@wordpress/element": "^5.18.0", + "@wordpress/i18n": "^4.41.0", + "@wordpress/icons": "^9.32.0", + "@wordpress/keyboard-shortcuts": "^4.18.0", + "@wordpress/private-apis": "^0.23.0", + "classnames": "^2.3.1", + "cmdk": "^0.2.0", + "rememo": "^4.0.2" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0" + } + }, "node_modules/@wordpress/components": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/@wordpress/components/-/components-25.5.0.tgz", - "integrity": "sha512-+2akjB7glLrVlZwAvWWWS+baPnHK6H5Vmgc7II2D32JhTTD139prCfxFTmyPxZbIv+arWSgs7yCvywF7CEnwQQ==", + "version": "25.7.0", + "resolved": "https://registry.npmjs.org/@wordpress/components/-/components-25.7.0.tgz", + "integrity": "sha512-CFUhdMKuIA+0flsA3ACnJ0h84uKo+PrGkWG3nCSJwifdC6AVB7b7VOriDRhYsJXKoyr3QV+gRfAnFn0Hrc7tQQ==", "dev": true, "dependencies": { "@ariakit/react": "^0.2.12", @@ -6241,26 +6432,26 @@ "@emotion/serialize": "^1.0.2", "@emotion/styled": "^11.6.0", "@emotion/utils": "^1.0.0", - "@floating-ui/react-dom": "1.0.0", + "@floating-ui/react-dom": "^2.0.1", "@radix-ui/react-dropdown-menu": "2.0.4", "@use-gesture/react": "^10.2.24", - "@wordpress/a11y": "^3.39.0", - "@wordpress/compose": "^6.16.0", - "@wordpress/date": "^4.39.0", - "@wordpress/deprecated": "^3.39.0", - "@wordpress/dom": "^3.39.0", - "@wordpress/element": "^5.16.0", - "@wordpress/escape-html": "^2.39.0", - "@wordpress/hooks": "^3.39.0", - "@wordpress/html-entities": "^3.39.0", - "@wordpress/i18n": "^4.39.0", - "@wordpress/icons": "^9.30.0", - "@wordpress/is-shallow-equal": "^4.39.0", - "@wordpress/keycodes": "^3.39.0", - "@wordpress/primitives": "^3.37.0", - "@wordpress/private-apis": "^0.21.0", - "@wordpress/rich-text": "^6.16.0", - "@wordpress/warning": "^2.39.0", + "@wordpress/a11y": "^3.41.0", + "@wordpress/compose": "^6.18.0", + "@wordpress/date": "^4.41.0", + "@wordpress/deprecated": "^3.41.0", + "@wordpress/dom": "^3.41.0", + "@wordpress/element": "^5.18.0", + "@wordpress/escape-html": "^2.41.0", + "@wordpress/hooks": "^3.41.0", + "@wordpress/html-entities": "^3.41.0", + "@wordpress/i18n": "^4.41.0", + "@wordpress/icons": "^9.32.0", + "@wordpress/is-shallow-equal": "^4.41.0", + "@wordpress/keycodes": "^3.41.0", + "@wordpress/primitives": "^3.39.0", + "@wordpress/private-apis": "^0.23.0", + "@wordpress/rich-text": "^6.18.0", + "@wordpress/warning": "^2.41.0", "change-case": "^4.1.2", "classnames": "^2.3.1", "colord": "^2.7.0", @@ -6278,7 +6469,7 @@ "re-resizable": "^6.4.0", "react-colorful": "^5.3.1", "reakit": "^1.3.11", - "remove-accents": "^0.4.2", + "remove-accents": "^0.5.0", "use-lilius": "^2.0.1", "uuid": "^8.3.0", "valtio": "1.7.0" @@ -6291,32 +6482,20 @@ "react-dom": "^18.0.0" } }, - "node_modules/@wordpress/components/node_modules/@wordpress/private-apis": { - "version": "0.21.0", - "resolved": "https://registry.npmjs.org/@wordpress/private-apis/-/private-apis-0.21.0.tgz", - "integrity": "sha512-sJRZUvI5gXhZf/RYZDhx0E9ubY4SzMWmqU3JHyZpuJFpixJGbBYSRP1Ob9JUKstPKfoYVXaxDRHZJ3bi4J6cig==", - "dev": true, - "dependencies": { - "@babel/runtime": "^7.16.0" - }, - "engines": { - "node": ">=12" - } - }, "node_modules/@wordpress/compose": { - "version": "6.16.0", - "resolved": "https://registry.npmjs.org/@wordpress/compose/-/compose-6.16.0.tgz", - "integrity": "sha512-GQWb93OR17clnK6J60ybMnM6ct8mSAx/lMP0Vvt5d7rficiVLYnXsfD7l7MRECWJf5rz8p4V6+5ESuv6EfMMFQ==", + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/@wordpress/compose/-/compose-6.18.0.tgz", + "integrity": "sha512-aZyvttCnT8HI4vS8Nb8y5Go+AycrThy0gvEl9jc9ZB9emm1ZifNkA6gTNpBd7zU2uzS4wUpiZYGGvUNeAuShvQ==", "dev": true, "dependencies": { "@babel/runtime": "^7.16.0", "@types/mousetrap": "^1.6.8", - "@wordpress/deprecated": "^3.39.0", - "@wordpress/dom": "^3.39.0", - "@wordpress/element": "^5.16.0", - "@wordpress/is-shallow-equal": "^4.39.0", - "@wordpress/keycodes": "^3.39.0", - "@wordpress/priority-queue": "^2.39.0", + "@wordpress/deprecated": "^3.41.0", + "@wordpress/dom": "^3.41.0", + "@wordpress/element": "^5.18.0", + "@wordpress/is-shallow-equal": "^4.41.0", + "@wordpress/keycodes": "^3.41.0", + "@wordpress/priority-queue": "^2.41.0", "change-case": "^4.1.2", "clipboard": "^2.0.8", "mousetrap": "^1.6.5", @@ -6330,19 +6509,19 @@ } }, "node_modules/@wordpress/data": { - "version": "9.9.0", - "resolved": "https://registry.npmjs.org/@wordpress/data/-/data-9.9.0.tgz", - "integrity": "sha512-E3lk4mUi6hvUFZV0tOT7QjMIixZguEMUYyChMLb9TYt+JrKR+OjQ0juQ86QQfZDHeXEVzj7TyNQSHXCXDb/oow==", + "version": "9.11.0", + "resolved": "https://registry.npmjs.org/@wordpress/data/-/data-9.11.0.tgz", + "integrity": "sha512-1UaummqcfxO4EU7eKfkxEaRjlZjRvqHRdtdjyBtrpWHxIaLrTzBdM7rBNu06tilZBoAXGRoBc5TyPuvNI3IWNg==", "dev": true, "dependencies": { "@babel/runtime": "^7.16.0", - "@wordpress/compose": "^6.16.0", - "@wordpress/deprecated": "^3.39.0", - "@wordpress/element": "^5.16.0", - "@wordpress/is-shallow-equal": "^4.39.0", - "@wordpress/priority-queue": "^2.39.0", - "@wordpress/private-apis": "^0.21.0", - "@wordpress/redux-routine": "^4.39.0", + "@wordpress/compose": "^6.18.0", + "@wordpress/deprecated": "^3.41.0", + "@wordpress/element": "^5.18.0", + "@wordpress/is-shallow-equal": "^4.41.0", + "@wordpress/priority-queue": "^2.41.0", + "@wordpress/private-apis": "^0.23.0", + "@wordpress/redux-routine": "^4.41.0", "deepmerge": "^4.3.0", "equivalent-key-map": "^0.2.2", "is-plain-object": "^5.0.0", @@ -6358,26 +6537,14 @@ "react": "^18.0.0" } }, - "node_modules/@wordpress/data/node_modules/@wordpress/private-apis": { - "version": "0.21.0", - "resolved": "https://registry.npmjs.org/@wordpress/private-apis/-/private-apis-0.21.0.tgz", - "integrity": "sha512-sJRZUvI5gXhZf/RYZDhx0E9ubY4SzMWmqU3JHyZpuJFpixJGbBYSRP1Ob9JUKstPKfoYVXaxDRHZJ3bi4J6cig==", - "dev": true, - "dependencies": { - "@babel/runtime": "^7.16.0" - }, - "engines": { - "node": ">=12" - } - }, "node_modules/@wordpress/date": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@wordpress/date/-/date-4.39.0.tgz", - "integrity": "sha512-cepDETyxQBlx8OwAZI44RYG8Vweq6V1urt1zR4lUMfdlwOnRf1wmuFSODQwQ65fKWjmJjQyC86ELoZacp29yjg==", + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@wordpress/date/-/date-4.41.0.tgz", + "integrity": "sha512-R0cTQKev7xT/srjAgUJOgW7CYnXuRdxSpXG7timYr3jEqgYoJFSAP2miq3+FWWSra1nesdT+r4o5z+3rIKFLLg==", "dev": true, "dependencies": { "@babel/runtime": "^7.16.0", - "@wordpress/deprecated": "^3.39.0", + "@wordpress/deprecated": "^3.41.0", "moment": "^2.29.4", "moment-timezone": "^0.5.40" }, @@ -6402,35 +6569,35 @@ } }, "node_modules/@wordpress/deprecated": { - "version": "3.39.0", - "resolved": "https://registry.npmjs.org/@wordpress/deprecated/-/deprecated-3.39.0.tgz", - "integrity": "sha512-djk/bX5oXLoW2T49ThCnc0nKgKNnHiW9g9I4Hka6Uk4AVoizqTHxGzGt8HrbMAzzmJ/4ltY0kbZyMYcp7DZPjw==", + "version": "3.41.0", + "resolved": "https://registry.npmjs.org/@wordpress/deprecated/-/deprecated-3.41.0.tgz", + "integrity": "sha512-8so9fJC6MvMeMxaRqEJYtzXm1RP2i+nq3NXG9DW4fbo8ICEIe1QqBpCFqV4FbkHs8PRqyJ8IJ7C6NnAvL3BWKw==", "dev": true, "dependencies": { "@babel/runtime": "^7.16.0", - "@wordpress/hooks": "^3.39.0" + "@wordpress/hooks": "^3.41.0" }, "engines": { "node": ">=12" } }, "node_modules/@wordpress/dom": { - "version": "3.39.0", - "resolved": "https://registry.npmjs.org/@wordpress/dom/-/dom-3.39.0.tgz", - "integrity": "sha512-OhPUys+peMqTDNfMs3jrKzYRhEs3ngdyrepR8N/BPjDLYcdElraFax/9YcqkjKiYFmdBHLYh1LYK5VrGgFVjQA==", + "version": "3.41.0", + "resolved": "https://registry.npmjs.org/@wordpress/dom/-/dom-3.41.0.tgz", + "integrity": "sha512-0FKG4c33G7jOElzy1adqgNjowYBaWX98Q99X9WjpjtF+AY7/Sljq4zOGh5iZXSM/1dpKLPs4f/frFEqueq6MGg==", "dev": true, "dependencies": { "@babel/runtime": "^7.16.0", - "@wordpress/deprecated": "^3.39.0" + "@wordpress/deprecated": "^3.41.0" }, "engines": { "node": ">=12" } }, "node_modules/@wordpress/dom-ready": { - "version": "3.39.0", - "resolved": "https://registry.npmjs.org/@wordpress/dom-ready/-/dom-ready-3.39.0.tgz", - "integrity": "sha512-Fv9FojQmozV0alxY0l+rAbe3rQ5wtnbOyOXFGGFLdguLlKrENrvue/rj4wRZm9/kOPkjU2YEkeowi+JPaVybcQ==", + "version": "3.41.0", + "resolved": "https://registry.npmjs.org/@wordpress/dom-ready/-/dom-ready-3.41.0.tgz", + "integrity": "sha512-xbTCYC1192nUiBLB384GrzlQMbFnSv31TjI4+Y4JhR46+alcSsF2KPmlR8htxRbAXBx7z8lT78Fv+0ULhSIERA==", "dev": true, "dependencies": { "@babel/runtime": "^7.16.0" @@ -6462,14 +6629,14 @@ } }, "node_modules/@wordpress/e2e-test-utils-playwright": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@wordpress/e2e-test-utils-playwright/-/e2e-test-utils-playwright-0.2.0.tgz", - "integrity": "sha512-Zc4IuKS16o5u0K9eHZylh0kF0DH6oUdenPBLdlmM0EgdypTW4TRyAPSRAsIMYIxN/36S/iai7mxAg66IB4LpmA==", + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@wordpress/e2e-test-utils-playwright/-/e2e-test-utils-playwright-0.9.0.tgz", + "integrity": "sha512-AMhMnEOxe1vR8ekK6GKEDUo2GbdI4Z+uIQA2MMkLJiTESEOVh4Y6Jxsj617pSomZisatZIsMl53rqdTh8+Ry3A==", "dev": true, "dependencies": { - "@wordpress/api-fetch": "^6.31.0", - "@wordpress/keycodes": "^3.34.0", - "@wordpress/url": "^3.35.0", + "@wordpress/api-fetch": "^6.38.0", + "@wordpress/keycodes": "^3.41.0", + "@wordpress/url": "^3.42.0", "change-case": "^4.1.2", "form-data": "^4.0.0", "mime": "^3.0.0" @@ -6511,14 +6678,14 @@ } }, "node_modules/@wordpress/element": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/@wordpress/element/-/element-5.16.0.tgz", - "integrity": "sha512-vMUL+y1KtsPEDA3ksXZ49U65JqkuKoFze9Zvuo+qGv35lS5cfmv4+NdePbHDu92fPasnsw1LAzZzKz1vvqYozA==", + "version": "5.18.0", + "resolved": "https://registry.npmjs.org/@wordpress/element/-/element-5.18.0.tgz", + "integrity": "sha512-OynuZuTFdmterh/ASmMSSKjdBj5r1hcwQi37AQnp7+GpyIV3Ol5PR4UWWYB0coW0Gkd0giJkQAwC71/ZkEPYqQ==", "dependencies": { "@babel/runtime": "^7.16.0", "@types/react": "^18.0.21", "@types/react-dom": "^18.0.6", - "@wordpress/escape-html": "^2.39.0", + "@wordpress/escape-html": "^2.41.0", "change-case": "^4.1.2", "is-plain-object": "^5.0.0", "react": "^18.2.0", @@ -6529,9 +6696,9 @@ } }, "node_modules/@wordpress/env": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/@wordpress/env/-/env-8.4.0.tgz", - "integrity": "sha512-3DnTW/WanvQpWqagCIMQYtSBYj9wXLQQqGgmKl8gVJ4MfxV3K5A9zE25Rv6iogdr7ydLcPSbmNJx6mYgQRaSog==", + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/@wordpress/env/-/env-8.7.0.tgz", + "integrity": "sha512-cqjDjFFLZ8691mzsuPaakoNbUJ5d6DNNRMyN6UZefLGKhthlqmyK5DqzXZUzCr9cgF/kdc//v3ZmBy9nywBYSA==", "dev": true, "dependencies": { "chalk": "^4.0.0", @@ -6552,9 +6719,9 @@ } }, "node_modules/@wordpress/escape-html": { - "version": "2.39.0", - "resolved": "https://registry.npmjs.org/@wordpress/escape-html/-/escape-html-2.39.0.tgz", - "integrity": "sha512-T9so6wu9OC+sg7yF7MpuOayykROpcTfemxsz0StzuLucGTVLK49CFE+08LAs10lIdmjXS9t2J3ggW7Vsmis9fg==", + "version": "2.41.0", + "resolved": "https://registry.npmjs.org/@wordpress/escape-html/-/escape-html-2.41.0.tgz", + "integrity": "sha512-fFDuAO/csLVemQrJKTrwxjmR7d2a6zEuDVCKi2jUt7j9rpLpz9IZnEVD2q/icOj2+u6joeDwvCyyPyTreqEZHA==", "dependencies": { "@babel/runtime": "^7.16.0" }, @@ -6718,9 +6885,9 @@ } }, "node_modules/@wordpress/hooks": { - "version": "3.39.0", - "resolved": "https://registry.npmjs.org/@wordpress/hooks/-/hooks-3.39.0.tgz", - "integrity": "sha512-/qdS87k61YAbab8O2gVRqaaX5vjWfCkcbiEK/qwVIJMMI3MjOni/8con5x0NaCAsRQzow9bCi5HMXmUj624w4g==", + "version": "3.41.0", + "resolved": "https://registry.npmjs.org/@wordpress/hooks/-/hooks-3.41.0.tgz", + "integrity": "sha512-o3fC6Z0kCLzZNFUT5W7C1d2mR+qjVwLaWjrwuJngJG92wly4IzKgAUDs/iJZojxtePFMP8JOFCg4FMuzG/VhWw==", "dev": true, "dependencies": { "@babel/runtime": "^7.16.0" @@ -6730,9 +6897,9 @@ } }, "node_modules/@wordpress/html-entities": { - "version": "3.39.0", - "resolved": "https://registry.npmjs.org/@wordpress/html-entities/-/html-entities-3.39.0.tgz", - "integrity": "sha512-68BEBpdPPW/NiENzjGg5hIBguSK8eKcJMzucQhPQ8SOsmLacxyATQ55OLyKNnniIlbKOuyT7lfVkLw9BpEJp2g==", + "version": "3.41.0", + "resolved": "https://registry.npmjs.org/@wordpress/html-entities/-/html-entities-3.41.0.tgz", + "integrity": "sha512-nb8ioGMi9yBfOzy2tIvIKQA+MaVNbgTflM/uiwhb5D/KTqtIUp+e84BOCOOZDI7xtWKQKhtGAn126Ko7hi73Jg==", "dev": true, "dependencies": { "@babel/runtime": "^7.16.0" @@ -6742,13 +6909,13 @@ } }, "node_modules/@wordpress/i18n": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@wordpress/i18n/-/i18n-4.39.0.tgz", - "integrity": "sha512-JbberLX7et//6l6OWMhtpVpiNvbLXJcYhqENbRpWTQ06aAQvsSrxQoDQ2UfFip9cpEBrDNoVxGNVN8oCdK3Jug==", + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@wordpress/i18n/-/i18n-4.41.0.tgz", + "integrity": "sha512-MXA+DiVSF2CS0ZhFEBq/eJjfHuKMcu3FUuiF/Dpc1YZRD1X7N6xPlfo4xKJZVUWIAsfgpc8oA2YMLw1VTFzrRA==", "dev": true, "dependencies": { "@babel/runtime": "^7.16.0", - "@wordpress/hooks": "^3.39.0", + "@wordpress/hooks": "^3.41.0", "gettext-parser": "^1.3.1", "memize": "^2.1.0", "sprintf-js": "^1.1.1", @@ -6762,22 +6929,22 @@ } }, "node_modules/@wordpress/icons": { - "version": "9.30.0", - "resolved": "https://registry.npmjs.org/@wordpress/icons/-/icons-9.30.0.tgz", - "integrity": "sha512-9fiFmsUcS1zYS0DzBFyywiItuRHIlxduo3MQifB8ekQ5bZGl6ZLl1lzIqaY1mmdc12bLB+Sxd9SeIcC2t08FfA==", + "version": "9.32.0", + "resolved": "https://registry.npmjs.org/@wordpress/icons/-/icons-9.32.0.tgz", + "integrity": "sha512-67imRDf5LF6v4vCBmvAEa4ZzcH0jvwIaBr1Y2ou4ElVJ1KZjiu1C93Lp6dcAy/08L8eX+eZZCwTrTYI+tl9v5w==", "dependencies": { "@babel/runtime": "^7.16.0", - "@wordpress/element": "^5.16.0", - "@wordpress/primitives": "^3.37.0" + "@wordpress/element": "^5.18.0", + "@wordpress/primitives": "^3.39.0" }, "engines": { "node": ">=12" } }, "node_modules/@wordpress/is-shallow-equal": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@wordpress/is-shallow-equal/-/is-shallow-equal-4.39.0.tgz", - "integrity": "sha512-49DiLWyQmyxXHLdxsoA0r4iid2/S2J/Nh0njzhJDp7SsdycX+LiBFHL/zb7suxIaUBWirFqV9uOsjq0FgXn//A==", + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@wordpress/is-shallow-equal/-/is-shallow-equal-4.41.0.tgz", + "integrity": "sha512-z0+bdJvjOcbPf7vGiC0Zfoff+2WyFRFwsJex9d9N9CTVvr87kaVHBZuZlPX6iFmS22RzM2tLeppBKlOSRtSI4w==", "dev": true, "dependencies": { "@babel/runtime": "^7.16.0" @@ -6842,15 +7009,15 @@ } }, "node_modules/@wordpress/keyboard-shortcuts": { - "version": "4.15.0", - "resolved": "https://registry.npmjs.org/@wordpress/keyboard-shortcuts/-/keyboard-shortcuts-4.15.0.tgz", - "integrity": "sha512-W+dcQyeqQCKZgAgaj/3/ACehhM4jbzBcenTKSO3UwG7YzDXquEmMwBj8AVxIuHYwlx+pzDdIBKdEJrN3pgxT/w==", + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@wordpress/keyboard-shortcuts/-/keyboard-shortcuts-4.18.0.tgz", + "integrity": "sha512-3q3Kmzzu6PTsx3abce4t/FHFuyN1jn7oukViNA17lIIFTfIL8Eu6ZfKy+UyPgAoCCXohEjCmaaB801qnsLsooQ==", "dev": true, "dependencies": { "@babel/runtime": "^7.16.0", - "@wordpress/data": "^9.8.0", - "@wordpress/element": "^5.15.0", - "@wordpress/keycodes": "^3.38.0", + "@wordpress/data": "^9.11.0", + "@wordpress/element": "^5.18.0", + "@wordpress/keycodes": "^3.41.0", "rememo": "^4.0.2" }, "engines": { @@ -6861,13 +7028,13 @@ } }, "node_modules/@wordpress/keycodes": { - "version": "3.39.0", - "resolved": "https://registry.npmjs.org/@wordpress/keycodes/-/keycodes-3.39.0.tgz", - "integrity": "sha512-5KPcrADRLzypFxz4s7meZ1Uw4soyPaHcPIxrbYlx8vMh/PZ+njsmhUcg09mBuL8emW+oJif/RN4tMgpXkfcliQ==", + "version": "3.41.0", + "resolved": "https://registry.npmjs.org/@wordpress/keycodes/-/keycodes-3.41.0.tgz", + "integrity": "sha512-0DY07PV5qATrvWLc5jWgrgUGpeFrqvY+K0qF0gLDw1rpIZBxLre+N3K7ANC/iZzSsPLbYxxVF0SSFDIVI23Pug==", "dev": true, "dependencies": { "@babel/runtime": "^7.16.0", - "@wordpress/i18n": "^4.39.0", + "@wordpress/i18n": "^4.41.0", "change-case": "^4.1.2" }, "engines": { @@ -6875,17 +7042,20 @@ } }, "node_modules/@wordpress/notices": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/@wordpress/notices/-/notices-4.6.0.tgz", - "integrity": "sha512-Y1syNulskog5SQ/etnfRram0YXxnM3n2MVt0JzE06BxFGKfbsUdcM2QUbyRJNfQKl9P3Wx7pJZju+NfRiWevTA==", + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/@wordpress/notices/-/notices-4.9.0.tgz", + "integrity": "sha512-X7LfAJy6jYBq6d7lbW32lrt1DxkdNOg/VpOyiv8klwn30AKSPws/vBSrTPNDpp9phleuIuRr3tDcpS9/p+jRIw==", "dev": true, "dependencies": { "@babel/runtime": "^7.16.0", - "@wordpress/a11y": "^3.38.0", - "@wordpress/data": "^9.8.0" + "@wordpress/a11y": "^3.41.0", + "@wordpress/data": "^9.11.0" }, "engines": { "node": ">=12" + }, + "peerDependencies": { + "react": "^18.0.0" } }, "node_modules/@wordpress/npm-package-json-lint-config": { @@ -6917,17 +7087,18 @@ } }, "node_modules/@wordpress/preferences": { - "version": "3.15.0", - "resolved": "https://registry.npmjs.org/@wordpress/preferences/-/preferences-3.15.0.tgz", - "integrity": "sha512-S27GEzPsHd2duEctFsoW7xJJhelh3NHKIb9MCLFpxQUxGw2vIPntqrzO4zIvAqvmm9nMpuse0h+ME0TeTLVn1w==", + "version": "3.18.0", + "resolved": "https://registry.npmjs.org/@wordpress/preferences/-/preferences-3.18.0.tgz", + "integrity": "sha512-S7eDEKvUIIJMWpiO+0j/iLDqspyhNdKR2N39HNkkrAA2mdr4LMvs5bOr/+m6xTpiik+8I29hwI/OuJOomqJ7PQ==", "dev": true, "dependencies": { "@babel/runtime": "^7.16.0", - "@wordpress/a11y": "^3.38.0", - "@wordpress/components": "^25.4.0", - "@wordpress/data": "^9.8.0", - "@wordpress/i18n": "^4.38.0", - "@wordpress/icons": "^9.29.0", + "@wordpress/a11y": "^3.41.0", + "@wordpress/components": "^25.7.0", + "@wordpress/data": "^9.11.0", + "@wordpress/element": "^5.18.0", + "@wordpress/i18n": "^4.41.0", + "@wordpress/icons": "^9.32.0", "classnames": "^2.3.1" }, "engines": { @@ -6951,12 +7122,12 @@ } }, "node_modules/@wordpress/primitives": { - "version": "3.37.0", - "resolved": "https://registry.npmjs.org/@wordpress/primitives/-/primitives-3.37.0.tgz", - "integrity": "sha512-d+TioEE+T4Hye0CKIQwxTQVdMOFnk/GJW1BS0NatNWKcwaj3nQszxHkWAUWYg401dKx+TDeYae4He1ufur5AWA==", + "version": "3.39.0", + "resolved": "https://registry.npmjs.org/@wordpress/primitives/-/primitives-3.39.0.tgz", + "integrity": "sha512-QvtVJFQGCOEwdXIpXqktz31p327lj/5n4iwbIlOaGf0AZCPA8974m+O/wFkQCskGOJ9tVRveqZtH60aqKH/ffA==", "dependencies": { "@babel/runtime": "^7.16.0", - "@wordpress/element": "^5.16.0", + "@wordpress/element": "^5.18.0", "classnames": "^2.3.1" }, "engines": { @@ -6964,9 +7135,9 @@ } }, "node_modules/@wordpress/priority-queue": { - "version": "2.39.0", - "resolved": "https://registry.npmjs.org/@wordpress/priority-queue/-/priority-queue-2.39.0.tgz", - "integrity": "sha512-noC3ywF5JpAbO+8GTV2p3WF1/W1Lmtgg4WCF+B5L4FY4ejaCXG2e78Wq30ipuVyMfV/LTnxOiRZM8vrEeDu/Rg==", + "version": "2.41.0", + "resolved": "https://registry.npmjs.org/@wordpress/priority-queue/-/priority-queue-2.41.0.tgz", + "integrity": "sha512-OntRPhdybFO5da+MO0/pvnRYG+D+c1kU0KRPDuEa+ArZmmQ/lQC+z+/du9nP/cgvQCAzLHJo2/VKCSQZVyewKw==", "dev": true, "dependencies": { "@babel/runtime": "^7.16.0", @@ -6977,9 +7148,9 @@ } }, "node_modules/@wordpress/private-apis": { - "version": "0.20.0", - "resolved": "https://registry.npmjs.org/@wordpress/private-apis/-/private-apis-0.20.0.tgz", - "integrity": "sha512-byyPRUNAD8/ca9N8gP2rUr8DHuMbzSoXO03nP8g3cecTN6iCOWqFDm6adkrbiAX527N9Ip+GOrRJd7Tta4kRIg==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@wordpress/private-apis/-/private-apis-0.23.0.tgz", + "integrity": "sha512-Z/mAnPF+IcN13S0k7F55lA5J0ducNB+IYDGtujErx4fjnYsBIoF9VeQ5pRc+SPDCx5yYDtR7yL8unLbVbztVBQ==", "dev": true, "dependencies": { "@babel/runtime": "^7.16.0" @@ -6989,9 +7160,9 @@ } }, "node_modules/@wordpress/redux-routine": { - "version": "4.39.0", - "resolved": "https://registry.npmjs.org/@wordpress/redux-routine/-/redux-routine-4.39.0.tgz", - "integrity": "sha512-aRPItqSR5W8uXh4733qo3LNST+Y0LB/uqbWAyXY3a88XWn4vyNLkwYLb4xF+H6oPcpE91z0DyFLC4fsAGxtF4w==", + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@wordpress/redux-routine/-/redux-routine-4.41.0.tgz", + "integrity": "sha512-u5MVqtcChtHOqy3Fk5ml0HPBF13uHoz6ca1XvzstHKOiMWi4xNj6Nn2YgnscfvsxGxxjQz2su1Qcs0SGZI0g+w==", "dev": true, "dependencies": { "@babel/runtime": "^7.16.0", @@ -7007,20 +7178,20 @@ } }, "node_modules/@wordpress/rich-text": { - "version": "6.16.0", - "resolved": "https://registry.npmjs.org/@wordpress/rich-text/-/rich-text-6.16.0.tgz", - "integrity": "sha512-3Q6sxmA4XOwj1a92CiIk6Zhar2xIw2Dat9R4XPltMLdfSiMB1PJxSirSCYmCs8Pq8i5B6OHdSxparR/snhxyWA==", + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/@wordpress/rich-text/-/rich-text-6.18.0.tgz", + "integrity": "sha512-BuBb/yWFLq+/joYI3XMYyF7MQDVYGh8V4AB0+v4mHH+7cTecSOOilUAW/JlEXqXS3LhvghabBk157UaH1gh8IA==", "dev": true, "dependencies": { "@babel/runtime": "^7.16.0", - "@wordpress/a11y": "^3.39.0", - "@wordpress/compose": "^6.16.0", - "@wordpress/data": "^9.9.0", - "@wordpress/deprecated": "^3.39.0", - "@wordpress/element": "^5.16.0", - "@wordpress/escape-html": "^2.39.0", - "@wordpress/i18n": "^4.39.0", - "@wordpress/keycodes": "^3.39.0", + "@wordpress/a11y": "^3.41.0", + "@wordpress/compose": "^6.18.0", + "@wordpress/data": "^9.11.0", + "@wordpress/deprecated": "^3.41.0", + "@wordpress/element": "^5.18.0", + "@wordpress/escape-html": "^2.41.0", + "@wordpress/i18n": "^4.41.0", + "@wordpress/keycodes": "^3.41.0", "memize": "^2.1.0", "rememo": "^4.0.2" }, @@ -7243,9 +7414,9 @@ } }, "node_modules/@wordpress/shortcode": { - "version": "3.38.0", - "resolved": "https://registry.npmjs.org/@wordpress/shortcode/-/shortcode-3.38.0.tgz", - "integrity": "sha512-IaMNs7wCrtzRZBqT8NLq6JwSSpSxpThPaTA9cS9KSzJkv55L2Qmse3XLvpoZSfcxK/Rr0bwssIj22M4O3MQCAA==", + "version": "3.41.0", + "resolved": "https://registry.npmjs.org/@wordpress/shortcode/-/shortcode-3.41.0.tgz", + "integrity": "sha512-pJuFIQbGwn8tMpPf5vtKZqQmXVm2+UnQZXVyyQGXUCBnvyGmGnbKv7lbx0NGUDBZ1AVo8/PAdssd7P/QUn7Cxg==", "dev": true, "dependencies": { "@babel/runtime": "^7.16.0", @@ -7256,9 +7427,9 @@ } }, "node_modules/@wordpress/style-engine": { - "version": "1.21.0", - "resolved": "https://registry.npmjs.org/@wordpress/style-engine/-/style-engine-1.21.0.tgz", - "integrity": "sha512-A6QgWui6VcAkBsrjFu2yx/VdwR81JsMz+vfEbHYmDhPMcA/TvL8Owy1CS8glEwjIdWcCjzXTtEmU9ySRHqcEYg==", + "version": "1.24.0", + "resolved": "https://registry.npmjs.org/@wordpress/style-engine/-/style-engine-1.24.0.tgz", + "integrity": "sha512-1qXkNwbuLeliQZ0yjdjjy2kJzWtvm56k0xUtFsF/0thMQ04EV0JJHwWDI6EOUCIVVFnJO6xdj2UK/KtnxGYfVw==", "dev": true, "dependencies": { "@babel/runtime": "^7.16.0", @@ -7285,9 +7456,9 @@ } }, "node_modules/@wordpress/token-list": { - "version": "2.38.0", - "resolved": "https://registry.npmjs.org/@wordpress/token-list/-/token-list-2.38.0.tgz", - "integrity": "sha512-HF7yGp2kme44+GnXBby+/2gTp1i4bbXK3n+xI55jZMQhPECcpxOCkJDl/vxXfVJaLTkL96RIbJTf4Be6xxyQ/w==", + "version": "2.41.0", + "resolved": "https://registry.npmjs.org/@wordpress/token-list/-/token-list-2.41.0.tgz", + "integrity": "sha512-GeGPzyoVshaifYiz11KqBwkUDqAbMVVkHn5ccpqLVbn+Yc7Ur3tRJ5ikgqNikSq0EyvnHdy8XdivdLJq2xcduA==", "dev": true, "dependencies": { "@babel/runtime": "^7.16.0" @@ -7297,31 +7468,31 @@ } }, "node_modules/@wordpress/url": { - "version": "3.39.0", - "resolved": "https://registry.npmjs.org/@wordpress/url/-/url-3.39.0.tgz", - "integrity": "sha512-+s7eEZgBkyNm93RNDeIzq5oJBMA6lAEH27hyJkY6sRamDbaercZyYSr/q3If8c6hHsbLeVMJpd3NkiZpe384Lg==", + "version": "3.42.0", + "resolved": "https://registry.npmjs.org/@wordpress/url/-/url-3.42.0.tgz", + "integrity": "sha512-Q1eZAkgnq/Ji3UDdBPxj2mBiBusGoTkcUH2XnJDGyPIezJjC7fY/9GXE6Jj0bm37CkEH3bP6G4Yrh+YpDwMn6Q==", "dev": true, "dependencies": { "@babel/runtime": "^7.16.0", - "remove-accents": "^0.4.2" + "remove-accents": "^0.5.0" }, "engines": { "node": ">=12" } }, "node_modules/@wordpress/warning": { - "version": "2.39.0", - "resolved": "https://registry.npmjs.org/@wordpress/warning/-/warning-2.39.0.tgz", - "integrity": "sha512-GBOe63ROWqGh7z83VXTJQes/nMI82Q/GKs7pv52Q6EUBk/CE3LTnfDMw/+WbtGBMDc+Rd07gYqB/OHoedk2wkg==", + "version": "2.41.0", + "resolved": "https://registry.npmjs.org/@wordpress/warning/-/warning-2.41.0.tgz", + "integrity": "sha512-kSqx1z7MaNjNFg+/b5H9oY5D6hYpsKPvaonpk5CADTXOoWoEdSSkwDnCZWxaXu3Kgz4qB5EmaC4D3bKTFkFldw==", "dev": true, "engines": { "node": ">=12" } }, "node_modules/@wordpress/wordcount": { - "version": "3.38.0", - "resolved": "https://registry.npmjs.org/@wordpress/wordcount/-/wordcount-3.38.0.tgz", - "integrity": "sha512-HXoN4MmPGo0Z21DJktvoPPGslaU9JhDfX67aOrBzJMFx06nDZYq9QtChNRMqlhz3w+hGfCC5FToJeXVvgfy79w==", + "version": "3.41.0", + "resolved": "https://registry.npmjs.org/@wordpress/wordcount/-/wordcount-3.41.0.tgz", + "integrity": "sha512-1GjIVx+JIeYl7mmRZf9AA5e/YhmGm043dt1/3Zto1k7/MLIpI5ValPPXlHWBA2GU1lfHdfMkSzSC5CLzBQM2ow==", "dev": true, "dependencies": { "@babel/runtime": "^7.16.0" @@ -9217,6 +9388,20 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/cmdk": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/cmdk/-/cmdk-0.2.0.tgz", + "integrity": "sha512-JQpKvEOb86SnvMZbYaFKYhvzFntWBeSZdyii0rZPhKJj9uwJBxu4DaVYDrRN7r3mPop56oPhRw+JYWTKs66TYw==", + "dev": true, + "dependencies": { + "@radix-ui/react-dialog": "1.0.0", + "command-score": "0.1.2" + }, + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0" + } + }, "node_modules/co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -9297,6 +9482,12 @@ "node": ">= 0.8" } }, + "node_modules/command-score": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/command-score/-/command-score-0.1.2.tgz", + "integrity": "sha512-VtDvQpIJBvBatnONUsPzXYFVKQQAhuf3XTNOAsdBxCNO/QCtUUd8LSgjn0GVarBkCad6aJCZfXgrjYbl/KRr7w==", + "dev": true + }, "node_modules/commander": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", @@ -13501,9 +13692,9 @@ "dev": true }, "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "hasInstallScript": true, "optional": true, "os": [ @@ -24363,9 +24554,9 @@ "dev": true }, "node_modules/remove-accents": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/remove-accents/-/remove-accents-0.4.4.tgz", - "integrity": "sha512-EpFcOa/ISetVHEXqu+VwI96KZBmq+a8LJnGkaeFw45epGlxIZz5dhEEnNZMsQXgORu3qaMoLX4qJCzOik6ytAg==", + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/remove-accents/-/remove-accents-0.5.0.tgz", + "integrity": "sha512-8g3/Otx1eJaVD12e31UbJj1YzdtVvzH85HV7t+9MJYk/u3XmkOUJ5Ys9wQrf9PCPK8+xn4ymzqYCiZl6QWKn+A==", "dev": true }, "node_modules/repeat-element": { @@ -27588,9 +27779,9 @@ "dev": true }, "node_modules/typescript": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", - "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", + "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", "dev": true, "bin": { "tsc": "bin/tsc", diff --git a/package.json b/package.json index 2678797cc..1d05dc4c9 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,7 @@ "lasttranslator": "Themeisle Translate Team " }, "dependencies": { - "@wordpress/icons": "^9.29.0", + "@wordpress/icons": "^9.32.0", "array-move": "^3.0.1", "classnames": "^2.3.1", "currency-symbol-map": "^5.0.1", @@ -78,16 +78,16 @@ "@types/wordpress__block-editor": "^11.5.1", "@types/wordpress__components": "^23.0.1", "@typescript-eslint/parser": "^6.3.0", - "@wordpress/block-editor": "^12.6.0", - "@wordpress/components": "^25.5.0", + "@wordpress/block-editor": "^12.9.0", + "@wordpress/components": "^25.7.0", "@wordpress/compose": "^6.15.0", "@wordpress/data": "^9.7.0", - "@wordpress/dom-ready": "^3.38.0", + "@wordpress/dom-ready": "^3.41.0", "@wordpress/e2e-test-utils": "^10.4.0", - "@wordpress/e2e-test-utils-playwright": "^0.2.0", + "@wordpress/e2e-test-utils-playwright": "^0.9.0", "@wordpress/e2e-tests": "^7.9.0", - "@wordpress/element": "^5.14.0", - "@wordpress/env": "^8.4.0", + "@wordpress/element": "^5.18.0", + "@wordpress/env": "^8.7.0", "@wordpress/scripts": "^26.9.0", "conventional-changelog-simple-preset": "^1.0.20", "eslint-config-wordpress": "^2.0.0", @@ -105,10 +105,10 @@ "semantic-release": "^19.0.5", "semantic-release-slack-bot": "^3.5.2", "simple-statistics": "^7.8.3", - "typescript": "^5.1.6" + "typescript": "^5.2.2" }, "optionalDependencies": { - "fsevents": "^2.3.2" + "fsevents": "^2.3.3" }, "overrides": { "react": "^17.0.0", diff --git a/plugins/otter-pro/inc/class-main.php b/plugins/otter-pro/inc/class-main.php index 4b7fabf6c..406876b67 100644 --- a/plugins/otter-pro/inc/class-main.php +++ b/plugins/otter-pro/inc/class-main.php @@ -105,6 +105,8 @@ public function register_blocks( $blocks ) { 'product-upsells', 'review-comparison', 'form-file', + 'form-hidden-field', + 'form-stripe-field', ); $blocks = array_merge( $blocks, $pro_blocks ); @@ -136,6 +138,8 @@ public function register_dynamic_blocks( $dynamic_blocks ) { 'product-upsells' => '\ThemeIsle\OtterPro\Render\WooCommerce\Product_Upsells_Block', 'review-comparison' => '\ThemeIsle\OtterPro\Render\Review_Comparison_Block', 'form-file' => '\ThemeIsle\OtterPro\Render\Form_File_Block', + 'form-hidden-field' => '\ThemeIsle\OtterPro\Render\Form_Hidden_Block', + 'form-stripe-field' => '\ThemeIsle\OtterPro\Render\Form_Stripe_Block', ); $dynamic_blocks = array_merge( $dynamic_blocks, $blocks ); @@ -157,6 +161,7 @@ public function register_blocks_css( $blocks ) { '\ThemeIsle\OtterPro\CSS\Blocks\Business_Hours_Item_CSS', '\ThemeIsle\OtterPro\CSS\Blocks\Review_Comparison_CSS', '\ThemeIsle\OtterPro\CSS\Blocks\Form_File_CSS', + '\ThemeIsle\OtterPro\CSS\Blocks\Form_Stripe_Field_CSS', ); $blocks = array_merge( $blocks, $pro_blocks ); diff --git a/plugins/otter-pro/inc/css/blocks/class-form-stripe-field-css.php b/plugins/otter-pro/inc/css/blocks/class-form-stripe-field-css.php new file mode 100644 index 000000000..33731b283 --- /dev/null +++ b/plugins/otter-pro/inc/css/blocks/class-form-stripe-field-css.php @@ -0,0 +1,70 @@ +add_item( + array( + 'properties' => array( + array( + 'property' => '--label-color', + 'value' => 'labelColor', + ), + array( + 'property' => '--stripe-border-color', + 'value' => 'borderColor', + ), + array( + 'property' => '--stripe-border-radius', + 'value' => 'borderRadius', + 'format' => function( $value ) { + return CSS_Utility::render_box( $value ); + }, + ), + array( + 'property' => '--stripe-border-width', + 'value' => 'borderWidth', + 'format' => function( $value ) { + return CSS_Utility::render_box( $value ); + }, + ), + ), + ) + ); + + $style = $css->generate(); + + return $style; + } + +} diff --git a/plugins/otter-pro/inc/plugins/class-form-emails-storing.php b/plugins/otter-pro/inc/plugins/class-form-emails-storing.php index 7efdf83fe..f69dfa984 100644 --- a/plugins/otter-pro/inc/plugins/class-form-emails-storing.php +++ b/plugins/otter-pro/inc/plugins/class-form-emails-storing.php @@ -8,6 +8,9 @@ namespace ThemeIsle\OtterPro\Plugins; use ThemeIsle\GutenbergBlocks\Integration\Form_Data_Request; +use ThemeIsle\GutenbergBlocks\Integration\Form_Data_Response; +use ThemeIsle\GutenbergBlocks\Integration\Form_Settings_Data; +use ThemeIsle\GutenbergBlocks\Plugins\Stripe_API; use ThemeIsle\GutenbergBlocks\Server\Form_Server; use WP_Post; use WP_Query; @@ -44,6 +47,7 @@ public function init() { add_action( 'init', array( $this, 'create_form_records_type' ) ); add_action( 'admin_init', array( $this, 'set_form_records_cap' ), 10, 0 ); add_action( 'otter_form_after_submit', array( $this, 'store_form_record' ) ); + add_action( 'admin_head', array( $this, 'add_style' ) ); // Customize the wp_list_table. @@ -67,6 +71,13 @@ public function init() { add_action( 'add_meta_boxes', array( $this, 'add_form_record_meta_box' ) ); add_action( 'admin_menu', array( $this, 'handle_admin_menu' ) ); add_action( 'save_post', array( $this, 'form_record_save_meta_box' ), 10, 2 ); + + add_filter( 'otter_form_record_confirm', array( $this, 'confirm_submission' ), 10, 2 ); + + add_action( 'draft_to_unread', array( $this, 'apply_hooks_on_draft_transition' ), 10 ); + add_action( 'otter_form_update_record_meta_dump', array( $this, 'update_submission_dump_data' ), 10, 2 ); + add_action( 'otter_form_automatic_confirmation', array( $this, 'move_old_stripe_draft_sessions_to_unread' ) ); + add_action( 'wp', array( $this, 'schedule_automatic_confirmation' ) ); } /** @@ -175,20 +186,24 @@ public function store_form_record( $form_data ) { return $form_data; } - $form_options = $form_data->get_form_options(); + $form_options = $form_data->get_wp_options(); if ( ! isset( $form_options ) ) { return $form_data; } - if ( false === strpos( $form_options->get_submissions_save_location(), 'database' ) ) { + if ( $form_data->is_duplicate() ) { + return $form_data; + } + + if ( false === strpos( $form_options->get_submissions_save_location(), 'database' ) && ! $form_data->is_temporary() ) { return $form_data; } $post_id = wp_insert_post( array( 'post_type' => self::FORM_RECORD_TYPE, - 'post_status' => 'unread', + 'post_status' => $form_data->is_temporary() ? 'draft' : 'unread', ) ); @@ -206,20 +221,24 @@ public function store_form_record( $form_data ) { $meta = array( 'form' => array( - 'label' => 'Form', - 'value' => $form_data->get_payload_field( 'formId' ), + 'label' => __( 'Form', 'otter-blocks' ), + 'value' => $form_data->get_data_from_payload( 'formId' ), ), 'post_url' => array( - 'label' => 'Post URL', - 'value' => $form_data->get_payload_field( 'postUrl' ), + 'label' => __( 'Post URL', 'otter-blocks' ), + 'value' => $form_data->get_data_from_payload( 'postUrl' ), ), 'post_id' => array( - 'label' => 'Post ID', - 'value' => $form_data->get_payload_field( 'postId' ), + 'label' => __( 'Post ID', 'otter-blocks' ), + 'value' => $form_data->get_data_from_payload( 'postId' ), + ), + 'dump' => array( + 'label' => __( 'Dumped data', 'otter-blocks' ), + 'value' => $form_data->is_temporary() ? $form_data->dump_data() : array(), ), ); - $form_inputs = $form_data->get_form_inputs(); + $form_inputs = $form_data->get_fields(); $uploaded_files = $form_data->get_uploaded_files_path(); $media_files = $form_data->get_files_loaded_to_media_library(); @@ -273,6 +292,9 @@ public function store_form_record( $form_data ) { } add_post_meta( $post_id, self::FORM_RECORD_META_KEY, $meta ); + + $form_data->metadata['otter_form_record_id'] = $post_id; + return $form_data; } @@ -699,13 +721,24 @@ public function fields_meta_box_markup( $post ) { $meta = get_post_meta( $post->ID, self::FORM_RECORD_META_KEY, true ); $previous_field_option = ''; + if ( empty( $meta ) ) { return; } + + $inputs = array(); + foreach ( $meta['inputs'] as $id => $field ) { + if ( empty( $field ) || 'stripe-field' === $field['type'] ) { + continue; + } + + $inputs[ $id ] = $field; + } + ?> - $field ) { ?> + $field ) { ?>