diff --git a/includes/Classifai/Providers/AWS/AmazonPolly.php b/includes/Classifai/Providers/AWS/AmazonPolly.php index cd67f16a4..d484500ae 100644 --- a/includes/Classifai/Providers/AWS/AmazonPolly.php +++ b/includes/Classifai/Providers/AWS/AmazonPolly.php @@ -198,61 +198,44 @@ public function get_default_provider_settings(): array { * @return array */ public function sanitize_settings( array $new_settings ): array { - $settings = $this->feature_instance->get_settings(); - $is_credentials_changed = false; + $settings = $this->feature_instance->get_settings(); + $authenticated = $this->authenticate_credentials( $new_settings ); - $new_settings[ static::ID ]['authenticated'] = $settings[ static::ID ]['authenticated']; - $new_settings[ static::ID ]['voices'] = $settings[ static::ID ]['voices']; - - if ( - ! empty( $new_settings[ static::ID ]['access_key_id'] ) && - ! empty( $new_settings[ static::ID ]['secret_access_key'] ) && - ! empty( $new_settings[ static::ID ]['aws_region'] ) - ) { - $new_access_key_id = sanitize_text_field( $new_settings[ static::ID ]['access_key_id'] ); - $new_secret_access_key = sanitize_text_field( $new_settings[ static::ID ]['secret_access_key'] ); - $new_aws_region = sanitize_text_field( $new_settings[ static::ID ]['aws_region'] ); - - if ( - $new_access_key_id !== $settings[ static::ID ]['access_key_id'] || - $new_secret_access_key !== $settings[ static::ID ]['secret_access_key'] || - $new_aws_region !== $settings[ static::ID ]['aws_region'] - ) { - $is_credentials_changed = true; - } - - if ( $is_credentials_changed ) { - $new_settings[ static::ID ]['access_key_id'] = $new_access_key_id; - $new_settings[ static::ID ]['secret_access_key'] = $new_secret_access_key; - $new_settings[ static::ID ]['aws_region'] = $new_aws_region; - - // Connect to the service and get voices. - $new_settings[ static::ID ]['voices'] = $this->connect_to_service( - array( - 'access_key_id' => $new_access_key_id, - 'secret_access_key' => $new_secret_access_key, - 'aws_region' => $new_aws_region, - ) - ); - - if ( ! empty( $new_settings[ static::ID ]['voices'] ) ) { - $new_settings[ static::ID ]['authenticated'] = true; - } else { - $new_settings[ static::ID ]['voices'] = []; - $new_settings[ static::ID ]['authenticated'] = false; - } - } - } else { - $new_settings[ static::ID ]['access_key_id'] = $settings[ static::ID ]['access_key_id']; - $new_settings[ static::ID ]['secret_access_key'] = $settings[ static::ID ]['secret_access_key']; - $new_settings[ static::ID ]['aws_region'] = $settings[ static::ID ]['aws_region']; + if ( is_wp_error( $authenticated ) ) { + $new_settings[ static::ID ]['authenticated'] = false; add_settings_error( - $this->feature_instance->get_option_name(), - 'classifai-ams-polly-auth-empty', - esc_html__( 'One or more credentials required to connect to the Amazon Polly service is empty.', 'classifai' ), + 'api_key', + 'classifai-auth', + $authenticated->get_error_message(), 'error' ); + } else { + $new_settings[ static::ID ]['authenticated'] = true; + } + + $new_access_key_id = sanitize_text_field( $new_settings[ static::ID ]['access_key_id'] ?? $settings[ static::ID ]['access_key_id'] ); + $new_secret_access_key = sanitize_text_field( $new_settings[ static::ID ]['secret_access_key'] ?? $settings[ static::ID ]['secret_access_key'] ); + $new_aws_region = sanitize_text_field( $new_settings[ static::ID ]['aws_region'] ?? $settings[ static::ID ]['aws_region'] ); + + $new_settings[ static::ID ]['access_key_id'] = $new_access_key_id; + $new_settings[ static::ID ]['secret_access_key'] = $new_secret_access_key; + $new_settings[ static::ID ]['aws_region'] = $new_aws_region; + + // Connect to the service and get voices. + $new_settings[ static::ID ]['voices'] = $this->connect_to_service( + array( + 'access_key_id' => $new_access_key_id, + 'secret_access_key' => $new_secret_access_key, + 'aws_region' => $new_aws_region, + ) + ); + + if ( ! empty( $new_settings[ static::ID ]['voices'] ) ) { + $new_settings[ static::ID ]['authenticated'] = true; + } else { + $new_settings[ static::ID ]['voices'] = []; + $new_settings[ static::ID ]['authenticated'] = false; } $new_settings[ static::ID ]['voice'] = sanitize_text_field( $new_settings[ static::ID ]['voice'] ?? $settings[ static::ID ]['voice'] ); @@ -260,6 +243,53 @@ public function sanitize_settings( array $new_settings ): array { return $new_settings; } + /** + * Authenticate our credentials. + * + * @param array $settings Settings being saved. + * @return bool|WP_Error + */ + protected function authenticate_credentials( array $settings = [] ) { + $response = false; + + try { + /** + * Filters the return value of the connect to services function. + * + * Returning a non-false value from the filter will short-circuit + * the describe voices request and return early with that value. + * This filter is useful for E2E tests. + * + * @since 3.1.0 + * @hook classifai_aws_polly_pre_connect_to_service + * + * @param bool $pre The value of pre connect to service. Default false. A non-false value will short-circuit the describe voices request. + * + * @return bool|mixed The filtered value of connect to service. + */ + $pre = apply_filters( 'classifai_' . self::ID . '_pre_connect_to_service', false ); + + if ( false !== $pre ) { + return $pre; + } + + $polly_client = $this->get_polly_client( $settings[ static::ID ] ); + + if ( $polly_client ) { + $polly_voices = $polly_client->describeVoices(); + $polly_voices = $polly_voices->get( 'Voices' ); + } else { + $polly_voices = []; + } + + $response = ! empty( $polly_voices ) ? true : new WP_Error( 'auth', esc_html__( 'Connection to Amazon Polly failed.', 'classifai' ) ); + } catch ( \Exception $e ) { + $response = new WP_Error( 'auth', esc_html__( 'Connection to Amazon Polly failed.', 'classifai' ) ); + } + + return ! is_wp_error( $response ) ? true : $response; + } + /** * Connects to the Amazon Polly service. * diff --git a/includes/Classifai/Providers/Azure/ComputerVision.php b/includes/Classifai/Providers/Azure/ComputerVision.php index c9efca667..b7daba47d 100644 --- a/includes/Classifai/Providers/Azure/ComputerVision.php +++ b/includes/Classifai/Providers/Azure/ComputerVision.php @@ -204,47 +204,31 @@ public function get_default_provider_settings(): array { } /** - * Sanitization + * Sanitize the settings for this Provider. * * @param array $new_settings The settings being saved. * @return array|mixed */ public function sanitize_settings( array $new_settings ) { - $settings = $this->feature_instance->get_settings(); + $settings = $this->feature_instance->get_settings(); + $authenticated = $this->authenticate_credentials( $new_settings ); - if ( ! empty( $new_settings[ static::ID ]['endpoint_url'] ) && ! empty( $new_settings[ static::ID ]['api_key'] ) ) { - $new_settings[ static::ID ]['authenticated'] = $settings[ static::ID ]['authenticated']; - $new_settings[ static::ID ]['endpoint_url'] = esc_url_raw( $new_settings[ static::ID ]['endpoint_url'] ?? $settings[ static::ID ]['endpoint_url'] ); - $new_settings[ static::ID ]['api_key'] = sanitize_text_field( $new_settings[ static::ID ]['api_key'] ?? $settings[ static::ID ]['api_key'] ); + if ( is_wp_error( $authenticated ) ) { + $new_settings[ static::ID ]['authenticated'] = false; - $is_authenticated = $new_settings[ static::ID ]['authenticated']; - $is_endpoint_same = $new_settings[ static::ID ]['endpoint_url'] === $settings[ static::ID ]['endpoint_url']; - $is_api_key_same = $new_settings[ static::ID ]['api_key'] === $settings[ static::ID ]['api_key']; - - if ( ! ( $is_authenticated && $is_endpoint_same && $is_api_key_same ) ) { - $auth_check = $this->authenticate_credentials( $new_settings ); - - if ( is_wp_error( $auth_check ) ) { - $new_settings[ static::ID ]['authenticated'] = false; - - $error_message = $auth_check->get_error_message(); - - // Add an error message. - add_settings_error( - 'api_key', - 'classifai-auth', - $error_message, - 'error' - ); - } else { - $new_settings[ static::ID ]['authenticated'] = true; - } - } + add_settings_error( + 'api_key', + 'classifai-auth', + $authenticated->get_error_message(), + 'error' + ); } else { - $new_settings[ static::ID ]['endpoint_url'] = $settings[ static::ID ]['endpoint_url']; - $new_settings[ static::ID ]['api_key'] = $settings[ static::ID ]['api_key']; + $new_settings[ static::ID ]['authenticated'] = true; } + $new_settings[ static::ID ]['endpoint_url'] = esc_url_raw( $new_settings[ static::ID ]['endpoint_url'] ?? $settings[ static::ID ]['endpoint_url'] ); + $new_settings[ static::ID ]['api_key'] = sanitize_text_field( $new_settings[ static::ID ]['api_key'] ?? $settings[ static::ID ]['api_key'] ); + if ( $this->feature_instance instanceof DescriptiveTextGenerator ) { $new_settings[ static::ID ]['descriptive_confidence_threshold'] = floatval( $new_settings[ static::ID ]['descriptive_confidence_threshold'] ?? $settings[ static::ID ]['descriptive_confidence_threshold'] ); } diff --git a/includes/Classifai/Providers/Azure/OpenAI.php b/includes/Classifai/Providers/Azure/OpenAI.php index 836a47e25..d9d39ec1c 100644 --- a/includes/Classifai/Providers/Azure/OpenAI.php +++ b/includes/Classifai/Providers/Azure/OpenAI.php @@ -200,47 +200,26 @@ public function get_default_provider_settings(): array { * @return array */ public function sanitize_settings( array $new_settings ): array { - $settings = $this->feature_instance->get_settings(); - - if ( - ! empty( $new_settings[ static::ID ]['endpoint_url'] ) && - ! empty( $new_settings[ static::ID ]['api_key'] ) && - ! empty( $new_settings[ static::ID ]['deployment'] ) - ) { - $new_settings[ static::ID ]['authenticated'] = $settings[ static::ID ]['authenticated']; - $new_settings[ static::ID ]['endpoint_url'] = esc_url_raw( $new_settings[ static::ID ]['endpoint_url'] ?? $settings[ static::ID ]['endpoint_url'] ); - $new_settings[ static::ID ]['api_key'] = sanitize_text_field( $new_settings[ static::ID ]['api_key'] ?? $settings[ static::ID ]['api_key'] ); - $new_settings[ static::ID ]['deployment'] = sanitize_text_field( $new_settings[ static::ID ]['deployment'] ?? $settings[ static::ID ]['deployment'] ); - - $is_authenticated = $new_settings[ static::ID ]['authenticated']; - $is_endpoint_same = $new_settings[ static::ID ]['endpoint_url'] === $settings[ static::ID ]['endpoint_url']; - $is_api_key_same = $new_settings[ static::ID ]['api_key'] === $settings[ static::ID ]['api_key']; - $is_deployment_same = $new_settings[ static::ID ]['deployment'] === $settings[ static::ID ]['deployment']; - - if ( ! ( $is_authenticated && $is_endpoint_same && $is_api_key_same && $is_deployment_same ) ) { - $auth_check = $this->authenticate_credentials( $new_settings ); - - if ( is_wp_error( $auth_check ) ) { - $new_settings[ static::ID ]['authenticated'] = false; - $error_message = $auth_check->get_error_message(); - - // Add an error message. - add_settings_error( - 'api_key', - 'classifai-auth', - $error_message, - 'error' - ); - } else { - $new_settings[ static::ID ]['authenticated'] = true; - } - } + $settings = $this->feature_instance->get_settings(); + $authenticated = $this->authenticate_credentials( $new_settings ); + + if ( is_wp_error( $authenticated ) ) { + $new_settings[ static::ID ]['authenticated'] = false; + + add_settings_error( + 'api_key', + 'classifai-auth', + $authenticated->get_error_message(), + 'error' + ); } else { - $new_settings[ static::ID ]['endpoint_url'] = $settings[ static::ID ]['endpoint_url']; - $new_settings[ static::ID ]['api_key'] = $settings[ static::ID ]['api_key']; - $new_settings[ static::ID ]['deployment'] = $settings[ static::ID ]['deployment']; + $new_settings[ static::ID ]['authenticated'] = true; } + $new_settings[ static::ID ]['endpoint_url'] = esc_url_raw( $new_settings[ static::ID ]['endpoint_url'] ?? $settings[ static::ID ]['endpoint_url'] ); + $new_settings[ static::ID ]['api_key'] = sanitize_text_field( $new_settings[ static::ID ]['api_key'] ?? $settings[ static::ID ]['api_key'] ); + $new_settings[ static::ID ]['deployment'] = sanitize_text_field( $new_settings[ static::ID ]['deployment'] ?? $settings[ static::ID ]['deployment'] ); + switch ( $this->feature_instance::ID ) { case ContentResizing::ID: case TitleGeneration::ID: diff --git a/includes/Classifai/Providers/Azure/Speech.php b/includes/Classifai/Providers/Azure/Speech.php index fd56b8b35..90b81b5e2 100644 --- a/includes/Classifai/Providers/Azure/Speech.php +++ b/includes/Classifai/Providers/Azure/Speech.php @@ -132,49 +132,40 @@ public function get_default_provider_settings(): array { * @return array */ public function sanitize_settings( array $new_settings ): array { - $settings = $this->feature_instance->get_settings(); - $is_credentials_changed = false; + $settings = $this->feature_instance->get_settings(); + $authenticated = $this->authenticate_credentials( $new_settings ); - $new_settings[ static::ID ]['authenticated'] = $settings[ static::ID ]['authenticated']; - $new_settings[ static::ID ]['voices'] = $settings[ static::ID ]['voices']; - - if ( ! empty( $new_settings[ static::ID ]['endpoint_url'] ) && ! empty( $new_settings[ static::ID ]['api_key'] ) ) { - $new_url = trailingslashit( esc_url_raw( $new_settings[ static::ID ]['endpoint_url'] ) ); - $new_key = sanitize_text_field( $new_settings[ static::ID ]['api_key'] ); - - if ( $new_url !== $settings[ static::ID ]['endpoint_url'] || $new_key !== $settings[ static::ID ]['api_key'] ) { - $is_credentials_changed = true; - } - - if ( $is_credentials_changed ) { - $new_settings[ static::ID ]['endpoint_url'] = $new_url; - $new_settings[ static::ID ]['api_key'] = $new_key; - - // Connect to the service and get voices. - $new_settings[ static::ID ]['voices'] = $this->connect_to_service( - array( - 'endpoint_url' => $new_url, - 'api_key' => $new_key, - ) - ); - - if ( ! empty( $new_settings[ static::ID ]['voices'] ) ) { - $new_settings[ static::ID ]['authenticated'] = true; - } else { - $new_settings[ static::ID ]['voices'] = []; - $new_settings[ static::ID ]['authenticated'] = false; - } - } - } else { - $new_settings[ static::ID ]['endpoint_url'] = $settings[ static::ID ]['endpoint_url']; - $new_settings[ static::ID ]['api_key'] = $settings[ static::ID ]['api_key']; + if ( is_wp_error( $authenticated ) ) { + $new_settings[ static::ID ]['authenticated'] = false; add_settings_error( - $this->feature_instance->get_option_name(), - 'classifai-azure-text-to-speech-auth-empty', - esc_html__( 'One or more credentials required to connect to the Azure Text to Speech service is empty.', 'classifai' ), + 'api_key', + 'classifai-auth', + $authenticated->get_error_message(), 'error' ); + } else { + $new_settings[ static::ID ]['authenticated'] = true; + } + + $new_settings[ static::ID ]['voices'] = $settings[ static::ID ]['voices']; + + $new_settings[ static::ID ]['endpoint_url'] = trailingslashit( esc_url_raw( $new_settings[ static::ID ]['endpoint_url'] ?? $settings[ static::ID ]['endpoint_url'] ) ); + $new_settings[ static::ID ]['api_key'] = sanitize_text_field( $new_settings[ static::ID ]['api_key'] ?? $settings[ static::ID ]['api_key'] ); + + // Connect to the service and get voices. + $new_settings[ static::ID ]['voices'] = $this->connect_to_service( + array( + 'endpoint_url' => $new_settings[ static::ID ]['endpoint_url'], + 'api_key' => $new_settings[ static::ID ]['api_key'], + ) + ); + + if ( ! empty( $new_settings[ static::ID ]['voices'] ) ) { + $new_settings[ static::ID ]['authenticated'] = true; + } else { + $new_settings[ static::ID ]['voices'] = []; + $new_settings[ static::ID ]['authenticated'] = false; } $new_settings[ static::ID ]['voice'] = sanitize_text_field( $new_settings[ static::ID ]['voice'] ?? $settings[ static::ID ]['voice'] ); @@ -182,6 +173,36 @@ public function sanitize_settings( array $new_settings ): array { return $new_settings; } + /** + * Authenticates our credentials. + * + * @param array $settings Settings being saved. + * @return bool|WP_Error + */ + public function authenticate_credentials( array $settings = [] ) { + $credentials = $this->get_credentials( $settings ); + + // Create request arguments. + $request_params = array( + 'headers' => array( + 'Ocp-Apim-Subscription-Key' => $credentials['api_key'] ?? '', + 'Content-Type' => 'application/json', + ), + 'timeout' => 20, // phpcs:ignore WordPressVIPMinimum.Performance.RemoteRequestTimeout.timeout_timeout + 'use_vip' => true, + ); + + // Create request URL. + $request_url = sprintf( + '%1$scognitiveservices/voices/list', + trailingslashit( $credentials['endpoint_url'] ?? '' ) + ); + + $response = safe_wp_remote_get( $request_url, $request_params ); + + return ! is_wp_error( $response ) ? true : $response; + } + /** * Connects to Azure's Text to Speech service. * @@ -216,7 +237,7 @@ public function connect_to_service( array $args = array() ): array { // Create request URL. $request_url = sprintf( '%1$scognitiveservices/voices/list', - $credentials['endpoint_url'] ?? '' + trailingslashit( $credentials['endpoint_url'] ?? '' ) ); $response = safe_wp_remote_get( $request_url, $request_params ); diff --git a/includes/Classifai/Providers/ElevenLabs/ElevenLabs.php b/includes/Classifai/Providers/ElevenLabs/ElevenLabs.php index 208eadc1b..5c08c3de6 100644 --- a/includes/Classifai/Providers/ElevenLabs/ElevenLabs.php +++ b/includes/Classifai/Providers/ElevenLabs/ElevenLabs.php @@ -163,22 +163,28 @@ public function request( string $url, string $api_key = '', string $type = 'post * @return array */ public function sanitize_api_key_settings( array $new_settings = [], array $settings = [] ): array { - $models = $this->get_models( $new_settings[ static::ID ]['api_key'] ?? '' ); - - $new_settings[ static::ID ]['authenticated'] = $settings[ static::ID ]['authenticated']; - $new_settings[ static::ID ]['models'] = $settings[ static::ID ]['models']; + $authenticated = $this->authenticate_credentials( $new_settings ); - if ( is_wp_error( $models ) ) { + if ( is_wp_error( $authenticated ) ) { $new_settings[ static::ID ]['authenticated'] = false; - $new_settings[ static::ID ]['models'] = []; - $error_message = $models->get_error_message(); add_settings_error( 'api_key', 'classifai-auth', - $error_message, + $authenticated->get_error_message(), 'error' ); + } else { + $new_settings[ static::ID ]['authenticated'] = true; + } + + $models = $this->get_models( $new_settings[ static::ID ]['api_key'] ?? '' ); + + $new_settings[ static::ID ]['models'] = $settings[ static::ID ]['models']; + + if ( is_wp_error( $models ) ) { + $new_settings[ static::ID ]['authenticated'] = false; + $new_settings[ static::ID ]['models'] = []; } else { $new_settings[ static::ID ]['authenticated'] = true; $new_settings[ static::ID ]['models'] = $models; @@ -207,6 +213,21 @@ public function sanitize_api_key_settings( array $new_settings = [], array $sett return $new_settings; } + /** + * Authenticate our credentials. + * + * @param array $settings The settings. + * @return bool|WP_Error + */ + public function authenticate_credentials( array $settings = [] ) { + $api_key = $settings[ static::ID ]['api_key'] ?? ''; + + // Make request to ensure credentials work. + $response = $this->request( $this->get_api_url( $this->model_path ), $api_key, 'get', [ 'use_vip' => true ] ); + + return ! is_wp_error( $response ) ? true : $response; + } + /** * Get the available models. * @@ -214,11 +235,6 @@ public function sanitize_api_key_settings( array $new_settings = [], array $sett * @return array|WP_Error */ protected function get_models( string $api_key = '' ) { - // Check that we have credentials before hitting the API. - if ( empty( $api_key ) ) { - return new WP_Error( 'auth', esc_html__( 'Please enter your ElevenLabs API key.', 'classifai' ) ); - } - $response = $this->request( $this->get_api_url( $this->model_path ), $api_key, 'get', [ 'use_vip' => true ] ); if ( is_wp_error( $response ) ) { @@ -255,11 +271,6 @@ function ( $model ) { * @return array|WP_Error */ protected function get_voices( string $api_key = '' ) { - // Check that we have credentials before hitting the API. - if ( empty( $api_key ) ) { - return new WP_Error( 'auth', esc_html__( 'Please enter your ElevenLabs API key.', 'classifai' ) ); - } - $response = $this->request( $this->get_api_url( 'voices?per_page=100' ), $api_key, 'get', [ 'use_vip' => true ] ); if ( is_wp_error( $response ) ) { diff --git a/includes/Classifai/Providers/OpenAI/OpenAI.php b/includes/Classifai/Providers/OpenAI/OpenAI.php index 024c0e375..043f276bc 100644 --- a/includes/Classifai/Providers/OpenAI/OpenAI.php +++ b/includes/Classifai/Providers/OpenAI/OpenAI.php @@ -29,8 +29,6 @@ trait OpenAI { public function sanitize_api_key_settings( array $new_settings = [], array $settings = [] ): array { $authenticated = $this->authenticate_credentials( $new_settings ); - $new_settings[ static::ID ]['authenticated'] = $settings[ static::ID ]['authenticated']; - if ( is_wp_error( $authenticated ) ) { $new_settings[ static::ID ]['authenticated'] = false; $error_message = $authenticated->get_error_message(); diff --git a/includes/Classifai/Providers/Watson/NLU.php b/includes/Classifai/Providers/Watson/NLU.php index 2901f7560..341219ce5 100644 --- a/includes/Classifai/Providers/Watson/NLU.php +++ b/includes/Classifai/Providers/Watson/NLU.php @@ -361,20 +361,12 @@ protected function use_username_password(): bool { } /** - * Helper to ensure the authentication works. + * Authenticate our credentials. * * @param array $settings The list of settings to be saved * @return bool|WP_Error */ - protected function authenticate_credentials( array $settings ) { - // Check that we have credentials before hitting the API. - if ( empty( $settings[ static::ID ]['username'] ) - || empty( $settings[ static::ID ]['password'] ) - || empty( $settings[ static::ID ]['endpoint_url'] ) - ) { - return new WP_Error( 'auth', esc_html__( 'Please enter your credentials.', 'classifai' ) ); - } - + protected function authenticate_credentials( array $settings = [] ) { $credentials = $this->get_credentials( $settings ); $request = new APIRequest(); $request->username = $credentials['username'] ?? '';