init_hooks(); } /** * Initialize hooks */ private function init_hooks() { // Enqueue scripts for Elementor editor add_action('elementor/editor/after_enqueue_scripts', array($this, 'enqueue_editor_scripts')); } /** * Check if we're in Elementor editor */ private function is_elementor_editor() { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only check for Elementor editor context return isset($_GET['action']) && sanitize_text_field(wp_unslash($_GET['action'])) === 'elementor'; } /** * Enqueue editor scripts */ public function enqueue_editor_scripts() { if (!$this->is_elementor_editor()) { return; } $script_url = plugins_url('assets/js/elementor-panel.js', dirname(__FILE__)); // Enqueue our Elementor panel script wp_enqueue_script( 'aeo-bob-lite-elementor-panel', $script_url, array('jquery', 'elementor-editor'), AEO_BOB_LITE_VERSION, true ); // Enqueue styles wp_enqueue_style( 'aeo-bob-lite-elementor-panel', plugins_url('assets/css/admin.css', dirname(__FILE__)), array(), AEO_BOB_LITE_VERSION ); // Add inline data AFTER the script is enqueued $this->add_inline_editor_data(); } /** * Localize editor data - mirrors Classic/Block editor data structure * * SECURITY NOTE: This is a script localization function (not AJAX handler). * It runs during 'elementor/editor/after_enqueue_scripts' hook in admin context. * Security is ensured by: * 1. Using global $post (no user input) to get post ID * 2. Verifying current_user_can('edit_post') before exposing any data * 3. Creating a nonce that will be verified when JS makes AJAX calls */ private function add_inline_editor_data() { // Get post ID from WordPress global $post object (safe - no user input) global $post; $post_id = 0; if ( $post && isset( $post->ID ) ) { $post_id = absint( $post->ID ); } elseif ( function_exists( 'get_the_ID' ) ) { $post_id = absint( get_the_ID() ); } // Exit early if no valid post ID available if ( ! $post_id ) { return; } // SECURITY: Capability check - only users who can edit this post get data if ( ! current_user_can( 'edit_post', $post_id ) ) { return; } // Get current scores (same as Classic/Block editor) $seo_score = get_post_meta($post_id, '_aeo_bob_seo_score', true); $aeo_score = get_post_meta($post_id, '_aeo_bob_aeo_score', true); // Check if API key is configured $api_key = get_option('aeo_bob_lite_openai_key', ''); $has_api_key = !empty($api_key); // Build optimize URL - opens the edit screen with anchor to optimization panel $optimize_url = admin_url('post.php?post=' . $post_id . '&action=edit#aeo-bob-lite-optimizer'); // Prepare data for JavaScript using wp_localize_script wp_localize_script( 'aeo-bob-lite-elementor-panel', 'aeoBobLiteElementorData', array( 'postId' => $post_id, 'ajaxUrl' => admin_url( 'admin-ajax.php' ), 'nonce' => wp_create_nonce( 'aeo_bob_lite_nonce' ), 'seoScore' => $seo_score ? intval( $seo_score ) : 0, 'aeoScore' => $aeo_score ? intval( $aeo_score ) : 0, 'hasApiKey' => $has_api_key, 'optimizeUrl' => $optimize_url, 'version' => AEO_BOB_LITE_VERSION, 'logoUrl' => AEO_BOB_LITE_URL . 'assets/images/aeoboblogo.png', 'i18n' => array( // Button texts 'optimizeContent' => __( 'OPTIMIZE CONTENT', 'aeo-bob-lite' ), 'reAnalyze' => __( 'RE-ANALYZE', 'aeo-bob-lite' ), 'analyzeContent' => __( 'ANALYZE CONTENT', 'aeo-bob-lite' ), 'analyzing' => __( 'Analyzing...', 'aeo-bob-lite' ), 'generating' => __( 'Generating...', 'aeo-bob-lite' ), 'applying' => __( 'Applying...', 'aeo-bob-lite' ), 'copyPasteManually' => __( 'Copy/paste these suggestions manually into your content', 'aeo-bob-lite' ), // API key messages 'addApiKeyInSettings' => __( 'Add API key in settings', 'aeo-bob-lite' ), 'noApiKeyConfigured' => __( 'No API key configured', 'aeo-bob-lite' ), 'connected' => __( 'Connected', 'aeo-bob-lite' ), 'openaiConnection' => __( 'OpenAI Connection', 'aeo-bob-lite' ), // Status messages 'analysisComplete' => __( 'Analysis Complete!', 'aeo-bob-lite' ), 'clickOptimizeToContinue' => __( 'Click "Optimize" to continue', 'aeo-bob-lite' ), 'analysisFailed' => __( 'Analysis failed', 'aeo-bob-lite' ), 'networkError' => __( 'Network error. Please try again.', 'aeo-bob-lite' ), 'suggestionsGenerated' => __( 'Suggestions generated!', 'aeo-bob-lite' ), 'failedToGenerateSuggestions' => __( 'Failed to generate suggestions', 'aeo-bob-lite' ), 'voiceAnalyzedSuccess' => __( 'Voice analyzed successfully!', 'aeo-bob-lite' ), 'analyzingYourWritingStyle' => __( 'Analyzing your writing style...', 'aeo-bob-lite' ), 'generatingAiSuggestions' => __( 'Generating AI suggestions...', 'aeo-bob-lite' ), // Panel titles 'contentOptimizer' => __( 'Content Optimizer', 'aeo-bob-lite' ), 'close' => __( 'Close', 'aeo-bob-lite' ), 'currentScores' => __( 'Current Scores', 'aeo-bob-lite' ), 'seoScore' => __( 'SEO Score', 'aeo-bob-lite' ), 'aeoScore' => __( 'AEO Score', 'aeo-bob-lite' ), 'seo' => __( 'SEO', 'aeo-bob-lite' ), 'aeo' => __( 'AEO', 'aeo-bob-lite' ), // Step labels 'step1AnalyzeVoice' => __( 'Step 1: Analyze Author Voice', 'aeo-bob-lite' ), 'step2GetSuggestions' => __( 'Step 2: Get AI Suggestions', 'aeo-bob-lite' ), 'analyzeVoice' => __( 'Analyze Voice', 'aeo-bob-lite' ), 'reAnalyzeVoice' => __( 'Re-analyze Voice', 'aeo-bob-lite' ), 'getAiSuggestions' => __( 'Get AI Suggestions', 'aeo-bob-lite' ), 'getNewSuggestions' => __( 'Get New Suggestions', 'aeo-bob-lite' ), // Voice profile 'style' => __( 'Style', 'aeo-bob-lite' ), 'tone' => __( 'Tone', 'aeo-bob-lite' ), 'readingLevel' => __( 'Reading Level', 'aeo-bob-lite' ), // Suggestions 'contentImprovements' => __( 'Content Improvements', 'aeo-bob-lite' ), 'faqSuggestions' => __( 'FAQ Suggestions', 'aeo-bob-lite' ), 'applySelectedSuggestions' => __( 'Apply Selected Suggestions', 'aeo-bob-lite' ), 'selectAtLeastOne' => __( 'Please select at least one suggestion to apply.', 'aeo-bob-lite' ), /* translators: %d: number of suggestions to apply */ 'confirmApply' => __( 'Apply %d suggestion(s) to your content?', 'aeo-bob-lite' ), 'confirmApplyNote' => __( 'Note: This will modify your Elementor content.', 'aeo-bob-lite' ), // Apply results 'suggestionsApplied' => __( 'Suggestions applied!', 'aeo-bob-lite' ), /* translators: %s: number of new sections added */ 'reloadingElementor' => __( 'Reloading Elementor to show %s new sections...', 'aeo-bob-lite' ), 'failedToUpdateElementor' => __( 'Failed to update Elementor content.', 'aeo-bob-lite' ), 'backendNotConfirmed' => __( 'The backend did not confirm the Elementor document was updated.', 'aeo-bob-lite' ), 'unknownError' => __( 'Unknown error', 'aeo-bob-lite' ), 'failedToApplySuggestions' => __( 'Failed to apply suggestions:', 'aeo-bob-lite' ), 'networkErrorApplying' => __( 'Network error while applying suggestions. Please try again.', 'aeo-bob-lite' ), ), ) ); } }