diff --git a/%7B$this-_menu_slug%7D_providers.html b/%7B$this-_menu_slug%7D_providers.html index 3cfc4c41b..44719e99c 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 ca562b43f..0ee306718 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 fda99d195..69df88761 100644 --- a/Admin_SavePostHandler.php.html +++ b/Admin_SavePostHandler.php.html @@ -451,7 +451,7 @@

Source: Admin/SavePostHandler.php

$rest_bases = apply_filters( 'classifai_rest_bases', array( 'posts', 'pages' ) ); foreach ( $rest_bases as $rest_base ) { - if ( false !== strpos( sanitize_text_field( $_SERVER['REQUEST_URI'] ), 'wp-json/wp/v2/' . $rest_base ) ) { + if ( false !== strpos( sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) ), 'wp-json/wp/v2/' . $rest_base ) ) { return true; } } @@ -465,7 +465,7 @@

Source: Admin/SavePostHandler.php

* @return void */ public function classifai_classify_post() { - if ( ! empty( $_GET['classifai_classify_post_nonce'] ) && wp_verify_nonce( sanitize_text_field( $_GET['classifai_classify_post_nonce'] ), 'classifai_classify_post_action' ) ) { + if ( ! empty( $_GET['classifai_classify_post_nonce'] ) && wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['classifai_classify_post_nonce'] ) ), 'classifai_classify_post_action' ) ) { $post_id = isset( $_GET['post_id'] ) ? absint( $_GET['post_id'] ) : 0; if ( $post_id ) { $result = $this->classify( $post_id ); @@ -510,7 +510,7 @@

Source: Admin/SavePostHandler.php


diff --git a/Helpers.php.html b/Helpers.php.html index fd8bf3bd4..fa3123c6f 100644 --- a/Helpers.php.html +++ b/Helpers.php.html @@ -36,6 +36,7 @@

Source: Helpers.php

namespace Classifai; use Classifai\Providers\Provider; +use Classifai\Providers\Azure; use Classifai\Services\Service; use Classifai\Services\ServicesManager; use WP_Error; @@ -308,6 +309,28 @@

Source: Helpers.php

return $post_types; } +/** + * The list of post types that TTS supports. + * + * @return array Supported Post Types. + */ +function get_tts_supported_post_types() { + $classifai_settings = get_plugin_settings( 'language_processing', Azure\TextToSpeech::FEATURE_NAME ); + + if ( empty( $classifai_settings ) ) { + $post_types = []; + } else { + $post_types = []; + foreach ( $classifai_settings['post_types'] as $post_type => $enabled ) { + if ( ! empty( $enabled ) ) { + $post_types[] = $post_type; + } + } + } + + return $post_types; +} + /** * The list of post statuses that get the ClassifAI taxonomies. Defaults * to 'publish'. @@ -800,7 +823,7 @@

Source: Helpers.php


diff --git a/Plugin.php.html b/Plugin.php.html index 38d273ab4..e1c994723 100644 --- a/Plugin.php.html +++ b/Plugin.php.html @@ -221,6 +221,16 @@

Source: Plugin.php

'ajax_nonce' => wp_create_nonce( 'classifai' ), ] ); + + if ( wp_script_is( 'wp-commands', 'registered' ) ) { + wp_enqueue_script( + 'classifai-commands', + CLASSIFAI_PLUGIN_URL . 'dist/commands.js', + get_asset_info( 'commands', 'dependencies' ), + get_asset_info( 'commands', 'version' ), + true + ); + } } /** @@ -271,7 +281,7 @@

Source: Plugin.php


diff --git a/Providers_Azure_ComputerVision.php.html b/Providers_Azure_ComputerVision.php.html index 68a0ac8a5..4371cee28 100644 --- a/Providers_Azure_ComputerVision.php.html +++ b/Providers_Azure_ComputerVision.php.html @@ -64,14 +64,14 @@

Source: Providers/Azure/ComputerVision.php

public function __construct( $service ) { parent::__construct( 'Microsoft Azure', - 'Computer Vision', + 'AI Vision', 'computer_vision', $service ); // Set the onboarding options. $this->onboarding_options = array( - 'title' => __( 'Microsoft Azure Computer Vision', 'classifai' ), + 'title' => __( 'Microsoft Azure AI Vision', 'classifai' ), 'fields' => array( 'url', 'api-key' ), 'features' => array( 'enable_image_captions' => __( 'Automatically add alt-text to images', 'classifai' ), @@ -95,7 +95,7 @@

Source: Providers/Azure/ComputerVision.php

* * @return array */ - private function get_default_settings() { + public function get_default_settings() { return [ 'valid' => false, 'url' => '', @@ -1086,7 +1086,7 @@

Source: Providers/Azure/ComputerVision.php

'input_type' => 'checkbox', 'default_value' => $default_settings['enable_smart_cropping'], 'description' => __( - 'ComputerVision detects and saves the most visually interesting part of your image (i.e., faces, animals, notable text).', + 'AI Vision detects and saves the most visually interesting part of your image (i.e., faces, animals, notable text).', 'classifai' ), ] @@ -1400,7 +1400,7 @@

Source: Providers/Azure/ComputerVision.php


diff --git a/Providers_Azure_OCR.php.html b/Providers_Azure_OCR.php.html index ff778ab26..1c4eea016 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 985f00dca..502efe05b 100644 --- a/Providers_Azure_Personalizer.php.html +++ b/Providers_Azure_Personalizer.php.html @@ -33,7 +33,7 @@

Source: Providers/Azure/Personalizer.php

<?php
 /**
- * Azure Personalizer
+ * Azure AI Personalizer
  */
 
 namespace Classifai\Providers\Azure;
@@ -68,14 +68,14 @@ 

Source: Providers/Azure/Personalizer.php

public function __construct( $service ) { parent::__construct( 'Microsoft Azure', - 'Personalizer', + 'AI Personalizer', 'personalizer', $service ); // Set the onboarding options. $this->onboarding_options = array( - 'title' => __( 'Microsoft Azure Personalizer', 'classifai' ), + 'title' => __( 'Microsoft Azure AI Personalizer', 'classifai' ), 'fields' => array( 'url', 'api-key' ), 'features' => array( 'authenticated' => __( 'Recommended content block', 'classifai' ), @@ -95,7 +95,7 @@

Source: Providers/Azure/Personalizer.php

* * @return array */ - private function get_default_settings() { + public function get_default_settings() { return [ 'authenticated' => false, 'url' => '', @@ -130,7 +130,7 @@

Source: Providers/Azure/Personalizer.php

'description' => sprintf( wp_kses( // translators: 1 - link to create a Personalizer resource. - __( 'Azure Cognitive Service Personalizer Endpoint, <a href="%1$s" target="_blank">create a Personalizer resource</a> in the Azure portal to get your key and endpoint.', 'classifai' ), + __( 'Azure AI Personalizer Endpoint, <a href="%1$s" target="_blank">create a Personalizer resource</a> in the Azure portal to get your key and endpoint.', 'classifai' ), array( 'a' => array( 'href' => array(), @@ -152,7 +152,7 @@

Source: Providers/Azure/Personalizer.php

'label_for' => 'api_key', 'input_type' => 'password', 'default_value' => $default_settings['api_key'], - 'description' => __( 'Azure Cognitive Service Personalizer Key.', 'classifai' ), + 'description' => __( 'Azure AI Personalizer Key.', 'classifai' ), ] ); } @@ -343,7 +343,7 @@

Source: Providers/Azure/Personalizer.php

if ( is_wp_error( $response ) ) { // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log - error_log( __( 'Failed to contact Azure Cognitive Service Personalizer: ', 'classifai' ) . $response->get_error_message() ); + error_log( __( 'Failed to contact Azure AI Personalizer: ', 'classifai' ) . $response->get_error_message() ); return array( 'response' => (object) array(), 'actions' => $action_ids, @@ -638,7 +638,7 @@

Source: Providers/Azure/Personalizer.php

} /** - * Get Ranked action by sending request to Azure Personalizer. + * Get Ranked action by sending request to Azure AI Personalizer. * * @param array $rank_request Prepared Request data. * @return object|string @@ -772,7 +772,7 @@

Source: Providers/Azure/Personalizer.php


diff --git a/Providers_Azure_Read.php.html b/Providers_Azure_Read.php.html index e725569cf..5481097f8 100644 --- a/Providers_Azure_Read.php.html +++ b/Providers_Azure_Read.php.html @@ -301,13 +301,10 @@

Source: Providers/Azure/Read.php

break; case 'failed': return $this->log_error( new WP_Error( 'failed_read_request', esc_html__( 'The Read operation has failed.', 'classifai' ) ) ); - break; case 'succeeded': return $this->update_document_description( $body ); - break; default: return $this->log_error( new WP_Error( 'invalid_read_result_status', esc_html__( 'Invalid Read result status.', 'classifai' ) ) ); - break; } } } @@ -414,7 +411,7 @@

Source: Providers/Azure/Read.php


diff --git a/Providers_Azure_SmartCropping.php.html b/Providers_Azure_SmartCropping.php.html index b0a957413..2f24ca500 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 25f909f7d..6e0f5967f 100644 --- a/Providers_Azure_TextToSpeech.php.html +++ b/Providers_Azure_TextToSpeech.php.html @@ -44,6 +44,7 @@

Source: Providers/Azure/TextToSpeech.php

use WP_Http; use function Classifai\get_post_types_for_language_settings; +use function Classifai\get_tts_supported_post_types; use function Classifai\get_asset_info; class TextToSpeech extends Provider { @@ -63,12 +64,11 @@

Source: Providers/Azure/TextToSpeech.php

const API_PATH = 'cognitiveservices/v1'; /** - * Meta key to store data indicating whether Text to Speech is enabled for - * the current post. + * Meta key to hide/unhide already generated audio file. * * @var string */ - const SYNTHESIZE_SPEECH_KEY = '_classifai_synthesize_speech'; + const DISPLAY_GENERATED_AUDIO = '_classifai_display_generated_audio'; /** * Meta key to get/set the ID of the speech audio file. @@ -120,21 +120,13 @@

Source: Providers/Azure/TextToSpeech.php

* Enqueue the editor scripts. */ public function enqueue_editor_assets() { - global $post; - - wp_enqueue_script( - 'classifai-editor', // Handle. - CLASSIFAI_PLUGIN_URL . 'dist/editor.js', - array( 'wp-blocks', 'wp-i18n', 'wp-element', 'wp-editor', 'wp-edit-post' ), - CLASSIFAI_PLUGIN_VERSION, - true - ); + $post = get_post(); if ( empty( $post ) ) { return; } - $supported_post_types = self::get_supported_post_types(); + $supported_post_types = get_tts_supported_post_types(); if ( ! in_array( $post->post_type, $supported_post_types, true ) ) { return; @@ -148,13 +140,13 @@

Source: Providers/Azure/TextToSpeech.php

true ); - wp_localize_script( + wp_add_inline_script( 'classifai-gutenberg-plugin', - 'classifaiTextToSpeechData', - [ - 'supportedPostTypes' => self::get_supported_post_types(), - 'noPermissions' => ! is_user_logged_in() || ! current_user_can( 'edit_post', $post->ID ), - ] + sprintf( + 'var classifaiTTSEnabled = %d;', + true + ), + 'before' ); } @@ -166,11 +158,17 @@

Source: Providers/Azure/TextToSpeech.php

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 ); + } + add_filter( 'the_content', [ $this, 'render_post_audio_controls' ] ); } /** - * Resets settings for the Personalizer provider. + * Resets settings for the TextToSpeech provider. */ public function reset_settings() { update_option( $this->get_option_name(), $this->get_default_settings() ); @@ -223,7 +221,7 @@

Source: Providers/Azure/TextToSpeech.php

'label_for' => 'post_types', 'option_index' => 'post_types', 'options' => $this->get_post_types_select_options(), - 'default_values' => $default_settings['post-types'], + 'default_values' => $default_settings['post_types'], ] ); @@ -273,6 +271,9 @@

Source: Providers/Azure/TextToSpeech.php

if ( ! empty( $current_settings['voices'] ) ) { $current_settings['authenticated'] = true; + } else { + $current_settings['voices'] = []; + $current_settings['authenticated'] = false; } } } else { @@ -300,8 +301,6 @@

Source: Providers/Azure/TextToSpeech.php

if ( isset( $settings['voice'] ) && ! empty( $settings['voice'] ) ) { $current_settings['voice'] = sanitize_text_field( $settings['voice'] ); - } else { - $current_settings['voice'] = ''; } return $current_settings; @@ -460,7 +459,7 @@

Source: Providers/Azure/TextToSpeech.php

/** * Returns the default settings. */ - private function get_default_settings() { + public function get_default_settings() { return [ 'credentials' => array( 'url' => '', @@ -469,44 +468,108 @@

Source: Providers/Azure/TextToSpeech.php

'voices' => array(), 'voice' => '', 'authenticated' => false, - 'post-types' => array(), + 'post_types' => array(), ]; } /** - * Add `classifai_synthesize_speech` to rest API for view/edit. + * Initial audio generation state. + * + * Fetch the initial state of audio generation prior to the audio existing for the post. + * + * @param int|WP_Post|null $post Optional. Post ID or post object. `null`, `false`, `0` and other PHP falsey values + * return the current global post inside the loop. A numerically valid post ID that + * points to a non-existent post returns `null`. Defaults to global $post. + * @return bool The initial state of audio generation. Default true. + */ + public function get_audio_generation_initial_state( $post = null ) { + /** + * Initial state of the audio generation toggle when no audio already exists for the post. + * + * @since 2.3.0 + * @hook classifai_audio_generation_initial_state + * + * @param {bool} $state Initial state of audio generation toggle on a post. Default true. + * @param {WP_Post} $post The current Post object. + * + * @return {bool} Initial state the audio generation toggle should be set to when no audio exists. + */ + return apply_filters( 'classifai_audio_generation_initial_state', true, get_post( $post ) ); + } + + /** + * Subsequent audio generation state. + * + * Fetch the subsequent state of audio generation once audio is generated for the post. + * + * @param int|WP_Post|null $post Optional. Post ID or post object. `null`, `false`, `0` and other PHP falsey values + * return the current global post inside the loop. A numerically valid post ID that + * points to a non-existent post returns `null`. Defaults to global $post. + * @return bool The subsequent state of audio generation. Default false. + */ + public function get_audio_generation_subsequent_state( $post = null ) { + /** + * Subsequent state of the audio generation toggle when audio exists for the post. + * + * @since 2.3.0 + * @hook classifai_audio_generation_subsequent_state + * + * @param {bool} $state Subsequent state of audio generation toggle on a post. Default false. + * @param {WP_Post} $post The current Post object. + * + * @return {bool} Subsequent state the audio generation toggle should be set to when audio exists. + */ + return apply_filters( 'classifai_audio_generation_subsequent_state', false, get_post( $post ) ); + } + + /** + * Add audio related fields to rest API for view/edit. */ public function add_synthesize_speech_meta_to_rest_api() { - $settings = $this->get_settings(); - $supported_post_types = $settings['post_types'] ?? array(); - - $supported_post_types = array_keys( - array_filter( - $supported_post_types, - function( $post_type ) { - return ! is_null( $post_type ); - } + $supported_post_types = get_tts_supported_post_types(); + + register_rest_field( + $supported_post_types, + 'classifai_synthesize_speech', + array( + 'get_callback' => function( $object ) { + $audio_id = get_post_meta( $object['id'], self::AUDIO_ID_KEY, true ); + if ( + ( $this->get_audio_generation_initial_state( $object['id'] ) && ! $audio_id ) || + ( $this->get_audio_generation_subsequent_state( $object['id'] ) && $audio_id ) + ) { + return true; + } else { + return false; + } + }, + 'schema' => [ + 'type' => 'boolean', + 'context' => [ 'view', 'edit' ], + ], ) ); - if ( empty( $supported_post_types ) ) { - return; - } - register_rest_field( $supported_post_types, - 'classifai_synthesize_speech', + 'classifai_display_generated_audio', array( 'get_callback' => function( $object ) { - $process_content = get_post_meta( $object['id'], self::SYNTHESIZE_SPEECH_KEY, true ); - return ( 'no' === $process_content ) ? 'no' : 'yes'; + // Default to display the audio if available. + if ( metadata_exists( 'post', $object['id'], self::DISPLAY_GENERATED_AUDIO ) ) { + return (bool) get_post_meta( $object['id'], self::DISPLAY_GENERATED_AUDIO, true ); + } + return true; }, - 'update_callback' => function ( $value, $object ) { - $value = ( 'no' === $value ) ? 'no' : 'yes'; - return update_post_meta( $object->ID, self::SYNTHESIZE_SPEECH_KEY, $value ); + 'update_callback' => function( $value, $object ) { + if ( $value ) { + delete_post_meta( $object->ID, self::DISPLAY_GENERATED_AUDIO ); + } else { + update_post_meta( $object->ID, self::DISPLAY_GENERATED_AUDIO, false ); + } }, 'schema' => [ - 'type' => 'string', + 'type' => 'boolean', 'context' => [ 'view', 'edit' ], ], ) @@ -528,13 +591,42 @@

Source: Providers/Azure/TextToSpeech.php

); } + /** + * Handles audio generation on rest updates / inserts. + * + * @param WP_Post $post Inserted or updated post object. + * @param WP_REST_Request $request Request object. + */ + public function rest_handle_audio( $post, $request ) { + + $audio_id = get_post_meta( $request->get_param( 'id' ), self::AUDIO_ID_KEY, true ); + + // Since we have dynamic generation option agnostic to meta saves we need a flag to differentiate audio generation accurately + $process_content = false; + if ( + ( $this->get_audio_generation_initial_state( $post ) && ! $audio_id ) || + ( $this->get_audio_generation_subsequent_state( $post ) && $audio_id ) + ) { + $process_content = true; + } + + // Add/Update audio if it was requested. + if ( + ( $process_content && null === $request->get_param( 'classifai_synthesize_speech' ) ) || + true === $request->get_param( 'classifai_synthesize_speech' ) + ) { + $save_post_handler = new SavePostHandler(); + $save_post_handler->synthesize_speech( $request->get_param( 'id' ) ); + } + } + /** * Add meta box to post types that support speech synthesis. * * @param string $post_type Post type. */ public function add_meta_box( $post_type ) { - if ( ! in_array( $post_type, $this->get_supported_post_types(), true ) ) { + if ( ! in_array( $post_type, get_tts_supported_post_types(), true ) ) { return; } @@ -544,7 +636,7 @@

Source: Providers/Azure/TextToSpeech.php

[ $this, 'render_meta_box' ], null, 'side', - 'low', + 'high', array( '__back_compat_meta_box' => true ) ); } @@ -557,38 +649,65 @@

Source: Providers/Azure/TextToSpeech.php

public function render_meta_box( $post ) { wp_nonce_field( 'classifai_text_to_speech_meta_action', 'classifai_text_to_speech_meta' ); - $process_content = get_post_meta( $post->ID, self::SYNTHESIZE_SPEECH_KEY, true ); - $process_content = ( 'no' === $process_content ) ? 'no' : 'yes'; + $source_url = false; + $audio_id = get_post_meta( $post->ID, self::AUDIO_ID_KEY, true ); + if ( $audio_id ) { + $source_url = wp_get_attachment_url( $audio_id ); + } + + $process_content = false; + if ( + ( $this->get_audio_generation_initial_state( $post ) && ! $audio_id ) || + ( $this->get_audio_generation_subsequent_state( $post ) && $audio_id ) + ) { + $process_content = true; + } + + $display_audio = true; + if ( metadata_exists( 'post', $post->ID, self::DISPLAY_GENERATED_AUDIO ) && + ! (bool) get_post_meta( $post->ID, self::DISPLAY_GENERATED_AUDIO, true ) ) { + $display_audio = false; + } - $post_type = get_post_type_object( get_post_type( $post ) ); $post_type_label = esc_html__( 'Post', 'classifai' ); + $post_type = get_post_type_object( get_post_type( $post ) ); if ( $post_type ) { $post_type_label = $post_type->labels->singular_name; } - $audio_id = get_post_meta( $post->ID, self::AUDIO_ID_KEY, true ); ?> - <p> <label for="classifai_synthesize_speech"> - <input type="checkbox" value="yes" id="classifai_synthesize_speech" name="classifai_synthesize_speech" <?php checked( $process_content, 'yes' ); ?> /> + <input type="checkbox" value="1" id="classifai_synthesize_speech" name="classifai_synthesize_speech" <?php checked( $process_content ); ?> /> <?php esc_html_e( 'Enable audio generation', 'classifai' ); ?> </label> <span class="description"> <?php /* translators: %s Post type label */ - printf( esc_html__( 'ClassifAI will generate audio for this %s when it is published or updated', 'classifai' ), esc_html( $post_type_label ) ); + printf( esc_html__( 'ClassifAI will generate audio for this %s when it is published or updated.', 'classifai' ), esc_html( $post_type_label ) ); + ?> + </span> + </p> + + <p<?php echo $source_url ? '' : ' class="hidden"'; ?>> + <label for="classifai_display_generated_audio"> + <input type="checkbox" value="1" id="classifai_display_generated_audio" name="classifai_display_generated_audio" <?php checked( $display_audio ); ?> /> + <?php esc_html_e( 'Display audio controls', 'classifai' ); ?> + </label> + <span class="description"> + <?php + esc_html__( 'Controls the display of the audio player on the front-end.', 'classifai' ); ?> </span> </p> <?php - if ( 'yes' === $process_content && $audio_id && wp_get_attachment_url( $audio_id ) ) { + if ( $source_url ) { $cache_busting_url = add_query_arg( [ 'ver' => time(), ], - wp_get_attachment_url( $audio_id ) + $source_url ); ?> @@ -607,26 +726,28 @@

Source: Providers/Azure/TextToSpeech.php

* @param int $post_id Post ID. */ public function save_post_metadata( $post_id ) { - if ( ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) || ! current_user_can( 'edit_post', $post_id ) || 'revision' === get_post_type( $post_id ) ) { + + if ( ! in_array( get_post_type( $post_id ), get_tts_supported_post_types(), true ) ) { return; } - if ( empty( $_POST['classifai_text_to_speech_meta'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['classifai_text_to_speech_meta'] ) ), 'classifai_text_to_speech_meta_action' ) ) { + if ( ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) || ! current_user_can( 'edit_post', $post_id ) || 'revision' === get_post_type( $post_id ) ) { return; } - $supported_post_types = $this->get_supported_post_types(); - if ( ! in_array( get_post_type( $post_id ), $supported_post_types, true ) ) { + if ( empty( $_POST['classifai_text_to_speech_meta'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['classifai_text_to_speech_meta'] ) ), 'classifai_text_to_speech_meta_action' ) ) { return; } - if ( isset( $_POST['classifai_synthesize_speech'] ) && 'yes' === sanitize_text_field( wp_unslash( $_POST['classifai_synthesize_speech'] ) ) ) { - update_post_meta( $post_id, self::SYNTHESIZE_SPEECH_KEY, 'yes' ); + if ( ! isset( $_POST['classifai_display_generated_audio'] ) ) { + update_post_meta( $post_id, self::DISPLAY_GENERATED_AUDIO, false ); + } else { + delete_post_meta( $post_id, self::DISPLAY_GENERATED_AUDIO ); + } + if ( isset( $_POST['classifai_synthesize_speech'] ) ) { $save_post_handler = new SavePostHandler(); $save_post_handler->synthesize_speech( $post_id ); - } else { - update_post_meta( $post_id, self::SYNTHESIZE_SPEECH_KEY, 'no' ); } } @@ -644,7 +765,11 @@

Source: Providers/Azure/TextToSpeech.php

return $content; } - if ( ! in_array( $_post->post_type, self::get_supported_post_types(), true ) ) { + if ( ! is_singular( $_post->post_type ) ) { + return $content; + } + + if ( ! in_array( $_post->post_type, get_tts_supported_post_types(), true ) ) { return $content; } @@ -663,8 +788,9 @@

Source: Providers/Azure/TextToSpeech.php

return $content; } - $is_audio_enabled = get_post_meta( $_post->ID, self::SYNTHESIZE_SPEECH_KEY, true ); - if ( 'no' === $is_audio_enabled ) { + // Respect the audio display settings of the post. + if ( metadata_exists( 'post', $_post->ID, self::DISPLAY_GENERATED_AUDIO ) && + ! (bool) get_post_meta( $_post->ID, self::DISPLAY_GENERATED_AUDIO, true ) ) { return $content; } @@ -785,26 +911,6 @@

Source: Providers/Azure/TextToSpeech.php

return $options; } - /** - * Returns supported post types for Azure Text to Speech. - * - * @todo Move this to a more generic method during refactoring of the plugin. - * @return array - */ - public static function get_supported_post_types() { - $settings = \Classifai\get_plugin_settings( 'language_processing', self::FEATURE_NAME ); - $supported_post_types = isset( $settings['post_types'] ) ? $settings['post_types'] : array(); - - return array_keys( - array_filter( - $supported_post_types, - function( $post_type ) { - return '0' !== $post_type; - } - ) - ); - } - }
@@ -824,7 +930,7 @@

Source: Providers/Azure/TextToSpeech.php


diff --git a/Providers_OpenAI_ChatGPT.php.html b/Providers_OpenAI_ChatGPT.php.html index 624e54aac..b2c298291 100644 --- a/Providers_OpenAI_ChatGPT.php.html +++ b/Providers_OpenAI_ChatGPT.php.html @@ -39,11 +39,9 @@

Source: Providers/OpenAI/ChatGPT.php

namespace Classifai\Providers\OpenAI; use Classifai\Providers\Provider; -use Classifai\Providers\OpenAI\APIRequest; -use Classifai\Providers\OpenAI\Tokenizer; use Classifai\Watson\Normalizer; -use function Classifai\get_asset_info; use WP_Error; +use function Classifai\get_asset_info; class ChatGPT extends Provider { @@ -88,19 +86,64 @@

Source: Providers/OpenAI/ChatGPT.php

'title' => __( 'OpenAI ChatGPT', 'classifai' ), 'fields' => array( 'api-key' ), 'features' => array( - 'enable_excerpt' => __( 'Excerpt generation', 'classifai' ), - 'enable_titles' => __( 'Title generation', 'classifai' ), + 'enable_excerpt' => __( 'Excerpt generation', 'classifai' ), + 'enable_titles' => __( 'Title generation', 'classifai' ), + 'enable_resize_content' => __( 'Content resizing', 'classifai' ), ), ); } + /** + * 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. * * This only fires if can_register returns true. */ public function register() { - add_action( 'enqueue_block_editor_assets', [ $this, 'enqueue_editor_assets' ] ); + add_action( 'enqueue_block_assets', [ $this, 'enqueue_editor_assets' ] ); add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_admin_assets' ] ); add_action( 'edit_form_before_permalink', [ $this, 'register_generated_titles_template' ] ); } @@ -135,14 +178,7 @@

Source: Providers/OpenAI/ChatGPT.php

return; } - $settings = $this->get_settings(); - $user_roles = wp_get_current_user()->roles ?? []; - $excerpt_roles = $settings['roles'] ?? []; - - if ( - ( ! empty( $excerpt_roles ) && empty( array_diff( $user_roles, $excerpt_roles ) ) ) - && ( isset( $settings['enable_excerpt'] ) && 1 === (int) $settings['enable_excerpt'] ) - ) { + if ( $this->is_feature_enabled( 'enable_excerpt' ) ) { // This script removes the core excerpt panel and replaces it with our own. wp_enqueue_script( 'classifai-post-excerpt', @@ -153,12 +189,7 @@

Source: Providers/OpenAI/ChatGPT.php

); } - $title_roles = $settings['title_roles'] ?? []; - - if ( - ( ! empty( $title_roles ) && empty( array_diff( $user_roles, $title_roles ) ) ) - && ( isset( $settings['enable_titles'] ) && 1 === (int) $settings['enable_titles'] ) - ) { + if ( $this->is_feature_enabled( 'enable_titles' ) ) { wp_enqueue_script( 'classifai-post-status-info', CLASSIFAI_PLUGIN_URL . 'dist/post-status-info.js', @@ -176,6 +207,24 @@

Source: Providers/OpenAI/ChatGPT.php

'before' ); } + + if ( $this->is_feature_enabled( 'enable_resize_content' ) ) { + wp_enqueue_script( + 'classifai-content-resizing-plugin-js', + CLASSIFAI_PLUGIN_URL . 'dist/content-resizing-plugin.js', + get_asset_info( 'content-resizing-plugin', 'dependencies' ), + get_asset_info( 'content-resizing-plugin', 'version' ), + true + ); + + wp_enqueue_style( + 'classifai-content-resizing-plugin-css', + CLASSIFAI_PLUGIN_URL . 'dist/content-resizing-plugin.css', + [], + CLASSIFAI_PLUGIN_VERSION, + 'all' + ); + } } /** @@ -188,41 +237,76 @@

Source: Providers/OpenAI/ChatGPT.php

return; } - $screen = get_current_screen(); - $settings = $this->get_settings(); - $user_roles = wp_get_current_user()->roles ?? []; - $title_roles = $settings['title_roles'] ?? []; + $screen = get_current_screen(); + $settings = $this->get_settings(); // Load the assets for the classic editor. - if ( - $screen && ! $screen->is_block_editor() - && ( ! empty( $title_roles ) && empty( array_diff( $user_roles, $title_roles ) ) ) - && ( isset( $settings['enable_titles'] ) && 1 === (int) $settings['enable_titles'] ) - ) { - wp_enqueue_style( - 'classifai-generate-title-classic-css', - CLASSIFAI_PLUGIN_URL . 'dist/generate-title-classic.css', - [], - CLASSIFAI_PLUGIN_VERSION, - 'all' - ); - - wp_enqueue_script( - 'classifai-generate-title-classic-js', - CLASSIFAI_PLUGIN_URL . 'dist/generate-title-classic.js', - array_merge( get_asset_info( 'generate-title-classic', 'dependencies' ), array( 'wp-api' ) ), - get_asset_info( 'generate-title-classic', 'version' ), - true - ); + if ( $screen && ! $screen->is_block_editor() ) { + if ( + post_type_supports( $screen->post_type, 'title' ) && + $this->is_feature_enabled( 'enable_titles' ) + ) { + wp_enqueue_style( + 'classifai-generate-title-classic-css', + CLASSIFAI_PLUGIN_URL . 'dist/generate-title-classic.css', + [], + CLASSIFAI_PLUGIN_VERSION, + 'all' + ); + + wp_enqueue_script( + 'classifai-generate-title-classic-js', + CLASSIFAI_PLUGIN_URL . 'dist/generate-title-classic.js', + array_merge( get_asset_info( 'generate-title-classic', 'dependencies' ), array( 'wp-api' ) ), + get_asset_info( 'generate-title-classic', 'version' ), + true + ); + + wp_add_inline_script( + 'classifai-generate-title-classic-js', + sprintf( + 'var classifaiChatGPTData = %s;', + wp_json_encode( $this->get_localised_vars() ) + ), + 'before' + ); + } - wp_add_inline_script( - 'classifai-generate-title-classic-js', - sprintf( - 'var classifaiChatGPTData = %s;', - wp_json_encode( $this->get_localised_vars() ) - ), - 'before' - ); + if ( + post_type_supports( $screen->post_type, 'excerpt' ) && + $this->is_feature_enabled( 'enable_excerpt' ) + ) { + wp_enqueue_style( + 'classifai-generate-title-classic-css', + CLASSIFAI_PLUGIN_URL . 'dist/generate-title-classic.css', + [], + CLASSIFAI_PLUGIN_VERSION, + 'all' + ); + + wp_enqueue_script( + 'classifai-generate-excerpt-classic-js', + CLASSIFAI_PLUGIN_URL . 'dist/generate-excerpt-classic.js', + array_merge( get_asset_info( 'generate-excerpt-classic', 'dependencies' ), array( 'wp-api' ) ), + get_asset_info( 'generate-excerpt-classic', 'version' ), + true + ); + + wp_add_inline_script( + 'classifai-generate-excerpt-classic-js', + sprintf( + 'var classifaiGenerateExcerpt = %s;', + wp_json_encode( + [ + 'path' => '/classifai/v1/openai/generate-excerpt/', + 'buttonText' => __( 'Generate excerpt', 'classifai' ), + 'regenerateText' => __( 'Re-generate excerpt', 'classifai' ), + ] + ) + ), + 'before' + ); + } } wp_enqueue_style( @@ -285,6 +369,19 @@

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' ), @@ -364,6 +461,60 @@

Source: Providers/OpenAI/ChatGPT.php

'description' => __( 'Number of titles that will be generated in one request.', 'classifai' ), ] ); + + // Add resizing content fields. + add_settings_section( + $this->get_option_name() . '_resize_content_settings', + esc_html__( 'Resizing content settings', 'classifai' ), + '', + $this->get_option_name() + ); + + add_settings_field( + 'enable-resize-content', + esc_html__( 'Enable content resizing', 'classifai' ), + [ $this, 'render_input' ], + $this->get_option_name(), + $this->get_option_name() . '_resize_content_settings', + [ + 'label_for' => 'enable_resize_content', + 'input_type' => 'checkbox', + 'default_value' => $default_settings['enable_resize_content'], + 'description' => __( '"Shrink content" and "Grow content" menu items will be added to the paragraph block\'s toolbar menu.', 'classifai' ), + ] + ); + + $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_settings_field( + 'number-resize-content', + esc_html__( 'Number of suggestions', 'classifai' ), + [ $this, 'render_select' ], + $this->get_option_name(), + $this->get_option_name() . '_resize_content_settings', + [ + 'label_for' => 'number_resize_content', + 'options' => array_combine( range( 1, 10 ), range( 1, 10 ) ), + 'default_value' => $default_settings['number_resize_content'], + 'description' => __( 'Number of suggestions that will be generated in one request.', 'classifai' ), + ] + ); } /** @@ -416,6 +567,24 @@

Source: Providers/OpenAI/ChatGPT.php

$new_settings['number_titles'] = 1; } + if ( empty( $settings['enable_resize_content'] ) || 1 !== (int) $settings['enable_resize_content'] ) { + $new_settings['enable_resize_content'] = 'no'; + } else { + $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 { + $new_settings['number_resize_content'] = 1; + } + return $new_settings; } @@ -431,18 +600,24 @@

Source: Providers/OpenAI/ChatGPT.php

* * @return array */ - private function get_default_settings() { + public function get_default_settings() { + if ( ! function_exists( 'get_editable_roles' ) ) { + require_once ABSPATH . 'wp-admin/includes/user.php'; + } $editable_roles = get_editable_roles() ?? []; return [ - 'authenticated' => false, - 'api_key' => '', - 'enable_excerpt' => false, - 'roles' => array_keys( $editable_roles ), - 'length' => (int) apply_filters( 'excerpt_length', 55 ), - 'enable_titles' => false, - 'title_roles' => array_keys( $editable_roles ), - 'number_titles' => 1, + 'authenticated' => false, + 'api_key' => '', + 'enable_excerpt' => false, + 'roles' => array_keys( $editable_roles ), + 'length' => (int) apply_filters( 'excerpt_length', 55 ), + 'enable_titles' => false, + 'title_roles' => array_keys( $editable_roles ), + 'number_titles' => 1, + 'enable_resize_content' => false, + 'resize_content_roles' => array_keys( $editable_roles ), + 'number_resize_content' => 1, ]; } @@ -470,6 +645,8 @@

Source: Providers/OpenAI/ChatGPT.php

__( 'Generate titles', 'classifai' ) => $enable_titles ? __( 'yes', 'classifai' ) : __( 'no', 'classifai' ), __( 'Allowed roles (titles)', 'classifai' ) => implode( ', ', $settings['title_roles'] ?? [] ), __( 'Number of titles', 'classifai' ) => absint( $settings['number_titles'] ?? 1 ), + __( 'Allowed roles (resize)', 'classifai' ) => implode( ', ', $settings['resize_content_roles'] ?? [] ), + __( 'Number of suggestions', 'classifai' ) => absint( $settings['number_resize_content'] ?? 1 ), __( 'Latest response', 'classifai' ) => $this->get_formatted_latest_response( get_transient( 'classifai_openai_chatgpt_latest_response' ) ), ]; } @@ -494,11 +671,14 @@

Source: Providers/OpenAI/ChatGPT.php

// Handle all of our routes. switch ( $route_to_call ) { case 'excerpt': - $return = $this->generate_excerpt( $post_id ); + $return = $this->generate_excerpt( $post_id, $args ); break; case 'title': $return = $this->generate_titles( $post_id, $args ); break; + case 'resize_content': + $return = $this->resize_content( $post_id, $args ); + break; } return $return; @@ -507,19 +687,27 @@

Source: Providers/OpenAI/ChatGPT.php

/** * Generate an excerpt using ChatGPT. * - * @param int $post_id The Post ID we're processing + * @param int $post_id The Post ID we're processing + * @param array $args Arguments passed in. * @return string|WP_Error */ - public function generate_excerpt( int $post_id = 0 ) { + public function generate_excerpt( int $post_id = 0, array $args = [] ) { if ( ! $post_id || ! get_post( $post_id ) ) { return new WP_Error( 'post_id_required', esc_html__( 'A valid post ID is required to generate an excerpt.', 'classifai' ) ); } $settings = $this->get_settings(); + $args = wp_parse_args( + array_filter( $args ), + [ + 'content' => '', + 'title' => get_the_title( $post_id ), + ] + ); // 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'] ) || ( isset( $settings['enable_excerpt'] ) && 'no' === $settings['enable_excerpt'] && ( ! defined( 'WP_CLI' ) || ! WP_CLI ) ) ) { + if ( empty( $settings ) || ( isset( $settings['authenticated'] ) && false === $settings['authenticated'] ) || ( ! $this->is_feature_enabled( 'enable_excerpt' ) && ( ! 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' ) ); } @@ -539,7 +727,7 @@

Source: Providers/OpenAI/ChatGPT.php

* * @return {string} Prompt. */ - $prompt = apply_filters( 'classifai_chatgpt_excerpt_prompt', 'Provide a teaser for the following text using a maximum of ' . $excerpt_length . ' words', $post_id, $excerpt_length ); + $prompt = apply_filters( 'classifai_chatgpt_excerpt_prompt', sprintf( 'Summarize the following message using a maximum of %d words. Ensure this summary pairs well with the following text: %s.', $excerpt_length, $args['title'] ), $post_id, $excerpt_length ); /** * Filter the request body before sending to ChatGPT. @@ -557,12 +745,16 @@

Source: Providers/OpenAI/ChatGPT.php

[ 'model' => $this->chatgpt_model, 'messages' => [ + [ + 'role' => 'system', + 'content' => $prompt, + ], [ 'role' => 'user', - 'content' => $prompt . ': ' . $this->get_content( $post_id, $excerpt_length ) . '', + 'content' => $this->get_content( $post_id, $excerpt_length, false, $args['content'] ) . '', ], ], - 'temperature' => 0, + 'temperature' => 0.9, ], $post_id ); @@ -606,13 +798,14 @@

Source: Providers/OpenAI/ChatGPT.php

$args = wp_parse_args( array_filter( $args ), [ - 'num' => $settings['number_titles'] ?? 1, + 'num' => $settings['number_titles'] ?? 1, + 'content' => '', ] ); // 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'] ) || ( isset( $settings['enable_titles'] ) && 'no' === $settings['enable_titles'] ) ) { + if ( empty( $settings ) || ( isset( $settings['authenticated'] ) && false === $settings['authenticated'] ) || ! $this->is_feature_enabled( 'enable_titles' ) ) { return new WP_Error( 'not_enabled', esc_html__( 'Title generation is disabled or OpenAI authentication failed. Please check your settings.', 'classifai' ) ); } @@ -630,7 +823,7 @@

Source: Providers/OpenAI/ChatGPT.php

* * @return {string} Prompt. */ - $prompt = apply_filters( 'classifai_chatgpt_title_prompt', 'Write an SEO-friendly title for the following content that will encourage readers to clickthrough, staying within a range of 40 to 60 characters', $post_id, $args ); + $prompt = apply_filters( 'classifai_chatgpt_title_prompt', 'Write an SEO-friendly title for the following content that will encourage readers to clickthrough, staying within a range of 40 to 60 characters.', $post_id, $args ); /** * Filter the request body before sending to ChatGPT. @@ -648,9 +841,115 @@

Source: Providers/OpenAI/ChatGPT.php

[ 'model' => $this->chatgpt_model, 'messages' => [ + [ + 'role' => 'system', + 'content' => $prompt, + ], + [ + 'role' => 'user', + 'content' => $this->get_content( $post_id, absint( $args['num'] ) * 15, false, $args['content'] ) . '', + ], + ], + 'temperature' => 0.9, + 'n' => absint( $args['num'] ), + ], + $post_id + ); + + // Make our API request. + $response = $request->post( + $this->chatgpt_url, + [ + 'body' => wp_json_encode( $body ), + ] + ); + + set_transient( 'classifai_openai_chatgpt_latest_response', $response, DAY_IN_SECONDS * 30 ); + + if ( is_wp_error( $response ) ) { + return $response; + } + + if ( empty( $response['choices'] ) ) { + return new WP_Error( 'no_choices', esc_html__( 'No choices were returned from OpenAI.', 'classifai' ) ); + } + + // Extract out the text response. + $return = []; + foreach ( $response['choices'] as $choice ) { + if ( isset( $choice['message'], $choice['message']['content'] ) ) { + // ChatGPT often adds quotes to strings, so remove those as well as extra spaces. + $return[] = sanitize_text_field( trim( $choice['message']['content'], ' "\'' ) ); + } + } + + return $return; + } + + /** + * Resizes content. + * + * @param int $post_id The Post Id we're processing + * @param array $args Arguments passed in. + * @return string|WP_Error + */ + public function resize_content( int $post_id, array $args = array() ) { + if ( ! $post_id || ! get_post( $post_id ) ) { + return new WP_Error( 'post_id_required', esc_html__( 'Post ID is required to resize content.', 'classifai' ) ); + } + + $settings = $this->get_settings(); + $args = wp_parse_args( + array_filter( $args ), + [ + 'num' => $settings['number_resize_content'] ?? 1, + ] + ); + + $request = new APIRequest( $settings['api_key'] ?? '' ); + + if ( 'shrink' === $args['resize_type'] ) { + $prompt = 'Decrease the content length no more than 2 to 4 sentences.'; + } else { + $prompt = 'Increase the content length no more than 2 to 4 sentences.'; + } + + /** + * Filter the resize prompt we will send to ChatGPT. + * + * @since 2.3.0 + * + * @param {string} $prompt Resize prompt we are sending to ChatGPT. Gets added as a system prompt. + * @param {int} $post_id ID of post. + * @param {array} $args Arguments passed to endpoint. + * + * @return {string} Prompt. + */ + $prompt = apply_filters( 'classifai_chatgpt_' . $args['resize_type'] . '_content_prompt', $prompt, $post_id, $args ); + + /** + * Filter the resize request body before sending to ChatGPT. + * + * @since 2.3.0 + * @hook classifai_chatgpt_resize_content_request_body + * + * @param {array} $body Request body that will be sent to ChatGPT. + * @param {int} $post_id ID of post. + * + * @return {array} Request body. + */ + $body = apply_filters( + 'classifai_chatgpt_resize_content_request_body', + [ + 'model' => $this->chatgpt_model, + 'messages' => [ + [ + 'role' => 'system', + 'content' => $prompt, + ], [ 'role' => 'user', - 'content' => esc_html( $prompt ) . ': ' . $this->get_content( $post_id, absint( $args['num'] ) * 15, false ) . '', + 'content' => esc_html( $args['content'] ), ], ], 'temperature' => 0.9, @@ -679,6 +978,7 @@

Source: Providers/OpenAI/ChatGPT.php

// Extract out the text response. $return = []; + foreach ( $response['choices'] as $choice ) { if ( isset( $choice['message'], $choice['message']['content'] ) ) { // ChatGPT often adds quotes to strings, so remove those as well as extra spaces. @@ -692,12 +992,13 @@

Source: Providers/OpenAI/ChatGPT.php

/** * Get our content, trimming if needed. * - * @param int $post_id Post ID to get content from. - * @param int $return_length Word length of returned content. - * @param bool $use_title Whether to use the title or not. + * @param int $post_id Post ID to get content from. + * @param int $return_length Word length of returned content. + * @param bool $use_title Whether to use the title or not. + * @param string $post_content The post content. * @return string */ - public function get_content( int $post_id = 0, int $return_length = 0, bool $use_title = true ) { + public function get_content( int $post_id = 0, int $return_length = 0, bool $use_title = true, string $post_content = '' ) { $tokenizer = new Tokenizer( $this->max_tokens ); $normalizer = new Normalizer(); @@ -711,22 +1012,25 @@

Source: Providers/OpenAI/ChatGPT.php

/** * We then subtract those tokens from the max number of tokens ChatGPT allows * in a single request, as well as subtracting out the number of tokens in our - * prompt (13). ChatGPT counts both the tokens in the request and in + * prompt (~50). ChatGPT counts both the tokens in the request and in * the response towards the max. */ - $max_content_tokens = $this->max_tokens - $return_tokens - 13; + $max_content_tokens = $this->max_tokens - $return_tokens - 50; + + if ( empty( $post_content ) ) { + $post = get_post( $post_id ); + $post_content = apply_filters( 'the_content', $post->post_content ); + } + + $post_content = preg_replace( '#\[.+\](.+)\[/.+\]#', '$1', $post_content ); // Then trim our content, if needed, to stay under the max. if ( $use_title ) { $content = $tokenizer->trim_content( - $normalizer->normalize( $post_id ), + $normalizer->normalize( $post_id, $post_content ), (int) $max_content_tokens ); } else { - $post = get_post( $post_id ); - $post_content = apply_filters( 'the_content', $post->post_content ); - $post_content = preg_replace( '#\[.+\](.+)\[/.+\]#', '$1', $post_content ); - $content = $tokenizer->trim_content( $normalizer->normalize_content( $post_content, '', $post_id ), (int) $max_content_tokens @@ -766,7 +1070,7 @@

Source: Providers/OpenAI/ChatGPT.php


diff --git a/Providers_OpenAI_DallE.php.html b/Providers_OpenAI_DallE.php.html index 648685e75..0866fd6a1 100644 --- a/Providers_OpenAI_DallE.php.html +++ b/Providers_OpenAI_DallE.php.html @@ -90,29 +90,37 @@

Source: Providers/OpenAI/DallE.php

* This only fires if can_register returns true. */ public function register() { - $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'] ) - ) { + if ( $this->is_feature_enabled() ) { + add_action( 'admin_menu', [ $this, 'register_generate_media_page' ], 0 ); add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_admin_scripts' ] ); add_action( 'print_media_templates', [ $this, 'print_media_templates' ] ); } } + /** + * Registers a Media > Generate Image submenu + */ + public function register_generate_media_page() { + $settings = $this->get_settings(); + $number_of_images = absint( $settings['number'] ); + + add_submenu_page( + 'upload.php', + $number_of_images > 1 ? esc_html__( 'Generate Images', 'classifai' ) : esc_html__( 'Generate Image', 'classifai' ), + $number_of_images > 1 ? esc_html__( 'Generate Images', 'classifai' ) : esc_html__( 'Generate Image', 'classifai' ), + 'upload_files', + esc_url( admin_url( 'upload.php?action=classifai-generate-image' ) ), + '' + ); + } + /** * Enqueue the admin scripts. * * @param string $hook_suffix The current admin page. */ public function enqueue_admin_scripts( $hook_suffix = '' ) { - if ( 'post.php' !== $hook_suffix && 'post-new.php' !== $hook_suffix ) { + if ( 'post.php' !== $hook_suffix && 'post-new.php' !== $hook_suffix && 'upload.php' !== $hook_suffix ) { return; } @@ -137,6 +145,14 @@

Source: Providers/OpenAI/DallE.php

true ); + wp_enqueue_script( + 'classifai-inserter-media-category', + CLASSIFAI_PLUGIN_URL . 'dist/inserter-media-category.js', + get_asset_info( 'inserter-media-category', 'dependencies' ), + get_asset_info( 'inserter-media-category', 'version' ), + true + ); + /** * Filter the default attribution added to generated images. * @@ -167,6 +183,28 @@

Source: Providers/OpenAI/DallE.php

'caption' => $caption, ] ); + + if ( 'upload.php' === $hook_suffix ) { + $action = isset( $_GET['action'] ) ? sanitize_key( wp_unslash( $_GET['action'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended + + if ( 'classifai-generate-image' === $action ) { + wp_enqueue_script( + 'classifai-generate-images-media-upload', + CLASSIFAI_PLUGIN_URL . 'dist/generate-image-media-upload.js', + [ 'jquery' ], + get_asset_info( 'classifai-generate-images-media-upload', 'version' ), + true + ); + + wp_localize_script( + 'classifai-generate-images-media-upload', + 'classifaiGenerateImages', + [ + 'upload_url' => esc_url( admin_url( 'upload.php' ) ), + ] + ); + } + } } /** @@ -278,6 +316,19 @@

Source: Providers/OpenAI/DallE.php

); $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' ), @@ -377,7 +428,7 @@

Source: Providers/OpenAI/DallE.php

* * @return array */ - private function get_default_settings() { + public function get_default_settings() { return [ 'authenticated' => false, 'api_key' => '', @@ -437,7 +488,7 @@

Source: Providers/OpenAI/DallE.php

// These checks already ran in the REST permission_callback, // but we run them again here in case this method is called directly. - if ( ! current_user_can( 'upload_files' ) ) { + if ( ! $this->is_feature_enabled() ) { // Note that we purposely leave off the textdomain here as this is the same error // message core uses, so we want translations to load from there. return new WP_Error( 'rest_forbidden', esc_html__( 'Sorry, you are not allowed to do that.' ) ); @@ -516,6 +567,41 @@

Source: Providers/OpenAI/DallE.php

return $response; } + /** + * Checks whether we can generate images. + * + * @return bool + */ + public function is_feature_enabled() { + $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'] ) + ) { + $access = true; + } + + /** + * Filter to override permission to use the image gen feature. + * + * @since 2.3.0 + * @hook classifai_openai_dalle_enable_image_gen + * + * @param {bool} $access Current access value. + * @param {array} $settings Feature settings. + * + * @return {bool} Should the user have access? + */ + return apply_filters( 'classifai_openai_dalle_enable_image_gen', $access, $settings ); + } + } @@ -535,7 +621,7 @@

Source: Providers/OpenAI/DallE.php


diff --git a/Providers_OpenAI_Embeddings.php.html b/Providers_OpenAI_Embeddings.php.html index 1b290e9bc..60a94af2f 100644 --- a/Providers_OpenAI_Embeddings.php.html +++ b/Providers_OpenAI_Embeddings.php.html @@ -315,7 +315,7 @@

Source: Providers/OpenAI/Embeddings.php

* * @return array */ - private function get_default_settings() { + public function get_default_settings() { return [ 'authenticated' => false, 'api_key' => '', @@ -477,7 +477,7 @@

Source: Providers/OpenAI/Embeddings.php

'taxonomy' => $tax, 'hide_empty' => false, 'fields' => 'ids', - 'meta_key' => 'classifai_openai_embeddings', + 'meta_key' => 'classifai_openai_embeddings', // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key // 'number' => 500, TODO: see if we need a limit here. ] ); @@ -532,7 +532,7 @@

Source: Providers/OpenAI/Embeddings.php

'taxonomy' => $taxonomy, 'hide_empty' => false, 'fields' => 'ids', - 'meta_key' => 'classifai_openai_embeddings', + 'meta_key' => 'classifai_openai_embeddings', // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key 'meta_compare' => 'NOT EXISTS', // 'number' => 500, TODO: see if we need a limit here. ] @@ -852,7 +852,7 @@

Source: Providers/OpenAI/Embeddings.php


diff --git a/Providers_OpenAI_OpenAI.php.html b/Providers_OpenAI_OpenAI.php.html index 793e22ade..07db4cf17 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_Whisper_Transcribe.php.html b/Providers_OpenAI_Whisper_Transcribe.php.html index db231640f..3ccdc2be3 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/Services_Service.php.html b/Services_Service.php.html index 08bf30b51..40c5541d2 100644 --- a/Services_Service.php.html +++ b/Services_Service.php.html @@ -141,7 +141,7 @@

Source: Services/Service.php

*/ public function render_settings_page() { $active_tab = $this->provider_classes ? $this->provider_classes[0]->get_settings_section() : ''; - $active_tab = isset( $_GET['provider'] ) ? sanitize_text_field( $_GET['provider'] ) : $active_tab; // phpcs:ignore WordPress.Security.NonceVerification.Recommended + $active_tab = isset( $_GET['provider'] ) ? sanitize_text_field( wp_unslash( $_GET['provider'] ) ) : $active_tab; // phpcs:ignore WordPress.Security.NonceVerification.Recommended $base_url = add_query_arg( array( 'page' => 'classifai', @@ -322,7 +322,7 @@

Source: Services/Service.php


diff --git a/Watson_Classifier.php.html b/Watson_Classifier.php.html index 6dc6af10b..c95230cf8 100644 --- a/Watson_Classifier.php.html +++ b/Watson_Classifier.php.html @@ -77,7 +77,7 @@

Source: Watson/Classifier.php

/** * Classifies the text specified using IBM Watson NLU API. * - * https://www.ibm.com/watson/developercloud/natural-language-understanding/api/v1/#post-analyze + * https://cloud.ibm.com/apidocs/natural-language-understanding#analyze * * @param string $text The plain text to classify * @param array $options NLU classification options @@ -177,7 +177,7 @@

Source: Watson/Classifier.php


diff --git a/Watson_Normalizer.php.html b/Watson_Normalizer.php.html index 49d35a013..3a48f3c15 100644 --- a/Watson_Normalizer.php.html +++ b/Watson_Normalizer.php.html @@ -50,12 +50,13 @@

Source: Watson/Normalizer.php

* The post title is also included in the content to improve * accuracy. * - * @param int $post_id The post to normalize + * @param int $post_id The post to normalize + * @param string $post_content The post content to normalize * @return string */ - public function normalize( $post_id ) { + public function normalize( $post_id, $post_content = '' ) { $post = get_post( $post_id ); - $post_content = apply_filters( 'the_content', $post->post_content ); + $post_content = empty( $post_content ) ? apply_filters( 'the_content', $post->post_content ) : $post_content; $post_title = apply_filters( 'the_title', $post->post_title ); /* Strip shortcodes but keep internal caption text */ @@ -126,7 +127,7 @@

Source: Watson/Normalizer.php


diff --git a/after_classifai_init.html b/after_classifai_init.html index 3e454488d..ed9c90ac7 100644 --- a/after_classifai_init.html +++ b/after_classifai_init.html @@ -143,7 +143,7 @@

do_action( 'after_classifai_init' )
diff --git a/before_classifai_init.html b/before_classifai_init.html index 32cd7bdb8..2ca3fa12c 100644 --- a/before_classifai_init.html +++ b/before_classifai_init.html @@ -143,7 +143,7 @@

do_action( 'before_classifai_init' )
diff --git a/classifai_all_post_statuses.html b/classifai_all_post_statuses.html index 9a89d9bc4..5c0ad434f 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_audio_generation_initial_state.html b/classifai_audio_generation_initial_state.html new file mode 100644 index 000000000..2b113df68 --- /dev/null +++ b/classifai_audio_generation_initial_state.html @@ -0,0 +1,248 @@ + + + + + Filter: classifai_audio_generation_initial_state - 10up ClassifAI Hook Docs + + + + + + + + + + + + + +
+ + +

Filter: classifai_audio_generation_initial_state

+ + + + + + + +
+ +
+

+ +
+ +
+
+ + + + + +

apply_filters( 'classifai_audio_generation_initial_state', $state, $post ) → {bool}

+ + + + + +
+

Initial state of the audio generation toggle when no audio already exists for the post.

+
+ + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
$state + + +bool + + + +

Initial state of audio generation toggle on a post. Default true.

$post + + +WP_Post + + + +

The current Post object.

+ + + + + + +
+ + + + +
Since:
+
  • 2.3.0
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + +
+

Initial state the audio generation toggle should be set to when no audio exists.

+
+ + + +
+
+ Type +
+
+ +bool + + +
+
+ + + + + + +
+ + +
+ +
+ + + + + + + + +
+ + + +
+ + + + + \ No newline at end of file diff --git a/classifai_audio_generation_subsequent_state.html b/classifai_audio_generation_subsequent_state.html new file mode 100644 index 000000000..766d84892 --- /dev/null +++ b/classifai_audio_generation_subsequent_state.html @@ -0,0 +1,248 @@ + + + + + Filter: classifai_audio_generation_subsequent_state - 10up ClassifAI Hook Docs + + + + + + + + + + + + + +
+ + +

Filter: classifai_audio_generation_subsequent_state

+ + + + + + + +
+ +
+

+ +
+ +
+
+ + + + + +

apply_filters( 'classifai_audio_generation_subsequent_state', $state, $post ) → {bool}

+ + + + + +
+

Subsequent state of the audio generation toggle when audio exists for the post.

+
+ + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
$state + + +bool + + + +

Subsequent state of audio generation toggle on a post. Default false.

$post + + +WP_Post + + + +

The current Post object.

+ + + + + + +
+ + + + +
Since:
+
  • 2.3.0
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + +
+

Subsequent state the audio generation toggle should be set to when audio exists.

+
+ + + +
+
+ Type +
+
+ +bool + + +
+
+ + + + + + +
+ + +
+ +
+ + + + + + + + +
+ + + +
+ + + + + \ No newline at end of file diff --git a/classifai_azure_read_after_request.html b/classifai_azure_read_after_request.html index 83d5a5cf9..0267de79b 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_result_max_page.html b/classifai_azure_read_result_max_page.html index 8a01ff091..a3804e6cb 100644 --- a/classifai_azure_read_result_max_page.html +++ b/classifai_azure_read_result_max_page.html @@ -144,7 +144,7 @@
Parameters:
Source:
@@ -214,7 +214,7 @@
Returns:

diff --git a/classifai_azure_read_retry_interval.html b/classifai_azure_read_retry_interval.html index 66c88a93e..d251dcd5d 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 910872aea..b65cb711f 100644 --- a/classifai_azure_read_should_process.html +++ b/classifai_azure_read_should_process.html @@ -437,7 +437,7 @@
Returns:

diff --git a/classifai_azure_read_text_result.html b/classifai_azure_read_text_result.html index 9ea3a8b33..203083167 100644 --- a/classifai_azure_read_text_result.html +++ b/classifai_azure_read_text_result.html @@ -190,7 +190,7 @@
Parameters:
Source:
@@ -260,7 +260,7 @@
Returns:

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

Filter: classifai_chatgpt_allowed_roles

+ + + + + + + +
+ +
+

+ +
+ +
+
+ + + + + +

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

+ + + + + +
+

Filter the allowed WordPress roles for ChatGTP

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

Array of arrays containing role information.

$default_settings + + +array + + + +

Default setting values.

+ + + + + + +
+ + + + +
Since:
+
  • 2.3.0
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + +
+

Roles array.

+
+ + + +
+
+ Type +
+
+ +array + + +
+
+ + + + + + +
+ + +
+ +
+ + + + + + + + +
+ + + +
+ + + + + \ No newline at end of file diff --git a/classifai_chatgpt_content.html b/classifai_chatgpt_content.html index d8c9fe4af..fd22c8c66 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 4fc0fc517..c5a3fb4ed 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 014932ea4..50c94d56a 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 new file mode 100644 index 000000000..d9a9effdb --- /dev/null +++ b/classifai_chatgpt_resize_content_request_body.html @@ -0,0 +1,248 @@ + + + + + Filter: classifai_chatgpt_resize_content_request_body - 10up ClassifAI Hook Docs + + + + + + + + + + + + + +
+ + +

Filter: classifai_chatgpt_resize_content_request_body

+ + + + + + + +
+ +
+

+ +
+ +
+
+ + + + + +

apply_filters( 'classifai_chatgpt_resize_content_request_body', $body, $post_id ) → {array}

+ + + + + +
+

Filter the resize request body before sending to ChatGPT.

+
+ + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
$body + + +array + + + +

Request body that will be sent to ChatGPT.

$post_id + + +int + + + +

ID of post.

+ + + + + + +
+ + + + +
Since:
+
  • 2.3.0
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + +
+

Request body.

+
+ + + +
+
+ Type +
+
+ +array + + +
+
+ + + + + + +
+ + +
+ +
+ + + + + + + + +
+ + + +
+ + + + + \ No newline at end of file diff --git a/classifai_chatgpt_title_prompt.html b/classifai_chatgpt_title_prompt.html index e742ef205..9d2c2df0f 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 439327ef1..dd742713f 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 9947fe0ea..462b159fa 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 7eb758601..7b15c710b 100644 --- a/classifai_computer_vision_caption_failed.html +++ b/classifai_computer_vision_caption_failed.html @@ -215,7 +215,7 @@
Parameters:

diff --git a/classifai_computer_vision_captions.html b/classifai_computer_vision_captions.html index ff3638068..528d85a7c 100644 --- a/classifai_computer_vision_captions.html +++ b/classifai_computer_vision_captions.html @@ -214,7 +214,7 @@
Returns:

diff --git a/classifai_computer_vision_image_tag_failed.html b/classifai_computer_vision_image_tag_failed.html index f87142b5c..eaebeabd6 100644 --- a/classifai_computer_vision_image_tag_failed.html +++ b/classifai_computer_vision_image_tag_failed.html @@ -215,7 +215,7 @@
Parameters:

diff --git a/classifai_computer_vision_image_tags.html b/classifai_computer_vision_image_tags.html index 731e17bf9..5e6ca50a5 100644 --- a/classifai_computer_vision_image_tags.html +++ b/classifai_computer_vision_image_tags.html @@ -214,7 +214,7 @@
Returns:

diff --git a/classifai_computer_vision_max_filesize.html b/classifai_computer_vision_max_filesize.html index f70a4ab48..3c8b9fb9d 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 659304382..61ac946c7 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 aba209ead..019f24f47 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 9a2ce7219..2767a49ec 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 a8532818b..15b9657b3 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 244d3971d..3c4110441 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 11abf9d12..63c586d0b 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 2a0c306cf..6cca51cf8 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_language_settings_post_statuses.html b/classifai_language_settings_post_statuses.html index a3b31e092..6738bbc5e 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 dd7213066..c46debc0b 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 81d7aef29..d39c81b20 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 1f87ce408..af8e13dd2 100644 --- a/classifai_normalize.html +++ b/classifai_normalize.html @@ -167,7 +167,7 @@
Parameters:
Source:
@@ -237,7 +237,7 @@
Returns:

diff --git a/classifai_ocr_after_request.html b/classifai_ocr_after_request.html index 19aea59c3..b170a0429 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 dd3ad56d3..725fd5c53 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 fff8efeb2..5b25681a2 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 7b62a2056..75ac9b53a 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 7436ec1e9..93865b0a1 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 d72654ccc..7a81646d0 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 29366f654..8841129a8 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 c74c8f207..403e9f888 100644 --- a/classifai_ocr_unsuccessful_response.html +++ b/classifai_ocr_unsuccessful_response.html @@ -218,7 +218,7 @@
Parameters:

diff --git a/classifai_openai_chatgpt_%7B$feature%7D.html b/classifai_openai_chatgpt_%7B$feature%7D.html new file mode 100644 index 000000000..555513ffc --- /dev/null +++ b/classifai_openai_chatgpt_%7B$feature%7D.html @@ -0,0 +1,248 @@ + + + + + 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 new file mode 100644 index 000000000..bce767b5b --- /dev/null +++ b/classifai_openai_dalle_allowed_image_roles.html @@ -0,0 +1,248 @@ + + + + + Filter: classifai_openai_dalle_allowed_image_roles - 10up ClassifAI Hook Docs + + + + + + + + + + + + + +
+ + +

Filter: classifai_openai_dalle_allowed_image_roles

+ + + + + + + +
+ +
+

+ +
+ +
+
+ + + + + +

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

+ + + + + +
+

Filter the allowed WordPress roles for DALL·E

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

Array of arrays containing role information.

$default_settings + + +array + + + +

Default setting values.

+ + + + + + +
+ + + + +
Since:
+
  • 2.3.0
+ + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + +
+

Roles array.

+
+ + + +
+
+ Type +
+
+ +array + + +
+
+ + + + + + +
+ + +
+ +
+ + + + + + + + +
+ + + +
+ + + + + \ No newline at end of file diff --git a/classifai_openai_dalle_enable_image_gen.html b/classifai_openai_dalle_enable_image_gen.html new file mode 100644 index 000000000..ca603442a --- /dev/null +++ b/classifai_openai_dalle_enable_image_gen.html @@ -0,0 +1,248 @@ + + + + + Filter: classifai_openai_dalle_enable_image_gen - 10up ClassifAI Hook Docs + + + + + + + + + + + + + +
+ + +

Filter: classifai_openai_dalle_enable_image_gen

+ + + + + + + +
+ +
+

+ +
+ +
+
+ + + + + +

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

+ + + + + +
+

Filter to override permission to use the image gen feature.

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

Current access value.

$settings + + +array + + + +

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_embeddings_content.html b/classifai_openai_embeddings_content.html index 5e1cc071a..3e3bf9a97 100644 --- a/classifai_openai_embeddings_content.html +++ b/classifai_openai_embeddings_content.html @@ -260,7 +260,7 @@
Returns:

diff --git a/classifai_openai_embeddings_post_statuses.html b/classifai_openai_embeddings_post_statuses.html index ef0aed326..c05890016 100644 --- a/classifai_openai_embeddings_post_statuses.html +++ b/classifai_openai_embeddings_post_statuses.html @@ -214,7 +214,7 @@
Returns:

diff --git a/classifai_openai_embeddings_request_body.html b/classifai_openai_embeddings_request_body.html index fcfd2b02e..d09e7d59c 100644 --- a/classifai_openai_embeddings_request_body.html +++ b/classifai_openai_embeddings_request_body.html @@ -260,7 +260,7 @@
Returns:

diff --git a/classifai_openai_embeddings_should_classify.html b/classifai_openai_embeddings_should_classify.html index ddbefd2b4..fef286491 100644 --- a/classifai_openai_embeddings_should_classify.html +++ b/classifai_openai_embeddings_should_classify.html @@ -261,7 +261,7 @@
Returns:

diff --git a/classifai_openai_embeddings_taxonomies.html b/classifai_openai_embeddings_taxonomies.html index 10324636e..f9a6a9cf9 100644 --- a/classifai_openai_embeddings_taxonomies.html +++ b/classifai_openai_embeddings_taxonomies.html @@ -214,7 +214,7 @@
Returns:

diff --git a/classifai_openai_settings_post_statuses.html b/classifai_openai_settings_post_statuses.html index bf2787b73..1fc3ca1d4 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 f54e3cc8a..41ecccbf7 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 e38844255..1253cddf9 100644 --- a/classifai_openai_settings_taxonomies.html +++ b/classifai_openai_settings_taxonomies.html @@ -237,7 +237,7 @@
Returns:

diff --git a/classifai_post_statuses.html b/classifai_post_statuses.html index 5ff3ce7d7..b7d4d4630 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 108249e7e..a6c6afd82 100644 --- a/classifai_post_statuses_for_post_type_or_id.html +++ b/classifai_post_statuses_for_post_type_or_id.html @@ -260,7 +260,7 @@
Returns:

diff --git a/classifai_post_types.html b/classifai_post_types.html index 269c07014..b64ff93ba 100644 --- a/classifai_post_types.html +++ b/classifai_post_types.html @@ -144,7 +144,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 29da83c09..3cbec8938 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 3e69c8fe1..c6200eed6 100644 --- a/classifai_recommended_block_attributes.html +++ b/classifai_recommended_block_attributes.html @@ -214,7 +214,7 @@
Returns:

diff --git a/classifai_recommended_block_markup.html b/classifai_recommended_block_markup.html index eb9b8a43d..a8bc18ada 100644 --- a/classifai_recommended_block_markup.html +++ b/classifai_recommended_block_markup.html @@ -283,7 +283,7 @@
Returns:

diff --git a/classifai_recommended_content_post_args.html b/classifai_recommended_content_post_args.html index 44858eee2..96f086bfe 100644 --- a/classifai_recommended_content_post_args.html +++ b/classifai_recommended_content_post_args.html @@ -237,7 +237,7 @@
Returns:

diff --git a/classifai_rest_bases.html b/classifai_rest_bases.html index efd3e13f5..54730cdeb 100644 --- a/classifai_rest_bases.html +++ b/classifai_rest_bases.html @@ -214,7 +214,7 @@
Returns:

diff --git a/classifai_services.html b/classifai_services.html index 8112c0f23..fe5d902a8 100644 --- a/classifai_services.html +++ b/classifai_services.html @@ -214,7 +214,7 @@
Returns:

diff --git a/classifai_should_classify_post.html b/classifai_should_classify_post.html index 50affb9fa..c21d5d9c5 100644 --- a/classifai_should_classify_post.html +++ b/classifai_should_classify_post.html @@ -239,7 +239,7 @@
Returns:

diff --git a/classifai_should_crop_size.html b/classifai_should_crop_size.html index fc581bdab..a60c2a4d8 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 17d6d2aa7..d6ebdf4f0 100644 --- a/classifai_should_ocr_scan_image.html +++ b/classifai_should_ocr_scan_image.html @@ -260,7 +260,7 @@
Returns:

diff --git a/classifai_should_register_save_post_handler.html b/classifai_should_register_save_post_handler.html index c85d236ad..cdf40afb3 100644 --- a/classifai_should_register_save_post_handler.html +++ b/classifai_should_register_save_post_handler.html @@ -214,7 +214,7 @@
Returns:

diff --git a/classifai_should_smart_crop_image.html b/classifai_should_smart_crop_image.html index a91707690..48dd1ac03 100644 --- a/classifai_should_smart_crop_image.html +++ b/classifai_should_smart_crop_image.html @@ -260,7 +260,7 @@
Returns:

diff --git a/classifai_smart_crop_max_pixel_dimension.html b/classifai_smart_crop_max_pixel_dimension.html index 410fe4547..e4289cf2b 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 cce6d993a..2cf165db4 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 c9e3fd492..8a9aa3dbd 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 31e842fca..700c055dc 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 482222795..3811b6b1c 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 63abcab83..e8ef71c06 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 67808c565..6c16beb03 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_whisper_transcribe_request_body.html b/classifai_whisper_transcribe_request_body.html index 66937a9bc..b39d75dd0 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 ea7142e34..61588348a 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 49edcb7ad..de1b86e50 100644 --- a/index.html +++ b/index.html @@ -119,7 +119,7 @@


diff --git a/tutorial-wp-cli.html b/tutorial-wp-cli.html index a9b715293..80741c9ad 100644 --- a/tutorial-wp-cli.html +++ b/tutorial-wp-cli.html @@ -286,7 +286,7 @@

Image Processing Commands