settings = $settings;
$this->ai_helper = new AIHelper( $settings );
// Only initialize for docs post type in admin
if ( is_admin() ) {
add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_admin_scripts' ] );
add_action( 'wp_ajax_betterdocs_analyze_quality', [ $this, 'ajax_analyze_article_quality' ] );
add_action( 'wp_ajax_betterdocs_save_quality_analysis', [ $this, 'ajax_save_quality_analysis' ] );
add_action( 'wp_ajax_betterdocs_check_cached_analysis', [ $this, 'ajax_check_cached_analysis' ] );
// Hook into unified metabox
add_action( 'betterdocs_quality_analysis_tab_content', [ $this, 'render_quality_analysis_content' ] );
}
}
/**
* Enqueue admin scripts for quality score functionality
*/
public function enqueue_admin_scripts( $hook ) {
global $post_type;
// Only load on docs edit screens
if ( ( $hook === 'post.php' || $hook === 'post-new.php' ) && $post_type === 'docs' ) {
betterdocs()->assets->enqueue( 'betterdocs-article-quality-score', 'admin/js/article-quality-score.js' );
// Localize script with AJAX data
wp_localize_script(
'betterdocs-article-quality-score',
'betterdocsQualityScore',
[
'ajax_url' => admin_url( 'admin-ajax.php' ),
'nonce' => wp_create_nonce( 'betterdocs_quality_score_nonce' ),
'post_id' => get_the_ID(),
'strings' => [
'analysing' => __( 'Analysing your docs with BetterDocs AI...', 'betterdocs' ),
'error' => __( 'Error', 'betterdocs' ),
'regenerate' => __( 'Regenerate', 'betterdocs' ),
'analyze' => __( 'Analyze with BetterDocs AI', 'betterdocs' ),
]
]
);
}
}
/**
* Render quality analysis content for the unified metabox tab
*
* @param \WP_Post $post Current post object
*/
public function render_quality_analysis_content( $post ) {
$nonce = wp_create_nonce( 'betterdocs_quality_score_nonce' );
$cached_analysis = get_post_meta( $post->ID, '_betterdocs_article_quality_analysis', true );
$has_results = ! empty( $cached_analysis ) && isset( $cached_analysis['overall_score'] );
?>
render_quality_results( $cached_analysis ); ?>
get_score_status_class( $overall_score );
$circumference = 2 * M_PI * 54; // radius = 54
$offset = $circumference - ( $overall_score / 100 ) * $circumference;
?>
get_score_status_text( $overall_score ) ); ?>
= 80 ) return 'green';
if ( $score >= 50 ) return 'yellow';
if ( $score >= 30 ) return 'orange';
return 'red';
}
/**
* Get text for score status
*
* @param int $score Overall score
* @return string Status text
*/
private function get_score_status_text( $score ) {
if ( $score >= 80 ) return __( 'Excellent', 'betterdocs' );
if ( $score >= 60 ) return __( 'Good', 'betterdocs' );
if ( $score >= 40 ) return __( 'Needs Improvement', 'betterdocs' );
return __( 'Poor', 'betterdocs' );
}
/**
* AJAX handler for docs quality analysis
*/
public function ajax_analyze_article_quality() {
// Verify nonce
if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( $_POST['nonce'], 'betterdocs_quality_score_nonce' ) ) {
wp_send_json_error( [ 'message' => __( 'Invalid nonce', 'betterdocs' ) ] );
}
$post_id = isset( $_POST['post_id'] ) ? intval( $_POST['post_id'] ) : 0;
if ( empty( $post_id ) ) {
wp_send_json_error( [ 'message' => __( 'Invalid post ID', 'betterdocs' ) ] );
}
// SECURITY: Check if user can edit THIS SPECIFIC post (prevents IDOR)
if ( ! current_user_can( 'edit_post', $post_id ) ) {
wp_send_json_error( [ 'message' => __( 'Insufficient permissions', 'betterdocs' ) ] );
}
$post = get_post( $post_id );
if ( ! $post || $post->post_type !== 'docs' ) {
wp_send_json_error( [ 'message' => __( 'Invalid post or post type', 'betterdocs' ) ] );
}
// Check if current content is provided (from editor, may be unsaved)
$current_content = isset( $_POST['current_content'] ) ? wp_kses_post( $_POST['current_content'] ) : '';
$current_title = isset( $_POST['current_title'] ) ? sanitize_text_field( $_POST['current_title'] ) : '';
// Use current content if provided, otherwise use saved post content
if ( ! empty( $current_content ) ) {
$content = $current_content;
$title = ! empty( $current_title ) ? $current_title : $post->post_title;
} else {
$content = $post->post_content;
$title = $post->post_title;
}
if ( empty( $content ) ) {
wp_send_json_error( [ 'message' => __( 'Post content is empty. Please add content to your post and try again.', 'betterdocs' ) ] );
}
// Strip HTML tags and shortcodes for analysis
$clean_content = wp_strip_all_tags( do_shortcode( $content ) );
$clean_content = preg_replace( '/\s+/', ' ', trim( $clean_content ) );
if ( strlen( $clean_content ) < 50 ) {
wp_send_json_error( [ 'message' => __( 'Content is too short for meaningful analysis. Please add more content (at least 50 characters) and try again.', 'betterdocs' ) ] );
}
// Perform AI analysis
$analysis_result = $this->ai_helper->analyze_article_quality( $clean_content, $title );
if ( is_wp_error( $analysis_result ) ) {
wp_send_json_error( [ 'message' => $analysis_result->get_error_message() ] );
}
// Save the analysis result
$saved = $this->ai_helper->save_article_quality_score( $post_id, $analysis_result );
if ( ! $saved ) {
wp_send_json_error( [ 'message' => __( 'Failed to save analysis result', 'betterdocs' ) ] );
}
// Return success with the analysis data
wp_send_json_success( [
'message' => __( 'Docs analyzed successfully', 'betterdocs' ),
'data' => $analysis_result
] );
}
/**
* AJAX handler for saving quality analysis results
*/
public function ajax_save_quality_analysis() {
// Verify nonce
if ( ! wp_verify_nonce( $_POST['nonce'], 'betterdocs_quality_score_nonce' ) ) {
wp_send_json_error( __( 'Security check failed', 'betterdocs' ) );
}
$post_id = isset( $_POST['post_id'] ) ? intval( $_POST['post_id'] ) : 0;
$analysis_data = isset( $_POST['analysis_data'] ) ? $_POST['analysis_data'] : '';
if ( ! $post_id || ! $analysis_data ) {
wp_send_json_error( __( 'Invalid data provided', 'betterdocs' ) );
}
// SECURITY: Check if user can edit THIS SPECIFIC post (prevents IDOR)
if ( ! current_user_can( 'edit_post', $post_id ) ) {
wp_send_json_error( __( 'Insufficient permissions', 'betterdocs' ) );
}
// Decode the analysis data
$decoded_data = json_decode( stripslashes( $analysis_data ), true );
if ( ! $decoded_data ) {
wp_send_json_error( __( 'Invalid analysis data format', 'betterdocs' ) );
}
// Add timestamp and post modified date to the analysis data
$post = get_post( $post_id );
$decoded_data['analysis_timestamp'] = current_time( 'timestamp' );
$decoded_data['post_modified'] = $post->post_modified;
// Save the analysis data as post meta
$meta_key = '_betterdocs_article_quality_analysis';
$saved = update_post_meta( $post_id, $meta_key, $decoded_data );
if ( $saved !== false ) {
wp_send_json_success( [
'message' => __( 'Analysis results saved successfully', 'betterdocs' ),
'data' => $decoded_data
] );
} else {
wp_send_json_error( __( 'Failed to save analysis results', 'betterdocs' ) );
}
}
/**
* AJAX handler for checking cached analysis results
*/
public function ajax_check_cached_analysis() {
// Verify nonce
if ( ! wp_verify_nonce( $_POST['nonce'], 'betterdocs_quality_score_nonce' ) ) {
wp_send_json_error( __( 'Security check failed', 'betterdocs' ) );
}
$post_id = isset( $_POST['post_id'] ) ? intval( $_POST['post_id'] ) : 0;
if ( ! $post_id ) {
wp_send_json_error( __( 'Invalid post ID', 'betterdocs' ) );
}
// SECURITY: Check if user can edit THIS SPECIFIC post (prevents IDOR)
if ( ! current_user_can( 'edit_post', $post_id ) ) {
wp_send_json_error( __( 'Insufficient permissions', 'betterdocs' ) );
}
// Get the post and its last modified time
$post = get_post( $post_id );
if ( ! $post ) {
wp_send_json_error( __( 'Post not found', 'betterdocs' ) );
}
// Get cached analysis data
$cached_analysis = get_post_meta( $post_id, '_betterdocs_article_quality_analysis', true );
// Check if we have cached data and if the post hasn't been modified since analysis
if ( $cached_analysis &&
isset( $cached_analysis['post_modified'] ) &&
$cached_analysis['post_modified'] === $post->post_modified ) {
// Return cached data
wp_send_json_success( [
'has_cache' => true,
'message' => __( 'Using cached analysis results', 'betterdocs' ),
'data' => $cached_analysis
] );
} else {
// No cache or post has been updated
wp_send_json_success( [
'has_cache' => false,
'message' => __( 'No valid cache found, new analysis required', 'betterdocs' )
] );
}
}
}