Skip to content

Commit

Permalink
Merge pull request #506 from 10up/feat/495
Browse files Browse the repository at this point in the history
feat/495: Generate post titles using OpenAI's ChatGPT for classic editor
  • Loading branch information
dkotter authored Jun 23, 2023
2 parents 8a39178 + 25582e2 commit 2b9118e
Show file tree
Hide file tree
Showing 8 changed files with 384 additions and 15 deletions.
5 changes: 4 additions & 1 deletion .wp-env.json
Original file line number Diff line number Diff line change
@@ -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"
}
}
89 changes: 76 additions & 13 deletions includes/Classifai/Providers/OpenAI/ChatGPT.php
Original file line number Diff line number Diff line change
Expand Up @@ -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 ),
];
}

/**
Expand Down Expand Up @@ -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'
);
Expand All @@ -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',
Expand All @@ -155,6 +201,23 @@ public function enqueue_admin_assets( $hook_suffix = '' ) {
);
}

/**
* HTML template for title generation result popup.
*/
public function register_generated_titles_template() {
?>
<div id="classifai-openai__results" style="display: none;">
<div id="classifai-openai__overlay" style="opacity: 0;"></div>
<div id="classifai-openai__modal" style="opacity: 0;">
<h2 id="classifai-openai__results-title"></h2>
<div id="classifai-openai__close-modal-button"></div>
<div id="classifai-openai__results-content">
</div>
</div>
</div>
<?php
}

/**
* Setup fields
*/
Expand Down
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
"env": "wp-env",
"env:start": "wp-env start",
"env:stop": "wp-env stop",
"postenv:start": "./tests/bin/initialize.sh",
"archive": "composer archive --file=$npm_package_name --format=zip",
"postarchive": "rm -rf ./release && unzip $npm_package_name.zip -d ./release",
"prepare": "husky install"
Expand Down
140 changes: 140 additions & 0 deletions src/js/openai/classic-editor-title-generator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import apiFetch from '@wordpress/api-fetch';
import '../../scss/openai/classic-editor-title-generator.scss';

const classifaiChatGPTData = window.classifaiChatGPTData || {};
const scriptData = classifaiChatGPTData.enabledFeatures.reduce(
( acc, cur ) => ( { [ 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.
$( '<span />', {
text: scriptData?.title?.buttonText ?? '',
class: 'classifai-openai__title-generate-btn--text',
} )
.wrap(
'<div class="button" id="classifai-openai__title-generate-btn" />'
)
.parent()
.append(
$( '<span />', {
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 ) => {
$( '<textarea>', {
text: title,
} )
.wrap( `<div class="classifai-openai__result-item" />` )
.parent()
.append(
$( '<button />', {
text: scriptData.title.selectBtnText,
type: 'button',
class: 'button classifai-openai__select-title',
} )
)
.appendTo( '#classifai-openai__results-content' );
} );

$( '#classifai-openai__results' )
.show()
.addClass( 'classifai-openai--fade-in' );
} );
};

// Event handler registration to generate the title.
$( document ).on(
'click',
'#classifai-openai__title-generate-btn',
generateTitle
);

// Event handler registration to hide the popup.
$( document ).on( 'click', '#classifai-openai__overlay', hidePopup );
$( document ).on(
'click',
'#classifai-openai__close-modal-button',
hidePopup
);

// Event handler registration to apply the selected title to the post title.
$( document ).on(
'click',
'.classifai-openai__select-title',
applyTitle
);

// Sets the modal title.
const resultWrapper = $( '#classifai-openai__results' );
resultWrapper
.find( '#classifai-openai__results-title' )
.text( scriptData.title.modalTitle );
}
} )( jQuery );
Loading

0 comments on commit 2b9118e

Please sign in to comment.