diff --git a/includes/Classifai/Providers/OpenAI/ChatGPT.php b/includes/Classifai/Providers/OpenAI/ChatGPT.php index 76707db4d..97222347b 100644 --- a/includes/Classifai/Providers/OpenAI/ChatGPT.php +++ b/includes/Classifai/Providers/OpenAI/ChatGPT.php @@ -6,11 +6,9 @@ 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 { @@ -192,35 +190,72 @@ public function enqueue_admin_assets( $hook_suffix = '' ) { $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 - ); + 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( diff --git a/package-lock.json b/package-lock.json index c6b546728..bbf68ebe1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18042,9 +18042,9 @@ "dev": true }, "node_modules/typescript": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.3.tgz", - "integrity": "sha512-XH627E9vkeqhlZFQuL+UsyAXEnibT0kWR2FWONlr4sTjvxyJYnyefgrkyECLzM5NenmKzRAy2rR/OlYLA1HkZw==", + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", + "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==", "dev": true, "peer": true, "bin": { diff --git a/src/js/openai/classic-editor-excerpt-generator.js b/src/js/openai/classic-editor-excerpt-generator.js new file mode 100644 index 000000000..43aa29391 --- /dev/null +++ b/src/js/openai/classic-editor-excerpt-generator.js @@ -0,0 +1,84 @@ +import apiFetch from '@wordpress/api-fetch'; +import '../../scss/openai/classic-editor-title-generator.scss'; + +const classifaiExcerptData = window.classifaiGenerateExcerpt || {}; + +( function ( $ ) { + $( document ).ready( () => { + if ( document.getElementById( 'postexcerpt' ) ) { + generateExcerptInit(); + } + } ); + + /** + * This function is solely responsibe for rendering, generating + * and applying the generated excerpt in the classic editor. + */ + function generateExcerptInit() { + const excerptContainer = document.getElementById( 'excerpt' ); + + // Boolean indicating whether generation is in progress. + let isProcessing = false; + + // Creates and appends the "Generate excerpt" button. + $( '', { + text: excerptContainer.value + ? classifaiExcerptData?.regenerateText ?? '' + : classifaiExcerptData?.buttonText ?? '', + class: 'classifai-openai__excerpt-generate-btn--text', + } ) + .wrap( + '
' + ) + .parent() + .append( + $( '', { + class: 'classifai-openai__excerpt-generate-btn--spinner', + } ) + ) + .insertAfter( excerptContainer ); + + // The current post ID. + const postId = $( '#post_ID' ).val(); + + // Callback to generate the excerpt. + const generateExcerpt = () => { + if ( isProcessing ) { + return; + } + + const generateTextEl = $( + '.classifai-openai__excerpt-generate-btn--text' + ); + const spinnerEl = $( + '.classifai-openai__excerpt-generate-btn--spinner' + ); + + generateTextEl.css( 'opacity', '0' ); + spinnerEl.show(); + isProcessing = true; + + const path = classifaiExcerptData?.path + postId; + + apiFetch( { + path, + } ).then( ( result ) => { + generateTextEl.css( 'opacity', '1' ); + spinnerEl.hide(); + isProcessing = false; + + $( excerptContainer ).val( result ).trigger( 'input' ); + generateTextEl.text( + classifaiExcerptData?.regenerateText ?? '' + ); + } ); + }; + + // Event handler registration to generate the excerpt. + $( document ).on( + 'click', + '#classifai-openai__excerpt-generate-btn', + generateExcerpt + ); + } +} )( jQuery ); diff --git a/src/scss/admin.scss b/src/scss/admin.scss index 267232cac..911c6ff4b 100644 --- a/src/scss/admin.scss +++ b/src/scss/admin.scss @@ -1,55 +1,55 @@ -:root{ - --classifai-admin-theme-color:#007cba; - --classifai-admin-theme-color--rgb:0, 124, 186; - --classifai-admin-theme-color-darker-10:#006ba1; +:root { + --classifai-admin-theme-color: #007cba; + --classifai-admin-theme-color--rgb: 0, 124, 186; + --classifai-admin-theme-color-darker-10: #006ba1; } -body.admin-color-light{ - --classifai-admin-theme-color:#0085ba; - --classifai-admin-theme-color--rgb:0, 133, 186; - --classifai-admin-theme-color-darker-10:#0073a1; +body.admin-color-light { + --classifai-admin-theme-color: #0085ba; + --classifai-admin-theme-color--rgb: 0, 133, 186; + --classifai-admin-theme-color-darker-10: #0073a1; } -body.admin-color-modern{ - --classifai-admin-theme-color:#3858e9; - --classifai-admin-theme-color--rgb:56, 88, 233; - --classifai-admin-theme-color-darker-10:#2145e6; +body.admin-color-modern { + --classifai-admin-theme-color: #3858e9; + --classifai-admin-theme-color--rgb: 56, 88, 233; + --classifai-admin-theme-color-darker-10: #2145e6; } -body.admin-color-blue{ - --classifai-admin-theme-color:#096484; - --classifai-admin-theme-color--rgb:9, 100, 132; - --classifai-admin-theme-color-darker-10:#07526c; +body.admin-color-blue { + --classifai-admin-theme-color: #096484; + --classifai-admin-theme-color--rgb: 9, 100, 132; + --classifai-admin-theme-color-darker-10: #07526c; } -body.admin-color-coffee{ - --classifai-admin-theme-color:#46403c; - --classifai-admin-theme-color--rgb:70, 64, 60; - --classifai-admin-theme-color-darker-10:#383330; +body.admin-color-coffee { + --classifai-admin-theme-color: #46403c; + --classifai-admin-theme-color--rgb: 70, 64, 60; + --classifai-admin-theme-color-darker-10: #383330; } -body.admin-color-ectoplasm{ - --classifai-admin-theme-color:#523f6d; - --classifai-admin-theme-color--rgb:82, 63, 109; - --classifai-admin-theme-color-darker-10:#46365d; +body.admin-color-ectoplasm { + --classifai-admin-theme-color: #523f6d; + --classifai-admin-theme-color--rgb: 82, 63, 109; + --classifai-admin-theme-color-darker-10: #46365d; } -body.admin-color-midnight{ - --classifai-admin-theme-color:#e14d43; - --classifai-admin-theme-color--rgb:225, 77, 67; - --classifai-admin-theme-color-darker-10:#dd382d; +body.admin-color-midnight { + --classifai-admin-theme-color: #e14d43; + --classifai-admin-theme-color--rgb: 225, 77, 67; + --classifai-admin-theme-color-darker-10: #dd382d; } -body.admin-color-ocean{ - --classifai-admin-theme-color:#627c83; - --classifai-admin-theme-color--rgb:98, 124, 131; - --classifai-admin-theme-color-darker-10:#576e74; +body.admin-color-ocean { + --classifai-admin-theme-color: #627c83; + --classifai-admin-theme-color--rgb: 98, 124, 131; + --classifai-admin-theme-color-darker-10: #576e74; } -body.admin-color-sunrise{ - --classifai-admin-theme-color:#dd823b; - --classifai-admin-theme-color--rgb:221, 130, 59; - --classifai-admin-theme-color-darker-10:#d97426; +body.admin-color-sunrise { + --classifai-admin-theme-color: #dd823b; + --classifai-admin-theme-color--rgb: 221, 130, 59; + --classifai-admin-theme-color-darker-10: #d97426; } #classifai-activation-notice { @@ -57,6 +57,7 @@ body.admin-color-sunrise{ .classifai-logo { line-height: 0; + img { width: 180px; } @@ -74,7 +75,7 @@ a.classifai-button, input.classifai-button { font-size: 14px; padding: 8px 16px; - background: var( --classifai-admin-theme-color ); + background: var(--classifai-admin-theme-color); color: #fff; font-style: normal; font-weight: 400; @@ -85,7 +86,7 @@ input.classifai-button { text-decoration: none; text-shadow: none; text-transform: uppercase; - border-color: var( --classifai-admin-theme-color ); + border-color: var(--classifai-admin-theme-color); border-radius: 4px; border-width: 1px; border-style: solid; @@ -94,12 +95,12 @@ input.classifai-button { &:focus, &:active { color: #fff; - background: var( --classifai-admin-theme-color-darker-10 ); - border-color: var( --classifai-admin-theme-color-darker-10 ); + background: var(--classifai-admin-theme-color-darker-10); + border-color: var(--classifai-admin-theme-color-darker-10); } &:focus { - box-shadow: 0 0 0 1px #fff, 0 0 0 3px var( --classifai-admin-theme-color-darker-10 ); + box-shadow: 0 0 0 1px #fff, 0 0 0 3px var(--classifai-admin-theme-color-darker-10); } &:active { @@ -145,6 +146,7 @@ input.classifai-button { .classifai-setup-page & { position: relative; + body.admin-bar & { @media (min-width: 601px) { top: 0px; @@ -165,6 +167,7 @@ input.classifai-button { #classifai-logo { line-height: 0; + img { max-width: 180px; } @@ -192,11 +195,11 @@ input.classifai-button { text-align: center; &[aria-expanded="true"] { - color: var( --classifai-admin-theme-color ); + color: var(--classifai-admin-theme-color); } &:hover { - color: var( --classifai-admin-theme-color ); + color: var(--classifai-admin-theme-color); } span.control-item-text { @@ -205,7 +208,7 @@ input.classifai-button { } } - .classifai-help-menu{ + .classifai-help-menu { display: block; width: 200px; @@ -216,12 +219,12 @@ input.classifai-button { padding: 8px 12px; &:hover { - color: var( --classifai-admin-theme-color ); + color: var(--classifai-admin-theme-color); } } } - .admin_page_classifai_setup &{ + .admin_page_classifai_setup & { box-shadow: none; } } @@ -269,16 +272,17 @@ input.classifai-button { left: 0px; right: 0px; border-radius: 3px 3px 0px 0px; - background: var( --classifai-admin-theme-color ); - box-shadow: 0px 4px 10px 3px rgba( var(--classifai-admin-theme-color--rgb), .15); + background: var(--classifai-admin-theme-color); + box-shadow: 0px 4px 10px 3px rgba(var(--classifai-admin-theme-color--rgb), .15); opacity: 0; transform: scale(0, 1); } &.nav-tab-active, &:hover { - color: var( --classifai-admin-theme-color ); + color: var(--classifai-admin-theme-color); font-weight: 600; + &:after { opacity: 1; transform: scale(1, 1); @@ -341,10 +345,13 @@ input.classifai-button { justify-content: center; } - a, a:hover, a:active, a:focus { + a, + a:hover, + a:active, + a:focus { display: inherit; text-decoration: none; - color: #3c434a; + color: #3c434a; } @media screen and (max-width: 480px) { @@ -354,7 +361,7 @@ input.classifai-button { &.is-complete { span.step-count { - background: var( --classifai-admin-theme-color ); + background: var(--classifai-admin-theme-color); color: #fff; } } @@ -365,7 +372,7 @@ input.classifai-button { } span.step-count { - background: var( --classifai-admin-theme-color ); + background: var(--classifai-admin-theme-color); color: #fff; } } @@ -450,7 +457,7 @@ input.classifai-button { margin-top: 20px; } - .classifai-step2-content{ + .classifai-step2-content { max-width: 480px; margin: 0 auto; } @@ -494,7 +501,7 @@ input.classifai-button { font-size: 14px; } - span.classifai-feature-text{ + span.classifai-feature-text { float: left; } @@ -513,6 +520,7 @@ input.classifai-button { } @media screen and (max-width: 767px) { + .classifai-step1-content, .classifai-step4-content { max-width: 100%; @@ -531,7 +539,7 @@ input.classifai-button { max-width: 480px; } - .classifai-setup-form-field{ + .classifai-setup-form-field { margin-top: 40px; } @@ -550,7 +558,7 @@ input.classifai-button { margin-bottom: 4px; } - .classifai-setup-footer{ + .classifai-setup-footer { margin-top: 40px; } } @@ -576,14 +584,17 @@ input.classifai-button { .classifai-tabs { display: block; + &.tabs-center { margin: auto; margin-bottom: 24px; } + &.tabs-justify { width: 100%; table-layout: fixed; } + a.tab { text-decoration: none; position: relative; @@ -595,12 +606,15 @@ input.classifai-button { white-space: nowrap; cursor: pointer; font-size: 14px; + &:focus { box-shadow: none; } + &:hover { - color: var( --classifai-admin-theme-color ); + color: var(--classifai-admin-theme-color); } + &:after { transition: all .3s cubic-bezier(1, 0, 0, 1); will-change: transform, box-shadow, opacity; @@ -611,14 +625,16 @@ input.classifai-button { left: 0px; right: 0px; border-radius: 3px 3px 0px 0px; - background: var( --classifai-admin-theme-color ); - box-shadow: 0px 4px 10px 3px rgba( var(--classifai-admin-theme-color--rgb), .15); + background: var(--classifai-admin-theme-color); + box-shadow: 0px 4px 10px 3px rgba(var(--classifai-admin-theme-color--rgb), .15); opacity: 0; transform: scale(0, 1); } + &.active { - color: var( --classifai-admin-theme-color ); + color: var(--classifai-admin-theme-color); font-weight: 600; + &:after { opacity: 1; transform: scale(1, 1); @@ -644,6 +660,7 @@ input.classifai-button { width: 100%; text-align: right; } + .classifai-toggle-switch { display: inline-block; background: transparent; @@ -655,10 +672,12 @@ input.classifai-button { vertical-align: middle; transition: background 0.25s; box-sizing: border-box; + &:before, &:after { content: ""; } + &:before { display: block; background: #1e1e1e; @@ -672,16 +691,24 @@ input.classifai-button { transition: left 0.25s; } - .classifai-toggle-checkbox:checked + & { + .classifai-toggle-checkbox:checked+& { background: var(--classifai-admin-theme-color); border-color: var(--classifai-admin-theme-color); + &:before { background-color: #fff; left: 20px; } } } + .classifai-toggle-checkbox { position: absolute; visibility: hidden; } + +#classifai-generate-excerpt { + position: absolute; + right: 12px; + top: 70px; +} diff --git a/src/scss/openai/classic-editor-title-generator.scss b/src/scss/openai/classic-editor-title-generator.scss index 0fab2c8ba..173e6a3e8 100644 --- a/src/scss/openai/classic-editor-title-generator.scss +++ b/src/scss/openai/classic-editor-title-generator.scss @@ -18,6 +18,26 @@ } } +#postexcerpt { + #classifai-openai__excerpt-generate-btn { + display: block; + width: 160px; + max-width: 100%; + margin: 1em 0 0 auto; + text-align: center; + position: relative; + + & > .classifai-openai__excerpt-generate-btn--spinner { + display: none; + position: absolute; + top: 50%; + left: 50%; + transform: translate( -50%, -50% ); + line-height: 29px; + } + } +} + #classifai-openai { &__overlay { position: fixed; @@ -108,7 +128,8 @@ } } - &__title-generate-btn--spinner { + &__title-generate-btn--spinner, + &__excerpt-generate-btn--spinner { &:before { display: block; animation: rotation 2s infinite linear; diff --git a/tests/cypress/integration/language-processing.test.js b/tests/cypress/integration/language-processing.test.js index afc78e181..5a8d0d63c 100644 --- a/tests/cypress/integration/language-processing.test.js +++ b/tests/cypress/integration/language-processing.test.js @@ -203,6 +203,39 @@ describe('Language processing Tests', () => { } ); } ); + it( 'Can see the generate excerpt button in a post (Classic Editor)', () => { + cy.visit( '/wp-admin/plugins.php' ); + cy.get( '#activate-classic-editor' ).click(); + + cy.visit( + '/wp-admin/tools.php?page=classifai&tab=language_processing&provider=openai_chatgpt' + ); + cy.get( '#enable_excerpt' ).check(); + cy.get( '#submit' ).click(); + + const data = getChatGPTData(); + + cy.classicCreatePost( { + title: 'Excerpt test classic', + content: 'Test GPT content.', + postType: 'post', + } ); + + // Ensure excerpt metabox is shown. + cy.get( '#show-settings-link' ).click(); + cy.get( '#postexcerpt-hide' ).check( { force: true } ); + + // Verify button exists. + cy.get( '#classifai-openai__excerpt-generate-btn' ).should( 'exist' ); + + // Click on button and verify data loads in. + cy.get( '#classifai-openai__excerpt-generate-btn' ).click(); + cy.get( '#excerpt' ).should( 'have.value', data ); + + cy.visit( '/wp-admin/plugins.php' ); + cy.get( '#deactivate-classic-editor' ).click(); + } ); + it( 'Can disable excerpt generation feature', () => { cy.visit( '/wp-admin/tools.php?page=classifai&tab=language_processing&provider=openai_chatgpt' ); diff --git a/webpack.config.js b/webpack.config.js index 766c45322..2136e4c1b 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -29,6 +29,9 @@ module.exports = { 'inserter-media-category': [ './src/js/gutenberg-plugins/inserter-media-category.js', ], + 'generate-excerpt-classic': [ + './src/js/openai/classic-editor-excerpt-generator.js', + ], 'generate-title-classic': [ './src/js/openai/classic-editor-title-generator.js', ],