'; echo '' . esc_html__('AEO Bob Lite Error:', 'aeo-bob-lite') . ' '; echo esc_html( sprintf( /* translators: %s: Current PHP version */ __('This plugin requires PHP 7.4 or higher. Your current PHP version is %s. Please contact your hosting provider to upgrade PHP.', 'aeo-bob-lite'), PHP_VERSION ) ); echo '

'; }); return; } // Check WordPress version compatibility if (version_compare(get_bloginfo('version'), '5.0', '<')) { add_action('admin_notices', function() { echo '

'; echo '' . esc_html__('AEO Bob Lite Error:', 'aeo-bob-lite') . ' '; echo esc_html__('This plugin requires WordPress 5.0 or higher. Please upgrade WordPress.', 'aeo-bob-lite'); echo '

'; }); return; } // Define plugin constants (with checks to prevent conflicts) if ( ! defined( 'AEO_BOB_LITE_VERSION' ) ) { define( 'AEO_BOB_LITE_VERSION', '1.5.93' ); } if (!defined('AEO_BOB_LITE_FILE')) { define('AEO_BOB_LITE_FILE', __FILE__); } if (!defined('AEO_BOB_LITE_PATH')) { define('AEO_BOB_LITE_PATH', plugin_dir_path(__FILE__)); } if (!defined('AEO_BOB_LITE_URL')) { define('AEO_BOB_LITE_URL', plugin_dir_url(__FILE__)); } /** * v1.5.80: the one official AEO Bob logo (same file as AEO Bob Pro's aeo_bob_logo_url(): * assets/images/aeoboblogo.png, 512x512). Every Lite screen uses this URL. */ if (!function_exists('aeo_bob_lite_logo_url')) { function aeo_bob_lite_logo_url() { return plugins_url('assets/images/aeoboblogo.png', AEO_BOB_LITE_FILE); } } /** * v1.5.81: what AEO Bob Pro adds over Lite - the ONE list the dashboard "Go Pro" card, the * optimization panel and the upgrade page render, so they can't drift apart. Every entry is * something Pro ships (checked against the Pro code); Lite's own AI suggestions and apply are * not listed here because Lite has them. * * @return array[] each array('icon', 'title', 'description', 'lite' => what Lite has instead, '' = nothing) */ if (!function_exists('aeo_bob_lite_pro_features')) { function aeo_bob_lite_pro_features() { return array( // v1.5.82: these three lead, in this order. array('icon' => '✏️', 'title' => __('Title and meta description rewrites', 'aeo-bob-lite'), 'description' => __('AI rewrites of the post title and meta description.', 'aeo-bob-lite'), 'lite' => ''), array('icon' => '🧩', 'title' => __('Full-page content and grammar passes', 'aeo-bob-lite'), 'description' => __('Content and grammar suggestions for the whole page, section by section, plus headings and FAQs - not just 3 sentences per request.', 'aeo-bob-lite'), 'lite' => __('Up to 3 rewrites, 3 headings, 3 FAQs per request', 'aeo-bob-lite')), array('icon' => '📡', 'title' => __('Full AI discovery', 'aeo-bob-lite'), 'description' => __('llms-full.txt, engine prompt files and Notify AI Models, served and kept up to date for you.', 'aeo-bob-lite'), 'lite' => __('Basic llms.txt (generated on request)', 'aeo-bob-lite')), array('icon' => '🤖', 'title' => __('5 AI providers', 'aeo-bob-lite'), 'description' => __('OpenAI, Anthropic (Claude), Google (Gemini), Perplexity and Zhipu AI (GLM).', 'aeo-bob-lite'), 'lite' => __('OpenAI only', 'aeo-bob-lite')), array('icon' => '🗣', 'title' => __('Fix Voice', 'aeo-bob-lite'), 'description' => __('Repairs posts whose point of view is inconsistent; changes only the voice, never the content.', 'aeo-bob-lite'), 'lite' => ''), array('icon' => '📊', 'title' => __('Score Dashboard', 'aeo-bob-lite'), 'description' => __('Every post\'s scores in one place, "Rescore all", and exclusions for single posts, categories and tags.', 'aeo-bob-lite'), 'lite' => __('Site averages on the dashboard', 'aeo-bob-lite')), array('icon' => '🔧', 'title' => __('Bulk operations', 'aeo-bob-lite'), 'description' => __('Rescore and optimize many posts at once.', 'aeo-bob-lite'), 'lite' => ''), array('icon' => '🧱', 'title' => __('Elementor floating panel', 'aeo-bob-lite'), 'description' => __('An AEO Bob panel that floats over the Elementor editor.', 'aeo-bob-lite'), 'lite' => __('A card in the Elementor widget panel', 'aeo-bob-lite')), array('icon' => '🔎', 'title' => __('Leaked-notes check', 'aeo-bob-lite'), 'description' => __('Finds AI reviewer notes and duplicated paragraphs left in your posts.', 'aeo-bob-lite'), 'lite' => ''), array('icon' => '✉️', 'title' => __('Priority email support', 'aeo-bob-lite'), 'description' => __('For AEO Bob Pro customers.', 'aeo-bob-lite'), 'lite' => ''), ); } } /** * v1.5.81: one sentence on what Lite itself does with AI (shown next to the Pro list). */ if (!function_exists('aeo_bob_lite_ai_summary')) { function aeo_bob_lite_ai_summary() { return __('Lite already includes AI suggestions and apply with your own OpenAI key: up to 3 rewrites, 3 headings and 3 FAQs per request.', 'aeo-bob-lite'); } } /** * v1.5.80: content-hashed version for a Lite asset (Pro 2.6.8 cache fix): the ?ver= changes * whenever the file changes, so a page cache or the browser never serves an old copy of a * script after an update. */ if (!function_exists('aeo_bob_lite_asset_ver')) { function aeo_bob_lite_asset_ver($relative_path) { $file = AEO_BOB_LITE_PATH . ltrim($relative_path, '/'); $hash = is_readable($file) ? md5_file($file) : ''; return AEO_BOB_LITE_VERSION . ($hash ? '.' . substr($hash, 0, 10) : ''); } } /* * v1.5.79: cap stored scores at 100 on read (port of AEO Bob Pro 2.6.3 aeo_bob_cap_stored_score). * Lite shares _aeo_bob_aeo_score/_aeo_bob_seo_score with Pro, and Pro before 2.6.3 could store up * to 115. Pro deactivates Lite, so a site that goes back from Pro to Lite would show "108/100". * Every Lite screen reads the scores via get_post_meta(), so one read filter covers them all. */ if (!function_exists('aeo_bob_lite_cap_stored_score')) { function aeo_bob_lite_cap_stored_score($value, $object_id, $meta_key, $single) { if ($value !== null || !in_array($meta_key, array('_aeo_bob_aeo_score', '_aeo_bob_seo_score'), true)) { return $value; } remove_filter('get_post_metadata', 'aeo_bob_lite_cap_stored_score', 10); $raw = get_post_meta($object_id, $meta_key, $single); add_filter('get_post_metadata', 'aeo_bob_lite_cap_stored_score', 10, 4); $cap = function ($v) { return (is_numeric($v) && $v > 100) ? (is_string($v) ? '100' : 100) : $v; }; // WP returns $check[0] for $single, so wrap the single value in an array. return $single ? array($cap($raw)) : array_map($cap, (array) $raw); } add_filter('get_post_metadata', 'aeo_bob_lite_cap_stored_score', 10, 4); } /** * Main AEO Bob Lite Class */ class AEO_Bob_Lite { /** * Single instance */ private static $instance = null; /** * Get singleton instance */ public static function get_instance() { if (null === self::$instance) { self::$instance = new self(); } return self::$instance; } /** * Constructor */ private function __construct() { $this->load_dependencies(); $this->init_hooks(); } /** * Load plugin dependencies */ private function load_dependencies() { // Load Lite-specific classes require_once AEO_BOB_LITE_PATH . 'includes/class-aeo-bob-lite-text.php'; require_once AEO_BOB_LITE_PATH . 'includes/class-aeo-bob-lite-openai.php'; require_once AEO_BOB_LITE_PATH . 'includes/class-aeo-bob-lite-settings.php'; require_once AEO_BOB_LITE_PATH . 'includes/class-aeo-bob-lite-optimizer.php'; require_once AEO_BOB_LITE_PATH . 'includes/class-aeo-bob-lite-admin.php'; require_once AEO_BOB_LITE_PATH . 'includes/class-aeo-bob-lite-post-columns.php'; require_once AEO_BOB_LITE_PATH . 'includes/class-aeo-bob-lite-meta-box.php'; require_once AEO_BOB_LITE_PATH . 'includes/class-aeo-bob-lite-elementor.php'; require_once AEO_BOB_LITE_PATH . 'includes/class-aeo-bob-lite-llms.php'; // v1.5.85 require_once AEO_BOB_LITE_PATH . 'includes/class-aeo-bob-lite-schema.php'; // v1.5.92 } /** * Initialize WordPress hooks */ private function init_hooks() { // Admin initialization if (is_admin()) { add_action('admin_menu', array($this, 'add_admin_menu')); add_action('admin_enqueue_scripts', array($this, 'enqueue_admin_assets')); // Initialize admin class (for AJAX handlers) AEO_Bob_Lite_Admin::get_instance(); // Initialize post columns AEO_Bob_Lite_Post_Columns::get_instance(); // Initialize meta box (includes both sidebar and main optimization panel) AEO_Bob_Lite_Meta_Box::get_instance(); } // v1.5.85: rescore on every save - in ALL requests (the block editor saves through the REST // API, where is_admin() is false). Only queues; scoring runs once at shutdown, never on a // front-end page view. add_action('save_post', array('AEO_Bob_Lite_Admin', 'queue_rescore'), 20, 2); // v1.5.92: post schema (JSON-LD in the head, refreshed on save) AEO_Bob_Lite_Schema::get_instance(); // Initialize Elementor integration (checks for Elementor internally) AEO_Bob_Lite_Elementor::get_instance(); // Settings link on plugins page add_filter('plugin_action_links_' . plugin_basename(AEO_BOB_LITE_FILE), array($this, 'add_action_links')); } /** * Add admin menu */ public function add_admin_menu() { add_menu_page( __('AEO Bob Lite', 'aeo-bob-lite'), __('AEO Bob', 'aeo-bob-lite'), 'manage_options', 'aeo-bob-lite', array($this, 'render_dashboard'), 'dashicons-chart-line', 30 ); add_submenu_page( 'aeo-bob-lite', __('Dashboard', 'aeo-bob-lite'), __('Dashboard', 'aeo-bob-lite'), 'manage_options', 'aeo-bob-lite', array($this, 'render_dashboard') ); add_submenu_page( 'aeo-bob-lite', __('Settings', 'aeo-bob-lite'), __('Settings', 'aeo-bob-lite'), 'manage_options', 'aeo-bob-lite-settings', array($this, 'render_settings_page') ); // v1.5.86: registered here with the other items (same hook, same moment, before Go Pro). In // 1.5.85 the llms.txt class added it from its own admin_menu callback at priority 11. add_submenu_page( 'aeo-bob-lite', __('llms.txt', 'aeo-bob-lite'), __('llms.txt', 'aeo-bob-lite'), 'manage_options', AEO_Bob_Lite_Llms::PAGE, array(AEO_Bob_Lite_Llms::get_instance(), 'render_page') ); add_submenu_page( 'aeo-bob-lite', __('Go Pro', 'aeo-bob-lite'), __( 'Go Pro', 'aeo-bob-lite' ), 'manage_options', 'aeo-bob-lite-upgrade', array($this, 'render_upgrade_page') ); } /** * Render dashboard page */ public function render_dashboard() { include AEO_BOB_LITE_PATH . 'admin/dashboard.php'; } /** * Render settings page */ public function render_settings_page() { $settings = AEO_Bob_Lite_Settings::get_instance(); $settings->render_settings_page(); } /** * Render upgrade page */ public function render_upgrade_page() { include AEO_BOB_LITE_PATH . 'admin/upgrade.php'; } /** * Enqueue admin assets */ public function enqueue_admin_assets($hook) { // Only load on our pages and post list screens if (strpos($hook, 'aeo-bob-lite') === false && !in_array($hook, array('post.php', 'post-new.php', 'edit.php'))) { return; } // CSS wp_enqueue_style( 'aeo-bob-lite-admin', AEO_BOB_LITE_URL . 'assets/css/admin.css', array(), aeo_bob_lite_asset_ver('assets/css/admin.css') ); // v1.5.86: no admin.js - its handlers targeted buttons none of these pages have. } /** * Add action links on plugins page */ public function add_action_links($links) { $custom_links = array( '' . esc_html__('Dashboard', 'aeo-bob-lite') . '', '' . esc_html__('Go Pro', 'aeo-bob-lite') . '', ); return array_merge($custom_links, $links); } } // Initialize plugin function aeo_bob_lite() { return AEO_Bob_Lite::get_instance(); } /** * Plugin activation callback */ function aeo_bob_lite_activate() { // v1.5.86: no database tables - Lite keeps everything in post meta and options (uninstall.php // still drops the aeo_bob_* tables older versions created). // Set default options add_option('aeo_bob_lite_version', AEO_BOB_LITE_VERSION); add_option('aeo_bob_lite_installed', time()); // Flush rewrite rules flush_rewrite_rules(); } /** * Plugin deactivation callback */ function aeo_bob_lite_deactivate() { // Clean up (but don't delete data) flush_rewrite_rules(); } // Register activation/deactivation hooks (BEFORE plugin starts) register_activation_hook(__FILE__, 'aeo_bob_lite_activate'); register_deactivation_hook(__FILE__, 'aeo_bob_lite_deactivate'); // Start the plugin aeo_bob_lite();