From 67f86fd4d4b511f47a2d71827ffae30ec3b17a6e Mon Sep 17 00:00:00 2001 From: James Morrison Date: Thu, 15 Jun 2023 18:08:45 +0100 Subject: [PATCH 01/16] Added Generate Excerpt for the Classic Editor. --- .../Classifai/Providers/OpenAI/ChatGPT.php | 99 +++++++++++++++++++ src/js/post-excerpt-classic-editor/index.js | 62 ++++++++++++ webpack.config.js | 25 ++--- 3 files changed, 174 insertions(+), 12 deletions(-) create mode 100644 src/js/post-excerpt-classic-editor/index.js diff --git a/includes/Classifai/Providers/OpenAI/ChatGPT.php b/includes/Classifai/Providers/OpenAI/ChatGPT.php index 835ab4f4d..59dad4783 100644 --- a/includes/Classifai/Providers/OpenAI/ChatGPT.php +++ b/includes/Classifai/Providers/OpenAI/ChatGPT.php @@ -69,6 +69,71 @@ 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( 'add_meta_boxes', [ $this, 'replace_classic_editor_excerpt' ] ); + } + + /** + * Replace the Classic Editor Excerpt meta box + * There are no content filters available, there's no other option here. + */ + public function replace_classic_editor_excerpt() { + $post_type = ! empty( $_GET['post_type'] ) ? sanitize_text_field( wp_unslash( $_GET['post_type'] ) ) : 'post'; // phpcs:ignore WordPress.Security.NonceVerification.Recommended + + if ( post_type_supports( $post_type, 'excerpt' ) ) { + // Default meta box is registered in register_and_do_post_meta_boxes() + remove_meta_box( 'postexcerpt', null, 'normal' ); + // Note: the text-domain is missing for `Excerpt` because this is set in WP core + add_meta_box( 'classifaipostexcerpt', __( 'Excerpt' ), [ $this, 'classifai_post_excerpt_meta_box' ], null, 'normal', 'core', array( '__back_compat_meta_box' => true ) ); + } + } + + /** + * Custom excerpt meta field + * Note: most of this is copied from post_excerpt_meta_box() + * + * @param WP_Post $post Current post object. + */ + public function classifai_post_excerpt_meta_box( $post ) { + global $pagenow; + + $is_new_post = 'post-new.php' === $pagenow; + $has_excerpt = false; + + // We need a post ID to look up an excerpt, a brand new post doesn't have one + if ( ! $is_new_post ) { + $has_excerpt = '' !== get_the_excerpt( $post->ID ); + } + + $button_text = __( 'Generate excerpt', 'classifai' ); + if ( $has_excerpt ) { + $button_text = __( 'Re-generate excerpt', 'classifai' ); + } + + ?> + + +

+ Learn more about manual excerpts.' ), // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + __( 'https://wordpress.org/documentation/article/what-is-an-excerpt-classic-editor/' ) // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + ); + ?> +

+ + + +

+ + + 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'] ) + ) { + + $_post_id = isset( $_GET['post'] ) ? filter_var( $_GET['post'], FILTER_VALIDATE_INT ) : false; + + if ( $_post_id ) { + + wp_enqueue_script( + 'classifai-post-excerpt-classic-editor', + CLASSIFAI_PLUGIN_URL . 'dist/post-excerpt-classic-editor.js', + [], + CLASSIFAI_PLUGIN_VERSION, + true + ); + wp_localize_script( + 'classifai-post-excerpt-classic-editor', + 'classifai_generate_excerpt', + array( + 'endpoint_url' => esc_url( get_rest_url( null, "/classifai/v1/openai/generate-excerpt/{$_post_id}" ) ), + 'script_debug' => defined( 'SCRIPT_DEBUG' ) ? SCRIPT_DEBUG : false, + 'generate_excerpt_text' => __( 'Generate excerpt', 'classifai' ), + 'regenerate_excerpt_text' => __( 'Re-generate excerpt', 'classifai' ), + 'nonce' => wp_create_nonce( 'wp_rest' ), + ) + ); + } + } + wp_enqueue_style( 'classifai-language-processing-style', CLASSIFAI_PLUGIN_URL . 'dist/language-processing.css', diff --git a/src/js/post-excerpt-classic-editor/index.js b/src/js/post-excerpt-classic-editor/index.js new file mode 100644 index 000000000..59a5dd175 --- /dev/null +++ b/src/js/post-excerpt-classic-editor/index.js @@ -0,0 +1,62 @@ + +/** + * This is the main webpack entry point for compiling STAT Comments plugin JS. + */ + +console.log('Classifai Generate Excerpt Debug: Script Loaded'); + +/* Variables */ +const classifaiGenerateExcerptButtonID = 'classifai-generate-excerpt'; +const classifaiGenerateExcerptButtonElement = document.getElementById(classifaiGenerateExcerptButtonID); +const classifaiGenerateExcerptTextareaID = 'excerpt'; +const classifaiGenerateExcerptTextareaElement = document.getElementById(classifaiGenerateExcerptTextareaID); +let classifarGenerateExcerptDebug = false; +let classifaiGenerateExcerptText; + +if (classifai_generate_excerpt && classifai_generate_excerpt.script_debug) { + classifarGenerateExcerptDebug = classifai_generate_excerpt.script_debug; +} + +/* Generate excerpt when the button is clicked */ +if (null !== classifaiGenerateExcerptButtonElement) { + + classifaiGenerateExcerptButtonElement.onclick = function () { + classifarGenerateExcerptDebug && console.log('Classifai Generate Excerpt Debug: Button clicked, excerpt is being generated..'); + classifaiExcerptGenerate(); + }; +} + +/* Generate excerpt from API endpoint */ +function classifaiExcerptGenerate() { + + // Confirm the endpoint URL is available; excerpt generation cannot function without this. + if (!classifai_generate_excerpt || false === classifai_generate_excerpt.endpoint_url) { + classifarGenerateExcerptDebug && console.log('Classifai Generate Excerpt Debug: Endpoint URL not set!'); + return; + } + + classifaiGenerateExcerptButtonElement.disabled = true; + + fetch(classifai_generate_excerpt.endpoint_url, { + headers: { + 'X-WP-Nonce': classifai_generate_excerpt.nonce + } + }) + .then((response) => response.json()) + .then((result) => { + classifaiGenerateExcerptText = result; + + classifarGenerateExcerptDebug && console.log('Classifai Generate Excerpt Debug: Generated Text', classifaiGenerateExcerptText) + + if (classifaiGenerateExcerptText) { + classifaiGenerateExcerptTextareaElement.textContent = classifaiGenerateExcerptText + classifaiGenerateExcerptButtonElement.value = classifai_generate_excerpt.regenerate_excerpt_text; + } + }) + .catch((error) => { + }) + .finally(() => { + classifaiGenerateExcerptButtonElement.disabled = false + }); + +} diff --git a/webpack.config.js b/webpack.config.js index 04ab30874..85543ee28 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,20 +1,20 @@ -const defaultConfig = require( '@wordpress/scripts/config/webpack.config' ); -const path = require( 'path' ); +const defaultConfig = require('@wordpress/scripts/config/webpack.config'); +const path = require('path'); module.exports = { ...defaultConfig, output: { ...defaultConfig.output, - path: path.resolve( __dirname, 'dist' ), + path: path.resolve(__dirname, 'dist'), }, entry: { - editor: [ './src/js/editor.js' ], - 'editor-ocr': [ './src/js/editor-ocr.js' ], - media: [ './src/js/media.js' ], - admin: [ './src/js/admin.js' ], - 'language-processing': [ './src/js/language-processing.js' ], - 'gutenberg-plugin': [ './src/js/gutenberg-plugin.js' ], - 'post-audio-controls': [ './src/js/post-audio-controls.js' ], + editor: ['./src/js/editor.js'], + 'editor-ocr': ['./src/js/editor-ocr.js'], + media: ['./src/js/media.js'], + admin: ['./src/js/admin.js'], + 'language-processing': ['./src/js/language-processing.js'], + 'gutenberg-plugin': ['./src/js/gutenberg-plugin.js'], + 'post-audio-controls': ['./src/js/post-audio-controls.js'], 'post-status-info': [ './src/js/gutenberg-plugins/post-status-info.js', ], @@ -24,8 +24,9 @@ module.exports = { 'recommended-content-block-frontend': [ './includes/Classifai/Blocks/recommended-content-block/frontend.js', ], - 'post-excerpt': [ './src/js/post-excerpt/index.js' ], - 'media-modal': [ './src/js/media-modal/index.js' ], + 'post-excerpt': ['./src/js/post-excerpt/index.js'], + 'post-excerpt-classic-editor': ['./src/js/post-excerpt-classic-editor/index.js'], + 'media-modal': ['./src/js/media-modal/index.js'], }, module: { rules: [ From b82f9aa00377cefd1b23c2d6b9030eb624f418a2 Mon Sep 17 00:00:00 2001 From: James Morrison Date: Thu, 13 Jul 2023 10:35:13 +0100 Subject: [PATCH 02/16] A little cleanup; added CSS (and removed inline classes). --- .../Classifai/Providers/OpenAI/ChatGPT.php | 20 +-- package-lock.json | 14 -- src/js/post-excerpt-classic-editor/index.js | 20 +-- src/scss/admin.scss | 151 +++++++++++------- 4 files changed, 107 insertions(+), 98 deletions(-) diff --git a/includes/Classifai/Providers/OpenAI/ChatGPT.php b/includes/Classifai/Providers/OpenAI/ChatGPT.php index 59dad4783..20b1e0146 100644 --- a/includes/Classifai/Providers/OpenAI/ChatGPT.php +++ b/includes/Classifai/Providers/OpenAI/ChatGPT.php @@ -101,7 +101,7 @@ public function classifai_post_excerpt_meta_box( $post ) { // We need a post ID to look up an excerpt, a brand new post doesn't have one if ( ! $is_new_post ) { - $has_excerpt = '' !== get_the_excerpt( $post->ID ); + $has_excerpt = '' !== $post->post_excerpt; } $button_text = __( 'Generate excerpt', 'classifai' ); @@ -128,10 +128,10 @@ public function classifai_post_excerpt_meta_box( $post ) {

- +

- + esc_url( get_rest_url( null, "/classifai/v1/openai/generate-excerpt/{$_post_id}" ) ), - 'script_debug' => defined( 'SCRIPT_DEBUG' ) ? SCRIPT_DEBUG : false, - 'generate_excerpt_text' => __( 'Generate excerpt', 'classifai' ), - 'regenerate_excerpt_text' => __( 'Re-generate excerpt', 'classifai' ), - 'nonce' => wp_create_nonce( 'wp_rest' ), + 'endpointUrl' => esc_url( get_rest_url( null, "/classifai/v1/openai/generate-excerpt/{$_post_id}" ) ), + 'scriptDebug' => defined( 'SCRIPT_DEBUG' ) ? SCRIPT_DEBUG : false, + 'generateExcerptText' => __( 'Generate excerpt', 'classifai' ), + 'regenerateExcerptText' => __( 'Re-generate excerpt', 'classifai' ), + 'nonce' => wp_create_nonce( 'wp_rest' ), ) ); } diff --git a/package-lock.json b/package-lock.json index 9d341f746..aec3a295a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18054,20 +18054,6 @@ "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", "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==", - "dev": true, - "peer": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, "node_modules/uc.micro": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", diff --git a/src/js/post-excerpt-classic-editor/index.js b/src/js/post-excerpt-classic-editor/index.js index 59a5dd175..0dc2bf21f 100644 --- a/src/js/post-excerpt-classic-editor/index.js +++ b/src/js/post-excerpt-classic-editor/index.js @@ -1,10 +1,4 @@ -/** - * This is the main webpack entry point for compiling STAT Comments plugin JS. - */ - -console.log('Classifai Generate Excerpt Debug: Script Loaded'); - /* Variables */ const classifaiGenerateExcerptButtonID = 'classifai-generate-excerpt'; const classifaiGenerateExcerptButtonElement = document.getElementById(classifaiGenerateExcerptButtonID); @@ -13,8 +7,10 @@ const classifaiGenerateExcerptTextareaElement = document.getElementById(classifa let classifarGenerateExcerptDebug = false; let classifaiGenerateExcerptText; -if (classifai_generate_excerpt && classifai_generate_excerpt.script_debug) { - classifarGenerateExcerptDebug = classifai_generate_excerpt.script_debug; +// Note: classifarGenerateExcerptDebug is set by the SCRIPT_DEBUG constant +// Errors are only logged to console when SCRIPT_DEBUG is defined with value: TRUE +if (classifaiGenerateExcerpt && classifaiGenerateExcerpt.scriptDebug) { + classifarGenerateExcerptDebug = classifaiGenerateExcerpt.scriptDebug; } /* Generate excerpt when the button is clicked */ @@ -30,16 +26,16 @@ if (null !== classifaiGenerateExcerptButtonElement) { function classifaiExcerptGenerate() { // Confirm the endpoint URL is available; excerpt generation cannot function without this. - if (!classifai_generate_excerpt || false === classifai_generate_excerpt.endpoint_url) { + if (!classifaiGenerateExcerpt || false === classifaiGenerateExcerpt.endpointUrl) { classifarGenerateExcerptDebug && console.log('Classifai Generate Excerpt Debug: Endpoint URL not set!'); return; } classifaiGenerateExcerptButtonElement.disabled = true; - fetch(classifai_generate_excerpt.endpoint_url, { + fetch(classifaiGenerateExcerpt.endpointUrl, { headers: { - 'X-WP-Nonce': classifai_generate_excerpt.nonce + 'X-WP-Nonce': classifaiGenerateExcerpt.nonce } }) .then((response) => response.json()) @@ -50,7 +46,7 @@ function classifaiExcerptGenerate() { if (classifaiGenerateExcerptText) { classifaiGenerateExcerptTextareaElement.textContent = classifaiGenerateExcerptText - classifaiGenerateExcerptButtonElement.value = classifai_generate_excerpt.regenerate_excerpt_text; + classifaiGenerateExcerptButtonElement.value = classifaiGenerateExcerpt.regenerateExcerptText; } }) .catch((error) => { 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; +} From 57aec1e21ef3fad3b2913f9f238f663d23cfb882 Mon Sep 17 00:00:00 2001 From: Ravinder Kumar Date: Thu, 10 Aug 2023 21:32:29 +0530 Subject: [PATCH 03/16] fix: add classifai excerpt generate if post support ecerpt feature --- .../Classifai/Providers/OpenAI/ChatGPT.php | 47 +++++++++++++++---- 1 file changed, 39 insertions(+), 8 deletions(-) diff --git a/includes/Classifai/Providers/OpenAI/ChatGPT.php b/includes/Classifai/Providers/OpenAI/ChatGPT.php index 8686b264a..6b8700646 100644 --- a/includes/Classifai/Providers/OpenAI/ChatGPT.php +++ b/includes/Classifai/Providers/OpenAI/ChatGPT.php @@ -9,8 +9,10 @@ use Classifai\Providers\OpenAI\APIRequest; use Classifai\Providers\OpenAI\Tokenizer; use Classifai\Watson\Normalizer; +use WP_Post; use function Classifai\get_asset_info; use WP_Error; +use function Classifai\get_post_types_for_language_settings; class ChatGPT extends Provider { @@ -118,23 +120,52 @@ public function register() { /** * Replace the Classic Editor Excerpt meta box * There are no content filters available, there's no other option here. + * + * @since x.x.x */ - public function replace_classic_editor_excerpt() { - $post_type = ! empty( $_GET['post_type'] ) ? sanitize_text_field( wp_unslash( $_GET['post_type'] ) ) : 'post'; // phpcs:ignore WordPress.Security.NonceVerification.Recommended + public function replace_classic_editor_excerpt(): void { + global $post; - if ( post_type_supports( $post_type, 'excerpt' ) ) { - // Default meta box is registered in register_and_do_post_meta_boxes() - remove_meta_box( 'postexcerpt', null, 'normal' ); - // Note: the text-domain is missing for `Excerpt` because this is set in WP core - add_meta_box( 'classifaipostexcerpt', __( 'Excerpt' ), [ $this, 'classifai_post_excerpt_meta_box' ], null, 'normal', 'core', array( '__back_compat_meta_box' => true ) ); + // Bail if the feature is not enabled, or we don't have a post. + if ( + ! ( $post instanceof WP_Post ) + || ! $this->is_feature_enabled( 'enable_excerpt' ) + ) { + return; } + + $chat_gpt_post_types = array_keys( get_post_types_for_language_settings() ); + + // Only replace the meta box on the post types we support. + if ( ! in_array( $post->post_type, $chat_gpt_post_types, true ) ) { + return; + } + + // Bail if the post type doesn't support excerpts. + if ( ! post_type_supports( $post->post_type, 'excerpt' ) ) { + return; + } + + // Default meta box is registered in register_and_do_post_meta_boxes() + remove_meta_box( 'postexcerpt', null, 'normal' ); + + // Note: the text-domain is missing for `Excerpt` because this is set in WP core + add_meta_box( + 'classifaipostexcerpt', + __( 'Excerpt' ), + [ $this, 'classifai_post_excerpt_meta_box' ], + null, + 'normal', + 'core', + array( '__back_compat_meta_box' => true ) + ); } /** * Custom excerpt meta field * Note: most of this is copied from post_excerpt_meta_box() * - * @param \WP_Post $post Current post object. + * @param WP_Post $post Current post object. */ public function classifai_post_excerpt_meta_box( $post ) { global $pagenow; From 6713554c73c2cdaf9b66f19809352472f437df2f Mon Sep 17 00:00:00 2001 From: Ravinder Kumar Date: Thu, 10 Aug 2023 21:34:28 +0530 Subject: [PATCH 04/16] format: remove unused namespaces --- includes/Classifai/Providers/OpenAI/ChatGPT.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/includes/Classifai/Providers/OpenAI/ChatGPT.php b/includes/Classifai/Providers/OpenAI/ChatGPT.php index 6b8700646..ec3402c8f 100644 --- a/includes/Classifai/Providers/OpenAI/ChatGPT.php +++ b/includes/Classifai/Providers/OpenAI/ChatGPT.php @@ -6,8 +6,6 @@ namespace Classifai\Providers\OpenAI; use Classifai\Providers\Provider; -use Classifai\Providers\OpenAI\APIRequest; -use Classifai\Providers\OpenAI\Tokenizer; use Classifai\Watson\Normalizer; use WP_Post; use function Classifai\get_asset_info; From c517f23e18008a4ed0e5ed422e0036ee07d3aa6d Mon Sep 17 00:00:00 2001 From: Ravinder Kumar Date: Thu, 10 Aug 2023 21:42:31 +0530 Subject: [PATCH 05/16] format: improve code formatting --- .../Classifai/Providers/OpenAI/ChatGPT.php | 31 ++++++++++++++----- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/includes/Classifai/Providers/OpenAI/ChatGPT.php b/includes/Classifai/Providers/OpenAI/ChatGPT.php index ec3402c8f..2a692cb55 100644 --- a/includes/Classifai/Providers/OpenAI/ChatGPT.php +++ b/includes/Classifai/Providers/OpenAI/ChatGPT.php @@ -187,25 +187,40 @@ public function classifai_post_excerpt_meta_box( $post ) { /* translators: Hidden accessibility text. */ _e( 'Excerpt' ); // phpcs:ignore WordPress.Security.EscapeOutput.UnsafePrintingFunction ?> - +

Learn more about manual excerpts.' ), // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + __( 'Excerpts are optional hand-crafted summaries of your content that can be used in your theme. Learn more about manual excerpts.' ), // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped __( 'https://wordpress.org/documentation/article/what-is-an-excerpt-classic-editor/' ) // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ); ?>

- - -

- - + + +

+ + + +

+ + Date: Thu, 10 Aug 2023 21:54:37 +0530 Subject: [PATCH 06/16] refactor: register classic editor excerpt related assets --- .../Classifai/Providers/OpenAI/ChatGPT.php | 64 ++++++++----------- 1 file changed, 26 insertions(+), 38 deletions(-) diff --git a/includes/Classifai/Providers/OpenAI/ChatGPT.php b/includes/Classifai/Providers/OpenAI/ChatGPT.php index 2a692cb55..dc699c79a 100644 --- a/includes/Classifai/Providers/OpenAI/ChatGPT.php +++ b/includes/Classifai/Providers/OpenAI/ChatGPT.php @@ -157,6 +157,31 @@ public function replace_classic_editor_excerpt(): void { 'core', array( '__back_compat_meta_box' => true ) ); + + // Register assets. + wp_enqueue_script( + 'classifai-post-excerpt-classic-editor', + CLASSIFAI_PLUGIN_URL . 'dist/post-excerpt-classic-editor.js', + [], + CLASSIFAI_PLUGIN_VERSION, + true + ); + wp_localize_script( + 'classifai-post-excerpt-classic-editor', + 'classifaiGenerateExcerpt', + [ + 'endpointUrl' => esc_url( + get_rest_url( + null, + "/classifai/v1/openai/generate-excerpt/{$post->ID}" + ) + ), + 'scriptDebug' => defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG, + 'generateExcerptText' => __( 'Generate excerpt', 'classifai' ), + 'regenerateExcerptText' => __( 'Re-generate excerpt', 'classifai' ), + 'nonce' => wp_create_nonce( 'wp_rest' ), + ] + ); } /** @@ -290,53 +315,16 @@ public function enqueue_editor_assets() { * * @param string $hook_suffix The current admin page. */ - public function enqueue_admin_assets( $hook_suffix = '' ) { + public function enqueue_admin_assets( string $hook_suffix = '' ): void { if ( 'post.php' !== $hook_suffix && 'post-new.php' !== $hook_suffix ) { return; } - $settings = $this->get_settings(); - $excerpt_roles = $settings['roles'] ?? []; $screen = get_current_screen(); $settings = $this->get_settings(); $user_roles = wp_get_current_user()->roles ?? []; $title_roles = $settings['title_roles'] ?? []; - if ( - ( ! empty( $excerpt_roles ) && empty( array_diff( $user_roles, $excerpt_roles ) ) ) - && ( isset( $settings['enable_excerpt'] ) && 1 === (int) $settings['enable_excerpt'] ) - ) { - $_post_id = isset( $_GET['post'] ) // phpcs:ignore WordPress.Security.NonceVerification.Recommended - ? filter_var( $_GET['post'], FILTER_VALIDATE_INT ) // phpcs:ignore WordPress.Security.NonceVerification.Recommended - : false; - - if ( $_post_id ) { - wp_enqueue_script( - 'classifai-post-excerpt-classic-editor', - CLASSIFAI_PLUGIN_URL . 'dist/post-excerpt-classic-editor.js', - [], - CLASSIFAI_PLUGIN_VERSION, - true - ); - wp_localize_script( - 'classifai-post-excerpt-classic-editor', - 'classifaiGenerateExcerpt', - [ - 'endpointUrl' => esc_url( - get_rest_url( - null, - "/classifai/v1/openai/generate-excerpt/{$_post_id}" - ) - ), - 'scriptDebug' => defined( 'SCRIPT_DEBUG' ) ? SCRIPT_DEBUG : false, - 'generateExcerptText' => __( 'Generate excerpt', 'classifai' ), - 'regenerateExcerptText' => __( 'Re-generate excerpt', 'classifai' ), - 'nonce' => wp_create_nonce( 'wp_rest' ), - ] - ); - } - } - // Load the assets for the classic editor. if ( $screen && ! $screen->is_block_editor() From ec7e355ae20aed72fd78250a4f8a4cb5f24aadfd Mon Sep 17 00:00:00 2001 From: Ravinder Kumar Date: Thu, 10 Aug 2023 21:55:44 +0530 Subject: [PATCH 07/16] format: improve code formatting --- includes/Classifai/Providers/OpenAI/ChatGPT.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/includes/Classifai/Providers/OpenAI/ChatGPT.php b/includes/Classifai/Providers/OpenAI/ChatGPT.php index dc699c79a..727b3cc1b 100644 --- a/includes/Classifai/Providers/OpenAI/ChatGPT.php +++ b/includes/Classifai/Providers/OpenAI/ChatGPT.php @@ -315,15 +315,15 @@ public function enqueue_editor_assets() { * * @param string $hook_suffix The current admin page. */ - public function enqueue_admin_assets( string $hook_suffix = '' ): void { + public function enqueue_admin_assets( $hook_suffix = '' ) { if ( 'post.php' !== $hook_suffix && 'post-new.php' !== $hook_suffix ) { 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(); + $user_roles = wp_get_current_user()->roles ?? []; + $title_roles = $settings['title_roles'] ?? []; // Load the assets for the classic editor. if ( From c9a90e61e04b253f341a6a0d872ec349b26168f7 Mon Sep 17 00:00:00 2001 From: Ravinder Kumar Date: Thu, 10 Aug 2023 22:20:40 +0530 Subject: [PATCH 08/16] fix: admin should see titles modal with classic editor --- .../Classifai/Providers/OpenAI/ChatGPT.php | 90 ++++++++++--------- package-lock.json | 14 +++ 2 files changed, 61 insertions(+), 43 deletions(-) diff --git a/includes/Classifai/Providers/OpenAI/ChatGPT.php b/includes/Classifai/Providers/OpenAI/ChatGPT.php index 727b3cc1b..633684075 100644 --- a/includes/Classifai/Providers/OpenAI/ChatGPT.php +++ b/includes/Classifai/Providers/OpenAI/ChatGPT.php @@ -112,16 +112,19 @@ public function is_feature_enabled( string $feature = '' ) { public function register() { add_action( 'enqueue_block_editor_assets', [ $this, 'enqueue_editor_assets' ] ); add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_admin_assets' ] ); - add_action( 'add_meta_boxes', [ $this, 'replace_classic_editor_excerpt' ] ); + add_action( 'add_meta_boxes', [ $this, 'add_support_for_classic_editor' ] ); } /** - * Replace the Classic Editor Excerpt meta box - * There are no content filters available, there's no other option here. + * Add support for classic editor. + * + * This function does following: + * - Replace the Classic Editor Excerpt meta box. There are no content filters available, there's no other option here. + * - Print the generated titles template for modal that is used by "Generate titles" feature. * * @since x.x.x */ - public function replace_classic_editor_excerpt(): void { + public function add_support_for_classic_editor(): void { global $post; // Bail if the feature is not enabled, or we don't have a post. @@ -139,49 +142,50 @@ public function replace_classic_editor_excerpt(): void { return; } - // Bail if the post type doesn't support excerpts. - if ( ! post_type_supports( $post->post_type, 'excerpt' ) ) { - return; + if ( post_type_supports( $post->post_type, 'title' ) ) { + add_action( 'edit_form_before_permalink', [ $this, 'register_generated_titles_template' ] ); } - // Default meta box is registered in register_and_do_post_meta_boxes() - remove_meta_box( 'postexcerpt', null, 'normal' ); + if ( post_type_supports( $post->post_type, 'excerpt' ) ) { + // Default meta box is registered in register_and_do_post_meta_boxes() + remove_meta_box( 'postexcerpt', null, 'normal' ); - // Note: the text-domain is missing for `Excerpt` because this is set in WP core - add_meta_box( - 'classifaipostexcerpt', - __( 'Excerpt' ), - [ $this, 'classifai_post_excerpt_meta_box' ], - null, - 'normal', - 'core', - array( '__back_compat_meta_box' => true ) - ); + // Note: the text-domain is missing for `Excerpt` because this is set in WP core + add_meta_box( + 'classifaipostexcerpt', + __( 'Excerpt' ), + [ $this, 'classifai_post_excerpt_meta_box' ], + null, + 'normal', + 'core', + array( '__back_compat_meta_box' => true ) + ); - // Register assets. - wp_enqueue_script( - 'classifai-post-excerpt-classic-editor', - CLASSIFAI_PLUGIN_URL . 'dist/post-excerpt-classic-editor.js', - [], - CLASSIFAI_PLUGIN_VERSION, - true - ); - wp_localize_script( - 'classifai-post-excerpt-classic-editor', - 'classifaiGenerateExcerpt', - [ - 'endpointUrl' => esc_url( - get_rest_url( - null, - "/classifai/v1/openai/generate-excerpt/{$post->ID}" - ) - ), - 'scriptDebug' => defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG, - 'generateExcerptText' => __( 'Generate excerpt', 'classifai' ), - 'regenerateExcerptText' => __( 'Re-generate excerpt', 'classifai' ), - 'nonce' => wp_create_nonce( 'wp_rest' ), - ] - ); + // Register assets. + wp_enqueue_script( + 'classifai-post-excerpt-classic-editor', + CLASSIFAI_PLUGIN_URL . 'dist/post-excerpt-classic-editor.js', + [], + CLASSIFAI_PLUGIN_VERSION, + true + ); + wp_localize_script( + 'classifai-post-excerpt-classic-editor', + 'classifaiGenerateExcerpt', + [ + 'endpointUrl' => esc_url( + get_rest_url( + null, + "/classifai/v1/openai/generate-excerpt/{$post->ID}" + ) + ), + 'scriptDebug' => defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG, + 'generateExcerptText' => __( 'Generate excerpt', 'classifai' ), + 'regenerateExcerptText' => __( 'Re-generate excerpt', 'classifai' ), + 'nonce' => wp_create_nonce( 'wp_rest' ), + ] + ); + } } /** diff --git a/package-lock.json b/package-lock.json index 7a626e4eb..984b711ed 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18054,6 +18054,20 @@ "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", "dev": true }, + "node_modules/typescript": { + "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": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, "node_modules/uc.micro": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", From 37007deabe58749a28c6202302f278f9eb52389c Mon Sep 17 00:00:00 2001 From: Ravinder Kumar Date: Thu, 10 Aug 2023 22:28:23 +0530 Subject: [PATCH 09/16] refactor: move classic editor exerpt script to openai directory --- includes/Classifai/Providers/OpenAI/ChatGPT.php | 2 +- .../index.js => openai/classic-editor-excerpt-generator.js} | 0 webpack.config.js | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) rename src/js/{post-excerpt-classic-editor/index.js => openai/classic-editor-excerpt-generator.js} (100%) diff --git a/includes/Classifai/Providers/OpenAI/ChatGPT.php b/includes/Classifai/Providers/OpenAI/ChatGPT.php index 633684075..f4d7dadd1 100644 --- a/includes/Classifai/Providers/OpenAI/ChatGPT.php +++ b/includes/Classifai/Providers/OpenAI/ChatGPT.php @@ -164,7 +164,7 @@ public function add_support_for_classic_editor(): void { // Register assets. wp_enqueue_script( 'classifai-post-excerpt-classic-editor', - CLASSIFAI_PLUGIN_URL . 'dist/post-excerpt-classic-editor.js', + CLASSIFAI_PLUGIN_URL . 'dist/generate-excerpt-classic.js', [], CLASSIFAI_PLUGIN_VERSION, true diff --git a/src/js/post-excerpt-classic-editor/index.js b/src/js/openai/classic-editor-excerpt-generator.js similarity index 100% rename from src/js/post-excerpt-classic-editor/index.js rename to src/js/openai/classic-editor-excerpt-generator.js diff --git a/webpack.config.js b/webpack.config.js index e8923d31d..10bf7d49d 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -25,8 +25,8 @@ module.exports = { './includes/Classifai/Blocks/recommended-content-block/frontend.js', ], 'post-excerpt': ['./src/js/post-excerpt/index.js'], - 'post-excerpt-classic-editor': ['./src/js/post-excerpt-classic-editor/index.js'], 'media-modal': ['./src/js/media-modal/index.js'], + 'generate-excerpt-classic': ['./src/js/openai/classic-editor-excerpt-generator.js'], 'generate-title-classic': [ './src/js/openai/classic-editor-title-generator.js', ], From b33ebd47a55b8bd8b267a27a059e2902473804a8 Mon Sep 17 00:00:00 2001 From: Ravinder Kumar Date: Thu, 10 Aug 2023 22:49:29 +0530 Subject: [PATCH 10/16] format: resole eslint issues --- .../classic-editor-excerpt-generator.js | 77 ++++++++++++------- webpack.config.js | 28 +++---- 2 files changed, 64 insertions(+), 41 deletions(-) diff --git a/src/js/openai/classic-editor-excerpt-generator.js b/src/js/openai/classic-editor-excerpt-generator.js index 0dc2bf21f..8235fa18e 100644 --- a/src/js/openai/classic-editor-excerpt-generator.js +++ b/src/js/openai/classic-editor-excerpt-generator.js @@ -1,58 +1,79 @@ - -/* Variables */ +/* globals classifaiGenerateExcerpt */ const classifaiGenerateExcerptButtonID = 'classifai-generate-excerpt'; -const classifaiGenerateExcerptButtonElement = document.getElementById(classifaiGenerateExcerptButtonID); +const classifaiGenerateExcerptButtonElement = document.getElementById( + classifaiGenerateExcerptButtonID +); const classifaiGenerateExcerptTextareaID = 'excerpt'; -const classifaiGenerateExcerptTextareaElement = document.getElementById(classifaiGenerateExcerptTextareaID); +const classifaiGenerateExcerptTextareaElement = document.getElementById( + classifaiGenerateExcerptTextareaID +); let classifarGenerateExcerptDebug = false; let classifaiGenerateExcerptText; // Note: classifarGenerateExcerptDebug is set by the SCRIPT_DEBUG constant // Errors are only logged to console when SCRIPT_DEBUG is defined with value: TRUE -if (classifaiGenerateExcerpt && classifaiGenerateExcerpt.scriptDebug) { +if ( classifaiGenerateExcerpt && classifaiGenerateExcerpt.scriptDebug ) { classifarGenerateExcerptDebug = classifaiGenerateExcerpt.scriptDebug; } /* Generate excerpt when the button is clicked */ -if (null !== classifaiGenerateExcerptButtonElement) { - +if ( null !== classifaiGenerateExcerptButtonElement ) { classifaiGenerateExcerptButtonElement.onclick = function () { - classifarGenerateExcerptDebug && console.log('Classifai Generate Excerpt Debug: Button clicked, excerpt is being generated..'); + if ( classifarGenerateExcerptDebug ) { + // eslint-disable-next-line no-console + console.log( + 'Classifai Generate Excerpt Debug: Button clicked, excerpt is being generated..' + ); + } + classifaiExcerptGenerate(); }; } /* Generate excerpt from API endpoint */ function classifaiExcerptGenerate() { - // Confirm the endpoint URL is available; excerpt generation cannot function without this. - if (!classifaiGenerateExcerpt || false === classifaiGenerateExcerpt.endpointUrl) { - classifarGenerateExcerptDebug && console.log('Classifai Generate Excerpt Debug: Endpoint URL not set!'); + if ( + ! classifaiGenerateExcerpt || + false === classifaiGenerateExcerpt.endpointUrl + ) { + if ( classifarGenerateExcerptDebug ) { + // eslint-disable-next-line no-console + console.log( + 'Classifai Generate Excerpt Debug: Endpoint URL not set!' + ); + } + return; } classifaiGenerateExcerptButtonElement.disabled = true; - fetch(classifaiGenerateExcerpt.endpointUrl, { + fetch( classifaiGenerateExcerpt.endpointUrl, { headers: { - 'X-WP-Nonce': classifaiGenerateExcerpt.nonce - } - }) - .then((response) => response.json()) - .then((result) => { + 'X-WP-Nonce': classifaiGenerateExcerpt.nonce, + }, + } ) + .then( ( response ) => response.json() ) + .then( ( result ) => { classifaiGenerateExcerptText = result; - classifarGenerateExcerptDebug && console.log('Classifai Generate Excerpt Debug: Generated Text', classifaiGenerateExcerptText) - - if (classifaiGenerateExcerptText) { - classifaiGenerateExcerptTextareaElement.textContent = classifaiGenerateExcerptText - classifaiGenerateExcerptButtonElement.value = classifaiGenerateExcerpt.regenerateExcerptText; + if ( classifarGenerateExcerptDebug ) { + // eslint-disable-next-line no-console + console.log( + 'Classifai Generate Excerpt Debug: Generated Text', + classifaiGenerateExcerptText + ); } - }) - .catch((error) => { - }) - .finally(() => { - classifaiGenerateExcerptButtonElement.disabled = false - }); + if ( classifaiGenerateExcerptText ) { + classifaiGenerateExcerptTextareaElement.textContent = + classifaiGenerateExcerptText; + classifaiGenerateExcerptButtonElement.value = + classifaiGenerateExcerpt.regenerateExcerptText; + } + } ) + .finally( () => { + classifaiGenerateExcerptButtonElement.disabled = false; + } ); } diff --git a/webpack.config.js b/webpack.config.js index 10bf7d49d..e778863e3 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,20 +1,20 @@ -const defaultConfig = require('@wordpress/scripts/config/webpack.config'); -const path = require('path'); +const defaultConfig = require( '@wordpress/scripts/config/webpack.config' ); +const path = require( 'path' ); module.exports = { ...defaultConfig, output: { ...defaultConfig.output, - path: path.resolve(__dirname, 'dist'), + path: path.resolve( __dirname, 'dist' ), }, entry: { - editor: ['./src/js/editor.js'], - 'editor-ocr': ['./src/js/editor-ocr.js'], - media: ['./src/js/media.js'], - admin: ['./src/js/admin.js'], - 'language-processing': ['./src/js/language-processing.js'], - 'gutenberg-plugin': ['./src/js/gutenberg-plugin.js'], - 'post-audio-controls': ['./src/js/post-audio-controls.js'], + editor: [ './src/js/editor.js' ], + 'editor-ocr': [ './src/js/editor-ocr.js' ], + media: [ './src/js/media.js' ], + admin: [ './src/js/admin.js' ], + 'language-processing': [ './src/js/language-processing.js' ], + 'gutenberg-plugin': [ './src/js/gutenberg-plugin.js' ], + 'post-audio-controls': [ './src/js/post-audio-controls.js' ], 'post-status-info': [ './src/js/gutenberg-plugins/post-status-info.js', ], @@ -24,9 +24,11 @@ module.exports = { 'recommended-content-block-frontend': [ './includes/Classifai/Blocks/recommended-content-block/frontend.js', ], - 'post-excerpt': ['./src/js/post-excerpt/index.js'], - 'media-modal': ['./src/js/media-modal/index.js'], - 'generate-excerpt-classic': ['./src/js/openai/classic-editor-excerpt-generator.js'], + 'post-excerpt': [ './src/js/post-excerpt/index.js' ], + 'media-modal': [ './src/js/media-modal/index.js' ], + 'generate-excerpt-classic': [ + './src/js/openai/classic-editor-excerpt-generator.js', + ], 'generate-title-classic': [ './src/js/openai/classic-editor-title-generator.js', ], From c7b6d83fbdba7d5531eb523f06925ab0871c4de4 Mon Sep 17 00:00:00 2001 From: Ravinder Kumar Date: Fri, 11 Aug 2023 09:49:48 +0530 Subject: [PATCH 11/16] format: improve code formatting --- webpack.config.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/webpack.config.js b/webpack.config.js index 969b7a771..2136e4c1b 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -26,9 +26,9 @@ module.exports = { ], 'post-excerpt': [ './src/js/post-excerpt/index.js' ], 'media-modal': [ './src/js/media-modal/index.js' ], - 'inserter-media-category': [ + 'inserter-media-category': [ './src/js/gutenberg-plugins/inserter-media-category.js', - ], + ], 'generate-excerpt-classic': [ './src/js/openai/classic-editor-excerpt-generator.js', ], From 7e735f1b0a1bca36ae9295873293e0bf77a6ae53 Mon Sep 17 00:00:00 2001 From: Darin Kotter Date: Thu, 17 Aug 2023 14:46:03 -0600 Subject: [PATCH 12/16] Refactor things to match what we did for the generate titles feature. Remove code that is no longer needed. Ensure we only load the features if enabled and if the post type supports it --- .../Classifai/Providers/OpenAI/ChatGPT.php | 233 +++++------------- .../classic-editor-excerpt-generator.js | 139 ++++++----- .../classic-editor-title-generator.scss | 23 +- 3 files changed, 160 insertions(+), 235 deletions(-) diff --git a/includes/Classifai/Providers/OpenAI/ChatGPT.php b/includes/Classifai/Providers/OpenAI/ChatGPT.php index f4d7dadd1..b4662549e 100644 --- a/includes/Classifai/Providers/OpenAI/ChatGPT.php +++ b/includes/Classifai/Providers/OpenAI/ChatGPT.php @@ -8,8 +8,8 @@ use Classifai\Providers\Provider; use Classifai\Watson\Normalizer; use WP_Post; -use function Classifai\get_asset_info; use WP_Error; +use function Classifai\get_asset_info; use function Classifai\get_post_types_for_language_settings; class ChatGPT extends Provider { @@ -112,144 +112,6 @@ public function is_feature_enabled( string $feature = '' ) { public function register() { add_action( 'enqueue_block_editor_assets', [ $this, 'enqueue_editor_assets' ] ); add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_admin_assets' ] ); - add_action( 'add_meta_boxes', [ $this, 'add_support_for_classic_editor' ] ); - } - - /** - * Add support for classic editor. - * - * This function does following: - * - Replace the Classic Editor Excerpt meta box. There are no content filters available, there's no other option here. - * - Print the generated titles template for modal that is used by "Generate titles" feature. - * - * @since x.x.x - */ - public function add_support_for_classic_editor(): void { - global $post; - - // Bail if the feature is not enabled, or we don't have a post. - if ( - ! ( $post instanceof WP_Post ) - || ! $this->is_feature_enabled( 'enable_excerpt' ) - ) { - return; - } - - $chat_gpt_post_types = array_keys( get_post_types_for_language_settings() ); - - // Only replace the meta box on the post types we support. - if ( ! in_array( $post->post_type, $chat_gpt_post_types, true ) ) { - return; - } - - if ( post_type_supports( $post->post_type, 'title' ) ) { - add_action( 'edit_form_before_permalink', [ $this, 'register_generated_titles_template' ] ); - } - - if ( post_type_supports( $post->post_type, 'excerpt' ) ) { - // Default meta box is registered in register_and_do_post_meta_boxes() - remove_meta_box( 'postexcerpt', null, 'normal' ); - - // Note: the text-domain is missing for `Excerpt` because this is set in WP core - add_meta_box( - 'classifaipostexcerpt', - __( 'Excerpt' ), - [ $this, 'classifai_post_excerpt_meta_box' ], - null, - 'normal', - 'core', - array( '__back_compat_meta_box' => true ) - ); - - // Register assets. - wp_enqueue_script( - 'classifai-post-excerpt-classic-editor', - CLASSIFAI_PLUGIN_URL . 'dist/generate-excerpt-classic.js', - [], - CLASSIFAI_PLUGIN_VERSION, - true - ); - wp_localize_script( - 'classifai-post-excerpt-classic-editor', - 'classifaiGenerateExcerpt', - [ - 'endpointUrl' => esc_url( - get_rest_url( - null, - "/classifai/v1/openai/generate-excerpt/{$post->ID}" - ) - ), - 'scriptDebug' => defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG, - 'generateExcerptText' => __( 'Generate excerpt', 'classifai' ), - 'regenerateExcerptText' => __( 'Re-generate excerpt', 'classifai' ), - 'nonce' => wp_create_nonce( 'wp_rest' ), - ] - ); - } - } - - /** - * Custom excerpt meta field - * Note: most of this is copied from post_excerpt_meta_box() - * - * @param WP_Post $post Current post object. - */ - public function classifai_post_excerpt_meta_box( $post ) { - global $pagenow; - - $is_new_post = 'post-new.php' === $pagenow; - $has_excerpt = false; - - // We need a post ID to look up an excerpt, a brand new post doesn't have one - if ( ! $is_new_post ) { - $has_excerpt = '' !== $post->post_excerpt; - } - - $button_text = __( 'Generate excerpt', 'classifai' ); - if ( $has_excerpt ) { - $button_text = __( 'Re-generate excerpt', 'classifai' ); - } - - ?> - - -

- Learn more about manual excerpts.' ), // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped - __( 'https://wordpress.org/documentation/article/what-is-an-excerpt-classic-editor/' ) // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped - ); - ?> -

- - - -

- - - -

- - - 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/src/js/openai/classic-editor-excerpt-generator.js b/src/js/openai/classic-editor-excerpt-generator.js index 8235fa18e..43aa29391 100644 --- a/src/js/openai/classic-editor-excerpt-generator.js +++ b/src/js/openai/classic-editor-excerpt-generator.js @@ -1,79 +1,84 @@ -/* globals classifaiGenerateExcerpt */ -const classifaiGenerateExcerptButtonID = 'classifai-generate-excerpt'; -const classifaiGenerateExcerptButtonElement = document.getElementById( - classifaiGenerateExcerptButtonID -); -const classifaiGenerateExcerptTextareaID = 'excerpt'; -const classifaiGenerateExcerptTextareaElement = document.getElementById( - classifaiGenerateExcerptTextareaID -); -let classifarGenerateExcerptDebug = false; -let classifaiGenerateExcerptText; +import apiFetch from '@wordpress/api-fetch'; +import '../../scss/openai/classic-editor-title-generator.scss'; -// Note: classifarGenerateExcerptDebug is set by the SCRIPT_DEBUG constant -// Errors are only logged to console when SCRIPT_DEBUG is defined with value: TRUE -if ( classifaiGenerateExcerpt && classifaiGenerateExcerpt.scriptDebug ) { - classifarGenerateExcerptDebug = classifaiGenerateExcerpt.scriptDebug; -} +const classifaiExcerptData = window.classifaiGenerateExcerpt || {}; -/* Generate excerpt when the button is clicked */ -if ( null !== classifaiGenerateExcerptButtonElement ) { - classifaiGenerateExcerptButtonElement.onclick = function () { - if ( classifarGenerateExcerptDebug ) { - // eslint-disable-next-line no-console - console.log( - 'Classifai Generate Excerpt Debug: Button clicked, excerpt is being generated..' - ); +( 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' ); - classifaiExcerptGenerate(); - }; -} + // 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; + } -/* Generate excerpt from API endpoint */ -function classifaiExcerptGenerate() { - // Confirm the endpoint URL is available; excerpt generation cannot function without this. - if ( - ! classifaiGenerateExcerpt || - false === classifaiGenerateExcerpt.endpointUrl - ) { - if ( classifarGenerateExcerptDebug ) { - // eslint-disable-next-line no-console - console.log( - 'Classifai Generate Excerpt Debug: Endpoint URL not set!' + const generateTextEl = $( + '.classifai-openai__excerpt-generate-btn--text' + ); + const spinnerEl = $( + '.classifai-openai__excerpt-generate-btn--spinner' ); - } - return; - } + generateTextEl.css( 'opacity', '0' ); + spinnerEl.show(); + isProcessing = true; - classifaiGenerateExcerptButtonElement.disabled = true; + const path = classifaiExcerptData?.path + postId; - fetch( classifaiGenerateExcerpt.endpointUrl, { - headers: { - 'X-WP-Nonce': classifaiGenerateExcerpt.nonce, - }, - } ) - .then( ( response ) => response.json() ) - .then( ( result ) => { - classifaiGenerateExcerptText = result; + apiFetch( { + path, + } ).then( ( result ) => { + generateTextEl.css( 'opacity', '1' ); + spinnerEl.hide(); + isProcessing = false; - if ( classifarGenerateExcerptDebug ) { - // eslint-disable-next-line no-console - console.log( - 'Classifai Generate Excerpt Debug: Generated Text', - classifaiGenerateExcerptText + $( excerptContainer ).val( result ).trigger( 'input' ); + generateTextEl.text( + classifaiExcerptData?.regenerateText ?? '' ); - } + } ); + }; - if ( classifaiGenerateExcerptText ) { - classifaiGenerateExcerptTextareaElement.textContent = - classifaiGenerateExcerptText; - classifaiGenerateExcerptButtonElement.value = - classifaiGenerateExcerpt.regenerateExcerptText; - } - } ) - .finally( () => { - classifaiGenerateExcerptButtonElement.disabled = false; - } ); -} + // Event handler registration to generate the excerpt. + $( document ).on( + 'click', + '#classifai-openai__excerpt-generate-btn', + generateExcerpt + ); + } +} )( jQuery ); 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; From d1f4d0cc68a45839dae1117c24e8c4ef3059619f Mon Sep 17 00:00:00 2001 From: Darin Kotter Date: Thu, 17 Aug 2023 14:46:34 -0600 Subject: [PATCH 13/16] Remove unneeded line --- includes/Classifai/Providers/OpenAI/ChatGPT.php | 1 - 1 file changed, 1 deletion(-) diff --git a/includes/Classifai/Providers/OpenAI/ChatGPT.php b/includes/Classifai/Providers/OpenAI/ChatGPT.php index b4662549e..409e99b58 100644 --- a/includes/Classifai/Providers/OpenAI/ChatGPT.php +++ b/includes/Classifai/Providers/OpenAI/ChatGPT.php @@ -7,7 +7,6 @@ use Classifai\Providers\Provider; use Classifai\Watson\Normalizer; -use WP_Post; use WP_Error; use function Classifai\get_asset_info; use function Classifai\get_post_types_for_language_settings; From 332903d95a0358f55d1bd7a62b76f6c5e38ce69c Mon Sep 17 00:00:00 2001 From: Darin Kotter Date: Thu, 17 Aug 2023 14:47:19 -0600 Subject: [PATCH 14/16] Remove unneeded line --- includes/Classifai/Providers/OpenAI/ChatGPT.php | 1 - 1 file changed, 1 deletion(-) diff --git a/includes/Classifai/Providers/OpenAI/ChatGPT.php b/includes/Classifai/Providers/OpenAI/ChatGPT.php index 409e99b58..97222347b 100644 --- a/includes/Classifai/Providers/OpenAI/ChatGPT.php +++ b/includes/Classifai/Providers/OpenAI/ChatGPT.php @@ -9,7 +9,6 @@ use Classifai\Watson\Normalizer; use WP_Error; use function Classifai\get_asset_info; -use function Classifai\get_post_types_for_language_settings; class ChatGPT extends Provider { From 2b3d482bbd2ab8618f51284937cf10c6b87d403c Mon Sep 17 00:00:00 2001 From: Darin Kotter Date: Thu, 17 Aug 2023 15:00:05 -0600 Subject: [PATCH 15/16] Add E2E test --- .../integration/language-processing.test.js | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/tests/cypress/integration/language-processing.test.js b/tests/cypress/integration/language-processing.test.js index afc78e181..37acd4cbc 100644 --- a/tests/cypress/integration/language-processing.test.js +++ b/tests/cypress/integration/language-processing.test.js @@ -203,6 +203,35 @@ 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', + } ); + + // 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' ); From 70caae85800d914380e2b0de2f97b33591b0d3b9 Mon Sep 17 00:00:00 2001 From: Darin Kotter Date: Thu, 17 Aug 2023 15:29:04 -0600 Subject: [PATCH 16/16] Ensure the excerpt metabox is shown --- tests/cypress/integration/language-processing.test.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/cypress/integration/language-processing.test.js b/tests/cypress/integration/language-processing.test.js index 37acd4cbc..5a8d0d63c 100644 --- a/tests/cypress/integration/language-processing.test.js +++ b/tests/cypress/integration/language-processing.test.js @@ -221,6 +221,10 @@ describe('Language processing Tests', () => { 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' );