From 38a1a35da931b5dbdb0f142274c7c85d09c19858 Mon Sep 17 00:00:00 2001 From: dkotter Date: Wed, 13 Dec 2023 15:16:27 +0000 Subject: [PATCH] deploy: b2f148a433ff04170486a2630a15393f79d4717f --- %7B$this-_menu_slug%7D_providers.html | 2 +- Admin_DebugInfo.php.html | 2 +- Admin_SavePostHandler.php.html | 57 +- Helpers.php.html | 90 +- Plugin.php.html | 31 +- Providers_AccessControl.php.html | 460 ++++++++++ Providers_Azure_ComputerVision.php.html | 225 +++-- Providers_Azure_OCR.php.html | 2 +- Providers_Azure_Personalizer.php.html | 81 +- Providers_Azure_Read.php.html | 2 +- Providers_Azure_SmartCropping.php.html | 2 +- Providers_Azure_TextToSpeech.php.html | 108 ++- Providers_OpenAI_APIRequest.php.html | 4 +- Providers_OpenAI_ChatGPT.php.html | 301 +++--- Providers_OpenAI_DallE.php.html | 144 +-- Providers_OpenAI_Embeddings.php.html | 273 +++++- Providers_OpenAI_OpenAI.php.html | 2 +- Providers_OpenAI_Tokenizer.php.html | 8 +- Providers_OpenAI_Whisper_Transcribe.php.html | 2 +- Providers_Provider.php.html | 868 ++++++++++++++++++ Services_Service.php.html | 37 +- Watson_Classifier.php.html | 2 +- Watson_Normalizer.php.html | 2 +- after_classifai_init.html | 4 +- before_classifai_init.html | 2 +- ..._option_name%7D_enable_%7B$feature%7D.html | 248 +++++ classifai_all_post_statuses.html | 4 +- classifai_allowed_roles.html | 271 ++++++ classifai_audio_generation_initial_state.html | 4 +- ...fai_audio_generation_subsequent_state.html | 4 +- classifai_azure_read_after_request.html | 2 +- classifai_azure_read_request_args.html | 2 +- classifai_azure_read_result_max_page.html | 2 +- classifai_azure_read_retry_interval.html | 2 +- classifai_azure_read_should_process.html | 2 +- classifai_azure_read_text_result.html | 2 +- classifai_chatgpt_allowed_roles.html | 4 +- classifai_chatgpt_content.html | 4 +- classifai_chatgpt_excerpt_prompt.html | 4 +- classifai_chatgpt_excerpt_request_body.html | 4 +- ...i_chatgpt_resize_content_request_body.html | 4 +- classifai_chatgpt_title_prompt.html | 4 +- classifai_chatgpt_title_request_body.html | 4 +- classifai_classified_data.html | 2 +- classifai_computer_vision_caption_failed.html | 4 +- classifai_computer_vision_captions.html | 4 +- ...ifai_computer_vision_image_tag_failed.html | 4 +- classifai_computer_vision_image_tags.html | 4 +- classifai_computer_vision_max_filesize.html | 4 +- classifai_dalle_caption.html | 4 +- classifai_dalle_prompt.html | 4 +- classifai_dalle_request_body.html | 4 +- classifai_debug_information.html | 2 +- classifai_disable_post_to_audio_block.html | 4 +- classifai_feature_threshold.html | 4 +- ...ai_generate_image_alt_tags_source_url.html | 4 +- classifai_has_access.html | 294 ++++++ classifai_is_%7B$feature%7D_enabled.html | 248 +++++ ...sifai_language_settings_post_statuses.html | 4 +- classifai_language_settings_post_types.html | 4 +- classifai_listen_to_this_post_text.html | 4 +- classifai_normalize.html | 2 +- classifai_ocr_after_request.html | 2 +- classifai_ocr_approved_media_types.html | 2 +- classifai_ocr_should_process.html | 2 +- classifai_ocr_tag_confidence.html | 2 +- classifai_ocr_tags.html | 2 +- classifai_ocr_text.html | 2 +- classifai_ocr_text_post_args.html | 2 +- classifai_ocr_unsuccessful_response.html | 2 +- classifai_openai_api_request_get_options.html | 2 +- classifai_openai_api_request_get_url.html | 2 +- ..._openai_api_request_post_form_options.html | 2 +- ...ifai_openai_api_request_post_form_url.html | 2 +- ...sifai_openai_api_request_post_options.html | 2 +- classifai_openai_api_request_post_url.html | 2 +- classifai_openai_api_response_get.html | 2 +- classifai_openai_api_response_post.html | 2 +- classifai_openai_api_response_post_form.html | 2 +- classifai_openai_characters_in_token.html | 2 +- classifai_openai_chatgpt_%7B$feature%7D.html | 248 ----- ...ifai_openai_dalle_allowed_image_roles.html | 4 +- classifai_openai_dalle_enable_image_gen.html | 4 +- classifai_openai_embeddings_content.html | 4 +- ...sifai_openai_embeddings_post_statuses.html | 4 +- classifai_openai_embeddings_request_body.html | 4 +- ...fai_openai_embeddings_should_classify.html | 4 +- classifai_openai_embeddings_taxonomies.html | 4 +- classifai_openai_settings_post_statuses.html | 2 +- classifai_openai_settings_post_types.html | 2 +- classifai_openai_settings_taxonomies.html | 2 +- classifai_openai_tokens_per_word.html | 2 +- classifai_post_statuses.html | 4 +- ...fai_post_statuses_for_post_type_or_id.html | 4 +- classifai_post_types.html | 6 +- classifai_pre_render_post_audio_controls.html | 4 +- classifai_recommended_block_attributes.html | 4 +- classifai_recommended_block_markup.html | 4 +- classifai_recommended_content_post_args.html | 4 +- classifai_rest_bases.html | 4 +- classifai_services.html | 4 +- classifai_should_classify_post.html | 4 +- classifai_should_crop_size.html | 2 +- classifai_should_ocr_scan_image.html | 4 +- ...fai_should_register_save_post_handler.html | 4 +- classifai_should_smart_crop_image.html | 4 +- classifai_smart_crop_max_pixel_dimension.html | 2 +- classifai_smart_crop_wp_filesystem.html | 2 +- classifai_smart_cropping_after_request.html | 2 +- classifai_smart_cropping_source_url.html | 2 +- classifai_smart_cropping_thumb_file_name.html | 2 +- ..._smart_cropping_unsuccessful_response.html | 2 +- classifai_taxonomy_for_feature.html | 4 +- classifai_threshold.html | 248 +++++ ...sifai_whisper_transcribe_request_body.html | 2 +- classifai_whisper_transcribe_result.html | 2 +- index.html | 2 +- tutorial-useful-snippets.html | 2 +- tutorial-wp-cli.html | 2 +- 119 files changed, 3735 insertions(+), 801 deletions(-) create mode 100644 Providers_AccessControl.php.html create mode 100644 Providers_Provider.php.html create mode 100644 classifai_%7B$this-_option_name%7D_enable_%7B$feature%7D.html create mode 100644 classifai_allowed_roles.html create mode 100644 classifai_has_access.html create mode 100644 classifai_is_%7B$feature%7D_enabled.html delete mode 100644 classifai_openai_chatgpt_%7B$feature%7D.html create mode 100644 classifai_threshold.html diff --git a/%7B$this-_menu_slug%7D_providers.html b/%7B$this-_menu_slug%7D_providers.html index 75a031492..fd649382a 100644 --- a/%7B$this-_menu_slug%7D_providers.html +++ b/%7B$this-_menu_slug%7D_providers.html @@ -214,7 +214,7 @@
Returns:

diff --git a/Admin_DebugInfo.php.html b/Admin_DebugInfo.php.html index 70f01034c..4eeb14e3d 100644 --- a/Admin_DebugInfo.php.html +++ b/Admin_DebugInfo.php.html @@ -143,7 +143,7 @@

Source: Admin/DebugInfo.php


diff --git a/Admin_SavePostHandler.php.html b/Admin_SavePostHandler.php.html index 9aa571d4e..9fcff39e6 100644 --- a/Admin_SavePostHandler.php.html +++ b/Admin_SavePostHandler.php.html @@ -37,6 +37,7 @@

Source: Admin/SavePostHandler.php

use \Classifai\Providers\Azure\TextToSpeech; use \Classifai\Watson\Normalizer; +use function Classifai\get_classification_mode; /** * Classifies Posts based on the current ClassifAI configuration. @@ -53,6 +54,7 @@

Source: Admin/SavePostHandler.php

*/ public function register() { add_filter( 'removable_query_args', [ $this, 'classifai_removable_query_args' ] ); + add_filter( 'default_post_metadata', [ $this, 'default_post_metadata' ], 10, 3 ); add_action( 'save_post', [ $this, 'did_save_post' ] ); add_action( 'admin_notices', [ $this, 'show_error_if' ] ); add_action( 'admin_post_classifai_classify_post', array( $this, 'classifai_classify_post' ) ); @@ -91,6 +93,28 @@

Source: Admin/SavePostHandler.php

return ! empty( get_option( 'classifai_configured' ) ) && ! empty( get_option( 'classifai_watson_nlu' )['credentials']['watson_url'] ); } + /** + * Sets the default value for the _classifai_process_content meta key. + * + * @param mixed $value The value get_metadata() should return - a single metadata value, + * or an array of values. + * @param int $object_id Object ID. + * @param string $meta_key Meta key. + * + * @return mixed + */ + public function default_post_metadata( $value, $object_id, $meta_key ) { + if ( '_classifai_process_content' === $meta_key ) { + if ( 'automatic_classification' === get_classification_mode() ) { + return 'yes'; + } else { + return 'no'; + } + } + + return $value; + } + /** * If current post type support is enabled in ClassifAI settings, it * is tagged using the IBM Watson classification result. @@ -140,11 +164,12 @@

Source: Admin/SavePostHandler.php

* Classifies the post specified with the PostClassifier object. * Existing terms relationships are removed before classification. * - * @param int $post_id the post to classify & link + * @param int $post_id the post to classify & link. + * @param bool $link_terms Whether to link the terms to the post. * * @return array */ - public function classify( $post_id ) { + public function classify( $post_id, $link_terms = true ) { /** * Filter whether ClassifAI should classify a post. * @@ -166,23 +191,25 @@

Source: Admin/SavePostHandler.php

$classifier = $this->get_classifier(); - if ( \Classifai\get_feature_enabled( 'category' ) ) { - wp_delete_object_term_relationships( $post_id, \Classifai\get_feature_taxonomy( 'category' ) ); - } + if ( $link_terms ) { + if ( \Classifai\get_feature_enabled( 'category' ) ) { + wp_delete_object_term_relationships( $post_id, \Classifai\get_feature_taxonomy( 'category' ) ); + } - if ( \Classifai\get_feature_enabled( 'keyword' ) ) { - wp_delete_object_term_relationships( $post_id, \Classifai\get_feature_taxonomy( 'keyword' ) ); - } + if ( \Classifai\get_feature_enabled( 'keyword' ) ) { + wp_delete_object_term_relationships( $post_id, \Classifai\get_feature_taxonomy( 'keyword' ) ); + } - if ( \Classifai\get_feature_enabled( 'concept' ) ) { - wp_delete_object_term_relationships( $post_id, \Classifai\get_feature_taxonomy( 'concept' ) ); - } + if ( \Classifai\get_feature_enabled( 'concept' ) ) { + wp_delete_object_term_relationships( $post_id, \Classifai\get_feature_taxonomy( 'concept' ) ); + } - if ( \Classifai\get_feature_enabled( 'entity' ) ) { - wp_delete_object_term_relationships( $post_id, \Classifai\get_feature_taxonomy( 'entity' ) ); + if ( \Classifai\get_feature_enabled( 'entity' ) ) { + wp_delete_object_term_relationships( $post_id, \Classifai\get_feature_taxonomy( 'entity' ) ); + } } - $output = $classifier->classify_and_link( $post_id ); + $output = $classifier->classify_and_link( $post_id, [], $link_terms ); if ( is_wp_error( $output ) ) { update_post_meta( @@ -510,7 +537,7 @@

Source: Admin/SavePostHandler.php


diff --git a/Helpers.php.html b/Helpers.php.html index b0df99502..23465b687 100644 --- a/Helpers.php.html +++ b/Helpers.php.html @@ -35,8 +35,10 @@

Source: Helpers.php

namespace Classifai; +use Classifai\Admin\UserProfile; use Classifai\Providers\Provider; use Classifai\Providers\Azure; +use Classifai\Providers\Watson\NLU; use Classifai\Services\Service; use Classifai\Services\ServicesManager; use WP_Error; @@ -159,7 +161,6 @@

Source: Helpers.php

} } - /** * Returns the currently configured Watson API URL. Lookup order is, * @@ -203,6 +204,48 @@

Source: Helpers.php

} } +/** + * Get Classification mode. + * + * @since 2.5.0 + * + * @return string + */ +function get_classification_mode() { + $provider = new NLU( 'Natural Language Understanding' ); + $settings = get_plugin_settings( 'language_processing', 'Natural Language Understanding' ); + $value = isset( $settings['classification_mode'] ) ? $settings['classification_mode'] : ''; + + if ( $provider->is_configured() ) { + if ( empty( $value ) ) { + // existing users + // default: automatic_classification + return 'automatic_classification'; + } + } else { + // new users + // default: manual_review + return 'manual_review'; + } + + return $value; +} + +/** + * Get IBM Watson Content Classification method. + * + * @since 2.6.0 + * + * @return string + */ +function get_classification_method() { + $provider = new NLU( 'language_processing' ); + $settings = $provider->get_settings(); + $value = $settings['classification_method'] ?? ''; + + return $value; +} + /** * Returns the currently configured Watson username. Lookup order is, * @@ -805,6 +848,49 @@

Source: Helpers.php

*/ return apply_filters( 'classifai_all_post_statuses', $all_statuses ); } + +/** + * Get the default settings for a feature. + * + * @since 2.4.0 + * + * @param string $feature Feature key. + * @return array + */ +function get_feature_default_settings( string $feature ) { + if ( ! function_exists( 'get_editable_roles' ) ) { + require_once ABSPATH . 'wp-admin/includes/user.php'; + } + $editable_roles = get_editable_roles() ?? []; + + return array( + $feature . '_role_based_access' => 1, + $feature . '_roles' => array_keys( $editable_roles ), + $feature . '_user_based_access' => 'no', + $feature . '_user_based_opt_out' => 'no', + $feature . '_users' => array(), + ); +} + +/** + * Renders a link to disable a specific feature. + * + * @since 2.5.0 + * + * @param string $feature Feature key. + */ +function render_disable_feature_link( string $feature ) { + $user_profile = new UserProfile(); + $allowed_features = $user_profile->get_allowed_features( get_current_user_id() ); + $profile_url = get_edit_profile_url( get_current_user_id() ) . '#classifai-profile-features-section'; + if ( ! empty( $allowed_features ) && isset( $allowed_features[ $feature ] ) ) { + ?> + <a href="<?php echo esc_url( $profile_url ); ?>" target="_blank" rel="noopener noreferrer" class="classifai-disable-feature-link" aria-label="<?php esc_attr_e( 'Opt out of using this ClassifAI feature', 'classifai' ); ?>"> + <?php esc_html_e( 'Disable this ClassifAI feature', 'classifai' ); ?> + </a> + <?php + } +} @@ -823,7 +909,7 @@

Source: Helpers.php


diff --git a/Plugin.php.html b/Plugin.php.html index 87aa44dff..d9be82670 100644 --- a/Plugin.php.html +++ b/Plugin.php.html @@ -106,6 +106,10 @@

Source: Plugin.php

$onboarding = new Admin\Onboarding(); $onboarding->init(); + // Initialize the classifAI User Profile. + $user_profile = new Admin\UserProfile(); + $user_profile->init(); + /** * Fires after ClassifAI services are loaded. * @@ -192,14 +196,17 @@

Source: Plugin.php

/** * Enqueue the admin scripts. * + * @param string $hook_suffix The current admin page. * @since 2.4.0 Use get_asset_info to get the asset version and dependencies. */ - public function enqueue_admin_assets() { + public function enqueue_admin_assets( $hook_suffix ) { + $user_profile = new Admin\UserProfile(); + $allowed_features = $user_profile->get_allowed_features( get_current_user_id() ); wp_enqueue_style( 'classifai-admin-style', CLASSIFAI_PLUGIN_URL . 'dist/admin.css', - array(), + array( 'wp-components' ), get_asset_info( 'admin', 'version' ), 'all' ); @@ -212,16 +219,20 @@

Source: Plugin.php

true ); + $localize_data = [ + 'api_password' => __( 'API Password', 'classifai' ), + 'api_key' => __( 'API Key', 'classifai' ), + 'use_key' => __( 'Use an API Key instead?', 'classifai' ), + 'use_password' => __( 'Use a username/password instead?', 'classifai' ), + 'ajax_nonce' => wp_create_nonce( 'classifai' ), + 'opt_out_enabled_features' => array_keys( $allowed_features ), + 'profile_url' => esc_url( get_edit_profile_url( get_current_user_id() ) . '#classifai-profile-features-section' ), + ]; + wp_localize_script( 'classifai-admin-script', 'ClassifAI', - [ - 'api_password' => __( 'API Password', 'classifai' ), - 'api_key' => __( 'API Key', 'classifai' ), - 'use_key' => __( 'Use an API Key instead?', 'classifai' ), - 'use_password' => __( 'Use a username/password instead?', 'classifai' ), - 'ajax_nonce' => wp_create_nonce( 'classifai' ), - ] + $localize_data ); if ( wp_script_is( 'wp-commands', 'registered' ) ) { @@ -283,7 +294,7 @@

Source: Plugin.php


diff --git a/Providers_AccessControl.php.html b/Providers_AccessControl.php.html new file mode 100644 index 000000000..87d1e81c9 --- /dev/null +++ b/Providers_AccessControl.php.html @@ -0,0 +1,460 @@ + + + + + Source: Providers/AccessControl.php - 10up ClassifAI Hook Docs + + + + + + + + + + + + + +
+ + +

Source: Providers/AccessControl.php

+ + + + + + + +
+
+
<?php
+/**
+ * Class for handling the access control for ClassifAI features.
+ *
+ * @package Classifai
+ * @since 2.4.0
+ */
+
+namespace Classifai\Providers;
+
+class AccessControl {
+
+	/**
+	 * The provider name.
+	 *
+	 * @var Provider $provider
+	 */
+	protected $provider;
+
+	/**
+	 * The feature name.
+	 *
+	 * @var string $feature
+	 */
+	protected $feature;
+
+	/**
+	 * The provider settings.
+	 *
+	 * @var array $settings
+	 */
+	protected $settings;
+
+	/**
+	 * The role based access key.
+	 *
+	 * @var string $role_based_access_key
+	 */
+	protected $role_based_access_key;
+
+	/**
+	 * The roles key.
+	 *
+	 * @var string $roles_key
+	 */
+	protected $roles_key;
+
+	/**
+	 * The user based access key.
+	 *
+	 * @var string $user_based_access_key
+	 */
+	protected $user_based_access_key;
+
+	/**
+	 * The user based opt out key.
+	 *
+	 * @var string $user_based_opt_out_key
+	 */
+	protected $user_based_opt_out_key;
+
+	/**
+	 * The users key.
+	 *
+	 * @var string $users_key
+	 */
+	protected $users_key;
+
+	/**
+	 * Constructor.
+	 *
+	 * @param Provider $provider The provider class instance.
+	 * @param string   $feature  The feature name.
+	 */
+	public function __construct( Provider $provider, string $feature ) {
+		$this->provider = $provider;
+		$this->feature  = $feature;
+
+		$this->role_based_access_key  = $feature . '_role_based_access';
+		$this->roles_key              = $feature . '_roles';
+		$this->user_based_access_key  = $feature . '_user_based_access';
+		$this->user_based_opt_out_key = $feature . '_user_based_opt_out';
+		$this->users_key              = $feature . '_users';
+	}
+
+	/**
+	 * Get settings for the current feature.
+	 *
+	 * @return array
+	 */
+	public function get_settings() {
+		if ( is_null( $this->settings ) ) {
+			$this->settings = $this->provider->get_settings();
+		}
+		return $this->settings;
+	}
+
+	/**
+	 * Determines whether user-based access control is enabled for the current feature.
+	 *
+	 * @return boolean
+	 */
+	public function is_user_based_access_enabled() {
+		$settings = $this->get_settings();
+		return isset( $settings[ $this->user_based_access_key ] ) && 1 === (int) $settings[ $this->user_based_access_key ];
+	}
+
+	/**
+	 * Determines whether user-based opt-out is enabled for the current feature.
+	 *
+	 * @return boolean
+	 */
+	public function is_user_based_opt_out_enabled() {
+		$settings = $this->get_settings();
+		return isset( $settings[ $this->user_based_opt_out_key ] ) && 1 === (int) $settings[ $this->user_based_opt_out_key ];
+	}
+
+	/**
+	 * Determines whether role-based access control is enabled for the current feature.
+	 *
+	 * @return boolean
+	 */
+	public function is_role_based_access_enabled() {
+		$settings = $this->get_settings();
+		return isset( $settings[ $this->role_based_access_key ] ) && 1 === (int) $settings[ $this->role_based_access_key ];
+	}
+
+	/**
+	 * Get the list of allowed roles for the current feature.
+	 *
+	 * @return array
+	 */
+	public function get_allowed_roles() {
+		$settings  = $this->get_settings();
+		$roles_key = $this->roles_key;
+		// Backward compatibility for old roles keys.
+		switch ( $this->feature ) {
+			case 'title_generation':
+				if ( ! isset( $settings[ $this->roles_key ] ) && isset( $settings['title_roles'] ) ) {
+					$roles_key = 'title_roles';
+				}
+				break;
+
+			case 'excerpt_generation':
+			case 'speech_to_text':
+			case 'image_generation':
+				if ( ! isset( $settings[ $this->roles_key ] ) && isset( $settings['roles'] ) ) {
+					$roles_key = 'roles';
+				}
+				break;
+
+			default:
+				break;
+		}
+
+		return $settings[ $roles_key ] ?? [];
+	}
+
+	/**
+	 * Get the list of allowed users for the current feature.
+	 *
+	 * @return array
+	 */
+	public function get_allowed_users() {
+		$settings = $this->get_settings();
+		$users    = $settings[ $this->users_key ] ?? [];
+		return array_map( 'absint', $users );
+	}
+
+	/**
+	 * Add settings fields for Role/User based access.
+	 *
+	 * @param string $section Settings section.
+	 * @return void
+	 */
+	public function add_settings( string $section = '' ) {
+		$default_settings = $this->provider->get_default_settings();
+		$settings         = $this->get_settings();
+
+		$option_name = $this->provider->get_option_name();
+		if ( empty( $section ) ) {
+			$section = $this->provider->get_option_name();
+		}
+
+		// Backward compatibility for old roles keys.
+		$backward_compatible_roles_key = '';
+		switch ( $this->feature ) {
+			case 'title_generation':
+				$backward_compatible_roles_key = 'title_roles';
+				break;
+
+			case 'excerpt_generation':
+			case 'speech_to_text':
+			case 'image_generation':
+				$backward_compatible_roles_key = 'roles';
+				break;
+
+			default:
+				break;
+		}
+
+		$default_settings = array_merge(
+			$this->provider->get_default_settings(),
+			$default_settings,
+		);
+
+		add_settings_field(
+			$this->role_based_access_key,
+			esc_html__( 'Enable role-based access', 'classifai' ),
+			[ $this->provider, 'render_input' ],
+			$option_name,
+			$section,
+			[
+				'label_for'     => $this->role_based_access_key,
+				'input_type'    => 'checkbox',
+				'default_value' => $default_settings[ $this->role_based_access_key ],
+				'description'   => __( 'Enables ability to select which roles can access this feature.', 'classifai' ),
+				'class'         => 'classifai-role-based-access',
+			]
+		);
+
+		// Add hidden class if role-based access is disabled.
+		$class = 'allowed_roles_row';
+		if ( ! isset( $settings[ $this->role_based_access_key ] ) || '1' !== $settings[ $this->role_based_access_key ] ) {
+			$class .= ' hidden';
+		}
+
+		add_settings_field(
+			$this->roles_key,
+			esc_html__( 'Allowed roles', 'classifai' ),
+			[ $this->provider, 'render_checkbox_group' ],
+			$option_name,
+			$section,
+			[
+				'label_for'               => $this->roles_key,
+				'options'                 => $this->provider->get_roles(),
+				'default_values'          => $default_settings[ $this->roles_key ],
+				'description'             => __( 'Choose which roles are allowed to access this feature.', 'classifai' ),
+				'class'                   => $class,
+				'backward_compatible_key' => $backward_compatible_roles_key,
+			]
+		);
+
+		add_settings_field(
+			$this->user_based_access_key,
+			esc_html__( 'Enable user-based access', 'classifai' ),
+			[ $this->provider, 'render_input' ],
+			$option_name,
+			$section,
+			[
+				'label_for'     => $this->user_based_access_key,
+				'input_type'    => 'checkbox',
+				'default_value' => $default_settings[ $this->user_based_access_key ],
+				'description'   => __( 'Enables ability to select which users can access this feature.', 'classifai' ),
+				'class'         => 'classifai-user-based-access',
+			]
+		);
+
+		// Add hidden class if user-based access is disabled.
+		$users_class = 'allowed_users_row';
+		if ( ! isset( $settings[ $this->user_based_access_key ] ) || '1' !== $settings[ $this->user_based_access_key ] ) {
+			$users_class .= ' hidden';
+		}
+
+		add_settings_field(
+			$this->users_key,
+			esc_html__( 'Allowed users', 'classifai' ),
+			[ $this->provider, 'render_allowed_users' ],
+			$option_name,
+			$section,
+			[
+				'label_for'     => $this->users_key,
+				'default_value' => $default_settings[ $this->users_key ],
+				'description'   => __( 'Users who have access to this feature.', 'classifai' ),
+				'class'         => $users_class,
+			]
+		);
+
+		add_settings_field(
+			$this->user_based_opt_out_key,
+			esc_html__( 'Enable user-based opt-out', 'classifai' ),
+			[ $this->provider, 'render_input' ],
+			$option_name,
+			$section,
+			[
+				'label_for'     => $this->user_based_opt_out_key,
+				'input_type'    => 'checkbox',
+				'default_value' => $default_settings[ $this->user_based_opt_out_key ],
+				'description'   => __( 'Enables ability for users to opt-out from their user profile page.', 'classifai' ),
+				'class'         => 'classifai-user-based-opt-out',
+			]
+		);
+	}
+
+	/**
+	 * Sanitization for the roles/users access options being saved.
+	 *
+	 * @param array $settings Array of settings about to be saved.
+	 *
+	 * @return array The sanitized settings to be saved.
+	 */
+	public function sanitize_settings( array $settings ) {
+		$new_settings = [];
+
+		if ( empty( $settings[ $this->role_based_access_key ] ) || 1 !== (int) $settings[ $this->role_based_access_key ] ) {
+			$new_settings[ $this->role_based_access_key ] = 'no';
+		} else {
+			$new_settings[ $this->role_based_access_key ] = '1';
+		}
+
+		// Allowed roles.
+		if ( isset( $settings[ $this->roles_key ] ) && is_array( $settings[ $this->roles_key ] ) ) {
+			$new_settings[ $this->roles_key ] = array_map( 'sanitize_text_field', $settings[ $this->roles_key ] );
+		} else {
+			$new_settings[ $this->roles_key ] = array_keys( get_editable_roles() ?? [] );
+		}
+
+		if ( empty( $settings[ $this->user_based_access_key ] ) || 1 !== (int) $settings[ $this->user_based_access_key ] ) {
+			$new_settings[ $this->user_based_access_key ] = 'no';
+		} else {
+			$new_settings[ $this->user_based_access_key ] = '1';
+		}
+
+		// Allowed users.
+		if ( isset( $settings[ $this->users_key ] ) && ! empty( $settings[ $this->users_key ] ) ) {
+			if ( is_array( $settings[ $this->users_key ] ) ) {
+				$new_settings[ $this->users_key ] = array_map( 'absint', $settings[ $this->users_key ] );
+			} else {
+				$new_settings[ $this->users_key ] = array_map( 'absint', explode( ',', $settings[ $this->users_key ] ) );
+			}
+		} else {
+			$new_settings[ $this->users_key ] = array();
+		}
+
+		// User-based opt-out.
+		if ( empty( $settings[ $this->user_based_opt_out_key ] ) || 1 !== (int) $settings[ $this->user_based_opt_out_key ] ) {
+			$new_settings[ $this->user_based_opt_out_key ] = 'no';
+		} else {
+			$new_settings[ $this->user_based_opt_out_key ] = '1';
+		}
+		return $new_settings;
+	}
+
+	/**
+	 * Determine if the current user has access of the feature
+	 *
+	 * @param int $user_id User ID.
+	 * @return bool
+	 */
+	public function has_access( $user_id = null ) {
+		if ( ! $user_id ) {
+			$user_id = get_current_user_id();
+		}
+		$user_id    = (int) $user_id;
+		$user       = get_user_by( 'id', $user_id );
+		$user_roles = $user->roles ?? [];
+		$access     = false;
+		$settings   = $this->get_settings();
+
+		$feature_roles = $this->get_allowed_roles();
+		$feature_users = $this->get_allowed_users();
+
+		/*
+		 * Checks if Role-based access is enabled and user role has access to the feature.
+		 */
+		if ( $this->is_role_based_access_enabled() ) {
+			$access = ( ! empty( $feature_roles ) && ! empty( array_intersect( $user_roles, $feature_roles ) ) );
+		}
+
+		/*
+		 * Checks if User-based access is enabled and user has access to the feature.
+		 */
+		if ( ! $access && $this->is_user_based_access_enabled() ) {
+			$access = ( ! empty( $feature_users ) && ! empty( in_array( $user_id, $feature_users, true ) ) );
+		}
+
+		/*
+		 * Checks if User-based opt-out is enabled and user has opted out from the feature.
+		 */
+		if ( $access && $this->is_user_based_opt_out_enabled() ) {
+			$opted_out_features = (array) get_user_meta( $user_id, 'classifai_opted_out_features', true );
+			$access             = ( ! in_array( $this->feature, $opted_out_features, true ) );
+		}
+
+		/**
+		 * Filter to override user access to a ClassifAI feature.
+		 *
+		 * @since 2.4.0
+		 * @hook classifai_has_access
+		 *
+		 * @param {bool}   $access   Current access value.
+		 * @param {string} $feature  Feature name.
+		 * @param {int}    $user_id  User ID.
+		 * @param {array}  $settings Feature settings.
+		 *
+		 * @return {bool} Should the user have access?
+		 */
+		return apply_filters( 'classifai_has_access', $access, $this->feature, $user_id, $settings );
+	}
+}
+
+
+
+ + + + + + + + +
+ + + +
+ + + + + diff --git a/Providers_Azure_ComputerVision.php.html b/Providers_Azure_ComputerVision.php.html index 66cf025f3..c21f5303a 100644 --- a/Providers_Azure_ComputerVision.php.html +++ b/Providers_Azure_ComputerVision.php.html @@ -69,6 +69,15 @@

Source: Providers/Azure/ComputerVision.php

$service ); + // Features provided by this provider. + $this->features = array( + 'image_captions' => __( 'Generate descriptive text', 'classifai' ), + 'image_tagging' => __( 'Generate tags', 'classifai' ), + 'smart_cropping' => __( 'Smart cropping', 'classifai' ), + 'ocr' => __( 'Scan images for text', 'classifai' ), + 'read_pdf' => __( 'Scan PDF', 'classifai' ), + ); + // Set the onboarding options. $this->onboarding_options = array( 'title' => __( 'Microsoft Azure AI Vision', 'classifai' ), @@ -96,23 +105,28 @@

Source: Providers/Azure/ComputerVision.php

* @return array */ public function get_default_settings() { - return [ - 'valid' => false, - 'url' => '', - 'api_key' => '', - 'enable_image_captions' => array( - 'alt' => 0, - 'caption' => 0, - 'description' => 0, - ), - 'enable_image_tagging' => true, - 'enable_smart_cropping' => false, - 'enable_ocr' => false, - 'enable_read_pdf' => false, - 'caption_threshold' => 75, - 'tag_threshold' => 70, - 'image_tag_taxonomy' => 'classifai-image-tags', - ]; + $default_settings = parent::get_default_settings() ?? []; + + return array_merge( + $default_settings, + [ + 'valid' => false, + 'url' => '', + 'api_key' => '', + 'enable_image_captions' => array( + 'alt' => 0, + 'caption' => 0, + 'description' => 0, + ), + 'enable_image_tagging' => true, + 'enable_smart_cropping' => false, + 'enable_ocr' => false, + 'enable_read_pdf' => false, + 'caption_threshold' => 75, + 'tag_threshold' => 70, + 'image_tag_taxonomy' => 'classifai-image-tags', + ] + ); } /** @@ -156,15 +170,13 @@

Source: Providers/Azure/ComputerVision.php

add_filter( 'wp_generate_attachment_metadata', [ $this, 'generate_image_alt_tags' ], 8, 2 ); add_filter( 'posts_clauses', [ $this, 'filter_attachment_query_keywords' ], 10, 1 ); - $settings = $this->get_settings(); - - if ( isset( $settings['enable_ocr'] ) && '1' === $settings['enable_ocr'] ) { + if ( $this->is_feature_enabled( 'ocr' ) ) { add_action( 'enqueue_block_editor_assets', [ $this, 'enqueue_editor_assets' ] ); add_filter( 'the_content', [ $this, 'add_ocr_aria_describedby' ] ); add_filter( 'rest_api_init', [ $this, 'add_ocr_data_to_api_response' ] ); } - if ( isset( $settings['enable_read_pdf'] ) && '1' === $settings['enable_read_pdf'] ) { + if ( $this->is_feature_enabled( 'read_pdf' ) ) { add_action( 'add_attachment', [ $this, 'read_pdf' ] ); add_action( 'classifai_retry_get_read_result', [ $this, 'do_read_cron' ], 10, 2 ); add_action( 'wp_ajax_classifai_get_read_status', [ $this, 'get_read_status_ajax' ] ); @@ -197,7 +209,7 @@

Source: Providers/Azure/ComputerVision.php

wp_enqueue_script( 'editor-ocr', CLASSIFAI_PLUGIN_URL . 'dist/editor-ocr.js', - get_asset_info( 'editor-ocr', 'dependencies' ), + array_merge( get_asset_info( 'editor-ocr', 'dependencies' ), array( 'lodash' ) ), get_asset_info( 'editor-ocr', 'version' ), true ); @@ -265,8 +277,6 @@

Source: Providers/Azure/ComputerVision.php

* @param \WP_Post $post The post object. */ public function setup_attachment_meta_box( $post ) { - $settings = $this->get_settings(); - if ( wp_attachment_is_image( $post ) ) { add_meta_box( 'attachment_meta_box', @@ -278,7 +288,7 @@

Source: Providers/Azure/ComputerVision.php

); } - if ( attachment_is_pdf( $post ) && is_array( $settings ) && isset( $settings['enable_read_pdf'] ) && '1' === $settings['enable_read_pdf'] ) { + if ( attachment_is_pdf( $post ) && $this->is_feature_enabled( 'read_pdf' ) ) { add_meta_box( 'attachment_meta_box', __( 'ClassifAI PDF Processing', 'classifai' ), @@ -296,7 +306,6 @@

Source: Providers/Azure/ComputerVision.php

* @param \WP_Post $post The post object. */ public function attachment_data_meta_box( $post ) { - $settings = $this->get_settings(); $captions = get_post_meta( $post->ID, '_wp_attachment_image_alt', true ) ? __( 'No descriptive text? Rescan image', 'classifai' ) : __( 'Generate descriptive text', 'classifai' ); $tags = ! empty( wp_get_object_terms( $post->ID, 'classifai-image-tags' ) ) ? __( 'Rescan image for new tags', 'classifai' ) : __( 'Generate image tags', 'classifai' ); $ocr = get_post_meta( $post->ID, 'classifai_computer_vision_ocr', true ) ? __( 'Rescan for text', 'classifai' ) : __( 'Scan image for text', 'classifai' ); @@ -304,7 +313,7 @@

Source: Providers/Azure/ComputerVision.php

?> <div class="misc-publishing-actions"> - <?php if ( ! empty( $this->get_alt_text_settings() ) ) : ?> + <?php if ( $this->is_feature_enabled( 'image_captions' ) ) : ?> <div class="misc-pub-section"> <label for="rescan-captions"> <input type="checkbox" value="yes" id="rescan-captions" name="rescan-captions"/> @@ -313,7 +322,7 @@

Source: Providers/Azure/ComputerVision.php

</div> <?php endif; ?> - <?php if ( is_array( $settings ) && isset( $settings['enable_image_tagging'] ) && '1' === $settings['enable_image_tagging'] ) : ?> + <?php if ( $this->is_feature_enabled( 'image_tagging' ) ) : ?> <div class="misc-pub-section"> <label for="rescan-tags"> <input type="checkbox" value="yes" id="rescan-tags" name="rescan-tags"/> @@ -322,7 +331,7 @@

Source: Providers/Azure/ComputerVision.php

</div> <?php endif; ?> - <?php if ( is_array( $settings ) && isset( $settings['enable_ocr'] ) && '1' === $settings['enable_ocr'] ) : ?> + <?php if ( $this->is_feature_enabled( 'ocr' ) ) : ?> <div class="misc-pub-section"> <label for="rescan-ocr"> <input type="checkbox" value="yes" id="rescan-ocr" name="rescan-ocr"/> @@ -331,7 +340,7 @@

Source: Providers/Azure/ComputerVision.php

</div> <?php endif; ?> - <?php if ( is_array( $settings ) && isset( $settings['enable_smart_cropping'] ) && '1' === $settings['enable_smart_cropping'] ) : ?> + <?php if ( $this->is_feature_enabled( 'smart_cropping' ) ) : ?> <div class="misc-pub-section"> <label for="rescan-smart-crop"> <input type="checkbox" value="yes" id="rescan-smart-crop" name="rescan-smart-crop"/> @@ -374,9 +383,7 @@

Source: Providers/Azure/ComputerVision.php

* @param \WP_post $post Post object for the attachment being viewed. */ public function add_rescan_button_to_media_modal( $form_fields, $post ) { - $settings = $this->get_settings(); - - if ( attachment_is_pdf( $post ) && is_array( $settings ) && isset( $settings['enable_read_pdf'] ) && '1' === $settings['enable_read_pdf'] ) { + if ( attachment_is_pdf( $post ) && $this->is_feature_enabled( 'read_pdf' ) ) { $read_text = empty( get_the_content( null, false, $post ) ) ? __( 'Scan', 'classifai' ) : __( 'Rescan', 'classifai' ); $status = get_post_meta( $post->ID, '_classifai_azure_read_status', true ); if ( ! empty( $status['status'] ) && 'running' === $status['status'] ) { @@ -399,7 +406,7 @@

Source: Providers/Azure/ComputerVision.php

$ocr_text = empty( get_post_meta( $post->ID, 'classifai_computer_vision_ocr', true ) ) ? __( 'Scan', 'classifai' ) : __( 'Rescan', 'classifai' ); $smart_crop_text = empty( get_transient( 'classifai_azure_computer_vision_smart_cropping_latest_response' ) ) ? __( 'Generate', 'classifai' ) : __( 'Regenerate', 'classifai' ); - if ( ! empty( $this->get_alt_text_settings() ) ) { + if ( $this->is_feature_enabled( 'image_captions' ) ) { $form_fields['rescan_alt_tags'] = [ 'label' => __( 'Descriptive text', 'classifai' ), 'input' => 'html', @@ -408,7 +415,7 @@

Source: Providers/Azure/ComputerVision.php

]; } - if ( is_array( $settings ) && isset( $settings['enable_image_tagging'] ) && '1' === $settings['enable_image_tagging'] ) { + if ( $this->is_feature_enabled( 'image_tagging' ) ) { $form_fields['rescan_captions'] = [ 'label' => __( 'Image tags', 'classifai' ), 'input' => 'html', @@ -417,7 +424,7 @@

Source: Providers/Azure/ComputerVision.php

]; } - if ( is_array( $settings ) && isset( $settings['enable_smart_cropping'] ) && '1' === $settings['enable_smart_cropping'] ) { + if ( $this->is_feature_enabled( 'smart_cropping' ) ) { $form_fields['rescan_smart_crop'] = [ 'label' => __( 'Smart thumbnail', 'classifai' ), 'input' => 'html', @@ -426,7 +433,7 @@

Source: Providers/Azure/ComputerVision.php

]; } - if ( is_array( $settings ) && isset( $settings['enable_ocr'] ) && '1' === $settings['enable_ocr'] ) { + if ( $this->is_feature_enabled( 'ocr' ) ) { $form_fields['rescan_ocr'] = [ 'label' => __( 'Scan image for text', 'classifai' ), 'input' => 'html', @@ -523,11 +530,11 @@

Source: Providers/Azure/ComputerVision.php

); } - if ( clean_input( 'rescan-captions' ) ) { + if ( clean_input( 'rescan-captions' ) && $this->is_feature_enabled( 'image_captions' ) ) { $routes[] = 'alt-tags'; - } elseif ( clean_input( 'rescan-tags' ) ) { + } elseif ( clean_input( 'rescan-tags' ) && $this->is_feature_enabled( 'image_tagging' ) ) { $routes[] = 'image-tags'; - } elseif ( clean_input( 'rescan-smart-crop' ) ) { + } elseif ( clean_input( 'rescan-smart-crop' ) && $this->is_feature_enabled( 'smart_cropping' ) ) { $routes[] = 'smart-crop'; } @@ -574,7 +581,7 @@

Source: Providers/Azure/ComputerVision.php

return $metadata; } - $should_smart_crop = isset( $settings['enable_smart_cropping'] ) && '1' === $settings['enable_smart_cropping']; + $should_smart_crop = $this->is_feature_enabled( 'smart_cropping' ); /** * Filters whether to apply smart cropping to the current image. @@ -618,10 +625,9 @@

Source: Providers/Azure/ComputerVision.php

public function generate_image_alt_tags( $metadata, $attachment_id ) { $image_scan = false; - $settings = $this->get_settings(); if ( - 'no' !== $settings['enable_image_tagging'] || - ! empty( $this->get_alt_text_settings() ) + $this->is_feature_enabled( 'image_tagging' ) || + $this->is_feature_enabled( 'image_captions' ) ) { // Allow scanning image that are not stored in local storage. @@ -684,7 +690,7 @@

Source: Providers/Azure/ComputerVision.php

return $metadata; } - $should_ocr_scan = isset( $settings['enable_ocr'] ) && '1' === $settings['enable_ocr']; + $should_ocr_scan = $this->is_feature_enabled( 'ocr' ); /** * Filters whether to run OCR scanning on the current image. @@ -775,11 +781,11 @@

Source: Providers/Azure/ComputerVision.php

$settings = $this->get_settings(); $api_features = []; - if ( in_array( 'alt-tags', $routes, true ) || ! empty( $this->get_alt_text_settings() ) ) { + if ( in_array( 'alt-tags', $routes, true ) || $this->is_feature_enabled( 'image_captions' ) ) { $api_features[] = 'Description'; } - if ( in_array( 'image-tags', $routes, true ) || ( isset( $settings['enable_image_tagging'] ) && 'no' !== $settings['enable_image_tagging'] ) ) { + if ( in_array( 'image-tags', $routes, true ) || $this->is_feature_enabled( 'image_tagging' ) ) { $api_features[] = 'Tags'; } @@ -794,13 +800,18 @@

Source: Providers/Azure/ComputerVision.php

* @param array $captions Captions returned from the API * @param int $attachment_id Post ID for the attachment. * - * @return string + * @return string|WP_Error */ protected function generate_alt_tags( $captions, $attachment_id ) { $rtn = ''; $enabled_fields = $this->get_alt_text_settings(); + // Don't save tags if feature is disabled or user don't have access to use it. + if ( ! $this->is_feature_enabled( 'image_captions' ) ) { + return new WP_Error( 'invalid_settings', esc_html__( 'Image descriptive text feature is disabled.', 'classifai' ) ); + } + /** * Filter the captions returned from the API. * @@ -874,7 +885,7 @@

Source: Providers/Azure/ComputerVision.php

return new WP_Error( 'invalid_settings', 'Can not retrieve the plugin settings.' ); } - $should_read_pdf = isset( $settings['enable_read_pdf'] ) && '1' === $settings['enable_read_pdf']; + $should_read_pdf = $this->is_feature_enabled( 'read_pdf' ); if ( ! $should_read_pdf ) { return false; @@ -917,11 +928,10 @@

Source: Providers/Azure/ComputerVision.php

* @return string|array|WP_Error */ protected function generate_image_tags( $tags, $attachment_id ) { - $rtn = ''; - $settings = $this->get_settings(); + $rtn = ''; // Don't save tags if the setting is disabled. - if ( ! is_array( $settings ) || ! isset( $settings['enable_image_tagging'] ) || '1' !== $settings['enable_image_tagging'] ) { + if ( ! $this->is_feature_enabled( 'image_tagging' ) ) { return new WP_Error( 'invalid_settings', esc_html__( 'Image tagging is disabled.', 'classifai' ) ); } @@ -1005,12 +1015,21 @@

Source: Providers/Azure/ComputerVision.php

'default_value' => $default_settings['api_key'], ] ); + + // Add Image descriptive text fields. + add_settings_section( + $this->get_option_name() . '_image_captions', + esc_html__( 'Image descriptive text settings', 'classifai' ), + '', + $this->get_option_name() + ); + add_settings_field( 'enable-image-captions', esc_html__( 'Generate descriptive text', 'classifai' ), [ $this, 'render_auto_caption_fields' ], $this->get_option_name(), - $this->get_option_name(), + $this->get_option_name() . '_image_captions', [ 'label_for' => 'enable_image_captions', 'input_type' => 'checkbox', @@ -1018,12 +1037,15 @@

Source: Providers/Azure/ComputerVision.php

'description' => __( 'Choose image fields where the generated captions should be applied.', 'classifai' ), ] ); + + $this->add_access_settings( 'image_captions', $this->get_option_name() . '_image_captions' ); + add_settings_field( 'caption-threshold', esc_html__( 'Descriptive text confidence threshold', 'classifai' ), [ $this, 'render_input' ], $this->get_option_name(), - $this->get_option_name(), + $this->get_option_name() . '_image_captions', [ 'label_for' => 'caption_threshold', 'input_type' => 'number', @@ -1031,12 +1053,21 @@

Source: Providers/Azure/ComputerVision.php

'description' => __( 'Minimum confidence score for automatically added alt text, numeric value from 0-100. Recommended to be set to at least 75.', 'classifai' ), ] ); + + // Add Tag images fields. + add_settings_section( + $this->get_option_name() . '_image_tagging', + esc_html__( 'Image tagging settings', 'classifai' ), + '', + $this->get_option_name() + ); + add_settings_field( 'enable-image-tagging', esc_html__( 'Tag images', 'classifai' ), [ $this, 'render_input' ], $this->get_option_name(), - $this->get_option_name(), + $this->get_option_name() . '_image_tagging', [ 'label_for' => 'enable_image_tagging', 'input_type' => 'checkbox', @@ -1044,12 +1075,15 @@

Source: Providers/Azure/ComputerVision.php

'description' => __( 'Image tags will be added automatically.', 'classifai' ), ] ); + + $this->add_access_settings( 'image_tagging', $this->get_option_name() . '_image_tagging' ); + add_settings_field( 'image-tag-threshold', esc_html__( 'Tag confidence threshold', 'classifai' ), [ $this, 'render_input' ], $this->get_option_name(), - $this->get_option_name(), + $this->get_option_name() . '_image_tagging', [ 'label_for' => 'tag_threshold', 'input_type' => 'number', @@ -1068,19 +1102,28 @@

Source: Providers/Azure/ComputerVision.php

esc_html__( 'Tag taxonomy', 'classifai' ), [ $this, 'render_select' ], $this->get_option_name(), - $this->get_option_name(), + $this->get_option_name() . '_image_tagging', [ 'label_for' => 'image_tag_taxonomy', 'options' => $options, 'default_value' => $default_settings['image_tag_taxonomy'], ] ); + + // Add Smart cropping fields. + add_settings_section( + $this->get_option_name() . '_smart_cropping', + esc_html__( 'Smart cropping settings', 'classifai' ), + '', + $this->get_option_name() + ); + add_settings_field( 'enable-smart-cropping', esc_html__( 'Enable smart cropping', 'classifai' ), [ $this, 'render_input' ], $this->get_option_name(), - $this->get_option_name(), + $this->get_option_name() . '_smart_cropping', [ 'label_for' => 'enable_smart_cropping', 'input_type' => 'checkbox', @@ -1091,12 +1134,23 @@

Source: Providers/Azure/ComputerVision.php

), ] ); + + $this->add_access_settings( 'smart_cropping', $this->get_option_name() . '_smart_cropping' ); + + // Add OCR fields. + add_settings_section( + $this->get_option_name() . '_ocr', + esc_html__( 'Scan images for text settings', 'classifai' ), + '', + $this->get_option_name() + ); + add_settings_field( 'enable-ocr', esc_html__( 'Scan images for text', 'classifai' ), [ $this, 'render_input' ], $this->get_option_name(), - $this->get_option_name(), + $this->get_option_name() . '_ocr', [ 'label_for' => 'enable_ocr', 'input_type' => 'checkbox', @@ -1107,12 +1161,23 @@

Source: Providers/Azure/ComputerVision.php

), ] ); + + $this->add_access_settings( 'ocr', $this->get_option_name() . '_ocr' ); + + // Add PDF fields. + add_settings_section( + $this->get_option_name() . '_read_pdf', + esc_html__( 'Scan PDF documents settings', 'classifai' ), + '', + $this->get_option_name() + ); + add_settings_field( 'enable-read-pdf', esc_html__( 'Enable scanning PDF', 'classifai' ), [ $this, 'render_input' ], $this->get_option_name(), - $this->get_option_name(), + $this->get_option_name() . '_read_pdf', [ 'label_for' => 'enable_read_pdf', 'input_type' => 'checkbox', @@ -1123,6 +1188,8 @@

Source: Providers/Azure/ComputerVision.php

), ] ); + + $this->add_access_settings( 'read_pdf', $this->get_option_name() . '_read_pdf' ); } /** @@ -1152,6 +1219,20 @@

Source: Providers/Azure/ComputerVision.php

$settings_errors['classifai-registration-credentials-empty'] = __( 'Please enter your credentials', 'classifai' ); } + $features = [ + 'image_captions', + 'image_tagging', + 'smart_cropping', + 'ocr', + 'read_pdf', + ]; + + $access_settings = []; + foreach ( $features as $feature ) { + $access_settings = array_merge( $access_settings, $this->sanitize_access_settings( $settings, $feature ) ); + } + $new_settings = array_merge( $new_settings, $access_settings ); + $checkbox_settings = [ 'enable_image_tagging', 'enable_smart_cropping', @@ -1381,6 +1462,28 @@

Source: Providers/Azure/ComputerVision.php

break; } } + + /** + * Determine if the feature is turned on. + * Note: This function does not check if the user has access to the feature. + * + * @since 2.5.0 + * + * @param string $feature Feature to check. + * @return bool + */ + public function is_enabled( string $feature ) { + $settings = $this->get_settings(); + $enable_key = 'enable_' . $feature; + + $is_enabled = ( isset( $settings[ $enable_key ] ) && 1 === (int) $settings[ $enable_key ] ); + if ( 'image_captions' === $feature ) { + $is_enabled = ( ! empty( $this->get_alt_text_settings() ) ); + } + + /** This filter is documented in includes/Classifai/Providers/Provider.php */ + return apply_filters( "classifai_is_{$feature}_enabled", $is_enabled, $settings ); + } } @@ -1400,7 +1503,7 @@

Source: Providers/Azure/ComputerVision.php


diff --git a/Providers_Azure_OCR.php.html b/Providers_Azure_OCR.php.html index 906433c7b..826bc84bb 100644 --- a/Providers_Azure_OCR.php.html +++ b/Providers_Azure_OCR.php.html @@ -417,7 +417,7 @@

Source: Providers/Azure/OCR.php


diff --git a/Providers_Azure_Personalizer.php.html b/Providers_Azure_Personalizer.php.html index 4f115335f..56ec900f8 100644 --- a/Providers_Azure_Personalizer.php.html +++ b/Providers_Azure_Personalizer.php.html @@ -73,12 +73,17 @@

Source: Providers/Azure/Personalizer.php

$service ); + // Features provided by this provider. + $this->features = array( + 'recommended_content' => __( 'Recommended content block', 'classifai' ), + ); + // Set the onboarding options. $this->onboarding_options = array( 'title' => __( 'Microsoft Azure AI Personalizer', 'classifai' ), 'fields' => array( 'url', 'api-key' ), 'features' => array( - 'authenticated' => __( 'Recommended content block', 'classifai' ), + 'enable_recommended_content' => __( 'Recommended content block', 'classifai' ), ), ); } @@ -96,19 +101,52 @@

Source: Providers/Azure/Personalizer.php

* @return array */ public function get_default_settings() { - return [ - 'authenticated' => false, - 'url' => '', - 'api_key' => '', - ]; + $default_settings = parent::get_default_settings() ?? []; + + return array_merge( + $default_settings, + [ + 'enable_recommended_content' => false, + 'authenticated' => false, + 'url' => '', + 'api_key' => '', + ] + ); + } + + /** + * Get the settings and allow for settings default values. + * + * @param string|bool|mixed $index Optional. Name of the settings option index. + * + * @return string|array|mixed + */ + public function get_settings( $index = false ) { + $defaults = $this->get_default_settings(); + $settings = get_option( $this->get_option_name(), [] ); + + // Backward compatibility for enable feature setting. + if ( ! empty( $settings ) && ! isset( $settings['enable_recommended_content'] ) ) { + $settings['enable_recommended_content'] = $settings['authenticated'] ?? $defaults['enable_recommended_content']; + } + + $settings = wp_parse_args( $settings, $defaults ); + + if ( $index && isset( $settings[ $index ] ) ) { + return $settings[ $index ]; + } + + return $settings; } /** * Register the functionality. */ public function register() { - // Setup Blocks - Blocks\setup(); + if ( $this->is_enabled( 'recommended_content' ) ) { + // Setup Blocks + Blocks\setup(); + } } /** @@ -155,6 +193,22 @@

Source: Providers/Azure/Personalizer.php

'description' => __( 'Azure AI Personalizer Key.', 'classifai' ), ] ); + + add_settings_field( + 'enable_recommended_content', + esc_html__( 'Recommend content', 'classifai' ), + [ $this, 'render_input' ], + $this->get_option_name(), + $this->get_option_name(), + [ + 'label_for' => 'enable_recommended_content', + 'input_type' => 'checkbox', + 'default_value' => $default_settings['enable_recommended_content'], + 'description' => __( 'Enables Recommended content block.', 'classifai' ), + ] + ); + + $this->add_access_settings( 'recommended_content' ); } /** @@ -165,7 +219,14 @@

Source: Providers/Azure/Personalizer.php

* @return array|mixed */ public function sanitize_settings( $settings ) { - $new_settings = []; + $new_settings = $this->sanitize_access_settings( $settings, 'recommended_content' ); + + if ( empty( $settings['enable_recommended_content'] ) || 1 !== (int) $settings['enable_recommended_content'] ) { + $new_settings['enable_recommended_content'] = 'no'; + } else { + $new_settings['enable_recommended_content'] = '1'; + } + if ( ! empty( $settings['url'] ) && ! empty( $settings['api_key'] ) ) { $auth_check = $this->authenticate_credentials( $settings['url'], $settings['api_key'] ); if ( is_wp_error( $auth_check ) ) { @@ -772,7 +833,7 @@

Source: Providers/Azure/Personalizer.php


diff --git a/Providers_Azure_Read.php.html b/Providers_Azure_Read.php.html index 6b5d529e3..c0a9b5fee 100644 --- a/Providers_Azure_Read.php.html +++ b/Providers_Azure_Read.php.html @@ -411,7 +411,7 @@

Source: Providers/Azure/Read.php


diff --git a/Providers_Azure_SmartCropping.php.html b/Providers_Azure_SmartCropping.php.html index 4ff0bcb47..f1974e6ac 100644 --- a/Providers_Azure_SmartCropping.php.html +++ b/Providers_Azure_SmartCropping.php.html @@ -434,7 +434,7 @@

Source: Providers/Azure/SmartCropping.php


diff --git a/Providers_Azure_TextToSpeech.php.html b/Providers_Azure_TextToSpeech.php.html index 969b16a4f..c3e8ff071 100644 --- a/Providers_Azure_TextToSpeech.php.html +++ b/Providers_Azure_TextToSpeech.php.html @@ -106,12 +106,17 @@

Source: Providers/Azure/TextToSpeech.php

$service ); + // Features provided by this provider. + $this->features = array( + 'text_to_speech' => __( 'Text to speech', 'classifai' ), + ); + // Set the onboarding options. $this->onboarding_options = array( 'title' => __( 'Microsoft Azure Text to Speech', 'classifai' ), 'fields' => array( 'url', 'api-key' ), 'features' => array( - 'authenticated' => __( 'Generate speech for post content', 'classifai' ), + 'enable_text_to_speech' => __( 'Generate speech for post content', 'classifai' ), ), ); } @@ -156,17 +161,20 @@

Source: Providers/Azure/TextToSpeech.php

* Register the actions needed. */ public function register() { - add_action( 'enqueue_block_editor_assets', [ $this, 'enqueue_editor_assets' ] ); - add_action( 'rest_api_init', [ $this, 'add_synthesize_speech_meta_to_rest_api' ] ); - add_action( 'add_meta_boxes', [ $this, 'add_meta_box' ] ); - add_action( 'save_post', [ $this, 'save_post_metadata' ], 5 ); - - $supported_post_type = get_tts_supported_post_types(); - foreach ( get_tts_supported_post_types() as $post_type ) { - add_action( 'rest_insert_' . $post_type, [ $this, 'rest_handle_audio' ], 10, 2 ); + if ( $this->is_feature_enabled( 'text_to_speech' ) ) { + add_action( 'enqueue_block_editor_assets', [ $this, 'enqueue_editor_assets' ] ); + add_action( 'rest_api_init', [ $this, 'add_synthesize_speech_meta_to_rest_api' ] ); + add_action( 'add_meta_boxes', [ $this, 'add_meta_box' ] ); + add_action( 'save_post', [ $this, 'save_post_metadata' ], 5 ); + + foreach ( get_tts_supported_post_types() as $post_type ) { + add_action( 'rest_insert_' . $post_type, [ $this, 'rest_handle_audio' ], 10, 2 ); + } } - add_filter( 'the_content', [ $this, 'render_post_audio_controls' ] ); + if ( $this->is_enabled( 'text_to_speech' ) ) { + add_filter( 'the_content', [ $this, 'render_post_audio_controls' ] ); + } } /** @@ -213,6 +221,23 @@

Source: Providers/Azure/TextToSpeech.php

] ); + add_settings_field( + 'enable_text_to_speech', + esc_html__( 'Generate speech for post content', 'classifai' ), + [ $this, 'render_input' ], + $this->get_option_name(), + $this->get_option_name(), + [ + 'label_for' => 'enable_text_to_speech', + 'input_type' => 'checkbox', + 'default_value' => $default_settings['enable_text_to_speech'], + 'description' => __( 'Enables speech generation for post content.', 'classifai' ), + ] + ); + + // Add user/role based access settings. + $this->add_access_settings( 'text_to_speech' ); + add_settings_field( 'post-types', esc_html__( 'Post Types', 'classifai' ), @@ -251,8 +276,15 @@

Source: Providers/Azure/TextToSpeech.php

*/ public function sanitize_settings( $settings ) { $current_settings = wp_parse_args( $this->get_settings(), $this->get_default_settings() ); + $current_settings = array_merge( $current_settings, $this->sanitize_access_settings( $settings, 'text_to_speech' ) ); $is_credentials_changed = false; + if ( empty( $settings['enable_text_to_speech'] ) || 1 !== (int) $settings['enable_text_to_speech'] ) { + $current_settings['enable_text_to_speech'] = 'no'; + } else { + $current_settings['enable_text_to_speech'] = '1'; + } + if ( ! empty( $settings['credentials']['url'] ) && ! empty( $settings['credentials']['api_key'] ) ) { $new_url = trailingslashit( esc_url_raw( $settings['credentials']['url'] ) ); $new_key = sanitize_text_field( $settings['credentials']['api_key'] ); @@ -462,16 +494,49 @@

Source: Providers/Azure/TextToSpeech.php

* Returns the default settings. */ public function get_default_settings() { - return [ - 'credentials' => array( - 'url' => '', - 'api_key' => '', - ), - 'voices' => array(), - 'voice' => '', - 'authenticated' => false, - 'post_types' => array(), - ]; + $default_settings = parent::get_default_settings() ?? []; + + return array_merge( + $default_settings, + [ + 'enable_text_to_speech' => false, + 'credentials' => array( + 'url' => '', + 'api_key' => '', + ), + 'voices' => array(), + 'voice' => '', + 'authenticated' => false, + 'post_types' => array( + 'post' => 'post', + ), + ] + ); + } + + /** + * Get the settings and allow for settings default values. + * + * @param string|bool|mixed $index Optional. Name of the settings option index. + * + * @return string|array|mixed + */ + public function get_settings( $index = false ) { + $defaults = $this->get_default_settings(); + $settings = get_option( $this->get_option_name(), [] ); + + // Backward compatibility for enable feature setting. + if ( ! empty( $settings ) && ! isset( $settings['enable_text_to_speech'] ) ) { + $settings['enable_text_to_speech'] = $settings['authenticated'] ?? $defaults['enable_text_to_speech']; + } + + $settings = wp_parse_args( $settings, $defaults ); + + if ( $index && isset( $settings[ $index ] ) ) { + return $settings[ $index ]; + } + + return $settings; } /** @@ -912,7 +977,6 @@

Source: Providers/Azure/TextToSpeech.php

return $options; } - } @@ -932,7 +996,7 @@

Source: Providers/Azure/TextToSpeech.php


diff --git a/Providers_OpenAI_APIRequest.php.html b/Providers_OpenAI_APIRequest.php.html index 88dc44839..3dd2a5c04 100644 --- a/Providers_OpenAI_APIRequest.php.html +++ b/Providers_OpenAI_APIRequest.php.html @@ -261,7 +261,7 @@

Source: Providers/OpenAI/APIRequest.php

$options = apply_filters( 'classifai_openai_api_request_post_form_options', [ - 'body' => $payload, + 'body' => $payload, 'headers' => [ 'Content-Type' => 'multipart/form-data; boundary=' . $boundary, ], @@ -378,7 +378,7 @@

Source: Providers/OpenAI/APIRequest.php


diff --git a/Providers_OpenAI_ChatGPT.php.html b/Providers_OpenAI_ChatGPT.php.html index 0758668fb..95655b9cb 100644 --- a/Providers_OpenAI_ChatGPT.php.html +++ b/Providers_OpenAI_ChatGPT.php.html @@ -66,7 +66,7 @@

Source: Providers/OpenAI/ChatGPT.php

* * @var int */ - protected $max_tokens = 4096; + protected $max_tokens = 16385; /** * Prompt for generating excerpts @@ -109,6 +109,13 @@

Source: Providers/OpenAI/ChatGPT.php

$service ); + // Features provided by this provider. + $this->features = array( + 'title_generation' => __( 'Generate titles', 'classifai' ), + 'excerpt_generation' => __( 'Generate excerpts', 'classifai' ), + 'resize_content' => __( 'Resize content', 'classifai' ), + ); + // Set the onboarding options. $this->onboarding_options = array( 'title' => __( 'OpenAI ChatGPT', 'classifai' ), @@ -121,50 +128,6 @@

Source: Providers/OpenAI/ChatGPT.php

); } - /** - * Determine if the current user can access the feature - * - * @param string $feature Feature to check. - * @return bool - */ - public function is_feature_enabled( string $feature = '' ) { - $access = false; - $settings = $this->get_settings(); - $user_roles = wp_get_current_user()->roles ?? []; - $feature_roles = []; - - $role_keys = [ - 'enable_excerpt' => 'roles', - 'enable_titles' => 'title_roles', - 'enable_resize_content' => 'resize_content_roles', - ]; - - if ( isset( $role_keys[ $feature ] ) ) { - $feature_roles = $settings[ $role_keys[ $feature ] ] ?? []; - } - - // Check if user has access to the feature and the feature is turned on. - if ( - ( ! empty( $feature_roles ) && ! empty( array_intersect( $user_roles, $feature_roles ) ) ) - && ( isset( $settings[ $feature ] ) && 1 === (int) $settings[ $feature ] ) - ) { - $access = true; - } - - /** - * Filter to override permission to a ChatGPT generate 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_openai_chatgpt_{$feature}", $access, $settings ); - } - /** * Register what we need for the plugin. * @@ -206,7 +169,7 @@

Source: Providers/OpenAI/ChatGPT.php

return; } - if ( $this->is_feature_enabled( 'enable_excerpt' ) ) { + if ( $this->is_feature_enabled( 'excerpt_generation' ) ) { // This script removes the core excerpt panel and replaces it with our own. wp_enqueue_script( 'classifai-post-excerpt', @@ -217,7 +180,7 @@

Source: Providers/OpenAI/ChatGPT.php

); } - if ( $this->is_feature_enabled( 'enable_titles' ) ) { + if ( $this->is_feature_enabled( 'title_generation' ) ) { wp_enqueue_script( 'classifai-post-status-info', CLASSIFAI_PLUGIN_URL . 'dist/post-status-info.js', @@ -236,7 +199,7 @@

Source: Providers/OpenAI/ChatGPT.php

); } - if ( $this->is_feature_enabled( 'enable_resize_content' ) ) { + if ( $this->is_feature_enabled( 'resize_content' ) ) { wp_enqueue_script( 'classifai-content-resizing-plugin-js', CLASSIFAI_PLUGIN_URL . 'dist/content-resizing-plugin.js', @@ -290,7 +253,7 @@

Source: Providers/OpenAI/ChatGPT.php

if ( $screen && ! $screen->is_block_editor() ) { if ( post_type_supports( $screen->post_type, 'title' ) && - $this->is_feature_enabled( 'enable_titles' ) + $this->is_feature_enabled( 'title_generation' ) ) { wp_enqueue_style( 'classifai-generate-title-classic-css', @@ -320,7 +283,7 @@

Source: Providers/OpenAI/ChatGPT.php

if ( post_type_supports( $screen->post_type, 'excerpt' ) && - $this->is_feature_enabled( 'enable_excerpt' ) + $this->is_feature_enabled( 'excerpt_generation' ) ) { wp_enqueue_style( 'classifai-generate-title-classic-css', @@ -412,35 +375,8 @@

Source: Providers/OpenAI/ChatGPT.php

] ); - $roles = get_editable_roles() ?? []; - $roles = array_combine( array_keys( $roles ), array_column( $roles, 'name' ) ); - - /** - * Filter the allowed WordPress roles for ChatGTP - * - * @since 2.3.0 - * @hook classifai_chatgpt_allowed_roles - * - * @param {array} $roles Array of arrays containing role information. - * @param {array} $default_settings Default setting values. - * - * @return {array} Roles array. - */ - $roles = apply_filters( 'classifai_chatgpt_allowed_roles', $roles, $default_settings ); - - add_settings_field( - 'roles', - esc_html__( 'Allowed roles', 'classifai' ), - [ $this, 'render_checkbox_group' ], - $this->get_option_name(), - $this->get_option_name() . '_excerpt', - [ - 'label_for' => 'roles', - 'options' => $roles, - 'default_values' => $default_settings['roles'], - 'description' => __( 'Choose which roles are allowed to generate excerpts.', 'classifai' ), - ] - ); + // Add user/role-based access settings for excerpt. + $this->add_access_settings( 'excerpt_generation', $this->get_option_name() . '_excerpt' ); add_settings_field( 'length', @@ -495,19 +431,8 @@

Source: Providers/OpenAI/ChatGPT.php

] ); - add_settings_field( - 'title-roles', - esc_html__( 'Allowed roles', 'classifai' ), - [ $this, 'render_checkbox_group' ], - $this->get_option_name(), - $this->get_option_name() . '_title', - [ - 'label_for' => 'title_roles', - 'options' => $roles, - 'default_values' => $default_settings['title_roles'], - 'description' => __( 'Choose which roles are allowed to generate titles.', 'classifai' ), - ] - ); + // Add user/role-based access settings for titles. + $this->add_access_settings( 'title_generation', $this->get_option_name() . '_title' ); add_settings_field( 'number-titles', @@ -559,23 +484,8 @@

Source: Providers/OpenAI/ChatGPT.php

] ); - $content_resize_roles = $roles; - - unset( $content_resize_roles['contributor'], $content_resize_roles['subscriber'] ); - - add_settings_field( - 'resize-content-roles', - esc_html__( 'Allowed roles', 'classifai' ), - [ $this, 'render_checkbox_group' ], - $this->get_option_name(), - $this->get_option_name() . '_resize_content_settings', - [ - 'label_for' => 'resize_content_roles', - 'options' => $content_resize_roles, - 'default_values' => $default_settings['resize_content_roles'], - 'description' => __( 'Choose which roles are allowed to resize content.', 'classifai' ), - ] - ); + // Add user/role-based access settings for resizing content. + $this->add_access_settings( 'resize_content', $this->get_option_name() . '_resize_content_settings' ); add_settings_field( 'number-resize-content', @@ -631,7 +541,10 @@

Source: Providers/OpenAI/ChatGPT.php

$new_settings = $this->get_settings(); $new_settings = array_merge( $new_settings, - $this->sanitize_api_key_settings( $new_settings, $settings ) + $this->sanitize_api_key_settings( $new_settings, $settings ), + $this->sanitize_access_settings( $settings, 'excerpt_generation' ), + $this->sanitize_access_settings( $settings, 'title_generation' ), + $this->sanitize_access_settings( $settings, 'resize_content' ), ); if ( empty( $settings['enable_excerpt'] ) || 1 !== (int) $settings['enable_excerpt'] ) { @@ -640,12 +553,6 @@

Source: Providers/OpenAI/ChatGPT.php

$new_settings['enable_excerpt'] = '1'; } - if ( isset( $settings['roles'] ) && is_array( $settings['roles'] ) ) { - $new_settings['roles'] = array_map( 'sanitize_text_field', $settings['roles'] ); - } else { - $new_settings['roles'] = array_keys( get_editable_roles() ?? [] ); - } - if ( isset( $settings['length'] ) && is_numeric( $settings['length'] ) && (int) $settings['length'] >= 0 ) { $new_settings['length'] = absint( $settings['length'] ); } else { @@ -664,12 +571,6 @@

Source: Providers/OpenAI/ChatGPT.php

$new_settings['enable_titles'] = '1'; } - if ( isset( $settings['title_roles'] ) && is_array( $settings['title_roles'] ) ) { - $new_settings['title_roles'] = array_map( 'sanitize_text_field', $settings['title_roles'] ); - } else { - $new_settings['title_roles'] = array_keys( get_editable_roles() ?? [] ); - } - if ( isset( $settings['number_titles'] ) && is_numeric( $settings['number_titles'] ) && (int) $settings['number_titles'] >= 1 && (int) $settings['number_titles'] <= 10 ) { $new_settings['number_titles'] = absint( $settings['number_titles'] ); } else { @@ -688,12 +589,6 @@

Source: Providers/OpenAI/ChatGPT.php

$new_settings['enable_resize_content'] = '1'; } - if ( isset( $settings['resize_content_roles'] ) && is_array( $settings['resize_content_roles'] ) ) { - $new_settings['resize_content_roles'] = array_map( 'sanitize_text_field', $settings['resize_content_roles'] ); - } else { - $new_settings['resize_content_roles'] = array_keys( get_editable_roles() ?? [] ); - } - if ( isset( $settings['number_resize_content'] ) && is_numeric( $settings['number_resize_content'] ) && (int) $settings['number_resize_content'] >= 1 && (int) $settings['number_resize_content'] <= 10 ) { $new_settings['number_resize_content'] = absint( $settings['number_resize_content'] ); } else { @@ -731,51 +626,50 @@

Source: Providers/OpenAI/ChatGPT.php

if ( ! function_exists( 'get_editable_roles' ) ) { require_once ABSPATH . 'wp-admin/includes/user.php'; } - $editable_roles = get_editable_roles() ?? []; - - $excerpt_length = absint( apply_filters( 'excerpt_length', 55 ) ); + $default_settings = parent::get_default_settings() ?? []; + $excerpt_length = absint( apply_filters( 'excerpt_length', 55 ) ); - return [ - 'authenticated' => false, - 'api_key' => '', - 'enable_excerpt' => false, - 'roles' => array_keys( $editable_roles ), - 'length' => $excerpt_length, - 'generate_excerpt_prompt' => array( - array( - 'title' => esc_html__( 'ClassifAI default', 'classifai' ), - 'prompt' => $this->generate_excerpt_prompt, - 'original' => 1, + return array_merge( + $default_settings, + [ + 'authenticated' => false, + 'api_key' => '', + 'enable_excerpt' => false, + 'length' => $excerpt_length, + 'generate_excerpt_prompt' => array( + array( + 'title' => esc_html__( 'ClassifAI default', 'classifai' ), + 'prompt' => $this->generate_excerpt_prompt, + 'original' => 1, + ), ), - ), - 'enable_titles' => false, - 'title_roles' => array_keys( $editable_roles ), - 'number_titles' => 1, - 'generate_title_prompt' => array( - array( - 'title' => esc_html__( 'ClassifAI default', 'classifai' ), - 'prompt' => $this->generate_title_prompt, - 'original' => 1, + 'enable_titles' => false, + 'number_titles' => 1, + 'generate_title_prompt' => array( + array( + 'title' => esc_html__( 'ClassifAI default', 'classifai' ), + 'prompt' => $this->generate_title_prompt, + 'original' => 1, + ), ), - ), - 'enable_resize_content' => false, - 'resize_content_roles' => array_keys( $editable_roles ), - 'number_resize_content' => 1, - 'shrink_content_prompt' => array( - array( - 'title' => esc_html__( 'ClassifAI default', 'classifai' ), - 'prompt' => $this->shrink_content_prompt, - 'original' => 1, + 'enable_resize_content' => false, + 'number_resize_content' => 1, + 'shrink_content_prompt' => array( + array( + 'title' => esc_html__( 'ClassifAI default', 'classifai' ), + 'prompt' => $this->shrink_content_prompt, + 'original' => 1, + ), ), - ), - 'grow_content_prompt' => array( - array( - 'title' => esc_html__( 'ClassifAI default', 'classifai' ), - 'prompt' => $this->grow_content_prompt, - 'original' => 1, + 'grow_content_prompt' => array( + array( + 'title' => esc_html__( 'ClassifAI default', 'classifai' ), + 'prompt' => $this->grow_content_prompt, + 'original' => 1, + ), ), - ), - ]; + ] + ); } /** @@ -864,7 +758,7 @@

Source: Providers/OpenAI/ChatGPT.php

// These checks (and the one above) happen in the REST permission_callback, // but we run them again here in case this method is called directly. - if ( empty( $settings ) || ( isset( $settings['authenticated'] ) && false === $settings['authenticated'] ) || ( ! $this->is_feature_enabled( 'enable_excerpt' ) && ( ! defined( 'WP_CLI' ) || ! WP_CLI ) ) ) { + if ( empty( $settings ) || ( isset( $settings['authenticated'] ) && false === $settings['authenticated'] ) || ( ! $this->is_feature_enabled( 'excerpt_generation' ) && ( ! defined( 'WP_CLI' ) || ! WP_CLI ) ) ) { return new WP_Error( 'not_enabled', esc_html__( 'Excerpt generation is disabled or OpenAI authentication failed. Please check your settings.', 'classifai' ) ); } @@ -969,7 +863,7 @@

Source: Providers/OpenAI/ChatGPT.php

// These checks happen in the REST permission_callback, // but we run them again here in case this method is called directly. - if ( empty( $settings ) || ( isset( $settings['authenticated'] ) && false === $settings['authenticated'] ) || ! $this->is_feature_enabled( 'enable_titles' ) ) { + if ( empty( $settings ) || ( isset( $settings['authenticated'] ) && false === $settings['authenticated'] ) || ! $this->is_feature_enabled( 'title_generation' ) ) { return new WP_Error( 'not_enabled', esc_html__( 'Title generation is disabled or OpenAI authentication failed. Please check your settings.', 'classifai' ) ); } @@ -1072,6 +966,12 @@

Source: Providers/OpenAI/ChatGPT.php

] ); + // These checks (and the one above) happen in the REST permission_callback, + // but we run them again here in case this method is called directly. + if ( empty( $settings ) || ( isset( $settings['authenticated'] ) && false === $settings['authenticated'] ) || ( ! $this->is_feature_enabled( 'resize_content' ) && ( ! defined( 'WP_CLI' ) || ! WP_CLI ) ) ) { + return new WP_Error( 'not_enabled', esc_html__( 'Resizing content is disabled or OpenAI authentication failed. Please check your settings.', 'classifai' ) ); + } + $request = new APIRequest( $settings['api_key'] ?? '', $this->get_option_name() ); if ( 'shrink' === $args['resize_type'] ) { @@ -1218,6 +1118,34 @@

Source: Providers/OpenAI/ChatGPT.php

return apply_filters( 'classifai_chatgpt_content', $content, $post_id ); } + /** + * Retrieves the allowed WordPress roles for OpenAI ChatGPT. + * + * @since 2.4.0 + * + * @return array An associative array where the keys are role keys and the values are role names. + */ + public function get_roles() { + $default_settings = $this->get_default_settings(); + $roles = get_editable_roles() ?? []; + $roles = array_combine( array_keys( $roles ), array_column( $roles, 'name' ) ); + + /** + * Filter the allowed WordPress roles for ChatGTP + * + * @since 2.3.0 + * @hook classifai_chatgpt_allowed_roles + * + * @param {array} $roles Array of arrays containing role information. + * @param {array} $default_settings Default setting values. + * + * @return {array} Roles array. + */ + $roles = apply_filters( 'classifai_chatgpt_allowed_roles', $roles, $default_settings ); + + return $roles; + } + /** * Sanitize the prompt data. * This is used for the repeater field. @@ -1296,6 +1224,37 @@

Source: Providers/OpenAI/ChatGPT.php

return $default_prompt; } + + /** + * Determine if the feature is turned on. + * Note: This function does not check if the user has access to the feature. + * + * @param string $feature Feature to check. + * @return bool + */ + public function is_enabled( string $feature ) { + $settings = $this->get_settings(); + $enable_key = 'enable_' . $feature; + + // Handle different enable keys. + switch ( $feature ) { + case 'title_generation': + $enable_key = 'enable_titles'; + break; + + case 'excerpt_generation': + $enable_key = 'enable_excerpt'; + break; + + default: + break; + } + + $is_enabled = ( isset( $settings[ $enable_key ] ) && 1 === (int) $settings[ $enable_key ] ); + + /** This filter is documented in includes/Classifai/Providers/Provider.php */ + return apply_filters( "classifai_is_{$feature}_enabled", $is_enabled, $settings ); + } } @@ -1315,7 +1274,7 @@

Source: Providers/OpenAI/ChatGPT.php


diff --git a/Providers_OpenAI_DallE.php.html b/Providers_OpenAI_DallE.php.html index d7836d36d..2184f985b 100644 --- a/Providers_OpenAI_DallE.php.html +++ b/Providers_OpenAI_DallE.php.html @@ -41,6 +41,8 @@

Source: Providers/OpenAI/DallE.php

use Classifai\Providers\Provider; use Classifai\Providers\OpenAI\APIRequest; use function Classifai\get_asset_info; +use function Classifai\render_disable_feature_link; + use WP_Error; class DallE extends Provider { @@ -74,6 +76,11 @@

Source: Providers/OpenAI/DallE.php

$service ); + // Features provided by this provider. + $this->features = array( + 'image_generation' => __( 'Generate images', 'classifai' ), + ); + // Set the onboarding options. $this->onboarding_options = array( 'title' => __( 'OpenAI DALL·E', 'classifai' ), @@ -263,6 +270,9 @@

Source: Providers/OpenAI/DallE.php

</h2> <span class="spinner"></span> <ul></ul> + <p> + <?php render_disable_feature_link( 'image_generation' ); ?> + </p> </div> </script> @@ -308,42 +318,8 @@

Source: Providers/OpenAI/DallE.php

] ); - // Get all roles that have the upload_files cap. - $roles = get_editable_roles() ?? []; - $roles = array_filter( - $roles, - function( $role ) { - return isset( $role['capabilities'], $role['capabilities']['upload_files'] ) && $role['capabilities']['upload_files']; - } - ); - $roles = array_combine( array_keys( $roles ), array_column( $roles, 'name' ) ); - - /** - * Filter the allowed WordPress roles for DALL·E - * - * @since 2.3.0 - * @hook classifai_openai_dalle_allowed_image_roles - * - * @param {array} $roles Array of arrays containing role information. - * @param {array} $default_settings Default setting values. - * - * @return {array} Roles array. - */ - $roles = apply_filters( 'classifai_openai_dalle_allowed_image_roles', $roles, $default_settings ); - - add_settings_field( - 'roles', - esc_html__( 'Allowed roles', 'classifai' ), - [ $this, 'render_checkbox_group' ], - $this->get_option_name(), - $this->get_option_name(), - [ - 'label_for' => 'roles', - 'options' => $roles, - 'default_values' => $default_settings['roles'], - 'description' => __( 'Choose which roles are allowed to generate images. Note that the roles above only include those that have permissions to upload media.', 'classifai' ), - ] - ); + // Add user/role based settings. + $this->add_access_settings( 'image_generation' ); add_settings_field( 'number', @@ -388,7 +364,8 @@

Source: Providers/OpenAI/DallE.php

$new_settings = $this->get_settings(); $new_settings = array_merge( $new_settings, - $this->sanitize_api_key_settings( $new_settings, $settings ) + $this->sanitize_api_key_settings( $new_settings, $settings ), + $this->sanitize_access_settings( $settings, 'image_generation' ) ); if ( empty( $settings['enable_image_gen'] ) || 1 !== (int) $settings['enable_image_gen'] ) { @@ -397,12 +374,6 @@

Source: Providers/OpenAI/DallE.php

$new_settings['enable_image_gen'] = '1'; } - if ( isset( $settings['roles'] ) && is_array( $settings['roles'] ) ) { - $new_settings['roles'] = array_map( 'sanitize_text_field', $settings['roles'] ); - } else { - $new_settings['roles'] = array_keys( get_editable_roles() ?? [] ); - } - if ( isset( $settings['number'] ) && is_numeric( $settings['number'] ) && (int) $settings['number'] >= 1 && (int) $settings['number'] <= 10 ) { $new_settings['number'] = absint( $settings['number'] ); } else { @@ -431,14 +402,18 @@

Source: Providers/OpenAI/DallE.php

* @return array */ public function get_default_settings() { - return [ - 'authenticated' => false, - 'api_key' => '', - 'enable_image_gen' => false, - 'roles' => array_keys( get_editable_roles() ?? [] ), - 'number' => 1, - 'size' => '1024x1024', - ]; + $default_settings = parent::get_default_settings() ?? []; + + return array_merge( + $default_settings, + [ + 'authenticated' => false, + 'api_key' => '', + 'enable_image_gen' => false, + 'number' => 1, + 'size' => '1024x1024', + ] + ); } /** @@ -572,20 +547,17 @@

Source: Providers/OpenAI/DallE.php

/** * Checks whether we can generate images. * + * @param string $feature Feature to check. * @return bool */ - public function is_feature_enabled() { + public function is_feature_enabled( string $feature = 'image_generation' ) { $access = false; $settings = $this->get_settings(); - // Check if the current user has permission to generate images. - $roles = $settings['roles'] ?? []; - $user_roles = wp_get_current_user()->roles ?? []; - if ( - current_user_can( 'upload_files' ) - && ( ! empty( $roles ) && empty( array_diff( $user_roles, $roles ) ) ) - && ( isset( $settings['enable_image_gen'] ) && 1 === (int) $settings['enable_image_gen'] ) + current_user_can( 'upload_files' ) && + $this->has_access( $feature ) && + $this->is_enabled( $feature ) ) { $access = true; } @@ -604,6 +576,58 @@

Source: Providers/OpenAI/DallE.php

return apply_filters( 'classifai_openai_dalle_enable_image_gen', $access, $settings ); } + /** + * Determine if the feature is turned on. + * Note: This function does not check if the user has access to the feature. + * + * @since 2.5.0 + * + * @param string $feature Feature to check. + * @return bool + */ + public function is_enabled( string $feature ) { + $settings = $this->get_settings(); + $enable_key = ( 'image_generation' === $feature ) ? 'enable_image_gen' : 'enable_' . $feature; + $is_enabled = ( isset( $settings[ $enable_key ] ) && 1 === (int) $settings[ $enable_key ] ); + + /** This filter is documented in includes/Classifai/Providers/Provider.php */ + return apply_filters( "classifai_is_{$feature}_enabled", $is_enabled, $settings ); + } + + /** + * Retrieves the allowed WordPress roles for OpenAI DALL·E + * + * @since 2.4.0 + * + * @return array An associative array where the keys are role keys and the values are role names. + */ + public function get_roles() { + $default_settings = $this->get_default_settings(); + // Get all roles that have the upload_files cap. + $roles = get_editable_roles() ?? []; + $roles = array_filter( + $roles, + function( $role ) { + return isset( $role['capabilities'], $role['capabilities']['upload_files'] ) && $role['capabilities']['upload_files']; + } + ); + $roles = array_combine( array_keys( $roles ), array_column( $roles, 'name' ) ); + + /** + * Filter the allowed WordPress roles for DALL·E + * + * @since 2.3.0 + * @hook classifai_openai_dalle_allowed_image_roles + * + * @param {array} $roles Array of arrays containing role information. + * @param {array} $default_settings Default setting values. + * + * @return {array} Roles array. + */ + $roles = apply_filters( 'classifai_openai_dalle_allowed_image_roles', $roles, $default_settings ); + + return $roles; + } } @@ -623,7 +647,7 @@

Source: Providers/OpenAI/DallE.php


diff --git a/Providers_OpenAI_Embeddings.php.html b/Providers_OpenAI_Embeddings.php.html index 35bc8d828..7cf3552af 100644 --- a/Providers_OpenAI_Embeddings.php.html +++ b/Providers_OpenAI_Embeddings.php.html @@ -86,6 +86,11 @@

Source: Providers/OpenAI/Embeddings.php

$service ); + // Features provided by this provider. + $this->features = array( + 'classification' => __( 'Content classification', 'classifai' ), + ); + // Set the onboarding options. $this->onboarding_options = array( 'title' => __( 'OpenAI Embeddings', 'classifai' ), @@ -104,17 +109,40 @@

Source: Providers/OpenAI/Embeddings.php

public function register() { $settings = $this->get_settings(); - if ( isset( $settings['enable_classification'] ) && 1 === (int) $settings['enable_classification'] ) { + if ( $this->is_feature_enabled( 'classification' ) ) { add_action( 'wp_insert_post', [ $this, 'generate_embeddings_for_post' ] ); add_action( 'created_term', [ $this, 'generate_embeddings_for_term' ] ); add_action( 'edited_terms', [ $this, 'generate_embeddings_for_term' ] ); + add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_admin_assets' ] ); add_action( 'enqueue_block_editor_assets', [ $this, 'enqueue_editor_assets' ], 9 ); add_filter( 'rest_api_init', [ $this, 'add_process_content_meta_to_rest_api' ] ); add_action( 'add_meta_boxes', [ $this, 'add_metabox' ] ); add_action( 'save_post', [ $this, 'save_metabox' ] ); + add_action( 'wp_ajax_get_post_classifier_embeddings_preview_data', array( $this, 'get_post_classifier_embeddings_preview_data' ) ); } } + /** + * Enqueue the admin scripts. + */ + public function enqueue_admin_assets() { + wp_enqueue_script( + 'classifai-language-processing-script', + CLASSIFAI_PLUGIN_URL . 'dist/language-processing.js', + get_asset_info( 'language-processing', 'dependencies' ), + get_asset_info( 'language-processing', 'version' ), + true + ); + + wp_enqueue_style( + 'classifai-language-processing-style', + CLASSIFAI_PLUGIN_URL . 'dist/language-processing.css', + array(), + get_asset_info( 'language-processing', 'version' ), + 'all' + ); + } + /** * Enqueue editor assets. */ @@ -172,6 +200,9 @@

Source: Providers/OpenAI/Embeddings.php

] ); + // Add user/role based access settings. + $this->add_access_settings( 'classification' ); + add_settings_field( 'post-types', esc_html__( 'Post types', 'classifai' ), @@ -243,7 +274,8 @@

Source: Providers/OpenAI/Embeddings.php

$new_settings = $this->get_settings(); $new_settings = array_merge( $new_settings, - $this->sanitize_api_key_settings( $new_settings, $settings ) + $this->sanitize_api_key_settings( $new_settings, $settings ), + $this->sanitize_access_settings( $settings, 'classification' ), ); if ( empty( $settings['enable_classification'] ) || 1 !== (int) $settings['enable_classification'] ) { @@ -252,7 +284,8 @@

Source: Providers/OpenAI/Embeddings.php

$new_settings['enable_classification'] = '1'; // If any NLU features are turned, show an admin notice. - if ( language_processing_features_enabled() ) { + $nlu = new NLU( $this->service ); + if ( language_processing_features_enabled() && $nlu->is_enabled( 'content_classification' ) ) { add_settings_error( 'enable_classification', 'conflict', @@ -291,6 +324,15 @@

Source: Providers/OpenAI/Embeddings.php

} else { $new_settings['taxonomies'][ $taxonomy_key ] = '0'; } + + // Sanitize the threshold setting. + $taxonomy_key = $taxonomy_key . '_threshold'; + if ( isset( $settings['taxonomies'][ $taxonomy_key ] ) && '0' !== $settings['taxonomies'][ $taxonomy_key ] ) { + $threshold_value = min( absint( $settings['taxonomies'][ $taxonomy_key ] ), 100 ); + $new_settings['taxonomies'][ $taxonomy_key ] = $threshold_value ? $threshold_value : 75; + } else { + $new_settings['taxonomies'][ $taxonomy_key ] = 75; + } } // Sanitize the number setting. @@ -316,15 +358,20 @@

Source: Providers/OpenAI/Embeddings.php

* @return array */ public function get_default_settings() { - return [ - 'authenticated' => false, - 'api_key' => '', - 'enable_classification' => false, - 'post_types' => [ 'post' ], - 'post_statuses' => [ 'publish' ], - 'taxonomies' => [ 'category' ], - 'number' => 1, - ]; + $default_settings = parent::get_default_settings() ?? []; + + return array_merge( + $default_settings, + [ + 'authenticated' => false, + 'api_key' => '', + 'enable_classification' => false, + 'post_types' => [ 'post' ], + 'post_statuses' => [ 'publish' ], + 'taxonomies' => [ 'category' ], + 'number' => 1, + ] + ); } /** @@ -372,6 +419,39 @@

Source: Providers/OpenAI/Embeddings.php

return apply_filters( 'classifai_openai_embeddings_post_types', $this->get_supported_post_types() ); } + /** + * Get the threshold for the similarity calculation. + * + * @since 2.5.0 + * + * @param string $taxonomy Taxonomy slug. + * @return float + */ + public function get_threshold( $taxonomy = '' ) { + $settings = $this->get_settings(); + $threshold = 1; + + if ( ! empty( $taxonomy ) ) { + $threshold = isset( $settings['taxonomies'][ $taxonomy . '_threshold' ] ) ? $settings['taxonomies'][ $taxonomy . '_threshold' ] : 75; + } + + // Convert $threshold (%) to decimal. + $threshold = 1 - ( (float) $threshold / 100 ); + + /** + * Filter the threshold for the similarity calculation. + * + * @since 2.5.0 + * @hook classifai_threshold + * + * @param {float} $threshold The threshold to use. + * @param {string} $taxonomy The taxonomy to get the threshold for. + * + * @return {float} The threshold to use. + */ + return apply_filters( 'classifai_threshold', $threshold, $taxonomy ); + } + /** * The list of supported post statuses. * @@ -410,12 +490,36 @@

Source: Providers/OpenAI/Embeddings.php

return apply_filters( 'classifai_openai_embeddings_taxonomies', $this->get_supported_taxonomies() ); } + /** + * Get the data to preview terms. + * + * @since 2.5.0 + * + * @return array + */ + public function get_post_classifier_embeddings_preview_data() { + $nonce = isset( $_POST['nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['nonce'] ) ) : false; + + if ( ! $nonce || ! wp_verify_nonce( $nonce, 'classifai-previewer-openai_embeddings-action' ) ) { + wp_send_json_error( esc_html__( 'Failed nonce check.', 'classifai' ) ); + } + + $post_id = filter_input( INPUT_POST, 'post_id', FILTER_SANITIZE_NUMBER_INT ); + + $embeddings_terms = $this->generate_embeddings_for_post( $post_id, true ); + + return wp_send_json_success( $embeddings_terms ); + } + /** * Trigger embedding generation for content being saved. * - * @param int $post_id ID of post being saved. + * @param int $post_id ID of post being saved. + * @param bool $dryrun Whether to run the process or just return the data. + * + * @return array|WP_Error */ - public function generate_embeddings_for_post( $post_id ) { + public function generate_embeddings_for_post( $post_id, $dryrun = false ) { // Don't run on autosaves. if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) { return; @@ -430,14 +534,17 @@

Source: Providers/OpenAI/Embeddings.php

// Only run on supported post types and statuses. if ( - ! in_array( $post->post_type, $this->supported_post_types(), true ) || - ! in_array( $post->post_status, $this->supported_post_statuses(), true ) + ! $dryrun + && ( + ! in_array( $post->post_type, $this->supported_post_types(), true ) || + ! in_array( $post->post_status, $this->supported_post_statuses(), true ) + ) ) { return; } // Don't run if turned off for this particular post. - if ( 'no' === get_post_meta( $post_id, '_classifai_process_content', true ) ) { + if ( 'no' === get_post_meta( $post_id, '_classifai_process_content', true ) && ! $dryrun ) { return; } @@ -445,8 +552,12 @@

Source: Providers/OpenAI/Embeddings.php

// Add terms to this item based on embedding data. if ( $embeddings && ! is_wp_error( $embeddings ) ) { - update_post_meta( $post_id, 'classifai_openai_embeddings', array_map( 'sanitize_text_field', $embeddings ) ); - $this->set_terms( $post_id, $embeddings ); + if ( $dryrun ) { + return $this->get_terms( $embeddings ); + } else { + update_post_meta( $post_id, 'classifai_openai_embeddings', array_map( 'sanitize_text_field', $embeddings ) ); + return $this->set_terms( $post_id, $embeddings ); + } } } @@ -467,6 +578,102 @@

Source: Providers/OpenAI/Embeddings.php

$settings = $this->get_settings(); $number_to_add = $settings['number'] ?? 1; + $embedding_similarity = $this->get_embeddings_similarity( $embedding ); + + if ( empty( $embedding_similarity ) ) { + return; + } + + // Set terms based on similarity. + foreach ( $embedding_similarity as $tax => $terms ) { + // Sort embeddings from lowest to highest. + asort( $terms ); + + // Only add the number of terms specified in settings. + if ( count( $terms ) > $number_to_add ) { + $terms = array_slice( $terms, 0, $number_to_add, true ); + } + + wp_set_object_terms( $post_id, array_map( 'absint', array_keys( $terms ) ), $tax, false ); + } + } + + /** + * Get the terms of a post based on embeddings. + * + * @param array $embedding Embedding data. + * + * @return array|WP_Error + */ + private function get_terms( array $embedding = [] ) { + if ( empty( $embedding ) ) { + return new WP_Error( 'data_required', esc_html__( 'Valid embedding data is required to get terms.', 'classifai' ) ); + } + + $settings = $this->get_settings(); + $number_to_add = $settings['number'] ?? 1; + $embedding_similarity = $this->get_embeddings_similarity( $embedding, false ); + + if ( empty( $embedding_similarity ) ) { + return; + } + + // Set terms based on similarity. + $index = 0; + $result = []; + + foreach ( $embedding_similarity as $tax => $terms ) { + // Get the taxonomy name. + $taxonomy = get_taxonomy( $tax ); + $tax_name = $taxonomy->labels->singular_name; + + // Sort embeddings from lowest to highest. + asort( $terms ); + + // Return the terms. + $result[ $index ] = new \stdClass(); + + $result[ $index ]->{$tax_name} = []; + + $term_added = 0; + foreach ( $terms as $term_id => $similarity ) { + // Stop if we have added the number of terms specified in settings. + if ( $number_to_add <= $term_added ) { + break; + } + + // Convert $similarity to percentage. + $similarity = round( ( 1 - $similarity ), 10 ); + + $result[ $index ]->{$tax_name}[] = [// phpcs:ignore Squiz.PHP.DisallowMultipleAssignments.Found + 'label' => get_term( $term_id )->name, + 'score' => $similarity, + ]; + $term_added++; + } + + // Only add the number of terms specified in settings. + if ( count( $terms ) > $number_to_add ) { + $terms = array_slice( $terms, 0, $number_to_add, true ); + } + + $index++; + } + + return $result; + } + + /** + * Get the similarity between an embedding and all terms. + * + * @since 2.5.0 + * + * @param array $embedding Embedding data. + * @param bool $consider_threshold Whether to consider the threshold setting. + * + * @return array + */ + private function get_embeddings_similarity( $embedding, $consider_threshold = true ) { $embedding_similarity = []; $taxonomies = $this->supported_taxonomies(); $calculations = new EmbeddingCalculations(); @@ -486,6 +693,9 @@

Source: Providers/OpenAI/Embeddings.php

continue; } + // Get threshold setting for this taxonomy. + $threshold = $this->get_threshold( $tax ); + // Get embedding similarity for each term. foreach ( $terms as $term_id ) { if ( ! current_user_can( 'assign_term', $term_id ) && ( ! defined( 'WP_CLI' ) || ! WP_CLI ) ) { @@ -496,29 +706,14 @@

Source: Providers/OpenAI/Embeddings.php

if ( $term_embedding ) { $similarity = $calculations->similarity( $embedding, $term_embedding ); - if ( false !== $similarity ) { - $embedding_similarity[ $tax ][ $term_id ] = $calculations->similarity( $embedding, $term_embedding ); + if ( false !== $similarity && ( ! $consider_threshold || $similarity <= $threshold ) ) { + $embedding_similarity[ $tax ][ $term_id ] = $similarity; } } } } - if ( empty( $embedding_similarity ) ) { - return; - } - - // Set terms based on similarity. - foreach ( $embedding_similarity as $tax => $terms ) { - // Sort embeddings from lowest to highest. - asort( $terms ); - - // Only add the number of terms specified in settings. - if ( count( $terms ) > $number_to_add ) { - $terms = array_slice( $terms, 0, $number_to_add, true ); - } - - wp_set_object_terms( $post_id, array_map( 'absint', array_keys( $terms ) ), $tax, false ); - } + return $embedding_similarity; } /** @@ -782,7 +977,7 @@

Source: Providers/OpenAI/Embeddings.php

<p> <label for="classifai-process-content" class="classifai-preview-toggle"> <input type="checkbox" value="yes" name="_classifai_process_content" id="classifai-process-content" <?php echo esc_html( $checked ); ?> /> - <strong><?php esc_html_e( 'Process content on update', 'classifai' ); ?></strong> + <strong><?php esc_html_e( 'Automatically tag content on update', 'classifai' ); ?></strong> </label> </p> </div> @@ -852,7 +1047,7 @@

Source: Providers/OpenAI/Embeddings.php


diff --git a/Providers_OpenAI_OpenAI.php.html b/Providers_OpenAI_OpenAI.php.html index 3b7b47dea..ff74223b1 100644 --- a/Providers_OpenAI_OpenAI.php.html +++ b/Providers_OpenAI_OpenAI.php.html @@ -346,7 +346,7 @@

Source: Providers/OpenAI/OpenAI.php


diff --git a/Providers_OpenAI_Tokenizer.php.html b/Providers_OpenAI_Tokenizer.php.html index 78cc3154a..54d5e76e3 100644 --- a/Providers_OpenAI_Tokenizer.php.html +++ b/Providers_OpenAI_Tokenizer.php.html @@ -50,9 +50,9 @@

Source: Providers/OpenAI/Tokenizer.php

/** * How many characters in one token (roughly) * - * @var int + * @var float */ - public $characters_in_token = 4; + public $characters_in_token = 3.5; /** * How many tokens a word will take (roughly) @@ -150,7 +150,7 @@

Source: Providers/OpenAI/Tokenizer.php

* can be and trim it up. */ $tokens_to_trim = $content_tokens - $max_tokens; - $characters_to_trim = $tokens_to_trim * $this->characters_in_token; + $characters_to_trim = (int) ceil( $tokens_to_trim * $this->characters_in_token ); $max_content_length = mb_strlen( $content ) - $characters_to_trim; $trimmed_content = mb_substr( $content, 0, $max_content_length ); @@ -183,7 +183,7 @@

Source: Providers/OpenAI/Tokenizer.php


diff --git a/Providers_OpenAI_Whisper_Transcribe.php.html b/Providers_OpenAI_Whisper_Transcribe.php.html index 204a3dba7..db7fc869e 100644 --- a/Providers_OpenAI_Whisper_Transcribe.php.html +++ b/Providers_OpenAI_Whisper_Transcribe.php.html @@ -194,7 +194,7 @@

Source: Providers/OpenAI/Whisper/Transcribe.php


diff --git a/Providers_Provider.php.html b/Providers_Provider.php.html new file mode 100644 index 000000000..dac55757b --- /dev/null +++ b/Providers_Provider.php.html @@ -0,0 +1,868 @@ + + + + + Source: Providers/Provider.php - 10up ClassifAI Hook Docs + + + + + + + + + + + + + +
+ + +

Source: Providers/Provider.php

+ + + + + + + +
+
+
<?php
+/**
+ *  Abstract class that defines the providers for a service.
+ */
+
+namespace Classifai\Providers;
+
+use function Classifai\get_feature_default_settings;
+
+abstract class Provider {
+
+	/**
+	 * @var string The display name for the provider. ie. Azure
+	 */
+	public $provider_name;
+
+
+	/**
+	 * @var string $provider_service_name The formal name of the service being provided. i.e Computer Vision, NLU, Rekongnition.
+	 */
+	public $provider_service_name;
+
+	/**
+	 * @var string $option_name Name of the option where the provider settings are stored.
+	 */
+	protected $option_name;
+
+
+	/**
+	 * @var string $service The name of the service this provider belongs to.
+	 */
+	protected $service;
+
+	/**
+	 * @var array $onboarding The onboarding options for this provider.
+	 */
+	public $onboarding_options;
+
+	/**
+	 * @var array $features Array of features provided by this provider.
+	 */
+	protected $features = array();
+
+	/**
+	 * Provider constructor.
+	 *
+	 * @param string $provider_name         The name of the Provider that will appear in the admin tab
+	 * @param string $provider_service_name The name of the Service.
+	 * @param string $option_name           Name of the option where the provider settings are stored.
+	 * @param string $service               What service does this provider belong to.
+	 */
+	public function __construct( $provider_name, $provider_service_name, $option_name, $service ) {
+		$this->provider_name         = $provider_name;
+		$this->provider_service_name = $provider_service_name;
+		$this->option_name           = $option_name;
+		$this->service               = $service;
+		$this->onboarding_options    = array();
+	}
+
+	/**
+	 * Provides the provider name.
+	 *
+	 * @return string
+	 */
+	public function get_provider_name() {
+		return $this->provider_name;
+	}
+
+	/** Returns the name of the settings section for this provider
+	 *
+	 * @return string
+	 */
+	public function get_settings_section() {
+		return $this->option_name;
+	}
+
+	/**
+	 * Get the option name.
+	 *
+	 * @return string
+	 */
+	public function get_option_name() {
+		return 'classifai_' . $this->option_name;
+	}
+
+	/**
+	 * Get provider features.
+	 *
+	 * @return array
+	 */
+	public function get_features() {
+		return $this->features;
+	}
+
+	/**
+	 * Get the onboarding options.
+	 *
+	 * @return array
+	 */
+	public function get_onboarding_options() {
+		if ( empty( $this->onboarding_options ) || ! isset( $this->onboarding_options['features'] ) ) {
+			return array();
+		}
+
+		$settings      = $this->get_settings();
+		$is_configured = $this->is_configured();
+
+		foreach ( $this->onboarding_options['features'] as $key => $title ) {
+			$enabled = isset( $settings[ $key ] ) ? 1 === absint( $settings[ $key ] ) : false;
+			if ( count( explode( '__', $key ) ) > 1 ) {
+				$keys    = explode( '__', $key );
+				$enabled = isset( $settings[ $keys[0] ][ $keys[1] ] ) ? 1 === absint( $settings[ $keys[0] ][ $keys[1] ] ) : false;
+			}
+			// Handle enable_image_captions
+			if ( 'enable_image_captions' === $key ) {
+				$enabled = isset( $settings['enable_image_captions']['alt'] ) && 'alt' === $settings['enable_image_captions']['alt'];
+			}
+			$enabled = $enabled && $is_configured;
+
+			$this->onboarding_options['features'][ $key ] = array(
+				'title'   => $title,
+				'enabled' => $enabled,
+			);
+		}
+
+		return $this->onboarding_options;
+	}
+
+	/**
+	 * Can the Provider be initialized?
+	 */
+	public function can_register() {
+		return $this->is_configured();
+	}
+
+	/**
+	 * Register the functionality for the Provider.
+	 */
+	abstract public function register();
+
+	/**
+	 * Resets the settings for this provider.
+	 */
+	abstract public function reset_settings();
+
+	/**
+	 * Initialization routine
+	 */
+	public function register_admin() {
+		add_action( 'admin_init', [ $this, 'register_settings' ] );
+		add_action( 'admin_init', [ $this, 'setup_fields_sections' ] );
+	}
+
+	/**
+	 * Register the settings and sanitization callback method.
+	 *
+	 * It's very important that the option group matches the page slug.
+	 */
+	public function register_settings() {
+		register_setting( $this->get_option_name(), $this->get_option_name(), [ $this, 'sanitize_settings' ] );
+	}
+
+	/**
+	 * Helper to get the settings and allow for settings default values.
+	 *
+	 * @param string|bool|mixed $index Optional. Name of the settings option index.
+	 *
+	 * @return string|array|mixed
+	 */
+	public function get_settings( $index = false ) {
+		$defaults = $this->get_default_settings();
+		$settings = get_option( $this->get_option_name(), [] );
+		$settings = wp_parse_args( $settings, $defaults );
+
+		if ( $index && isset( $settings[ $index ] ) ) {
+			return $settings[ $index ];
+		}
+
+		return $settings;
+	}
+
+	/**
+	 * Returns the default settings.
+	 *
+	 * @return array
+	 */
+	public function get_default_settings() {
+		$defaults = [];
+		$features = $this->get_features();
+		if ( empty( $features ) ) {
+			return $defaults;
+		}
+
+		foreach ( $features as $feature => $title ) {
+			$defaults = array_merge(
+				$defaults,
+				get_feature_default_settings( $feature )
+			);
+		}
+
+		return $defaults;
+	}
+
+	/**
+	 * Generic text input field callback
+	 *
+	 * @param array $args The args passed to add_settings_field.
+	 */
+	public function render_input( $args ) {
+		$option_index  = isset( $args['option_index'] ) ? $args['option_index'] : false;
+		$setting_index = $this->get_settings( $option_index );
+		$type          = $args['input_type'] ?? 'text';
+		$value         = ( isset( $setting_index[ $args['label_for'] ] ) ) ? $setting_index[ $args['label_for'] ] : '';
+
+		// Check for a default value
+		$value = ( empty( $value ) && isset( $args['default_value'] ) ) ? $args['default_value'] : $value;
+		$attrs = '';
+		$class = '';
+
+		switch ( $type ) {
+			case 'text':
+			case 'password':
+				$attrs = ' value="' . esc_attr( $value ) . '"';
+				$class = 'regular-text';
+				break;
+			case 'number':
+				$attrs = ' value="' . esc_attr( $value ) . '"';
+
+				if ( isset( $args['max'] ) && is_numeric( $args['max'] ) ) {
+					$attrs .= ' max="' . esc_attr( (float) $args['max'] ) . '"';
+				}
+
+				if ( isset( $args['min'] ) && is_numeric( $args['min'] ) ) {
+					$attrs .= ' min="' . esc_attr( (float) $args['min'] ) . '"';
+				}
+
+				if ( isset( $args['step'] ) && is_numeric( $args['step'] ) ) {
+					$attrs .= ' step="' . esc_attr( (float) $args['step'] ) . '"';
+				}
+
+				$class = 'small-text';
+				break;
+			case 'checkbox':
+				$attrs = ' value="1"' . checked( '1', $value, false );
+				break;
+		}
+		?>
+		<input
+			type="<?php echo esc_attr( $type ); ?>"
+			id="<?php echo esc_attr( $args['label_for'] ); ?>"
+			class="<?php echo esc_attr( $class ); ?>"
+			name="classifai_<?php echo esc_attr( $this->option_name ); ?><?php echo $option_index ? '[' . esc_attr( $option_index ) . ']' : ''; ?>[<?php echo esc_attr( $args['label_for'] ); ?>]"
+			<?php echo $attrs; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> />
+		<?php
+		if ( ! empty( $args['description'] ) ) {
+			echo '<span class="description">' . wp_kses_post( $args['description'] ) . '</span>';
+		}
+	}
+
+	/**
+	 * Generic prompt repeater field callback
+	 *
+	 * @since 2.4.0
+	 *
+	 * @param array $args The args passed to add_settings_field.
+	 */
+	public function render_prompt_repeater_field( array $args ): void {
+		$option_index      = $args['option_index'] ?? false;
+		$setting_index     = $this->get_settings( $option_index );
+		$prompts           = $setting_index[ $args['label_for'] ] ?? '';
+		$class             = $args['class'] ?? 'large-text';
+		$placeholder       = $args['placeholder'] ?? '';
+		$field_name_prefix = sprintf(
+			'classifai_%1$s%2$s[%3$s]',
+			$this->option_name,
+			$option_index ? "[$option_index]" : '',
+			$args['label_for']
+		);
+
+		$prompts = empty( $prompts ) && isset( $args['default_value'] ) ? $args['default_value'] : $prompts;
+
+		$prompt_count = count( $prompts );
+		$field_index  = 0;
+		?>
+
+		<?php foreach ( $prompts as $prompt ) : ?>
+			<?php
+			$is_default_prompt  = ( isset( $prompt['default'] ) && 1 === $prompt['default'] ) || 1 === $prompt_count;
+			$is_original_prompt = isset( $prompt['original'] ) && 1 === $prompt['original'];
+			?>
+
+			<fieldset class="classifai-field-type-prompt-setting">
+				<?php if ( $is_original_prompt ) : ?>
+					<p class="classifai-original-prompt">
+						<?php
+						printf(
+							/* translators: %1$s is replaced with <strong>; %2$s with </strong>; %3$s with prompt. */
+							esc_html__( '%1$sClassifAI default prompt%2$s: %3$s', 'classifai' ),
+							'<strong>',
+							'</strong>',
+							esc_html( $placeholder )
+						);
+						?>
+					</p>
+				<?php endif; ?>
+
+				<input type="hidden"
+					name="<?php echo esc_attr( $field_name_prefix . "[$field_index][default]" ); ?>"
+					value="<?php echo esc_attr( $prompt['default'] ?? '' ); ?>"
+					class="js-setting-field__default">
+				<input type="hidden"
+					name="<?php echo esc_attr( $field_name_prefix . "[$field_index][original]" ); ?>"
+					value="<?php echo esc_attr( $prompt['original'] ?? '' ); ?>">
+				<label>
+					<?php esc_html_e( 'Title', 'classifai' ); ?>&nbsp;*
+					<span class="dashicons dashicons-editor-help"
+						title="<?php esc_attr_e( 'Short description of prompt to use for identification', 'classifai' ); ?>"></span>
+					<input type="text"
+						name="<?php echo esc_attr( $field_name_prefix . "[$field_index][title]" ); ?>"
+						placeholder="<?php esc_attr_e( 'Prompt title', 'classifai' ); ?>"
+						value="<?php echo esc_attr( $prompt['title'] ?? '' ); ?>"
+						<?php echo $is_original_prompt ? 'readonly' : ''; ?>
+						required>
+				</label>
+
+				<label>
+					<?php esc_html_e( 'Prompt', 'classifai' ); ?>
+					<textarea
+						class="<?php echo esc_attr( $class ); ?>"
+						rows="4"
+						name="<?php echo esc_attr( $field_name_prefix . "[$field_index][prompt]" ); ?>"
+						placeholder="<?php echo esc_attr( $placeholder ); ?>"
+						<?php echo $is_original_prompt ? 'readonly' : ''; ?>
+					><?php echo esc_textarea( $prompt['prompt'] ?? '' ); ?></textarea>
+				</label>
+
+				<div class="actions-rows">
+					<a href="#" class="action__set_default <?php echo $is_default_prompt ? 'selected' : ''; ?>">
+						<?php if ( $is_default_prompt ) : ?>
+							<?php esc_html_e( 'Default prompt', 'classifai' ); ?>
+						<?php else : ?>
+							<?php esc_html_e( 'Set as default prompt', 'classifai' ); ?>
+						<?php endif; ?>
+					</a>
+					<a href="#" class="action__remove_prompt" style="<?php echo 1 === $prompt_count || $is_original_prompt ? 'display:none;' : ''; ?>">
+						<?php esc_html_e( 'Trash', 'classifai' ); ?>
+					</a>
+				</div>
+			</fieldset>
+			<?php ++$field_index; ?>
+		<?php endforeach; ?>
+
+		<button
+			class="button-secondary js-classifai-add-prompt-fieldset">
+			<?php esc_html_e( 'Add new prompt', 'classifai' ); ?>
+		</button>
+
+		<?php
+		if ( ! empty( $args['description'] ) ) {
+			echo '<br /><span class="description">' . wp_kses_post( $args['description'] ) . '</span>';
+		}
+	}
+
+	/**
+	 * Renders a select menu
+	 *
+	 * @param array $args The args passed to add_settings_field.
+	 */
+	public function render_select( $args ) {
+		$setting_index = $this->get_settings();
+		$saved         = ( isset( $setting_index[ $args['label_for'] ] ) ) ? $setting_index[ $args['label_for'] ] : '';
+
+		// Check for a default value
+		$saved   = ( empty( $saved ) && isset( $args['default_value'] ) ) ? $args['default_value'] : $saved;
+		$options = isset( $args['options'] ) ? $args['options'] : [];
+		?>
+
+		<select
+			id="<?php echo esc_attr( $args['label_for'] ); ?>"
+			name="classifai_<?php echo esc_attr( $this->option_name ); ?>[<?php echo esc_attr( $args['label_for'] ); ?>]"
+			>
+			<?php foreach ( $options as $value => $name ) : ?>
+				<option value="<?php echo esc_attr( $value ); ?>" <?php selected( $saved, $value ); ?>>
+					<?php echo esc_attr( $name ); ?>
+				</option>
+			<?php endforeach; ?>
+		</select>
+
+		<?php
+		if ( ! empty( $args['description'] ) ) {
+			echo '<br /><span class="description">' . wp_kses_post( $args['description'] ) . '</span>';
+		}
+	}
+
+	/**
+	 * Render a group of checkboxes.
+	 *
+	 * @param array $args The args passed to add_settings_field
+	 */
+	public function render_checkbox_group( array $args = array() ) {
+		$setting_index = $this->get_settings();
+		$options       = $args['options'] ?? [];
+		if ( ! is_array( $options ) ) {
+			return;
+		}
+
+		// Iterate through all of our options.
+		foreach ( $options as $option_value => $option_label ) {
+			$value                 = '';
+			$default_key           = array_search( $option_value, $args['default_values'], true );
+			$option_value_theshold = $option_value . '_threshold';
+
+			// Get saved value, if any.
+			if ( isset( $setting_index[ $args['label_for'] ] ) ) {
+				$value           = $setting_index[ $args['label_for'] ][ $option_value ] ?? '';
+				$threshold_value = $setting_index[ $args['label_for'] ][ $option_value_theshold ] ?? '';
+			}
+
+			// Check for backward compatibility.
+			if ( empty( $value ) && '0' !== $value && ! empty( $args['backward_compatible_key'] ) && isset( $setting_index[ $args['backward_compatible_key'] ] ) ) {
+				$value = $setting_index[ $args['backward_compatible_key'] ][ $option_value ] ?? '';
+			}
+
+			// If no saved value, check if we have a default value.
+			if ( empty( $value ) && '0' !== $value && isset( $args['default_values'][ $default_key ] ) ) {
+				$value = $args['default_values'][ $default_key ];
+			}
+
+			// Render checkbox.
+			printf(
+				'<p>
+					<label for="%1$s_%2$s_%3$s">
+						<input type="hidden" name="classifai_%1$s[%2$s][%3$s]" value="0" />
+						<input type="checkbox" id="%1$s_%2$s_%3$s" name="classifai_%1$s[%2$s][%3$s]" value="%3$s" %4$s />
+						%5$s
+					</label>
+				</p>',
+				esc_attr( $this->option_name ),
+				esc_attr( $args['label_for'] ?? '' ),
+				esc_attr( $option_value ),
+				checked( $value, $option_value, false ),
+				esc_html( $option_label )
+			);
+
+			// Render Threshold field.
+			if ( 'openai_embeddings' === $this->option_name && 'taxonomies' === $args['label_for'] ) {
+				$this->render_threshold_field( $args, $option_value_theshold, $threshold_value );
+			}
+		}
+
+		// Render description, if any.
+		if ( ! empty( $args['description'] ) ) {
+			printf(
+				'<span class="description">%s</span>',
+				esc_html( $args['description'] )
+			);
+		}
+	}
+
+	/**
+	 * Render a group of radio.
+	 *
+	 * @param array $args The args passed to add_settings_field
+	 */
+	public function render_radio_group( array $args = array() ) {
+		$setting_index = $this->get_settings();
+		$value         = $setting_index[ $args['label_for'] ] ?? '';
+		$options       = $args['options'] ?? [];
+		if ( ! is_array( $options ) ) {
+			return;
+		}
+
+		// Iterate through all of our options.
+		foreach ( $options as $option_value => $option_label ) {
+			// Render radio button.
+			printf(
+				'<p>
+					<label for="%1$s_%2$s_%3$s">
+						<input type="radio" id="%1$s_%2$s_%3$s" name="classifai_%1$s[%2$s]" value="%3$s" %4$s />
+						%5$s
+					</label>
+				</p>',
+				esc_attr( $this->option_name ),
+				esc_attr( $args['label_for'] ),
+				esc_attr( $option_value ),
+				checked( $value, $option_value, false ),
+				esc_html( $option_label )
+			);
+		}
+
+		// Render description, if any.
+		if ( ! empty( $args['description'] ) ) {
+			printf(
+				'<span class="description">%s</span>',
+				esc_html( $args['description'] )
+			);
+		}
+	}
+
+	/**
+	 * Render a threshold field.
+	 *
+	 * @since 2.5.0
+	 *
+	 * @param array  $args          The args passed to add_settings_field
+	 * @param string $option_value  The option value.
+	 * @param string $value         The value.
+	 *
+	 * @return void
+	 */
+	public function render_threshold_field( $args, $option_value, $value ) {
+		printf(
+			'<p class="threshold_wrapper">
+				<label for="%1$s_%2$s_%3$s">%4$s</label>
+				<br>
+				<input type="number" id="%1$s_%2$s_%3$s" class="small-text" name="classifai_%1$s[%2$s][%3$s]" value="%5$s" />
+			</p>',
+			esc_attr( $this->option_name ),
+			esc_attr( $args['label_for'] ?? '' ),
+			esc_attr( $option_value ),
+			esc_html__( 'Threshold (%)', 'classifai' ),
+			$value ? esc_attr( $value ) : 75
+		);
+	}
+
+	/**
+	 * Renders the checkbox group for 'Generate descriptive text' setting.
+	 *
+	 * @param array $args The args passed to add_settings_field.
+	 */
+	public function render_auto_caption_fields( $args ) {
+		$setting_index = $this->get_settings();
+
+		$default_value = '';
+
+		if ( isset( $setting_index['enable_image_captions'] ) ) {
+			if ( ! is_array( $setting_index['enable_image_captions'] ) ) {
+				if ( '1' === $setting_index['enable_image_captions'] ) {
+					$default_value = 'alt';
+				} elseif ( 'no' === $setting_index['enable_image_captions'] ) {
+					$default_value = '';
+				}
+			}
+		}
+
+		$checkbox_options = array(
+			'alt'         => esc_html__( 'Alt text', 'classifai' ),
+			'caption'     => esc_html__( 'Image caption', 'classifai' ),
+			'description' => esc_html__( 'Image description', 'classifai' ),
+		);
+
+		foreach ( $checkbox_options as $option_value => $option_label ) {
+			if ( isset( $setting_index['enable_image_captions'] ) ) {
+				if ( ! is_array( $setting_index['enable_image_captions'] ) ) {
+					$default_value = '1' === $setting_index['enable_image_captions'] ? 'alt' : '';
+				} else {
+					$default_value = $setting_index['enable_image_captions'][ $option_value ];
+				}
+			}
+
+			printf(
+				'<p>
+					<label for="%1$s_%2$s_%3$s">
+						<input type="hidden" name="classifai_%1$s[%2$s][%3$s]" value="0" />
+						<input type="checkbox" id="%1$s_%2$s_%3$s" name="classifai_%1$s[%2$s][%3$s]" value="%3$s" %4$s />
+						%5$s
+					</label>
+				</p>',
+				esc_attr( $this->option_name ),
+				esc_attr( $args['label_for'] ?? '' ),
+				esc_attr( $option_value ),
+				checked( $default_value, $option_value, false ),
+				esc_html( $option_label )
+			);
+		}
+
+		// Render description, if any.
+		if ( ! empty( $args['description'] ) ) {
+			printf(
+				'<span class="description">%s</span>',
+				esc_html( $args['description'] )
+			);
+		}
+	}
+
+	/**
+	 * Render allowed users table.
+	 *
+	 * @param array $args The args passed to add_settings_field
+	 */
+	public function render_allowed_users( array $args = array() ) {
+		$setting_index = $this->get_settings();
+		$value         = $setting_index[ $args['label_for'] ] ?? array();
+		?>
+		<div class="classifai-search-users-container">
+			<div class="classifai-user-selector" data-id="<?php echo esc_attr( $args['label_for'] ); ?>" id="<?php echo esc_attr( $args['label_for'] ); ?>-container"></div>
+			<input
+				id="<?php echo esc_attr( $args['label_for'] ); ?>"
+				class="classifai-search-users"
+				type="hidden"
+				name="classifai_<?php echo esc_attr( $this->option_name ); ?>[<?php echo esc_attr( $args['label_for'] ); ?>]"
+				value="<?php echo esc_attr( implode( ',', $value ) ); ?>"
+			/>
+		</div>
+		<?php
+		if ( ! empty( $args['description'] ) ) {
+			echo '<span class="description">' . wp_kses_post( $args['description'] ) . '</span>';
+		}
+	}
+
+	/**
+	 * Set up the fields for each section.
+	 */
+	abstract public function setup_fields_sections();
+
+	/**
+	 * Sanitization
+	 *
+	 * @param array $settings The settings being saved.
+	 */
+	abstract public function sanitize_settings( $settings );
+
+	/**
+	 * Provides debug information related to the provider.
+	 *
+	 * @return string|array Debug info to display on the Site Health screen. Accepts a string or key-value pairs.
+	 * @since 1.4.0
+	 */
+	abstract public function get_provider_debug_information();
+
+	/**
+	 * Common entry point for all REST endpoints for this provider.
+	 * This is called by the Service.
+	 *
+	 * @param int    $post_id       The Post Id we're processing.
+	 * @param string $route_to_call The name of the route we're going to be processing.
+	 * @param array  $args          Optional arguments to pass to the route.
+	 *
+	 * @return mixed
+	 */
+	public function rest_endpoint_callback( $post_id, $route_to_call, $args = [] ) {
+		return null;
+	}
+
+	/**
+	 * Format the result of most recent request.
+	 *
+	 * @param array|WP_Error $data Response data to format.
+	 *
+	 * @return string
+	 */
+	protected function get_formatted_latest_response( $data ) {
+		if ( ! $data ) {
+			return __( 'N/A', 'classifai' );
+		}
+
+		if ( is_wp_error( $data ) ) {
+			return $data->get_error_message();
+		}
+
+		return preg_replace( '/,"/', ', "', wp_json_encode( $data ) );
+	}
+
+	/**
+	 * Returns whether the provider is configured or not.
+	 *
+	 * @return bool
+	 */
+	public function is_configured() {
+		$settings = $this->get_settings();
+
+		$is_configured = false;
+		if ( ! empty( $settings ) && ! empty( $settings['authenticated'] ) ) {
+			$is_configured = true;
+		}
+
+		return $is_configured;
+	}
+
+	/**
+	 * Add settings fields for Role/User based access.
+	 *
+	 * @param string $feature Feature.
+	 * @param string $section Settings section.
+	 * @return void
+	 */
+	protected function add_access_settings( string $feature, string $section = '' ) {
+		$access_control = new AccessControl( $this, $feature );
+		$access_control->add_settings( $section );
+	}
+
+	/**
+	 * Sanitization for the roles/users access options being saved.
+	 *
+	 * @param array  $settings Array of settings about to be saved.
+	 * @param string $feature  Feature key.
+	 *
+	 * @return array The sanitized settings to be saved.
+	 */
+	protected function sanitize_access_settings( array $settings, string $feature ) {
+		$access_control = new AccessControl( $this, $feature );
+		return $access_control->sanitize_settings( $settings );
+	}
+
+	/**
+	 * Determine if the current user has access of the feature
+	 *
+	 * @param string $feature Feature to check.
+	 * @return bool
+	 */
+	protected function has_access( string $feature ) {
+		$access_control = new AccessControl( $this, $feature );
+		return $access_control->has_access();
+	}
+
+	/**
+	 * Retrieves the allowed WordPress roles for ClassifAI.
+	 *
+	 * @since 2.4.0
+	 *
+	 * @return array An associative array where the keys are role keys and the values are role names.
+	 */
+	public function get_roles() {
+		$default_settings = $this->get_default_settings();
+		$editable_roles   = get_editable_roles() ?? [];
+		$roles            = array_combine( array_keys( $editable_roles ), array_column( $editable_roles, 'name' ) );
+
+		/**
+		 * Filter the allowed WordPress roles for ClassifAI
+		 *
+		 * @since 2.4.0
+		 * @hook classifai_allowed_roles
+		 *
+		 * @param {array}  $roles            Array of arrays containing role information.
+		 * @param {string} $option_name      Option name.
+		 * @param {array}  $default_settings Default setting values.
+		 *
+		 * @return {array} Roles array.
+		 */
+		$roles = apply_filters( 'classifai_allowed_roles', $roles, $this->get_option_name(), $default_settings );
+
+		return $roles;
+	}
+
+	/**
+	 * Determine if the feature is enabled and current user can access the feature
+	 *
+	 * @param string $feature Feature to check.
+	 * @return bool
+	 */
+	public function is_feature_enabled( string $feature ) {
+		$is_feature_enabled = false;
+		$settings           = $this->get_settings();
+
+		// Check if provider is configured, user has access to the feature and the feature is turned on.
+		if (
+			$this->is_configured() &&
+			$this->has_access( $feature ) &&
+			$this->is_enabled( $feature )
+		) {
+			$is_feature_enabled = true;
+		}
+
+		/**
+		 * Filter to override permission to a specific classifai feature.
+		 *
+		 * @since 2.4.0
+		 * @hook classifai_{$this->option_name}_enable_{$feature}
+		 *
+		 * @param {bool}  $is_feature_enabled Is the feature enabled?
+		 * @param {array} $settings           Current feature settings.
+		 *
+		 * @return {bool} Returns true if the user has access and the feature is enabled, false otherwise.
+		 */
+		return apply_filters( "classifai_{$this->option_name}_enable_{$feature}", $is_feature_enabled, $settings );
+	}
+
+	/**
+	 * Determine if the feature is turned on.
+	 * Note: This function does not check if the user has access to the feature.
+	 *
+	 * - Use `is_feature_enabled()` to check if the user has access to the feature and feature is turned on.
+	 * - Use `has_access()` to check if the user has access to the feature.
+	 *
+	 * @param string $feature Feature to check.
+	 * @return bool
+	 */
+	public function is_enabled( string $feature ) {
+		$settings   = $this->get_settings();
+		$enable_key = 'enable_' . $feature;
+
+		// Check if feature is turned on.
+		$is_enabled = ( isset( $settings[ $enable_key ] ) && 1 === (int) $settings[ $enable_key ] );
+
+		/**
+		 * Filter to override a specific classifai feature enabled.
+		 *
+		 * @since 2.5.0
+		 * @hook classifai_is_{$feature}_enabled
+		 *
+		 * @param {bool}  $is_enabled Is the feature enabled?
+		 * @param {array} $settings   Current feature settings.
+		 *
+		 * @return {bool} Returns true if the feature is enabled, false otherwise.
+		 */
+		return apply_filters( "classifai_is_{$feature}_enabled", $is_enabled, $settings );
+	}
+}
+
+
+
+ + + + + + + + +
+ + + +
+ + + + + diff --git a/Services_Service.php.html b/Services_Service.php.html index edb5dd588..0556afee5 100644 --- a/Services_Service.php.html +++ b/Services_Service.php.html @@ -184,8 +184,17 @@

Source: Services/Service.php

<?php // Find the right provider class. $provider = find_provider_class( $this->provider_classes ?? [], 'Natural Language Understanding' ); - - if ( ! is_wp_error( $provider ) && ! empty( $provider->can_register() ) ) : + if ( 'openai_embeddings' === $active_tab ) { + $provider = find_provider_class( $this->provider_classes ?? [], 'Embeddings' ); + } + + if ( + ! is_wp_error( $provider ) + && ! empty( + $provider->can_register() + && ( $provider->is_feature_enabled( 'content_classification' ) || $provider->is_feature_enabled( 'classification' ) ) + ) + ) : ?> <div id="classifai-post-preview-app"> <?php @@ -224,7 +233,7 @@

Source: Services/Service.php

); ?> - <?php if ( 'watson_nlu' === $active_tab ) : ?> + <?php if ( 'watson_nlu' === $active_tab || 'openai_embeddings' === $active_tab ) : ?> <h2><?php esc_html_e( 'Preview Language Processing', 'classifai' ); ?></h2> <div id="classifai-post-preview-controls"> <select id="classifai-preview-post-selector"> @@ -232,18 +241,24 @@

Source: Services/Service.php

<option value="<?php echo esc_attr( $post->ID ); ?>"><?php echo esc_html( $post->post_title ); ?></option> <?php endforeach; ?> </select> - <?php wp_nonce_field( 'classifai-previewer-action', 'classifai-previewer-nonce' ); ?> + <?php wp_nonce_field( "classifai-previewer-$active_tab-action", "classifai-previewer-$active_tab-nonce" ); ?> <button type="button" class="button" id="get-classifier-preview-data-btn"> <span><?php esc_html_e( 'Preview', 'classifai' ); ?></span> </button> </div> <div id="classifai-post-preview-wrapper"> - <?php foreach ( $features as $feature_slug => $feature ) : ?> - <div class="tax-row tax-row--<?php echo esc_attr( $feature['plural'] ); ?> <?php echo esc_attr( $feature['enabled'] ) ? '' : 'tax-row--hide'; ?>"> - <div class="tax-type"><?php echo esc_html( $feature['name'] ); ?></div> - </div> - <?php endforeach; ?> - </div> + <?php + if ( 'watson_nlu' === $active_tab ) : + foreach ( $features as $feature_slug => $feature ) : + ?> + <div class="tax-row tax-row--<?php echo esc_attr( $feature['plural'] ); ?> <?php echo esc_attr( $feature['enabled'] ) ? '' : 'tax-row--hide'; ?>"> + <div class="tax-type"><?php echo esc_html( $feature['name'] ); ?></div> + </div> + <?php + endforeach; + endif; + ?> + </div> <?php endif; ?> </div> <?php endif; ?> @@ -322,7 +337,7 @@

Source: Services/Service.php


diff --git a/Watson_Classifier.php.html b/Watson_Classifier.php.html index 4b11c287e..b796b08b9 100644 --- a/Watson_Classifier.php.html +++ b/Watson_Classifier.php.html @@ -177,7 +177,7 @@

Source: Watson/Classifier.php


diff --git a/Watson_Normalizer.php.html b/Watson_Normalizer.php.html index e14bb3466..3c9a562fa 100644 --- a/Watson_Normalizer.php.html +++ b/Watson_Normalizer.php.html @@ -127,7 +127,7 @@

Source: Watson/Normalizer.php


diff --git a/after_classifai_init.html b/after_classifai_init.html index b62f8a834..e8b45bb06 100644 --- a/after_classifai_init.html +++ b/after_classifai_init.html @@ -95,7 +95,7 @@

do_action( 'after_classifai_init' )Source:
@@ -143,7 +143,7 @@

do_action( 'after_classifai_init' )
diff --git a/before_classifai_init.html b/before_classifai_init.html index 0d246fe20..4c4992394 100644 --- a/before_classifai_init.html +++ b/before_classifai_init.html @@ -143,7 +143,7 @@

do_action( 'before_classifai_init' )
diff --git a/classifai_%7B$this-_option_name%7D_enable_%7B$feature%7D.html b/classifai_%7B$this-_option_name%7D_enable_%7B$feature%7D.html new file mode 100644 index 000000000..d69e8839d --- /dev/null +++ b/classifai_%7B$this-_option_name%7D_enable_%7B$feature%7D.html @@ -0,0 +1,248 @@ + + + + + Filter: classifai_{$this->option_name}_enable_{$feature} - 10up ClassifAI Hook Docs + + + + + + + + + + + + + +
+ + +

Filter: classifai_{$this->option_name}_enable_{$feature}

+ + + + + + + +
+ +
+

+ +
+ +
+
+ + + + + +

apply_filters( 'classifai_{$this->option_name}_enable_{$feature}', $is_feature_enabled, $settings ) → {bool}

+ + + + + +
+

Filter to override permission to a specific classifai feature.

+
+ + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
$is_feature_enabled + + +bool + + + +

Is the feature enabled?

$settings + + +array + + + +

Current feature settings.

+ + + + + + +
+ + + + +
Since:
+
  • 2.4.0
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + +
+

Returns true if the user has access and the feature is enabled, false otherwise.

+
+ + + +
+
+ Type +
+
+ +bool + + +
+
+ + + + + + +
+ + +
+ +
+ + + + + + + + +
+ + + +
+ + + + + \ No newline at end of file diff --git a/classifai_all_post_statuses.html b/classifai_all_post_statuses.html index 7690d0fba..459a17da3 100644 --- a/classifai_all_post_statuses.html +++ b/classifai_all_post_statuses.html @@ -144,7 +144,7 @@

Parameters:
Source:
@@ -214,7 +214,7 @@
Returns:

diff --git a/classifai_allowed_roles.html b/classifai_allowed_roles.html new file mode 100644 index 000000000..f5a41aabc --- /dev/null +++ b/classifai_allowed_roles.html @@ -0,0 +1,271 @@ + + + + + Filter: classifai_allowed_roles - 10up ClassifAI Hook Docs + + + + + + + + + + + + + +
+ + +

Filter: classifai_allowed_roles

+ + + + + + + +
+ +
+

+ +
+ +
+
+ + + + + +

apply_filters( 'classifai_allowed_roles', $roles, $option_name, $default_settings ) → {array}

+ + + + + +
+

Filter the allowed WordPress roles for ClassifAI

+
+ + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
$roles + + +array + + + +

Array of arrays containing role information.

$option_name + + +string + + + +

Option name.

$default_settings + + +array + + + +

Default setting values.

+ + + + + + +
+ + + + +
Since:
+
  • 2.4.0
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + +
+

Roles array.

+
+ + + +
+
+ Type +
+
+ +array + + +
+
+ + + + + + +
+ + +
+ +
+ + + + + + + + +
+ + + +
+ + + + + \ No newline at end of file diff --git a/classifai_audio_generation_initial_state.html b/classifai_audio_generation_initial_state.html index 2b2aa94e6..9308b1a05 100644 --- a/classifai_audio_generation_initial_state.html +++ b/classifai_audio_generation_initial_state.html @@ -167,7 +167,7 @@
Parameters:
Source:
@@ -237,7 +237,7 @@
Returns:

diff --git a/classifai_audio_generation_subsequent_state.html b/classifai_audio_generation_subsequent_state.html index f5becd9b5..dad364019 100644 --- a/classifai_audio_generation_subsequent_state.html +++ b/classifai_audio_generation_subsequent_state.html @@ -167,7 +167,7 @@
Parameters:
Source:
@@ -237,7 +237,7 @@
Returns:

diff --git a/classifai_azure_read_after_request.html b/classifai_azure_read_after_request.html index 55e52d022..b223c5624 100644 --- a/classifai_azure_read_after_request.html +++ b/classifai_azure_read_after_request.html @@ -264,7 +264,7 @@
Parameters:

diff --git a/classifai_azure_read_request_args.html b/classifai_azure_read_request_args.html index 3d7387ddc..fc548675d 100644 --- a/classifai_azure_read_request_args.html +++ b/classifai_azure_read_request_args.html @@ -237,7 +237,7 @@
Returns:

diff --git a/classifai_azure_read_result_max_page.html b/classifai_azure_read_result_max_page.html index 7223bae9b..8e8e5f17a 100644 --- a/classifai_azure_read_result_max_page.html +++ b/classifai_azure_read_result_max_page.html @@ -214,7 +214,7 @@
Returns:

diff --git a/classifai_azure_read_retry_interval.html b/classifai_azure_read_retry_interval.html index b8dd7c33a..547c829fe 100644 --- a/classifai_azure_read_retry_interval.html +++ b/classifai_azure_read_retry_interval.html @@ -214,7 +214,7 @@
Returns:

diff --git a/classifai_azure_read_should_process.html b/classifai_azure_read_should_process.html index fad43cb77..2251caf39 100644 --- a/classifai_azure_read_should_process.html +++ b/classifai_azure_read_should_process.html @@ -237,7 +237,7 @@
Returns:

diff --git a/classifai_azure_read_text_result.html b/classifai_azure_read_text_result.html index 7b9a1ded0..116ad9bef 100644 --- a/classifai_azure_read_text_result.html +++ b/classifai_azure_read_text_result.html @@ -260,7 +260,7 @@
Returns:

diff --git a/classifai_chatgpt_allowed_roles.html b/classifai_chatgpt_allowed_roles.html index 8ee93a123..b682c1043 100644 --- a/classifai_chatgpt_allowed_roles.html +++ b/classifai_chatgpt_allowed_roles.html @@ -167,7 +167,7 @@
Parameters:
Source:
@@ -237,7 +237,7 @@
Returns:

diff --git a/classifai_chatgpt_content.html b/classifai_chatgpt_content.html index 793179fda..72fb985ff 100644 --- a/classifai_chatgpt_content.html +++ b/classifai_chatgpt_content.html @@ -167,7 +167,7 @@
Parameters:
Source:
@@ -237,7 +237,7 @@
Returns:

diff --git a/classifai_chatgpt_excerpt_prompt.html b/classifai_chatgpt_excerpt_prompt.html index 82ff55263..db43f5fec 100644 --- a/classifai_chatgpt_excerpt_prompt.html +++ b/classifai_chatgpt_excerpt_prompt.html @@ -190,7 +190,7 @@
Parameters:
Source:
@@ -260,7 +260,7 @@
Returns:

diff --git a/classifai_chatgpt_excerpt_request_body.html b/classifai_chatgpt_excerpt_request_body.html index 5028f681c..33302bd8d 100644 --- a/classifai_chatgpt_excerpt_request_body.html +++ b/classifai_chatgpt_excerpt_request_body.html @@ -167,7 +167,7 @@
Parameters:
Source:
@@ -237,7 +237,7 @@
Returns:

diff --git a/classifai_chatgpt_resize_content_request_body.html b/classifai_chatgpt_resize_content_request_body.html index 74ef5034b..7f2855914 100644 --- a/classifai_chatgpt_resize_content_request_body.html +++ b/classifai_chatgpt_resize_content_request_body.html @@ -167,7 +167,7 @@
Parameters:
Source:
@@ -237,7 +237,7 @@
Returns:

diff --git a/classifai_chatgpt_title_prompt.html b/classifai_chatgpt_title_prompt.html index 882de0f26..43ef96d62 100644 --- a/classifai_chatgpt_title_prompt.html +++ b/classifai_chatgpt_title_prompt.html @@ -190,7 +190,7 @@
Parameters:
Source:
@@ -260,7 +260,7 @@
Returns:

diff --git a/classifai_chatgpt_title_request_body.html b/classifai_chatgpt_title_request_body.html index c9ee4643e..11df16b57 100644 --- a/classifai_chatgpt_title_request_body.html +++ b/classifai_chatgpt_title_request_body.html @@ -167,7 +167,7 @@
Parameters:
Source:
@@ -237,7 +237,7 @@
Returns:

diff --git a/classifai_classified_data.html b/classifai_classified_data.html index e2710a55d..927ba7838 100644 --- a/classifai_classified_data.html +++ b/classifai_classified_data.html @@ -214,7 +214,7 @@
Returns:

diff --git a/classifai_computer_vision_caption_failed.html b/classifai_computer_vision_caption_failed.html index 5c8e56b4c..b37fbee0e 100644 --- a/classifai_computer_vision_caption_failed.html +++ b/classifai_computer_vision_caption_failed.html @@ -167,7 +167,7 @@
Parameters:
Source:
@@ -215,7 +215,7 @@
Parameters:

diff --git a/classifai_computer_vision_captions.html b/classifai_computer_vision_captions.html index f5b0760de..e8010ba6b 100644 --- a/classifai_computer_vision_captions.html +++ b/classifai_computer_vision_captions.html @@ -144,7 +144,7 @@
Parameters:
Source:
@@ -214,7 +214,7 @@
Returns:

diff --git a/classifai_computer_vision_image_tag_failed.html b/classifai_computer_vision_image_tag_failed.html index ae12941e7..f004c1707 100644 --- a/classifai_computer_vision_image_tag_failed.html +++ b/classifai_computer_vision_image_tag_failed.html @@ -167,7 +167,7 @@
Parameters:
Source:
@@ -215,7 +215,7 @@
Parameters:

diff --git a/classifai_computer_vision_image_tags.html b/classifai_computer_vision_image_tags.html index 567edcc9b..78ddc00a3 100644 --- a/classifai_computer_vision_image_tags.html +++ b/classifai_computer_vision_image_tags.html @@ -144,7 +144,7 @@
Parameters:
Source:
@@ -214,7 +214,7 @@
Returns:

diff --git a/classifai_computer_vision_max_filesize.html b/classifai_computer_vision_max_filesize.html index 91188249b..e19bd0b58 100644 --- a/classifai_computer_vision_max_filesize.html +++ b/classifai_computer_vision_max_filesize.html @@ -144,7 +144,7 @@
Parameters:
Source:
@@ -214,7 +214,7 @@
Returns:

diff --git a/classifai_dalle_caption.html b/classifai_dalle_caption.html index a33c78b94..128e5e162 100644 --- a/classifai_dalle_caption.html +++ b/classifai_dalle_caption.html @@ -144,7 +144,7 @@
Parameters:
Source:
@@ -214,7 +214,7 @@
Returns:

diff --git a/classifai_dalle_prompt.html b/classifai_dalle_prompt.html index 8e54c51bc..16d30e9f2 100644 --- a/classifai_dalle_prompt.html +++ b/classifai_dalle_prompt.html @@ -144,7 +144,7 @@
Parameters:
Source:
@@ -214,7 +214,7 @@
Returns:

diff --git a/classifai_dalle_request_body.html b/classifai_dalle_request_body.html index 1fd95ddcf..7128c7ed1 100644 --- a/classifai_dalle_request_body.html +++ b/classifai_dalle_request_body.html @@ -144,7 +144,7 @@
Parameters:
Source:
@@ -214,7 +214,7 @@
Returns:

diff --git a/classifai_debug_information.html b/classifai_debug_information.html index 0972c0c66..d4ea268f8 100644 --- a/classifai_debug_information.html +++ b/classifai_debug_information.html @@ -222,7 +222,7 @@
Returns:

diff --git a/classifai_disable_post_to_audio_block.html b/classifai_disable_post_to_audio_block.html index 6e6367070..260e261e1 100644 --- a/classifai_disable_post_to_audio_block.html +++ b/classifai_disable_post_to_audio_block.html @@ -167,7 +167,7 @@
Parameters:
Source:
@@ -237,7 +237,7 @@
Returns:

diff --git a/classifai_feature_threshold.html b/classifai_feature_threshold.html index c69eaf917..3b9c48c8f 100644 --- a/classifai_feature_threshold.html +++ b/classifai_feature_threshold.html @@ -168,7 +168,7 @@
Parameters:
Source:
@@ -238,7 +238,7 @@
Returns:

diff --git a/classifai_generate_image_alt_tags_source_url.html b/classifai_generate_image_alt_tags_source_url.html index 7930d4bbf..2933e9eb6 100644 --- a/classifai_generate_image_alt_tags_source_url.html +++ b/classifai_generate_image_alt_tags_source_url.html @@ -170,7 +170,7 @@
Parameters:
Source:
@@ -240,7 +240,7 @@
Returns:

diff --git a/classifai_has_access.html b/classifai_has_access.html new file mode 100644 index 000000000..aff21bc12 --- /dev/null +++ b/classifai_has_access.html @@ -0,0 +1,294 @@ + + + + + Filter: classifai_has_access - 10up ClassifAI Hook Docs + + + + + + + + + + + + + +
+ + +

Filter: classifai_has_access

+ + + + + + + +
+ +
+

+ +
+ +
+
+ + + + + +

apply_filters( 'classifai_has_access', $access, $feature, $user_id, $settings ) → {bool}

+ + + + + +
+

Filter to override user access to a ClassifAI feature.

+
+ + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
$access + + +bool + + + +

Current access value.

$feature + + +string + + + +

Feature name.

$user_id + + +int + + + +

User ID.

$settings + + +array + + + +

Feature settings.

+ + + + + + +
+ + + + +
Since:
+
  • 2.4.0
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + +
+

Should the user have access?

+
+ + + +
+
+ Type +
+
+ +bool + + +
+
+ + + + + + +
+ + +
+ +
+ + + + + + + + +
+ + + +
+ + + + + \ No newline at end of file diff --git a/classifai_is_%7B$feature%7D_enabled.html b/classifai_is_%7B$feature%7D_enabled.html new file mode 100644 index 000000000..1ded301a5 --- /dev/null +++ b/classifai_is_%7B$feature%7D_enabled.html @@ -0,0 +1,248 @@ + + + + + Filter: classifai_is_{$feature}_enabled - 10up ClassifAI Hook Docs + + + + + + + + + + + + + +
+ + +

Filter: classifai_is_{$feature}_enabled

+ + + + + + + +
+ +
+

+ +
+ +
+
+ + + + + +

apply_filters( 'classifai_is_{$feature}_enabled', $is_enabled, $settings ) → {bool}

+ + + + + +
+

Filter to override a specific classifai feature enabled.

+
+ + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
$is_enabled + + +bool + + + +

Is the feature enabled?

$settings + + +array + + + +

Current feature settings.

+ + + + + + +
+ + + + +
Since:
+
  • 2.5.0
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + +
+

Returns true if the feature is enabled, false otherwise.

+
+ + + +
+
+ Type +
+
+ +bool + + +
+
+ + + + + + +
+ + +
+ +
+ + + + + + + + +
+ + + +
+ + + + + \ No newline at end of file diff --git a/classifai_language_settings_post_statuses.html b/classifai_language_settings_post_statuses.html index bde8b9d54..503548cc8 100644 --- a/classifai_language_settings_post_statuses.html +++ b/classifai_language_settings_post_statuses.html @@ -144,7 +144,7 @@
Parameters:
Source:
@@ -214,7 +214,7 @@
Returns:

diff --git a/classifai_language_settings_post_types.html b/classifai_language_settings_post_types.html index e1421939b..298932c36 100644 --- a/classifai_language_settings_post_types.html +++ b/classifai_language_settings_post_types.html @@ -144,7 +144,7 @@
Parameters:
Source:
@@ -214,7 +214,7 @@
Returns:

diff --git a/classifai_listen_to_this_post_text.html b/classifai_listen_to_this_post_text.html index b5ecb4670..2f7f8a3da 100644 --- a/classifai_listen_to_this_post_text.html +++ b/classifai_listen_to_this_post_text.html @@ -167,7 +167,7 @@
Parameters:
Source:
@@ -237,7 +237,7 @@
Returns:

diff --git a/classifai_normalize.html b/classifai_normalize.html index fb236e1c7..677993394 100644 --- a/classifai_normalize.html +++ b/classifai_normalize.html @@ -237,7 +237,7 @@
Returns:

diff --git a/classifai_ocr_after_request.html b/classifai_ocr_after_request.html index 72df42719..cf6a721eb 100644 --- a/classifai_ocr_after_request.html +++ b/classifai_ocr_after_request.html @@ -218,7 +218,7 @@
Parameters:

diff --git a/classifai_ocr_approved_media_types.html b/classifai_ocr_approved_media_types.html index c15ba9200..26c7c92db 100644 --- a/classifai_ocr_approved_media_types.html +++ b/classifai_ocr_approved_media_types.html @@ -237,7 +237,7 @@
Returns:

diff --git a/classifai_ocr_should_process.html b/classifai_ocr_should_process.html index f0e943343..65f9f7f69 100644 --- a/classifai_ocr_should_process.html +++ b/classifai_ocr_should_process.html @@ -263,7 +263,7 @@
Returns:

diff --git a/classifai_ocr_tag_confidence.html b/classifai_ocr_tag_confidence.html index ec3da458d..3ecc26631 100644 --- a/classifai_ocr_tag_confidence.html +++ b/classifai_ocr_tag_confidence.html @@ -263,7 +263,7 @@
Returns:

diff --git a/classifai_ocr_tags.html b/classifai_ocr_tags.html index 88c5ef3c5..0b5491b53 100644 --- a/classifai_ocr_tags.html +++ b/classifai_ocr_tags.html @@ -263,7 +263,7 @@
Returns:

diff --git a/classifai_ocr_text.html b/classifai_ocr_text.html index 87fb92214..d6f125077 100644 --- a/classifai_ocr_text.html +++ b/classifai_ocr_text.html @@ -237,7 +237,7 @@
Returns:

diff --git a/classifai_ocr_text_post_args.html b/classifai_ocr_text_post_args.html index 37b4013d2..df8edefce 100644 --- a/classifai_ocr_text_post_args.html +++ b/classifai_ocr_text_post_args.html @@ -308,7 +308,7 @@
Returns:

diff --git a/classifai_ocr_unsuccessful_response.html b/classifai_ocr_unsuccessful_response.html index b8d5c02d4..b71754f87 100644 --- a/classifai_ocr_unsuccessful_response.html +++ b/classifai_ocr_unsuccessful_response.html @@ -218,7 +218,7 @@
Parameters:

diff --git a/classifai_openai_api_request_get_options.html b/classifai_openai_api_request_get_options.html index a1b568c3b..88e89117b 100644 --- a/classifai_openai_api_request_get_options.html +++ b/classifai_openai_api_request_get_options.html @@ -260,7 +260,7 @@
Returns:

diff --git a/classifai_openai_api_request_get_url.html b/classifai_openai_api_request_get_url.html index 58b30e30d..b64137c22 100644 --- a/classifai_openai_api_request_get_url.html +++ b/classifai_openai_api_request_get_url.html @@ -260,7 +260,7 @@
Returns:

diff --git a/classifai_openai_api_request_post_form_options.html b/classifai_openai_api_request_post_form_options.html index b0b1dbd1b..05b6bdd79 100644 --- a/classifai_openai_api_request_post_form_options.html +++ b/classifai_openai_api_request_post_form_options.html @@ -283,7 +283,7 @@
Returns:

diff --git a/classifai_openai_api_request_post_form_url.html b/classifai_openai_api_request_post_form_url.html index 1ca8f0777..714e73a24 100644 --- a/classifai_openai_api_request_post_form_url.html +++ b/classifai_openai_api_request_post_form_url.html @@ -237,7 +237,7 @@
Returns:

diff --git a/classifai_openai_api_request_post_options.html b/classifai_openai_api_request_post_options.html index 29fe65376..5790c2472 100644 --- a/classifai_openai_api_request_post_options.html +++ b/classifai_openai_api_request_post_options.html @@ -260,7 +260,7 @@
Returns:

diff --git a/classifai_openai_api_request_post_url.html b/classifai_openai_api_request_post_url.html index ff16c0a36..eaaa8f778 100644 --- a/classifai_openai_api_request_post_url.html +++ b/classifai_openai_api_request_post_url.html @@ -260,7 +260,7 @@
Returns:

diff --git a/classifai_openai_api_response_get.html b/classifai_openai_api_response_get.html index 69e7e9f21..87ccc2142 100644 --- a/classifai_openai_api_response_get.html +++ b/classifai_openai_api_response_get.html @@ -260,7 +260,7 @@
Returns:

diff --git a/classifai_openai_api_response_post.html b/classifai_openai_api_response_post.html index 83abefca1..71c596afd 100644 --- a/classifai_openai_api_response_post.html +++ b/classifai_openai_api_response_post.html @@ -260,7 +260,7 @@
Returns:

diff --git a/classifai_openai_api_response_post_form.html b/classifai_openai_api_response_post_form.html index 0f2683ae5..033f14db4 100644 --- a/classifai_openai_api_response_post_form.html +++ b/classifai_openai_api_response_post_form.html @@ -260,7 +260,7 @@
Returns:

diff --git a/classifai_openai_characters_in_token.html b/classifai_openai_characters_in_token.html index 3248288a9..0e1ac8304 100644 --- a/classifai_openai_characters_in_token.html +++ b/classifai_openai_characters_in_token.html @@ -233,7 +233,7 @@
Returns:

diff --git a/classifai_openai_chatgpt_%7B$feature%7D.html b/classifai_openai_chatgpt_%7B$feature%7D.html deleted file mode 100644 index fd41c8505..000000000 --- a/classifai_openai_chatgpt_%7B$feature%7D.html +++ /dev/null @@ -1,248 +0,0 @@ - - - - - Filter: classifai_openai_chatgpt_{$feature} - 10up ClassifAI Hook Docs - - - - - - - - - - - - - -
- - -

Filter: classifai_openai_chatgpt_{$feature}

- - - - - - - -
- -
-

- -
- -
-
- - - - - -

apply_filters( 'classifai_openai_chatgpt_{$feature}', $access, $settings ) → {bool}

- - - - - -
-

Filter to override permission to a ChatGPT generate feature.

-
- - - - - - - -
Parameters:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescription
$access - - -bool - - - -

Current access value.

$settings - - -array - - - -

Current feature settings.

- - - - - - -
- - - - -
Since:
-
  • 2.3.0
- - - - - - - - - - - - - - - - - - - - - - - -
Source:
-
- - - - - - - -
- - - - - - - - - - - - - -
Returns:
- - -
-

Should the user have access?

-
- - - -
-
- Type -
-
- -bool - - -
-
- - - - - - -
- - -
- -
- - - - - - - - -
- - - -
- - - - - \ No newline at end of file diff --git a/classifai_openai_dalle_allowed_image_roles.html b/classifai_openai_dalle_allowed_image_roles.html index 9efbfe53e..230dcb874 100644 --- a/classifai_openai_dalle_allowed_image_roles.html +++ b/classifai_openai_dalle_allowed_image_roles.html @@ -167,7 +167,7 @@
Parameters:
Source:
@@ -237,7 +237,7 @@
Returns:

diff --git a/classifai_openai_dalle_enable_image_gen.html b/classifai_openai_dalle_enable_image_gen.html index 3ef0fa0be..1f0ed5d64 100644 --- a/classifai_openai_dalle_enable_image_gen.html +++ b/classifai_openai_dalle_enable_image_gen.html @@ -167,7 +167,7 @@
Parameters:
Source:
@@ -237,7 +237,7 @@
Returns:

diff --git a/classifai_openai_embeddings_content.html b/classifai_openai_embeddings_content.html index e25900169..fb12f435e 100644 --- a/classifai_openai_embeddings_content.html +++ b/classifai_openai_embeddings_content.html @@ -190,7 +190,7 @@
Parameters:
Source:
@@ -260,7 +260,7 @@
Returns:

diff --git a/classifai_openai_embeddings_post_statuses.html b/classifai_openai_embeddings_post_statuses.html index eec533b15..f30f05f80 100644 --- a/classifai_openai_embeddings_post_statuses.html +++ b/classifai_openai_embeddings_post_statuses.html @@ -144,7 +144,7 @@
Parameters:
Source:
@@ -214,7 +214,7 @@
Returns:

diff --git a/classifai_openai_embeddings_request_body.html b/classifai_openai_embeddings_request_body.html index fdf42054c..1af3dee61 100644 --- a/classifai_openai_embeddings_request_body.html +++ b/classifai_openai_embeddings_request_body.html @@ -190,7 +190,7 @@
Parameters:
Source:
@@ -260,7 +260,7 @@
Returns:

diff --git a/classifai_openai_embeddings_should_classify.html b/classifai_openai_embeddings_should_classify.html index e828c2b87..2e233435c 100644 --- a/classifai_openai_embeddings_should_classify.html +++ b/classifai_openai_embeddings_should_classify.html @@ -191,7 +191,7 @@
Parameters:
Source:
@@ -261,7 +261,7 @@
Returns:

diff --git a/classifai_openai_embeddings_taxonomies.html b/classifai_openai_embeddings_taxonomies.html index 69d7e97c7..11035d153 100644 --- a/classifai_openai_embeddings_taxonomies.html +++ b/classifai_openai_embeddings_taxonomies.html @@ -144,7 +144,7 @@
Parameters:
Source:
@@ -214,7 +214,7 @@
Returns:

diff --git a/classifai_openai_settings_post_statuses.html b/classifai_openai_settings_post_statuses.html index a269bca7c..070c1985a 100644 --- a/classifai_openai_settings_post_statuses.html +++ b/classifai_openai_settings_post_statuses.html @@ -237,7 +237,7 @@
Returns:

diff --git a/classifai_openai_settings_post_types.html b/classifai_openai_settings_post_types.html index c4aed0250..891eb1e0b 100644 --- a/classifai_openai_settings_post_types.html +++ b/classifai_openai_settings_post_types.html @@ -237,7 +237,7 @@
Returns:

diff --git a/classifai_openai_settings_taxonomies.html b/classifai_openai_settings_taxonomies.html index 969191f7b..3373f0134 100644 --- a/classifai_openai_settings_taxonomies.html +++ b/classifai_openai_settings_taxonomies.html @@ -237,7 +237,7 @@
Returns:

diff --git a/classifai_openai_tokens_per_word.html b/classifai_openai_tokens_per_word.html index 311ebbe3a..b0f5ffaac 100644 --- a/classifai_openai_tokens_per_word.html +++ b/classifai_openai_tokens_per_word.html @@ -233,7 +233,7 @@
Returns:

diff --git a/classifai_post_statuses.html b/classifai_post_statuses.html index 104891d33..d986eabd5 100644 --- a/classifai_post_statuses.html +++ b/classifai_post_statuses.html @@ -144,7 +144,7 @@
Parameters:
Source:
@@ -214,7 +214,7 @@
Returns:

diff --git a/classifai_post_statuses_for_post_type_or_id.html b/classifai_post_statuses_for_post_type_or_id.html index cd1ef1621..9fa0d9d1e 100644 --- a/classifai_post_statuses_for_post_type_or_id.html +++ b/classifai_post_statuses_for_post_type_or_id.html @@ -190,7 +190,7 @@
Parameters:
Source:
@@ -260,7 +260,7 @@
Returns:

diff --git a/classifai_post_types.html b/classifai_post_types.html index ae65c08bc..f44aeb6cc 100644 --- a/classifai_post_types.html +++ b/classifai_post_types.html @@ -144,7 +144,7 @@
Parameters:
Source:
@@ -321,7 +321,7 @@
Parameters:
Source:
@@ -391,7 +391,7 @@
Returns:

diff --git a/classifai_pre_render_post_audio_controls.html b/classifai_pre_render_post_audio_controls.html index c60148825..191507116 100644 --- a/classifai_pre_render_post_audio_controls.html +++ b/classifai_pre_render_post_audio_controls.html @@ -245,7 +245,7 @@
Parameters:
Source:
@@ -318,7 +318,7 @@
Returns:

diff --git a/classifai_recommended_block_attributes.html b/classifai_recommended_block_attributes.html index 2e6ac683f..4e407d58a 100644 --- a/classifai_recommended_block_attributes.html +++ b/classifai_recommended_block_attributes.html @@ -144,7 +144,7 @@
Parameters:
Source:
@@ -214,7 +214,7 @@
Returns:

diff --git a/classifai_recommended_block_markup.html b/classifai_recommended_block_markup.html index 00237ddbd..b950e967d 100644 --- a/classifai_recommended_block_markup.html +++ b/classifai_recommended_block_markup.html @@ -213,7 +213,7 @@
Parameters:
Source:
@@ -283,7 +283,7 @@
Returns:

diff --git a/classifai_recommended_content_post_args.html b/classifai_recommended_content_post_args.html index 12d51b1db..21df0bf10 100644 --- a/classifai_recommended_content_post_args.html +++ b/classifai_recommended_content_post_args.html @@ -167,7 +167,7 @@
Parameters:
Source:
@@ -237,7 +237,7 @@
Returns:

diff --git a/classifai_rest_bases.html b/classifai_rest_bases.html index ce5e98996..99c1c8281 100644 --- a/classifai_rest_bases.html +++ b/classifai_rest_bases.html @@ -144,7 +144,7 @@
Parameters:
Source:
@@ -214,7 +214,7 @@
Returns:

diff --git a/classifai_services.html b/classifai_services.html index 129ea5caf..3a4d78f06 100644 --- a/classifai_services.html +++ b/classifai_services.html @@ -144,7 +144,7 @@
Parameters:
Source:
@@ -214,7 +214,7 @@
Returns:

diff --git a/classifai_should_classify_post.html b/classifai_should_classify_post.html index 41e788de0..6730b198a 100644 --- a/classifai_should_classify_post.html +++ b/classifai_should_classify_post.html @@ -169,7 +169,7 @@
Parameters:
Source:
@@ -239,7 +239,7 @@
Returns:

diff --git a/classifai_should_crop_size.html b/classifai_should_crop_size.html index 25e5ef80d..2b66fb821 100644 --- a/classifai_should_crop_size.html +++ b/classifai_should_crop_size.html @@ -237,7 +237,7 @@
Returns:

diff --git a/classifai_should_ocr_scan_image.html b/classifai_should_ocr_scan_image.html index ffb578911..1782ab171 100644 --- a/classifai_should_ocr_scan_image.html +++ b/classifai_should_ocr_scan_image.html @@ -190,7 +190,7 @@
Parameters:
Source:
@@ -260,7 +260,7 @@
Returns:

diff --git a/classifai_should_register_save_post_handler.html b/classifai_should_register_save_post_handler.html index 7be37f4ad..c474df382 100644 --- a/classifai_should_register_save_post_handler.html +++ b/classifai_should_register_save_post_handler.html @@ -144,7 +144,7 @@
Parameters:
Source:
@@ -214,7 +214,7 @@
Returns:

diff --git a/classifai_should_smart_crop_image.html b/classifai_should_smart_crop_image.html index e33396cce..680223d69 100644 --- a/classifai_should_smart_crop_image.html +++ b/classifai_should_smart_crop_image.html @@ -190,7 +190,7 @@
Parameters:
Source:
@@ -260,7 +260,7 @@
Returns:

diff --git a/classifai_smart_crop_max_pixel_dimension.html b/classifai_smart_crop_max_pixel_dimension.html index 2cb38b039..00c91704a 100644 --- a/classifai_smart_crop_max_pixel_dimension.html +++ b/classifai_smart_crop_max_pixel_dimension.html @@ -214,7 +214,7 @@
Returns:

diff --git a/classifai_smart_crop_wp_filesystem.html b/classifai_smart_crop_wp_filesystem.html index 219be3f38..a15d06a57 100644 --- a/classifai_smart_crop_wp_filesystem.html +++ b/classifai_smart_crop_wp_filesystem.html @@ -214,7 +214,7 @@
Returns:

diff --git a/classifai_smart_cropping_after_request.html b/classifai_smart_cropping_after_request.html index 0d4318a64..cd2557f5f 100644 --- a/classifai_smart_cropping_after_request.html +++ b/classifai_smart_cropping_after_request.html @@ -241,7 +241,7 @@
Parameters:

diff --git a/classifai_smart_cropping_source_url.html b/classifai_smart_cropping_source_url.html index 228a36ffd..08ff62c0a 100644 --- a/classifai_smart_cropping_source_url.html +++ b/classifai_smart_cropping_source_url.html @@ -244,7 +244,7 @@
Returns:

diff --git a/classifai_smart_cropping_thumb_file_name.html b/classifai_smart_cropping_thumb_file_name.html index 463e7f785..a5ff6f8a1 100644 --- a/classifai_smart_cropping_thumb_file_name.html +++ b/classifai_smart_cropping_thumb_file_name.html @@ -262,7 +262,7 @@
Returns:

diff --git a/classifai_smart_cropping_unsuccessful_response.html b/classifai_smart_cropping_unsuccessful_response.html index e3a3cf34f..725221ebb 100644 --- a/classifai_smart_cropping_unsuccessful_response.html +++ b/classifai_smart_cropping_unsuccessful_response.html @@ -241,7 +241,7 @@
Parameters:

diff --git a/classifai_taxonomy_for_feature.html b/classifai_taxonomy_for_feature.html index 0207bba0d..3fc0cc1c7 100644 --- a/classifai_taxonomy_for_feature.html +++ b/classifai_taxonomy_for_feature.html @@ -167,7 +167,7 @@
Parameters:
Source:
@@ -237,7 +237,7 @@
Returns:

diff --git a/classifai_threshold.html b/classifai_threshold.html new file mode 100644 index 000000000..d486456b8 --- /dev/null +++ b/classifai_threshold.html @@ -0,0 +1,248 @@ + + + + + Filter: classifai_threshold - 10up ClassifAI Hook Docs + + + + + + + + + + + + + +
+ + +

Filter: classifai_threshold

+ + + + + + + +
+ +
+

+ +
+ +
+
+ + + + + +

apply_filters( 'classifai_threshold', $threshold, $taxonomy ) → {float}

+ + + + + +
+

Filter the threshold for the similarity calculation.

+
+ + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
$threshold + + +float + + + +

The threshold to use.

$taxonomy + + +string + + + +

The taxonomy to get the threshold for.

+ + + + + + +
+ + + + +
Since:
+
  • 2.5.0
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + +
+

The threshold to use.

+
+ + + +
+
+ Type +
+
+ +float + + +
+
+ + + + + + +
+ + +
+ +
+ + + + + + + + +
+ + + +
+ + + + + \ No newline at end of file diff --git a/classifai_whisper_transcribe_request_body.html b/classifai_whisper_transcribe_request_body.html index ec8bdb862..2a7d00e5d 100644 --- a/classifai_whisper_transcribe_request_body.html +++ b/classifai_whisper_transcribe_request_body.html @@ -237,7 +237,7 @@
Returns:

diff --git a/classifai_whisper_transcribe_result.html b/classifai_whisper_transcribe_result.html index 87c5ac477..cfd414f2a 100644 --- a/classifai_whisper_transcribe_result.html +++ b/classifai_whisper_transcribe_result.html @@ -233,7 +233,7 @@
Returns:

diff --git a/index.html b/index.html index 95a52ffc6..c941a79c9 100644 --- a/index.html +++ b/index.html @@ -119,7 +119,7 @@


diff --git a/tutorial-useful-snippets.html b/tutorial-useful-snippets.html index 790c45042..e5ea94b2e 100644 --- a/tutorial-useful-snippets.html +++ b/tutorial-useful-snippets.html @@ -61,7 +61,7 @@

Make ClassifAI taxonomies private


diff --git a/tutorial-wp-cli.html b/tutorial-wp-cli.html index cf5a2bfb2..3074d0511 100644 --- a/tutorial-wp-cli.html +++ b/tutorial-wp-cli.html @@ -349,7 +349,7 @@

ClassifAI Settings Commands