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 ) ); ?>

$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' ) ] ); } } }