'; echo esc_html__('A legacy Aetos SEO Pro replacement build is active, so this WordPress.org edition is standing down to avoid duplicate Aetos admin screens. Your settings and audit history remain available.', 'aetos-seo'); echo '

'; }); } unset($aetos_self_basename, $aetos_sibling); return; } unset($aetos_sibling); } unset($aetos_self_basename); // Constants. if (!defined('AETOS_SEO_VERSION')) { define('AETOS_SEO_VERSION', '4.0.18'); } // Edition gate. The build sets this constant for each edition; the audit // engine reads it to scope itself to the matching feature set. if (!defined('AETOS_EDITION')) { define('AETOS_EDITION', 'lite'); } // DB schema version (used for automatic upgrades via Activator::maybe_upgrade()). // v3.3.40 — bumped so the one-time crawl_max_urls 1000→200 settings migration // in Activator::maybe_upgrade() fires once for every customer on upgrade. if (!defined('AETOS_SEO_DB_VERSION')) { define('AETOS_SEO_DB_VERSION', '2026-05-18-1'); } if (!defined('AETOS_SEO_SLUG')) { $aetos_seo_init_base = plugin_basename(__FILE__); $aetos_seo_init_slug = dirname($aetos_seo_init_base); if (!is_string($aetos_seo_init_slug) || $aetos_seo_init_slug === '' || $aetos_seo_init_slug === '.') { $aetos_seo_init_slug = 'aetos-seo'; } define('AETOS_SEO_SLUG', $aetos_seo_init_slug); unset($aetos_seo_init_base, $aetos_seo_init_slug); } // The plugin's own active_plugins entry ("folder/main-file.php"), captured from // the real loading file. Each edition ships its own folder/main-file path, so // anything that needs to // recognise "our own row" must read THIS, never a hardcoded slug — see // Activator::normalize_active_plugin_entries() (the old hardcoded canonical // silently self-deactivated any install whose path merely contained "aetos-seo"). if (!defined('AETOS_SEO_BASENAME')) { define('AETOS_SEO_BASENAME', str_replace('\\', '/', plugin_basename(__FILE__))); } if (!defined('AETOS_SEO_PATH')) { define('AETOS_SEO_PATH', plugin_dir_path(__FILE__)); } if (!defined('AETOS_SEO_URL')) { define('AETOS_SEO_URL', plugin_dir_url(__FILE__)); } // Aetos SEO product identity. Public product surfaces use the Aetos brand; // legal publisher details remain on the website's legal pages. if (!defined('AETOS_SEO_BRAND_NAME')) { define('AETOS_SEO_BRAND_NAME', 'Aetos SEO'); } if (!defined('AETOS_SEO_BRAND_NAME_AR')) { // Brand-mark stays in Latin script even in Arabic UI (industry-standard // for global SaaS). The Arabic publisher line below reads "نسر المتاجر". define('AETOS_SEO_BRAND_NAME_AR', 'Aetos SEO'); } if (!defined('AETOS_SEO_BRAND_TAGLINE')) { define('AETOS_SEO_BRAND_TAGLINE', 'SEO & AI Auditor for WordPress'); } if (!defined('AETOS_SEO_BRAND_TAGLINE_AR')) { define('AETOS_SEO_BRAND_TAGLINE_AR', 'فحص SEO و AI لمواقع ووردبريس'); } if (!defined('AETOS_SEO_PUBLISHER')) { define('AETOS_SEO_PUBLISHER', 'Aetos SEO'); } if (!defined('AETOS_SEO_PUBLISHER_AR')) { define('AETOS_SEO_PUBLISHER_AR', 'Aetos SEO'); } if (!defined('AETOS_SEO_BRAND_URL')) { define('AETOS_SEO_BRAND_URL', 'https://aetosseo.com'); } if (!defined('AETOS_SEO_BRAND_EMAIL')) { define('AETOS_SEO_BRAND_EMAIL', 'support@aetosseo.com'); } if (!defined('AETOS_SEO_BRAND_WHATSAPP')) { define('AETOS_SEO_BRAND_WHATSAPP', '+201068187346'); } if (!defined('AETOS_SEO_BRAND_PRIMARY')) { define('AETOS_SEO_BRAND_PRIMARY', '#162534'); } if (!defined('AETOS_SEO_BRAND_ACCENT')) { define('AETOS_SEO_BRAND_ACCENT', '#32A832'); } $aetos_seo_base_path = plugin_dir_path(__FILE__); /** * Locate a plugin file with safe fallbacks. * Handles rare broken extraction cases where backslashes are stored * as literal characters in file names. */ if (!function_exists('aetos_seo_locate_file')) { function aetos_seo_locate_file(string $relative): string { $base = plugin_dir_path(__FILE__); $rel = ltrim($relative, '\\/'); $rel_slash = str_replace('\\', '/', $rel); $rel_back = str_replace('/', '\\', $rel_slash); $candidates = [ $base . $rel_slash, $base . $rel_back, $base . 'aetos-seo/' . $rel_slash, $base . 'aetos-seo\\' . $rel_back, ]; foreach ($candidates as $file) { if (is_string($file) && $file !== '' && is_readable($file)) { return $file; } } return ''; } } if (!function_exists('aetos_seo_plugins_root')) { function aetos_seo_plugins_root(): string { // active_plugins basenames resolve relative to the plugins directory, so it is // the canonical plugins root even on custom wp-content layouts. The // fallback is the direct parent of THIS plugin's folder — never a // fixed-level walk that assumes the default directory structure. return dirname(rtrim((string) plugin_dir_path(__FILE__), '/\\')); } } if (!function_exists('aetos_seo_site_root_path')) { function aetos_seo_site_root_path(): string { // Prefer WordPress' public home-path helper when it is already loaded. if (function_exists('get_home_path')) { $home_path = (string) get_home_path(); if ($home_path !== '' && $home_path !== '/' && is_dir($home_path)) { return rtrim($home_path, '/\\'); } } return ''; } } if (!function_exists('aetos_seo_plugin_headers')) { /** * @return array */ function aetos_seo_plugin_headers(string $file): array { $headers = [ 'Name' => 'Plugin Name', 'PluginURI' => 'Plugin URI', 'Version' => 'Version', 'Description' => 'Description', 'Author' => 'Author', 'AuthorURI' => 'Author URI', 'TextDomain' => 'Text Domain', 'DomainPath' => 'Domain Path', ]; if (function_exists('get_file_data')) { $data = get_file_data($file, $headers, 'plugin'); return is_array($data) ? array_map('strval', $data) : []; } $contents = is_readable($file) ? (string) file_get_contents($file, false, null, 0, 8192) : ''; $data = []; foreach ($headers as $field => $label) { $data[$field] = preg_match('/^[ \t\/*#@]*' . preg_quote($label, '/') . ':(.*)$/mi', $contents, $m) ? trim((string) $m[1]) : ''; } return $data; } } if (!function_exists('aetos_seo_get_plugins')) { /** * Lightweight get_plugins() fallback without loading wp-admin files. * * @return array> */ function aetos_seo_get_plugins(): array { if (function_exists('get_plugins')) { $plugins = get_plugins(); return is_array($plugins) ? $plugins : []; } $root = function_exists('aetos_seo_plugins_root') ? aetos_seo_plugins_root() : ''; if ($root === '' || !is_dir($root)) { return []; } $files = []; foreach ((array) glob($root . DIRECTORY_SEPARATOR . '*.php') as $file) { if (is_string($file)) { $files[basename($file)] = $file; } } foreach ((array) glob($root . DIRECTORY_SEPARATOR . '*', GLOB_ONLYDIR) as $dir) { if (!is_string($dir)) { continue; } foreach ((array) glob($dir . DIRECTORY_SEPARATOR . '*.php') as $file) { if (is_string($file)) { $files[basename($dir) . '/' . basename($file)] = $file; } } } $plugins = []; foreach ($files as $basename => $file) { $data = aetos_seo_plugin_headers($file); if (($data['Name'] ?? '') === '') { continue; } $plugins[(string) $basename] = $data; } return $plugins; } } if (!function_exists('aetos_seo_is_plugin_active')) { function aetos_seo_is_plugin_active(string $plugin_file): bool { if (function_exists('is_plugin_active')) { return (bool) is_plugin_active($plugin_file); } $active = function_exists('get_option') ? (array) get_option('active_plugins', []) : []; if (in_array($plugin_file, array_map('strval', $active), true)) { return true; } if (function_exists('is_multisite') && is_multisite() && function_exists('get_site_option')) { $network_active = (array) get_site_option('active_sitewide_plugins', []); return isset($network_active[$plugin_file]); } return false; } } /** * Resolve an asset URL safely even if the plugin folder was nested during a bad * replace-current deployment (e.g. aetos-seo/aetos-seo/...). */ if (!function_exists('aetos_seo_asset_url')) { function aetos_seo_asset_url(string $relative): string { $rel = ltrim(str_replace('\\', '/', $relative), '/'); $base_path = rtrim(plugin_dir_path(__FILE__), '\\/'); $base_url = trailingslashit(plugin_dir_url(__FILE__)); $parent_path = dirname($base_path); $parent_url = trailingslashit(dirname(rtrim($base_url, '/'))); $candidates = [ [$base_path, $base_url], [$base_path . DIRECTORY_SEPARATOR . 'aetos-seo', trailingslashit($base_url . 'aetos-seo')], [$parent_path, $parent_url], [$parent_path . DIRECTORY_SEPARATOR . 'aetos-seo', trailingslashit($parent_url . 'aetos-seo')], ]; foreach ($candidates as [$path_base, $url_base]) { $path_base = is_string($path_base) ? rtrim($path_base, '\\/') : ''; $url_base = is_string($url_base) ? trailingslashit($url_base) : ''; if ($path_base === '' || $url_base === '') { continue; } $file = $path_base . DIRECTORY_SEPARATOR . str_replace('/', DIRECTORY_SEPARATOR, $rel); if (is_readable($file)) { return $url_base . $rel; } } return $base_url . $rel; } } // Simple PSR-4-ish autoloader. spl_autoload_register(static function (string $class) use ($aetos_seo_base_path): void { $prefix = 'AetosSeo\\'; if (strpos($class, $prefix) !== 0) { return; } $relative = substr($class, strlen($prefix)); $relative = str_replace('\\', '/', $relative) . '.php'; $file = aetos_seo_locate_file('includes/' . $relative); if ($file !== '') { require_once $file; } }); // Activation/Deactivation. register_activation_hook(__FILE__, ['AetosSeo\\Core\\Activator', 'activate']); register_deactivation_hook(__FILE__, ['AetosSeo\\Core\\Deactivator', 'deactivate']); register_uninstall_hook(__FILE__, ['AetosSeo\\Core\\Uninstaller', 'uninstall_hook']); // i18n: WordPress 4.6+ auto-loads translations from /languages when the Text Domain header is set. add_action('plugins_loaded', static function () use ($aetos_seo_base_path): void { if (!class_exists('AetosSeo\\Core\\Plugin')) { $bootstrap = aetos_seo_locate_file('includes/Core/Plugin.php'); if ($bootstrap !== '') { require_once $bootstrap; } } if (!class_exists('AetosSeo\\Core\\Plugin')) { add_action('admin_notices', static function (): void { if (!current_user_can('activate_plugins')) { return; } echo '

'; echo esc_html__('Aetos SEO bootstrap failed: Core plugin class is missing. Please re-upload the canonical Aetos SEO ZIP and ensure only one plugin folder exists.', 'aetos-seo'); echo '

'; }); return; } try { $plugin = new AetosSeo\Core\Plugin(); $plugin->boot(); } catch (\Throwable $e) { // v3.3.16 — separate the technical message (full path, for the // WP_DEBUG log) from the UI message (basename only, shown to the // customer). Gemini PASS 6 caught that the recovery notice missed // the `aetos-notice` class; Claude PASS 6 caught that it leaked the // full server filesystem path to anyone with `activate_plugins` cap. $tech_msg = 'Aetos SEO boot failed: ' . $e->getMessage() . ' in ' . $e->getFile() . ':' . $e->getLine(); $ui_msg = 'Aetos SEO boot failed: ' . $e->getMessage() . ' in ' . basename($e->getFile()) . ':' . $e->getLine(); if (defined('WP_DEBUG') && WP_DEBUG) { error_log($tech_msg); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- WP_DEBUG-guarded debug logging only } add_action('admin_notices', static function () use ($ui_msg): void { if (!current_user_can('activate_plugins')) { return; } // aetos-notice class so Layer 2 of our notice suppression // doesn't hide our own recovery banner on Aetos pages. echo '

Aetos SEO Recovery: '; echo esc_html($ui_msg); echo '

'; }); add_action('admin_menu', static function () use ($ui_msg): void { if (!current_user_can('activate_plugins') && !current_user_can('manage_options')) { return; } add_menu_page( __('Aetos SEO (Recovery)', 'aetos-seo'), __('Aetos SEO', 'aetos-seo'), 'activate_plugins', 'aetos-seo-recovery', static function () use ($ui_msg): void { echo '

Aetos SEO Recovery

'; echo '

' . esc_html__('Plugin boot failed. Use the details below to fix deployment quickly.', 'aetos-seo') . '

'; echo '
' . esc_html($ui_msg) . '
'; echo '
    '; echo '
  1. ' . esc_html__('Keep exactly one plugin folder: wp-content/plugins/aetos-seo', 'aetos-seo') . '
  2. '; echo '
  3. ' . esc_html__('Re-upload canonical aetos-seo.zip', 'aetos-seo') . '
  4. '; echo '
  5. ' . esc_html__('Activate from Installed Plugins row (not stale direct URL)', 'aetos-seo') . '
  6. '; echo '
'; }, 'dashicons-warning', 80 ); }); } }); // Plugin action-row links on the WP Plugins screen. Adds Settings plus an // Upgrade link in the free build so users can jump to the most relevant page // without opening the sidebar menu. add_filter('plugin_action_links_' . plugin_basename(__FILE__), static function ($links) { $action_links = [ 'settings' => '' . esc_html__('Settings', 'aetos-seo') . '', ]; $has_pro_addon = defined('AETOS_EDITION') && AETOS_EDITION === 'lite' && (bool) apply_filters('aetos_seo_pro_addon_active', false); if (defined('AETOS_EDITION') && AETOS_EDITION === 'lite' && !$has_pro_addon) { $action_links['upgrade'] = '' . esc_html__('Upgrade to Pro', 'aetos-seo') . ''; } return array_merge($action_links, is_array($links) ? $links : []); }); add_filter('plugin_row_meta', static function ($links, $plugin_file) { if ($plugin_file !== plugin_basename(__FILE__)) { return $links; } $extra = [ '' . esc_html__('Changelog', 'aetos-seo') . '', '' . esc_html__('Docs', 'aetos-seo') . '', '' . esc_html__('Support', 'aetos-seo') . '', ]; return array_merge(is_array($links) ? $links : [], $extra); }, 10, 2);