';
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.92' );
}
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();