diff --git a/includes/Classifai/Features/DescriptiveTextGenerator.php b/includes/Classifai/Features/DescriptiveTextGenerator.php new file mode 100644 index 000000000..04048b314 --- /dev/null +++ b/includes/Classifai/Features/DescriptiveTextGenerator.php @@ -0,0 +1,206 @@ +provider_instances = $this->get_provider_instances( $service_providers ); + } + + /** + * Returns the label of the feature. + * + * @return string + */ + public function get_label() { + return apply_filters( + 'classifai_' . static::ID . '_label', + __( 'Descriptive Text Generator', 'classifai' ) + ); + } + + /** + * Returns the providers supported by the feature. + * + * @internal + * + * @return array + */ + protected function get_providers() { + return apply_filters( + 'classifai_' . static::ID . '_providers', + [ + ComputerVision::ID => __( 'Microsoft Azure AI Vision', 'classifai' ), + ] + ); + } + + /** + * Sets up the fields and sections for the feature. + */ + public function setup_fields_sections() { + $settings = $this->get_settings(); + + add_settings_section( + $this->get_option_name() . '_section', + esc_html__( 'Feature settings', 'classifai' ), + '__return_empty_string', + $this->get_option_name() + ); + + add_settings_field( + 'status', + esc_html__( 'Enable descriptive text generation', 'classifai' ), + [ $this, 'render_input' ], + $this->get_option_name(), + $this->get_option_name() . '_section', + [ + 'label_for' => 'status', + 'input_type' => 'checkbox', + 'default_value' => $settings['status'], + 'description' => __( 'A button will be added to the status panel that can be used to generate titles.', 'classifai' ), + ] + ); + + add_settings_field( + 'roles', + esc_html__( 'Allowed roles', 'classifai' ), + [ $this, 'render_checkbox_group' ], + $this->get_option_name(), + $this->get_option_name() . '_section', + [ + 'label_for' => 'roles', + 'options' => $this->roles, + 'default_values' => $settings['roles'], + 'description' => __( 'Choose which roles are allowed to generate titles.', 'classifai' ), + ] + ); + + add_settings_field( + 'provider', + esc_html__( 'Select a provider', 'classifai' ), + [ $this, 'render_select' ], + $this->get_option_name(), + $this->get_option_name() . '_section', + [ + 'label_for' => 'provider', + 'options' => $this->get_providers(), + 'default_value' => $settings['provider'], + ] + ); + + foreach( array_keys( $this->get_providers() ) as $provider_id ) { + $provider = $this->get_feature_provider_instance( $provider_id ); + + if ( method_exists( $provider, 'render_provider_fields' ) ) { + $provider->render_provider_fields(); + } + } + } + + /** + * Returns true if the feature meets all the criteria to be enabled. + * + * @return boolean + */ + public function is_feature_enabled() { + $access = false; + $settings = $this->get_settings(); + $provider_id = $settings['provider'] ?? ComputerVision::ID; + $user_roles = wp_get_current_user()->roles ?? []; + $feature_roles = $settings['roles'] ?? []; + + $user_access = ! empty( $feature_roles ) && ! empty( array_intersect( $user_roles, $feature_roles ) ); + $provider_access = $settings[ $provider_id ]['authenticated'] ?? false; + $feature_status = isset( $settings['status'] ) && '1' === $settings['status']; + $access = $user_access && $provider_access && $feature_status; + + /** + * Filter to override permission to the generate title feature. + * + * @since 2.3.0 + * @hook classifai_openai_chatgpt_{$feature} + * + * @param {bool} $access Current access value. + * @param {array} $settings Current feature settings. + * + * @return {bool} Should the user have access? + */ + return apply_filters( 'classifai_' . static::ID . '_is_feature_enabled', $access, $settings ); + } + + /** + * Returns the default settings for the feature. + * + * The root-level keys are the setting keys that are independent of the provider. + * Provider specific settings should be nested under the provider key. + * + * @internal + * + * @todo Add a filter hook to allow other plugins to add their own settings. + * + * @return array + */ + protected function get_default_settings() { + $provider_settings = []; + $feature_settings = [ + 'status' => '0', + 'roles' => $this->roles, + 'provider' => ComputerVision::ID, + ]; + + $provider_instance = $this->get_feature_provider_instance( ComputerVision::ID ); + $provider_settings[ ComputerVision::ID ] = $provider_instance->get_default_provider_settings(); + + return + apply_filters( + 'classifai_' . static::ID . '_get_default_settings', + array_merge( + $feature_settings, + $provider_settings + ) + ); + } + + /** + * Sanitizes the settings before saving. + * + * @param array $new_settings The settings to be sanitized on save. + * + * @internal + * + * @return array + */ + public function sanitize_settings( $new_settings ) { + $settings = $this->get_settings(); + + $new_settings['status'] = $new_settings['status'] ?? $settings['status']; + $new_settings['roles'] = isset( $new_settings['roles'] ) ? array_map( 'sanitize_text_field', $new_settings['roles'] ) : $settings['roles']; + $new_settings['provider'] = isset( $new_settings['provider'] ) ? sanitize_text_field( $new_settings['provider'] ) : $settings['provider']; + + $provider_instance = $this->get_feature_provider_instance( $new_settings['provider'] ); + $new_settings = $provider_instance->sanitize_settings( $new_settings ); + + return apply_filters( + 'classifai_' . static::ID . '_sanitize_settings', + $new_settings, + $settings + ); + } +} diff --git a/includes/Classifai/Features/ImageTagsGenerator.php b/includes/Classifai/Features/ImageTagsGenerator.php new file mode 100644 index 000000000..3c15adf80 --- /dev/null +++ b/includes/Classifai/Features/ImageTagsGenerator.php @@ -0,0 +1,206 @@ +provider_instances = $this->get_provider_instances( $service_providers ); + } + + /** + * Returns the label of the feature. + * + * @return string + */ + public function get_label() { + return apply_filters( + 'classifai_' . static::ID . '_label', + __( 'Image Tags Generator', 'classifai' ) + ); + } + + /** + * Returns the providers supported by the feature. + * + * @internal + * + * @return array + */ + protected function get_providers() { + return apply_filters( + 'classifai_' . static::ID . '_providers', + [ + ComputerVision::ID => __( 'Microsoft Azure AI Vision', 'classifai' ), + ] + ); + } + + /** + * Sets up the fields and sections for the feature. + */ + public function setup_fields_sections() { + $settings = $this->get_settings(); + + add_settings_section( + $this->get_option_name() . '_section', + esc_html__( 'Feature settings', 'classifai' ), + '__return_empty_string', + $this->get_option_name() + ); + + add_settings_field( + 'status', + esc_html__( 'Enable image tag generation', 'classifai' ), + [ $this, 'render_input' ], + $this->get_option_name(), + $this->get_option_name() . '_section', + [ + 'label_for' => 'status', + 'input_type' => 'checkbox', + 'default_value' => $settings['status'], + 'description' => __( 'Image tags will be added automatically.', 'classifai' ), + ] + ); + + add_settings_field( + 'roles', + esc_html__( 'Allowed roles', 'classifai' ), + [ $this, 'render_checkbox_group' ], + $this->get_option_name(), + $this->get_option_name() . '_section', + [ + 'label_for' => 'roles', + 'options' => $this->roles, + 'default_values' => $settings['roles'], + 'description' => __( 'Choose which roles are allowed to generate image tags.', 'classifai' ), + ] + ); + + add_settings_field( + 'provider', + esc_html__( 'Select a provider', 'classifai' ), + [ $this, 'render_select' ], + $this->get_option_name(), + $this->get_option_name() . '_section', + [ + 'label_for' => 'provider', + 'options' => $this->get_providers(), + 'default_value' => $settings['provider'], + ] + ); + + foreach( array_keys( $this->get_providers() ) as $provider_id ) { + $provider = $this->get_feature_provider_instance( $provider_id ); + + if ( method_exists( $provider, 'render_provider_fields' ) ) { + $provider->render_provider_fields(); + } + } + } + + /** + * Returns true if the feature meets all the criteria to be enabled. + * + * @return boolean + */ + public function is_feature_enabled() { + $access = false; + $settings = $this->get_settings(); + $provider_id = $settings['provider'] ?? ComputerVision::ID; + $user_roles = wp_get_current_user()->roles ?? []; + $feature_roles = $settings['roles'] ?? []; + + $user_access = ! empty( $feature_roles ) && ! empty( array_intersect( $user_roles, $feature_roles ) ); + $provider_access = $settings[ $provider_id ]['authenticated'] ?? false; + $feature_status = isset( $settings['status'] ) && '1' === $settings['status']; + $access = $user_access && $provider_access && $feature_status; + + /** + * Filter to override permission to the generate title feature. + * + * @since 2.3.0 + * @hook classifai_openai_chatgpt_{$feature} + * + * @param {bool} $access Current access value. + * @param {array} $settings Current feature settings. + * + * @return {bool} Should the user have access? + */ + return apply_filters( 'classifai_' . static::ID . '_is_feature_enabled', $access, $settings ); + } + + /** + * Returns the default settings for the feature. + * + * The root-level keys are the setting keys that are independent of the provider. + * Provider specific settings should be nested under the provider key. + * + * @internal + * + * @todo Add a filter hook to allow other plugins to add their own settings. + * + * @return array + */ + protected function get_default_settings() { + $provider_settings = []; + $feature_settings = [ + 'status' => '0', + 'roles' => $this->roles, + 'provider' => ComputerVision::ID, + ]; + + $provider_instance = $this->get_feature_provider_instance( ComputerVision::ID ); + $provider_settings[ ComputerVision::ID ] = $provider_instance->get_default_provider_settings(); + + return + apply_filters( + 'classifai_' . static::ID . '_get_default_settings', + array_merge( + $feature_settings, + $provider_settings + ) + ); + } + + /** + * Sanitizes the settings before saving. + * + * @param array $new_settings The settings to be sanitized on save. + * + * @internal + * + * @return array + */ + public function sanitize_settings( $new_settings ) { + $settings = $this->get_settings(); + + $new_settings['status'] = $new_settings['status'] ?? $settings['status']; + $new_settings['roles'] = isset( $new_settings['roles'] ) ? array_map( 'sanitize_text_field', $new_settings['roles'] ) : $settings['roles']; + $new_settings['provider'] = isset( $new_settings['provider'] ) ? sanitize_text_field( $new_settings['provider'] ) : $settings['provider']; + + $provider_instance = $this->get_feature_provider_instance( $new_settings['provider'] ); + $new_settings = $provider_instance->sanitize_settings( $new_settings ); + + return apply_filters( + 'classifai_' . static::ID . '_sanitize_settings', + $new_settings, + $settings + ); + } +} diff --git a/includes/Classifai/Features/ImageToText.php b/includes/Classifai/Features/ImageToText.php new file mode 100644 index 000000000..aabce1093 --- /dev/null +++ b/includes/Classifai/Features/ImageToText.php @@ -0,0 +1,206 @@ +provider_instances = $this->get_provider_instances( $service_providers ); + } + + /** + * Returns the label of the feature. + * + * @return string + */ + public function get_label() { + return apply_filters( + 'classifai_' . static::ID . '_label', + __( 'Text extraction from images', 'classifai' ) + ); + } + + /** + * Returns the providers supported by the feature. + * + * @internal + * + * @return array + */ + protected function get_providers() { + return apply_filters( + 'classifai_' . static::ID . '_providers', + [ + ComputerVision::ID => __( 'Microsoft Azure AI Vision', 'classifai' ), + ] + ); + } + + /** + * Sets up the fields and sections for the feature. + */ + public function setup_fields_sections() { + $settings = $this->get_settings(); + + add_settings_section( + $this->get_option_name() . '_section', + esc_html__( 'Feature settings', 'classifai' ), + '__return_empty_string', + $this->get_option_name() + ); + + add_settings_field( + 'status', + esc_html__( 'Enable text extraction from images', 'classifai' ), + [ $this, 'render_input' ], + $this->get_option_name(), + $this->get_option_name() . '_section', + [ + 'label_for' => 'status', + 'input_type' => 'checkbox', + 'default_value' => $settings['status'], + 'description' => __( 'OCR detects text in images (e.g., handwritten notes) and saves that as post content.', 'classifai' ), + ] + ); + + add_settings_field( + 'roles', + esc_html__( 'Allowed roles', 'classifai' ), + [ $this, 'render_checkbox_group' ], + $this->get_option_name(), + $this->get_option_name() . '_section', + [ + 'label_for' => 'roles', + 'options' => $this->roles, + 'default_values' => $settings['roles'], + 'description' => __( 'Choose which roles are allowed to generate image tags.', 'classifai' ), + ] + ); + + add_settings_field( + 'provider', + esc_html__( 'Select a provider', 'classifai' ), + [ $this, 'render_select' ], + $this->get_option_name(), + $this->get_option_name() . '_section', + [ + 'label_for' => 'provider', + 'options' => $this->get_providers(), + 'default_value' => $settings['provider'], + ] + ); + + foreach( array_keys( $this->get_providers() ) as $provider_id ) { + $provider = $this->get_feature_provider_instance( $provider_id ); + + if ( method_exists( $provider, 'render_provider_fields' ) ) { + $provider->render_provider_fields(); + } + } + } + + /** + * Returns true if the feature meets all the criteria to be enabled. + * + * @return boolean + */ + public function is_feature_enabled() { + $access = false; + $settings = $this->get_settings(); + $provider_id = $settings['provider'] ?? ComputerVision::ID; + $user_roles = wp_get_current_user()->roles ?? []; + $feature_roles = $settings['roles'] ?? []; + + $user_access = ! empty( $feature_roles ) && ! empty( array_intersect( $user_roles, $feature_roles ) ); + $provider_access = $settings[ $provider_id ]['authenticated'] ?? false; + $feature_status = isset( $settings['status'] ) && '1' === $settings['status']; + $access = $user_access && $provider_access && $feature_status; + + /** + * Filter to override permission to the generate title feature. + * + * @since 2.3.0 + * @hook classifai_openai_chatgpt_{$feature} + * + * @param {bool} $access Current access value. + * @param {array} $settings Current feature settings. + * + * @return {bool} Should the user have access? + */ + return apply_filters( 'classifai_' . static::ID . '_is_feature_enabled', $access, $settings ); + } + + /** + * Returns the default settings for the feature. + * + * The root-level keys are the setting keys that are independent of the provider. + * Provider specific settings should be nested under the provider key. + * + * @internal + * + * @todo Add a filter hook to allow other plugins to add their own settings. + * + * @return array + */ + protected function get_default_settings() { + $provider_settings = []; + $feature_settings = [ + 'status' => '0', + 'roles' => $this->roles, + 'provider' => ComputerVision::ID, + ]; + + $provider_instance = $this->get_feature_provider_instance( ComputerVision::ID ); + $provider_settings[ ComputerVision::ID ] = $provider_instance->get_default_provider_settings(); + + return + apply_filters( + 'classifai_' . static::ID . '_get_default_settings', + array_merge( + $feature_settings, + $provider_settings + ) + ); + } + + /** + * Sanitizes the settings before saving. + * + * @param array $new_settings The settings to be sanitized on save. + * + * @internal + * + * @return array + */ + public function sanitize_settings( $new_settings ) { + $settings = $this->get_settings(); + + $new_settings['status'] = $new_settings['status'] ?? $settings['status']; + $new_settings['roles'] = isset( $new_settings['roles'] ) ? array_map( 'sanitize_text_field', $new_settings['roles'] ) : $settings['roles']; + $new_settings['provider'] = isset( $new_settings['provider'] ) ? sanitize_text_field( $new_settings['provider'] ) : $settings['provider']; + + $provider_instance = $this->get_feature_provider_instance( $new_settings['provider'] ); + $new_settings = $provider_instance->sanitize_settings( $new_settings ); + + return apply_filters( + 'classifai_' . static::ID . '_sanitize_settings', + $new_settings, + $settings + ); + } +} diff --git a/includes/Classifai/Features/SmartCropping.php b/includes/Classifai/Features/SmartCropping.php new file mode 100644 index 000000000..916e8a3e0 --- /dev/null +++ b/includes/Classifai/Features/SmartCropping.php @@ -0,0 +1,206 @@ +provider_instances = $this->get_provider_instances( $service_providers ); + } + + /** + * Returns the label of the feature. + * + * @return string + */ + public function get_label() { + return apply_filters( + 'classifai_' . static::ID . '_label', + __( 'Smart Cropping', 'classifai' ) + ); + } + + /** + * Returns the providers supported by the feature. + * + * @internal + * + * @return array + */ + protected function get_providers() { + return apply_filters( + 'classifai_' . static::ID . '_providers', + [ + ComputerVision::ID => __( 'Microsoft Azure AI Vision', 'classifai' ), + ] + ); + } + + /** + * Sets up the fields and sections for the feature. + */ + public function setup_fields_sections() { + $settings = $this->get_settings(); + + add_settings_section( + $this->get_option_name() . '_section', + esc_html__( 'Feature settings', 'classifai' ), + '__return_empty_string', + $this->get_option_name() + ); + + add_settings_field( + 'status', + esc_html__( 'Enable smart cropping', 'classifai' ), + [ $this, 'render_input' ], + $this->get_option_name(), + $this->get_option_name() . '_section', + [ + 'label_for' => 'status', + 'input_type' => 'checkbox', + 'default_value' => $settings['status'], + 'description' => __( 'AI Vision detects and saves the most visually interesting part of your image (i.e., faces, animals, notable text).', 'classifai' ), + ] + ); + + add_settings_field( + 'roles', + esc_html__( 'Allowed roles', 'classifai' ), + [ $this, 'render_checkbox_group' ], + $this->get_option_name(), + $this->get_option_name() . '_section', + [ + 'label_for' => 'roles', + 'options' => $this->roles, + 'default_values' => $settings['roles'], + 'description' => __( 'Choose which roles are allowed to generate image tags.', 'classifai' ), + ] + ); + + add_settings_field( + 'provider', + esc_html__( 'Select a provider', 'classifai' ), + [ $this, 'render_select' ], + $this->get_option_name(), + $this->get_option_name() . '_section', + [ + 'label_for' => 'provider', + 'options' => $this->get_providers(), + 'default_value' => $settings['provider'], + ] + ); + + foreach( array_keys( $this->get_providers() ) as $provider_id ) { + $provider = $this->get_feature_provider_instance( $provider_id ); + + if ( method_exists( $provider, 'render_provider_fields' ) ) { + $provider->render_provider_fields(); + } + } + } + + /** + * Returns true if the feature meets all the criteria to be enabled. + * + * @return boolean + */ + public function is_feature_enabled() { + $access = false; + $settings = $this->get_settings(); + $provider_id = $settings['provider'] ?? ComputerVision::ID; + $user_roles = wp_get_current_user()->roles ?? []; + $feature_roles = $settings['roles'] ?? []; + + $user_access = ! empty( $feature_roles ) && ! empty( array_intersect( $user_roles, $feature_roles ) ); + $provider_access = $settings[ $provider_id ]['authenticated'] ?? false; + $feature_status = isset( $settings['status'] ) && '1' === $settings['status']; + $access = $user_access && $provider_access && $feature_status; + + /** + * Filter to override permission to the generate title feature. + * + * @since 2.3.0 + * @hook classifai_openai_chatgpt_{$feature} + * + * @param {bool} $access Current access value. + * @param {array} $settings Current feature settings. + * + * @return {bool} Should the user have access? + */ + return apply_filters( 'classifai_' . static::ID . '_is_feature_enabled', $access, $settings ); + } + + /** + * Returns the default settings for the feature. + * + * The root-level keys are the setting keys that are independent of the provider. + * Provider specific settings should be nested under the provider key. + * + * @internal + * + * @todo Add a filter hook to allow other plugins to add their own settings. + * + * @return array + */ + protected function get_default_settings() { + $provider_settings = []; + $feature_settings = [ + 'status' => '0', + 'roles' => $this->roles, + 'provider' => ComputerVision::ID, + ]; + + $provider_instance = $this->get_feature_provider_instance( ComputerVision::ID ); + $provider_settings[ ComputerVision::ID ] = $provider_instance->get_default_provider_settings(); + + return + apply_filters( + 'classifai_' . static::ID . '_get_default_settings', + array_merge( + $feature_settings, + $provider_settings + ) + ); + } + + /** + * Sanitizes the settings before saving. + * + * @param array $new_settings The settings to be sanitized on save. + * + * @internal + * + * @return array + */ + public function sanitize_settings( $new_settings ) { + $settings = $this->get_settings(); + + $new_settings['status'] = $new_settings['status'] ?? $settings['status']; + $new_settings['roles'] = isset( $new_settings['roles'] ) ? array_map( 'sanitize_text_field', $new_settings['roles'] ) : $settings['roles']; + $new_settings['provider'] = isset( $new_settings['provider'] ) ? sanitize_text_field( $new_settings['provider'] ) : $settings['provider']; + + $provider_instance = $this->get_feature_provider_instance( $new_settings['provider'] ); + $new_settings = $provider_instance->sanitize_settings( $new_settings ); + + return apply_filters( + 'classifai_' . static::ID . '_sanitize_settings', + $new_settings, + $settings + ); + } +} diff --git a/includes/Classifai/Plugin.php b/includes/Classifai/Plugin.php index 502c3c3d4..255b29db9 100644 --- a/includes/Classifai/Plugin.php +++ b/includes/Classifai/Plugin.php @@ -107,7 +107,7 @@ public function init_services() { 'classifai_services', [ 'language_processing' => 'Classifai\Services\LanguageProcessing', - // 'image_processing' => 'Classifai\Services\ImageProcessing', + 'image_processing' => 'Classifai\Services\ImageProcessing', 'personalizer' => 'Classifai\Services\Personalizer', ] ); diff --git a/includes/Classifai/Providers/Azure/ComputerVision.php b/includes/Classifai/Providers/Azure/ComputerVision.php index e441083ae..6d10d27a5 100644 --- a/includes/Classifai/Providers/Azure/ComputerVision.php +++ b/includes/Classifai/Providers/Azure/ComputerVision.php @@ -5,6 +5,8 @@ namespace Classifai\Providers\Azure; +use Classifai\Features\DescriptiveTextGenerator; +use Classifai\Features\ImageTagsGenerator; use Classifai\Providers\Provider; use DOMDocument; use WP_Error; @@ -30,12 +32,11 @@ class ComputerVision extends Provider { * * @param string $service The service this class belongs to. */ - public function __construct( $service ) { + public function __construct( $feature_instance = null ) { parent::__construct( 'Microsoft Azure', 'AI Vision', - 'computer_vision', - $service + 'computer_vision' ); // Set the onboarding options. @@ -50,6 +51,8 @@ public function __construct( $service ) { 'enable_read_pdf' => __( 'Scan PDFs for text', 'classifai' ), ), ); + + $this->feature_instance = $feature_instance; } /** @@ -59,6 +62,316 @@ public function reset_settings() { update_option( $this->get_option_name(), $this->get_default_settings() ); } + public function render_provider_fields() { + $settings = $this->feature_instance->get_settings( static::ID ); + + add_settings_field( + 'endpoint_url', + esc_html__( 'Endpoint URL', 'classifai' ), + [ $this->feature_instance, 'render_input' ], + $this->feature_instance->get_option_name(), + $this->feature_instance->get_option_name() . '_section', + [ + 'option_index' => static::ID, + 'label_for' => 'endpoint_url', + 'input_type' => 'text', + 'default_value' => $settings['endpoint_url'], + 'description' => __( 'Supported protocol and hostname endpoints, e.g., https://REGION.api.cognitive.microsoft.com or https://EXAMPLE.cognitiveservices.azure.com. This can look different based on your setting choices in Azure.', 'classifai' ), + 'class' => 'large-text classifai-provider-field hidden' . ' provider-scope-' . static::ID, // Important to add this. + ] + ); + + add_settings_field( + 'api_key', + esc_html__( 'API Key', 'classifai' ), + [ $this->feature_instance, 'render_input' ], + $this->feature_instance->get_option_name(), + $this->feature_instance->get_option_name() . '_section', + [ + 'option_index' => static::ID, + 'label_for' => 'api_key', + 'input_type' => 'password', + 'default_value' => $settings['api_key'], + 'class' => 'classifai-provider-field hidden' . ' provider-scope-' . static::ID, // Important to add this. + ] + ); + + switch ( $this->feature_instance::ID ) { + case DescriptiveTextGenerator::ID: + $this->add_descriptive_text_generation_fields(); + break; + + case ImageTagsGenerator::ID: + $this->add_image_tags_generation_fields(); + break; + } + + do_action( 'classifai_' . static::ID . '_render_provider_fields', $this ); + } + + public function add_descriptive_text_generation_fields() { + $settings = $this->feature_instance->get_settings( static::ID ); + + $checkbox_options = array( + 'alt' => esc_html__( 'Alt text', 'classifai' ), + 'caption' => esc_html__( 'Image caption', 'classifai' ), + 'description' => esc_html__( 'Image description', 'classifai' ), + ); + + add_settings_field( + 'descriptive_text_fields', + esc_html__( 'Generate descriptive text', 'classifai' ), + [ $this->feature_instance, 'render_checkbox_group' ], + $this->feature_instance->get_option_name(), + $this->feature_instance->get_option_name() . '_section', + [ + 'option_index' => static::ID, + 'label_for' => 'descriptive_text_fields', + 'options' => $checkbox_options, + 'default_values' => $settings['descriptive_text_fields'], + 'description' => __( 'Choose image fields where the generated captions should be applied.', 'classifai' ), + 'class' => 'classifai-provider-field hidden' . ' provider-scope-' . static::ID, // Important to add this. + ] + ); + + add_settings_field( + 'descriptive_confidence_threshold', + esc_html__( 'Descriptive text confidence threshold', 'classifai' ), + [ $this->feature_instance, 'render_input' ], + $this->feature_instance->get_option_name(), + $this->feature_instance->get_option_name() . '_section', + [ + 'option_index' => static::ID, + 'label_for' => 'descriptive_confidence_threshold', + 'input_type' => 'number', + 'min' => 1, + 'step' => 1, + 'default_value' => $settings['descriptive_confidence_threshold'], + 'description' => esc_html__( 'Minimum confidence score for automatically added alt text, numeric value from 0-100. Recommended to be set to at least 75.', 'classifai' ), + 'class' => 'classifai-provider-field hidden' . ' provider-scope-' . static::ID, // Important to add this. + ] + ); + } + + public function add_image_tags_generation_fields() { + $settings = $this->feature_instance->get_settings( static::ID ); + + $attachment_taxonomies = get_object_taxonomies( 'attachment', 'objects' ); + $options = []; + + foreach ( $attachment_taxonomies as $name => $taxonomy ) { + $options[ $name ] = $taxonomy->label; + } + + add_settings_field( + 'tag_taxonomy', + esc_html__( 'Tag taxonomy', 'classifai' ), + [ $this->feature_instance, 'render_select' ], + $this->feature_instance->get_option_name(), + $this->feature_instance->get_option_name() . '_section', + [ + 'option_index' => static::ID, + 'label_for' => 'tag_taxonomy', + 'options' => $options, + 'default_value' => $settings['tag_taxonomy'], + 'class' => 'classifai-provider-field hidden' . ' provider-scope-' . static::ID, // Important to add this. + ] + ); + + add_settings_field( + 'tag_confidence_threshold', + esc_html__( 'Tag confidence threshold', 'classifai' ), + [ $this->feature_instance, 'render_input' ], + $this->feature_instance->get_option_name(), + $this->feature_instance->get_option_name() . '_section', + [ + 'option_index' => static::ID, + 'label_for' => 'tag_confidence_threshold', + 'input_type' => 'number', + 'min' => 1, + 'step' => 1, + 'default_value' => $settings['tag_confidence_threshold'], + 'description' => esc_html__( 'Minimum confidence score for automatically added image tags, numeric value from 0-100. Recommended to be set to at least 70.', 'classifai' ), + 'class' => 'classifai-provider-field hidden' . ' provider-scope-' . static::ID, // Important to add this. + ] + ); + } + + public function get_default_provider_settings() { + $common_settings = [ + 'endpoint_url' => '', + 'api_key' => '', + 'authenticated' => false, + ]; + + switch ( $this->feature_instance::ID ) { + case DescriptiveTextGenerator::ID: + return array_merge( + $common_settings, + [ + 'descriptive_text_fields' => [ + 'alt' => 0, + 'caption' => 0, + 'description' => 0, + ], + 'descriptive_confidence_threshold' => 75 + ] + ); + + case ImageTagsGenerator::ID: + $attachment_taxonomies = get_object_taxonomies( 'attachment', 'objects' ); + $options = []; + + foreach ( $attachment_taxonomies as $name => $taxonomy ) { + $options[ $name ] = $taxonomy->label; + } + + return array_merge( + $common_settings, + [ + 'tag_confidence_threshold' => 70, + 'tag_taxonomy' => array_key_first( $options ), + ] + ); + } + + return $common_settings; + } + + /** + * Sanitization + * + * @param array $settings The settings being saved. + * + * @return array|mixed + */ + public function sanitize_settings( $new_settings ) { + $settings = $this->feature_instance->get_settings( static::ID ); + + 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( $settings['url'] ); + $new_settings[ static::ID ]['api_key'] = sanitize_text_field( $settings['api_key'] ); + + $auth_check = $this->authenticate_credentials( + $new_settings[ static::ID ]['endpoint_url'], + $new_settings[ static::ID ]['api_key'] + ); + + if ( is_wp_error( $auth_check ) ) { + $new_settings[ static::ID ]['authenticated'] = false; + } else { + $new_settings[ static::ID ]['authenticated'] = true; + } + } else { + $new_settings[ static::ID ]['endpoint_url'] = $settings[ static::ID ]['endpoint_url']; + $new_settings[ static::ID ]['api_key'] = $settings[ static::ID ]['api_key']; + } + + if ( $this->feature_instance instanceof DescriptiveTextGenerator ) { + $new_settings[ static::ID ][ 'descriptive_confidence_threshold' ] = absint( $new_settings[ static::ID ]['descriptive_confidence_threshold'] ?? $settings[ static::ID ]['descriptive_confidence_threshold'] ); + $new_settings[ static::ID ][ 'descriptive_text_fields' ] = array_map( 'sanitize_text_field', $new_settings[ static::ID ]['descriptive_text_fields'] ?? $settings[ static::ID ]['descriptive_text_fields'] ); + } + + if ( $this->feature_instance instanceof ImageTagsGenerator ) { + $new_settings[ static::ID ][ 'tag_confidence_threshold' ] = absint( $new_settings[ static::ID ]['tag_confidence_threshold'] ?? $settings[ static::ID ]['descriptive_confidence_threshold'] ); + $new_settings[ static::ID ][ 'tag_taxonomy' ] = array_map( 'sanitize_text_field', $new_settings[ static::ID ]['tag_taxonomy'] ?? $settings[ static::ID ]['descriptive_text_fields'] ); + } + + return $new_settings; + + $new_settings = []; + if ( ! empty( $settings['url'] ) && ! empty( $settings['api_key'] ) ) { + $auth_check = $this->authenticate_credentials( $settings['url'], $settings['api_key'] ); + if ( is_wp_error( $auth_check ) ) { + $settings_errors['classifai-registration-credentials-error'] = $auth_check->get_error_message(); + $new_settings['authenticated'] = false; + } else { + $new_settings['authenticated'] = true; + } + $new_settings['url'] = esc_url_raw( $settings['url'] ); + $new_settings['api_key'] = sanitize_text_field( $settings['api_key'] ); + } else { + $new_settings['valid'] = false; + $new_settings['url'] = ''; + $new_settings['api_key'] = ''; + + $settings_errors['classifai-registration-credentials-empty'] = __( 'Please enter your credentials', 'classifai' ); + } + + $checkbox_settings = [ + 'enable_image_tagging', + 'enable_smart_cropping', + 'enable_ocr', + 'enable_read_pdf', + ]; + + foreach ( $checkbox_settings as $checkbox_setting ) { + + if ( empty( $settings[ $checkbox_setting ] ) || 1 !== (int) $settings[ $checkbox_setting ] ) { + $new_settings[ $checkbox_setting ] = 'no'; + } else { + $new_settings[ $checkbox_setting ] = '1'; + } + } + + if ( isset( $settings['caption_threshold'] ) && is_numeric( $settings['caption_threshold'] ) && (int) $settings['caption_threshold'] >= 0 && (int) $settings['caption_threshold'] <= 100 ) { + $new_settings['caption_threshold'] = absint( $settings['caption_threshold'] ); + } else { + $new_settings['caption_threshold'] = 75; + } + + if ( isset( $settings['tag_threshold'] ) && is_numeric( $settings['tag_threshold'] ) && (int) $settings['tag_threshold'] >= 0 && (int) $settings['tag_threshold'] <= 100 ) { + $new_settings['tag_threshold'] = absint( $settings['tag_threshold'] ); + } else { + $new_settings['tag_threshold'] = 75; + } + + if ( isset( $settings['image_tag_taxonomy'] ) && taxonomy_exists( $settings['image_tag_taxonomy'] ) ) { + $new_settings['image_tag_taxonomy'] = $settings['image_tag_taxonomy']; + } elseif ( taxonomy_exists( 'classifai-image-tags' ) ) { + $new_settings['image_tag_taxonomy'] = 'classifai-image-tags'; + } + + if ( isset( $settings['enable_image_captions'] ) ) { + if ( is_array( $settings['enable_image_captions'] ) ) { + $new_settings['enable_image_captions'] = $settings['enable_image_captions']; + } elseif ( 1 === (int) $settings['enable_image_captions'] ) { + // Handle submission from onboarding wizard. + $new_settings['enable_image_captions'] = array( + 'alt' => 'alt', + 'caption' => 0, + 'description' => 0, + ); + } + } else { + $new_settings['enable_image_captions'] = array( + 'alt' => 0, + 'caption' => 0, + 'description' => 0, + ); + } + + if ( ! empty( $settings_errors ) ) { + + $registered_settings_errors = wp_list_pluck( get_settings_errors( $this->get_option_name() ), 'code' ); + + foreach ( $settings_errors as $code => $message ) { + + if ( ! in_array( $code, $registered_settings_errors, true ) ) { + add_settings_error( + $this->get_option_name(), + $code, + esc_html( $message ), + 'error' + ); + } + } + } + + return $new_settings; + } + /** * Default settings for ComputerVision * @@ -1094,106 +1407,6 @@ public function setup_fields_sections() { ); } - /** - * Sanitization - * - * @param array $settings The settings being saved. - * - * @return array|mixed - */ - public function sanitize_settings( $settings ) { - $new_settings = []; - if ( ! empty( $settings['url'] ) && ! empty( $settings['api_key'] ) ) { - $auth_check = $this->authenticate_credentials( $settings['url'], $settings['api_key'] ); - if ( is_wp_error( $auth_check ) ) { - $settings_errors['classifai-registration-credentials-error'] = $auth_check->get_error_message(); - $new_settings['authenticated'] = false; - } else { - $new_settings['authenticated'] = true; - } - $new_settings['url'] = esc_url_raw( $settings['url'] ); - $new_settings['api_key'] = sanitize_text_field( $settings['api_key'] ); - } else { - $new_settings['valid'] = false; - $new_settings['url'] = ''; - $new_settings['api_key'] = ''; - - $settings_errors['classifai-registration-credentials-empty'] = __( 'Please enter your credentials', 'classifai' ); - } - - $checkbox_settings = [ - 'enable_image_tagging', - 'enable_smart_cropping', - 'enable_ocr', - 'enable_read_pdf', - ]; - - foreach ( $checkbox_settings as $checkbox_setting ) { - - if ( empty( $settings[ $checkbox_setting ] ) || 1 !== (int) $settings[ $checkbox_setting ] ) { - $new_settings[ $checkbox_setting ] = 'no'; - } else { - $new_settings[ $checkbox_setting ] = '1'; - } - } - - if ( isset( $settings['caption_threshold'] ) && is_numeric( $settings['caption_threshold'] ) && (int) $settings['caption_threshold'] >= 0 && (int) $settings['caption_threshold'] <= 100 ) { - $new_settings['caption_threshold'] = absint( $settings['caption_threshold'] ); - } else { - $new_settings['caption_threshold'] = 75; - } - - if ( isset( $settings['tag_threshold'] ) && is_numeric( $settings['tag_threshold'] ) && (int) $settings['tag_threshold'] >= 0 && (int) $settings['tag_threshold'] <= 100 ) { - $new_settings['tag_threshold'] = absint( $settings['tag_threshold'] ); - } else { - $new_settings['tag_threshold'] = 75; - } - - if ( isset( $settings['image_tag_taxonomy'] ) && taxonomy_exists( $settings['image_tag_taxonomy'] ) ) { - $new_settings['image_tag_taxonomy'] = $settings['image_tag_taxonomy']; - } elseif ( taxonomy_exists( 'classifai-image-tags' ) ) { - $new_settings['image_tag_taxonomy'] = 'classifai-image-tags'; - } - - if ( isset( $settings['enable_image_captions'] ) ) { - if ( is_array( $settings['enable_image_captions'] ) ) { - $new_settings['enable_image_captions'] = $settings['enable_image_captions']; - } elseif ( 1 === (int) $settings['enable_image_captions'] ) { - // Handle submission from onboarding wizard. - $new_settings['enable_image_captions'] = array( - 'alt' => 'alt', - 'caption' => 0, - 'description' => 0, - ); - } - } else { - $new_settings['enable_image_captions'] = array( - 'alt' => 0, - 'caption' => 0, - 'description' => 0, - ); - } - - if ( ! empty( $settings_errors ) ) { - - $registered_settings_errors = wp_list_pluck( get_settings_errors( $this->get_option_name() ), 'code' ); - - foreach ( $settings_errors as $code => $message ) { - - if ( ! in_array( $code, $registered_settings_errors, true ) ) { - add_settings_error( - $this->get_option_name(), - $code, - esc_html( $message ), - 'error' - ); - } - } - } - - return $new_settings; - } - /** * Authenticates our credentials. * diff --git a/includes/Classifai/Providers/Azure/Personalizer.php b/includes/Classifai/Providers/Azure/Personalizer.php index 0f826d4ea..cf5bd7175 100644 --- a/includes/Classifai/Providers/Azure/Personalizer.php +++ b/includes/Classifai/Providers/Azure/Personalizer.php @@ -34,7 +34,7 @@ class Personalizer extends Provider { * * @param string $service The service this class belongs to. */ - public function __construct( $service ) { + public function __construct( $service = null ) { parent::__construct( 'Microsoft Azure', 'AI Personalizer', diff --git a/includes/Classifai/Providers/Azure/Speech.php b/includes/Classifai/Providers/Azure/Speech.php index fc86f11bb..6c9bc630d 100644 --- a/includes/Classifai/Providers/Azure/Speech.php +++ b/includes/Classifai/Providers/Azure/Speech.php @@ -72,7 +72,7 @@ class Speech extends Provider { * * @param \Classifai\Features\Feature $feature_instance The feature instance. */ - public function __construct( $feature_instance ) { + public function __construct( $feature_instance = null ) { parent::__construct( 'Microsoft Azure', self::FEATURE_NAME, diff --git a/includes/Classifai/Providers/OpenAI/ChatGPT.php b/includes/Classifai/Providers/OpenAI/ChatGPT.php index 8b51d3268..ced19e054 100644 --- a/includes/Classifai/Providers/OpenAI/ChatGPT.php +++ b/includes/Classifai/Providers/OpenAI/ChatGPT.php @@ -75,7 +75,7 @@ class ChatGPT extends Provider { * * @param \Classifai\Features\Feature $feature_instance The feature instance. */ - public function __construct( $feature_instance ) { + public function __construct( $feature_instance = null ) { parent::__construct( 'OpenAI ChatGPT', 'ChatGPT', diff --git a/includes/Classifai/Providers/OpenAI/DallE.php b/includes/Classifai/Providers/OpenAI/DallE.php index 32f23bd09..8f8eba38e 100644 --- a/includes/Classifai/Providers/OpenAI/DallE.php +++ b/includes/Classifai/Providers/OpenAI/DallE.php @@ -35,7 +35,7 @@ class DallE extends Provider { * * @param string $service The service this class belongs to. */ - public function __construct( $service ) { + public function __construct( $service = null ) { parent::__construct( 'OpenAI', 'DALLĀ·E', diff --git a/includes/Classifai/Providers/OpenAI/Whisper.php b/includes/Classifai/Providers/OpenAI/Whisper.php index 0adc376cf..5fd5f3bfe 100644 --- a/includes/Classifai/Providers/OpenAI/Whisper.php +++ b/includes/Classifai/Providers/OpenAI/Whisper.php @@ -30,7 +30,7 @@ class Whisper extends Provider { * * @param \Classifai\Features\Feature $feature_instance The feature instance. */ - public function __construct( $feature_instance ) { + public function __construct( $feature_instance = null ) { parent::__construct( 'OpenAI Whisper', 'Whisper', diff --git a/includes/Classifai/Services/ImageProcessing.php b/includes/Classifai/Services/ImageProcessing.php index e5621359e..600e79532 100644 --- a/includes/Classifai/Services/ImageProcessing.php +++ b/includes/Classifai/Services/ImageProcessing.php @@ -39,6 +39,15 @@ public function init() { add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_media_scripts' ] ); } + public static function get_service_providers() { + return apply_filters( + 'classifai_language_processing_service_providers', + [ + 'Classifai\Providers\Azure\ComputerVision', + ] + ); + } + /** * Enqueue the script for the media modal. * diff --git a/includes/Classifai/Services/Service.php b/includes/Classifai/Services/Service.php index fdfa514b9..21eaf2a00 100644 --- a/includes/Classifai/Services/Service.php +++ b/includes/Classifai/Services/Service.php @@ -72,7 +72,7 @@ public function init() { if ( ! empty( $this->providers ) && is_array( $this->providers ) ) { foreach ( $this->providers as $provider ) { if ( class_exists( $provider ) ) { - $this->provider_classes[] = new $provider( $this->menu_slug ); + $this->provider_classes[] = new $provider(); } } } diff --git a/includes/Classifai/Services/ServicesManager.php b/includes/Classifai/Services/ServicesManager.php index b8ad0215c..172adc747 100644 --- a/includes/Classifai/Services/ServicesManager.php +++ b/includes/Classifai/Services/ServicesManager.php @@ -42,7 +42,8 @@ public function __construct( $services = [] ) { * Register the actions required for the settings page. */ public function register() { - add_filter( 'language_processing_features', [ $this, 'language_processing_features' ] ); + add_filter( 'language_processing_features', [ $this, 'register_language_processing_features' ] ); + add_filter( 'image_processing_features', [ $this, 'register_image_processing_features' ] ); foreach ( $this->services as $key => $service ) { if ( class_exists( $service ) ) { @@ -59,7 +60,7 @@ public function register() { add_filter( 'classifai_debug_information', [ $this, 'add_debug_information' ], 1 ); } - public function language_processing_features() { + public function register_language_processing_features() { return [ '\Classifai\Features\TitleGeneration', '\Classifai\Features\ExcerptGeneration', @@ -69,6 +70,15 @@ public function language_processing_features() { ]; } + public function register_image_processing_features() { + return [ + '\Classifai\Features\DescriptiveTextGenerator', + '\Classifai\Features\ImageTagsGenerator', + '\Classifai\Features\SmartCropping', + '\Classifai\Features\ImageToText', + ]; + } + /** * Get general ClassifAI settings *