diff --git a/.wp-env.json b/.wp-env.json index 7881ddaa8..d3be216b9 100644 --- a/.wp-env.json +++ b/.wp-env.json @@ -1,10 +1,13 @@ { - "plugins": [".", "./tests/test-plugin"], + "plugins": [".", "./tests/test-plugin", "https://downloads.wordpress.org/plugin/classic-editor.zip"], "env": { "tests": { "mappings": { "wp-cli.yml": "./tests/bin/wp-cli.yml" } } + }, + "lifecycleScripts": { + "afterStart": "bash ./tests/bin/initialize.sh" } } diff --git a/includes/Classifai/Providers/OpenAI/ChatGPT.php b/includes/Classifai/Providers/OpenAI/ChatGPT.php index 835ab4f4d..3e22488f4 100644 --- a/includes/Classifai/Providers/OpenAI/ChatGPT.php +++ b/includes/Classifai/Providers/OpenAI/ChatGPT.php @@ -69,6 +69,27 @@ public function __construct( $service ) { public function register() { add_action( 'enqueue_block_editor_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' ] ); + } + + /** + * Returns localised data for title generation. + */ + public function get_localised_vars() { + global $post; + + return [ + 'enabledFeatures' => [ + 0 => [ + 'feature' => 'title', + 'path' => '/classifai/v1/openai/generate-title/', + 'buttonText' => __( 'Generate titles', 'classifai' ), + 'modalTitle' => __( 'Select a title', 'classifai' ), + 'selectBtnText' => __( 'Select', 'classifai' ), + ], + ], + 'noPermissions' => ! is_user_logged_in() || ! current_user_can( 'edit_post', $post->ID ), + ]; } /** @@ -117,19 +138,7 @@ public function enqueue_editor_assets() { 'classifai-post-status-info', sprintf( 'var classifaiChatGPTData = %s;', - wp_json_encode( - [ - 'enabledFeatures' => [ - 0 => [ - 'feature' => 'title', - 'path' => '/classifai/v1/openai/generate-title/', - 'buttonText' => __( 'Generate titles', 'classifai' ), - 'modalTitle' => __( 'Select a title', 'classifai' ), - ], - ], - 'noPermissions' => ! is_user_logged_in() || ! current_user_can( 'edit_post', $post->ID ), - ] - ) + wp_json_encode( $this->get_localised_vars() ) ), 'before' ); @@ -146,6 +155,43 @@ public function enqueue_admin_assets( $hook_suffix = '' ) { return; } + $screen = get_current_screen(); + $settings = $this->get_settings(); + $user_roles = wp_get_current_user()->roles ?? []; + $title_roles = $settings['title_roles'] ?? []; + + // 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 + ); + + wp_add_inline_script( + 'classifai-generate-title-classic-js', + sprintf( + 'var classifaiChatGPTData = %s;', + wp_json_encode( $this->get_localised_vars() ) + ), + 'before' + ); + } + wp_enqueue_style( 'classifai-language-processing-style', CLASSIFAI_PLUGIN_URL . 'dist/language-processing.css', @@ -155,6 +201,23 @@ public function enqueue_admin_assets( $hook_suffix = '' ) { ); } + /** + * HTML template for title generation result popup. + */ + public function register_generated_titles_template() { + ?> + + ( { [ cur.feature ]: cur } ), + {} +); + +( function ( $ ) { + $( document ).ready( () => { + if ( scriptData?.title ) { + generateTitleInit(); + } + } ); + + /** + * This function is solely responsibe for rendering, generating + * and applying the generated title for the classic editor. + */ + function generateTitleInit() { + // Boolean indicating whether title generation is in progress. + let isProcessing = false; + + // Creates and appens the "Generate titles" button. + $( '', { + text: scriptData?.title?.buttonText ?? '', + class: 'classifai-openai__title-generate-btn--text', + } ) + .wrap( + '
' + ) + .parent() + .append( + $( '', { + class: 'classifai-openai__title-generate-btn--spinner', + } ) + ) + .appendTo( '#titlewrap' ); + + // The current post ID. + const postId = $( '#post_ID' ).val(); + + // Callback to hide the popup. + const hidePopup = () => { + $( '#classifai-openai__results' ) + .removeClass( 'classifai-openai--fade-in' ) + .delay( 300 ) + .fadeOut( 0 ); + }; + + // Callback to apply the title from the result to the post title. + const applyTitle = ( e ) => { + const selectBtnEl = $( e.target ); + const textarea = selectBtnEl + .closest( '.classifai-openai__result-item' ) + .find( 'textarea' ); + + $( '#title' ).val( textarea.val() ).trigger( 'input' ); + hidePopup(); + }; + + // Callback to generate the title. + const generateTitle = () => { + if ( isProcessing ) { + return; + } + + $( '#classifai-openai__results-content' ).html( '' ); + const generateTextEl = $( + '.classifai-openai__title-generate-btn--text' + ); + const spinnerEl = $( + '.classifai-openai__title-generate-btn--spinner' + ); + + generateTextEl.css( 'opacity', '0' ); + spinnerEl.show(); + isProcessing = true; + + const path = scriptData.title?.path + postId; + + apiFetch( { + path, + } ).then( ( result ) => { + generateTextEl.css( 'opacity', '1' ); + spinnerEl.hide(); + isProcessing = false; + + result.forEach( ( title ) => { + $( '