1, 'missing' => 2, 'generated' => 4, 'queued' => 8, 'processing' => 16, 'failed' => 32, 'hidden' => 64, 'auto_queue_disallowed' => 128, 'all' => 256, 'unverified' => 512, 'waiting_to_queue' => 1024, ); const AI4SEO_POSTS_TO_BE_ANALYZED_OPTION_NAME = '_ai4seo_posts_to_be_analyzed'; const AI4SEO_NOTIFICATIONS_OPTION_NAME = '_ai4seo_notifications'; // Authenticated provenance prevents API metadata from selecting the richer trusted-local HTML policy. const AI4SEO_NOTIFICATION_MESSAGE_SOURCE_FIELD = 'message_source'; const AI4SEO_NOTIFICATION_MESSAGE_SOURCE_SIGNATURE_FIELD = 'message_source_signature'; const AI4SEO_NOTIFICATION_MESSAGE_SOURCE_LOCAL = 'local'; const AI4SEO_NOTIFICATION_MESSAGE_SOURCE_REMOTE = 'remote'; // Bound repair and mutation retries so persistent concurrency cannot monopolize a request. const AI4SEO_NOTIFICATION_CAS_MAX_ATTEMPTS = 3; // Keep general option and post-meta identifiers separate from notification provenance policy. const AI4SEO_DEBUG_MESSAGES_OPTION_NAME = 'ai4seo_debug_messages'; const AI4SEO_SETTINGS_OPTION_NAME = 'ai4seo_settings'; const AI4SEO_POST_META_GENERATED_DATA_META_KEY = 'ai4seo_generated_data'; const AI4SEO_POST_META_ACTIVE_METADATA_META_KEY = 'ai4seo_active_metadata'; const AI4SEO_POST_META_POST_CONTENT_SUMMARY_META_KEY = 'ai4seo_content_summary'; const AI4SEO_POST_META_RELATED_POST_ID_META_KEY = 'ai4seo_related_post_id'; const AI4SEO_POST_META_ATTACHMENT_CONTEXT_POLICY = 'ai4seo_attachment_context_policy'; const AI4SEO_ATTACHMENT_CONTEXT_MAX_CANDIDATES = 20; const AI4SEO_ATTACHMENT_CONTEXT_MAX_BYTES = 4194304; // Keep recovery readers, classifiers, and writers within the same per-post storage bounds. const AI4SEO_ACTIVE_METADATA_MAX_ROWS = 100; const AI4SEO_ACTIVE_METADATA_MAX_ROW_BYTES = 65536; // Entry-level custom instructions are stored separately from generated data and active metadata. const AI4SEO_POST_META_METADATA_CUSTOM_INSTRUCTIONS_META_KEY = 'ai4seo_metadata_custom_instructions'; const AI4SEO_POST_META_ATTACHMENT_ATTRIBUTES_CUSTOM_INSTRUCTIONS_META_KEY = 'ai4seo_attachment_attributes_custom_instructions'; const AI4SEO_STYLES_HANDLE = 'ai-for-seo-styles'; const AI4SEO_SCRIPTS_HANDLE = 'ai-for-seo-scripts'; const AI4SEO_INJECTION_SCRIPTS_HANDLE = 'ai-for-seo-injection-scripts'; const AI4SEO_STYLES_FILE_NAME = 'ai-for-seo-styles-' . AI4SEO_PLUGIN_VERSION_NUMBER . '.css'; const AI4SEO_SCRIPTS_FILE_NAME = 'ai-for-seo-scripts-' . AI4SEO_PLUGIN_VERSION_NUMBER . '.js'; const AI4SEO_INJECTION_SCRIPTS_FILE_NAME = 'ai-for-seo-alt-text-injection-' . AI4SEO_PLUGIN_VERSION_NUMBER . '.js'; // Keep PHP context validation and the localized JavaScript dispatcher on one stable vocabulary. const AI4SEO_PRIMARY_ASSET_CONTEXTS = array( 'activation-offer', 'plugin-ui', 'frontend-metadata-editor', 'content-list', 'post-editor', 'external-media', 'plugin-deactivation', 'tos-gate', ); const AI4SEO_VERY_LOW_CREDITS_THRESHOLD = 10; const AI4SEO_LOW_CREDITS_THRESHOLD = 40; const AI4SEO_CUSTOM_PLAN_DISCOUNT = 30; // in percent. const AI4SEO_DAILY_FREE_CREDITS_AMOUNT = 5; const AI4SEO_MONEY_BACK_GUARANTEE_DAYS = 14; const AI4SEO_MAX_LATEST_ACTIVITY_LOGS = 10; const AI4SEO_BLUE_GET_MORE_CREDITS_BUTTON_THRESHOLD = 100; const AI4SEO_GIVING_FEEDBACK_CREDITS = 100; const AI4SEO_GIVING_FEEDBACK_DISCOUNT = 10; // in percent. const AI4SEO_NEXTGEN_GALLERY_POST_TYPE = 'ai4seo_ngg'; const AI4SEO_TAXONOMY_PROXY_POST_TYPE = 'ai4seo_taxonomy'; const AI4SEO_TAXONOMY_PROXY_SOURCE_META_KEY = '_ai4seo_taxonomy_source'; const AI4SEO_TAXONOMY_PROXY_NAME_META_KEY = '_ai4seo_taxonomy_name'; const AI4SEO_TAXONOMY_PROXY_AVAILABILITY_META_KEY = '_ai4seo_taxonomy_availability'; const AI4SEO_TAXONOMY_PROXY_SYNC_OPTION_NAME = 'ai4seo_taxonomy_proxy_sync_v1'; const AI4SEO_ANALYSIS_ISSUE_OPTION_PREFIX = 'ai4seo_analysis_issue_v1_'; const AI4SEO_ANALYSIS_ISSUE_MIGRATION_OPTION = 'ai4seo_analysis_issue_inventory_v1'; const AI4SEO_ANALYSIS_ISSUE_PAGE_SIZE = 50; const AI4SEO_TAXONOMY_PROXY_SYNC_CRON_HOOK = 'ai4seo_taxonomy_proxy_sync'; const AI4SEO_TAXONOMY_PROXY_CONTINUATION_CRON_HOOK = 'ai4seo_taxonomy_proxy_sync_continue'; const AI4SEO_TAXONOMY_PROXY_SYNC_BATCH_SIZE = 200; const AI4SEO_TAXONOMY_PROXY_SYNC_TIME_LIMIT = 10; const AI4SEO_MAX_DISPLAYABLE_ALREADY_READ_NOTIFICATIONS = 2; const AI4SEO_ANALYZE_PERFORMANCE_INTERVAL = 7200; // 2h const AI4SEO_GLOBAL_NONCE_IDENTIFIER = 'ai4seo_ajax_nonce'; // These values form the single-use checkout-return handshake shared by URL generation and validation. const AI4SEO_PURCHASE_RETURN_QUERY_PARAMETER = 'ai4seo-just-purchased'; const AI4SEO_PURCHASE_RETURN_TOKEN_QUERY_PARAMETER = 'ai4seo_purchase_return_token'; const AI4SEO_PURCHASE_RETURN_TOKEN_OPTION_PREFIX = 'ai4seo_purchase_return_token_'; const AI4SEO_PURCHASE_RETURN_TOKEN_EXPIRY_CRON_HOOK = 'ai4seo_expire_purchase_return_token'; const AI4SEO_PURCHASE_RETURN_TOKEN_TTL_SECONDS = 604800; // 7 days. // Keep the durable purchase namespace and stable RobHub outcomes shared by AJAX and return handling. const AI4SEO_CREDIT_PURCHASE_ATTEMPT_OPTION_PREFIX = '_ai4seo_credit_purchase_v1_'; const AI4SEO_CREDIT_PURCHASE_BUSY = 5092601; const AI4SEO_CREDIT_PURCHASE_EXPIRED = 5092602; const AI4SEO_CREDIT_PURCHASE_COMPLETED = 5092603; const AI4SEO_CREDIT_PURCHASE_CONFLICT = 5092604; const AI4SEO_CREDIT_PURCHASE_PENDING = 5092605; const AI4SEO_CREDIT_PURCHASE_REJECTED = 5092606; const AI4SEO_CREDIT_PURCHASE_STORAGE_ERROR = 5092607; const AI4SEO_CREDIT_PURCHASE_INVALID = 5092608; const AI4SEO_PAYG_CREDITS_THRESHOLD = 100; const AI4SEO_ALLOWED_PAYG_STATUS = array( 'idle', 'budget-limit-reached', 'processing', 'payment-pending', 'payment-received', 'payment-failed', 'payment-method-failed', 'error' ); const AI4SEO_SEMAPHORE_MAX_WAIT_SECONDS = 5; // 5 seconds const AI4SEO_SEMAPHORE_POLL_INTERVAL_SECONDS = .1; // .1 seconds const AI4SEO_SEMAPHORE_TTL_SECONDS = 30; // 30 seconds const AI4SEO_POST_TABLE_ANALYSIS_BATCH_SIZE = 10000; // number of posts to analyze per batch. const AI4SEO_POST_TABLE_ANALYSIS_MAX_EXECUTION_TIME = 2; // maximum execution time in seconds per batch. const AI4SEO_DASHBOARD_ANALYSIS_MAX_EXECUTION_TIME = 3; // Shared analysis budget per dashboard request. const AI4SEO_POST_TABLE_ANALYSIS_SLEEP_BETWEEN_RUNS = 100000; // microseconds to sleep between runs. const AI4SEO_POST_TABLE_ANALYSIS_PROCESSING_TIMEOUT = 90; // seconds. const AI4SEO_LARGE_SITE_POSTS_THRESHOLD = 50000; const AI4SEO_LARGE_SITE_ANALYSIS_BATCH_SIZE = 1000; const AI4SEO_ANALYSIS_RECONCILIATION_BATCH_SIZE = 25; const AI4SEO_ANALYSIS_RECONCILIATION_RETRY_DELAY = 900; const AI4SEO_LARGE_SITE_AUTOMATIC_ANALYSIS_INTERVAL = 86400; // 24h const AI4SEO_DEEP_CONTEXT_SEARCH_POSTMETA_THRESHOLD = 1000000; const AI4SEO_DEEP_CONTEXT_SEARCH_MAX_TIMEOUTS_PER_CRON_RUN = 3; // Maximum block counts and their full-analysis quiet periods; larger sites use the daily interval. const AI4SEO_ANALYSIS_REFRESH_INTERVALS = array( 2 => 0, 10 => 300, 100 => 3600, ); // Keep neutral SQL date bindings inside MySQL's supported DATETIME range under strict modes. const AI4SEO_MYSQL_DATETIME_MINIMUM = '1000-01-01 00:00:00'; // Custom instruction limits are tied to subscription status rather than one-off credit balances. const AI4SEO_CUSTOM_INSTRUCTIONS_FREE_LENGTH_LIMIT = 200; const AI4SEO_CUSTOM_INSTRUCTIONS_SUBSCRIPTION_LENGTH_LIMIT = 1000; const AI4SEO_CRON_JOBS_ENABLED = true; // set to true to enable cron jobs, false to disable them. $GLOBALS['ai4seo_held_semaphores'] = isset( $GLOBALS['ai4seo_held_semaphores'] ) && is_array( $GLOBALS['ai4seo_held_semaphores'] ) ? $GLOBALS['ai4seo_held_semaphores'] : array(); $GLOBALS['ai4seo_ajax_nonce'] = ''; const AI4SEO_MAX_EDITOR_INPUT_LENGTHS = array( 'focus-keyphrase' => 128, 'meta-title' => 205, 'meta-description' => 505, 'keywords' => 512, 'facebook-title' => 205, 'facebook-description' => 375, 'twitter-title' => 205, 'twitter-description' => 375, 'title' => 256, 'alt-text' => 390, 'caption' => 256, 'description' => 512, // Reserve filename capacity here so API-generated names use the same immutable cap registry as editor fields. 'file-name' => 255, 'fallback' => 512, ); // Mirror API prompt targets for save-time diagnostics; misses stay usable while the hard caps above remain authoritative. const AI4SEO_GENERATED_OUTPUT_QUALITY_WINDOWS = array( 'metadata' => array( 'meta-title' => array( 'min-length' => 45, 'max-length' => 65, ), 'meta-description' => array( 'min-length' => 130, 'max-length' => 160, ), 'keywords' => array( 'max-items' => 10, ), 'facebook-title' => array( 'min-length' => 45, 'max-length' => 65, ), 'facebook-description' => array( 'min-length' => 90, 'max-length' => 120, ), 'twitter-title' => array( 'min-length' => 45, 'max-length' => 65, ), 'twitter-description' => array( 'min-length' => 90, 'max-length' => 125, ), ), 'attachment_attributes' => array( 'title' => array( 'min-length' => 50, 'max-length' => 80, ), 'alt-text' => array( 'min-length' => 95, 'max-length' => 125, ), 'caption' => array( 'min-length' => 130, 'max-length' => 160, ), 'description' => array( 'min-length' => 180, 'max-length' => 240, ), 'file-name' => array( 'min-length' => 40, 'max-length' => 80, ), ), ); const AI4SEO_FOCUS_KEYPHRASE_RECOMMENDED_MAX_LENGTH = 30; const AI4SEO_METADATA_KEYWORDS_RECOMMENDED_MIN_ITEMS = 5; const AI4SEO_METADATA_KEYWORDS_RECOMMENDED_MAX_ITEMS = 10; /** * Return the plugin change log. * * @return array[] the change log of the plugin */ function ai4seo_get_change_log(): array { return array( array( 'date' => '2026-11-05', 'version' => '2.6.2', 'important' => false, 'updates' => array( 'Added a choice of verified source posts for image generation, with automatic context checks and clear feedback when a source is unavailable.', 'Added detailed lists of entries affected by SEO analysis problems, while allowing unaffected content to continue processing.', 'Made analysis preparation, pauses, background updates, and provisional statistics easier to distinguish.', 'Improved filtering and sorting of large content libraries, including clearer messages when a list cannot be loaded.', 'Fixed retrying all failed generations so the affected entries can be processed again.', 'Enabled metadata editing for the first draft of supported public content types.', 'Improved reconnection for eligible free accounts after connection errors.', 'Clarified image-generation settings and the preview of unsaved Keywords changes.', 'Bug Fixes & Maintenance: Fixed 2 minor bugs and addressed 1 security issue.', ), ), array( 'date' => '2026-10-27', 'version' => '2.6.1', 'important' => false, 'updates' => array( 'Improved handling of large content libraries in filtered lists, title sorting, and SEO Autopilot.', 'Kept completed SEO statistics visible during background updates, with changes to content included before the new results appear.', 'Made large dashboard counts easier to read, with exact values in tooltips and clearer analysis progress, scheduling, and pause messages.', 'Improved recovery from temporary SVG generation outages with clearer explanations and preserved selections for retrying.', 'Fixed product price placeholders in metadata previews and inherited social sharing tags when using WooCommerce with Yoast SEO.', 'Kept metadata editor fields and actions accessible at high browser zoom and in short windows.', 'Fixed an upgrade issue that could leave updates from very old plugin versions unfinished.', 'Updated translations for nine languages and made bundled JavaScript translations available across plugin updates.', 'Bug Fixes & Maintenance: Fixed 2 minor bugs.', ), ), array( 'date' => '2026-10-17', 'version' => '2.6.0', 'important' => true, 'updates' => array( 'Added metadata editing and generation for category, tag, and public taxonomy archives, with separate SEO coverage and SEO Autopilot selection.', 'Added support for generating image attributes from validated static SVGs while preserving the original media file, including native WordPress media controls.', 'Added email-confirmed license owner changes and license key rotation for eligible accounts, with recovery when a confirmation result is uncertain.', 'Added separate controls for offers, optional notifications, menu counters, and email preferences, with reliable saving and dismissal across admin pages.', 'Improved SEO Autopilot and bulk generation with clearer queue status, retained jobs when data is unavailable, and safer retries after interruptions.', 'Improved metadata and settings editors to preserve unsaved work, explain unavailable data, and recover from conflicting or incomplete saves.', 'Improved account refresh and offer handling so stale account data or expired offers cannot silently replace current information.', 'Improved plugin upgrades with visible progress, safe retries, and preservation of legacy metadata when an update step cannot finish.', 'Improved synchronization with supported SEO plugins and navigation between filtered content entries, with clearer responsive dialogs and keyboard focus.', 'Simplified taxonomy navigation and Credits Pack dialogs, named the processed content types in Autopilot completion messages, and clarified metadata help and notification labels.', 'Bug Fixes & Maintenance: Fixed 8 minor bugs and addressed 1 security issue.', ), ), array( 'date' => '2026-10-09', 'version' => '2.5.9', 'important' => false, 'updates' => array( 'Improved SEO coverage analysis so unreadable or duplicate generation history no longer stops scans, with clear warnings and affected entries excluded from SEO Autopilot until resolved.', 'Improved account connection recovery to stop repeated unsuccessful setup attempts while preserving valid connections and allowing manual reconnection.', 'Fixed incorrect unsaved-change warnings after saving metadata that WordPress normalizes, while preserving edits made during saving.', 'Clarified where media titles, alt text, captions, and descriptions are saved and when they appear on your website.', 'Made restoring default settings safer by initially focusing Cancel in the confirmation dialog.', 'Bug Fixes & Maintenance: Fixed 2 minor bugs.', ), ), array( 'date' => '2026-10-04', 'version' => '2.5.8', 'important' => false, 'updates' => array( 'Reduced memory use when analyzing SEO coverage on larger websites.', 'Improved recognition of older generation history so supported saved values can be included in SEO analysis without changing stored content.', 'Fixed clearing custom instructions for multiple selected entries when the text box is left empty.', 'Improved keyboard and screen-reader navigation in metadata and media editors, settings, and media lists, with clearer field warnings and link descriptions.', 'Kept keyboard focus in the Related Media search field after searching or clearing filters.', 'Bug Fixes & Maintenance: Fixed 1 minor bug and implemented 2 performance improvements.', ), ), array( 'date' => '2026-09-25', 'version' => '2.5.7', 'important' => false, 'updates' => array( 'Improved image generation reliability by using available local image files when preparing image data, with clearer guidance when an image cannot be processed.', 'Improved account connection recovery to avoid repeated unsuccessful recovery attempts while keeping valid connections usable.', 'Improved generation context by removing unfinished page-builder markup from excerpts while preserving readable text.', 'Clarified when complete entries can be included in generation, with overwrite requirements shown directly in Settings and saved preferences retained.', 'Added clear, accessible notices when custom instructions exceed the character limit, including the number of characters removed.', 'Improved keyboard navigation in metadata and media editors, preserved unsaved edits when closing image previews, and added editor links for image context from unpublished content.', 'Bug Fixes & Maintenance: Fixed 3 minor bugs and implemented 1 performance improvement.', ), ), array( 'date' => '2026-09-18', 'version' => '2.5.6', 'important' => false, 'updates' => array( 'Added recovery options for duplicate or conflicting saved metadata, with explicit choices for conflicting values and protection for unsaved edits.', 'Improved generation context with cleaner visible page content, better Elementor support, and more accurate matching of images to their surrounding text.', 'Added confirmation before overwriting existing metadata or media attributes, showing the affected fields and generation cost.', 'Added dismissal of resolved SEO Autopilot warnings and automatic acknowledgement after successful generation or saving, while retaining activity history.', 'Improved navigation with a Back control in Related Media that preserves parent edits, direct Dashboard links to missing SEO fields, and a shortcut to Deep Search settings.', 'Expanded the Help page post inspector to make saved-data and metadata-recovery problems easier to diagnose.', 'Improved Help headings and custom-instruction tooltips for clearer navigation and accessibility.', 'Bug Fixes & Maintenance: Fixed 1 minor bug and implemented 2 performance improvements.', ), ), array( 'date' => '2026-09-14', 'version' => '2.5.5', 'important' => false, 'updates' => array( 'Improved SEO coverage displays with details for filled, missing, and excluded fields, clearer completion guidance, and highlighted edit buttons for incomplete entries.', 'Added direct SOOZ editor shortcuts to WordPress Posts, Pages, and Media lists.', 'Added an Account option to request existing license details by checkout email, with clearer help when the email does not arrive.', 'Improved editor guidance for using and saving custom instructions, choosing the default editor view, and understanding generation costs.', 'Improved Help navigation with clearer expandable headings, a dedicated support area, and easier access to useful resources.', 'Updated translations for Arabic, Spanish, French, Italian, Japanese, Dutch, Polish, Portuguese, and Swedish.', 'Bug Fixes & Maintenance: Fixed 3 minor bugs.', ), ), array( 'date' => '2026-09-11', 'version' => '2.5.4', 'important' => false, 'updates' => array( 'Improved metadata saving with clearer recovery guidance and retained edits when saving or refreshing a connected SEO editor fails.', 'Improved SEO coverage analysis with progress reporting, reliable recovery after interruptions, and accurate history for partially generated entries.', 'Improved image selection in WordPress and Elementor media dialogs so generation controls follow the selected image when opening or reopening a dialog.', 'Improved metadata and media editors on small screens, with clearer guidance for empty fields and shared Facebook and WhatsApp previews.', 'Added a three-step welcome for new installations that stays dismissed and does not reappear on established sites after updates.', 'Added a Help tool to inspect saved generated data when troubleshooting, with readable output for long entries.', 'Bug Fixes & Maintenance: Fixed 2 minor bugs and implemented 1 performance improvement.', ), ), array( 'date' => '2026-09-08', 'version' => '2.5.3', 'important' => false, 'updates' => array( 'Added Settings search to find options by name or description, including advanced settings, without changing saved preferences.', 'Added Next item and Save & edit next controls to move through metadata and media editors while preserving the selected view and detecting edits reliably.', 'Improved image generation so Auto switches to Data after three successful recoveries from failed image URL requests, while respecting a manually selected upload method.', 'Improved account synchronization and credit checkout recovery so interrupted or repeated requests can resume reliably.', 'Added the site timezone and a live clock to SEO Autopilot date controls, with validation that prevents invalid cutoffs from partially saving settings.', 'Added dashboard guidance explaining which settings exclude entries from refreshed statistics.', 'Added selection counts to bulk actions so you can confirm how many entries will be affected.', 'Bug Fixes & Maintenance: Fixed 4 minor bugs and implemented 1 performance improvement.', ), ), array( 'date' => 'September 3rd, 2026', 'version' => '2.5.2', 'important' => false, 'updates' => array( 'Improved account recovery and purchase setup so license credentials are refreshed safely and subscription or credit checkout starts more reliably.', 'Added clearer Latest Activity details that show which metadata or media fields were generated, with direct WordPress edit links from content lists.', 'Improved the Metadata Editor so drafts can save manual changes and field removals reliably before publication.', 'Improved Settings and bulk-action controls so actions activate only after meaningful changes and valid selections, with a simpler accessible Advanced Settings toggle.', 'Improved automatic repair of legacy generation queues and saved data so older site state no longer interrupts analysis.', 'Bug Fixes & Maintenance: Fixed 4 minor bugs and addressed 2 security issues.', ), ), array( 'date' => 'August 26th, 2026', 'version' => '2.5.1', 'important' => false, 'updates' => array( 'Added direct SOOZ metadata-editor controls for Elementor and the WordPress Block Editor, with more reliable initialization for dynamically loaded editor fields.', 'Added AVIF support for media attribute generation, including automatic preparation of compatible image data.', 'Improved media attribute saving and coverage tracking so valid existing image text stays intact and complete entries are recognized reliably.', 'Improved role-based access so permitted editors can use SOOZ only for content they are allowed to edit.', 'Bug Fixes & Maintenance: Fixed 5 minor bugs, implemented 1 performance improvement, and addressed 2 security issues.', ), ), array( 'date' => 'August 17th, 2026', 'version' => '2.5.0', 'important' => true, 'updates' => array( 'Added preview-first Metadata and Media Attributes editors with responsive Google, social, keyword, accessibility, and media appearance previews, field-specific edit shortcuts, and live quality guidance.', 'Added a setting to choose whether Preview or Editor mode opens by default while preserving Editor mode for existing users.', 'Improved "Generate with SOOZ" controls across the WordPress editor, Media Library, frontend page builders, and supported third-party SEO editors so dynamically loaded fields and frames initialize more reliably.', 'Added AVIF support for media attribute generation by preparing compatible image data automatically.', 'Improved SEO Autopilot with consistent new-versus-existing date filters, clearer paused-state feedback, and contextual descriptions for bulk actions.', 'Changed third-party SEO synchronization on fresh installations to opt-in while preserving existing sites\' current synchronization choices.', 'Improved the Get More Credits flow with reliable on-demand access to subscriptions, credit packs, Pay-As-You-Go, and free-credit options.', 'Improved Help and account experiences with accessible FAQ accordions, direct Help-section links, and WordPress-language-aware account synchronization.', 'Bug Fixes & Maintenance: Fixed 5 minor bugs and implemented 2 performance improvements.', ), ), array( 'date' => 'August 10th, 2026', 'version' => '2.4.3', 'important' => false, 'updates' => array( 'Improved third-party SEO workflows with embedded generation controls, two-way metadata synchronization where supported, template-aware values, and live editor refreshes across Yoast SEO, Rank Math, All in One SEO, SEOPress, Slim SEO, SEO Simple Pack, Squirrly SEO, The SEO Framework, and SEOKEY.', 'Improved metadata generation accuracy for Gutenberg and builder-driven pages by analyzing visible local content and avoiding unrelated fallback text on sparse or structural pages.', 'Strengthened role-based access so permitted editors can generate and manage metadata or media attributes only for content they are allowed to edit.', 'Improved metadata editor usability with live length guidance, missing-keyphrase warnings, clearer accessible actions and navigation, and more reliable saved-state feedback.', 'Removed the legacy Blog2Social integration from the supported compatibility list.', 'Bug Fixes & Maintenance: Fixed 6 minor bugs and addressed 2 security issues.', ), ), array( 'date' => 'July 29th, 2026', 'version' => '2.4.2', 'important' => false, 'updates' => array( 'Improved media attribute generation reliability by automatically recovering with direct image data when a remote image URL cannot be accessed.', 'Improved dashboard and SEO Autopilot status accuracy and performance with incremental background updates and clearer, accessible progress details.', 'Added sticky section navigation to Settings so longer configuration pages are easier to move through.', 'Bug Fixes & Maintenance: Fixed 8 minor bugs, implemented 2 performance improvements, and addressed 1 security issue.', ), ), array( 'date' => 'July 24th, 2026', 'version' => '2.4.1', 'important' => false, 'updates' => array( 'Added experimental generation-length sliders for meta titles and descriptions, social metadata, and image alt text, available under Advanced Settings.', 'Added bundled translations for Arabic, Brazilian Portuguese, Dutch, French, Italian, Japanese, Polish, Russian, Spanish, and Swedish.', 'Improved the quality and reliability of generated metadata and media attributes with field-by-field validation and targeted retries for incomplete or unsuitable results.', 'Improved Custom Instructions so compatible preferences are applied without overriding configured languages, field formats, prefixes, suffixes, quality limits, factual accuracy, or safety requirements.', 'Improved usability and accessibility across dashboards, editors, settings, tooltips, notifications, and account controls.', 'Bug Fixes & Maintenance: Improved settings saving and error feedback, dashboard refreshes, frontend alt-text handling, selection controls, notifications, and security checks.', ), ), array( 'date' => 'July 3rd, 2026', 'version' => '2.4.0', 'important' => true, 'updates' => array( 'Improved media context detection for better image attributes by finding featured images, uploaded child media, WooCommerce variation images, galleries, local image URLs, and media stored by page builders or custom fields.', 'Added Metadata prompt sliders so users can tune tone, keyword intensity, focus keyphrase influence, CTA style, social variation, brand context, and existing-value reference strength.', 'Added Attachment Attributes prompt sliders so users can tune visual tone, surrounding context, file name influence, keyword intensity, recognizable entity handling, brand context, and existing-value reference strength.', 'Added custom instruction fields for global generation behavior, metadata, post types, media attributes, and individual editor entries so users can guide future AI generations more precisely.', 'Added WPML language controls so site owners can exclude specific languages from SOOZ dashboards and SEO Autopilot for metadata or media attributes.', 'Improved image attribute generation reliability by retrying with direct image data when an image URL cannot be accessed.', 'Added an "Auto Queue Entries" setting so SEO Autopilot can either automatically queue applicable entries or process only manually queued entries.', 'Added bulk actions to add selected entries to the queue safely, force regeneration, and remove selected entries from the queue.', 'Added bulk actions to add all related images to the queue, force regeneration for related images, or remove related images from the queue.', 'Improved SOOZ post and media lists with status counts, hidden and waiting queue states, WPML language filters, text search, sortable columns, and bulk queue controls.', 'Improved dashboard and list feedback for SEO Autopilot, including clearer queue, processing, insufficient-credit, and manual-queue status messages.', 'Added a "Related Media" workflow that shows media connected to a post, page, or product.', 'Added a General setting to show SOOZ bulk actions in native WordPress post, page, product, and Media Library tables when needed.', 'Added bulk actions to hide selected entries from SOOZ lists or show hidden entries again.', 'Added bulk actions to exclude selected entries from the Auto Queue feature or allow them again.', 'Added bulk actions to mark selected entries as not generated or delete saved SOOZ metadata.', 'Added a bulk action to set or clear custom instructions for selected posts, pages, products, and media files.', 'Added source hints in the Metadata and Media Attributes editors so users can see whether field values were generated by SOOZ, imported from SEO plugins, or edited afterward.', 'Added editor notices for inactive meta tags and media attributes, including a direct link to the settings that can show them again.', 'Added a Metadata Editor warning when an entry has title or description data but no focus keyphrase, helping users generate related metadata together.', 'Improved settings import/export with JSON files, category selection, preview before import, and clearer feedback for skipped or invalid settings.', 'Improved settings imports from older plugin versions so exported settings are migrated more safely into 2.4.0 prompt controls.', 'Improved the Credits Pack selection flow with recommended packs, cost-per-page and cost-per-image estimates, and a custom-plan prompt for large websites.', 'Improved Pay-As-You-Go setup with clearer budget guidance and an explicit confirmation before enabling automatic refills.', 'Improved billing guidance before plugin deactivation, including warnings that deactivation does not cancel subscriptions or Pay-As-You-Go refills.', 'Bug Fixes & Maintenance: Improved API error messages, strengthened AJAX validation and permission checks, optimized large-site background analysis, and fixed minor UI issues.', ), ), array( 'date' => 'March 9th, 2026', 'version' => '2.3.0', 'important' => true, 'updates' => array( 'Changed branding from "AI for SEO" to "SOOZ - AI for SEO". Visit sooz.ai for more information.', 'Enhanced the quality of alt text and image attribute generation by incorporating a broader context from the surrounding content, resulting in more accurate descriptions.', 'Performance improvements across multiple areas. The plugin should feel more responsive on large sites.', 'Posts and Media lists now show all languages. Entries are labeled and can be filtered by WPML language.', 'New advanced setting: Toggle frontend cache purging after metadata updates.', 'Added a warning when leaving a page or modal with unsaved changes, on various editors and settings pages.', 'Added a feedback modal on plugin deactivation, so users can share why they are leaving.', 'Added a new "Remove License" button on the account page.', 'Added a new setting "Query IDs Chunk Size": Advanced troubleshooting option to adjust chunk size when processing large amounts of entries. Lower values may reduce database load and fix MySQL-related issues. Higher values can improve performance if your database allows it.', 'Bug Fixes & Maintenance: Fixed 21 minor bugs, implemented 9 usability improvements, implemented 14 performance optimizations, and resolved 13 security issues.', ), ), array( 'date' => 'November 14th, 2025', 'version' => '2.2.0', 'important' => true, 'updates' => array( 'Changed how Credits are consumed. Credits are now charged per generated field instead of per entry. Use the “Active Meta Tags” and “Active Media Attributes” settings to control which fields are generated and how many Credits each action requires.', 'Added Focus Keyphrase generation, editing, and syncing for Yoast SEO and RankMath.', 'Added Meta Keywords generation, editing, and front-end output.', 'Added WooCommerce price inclusion modes for AI-generated metadata (Never, Fixed Price, Dynamic Price).', 'Added additional “Generate with SOOZ” buttons inside the Gutenberg editor when RankMath is active.', 'Added “Generate with SOOZ” buttons for Focus Keyphrase (Yoast SEO) and Focus Keyword (RankMath) inside their editors.', 'Added a filter bar to the Posts and Media views including SEO-status filters and a text search.', 'Added placeholder support for prefixes and suffixes across all syntaxes ({TITLE}, [TITLE], %%TITLE%% and their placeholder variants).', 'Added a setting to include existing values when generating metadata and media attributes (Basic Plan+).', 'Added a setting to enable enhanced entity recognition for media attributes (Pro Plan+).', 'Added a setting to enable advanced celebrity face recognition for media attributes (Premium Plan+).', 'Added configurable meta tag fallback rules to reuse existing values when no generated data is available.', 'Added a toggle to control JavaScript-based alt text injection separately from render-level injection.', 'Added a troubleshooting toggle to pause database refresh operations during debugging.', 'Added the “Active Meta Tags” setting to control which meta tags the plugin should generate.', 'Added the “Active Post Types” setting to define which post types the plugin should process.', 'Added a reminder and one-click removal option for previously generated SEO-relevant data within the SEO Autopilot modal.', 'Added a “Generate Data for X Empty Fields” button in the Metadata and Media Attribute Editors.', 'Added a “Save & edit next” button in the Metadata and Media Attribute Editors for faster sequential editing.', 'Added a “Retry all failed” quick action to the Dashboard when failed entries are detected.', 'Added Credits badges across the plugin UI to indicate the cost of generation actions.', 'Added an “SEO-Expert Concierge” card to the Dashboard with direct contact options.', 'Submit and Abort buttons are now sticky for easier access during editing.', 'Bug Fixes & Maintenance: Fixed 21 minor bugs, implemented 29 usability improvements, implemented 35 stability improvements, and resolved 8 security issues.', ), ), array( 'date' => 'August 3rd, 2025', 'version' => '2.1.0', 'important' => true, 'updates' => array( 'Added "Generate with SOOZ" buttons in the media section of the Gutenberg editor, allowing users to generate media attributes directly from the editor.', 'Improved context awareness for pages, posts, and products, especially for content with short text. Ensures AI-generated metadata is more relevant and tailored.', 'Added a setting for render-level alt text injection. This checkbox setting (enabled by default) ensures images always have the correct alt text, even if themes or other mechanisms fail to display it.', 'Added a setting for render-level image title injection. Includes a select input to choose what should be injected as the title attribute: Disabled, Inject image title, Inject alt text (default), Inject caption, or Inject image description.', 'Added a setting to the "SEO Autopilot" modal that allows users to customize the reference time used by the "Generate Metadata for" option. This gives more precise control over how new and old entries are distinguished.', 'Added an "Export/Import" button to the plugin settings, enabling users to export their configuration and import it on another website. Useful for SEO and web agencies managing multiple sites.', 'Added a "Restore Default" button to the plugin settings.', 'Added a "Show/Hide Advanced Settings" toggle in the plugin settings. Some advanced settings are now hidden by default to simplify the interface for most users.', 'Made setting descriptions more concise and user-friendly. Rearranged several settings for improved clarity and usability.', 'Private or pending posts, pages, and attachments are now ignored by the plugin, preventing them from being processed.', // 'The plugin now indicates posts, pages, and attachments correctly when ignored by the SEO Autopilot, fully respecting the user\'s selection and settings.', 'Improved UX: The SOOZ - AI for SEO sidebar is now sticky on desktop, keeping it visible during page scrolling for easier navigation.', 'Changed how plugin notifications are handled. All notifications are now indicated by a red bubble in the admin menu. Detailed notices can be viewed in the SOOZ - AI for SEO dashboard.', 'Added new notifications: ', 'Bug Fixes & Maintenance: Fixed 17 minor bugs, added 6 quality-of-life improvements, implemented 3 performance optimizations, and 2 security updates.', ), ), array( 'date' => 'March 20th, 2025', 'version' => '2.0.0', 'important' => true, 'updates' => array( 'Complete UI/UX Overhaul: The look, feel, design, layout, and navigation of the plugin have been completely redesigned.', 'Enhanced Mobile Experience: Improved usability and user experience for mobile users.', 'New "Account" Page: Users can now manage their license key directly from this page.', 'Incognito Mode: SEO and web agencies can hide the plugin from other users/admins (available in the new "Account" page).', 'White-Label Feature: SEO and web agencies can rebrand the plugin with their own name or further hide it from other users/admins (available in the new "Account" page).', 'Customizable Generator Hints: Added a setting to modify or disable generator hints in the source code for additional privacy (available in the "Account" page).', 'Privacy & Data Policy Update: Moved to the new "Account" page.', 'New Metadata Customization Options: Added settings to apply prefixes and suffixes to metadata and media attributes.', 'Advanced Media Attribute Control: New setting allows users to specify which media attributes the plugin should use.', '"SEO Autopilot" Feature: Replaces bulk generation checkboxes with a more intuitive and easy-to-use interface, directly accessible from the dashboard.', '"Recent Activity" Dashboard Section: Track all manual and automatic metadata and media attribute generations in one place.', 'Implemented new ways to get credits:
  1. * Introduced Credit Packs, allowing users to purchase additional credits as needed.
  2. * Added a Pay-As-You-Go option for automatic credit refills when running low.
  3. * All credit purchasing options are now combined in a "Get more Credits" modal, accessible from the dashboard.
', '"Guarantee" Section: Review our Guarantees and Refund Policy directly on the dashboard.', '"Recent Plugin Updates" Section: Stay informed about the latest updates from the dashboard.', 'New "Support & Feedback" Section: Easily access support and provide feedback directly from the dashboard.', 'Tons more minor improvements, bug fixes, and performance enhancements.', ), ), ); } /** * Function to return the credits packs available for purchase * * @return array[] */ function ai4seo_get_credits_packs(): array { $credits_packs = array( 'price_1S6ThfHNyvfVK0r9KimFGz1E' => array( 'credits_amount' => 500, 'price_usd' => 9, 'reference_price_usd' => 9, 'price_eur' => 8, 'reference_price_eur' => 8, 'stripe_product_id' => 'prod_RD8C2kl2gPqozh', 'stripe_payment_link' => 'https://buy.stripe.com/5kA00X7yF5Rc3BK8ww', ), 'price_1S6TjQHNyvfVK0r9WttAsfP9' => array( 'credits_amount' => 1500, 'price_usd' => 19, 'reference_price_usd' => 19, 'price_eur' => 16, 'reference_price_eur' => 16, 'stripe_product_id' => 'prod_RD8JI7ELrXPSWg', 'stripe_payment_link' => '', ), 'price_1S6TlCHNyvfVK0r9s0CZ3z1Z' => array( 'credits_amount' => 5000, 'price_usd' => 49, 'reference_price_usd' => 49, 'price_eur' => 45, 'reference_price_eur' => 45, 'stripe_product_id' => 'prod_RD8KgysYBIyi2Z', 'stripe_payment_link' => '', ), ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only feature-preview flag; no state is changed. if ( isset( $_GET['ai4seo_show_all_credits_packs'] ) ) { $credits_packs += array( 'price_1SjAj1HNyvfVK0r9TkVWQsJE' => array( 'credits_amount' => 15000, 'price_usd' => 129, 'reference_price_usd' => 129, 'price_eur' => 109, 'reference_price_eur' => 109, 'stripe_product_id' => 'prod_RD8LcGkIHN7O0K', 'stripe_payment_link' => '', ), 'price_1SjAk2HNyvfVK0r9NJDtHuRJ' => array( 'credits_amount' => 50000, 'price_usd' => 349, 'reference_price_usd' => 349, 'price_eur' => 299, 'reference_price_eur' => 299, 'stripe_product_id' => 'prod_RD8LWAmW1fQ32n', 'stripe_payment_link' => '', ), ); } return $credits_packs; } /** * Return the SVG icons used in the plugin. * * @return string[] associative array with icon names as keys and SVG strings as values */ function ai4seo_get_svg_tags(): array { return array( 'ai-for-seo-main-menu-icon' => ' ', 'all-in-one-seo' => '', 'angle-down' => '', 'arrow-right' => '', 'arrow-up-right-from-square' => '', 'bars-sort' => '', 'betheme' => 'Be', 'bolt' => '', 'caret-down' => '', 'caret-up' => '', 'check' => '', 'circle' => '', 'circle-check' => '', 'circle-plus' => '', 'circle-question' => '', 'crown' => '', 'circle-up' => '', 'circle-xmark' => '', 'code' => '', 'comment' => '', 'comments' => '', 'copy' => '', 'download' => '', 'envelope' => '', 'eye' => '', 'eye-slash' => '', 'file-arrow-down' => '', 'file-arrow-up' => '', 'file-export' => '', 'flag' => '', 'gear' => '', 'gift' => '', 'globe' => '', 'handshake' => '', 'hashtag' => '', 'headline' => '', 'hourglass-start' => '', 'image' => '', 'image-slash' => '', 'key' => '', 'key-slash' => '', 'list' => '', 'magnifying-glass' => ' ', 'paper-plane' => '', 'rank-math' => '', 'robot' => '', 'rotate' => '', 'pen-to-square' => '', 'rocket' => '', 'rocket-chat' => '', 'seopress' => '', 'shopping-cart' => '', 'sliders' => '', 'sooz-with-ai-for-seo' => '', 'sooz' => '', 'sooz-oo' => '', 'square-check' => '', 'square-facebook' => '', 'square-twitter-x' => '', 'square-xmark' => '', 'star' => '', 'stripe' => '', 'subtitle' => '', 'subtitles' => '', 'trash' => '', 'triangle-exclamation' => '', 'xmark' => '', 'yoast' => '', 'woocommerce' => 'WooCommerce Logoimage/svg+xml', 'wpml' => '', ); } const AI4SEO_STRIPE_BILLING_URL = 'https://sooz.ai/manage-plan'; const AI4SEO_POST_TYPES_PLUGIN_PAGE_NAME = 'post'; const AI4SEO_DEEP_CONTEXT_SEARCH_QUERY_TIMEOUT_SECONDS = 10; // Constants for the wp_options entries. const AI4SEO_FULLY_COVERED_METADATA_POST_IDS_OPTION_NAME = 'ai4seo_fully_covered_metadata_post_ids'; const AI4SEO_MISSING_METADATA_POST_IDS_OPTION_NAME = 'ai4seo_missing_metadata_post_ids'; const AI4SEO_PENDING_METADATA_POST_IDS_OPTION_NAME = 'ai4seo_pending_metadata_post_ids'; const AI4SEO_PROCESSING_METADATA_POST_IDS_OPTION_NAME = 'ai4seo_processing_metadata_post_ids'; const AI4SEO_GENERATED_METADATA_POST_IDS_OPTION_NAME = 'ai4seo_generated_metadata_post_ids'; const AI4SEO_FAILED_METADATA_POST_IDS_OPTION_NAME = 'ai4seo_failed_metadata_post_ids'; const AI4SEO_FORCE_OVERWRITE_METADATA_POST_IDS_OPTION_NAME = 'ai4seo_force_overwrite_metadata_post_ids'; const AI4SEO_HIDDEN_METADATA_POST_IDS_OPTION_NAME = 'ai4seo_hidden_metadata_post_ids'; const AI4SEO_AUTO_QUEUE_DISALLOWED_METADATA_POST_IDS_OPTION_NAME = 'ai4seo_auto_queue_disallowed_metadata_post_ids'; const AI4SEO_LATEST_ACTIVITY_OPTION_NAME = '_ai4seo_latest_activity'; // todo: replace with database table. const AI4SEO_AUTOPILOT_ITEM_DISMISSALS_USER_OPTION = 'ai4seo_autopilot_item_dismissals'; const AI4SEO_FULLY_COVERED_ATTACHMENT_ATTRIBUTES_POST_IDS_OPTION_NAME = 'ai4seo_fully_covered_attachment_attributes_post_ids'; const AI4SEO_MISSING_ATTACHMENT_ATTRIBUTES_POST_IDS_OPTION_NAME = 'ai4seo_missing_attachment_attributes_post_ids'; const AI4SEO_PENDING_ATTACHMENT_ATTRIBUTES_POST_IDS_OPTION_NAME = 'ai4seo_pending_attachment_attributes_post_ids'; const AI4SEO_PROCESSING_ATTACHMENT_ATTRIBUTES_POST_IDS_OPTION_NAME = 'ai4seo_processing_attachment_attributes_post_ids'; const AI4SEO_GENERATED_ATTACHMENT_ATTRIBUTES_POST_IDS_OPTION_NAME = 'ai4seo_generated_attachment_attributes_post_ids'; const AI4SEO_FAILED_ATTACHMENT_ATTRIBUTES_POST_IDS_OPTION_NAME = 'ai4seo_failed_attachment_attributes_post_ids'; const AI4SEO_FORCE_OVERWRITE_ATTACHMENT_ATTRIBUTES_POST_IDS_OPTION_NAME = 'ai4seo_force_overwrite_attachment_attributes_post_ids'; const AI4SEO_HIDDEN_ATTACHMENT_ATTRIBUTES_POST_IDS_OPTION_NAME = 'ai4seo_hidden_attachment_attributes_post_ids'; const AI4SEO_AUTO_QUEUE_DISALLOWED_ATTACHMENT_ATTRIBUTES_POST_IDS_OPTION_NAME = 'ai4seo_auto_queue_disallowed_attachment_attributes_post_ids'; // Persisted bucket identifiers never depend on registry order or the public plugin version. const AI4SEO_POST_STATE_STORAGE_VERSION = 1; const AI4SEO_ANALYSIS_BUILD_WORK_KIND = 2; const AI4SEO_BACKGROUND_ANALYSIS_INTERVAL = 30; const AI4SEO_POST_STATE_BUCKETS = array( AI4SEO_MISSING_METADATA_POST_IDS_OPTION_NAME => 1, AI4SEO_GENERATED_METADATA_POST_IDS_OPTION_NAME => 2, AI4SEO_FULLY_COVERED_METADATA_POST_IDS_OPTION_NAME => 3, AI4SEO_PENDING_METADATA_POST_IDS_OPTION_NAME => 4, AI4SEO_PROCESSING_METADATA_POST_IDS_OPTION_NAME => 5, AI4SEO_FAILED_METADATA_POST_IDS_OPTION_NAME => 6, AI4SEO_FORCE_OVERWRITE_METADATA_POST_IDS_OPTION_NAME => 7, AI4SEO_HIDDEN_METADATA_POST_IDS_OPTION_NAME => 8, AI4SEO_AUTO_QUEUE_DISALLOWED_METADATA_POST_IDS_OPTION_NAME => 9, AI4SEO_MISSING_ATTACHMENT_ATTRIBUTES_POST_IDS_OPTION_NAME => 10, AI4SEO_GENERATED_ATTACHMENT_ATTRIBUTES_POST_IDS_OPTION_NAME => 11, AI4SEO_FULLY_COVERED_ATTACHMENT_ATTRIBUTES_POST_IDS_OPTION_NAME => 12, AI4SEO_PENDING_ATTACHMENT_ATTRIBUTES_POST_IDS_OPTION_NAME => 13, AI4SEO_PROCESSING_ATTACHMENT_ATTRIBUTES_POST_IDS_OPTION_NAME => 14, AI4SEO_FAILED_ATTACHMENT_ATTRIBUTES_POST_IDS_OPTION_NAME => 15, AI4SEO_FORCE_OVERWRITE_ATTACHMENT_ATTRIBUTES_POST_IDS_OPTION_NAME => 16, AI4SEO_HIDDEN_ATTACHMENT_ATTRIBUTES_POST_IDS_OPTION_NAME => 17, AI4SEO_AUTO_QUEUE_DISALLOWED_ATTACHMENT_ATTRIBUTES_POST_IDS_OPTION_NAME => 18, ); const AI4SEO_ADDITIONAL_TOS_ACCEPT_DETAILS_OPTION_NAME = '_ai4seo_additional_tos_accept_details'; const AI4SEO_ADDITIONAL_TOS_ACCEPT_DETAILS_LAST_TRY_TIMESTAMP_OPTION_NAME = '_ai4seo_additional_tos_accept_details_last_try_timestamp'; // all wp_options that contain post ids. const AI4SEO_ALL_POST_ID_OPTIONS = array( AI4SEO_MISSING_METADATA_POST_IDS_OPTION_NAME, AI4SEO_GENERATED_METADATA_POST_IDS_OPTION_NAME, AI4SEO_FULLY_COVERED_METADATA_POST_IDS_OPTION_NAME, AI4SEO_PENDING_METADATA_POST_IDS_OPTION_NAME, AI4SEO_PROCESSING_METADATA_POST_IDS_OPTION_NAME, AI4SEO_FAILED_METADATA_POST_IDS_OPTION_NAME, AI4SEO_FORCE_OVERWRITE_METADATA_POST_IDS_OPTION_NAME, AI4SEO_HIDDEN_METADATA_POST_IDS_OPTION_NAME, AI4SEO_AUTO_QUEUE_DISALLOWED_METADATA_POST_IDS_OPTION_NAME, AI4SEO_MISSING_ATTACHMENT_ATTRIBUTES_POST_IDS_OPTION_NAME, AI4SEO_GENERATED_ATTACHMENT_ATTRIBUTES_POST_IDS_OPTION_NAME, AI4SEO_FULLY_COVERED_ATTACHMENT_ATTRIBUTES_POST_IDS_OPTION_NAME, AI4SEO_PENDING_ATTACHMENT_ATTRIBUTES_POST_IDS_OPTION_NAME, AI4SEO_PROCESSING_ATTACHMENT_ATTRIBUTES_POST_IDS_OPTION_NAME, AI4SEO_FAILED_ATTACHMENT_ATTRIBUTES_POST_IDS_OPTION_NAME, AI4SEO_FORCE_OVERWRITE_ATTACHMENT_ATTRIBUTES_POST_IDS_OPTION_NAME, AI4SEO_HIDDEN_ATTACHMENT_ATTRIBUTES_POST_IDS_OPTION_NAME, AI4SEO_AUTO_QUEUE_DISALLOWED_ATTACHMENT_ATTRIBUTES_POST_IDS_OPTION_NAME, ); // all wp_options that define the seo coverage // a post id cannot be in MISSING and one of the other options at the same time. const AI4SEO_SEO_COVERAGE_POST_ID_OPTIONS = array( AI4SEO_MISSING_METADATA_POST_IDS_OPTION_NAME, AI4SEO_FULLY_COVERED_METADATA_POST_IDS_OPTION_NAME, AI4SEO_GENERATED_METADATA_POST_IDS_OPTION_NAME, AI4SEO_MISSING_ATTACHMENT_ATTRIBUTES_POST_IDS_OPTION_NAME, AI4SEO_FULLY_COVERED_ATTACHMENT_ATTRIBUTES_POST_IDS_OPTION_NAME, AI4SEO_GENERATED_ATTACHMENT_ATTRIBUTES_POST_IDS_OPTION_NAME, ); // Keep persisted analysis diagnostics bounded even when many historical rows are corrupt. const AI4SEO_POSTS_TABLE_ANALYSIS_WARNING_SAMPLE_LIMIT = 20; // all wp-Options that define the generation status of a given post // a post id cannot be in PENDING and PROCESSING at the same time. const AI4SEO_GENERATION_STATUS_POST_ID_OPTIONS = array( AI4SEO_PENDING_METADATA_POST_IDS_OPTION_NAME, AI4SEO_PROCESSING_METADATA_POST_IDS_OPTION_NAME, AI4SEO_FAILED_METADATA_POST_IDS_OPTION_NAME, AI4SEO_PENDING_ATTACHMENT_ATTRIBUTES_POST_IDS_OPTION_NAME, AI4SEO_PROCESSING_ATTACHMENT_ATTRIBUTES_POST_IDS_OPTION_NAME, AI4SEO_FAILED_ATTACHMENT_ATTRIBUTES_POST_IDS_OPTION_NAME, ); // all wp_options that mark queued entries as force-overwrite runs. const AI4SEO_FORCE_OVERWRITE_BULK_GENERATION_POST_ID_OPTIONS = array( AI4SEO_FORCE_OVERWRITE_METADATA_POST_IDS_OPTION_NAME, AI4SEO_FORCE_OVERWRITE_ATTACHMENT_ATTRIBUTES_POST_IDS_OPTION_NAME, ); // Bulk action IDs are shared by PHP handlers, native table redirects, and AJAX response routing. const AI4SEO_BULK_GENERATION_QUEUE_ACTION_ADD_TO_QUEUE = 'ai4seo_bulk_generation_add_to_queue'; const AI4SEO_BULK_GENERATION_QUEUE_ACTION_ADD_TO_QUEUE_FORCE_OVERWRITE = 'ai4seo_bulk_generation_add_to_queue_force_overwrite'; const AI4SEO_BULK_GENERATION_QUEUE_ACTION_ADD_RELATED_ATTACHMENTS_TO_QUEUE = 'ai4seo_bulk_generation_add_related_attachments_to_queue'; const AI4SEO_BULK_GENERATION_QUEUE_ACTION_ADD_RELATED_ATTACHMENTS_TO_QUEUE_FORCE_OVERWRITE = 'ai4seo_bulk_generation_add_related_attachments_to_queue_force_overwrite'; const AI4SEO_BULK_GENERATION_QUEUE_ACTION_REMOVE_RELATED_ATTACHMENTS_FROM_QUEUE = 'ai4seo_bulk_generation_remove_related_attachments_from_queue'; const AI4SEO_BULK_GENERATION_QUEUE_ACTION_SET_CUSTOM_INSTRUCTIONS = 'ai4seo_bulk_generation_set_custom_instructions'; const AI4SEO_BULK_GENERATION_QUEUE_ACTION_REMOVE_FROM_QUEUE = 'ai4seo_bulk_generation_remove_from_queue'; const AI4SEO_BULK_GENERATION_QUEUE_ACTION_HIDE_ENTRY = 'ai4seo_bulk_generation_hide_entry'; const AI4SEO_BULK_GENERATION_QUEUE_ACTION_SHOW_ENTRY = 'ai4seo_bulk_generation_show_entry'; const AI4SEO_BULK_GENERATION_QUEUE_ACTION_DISALLOW_AUTO_QUEUE = 'ai4seo_bulk_generation_disallow_auto_queue'; const AI4SEO_BULK_GENERATION_QUEUE_ACTION_ALLOW_AUTO_QUEUE = 'ai4seo_bulk_generation_allow_auto_queue'; const AI4SEO_BULK_GENERATION_QUEUE_ACTION_REMOVE_GENERATED_DATA = 'ai4seo_bulk_generation_remove_generated_data'; const AI4SEO_BULK_GENERATION_QUEUE_ACTION_REMOVE_SAVED_DATA = 'ai4seo_bulk_generation_remove_saved_data'; // Queue contexts separate post/page/product metadata from media-library attachment attributes. const AI4SEO_BULK_GENERATION_QUEUE_CONTEXT_METADATA = 'metadata'; const AI4SEO_BULK_GENERATION_QUEUE_CONTEXT_ATTACHMENT_ATTRIBUTES = 'attachment_attributes'; // region CRON JOBS ==========================================================================================. const AI4SEO_BULK_GENERATION_CRON_JOB_NAME = 'ai4seo_automated_generation_cron_job'; const AI4SEO_ANALYSE_PLUGIN_PERFORMANCE_CRON_JOB_NAME = 'ai4seo_analyze_plugin_performance'; const AI4SEO_GENERATION_STATUS_SUMMARY_REBUILD_CRON_JOB_NAME = 'ai4seo_rebuild_generation_status_summary'; const AI4SEO_ACTIVE_METADATA_MIGRATION_V235_CRON_JOB_NAME = 'ai4seo_active_metadata_migration_v235_cron_job'; const AI4SEO_ACTIVE_METADATA_MIGRATION_V235_BATCH_SIZE = 500; const AI4SEO_PLUGIN_UPDATE_PROGRESS_OPTION_NAME = 'ai4seo_plugin_update_progress'; // endregion // region THIRD PARTY PLUGINS ==============================================================================. // Constants for third party plugin identifiers // editors. const AI4SEO_THIRD_PARTY_PLUGIN_ELEMENTOR = 'elementor'; // shops. const AI4SEO_THIRD_PARTY_PLUGIN_WOOCOMMERCE = 'woocommerce'; // traditional seo plugins. const AI4SEO_THIRD_PARTY_PLUGIN_YOAST_SEO = 'yoast-seo'; const AI4SEO_THIRD_PARTY_PLUGIN_ALL_IN_ONE_SEO = 'all-in-one-seo-pack'; const AI4SEO_THIRD_PARTY_PLUGIN_THE_SEO_FRAMEWORK = 'the-seo-framework'; const AI4SEO_THIRD_PARTY_PLUGIN_RANK_MATH = 'rank-math'; const AI4SEO_THIRD_PARTY_PLUGIN_SEOPRESS = 'seopress'; const AI4SEO_THIRD_PARTY_PLUGIN_SEO_SIMPLE_PACK = 'seo-simple-pack'; const AI4SEO_THIRD_PARTY_PLUGIN_SLIM_SEO = 'slim-seo'; const AI4SEO_THIRD_PARTY_PLUGIN_SQUIRRLY_SEO = 'squirrly-seo'; const AI4SEO_THIRD_PARTY_PLUGIN_SEO_KEY = 'seo-key'; // Preserve the former default selection only for sites upgrading from versions before 2.4.4. const AI4SEO_LEGACY_DEFAULT_THIRD_PARTY_SEO_PLUGIN_SYNC_IDENTIFIERS = array( AI4SEO_THIRD_PARTY_PLUGIN_YOAST_SEO, AI4SEO_THIRD_PARTY_PLUGIN_RANK_MATH, AI4SEO_THIRD_PARTY_PLUGIN_SEOPRESS, AI4SEO_THIRD_PARTY_PLUGIN_THE_SEO_FRAMEWORK, AI4SEO_THIRD_PARTY_PLUGIN_SEO_SIMPLE_PACK, AI4SEO_THIRD_PARTY_PLUGIN_SEO_KEY, ); // editors + seo plugins. const AI4SEO_THIRD_PARTY_PLUGIN_BETHEME = 'betheme'; // multi-language plugins. const AI4SEO_THIRD_PARTY_PLUGIN_WPML = 'wpml'; // attachments / images plugins. const AI4SEO_THIRD_PARTY_PLUGIN_NEXTGEN_GALLERY = 'nextgen-gallery'; /** * Return integration details for supported third-party SEO plugins. * * @return array Third-party SEO plugin details keyed by plugin identifier. */ function ai4seo_get_third_party_seo_plugin_details(): array { return array( AI4SEO_THIRD_PARTY_PLUGIN_YOAST_SEO => array( 'name' => 'Yoast SEO', 'icon' => 'yoast', 'icon-css-class' => 'ai4seo-purple-icon', // Direct Yoast postmeta writes can be mirrored without integration-specific storage handling. 'inbound-postmeta-sync' => true, 'generation-field-postmeta-keys' => array( 'focus-keyphrase' => '_yoast_wpseo_focuskw', 'meta-title' => '_yoast_wpseo_title', 'meta-description' => '_yoast_wpseo_metadesc', 'facebook-title' => '_yoast_wpseo_opengraph-title', 'facebook-description' => '_yoast_wpseo_opengraph-description', 'twitter-title' => '_yoast_wpseo_twitter-title', 'twitter-description' => '_yoast_wpseo_twitter-description', ), ), AI4SEO_THIRD_PARTY_PLUGIN_BETHEME => array( 'name' => 'BeTheme', 'icon' => 'betheme', 'icon-css-class' => 'ai4seo-blue-icon', 'generation-field-postmeta-keys' => array( 'meta-title' => 'mfn-meta-seo-title', 'meta-description' => 'mfn-meta-seo-description', ), ), AI4SEO_THIRD_PARTY_PLUGIN_ALL_IN_ONE_SEO => array( 'name' => 'All in One SEO', 'icon' => 'all-in-one-seo', // AIOSEO updates these postmeta mirrors before it saves its canonical aioseo_posts row. 'inbound-postmeta-sync' => true, 'generation-field-postmeta-keys' => array( 'meta-title' => '_aioseo_title', 'meta-description' => '_aioseo_description', 'facebook-title' => '_aioseo_og_title', 'facebook-description' => '_aioseo_og_description', 'twitter-title' => '_aioseo_twitter_title', 'twitter-description' => '_aioseo_twitter_description', ), ), AI4SEO_THIRD_PARTY_PLUGIN_RANK_MATH => array( 'name' => 'Rank Math', 'icon' => 'rank-math', 'icon-css-class' => 'ai4seo-purple-icon', 'seo-score-postmeta-key' => 'rank_math_seo_score', // todo: make this dynamic. // Direct Rank Math postmeta writes can be mirrored without integration-specific storage handling. 'inbound-postmeta-sync' => true, 'generation-field-postmeta-keys' => array( 'focus-keyphrase' => 'rank_math_focus_keyword', 'meta-title' => 'rank_math_title', 'meta-description' => 'rank_math_description', 'facebook-title' => 'rank_math_facebook_title', 'facebook-description' => 'rank_math_facebook_description', 'twitter-title' => 'rank_math_twitter_title', 'twitter-description' => 'rank_math_twitter_description', ), ), AI4SEO_THIRD_PARTY_PLUGIN_SEO_SIMPLE_PACK => array( 'name' => 'SEO Simple Pack', // SEO SIMPLE PACK persists these independent editor fields as direct postmeta values. 'inbound-postmeta-sync' => true, 'generation-field-postmeta-keys' => array( 'meta-title' => 'ssp_meta_title', 'meta-description' => 'ssp_meta_description', 'keywords' => 'ssp_meta_keyword', ), ), AI4SEO_THIRD_PARTY_PLUGIN_SEOPRESS => array( 'name' => 'SEOPress', 'icon' => 'seopress', // SEOPress persists every supported editor field as direct postmeta. 'inbound-postmeta-sync' => true, 'generation-field-postmeta-keys' => array( 'meta-title' => '_seopress_titles_title', 'meta-description' => '_seopress_titles_desc', 'facebook-title' => '_seopress_social_fb_title', 'facebook-description' => '_seopress_social_fb_desc', 'twitter-title' => '_seopress_social_twitter_title', 'twitter-description' => '_seopress_social_twitter_desc', ), ), AI4SEO_THIRD_PARTY_PLUGIN_SLIM_SEO => array( 'name' => 'Slim SEO', // Slim SEO persists both fields inside one shared postmeta array. 'inbound-postmeta-sync' => array( 'postmeta-key' => 'slim_seo', 'generation-field-array-keys' => array( 'meta-title' => 'title', 'meta-description' => 'description', ), ), 'generation-field-postmeta-keys' => array( 'meta-title' => '_ai4seo_workaround', 'meta-description' => '_ai4seo_workaround', ), ), AI4SEO_THIRD_PARTY_PLUGIN_SQUIRRLY_SEO => array( 'name' => 'Squirrly SEO', // Squirrly persists its editor fields in the custom qss table instead of WordPress postmeta. 'inbound-custom-sync' => true, 'generation-field-postmeta-keys' => array( 'meta-title' => '_ai4seo_workaround', 'meta-description' => '_ai4seo_workaround', 'keywords' => '_ai4seo_workaround', 'facebook-title' => '_ai4seo_workaround', 'facebook-description' => '_ai4seo_workaround', 'twitter-title' => '_ai4seo_workaround', 'twitter-description' => '_ai4seo_workaround', ), ), AI4SEO_THIRD_PARTY_PLUGIN_THE_SEO_FRAMEWORK => array( 'name' => 'The SEO Framework', // The SEO Framework persists all six supported fields as direct postmeta values. 'inbound-postmeta-sync' => true, 'generation-field-postmeta-keys' => array( 'meta-title' => '_genesis_title', 'meta-description' => '_genesis_description', 'facebook-title' => '_open_graph_title', 'facebook-description' => '_open_graph_description', 'twitter-title' => '_twitter_title', 'twitter-description' => '_twitter_description', ), ), AI4SEO_THIRD_PARTY_PLUGIN_SEO_KEY => array( 'name' => 'SEOKEY', 'generation-field-postmeta-keys' => array( 'meta-title' => 'seokey-metatitle', 'meta-description' => 'seokey-metadesc', ), ), ); } /** * Return translatable country names keyed by their two-letter identifiers. * * Based on WooCommerce's country registry; availability never grants campaign permission. * * @link https://github.com/woocommerce/woocommerce/blob/trunk/plugins/woocommerce/i18n/countries.php * * @return array Country codes and display names. */ function ai4seo_get_country_names(): array { return array( 'AF' => __( 'Afghanistan', 'ai-for-seo' ), 'AX' => __( 'Åland Islands', 'ai-for-seo' ), 'AL' => __( 'Albania', 'ai-for-seo' ), 'DZ' => __( 'Algeria', 'ai-for-seo' ), 'AS' => __( 'American Samoa', 'ai-for-seo' ), 'AD' => __( 'Andorra', 'ai-for-seo' ), 'AO' => __( 'Angola', 'ai-for-seo' ), 'AI' => __( 'Anguilla', 'ai-for-seo' ), 'AQ' => __( 'Antarctica', 'ai-for-seo' ), 'AG' => __( 'Antigua and Barbuda', 'ai-for-seo' ), 'AR' => __( 'Argentina', 'ai-for-seo' ), 'AM' => __( 'Armenia', 'ai-for-seo' ), 'AW' => __( 'Aruba', 'ai-for-seo' ), 'AU' => __( 'Australia', 'ai-for-seo' ), 'AT' => __( 'Austria', 'ai-for-seo' ), 'AZ' => __( 'Azerbaijan', 'ai-for-seo' ), 'BS' => __( 'Bahamas', 'ai-for-seo' ), 'BH' => __( 'Bahrain', 'ai-for-seo' ), 'BD' => __( 'Bangladesh', 'ai-for-seo' ), 'BB' => __( 'Barbados', 'ai-for-seo' ), 'BY' => __( 'Belarus', 'ai-for-seo' ), 'BE' => __( 'Belgium', 'ai-for-seo' ), 'PW' => __( 'Belau', 'ai-for-seo' ), 'BZ' => __( 'Belize', 'ai-for-seo' ), 'BJ' => __( 'Benin', 'ai-for-seo' ), 'BM' => __( 'Bermuda', 'ai-for-seo' ), 'BT' => __( 'Bhutan', 'ai-for-seo' ), 'BO' => __( 'Bolivia', 'ai-for-seo' ), 'BQ' => __( 'Bonaire, Saint Eustatius and Saba', 'ai-for-seo' ), 'BA' => __( 'Bosnia and Herzegovina', 'ai-for-seo' ), 'BW' => __( 'Botswana', 'ai-for-seo' ), 'BV' => __( 'Bouvet Island', 'ai-for-seo' ), 'BR' => __( 'Brazil', 'ai-for-seo' ), 'IO' => __( 'British Indian Ocean Territory', 'ai-for-seo' ), 'BN' => __( 'Brunei', 'ai-for-seo' ), 'BG' => __( 'Bulgaria', 'ai-for-seo' ), 'BF' => __( 'Burkina Faso', 'ai-for-seo' ), 'BI' => __( 'Burundi', 'ai-for-seo' ), 'KH' => __( 'Cambodia', 'ai-for-seo' ), 'CM' => __( 'Cameroon', 'ai-for-seo' ), 'CA' => __( 'Canada', 'ai-for-seo' ), 'CV' => __( 'Cape Verde', 'ai-for-seo' ), 'KY' => __( 'Cayman Islands', 'ai-for-seo' ), 'CF' => __( 'Central African Republic', 'ai-for-seo' ), 'TD' => __( 'Chad', 'ai-for-seo' ), 'CL' => __( 'Chile', 'ai-for-seo' ), 'CN' => __( 'China', 'ai-for-seo' ), 'CX' => __( 'Christmas Island', 'ai-for-seo' ), 'CC' => __( 'Cocos (Keeling) Islands', 'ai-for-seo' ), 'CO' => __( 'Colombia', 'ai-for-seo' ), 'KM' => __( 'Comoros', 'ai-for-seo' ), 'CG' => __( 'Congo (Brazzaville)', 'ai-for-seo' ), 'CD' => __( 'Congo (Kinshasa)', 'ai-for-seo' ), 'CK' => __( 'Cook Islands', 'ai-for-seo' ), 'CR' => __( 'Costa Rica', 'ai-for-seo' ), 'HR' => __( 'Croatia', 'ai-for-seo' ), 'CU' => __( 'Cuba', 'ai-for-seo' ), 'CW' => __( 'Curaçao', 'ai-for-seo' ), 'CY' => __( 'Cyprus', 'ai-for-seo' ), 'CZ' => __( 'Czech Republic', 'ai-for-seo' ), 'DK' => __( 'Denmark', 'ai-for-seo' ), 'DJ' => __( 'Djibouti', 'ai-for-seo' ), 'DM' => __( 'Dominica', 'ai-for-seo' ), 'DO' => __( 'Dominican Republic', 'ai-for-seo' ), 'EC' => __( 'Ecuador', 'ai-for-seo' ), 'EG' => __( 'Egypt', 'ai-for-seo' ), 'SV' => __( 'El Salvador', 'ai-for-seo' ), 'GQ' => __( 'Equatorial Guinea', 'ai-for-seo' ), 'ER' => __( 'Eritrea', 'ai-for-seo' ), 'EE' => __( 'Estonia', 'ai-for-seo' ), 'ET' => __( 'Ethiopia', 'ai-for-seo' ), 'FK' => __( 'Falkland Islands', 'ai-for-seo' ), 'FO' => __( 'Faroe Islands', 'ai-for-seo' ), 'FJ' => __( 'Fiji', 'ai-for-seo' ), 'FI' => __( 'Finland', 'ai-for-seo' ), 'FR' => __( 'France', 'ai-for-seo' ), 'GF' => __( 'French Guiana', 'ai-for-seo' ), 'PF' => __( 'French Polynesia', 'ai-for-seo' ), 'TF' => __( 'French Southern Territories', 'ai-for-seo' ), 'GA' => __( 'Gabon', 'ai-for-seo' ), 'GM' => __( 'Gambia', 'ai-for-seo' ), 'GE' => __( 'Georgia', 'ai-for-seo' ), 'DE' => __( 'Germany', 'ai-for-seo' ), 'GH' => __( 'Ghana', 'ai-for-seo' ), 'GI' => __( 'Gibraltar', 'ai-for-seo' ), 'GR' => __( 'Greece', 'ai-for-seo' ), 'GL' => __( 'Greenland', 'ai-for-seo' ), 'GD' => __( 'Grenada', 'ai-for-seo' ), 'GP' => __( 'Guadeloupe', 'ai-for-seo' ), 'GU' => __( 'Guam', 'ai-for-seo' ), 'GT' => __( 'Guatemala', 'ai-for-seo' ), 'GG' => __( 'Guernsey', 'ai-for-seo' ), 'GN' => __( 'Guinea', 'ai-for-seo' ), 'GW' => __( 'Guinea-Bissau', 'ai-for-seo' ), 'GY' => __( 'Guyana', 'ai-for-seo' ), 'HT' => __( 'Haiti', 'ai-for-seo' ), 'HM' => __( 'Heard Island and McDonald Islands', 'ai-for-seo' ), 'HN' => __( 'Honduras', 'ai-for-seo' ), 'HK' => __( 'Hong Kong', 'ai-for-seo' ), 'HU' => __( 'Hungary', 'ai-for-seo' ), 'IS' => __( 'Iceland', 'ai-for-seo' ), 'IN' => __( 'India', 'ai-for-seo' ), 'ID' => __( 'Indonesia', 'ai-for-seo' ), 'IR' => __( 'Iran', 'ai-for-seo' ), 'IQ' => __( 'Iraq', 'ai-for-seo' ), 'IE' => __( 'Ireland', 'ai-for-seo' ), 'IM' => __( 'Isle of Man', 'ai-for-seo' ), 'IL' => __( 'Israel', 'ai-for-seo' ), 'IT' => __( 'Italy', 'ai-for-seo' ), 'CI' => __( 'Ivory Coast', 'ai-for-seo' ), 'JM' => __( 'Jamaica', 'ai-for-seo' ), 'JP' => __( 'Japan', 'ai-for-seo' ), 'JE' => __( 'Jersey', 'ai-for-seo' ), 'JO' => __( 'Jordan', 'ai-for-seo' ), 'KZ' => __( 'Kazakhstan', 'ai-for-seo' ), 'KE' => __( 'Kenya', 'ai-for-seo' ), 'KI' => __( 'Kiribati', 'ai-for-seo' ), 'XK' => __( 'Kosovo', 'ai-for-seo' ), 'KW' => __( 'Kuwait', 'ai-for-seo' ), 'KG' => __( 'Kyrgyzstan', 'ai-for-seo' ), 'LA' => __( 'Laos', 'ai-for-seo' ), 'LV' => __( 'Latvia', 'ai-for-seo' ), 'LB' => __( 'Lebanon', 'ai-for-seo' ), 'LS' => __( 'Lesotho', 'ai-for-seo' ), 'LR' => __( 'Liberia', 'ai-for-seo' ), 'LY' => __( 'Libya', 'ai-for-seo' ), 'LI' => __( 'Liechtenstein', 'ai-for-seo' ), 'LT' => __( 'Lithuania', 'ai-for-seo' ), 'LU' => __( 'Luxembourg', 'ai-for-seo' ), 'MO' => __( 'Macao', 'ai-for-seo' ), 'MK' => __( 'North Macedonia', 'ai-for-seo' ), 'MG' => __( 'Madagascar', 'ai-for-seo' ), 'MW' => __( 'Malawi', 'ai-for-seo' ), 'MY' => __( 'Malaysia', 'ai-for-seo' ), 'MV' => __( 'Maldives', 'ai-for-seo' ), 'ML' => __( 'Mali', 'ai-for-seo' ), 'MT' => __( 'Malta', 'ai-for-seo' ), 'MH' => __( 'Marshall Islands', 'ai-for-seo' ), 'MQ' => __( 'Martinique', 'ai-for-seo' ), 'MR' => __( 'Mauritania', 'ai-for-seo' ), 'MU' => __( 'Mauritius', 'ai-for-seo' ), 'YT' => __( 'Mayotte', 'ai-for-seo' ), 'MX' => __( 'Mexico', 'ai-for-seo' ), 'FM' => __( 'Micronesia', 'ai-for-seo' ), 'MD' => __( 'Moldova', 'ai-for-seo' ), 'MC' => __( 'Monaco', 'ai-for-seo' ), 'MN' => __( 'Mongolia', 'ai-for-seo' ), 'ME' => __( 'Montenegro', 'ai-for-seo' ), 'MS' => __( 'Montserrat', 'ai-for-seo' ), 'MA' => __( 'Morocco', 'ai-for-seo' ), 'MZ' => __( 'Mozambique', 'ai-for-seo' ), 'MM' => __( 'Myanmar', 'ai-for-seo' ), 'NA' => __( 'Namibia', 'ai-for-seo' ), 'NR' => __( 'Nauru', 'ai-for-seo' ), 'NP' => __( 'Nepal', 'ai-for-seo' ), 'NL' => __( 'Netherlands', 'ai-for-seo' ), 'NC' => __( 'New Caledonia', 'ai-for-seo' ), 'NZ' => __( 'New Zealand', 'ai-for-seo' ), 'NI' => __( 'Nicaragua', 'ai-for-seo' ), 'NE' => __( 'Niger', 'ai-for-seo' ), 'NG' => __( 'Nigeria', 'ai-for-seo' ), 'NU' => __( 'Niue', 'ai-for-seo' ), 'NF' => __( 'Norfolk Island', 'ai-for-seo' ), 'MP' => __( 'Northern Mariana Islands', 'ai-for-seo' ), 'KP' => __( 'North Korea', 'ai-for-seo' ), 'NO' => __( 'Norway', 'ai-for-seo' ), 'OM' => __( 'Oman', 'ai-for-seo' ), 'PK' => __( 'Pakistan', 'ai-for-seo' ), 'PS' => __( 'Palestinian Territory', 'ai-for-seo' ), 'PA' => __( 'Panama', 'ai-for-seo' ), 'PG' => __( 'Papua New Guinea', 'ai-for-seo' ), 'PY' => __( 'Paraguay', 'ai-for-seo' ), 'PE' => __( 'Peru', 'ai-for-seo' ), 'PH' => __( 'Philippines', 'ai-for-seo' ), 'PN' => __( 'Pitcairn', 'ai-for-seo' ), 'PL' => __( 'Poland', 'ai-for-seo' ), 'PT' => __( 'Portugal', 'ai-for-seo' ), 'PR' => __( 'Puerto Rico', 'ai-for-seo' ), 'QA' => __( 'Qatar', 'ai-for-seo' ), 'RE' => __( 'Reunion', 'ai-for-seo' ), 'RO' => __( 'Romania', 'ai-for-seo' ), 'RU' => __( 'Russia', 'ai-for-seo' ), 'RW' => __( 'Rwanda', 'ai-for-seo' ), 'BL' => __( 'Saint Barthélemy', 'ai-for-seo' ), 'SH' => __( 'Saint Helena', 'ai-for-seo' ), 'KN' => __( 'Saint Kitts and Nevis', 'ai-for-seo' ), 'LC' => __( 'Saint Lucia', 'ai-for-seo' ), 'MF' => __( 'Saint Martin (French part)', 'ai-for-seo' ), 'SX' => __( 'Saint Martin (Dutch part)', 'ai-for-seo' ), 'PM' => __( 'Saint Pierre and Miquelon', 'ai-for-seo' ), 'VC' => __( 'Saint Vincent and the Grenadines', 'ai-for-seo' ), 'SM' => __( 'San Marino', 'ai-for-seo' ), 'ST' => __( 'São Tomé and Príncipe', 'ai-for-seo' ), 'SA' => __( 'Saudi Arabia', 'ai-for-seo' ), 'SN' => __( 'Senegal', 'ai-for-seo' ), 'RS' => __( 'Serbia', 'ai-for-seo' ), 'SC' => __( 'Seychelles', 'ai-for-seo' ), 'SL' => __( 'Sierra Leone', 'ai-for-seo' ), 'SG' => __( 'Singapore', 'ai-for-seo' ), 'SK' => __( 'Slovakia', 'ai-for-seo' ), 'SI' => __( 'Slovenia', 'ai-for-seo' ), 'SB' => __( 'Solomon Islands', 'ai-for-seo' ), 'SO' => __( 'Somalia', 'ai-for-seo' ), 'ZA' => __( 'South Africa', 'ai-for-seo' ), 'GS' => __( 'South Georgia/Sandwich Islands', 'ai-for-seo' ), 'KR' => __( 'South Korea', 'ai-for-seo' ), 'SS' => __( 'South Sudan', 'ai-for-seo' ), 'ES' => __( 'Spain', 'ai-for-seo' ), 'LK' => __( 'Sri Lanka', 'ai-for-seo' ), 'SD' => __( 'Sudan', 'ai-for-seo' ), 'SR' => __( 'Suriname', 'ai-for-seo' ), 'SJ' => __( 'Svalbard and Jan Mayen', 'ai-for-seo' ), 'SZ' => __( 'Eswatini', 'ai-for-seo' ), 'SE' => __( 'Sweden', 'ai-for-seo' ), 'CH' => __( 'Switzerland', 'ai-for-seo' ), 'SY' => __( 'Syria', 'ai-for-seo' ), 'TW' => __( 'Taiwan', 'ai-for-seo' ), 'TJ' => __( 'Tajikistan', 'ai-for-seo' ), 'TZ' => __( 'Tanzania', 'ai-for-seo' ), 'TH' => __( 'Thailand', 'ai-for-seo' ), 'TL' => __( 'Timor-Leste', 'ai-for-seo' ), 'TG' => __( 'Togo', 'ai-for-seo' ), 'TK' => __( 'Tokelau', 'ai-for-seo' ), 'TO' => __( 'Tonga', 'ai-for-seo' ), 'TT' => __( 'Trinidad and Tobago', 'ai-for-seo' ), 'TN' => __( 'Tunisia', 'ai-for-seo' ), 'TR' => __( 'Türkiye', 'ai-for-seo' ), 'TM' => __( 'Turkmenistan', 'ai-for-seo' ), 'TC' => __( 'Turks and Caicos Islands', 'ai-for-seo' ), 'TV' => __( 'Tuvalu', 'ai-for-seo' ), 'UG' => __( 'Uganda', 'ai-for-seo' ), 'UA' => __( 'Ukraine', 'ai-for-seo' ), 'AE' => __( 'United Arab Emirates', 'ai-for-seo' ), 'GB' => __( 'United Kingdom (UK)', 'ai-for-seo' ), 'US' => __( 'United States (US)', 'ai-for-seo' ), 'UM' => __( 'United States (US) Minor Outlying Islands', 'ai-for-seo' ), 'UY' => __( 'Uruguay', 'ai-for-seo' ), 'UZ' => __( 'Uzbekistan', 'ai-for-seo' ), 'VU' => __( 'Vanuatu', 'ai-for-seo' ), 'VA' => __( 'Vatican', 'ai-for-seo' ), 'VE' => __( 'Venezuela', 'ai-for-seo' ), 'VN' => __( 'Vietnam', 'ai-for-seo' ), 'VG' => __( 'Virgin Islands (British)', 'ai-for-seo' ), 'VI' => __( 'Virgin Islands (US)', 'ai-for-seo' ), 'WF' => __( 'Wallis and Futuna', 'ai-for-seo' ), 'EH' => __( 'Western Sahara', 'ai-for-seo' ), 'WS' => __( 'Samoa', 'ai-for-seo' ), 'YE' => __( 'Yemen', 'ai-for-seo' ), 'ZM' => __( 'Zambia', 'ai-for-seo' ), 'ZW' => __( 'Zimbabwe', 'ai-for-seo' ), ); } /** * Return the currency codes supported by plugin billing controls. * * @return array Supported ISO currency codes. */ function ai4seo_get_allowed_currencies(): array { return array( 'AED', 'AFN', 'ALL', 'AMD', 'ANG', 'AOA', 'ARS', 'AUD', 'AWG', 'AZN', 'BAM', 'BBD', 'BDT', 'BGN', 'BHD', 'BIF', 'BMD', 'BND', 'BOB', 'BRL', 'BSD', 'BTC', 'BTN', 'BWP', 'BYN', 'BZD', 'CAD', 'CDF', 'CHF', 'CLF', 'CLP', 'CNH', 'CNY', 'COP', 'CRC', 'CUC', 'CUP', 'CVE', 'CZK', 'DJF', 'DKK', 'DOP', 'DZD', 'EGP', 'ERN', 'ETB', 'EUR', 'FJD', 'FKP', 'GBP', 'GEL', 'GGP', 'GHS', 'GIP', 'GMD', 'GNF', 'GTQ', 'GYD', 'HKD', 'HNL', 'HRK', 'HTG', 'HUF', 'IDR', 'ILS', 'IMP', 'INR', 'IQD', 'IRR', 'ISK', 'JEP', 'JMD', 'JOD', 'JPY', 'KES', 'KGS', 'KHR', 'KMF', 'KPW', 'KRW', 'KWD', 'KYD', 'KZT', 'LAK', 'LBP', 'LKR', 'LRD', 'LSL', 'LYD', 'MAD', 'MDL', 'MGA', 'MKD', 'MMK', 'MNT', 'MOP', 'MRU', 'MUR', 'MVR', 'MWK', 'MXN', 'MYR', 'MZN', 'NAD', 'NGN', 'NIO', 'NOK', 'NPR', 'NZD', 'OMR', 'PAB', 'PEN', 'PGK', 'PHP', 'PKR', 'PLN', 'PYG', 'QAR', 'RON', 'RSD', 'RUB', 'RWF', 'SAR', 'SBD', 'SCR', 'SDG', 'SEK', 'SGD', 'SHP', 'SLL', 'SOS', 'SRD', 'SSP', 'STD', 'STN', 'SVC', 'SYP', 'SZL', 'THB', 'TJS', 'TMT', 'TND', 'TOP', 'TRY', 'TTD', 'TWD', 'TZS', 'UAH', 'UGX', 'USD', 'UYU', 'UZS', 'VES', 'VND', 'VUV', 'WST', 'XAF', 'XAG', 'XAU', 'XCD', 'XDR', 'XOF', 'XPD', 'XPF', 'XPT', 'YER', 'ZAR', 'ZMW', 'ZWL', ); } // endregion _________________________________________________________________________________ \\ // region PLUGIN'S SETTINGS ==============================================================================. /** Check .agent/rules/settings.md for a guide on how to use Plugin's settings */ // SETTINGS FROM THE SETTINGS PAGE. const AI4SEO_SETTING_SHOW_ADVANCED_SETTINGS = 'show_advanced_settings'; const AI4SEO_SETTING_GLOBAL_CUSTOM_INSTRUCTIONS = 'global_custom_instructions'; const AI4SEO_SETTING_DEFAULT_EDITOR_VIEW_MODE = 'default_editor_view_mode'; const AI4SEO_SETTING_ENABLE_NATIVE_BULK_ACTIONS = 'enable_native_bulk_actions'; const AI4SEO_SETTING_VISIBLE_META_TAGS = 'visible_meta_tags'; // deprecated < 2.2.0. const AI4SEO_SETTING_ACTIVE_META_TAGS = 'active_meta_tags'; // added in 2.2.0. const AI4SEO_SETTING_METADATA_CUSTOM_INSTRUCTIONS = 'metadata_custom_instructions'; const AI4SEO_SETTING_METADATA_POST_TYPE_CUSTOM_INSTRUCTIONS = 'metadata_post_type_custom_instructions'; const AI4SEO_SETTING_METADATA_FALLBACK_META_TITLE = 'metadata_fallback_meta_title'; const AI4SEO_SETTING_METADATA_FALLBACK_META_DESCRIPTION = 'metadata_fallback_meta_description'; const AI4SEO_SETTING_METADATA_FALLBACK_FACEBOOK_TITLE = 'metadata_fallback_facebook_title'; const AI4SEO_SETTING_METADATA_FALLBACK_FACEBOOK_DESCRIPTION = 'metadata_fallback_facebook_description'; const AI4SEO_SETTING_METADATA_FALLBACK_TWITTER_TITLE = 'metadata_fallback_twitter_title'; const AI4SEO_SETTING_METADATA_FALLBACK_TWITTER_DESCRIPTION = 'metadata_fallback_twitter_description'; const AI4SEO_SETTING_META_TAG_OUTPUT_MODE = 'meta_tags_output_method'; const AI4SEO_SETTING_APPLY_CHANGES_TO_THIRD_PARTY_SEO_PLUGINS = 'apply_changes_to_this_party_seo_plugins'; const AI4SEO_SETTING_ENABLE_EXTERNAL_METADATA_GENERATE_BUTTONS = 'enable_external_metadata_generate_buttons'; const AI4SEO_SETTING_SYNC_ONLY_THESE_METADATA = 'sync_only_these_metadata'; const AI4SEO_SETTING_METADATA_GENERATION_LANGUAGE = 'metadata_generation_language'; const AI4SEO_SETTING_ATTACHMENT_ATTRIBUTES_GENERATION_LANGUAGE = 'attachment_attributes_generation_language'; const AI4SEO_SETTING_ACTIVE_ATTACHMENT_ATTRIBUTES = 'active_attachment_attributes'; const AI4SEO_SETTING_ENABLED_ATTACHMENT_FILE_TYPES = 'enabled_attachment_file_types'; const AI4SEO_ATTACHMENT_FILE_TYPE_MIME_TYPES = array( 'jpeg' => 'image/jpeg', 'png' => 'image/png', 'gif' => 'image/gif', 'webp' => 'image/webp', 'avif' => 'image/avif', 'svg' => 'image/svg+xml', ); const AI4SEO_LEGACY_ATTACHMENT_FILE_TYPES = array( 'jpeg', 'png', 'gif', 'webp', 'avif' ); const AI4SEO_SVG_MAX_SOURCE_BYTES = 1048576; const AI4SEO_SETTING_OVERWRITE_EXISTING_METADATA = 'overwrite_existing_metadata'; const AI4SEO_SETTING_OVERWRITE_EXISTING_ATTACHMENT_ATTRIBUTES = 'overwrite_existing_attachment_attributes'; const AI4SEO_SETTING_GENERATE_METADATA_FOR_FULLY_COVERED_ENTRIES = 'generate_metadata_for_fully_covered_entries'; const AI4SEO_SETTING_GENERATE_ATTACHMENT_ATTRIBUTES_FOR_FULLY_COVERED_ENTRIES = 'generate_attachment_attributes_for_fully_covered_entries'; const AI4SEO_SETTING_ENABLE_RENDER_LEVEL_ALT_TEXT_INJECTION = 'enable_render_level_alt_text_injection'; const AI4SEO_SETTING_ENABLE_JS_ALT_TEXT_INJECTION = 'enable_js_alt_text_injection'; const AI4SEO_SETTING_IMAGE_TITLE_INJECTION_MODE = 'image_title_injection_mode'; const AI4SEO_SETTING_METADATA_PREFIXES = 'metadata_prefix'; const AI4SEO_SETTING_METADATA_SUFFIXES = 'metadata_suffix'; const AI4SEO_SETTING_INCLUDE_PRODUCT_PRICE_IN_METADATA = 'include_product_price_in_metadata'; const AI4SEO_SETTING_FOCUS_KEYPHRASE_BEHAVIOR_ON_EXISTING_METADATA = 'focus_keyphrase_behavior_on_existing_metadata'; const AI4SEO_SETTING_USE_EXISTING_METADATA_AS_REFERENCE = 'use_existing_metadata_as_reference'; // Shared view modes for the Metadata and Media Attributes editors. const AI4SEO_EDITOR_VIEW_MODE_PREVIEW = 'preview'; const AI4SEO_EDITOR_VIEW_MODE_EDITOR = 'editor'; const AI4SEO_EDITOR_VIEW_MODES = array( AI4SEO_EDITOR_VIEW_MODE_PREVIEW, AI4SEO_EDITOR_VIEW_MODE_EDITOR, ); // Prompt sliders are saved settings that the RobHub API reads as staged generation guidance. const AI4SEO_SETTING_METADATA_EXISTING_VALUES_REFERENCE_STRENGTH = 'metadata_existing_values_reference_strength'; const AI4SEO_SETTING_METADATA_FOCUS_KEYPHRASE_INFLUENCE = 'metadata_focus_keyphrase_influence'; const AI4SEO_SETTING_METADATA_SEO_KEYWORD_INTENSITY = 'metadata_seo_keyword_intensity'; const AI4SEO_SETTING_METADATA_COMMERCIAL_TONE = 'metadata_commercial_tone'; const AI4SEO_SETTING_METADATA_SOCIAL_VARIATION = 'metadata_social_variation'; const AI4SEO_SETTING_METADATA_WEBSITE_BRAND_CONTEXT_INFLUENCE = 'metadata_website_brand_context_influence'; const AI4SEO_SETTING_METADATA_TONE_VARIANT = 'metadata_tone_variant'; // Generation-length controls are separate saved sliders so each output field can resolve its own request contract. const AI4SEO_SETTING_METADATA_META_TITLE_GENERATION_LENGTH = 'metadata_meta_title_generation_length'; const AI4SEO_SETTING_METADATA_META_DESCRIPTION_GENERATION_LENGTH = 'metadata_meta_description_generation_length'; const AI4SEO_SETTING_METADATA_FACEBOOK_TITLE_GENERATION_LENGTH = 'metadata_facebook_title_generation_length'; const AI4SEO_SETTING_METADATA_FACEBOOK_DESCRIPTION_GENERATION_LENGTH = 'metadata_facebook_description_generation_length'; const AI4SEO_SETTING_METADATA_TWITTER_TITLE_GENERATION_LENGTH = 'metadata_twitter_title_generation_length'; const AI4SEO_SETTING_METADATA_TWITTER_DESCRIPTION_GENERATION_LENGTH = 'metadata_twitter_description_generation_length'; const AI4SEO_SETTING_ATTACHMENT_ATTRIBUTES_CUSTOM_INSTRUCTIONS = 'attachment_attributes_custom_instructions'; const AI4SEO_SETTING_ATTACHMENT_ATTRIBUTES_PREFIXES = 'attachment_attributes_prefix'; const AI4SEO_SETTING_ATTACHMENT_ATTRIBUTES_SUFFIXES = 'attachment_attributes_suffix'; const AI4SEO_SETTING_ENABLE_EXTERNAL_MEDIA_GENERATE_BUTTONS = 'enable_external_media_generate_buttons'; const AI4SEO_SETTING_USE_EXISTING_ATTACHMENT_ATTRIBUTES_AS_REFERENCE = 'use_existing_attachment_attributes_as_reference'; // Media prompt sliders share the same staged-radio renderer as metadata sliders but target media prompts. const AI4SEO_SETTING_ATTACHMENT_ATTRIBUTES_SURROUNDING_CONTEXT_INFLUENCE = 'attachment_attributes_surrounding_context_influence'; const AI4SEO_SETTING_ATTACHMENT_ATTRIBUTES_FILE_NAME_INFLUENCE = 'attachment_attributes_file_name_influence'; const AI4SEO_SETTING_ATTACHMENT_ATTRIBUTES_EXISTING_VALUES_REFERENCE_STRENGTH = 'attachment_attributes_existing_values_reference_strength'; const AI4SEO_SETTING_ATTACHMENT_ATTRIBUTES_SEO_KEYWORD_INTENSITY = 'attachment_attributes_seo_keyword_intensity'; const AI4SEO_SETTING_ATTACHMENT_ATTRIBUTES_RECOGNIZABLE_ENTITY_INCLUSION = 'attachment_attributes_recognizable_entity_inclusion'; const AI4SEO_SETTING_ATTACHMENT_ATTRIBUTES_WEBSITE_BRAND_CONTEXT_INFLUENCE = 'attachment_attributes_website_brand_context_influence'; const AI4SEO_SETTING_ATTACHMENT_ATTRIBUTES_TONE_VARIANT = 'attachment_attributes_tone_variant'; const AI4SEO_SETTING_ATTACHMENT_ATTRIBUTES_ALT_TEXT_GENERATION_LENGTH = 'attachment_attributes_alt_text_generation_length'; const AI4SEO_SETTING_ENABLE_ENHANCED_ENTITY_RECOGNITION = 'enable_enhanced_entity_recognition'; const AI4SEO_SETTING_ENABLE_ENHANCED_CELEBRITY_RECOGNITION = 'enable_enhanced_celebrity_recognition'; const AI4SEO_SETTING_IMAGE_UPLOAD_METHOD = 'image_upload_method'; const AI4SEO_SETTING_ALLOWED_USER_ROLES = 'allowed_user_roles'; const AI4SEO_SETTING_DISABLED_POST_TYPES = 'disabled_post_types'; const AI4SEO_SETTING_DISABLED_POST_AUTHORS = 'disabled_post_authors'; const AI4SEO_SETTING_DISABLED_ATTACHMENT_POST_AUTHORS = 'disabled_attachment_post_authors'; const AI4SEO_SETTING_DISABLED_METADATA_WPML_LANGUAGES = 'disabled_metadata_wpml_languages'; const AI4SEO_SETTING_DISABLED_ATTACHMENT_ATTRIBUTES_WPML_LANGUAGES = 'disabled_attachment_attributes_wpml_languages'; const AI4SEO_SETTING_DISABLED_TAXONOMY_TERMS = 'disabled_taxonomy_terms'; const AI4SEO_SETTING_EXCLUDE_POSTS_IF_ANY_DISABLED_TAXONOMY_TERM = 'exclude_posts_if_any_disabled_taxonomy_term'; const AI4SEO_SETTING_BULK_GENERATION_DURATION = 'bulk_generation_duration'; const AI4SEO_SETTING_DISABLE_HEAVY_DB_OPERATIONS = 'disable_heavy_db_operations'; const AI4SEO_SETTING_ENABLE_FRONTEND_CACHE_PURGE = 'enable_frontend_cache_purge'; const AI4SEO_SETTING_DEEP_CONTEXT_SEARCH_FOR_IMAGES = 'deep_context_search_for_images'; const AI4SEO_SETTING_DEBUG_OUTPUT_MODE = 'debug_output_mode'; const AI4SEO_SETTING_QUERY_IDS_CHUNK_SIZE = 'query_ids_chunk_size'; // settings option values. const AI4SEO_FOCUS_KEYPHRASE_BEHAVIOR_GENERATE_KEYPHRASE = 'generate_keyphrase'; const AI4SEO_FOCUS_KEYPHRASE_BEHAVIOR_SKIP = 'skip'; const AI4SEO_FOCUS_KEYPHRASE_BEHAVIOR_REGENERATE = 'regenerate'; const AI4SEO_AVAILABLE_METADATA_IDENTIFIERS = array( 'focus-keyphrase', 'meta-title', 'meta-description', 'keywords', 'facebook-title', 'facebook-description', 'twitter-title', 'twitter-description', ); const AI4SEO_AVAILABLE_ATTACHMENT_ATTRIBUTE_IDENTIFIERS = array( 'title', 'alt-text', 'caption', 'description', ); const AI4SEO_AVAILABLE_DEBUG_OUTPUT_MODE_OPTIONS = array( 'none', 'error_log', 'file', 'database', 'notice', 'print_r', ); const AI4SEO_AVAILABLE_META_TAG_OUTPUT_MODE_OPTIONS = array( 'disable', 'force', 'replace', 'complement', ); const AI4SEO_AVAILABLE_IMAGE_TITLE_INJECTION_MODE_OPTIONS = array( 'disabled', 'inject_title', 'inject_alt_text', 'inject_caption', 'inject_description', ); const AI4SEO_AVAILABLE_INCLUDE_PRODUCT_PRICE_IN_METADATA_OPTIONS = array( 'never', 'fixed', 'dynamic', ); const AI4SEO_AVAILABLE_FOCUS_KEYPHRASE_BEHAVIOR_OPTIONS = array( AI4SEO_FOCUS_KEYPHRASE_BEHAVIOR_SKIP, AI4SEO_FOCUS_KEYPHRASE_BEHAVIOR_GENERATE_KEYPHRASE, AI4SEO_FOCUS_KEYPHRASE_BEHAVIOR_REGENERATE, ); // Stage counts keep import/save validation aligned with each slider's supported prompt-control range. const AI4SEO_PROMPT_SLIDER_SETTING_STAGE_COUNTS = array( AI4SEO_SETTING_ATTACHMENT_ATTRIBUTES_SURROUNDING_CONTEXT_INFLUENCE => 5, AI4SEO_SETTING_ATTACHMENT_ATTRIBUTES_FILE_NAME_INFLUENCE => 5, AI4SEO_SETTING_METADATA_EXISTING_VALUES_REFERENCE_STRENGTH => 5, AI4SEO_SETTING_ATTACHMENT_ATTRIBUTES_EXISTING_VALUES_REFERENCE_STRENGTH => 5, AI4SEO_SETTING_METADATA_FOCUS_KEYPHRASE_INFLUENCE => 5, AI4SEO_SETTING_METADATA_SEO_KEYWORD_INTENSITY => 5, AI4SEO_SETTING_ATTACHMENT_ATTRIBUTES_SEO_KEYWORD_INTENSITY => 5, AI4SEO_SETTING_METADATA_COMMERCIAL_TONE => 5, AI4SEO_SETTING_METADATA_SOCIAL_VARIATION => 5, AI4SEO_SETTING_ATTACHMENT_ATTRIBUTES_RECOGNIZABLE_ENTITY_INCLUSION => 5, AI4SEO_SETTING_METADATA_WEBSITE_BRAND_CONTEXT_INFLUENCE => 5, AI4SEO_SETTING_ATTACHMENT_ATTRIBUTES_WEBSITE_BRAND_CONTEXT_INFLUENCE => 5, AI4SEO_SETTING_METADATA_TONE_VARIANT => 5, AI4SEO_SETTING_ATTACHMENT_ATTRIBUTES_TONE_VARIANT => 5, AI4SEO_SETTING_METADATA_META_TITLE_GENERATION_LENGTH => 5, AI4SEO_SETTING_METADATA_META_DESCRIPTION_GENERATION_LENGTH => 5, AI4SEO_SETTING_METADATA_FACEBOOK_TITLE_GENERATION_LENGTH => 5, AI4SEO_SETTING_METADATA_FACEBOOK_DESCRIPTION_GENERATION_LENGTH => 5, AI4SEO_SETTING_METADATA_TWITTER_TITLE_GENERATION_LENGTH => 5, AI4SEO_SETTING_METADATA_TWITTER_DESCRIPTION_GENERATION_LENGTH => 5, AI4SEO_SETTING_ATTACHMENT_ATTRIBUTES_ALT_TEXT_GENERATION_LENGTH => 5, ); // Paid-stage requirements are shared by rendering, request serialization, and effective-stage resolution. const AI4SEO_GENERATION_LENGTH_STAGE_MINIMUM_PLANS = array( '4' => 's', '5' => 'm', ); // Reuse title stages for metadata and both social networks so their contracts cannot drift. const AI4SEO_GENERATION_LENGTH_TITLE_STAGES = array( '1' => array( 'min-length' => 30, 'max-length' => 45, ), '2' => array( 'min-length' => 45, 'max-length' => 65, ), '3' => array( 'min-length' => 65, 'max-length' => 85, ), '4' => array( 'min-length' => 85, 'max-length' => 110, ), '5' => array( 'min-length' => 110, 'max-length' => 135, ), ); // Meta descriptions use a wider search-result window than title or social-description fields. const AI4SEO_GENERATION_LENGTH_META_DESCRIPTION_STAGES = array( '1' => array( 'min-length' => 105, 'max-length' => 130, ), '2' => array( 'min-length' => 130, 'max-length' => 160, ), '3' => array( 'min-length' => 160, 'max-length' => 210, ), '4' => array( 'min-length' => 210, 'max-length' => 270, ), '5' => array( 'min-length' => 270, 'max-length' => 335, ), ); // Facebook descriptions retain the established social-preview progression. const AI4SEO_GENERATION_LENGTH_FACEBOOK_DESCRIPTION_STAGES = array( '1' => array( 'min-length' => 65, 'max-length' => 90, ), '2' => array( 'min-length' => 90, 'max-length' => 120, ), '3' => array( 'min-length' => 120, 'max-length' => 155, ), '4' => array( 'min-length' => 155, 'max-length' => 195, ), '5' => array( 'min-length' => 195, 'max-length' => 250, ), ); // Twitter descriptions use a slightly wider recommended window while keeping adjacent stages contiguous. const AI4SEO_GENERATION_LENGTH_TWITTER_DESCRIPTION_STAGES = array( '1' => array( 'min-length' => 65, 'max-length' => 90, ), '2' => array( 'min-length' => 90, 'max-length' => 125, ), '3' => array( 'min-length' => 125, 'max-length' => 155, ), '4' => array( 'min-length' => 155, 'max-length' => 195, ), '5' => array( 'min-length' => 195, 'max-length' => 250, ), ); // Alt text keeps its own accessibility-oriented progression and larger later-stage windows. const AI4SEO_GENERATION_LENGTH_ALT_TEXT_STAGES = array( '1' => array( 'min-length' => 70, 'max-length' => 95, ), '2' => array( 'min-length' => 95, 'max-length' => 125, ), '3' => array( 'min-length' => 125, 'max-length' => 160, ), '4' => array( 'min-length' => 160, 'max-length' => 205, ), '5' => array( 'min-length' => 205, 'max-length' => 260, ), ); // This registry is the plugin-side source of truth for slider labels and entitlement-aware quality diagnostics. const AI4SEO_GENERATION_LENGTH_SETTING_DETAILS = array( AI4SEO_SETTING_METADATA_META_TITLE_GENERATION_LENGTH => array( 'context' => 'metadata', 'field-identifier' => 'meta-title', 'stages' => AI4SEO_GENERATION_LENGTH_TITLE_STAGES, ), AI4SEO_SETTING_METADATA_META_DESCRIPTION_GENERATION_LENGTH => array( 'context' => 'metadata', 'field-identifier' => 'meta-description', 'stages' => AI4SEO_GENERATION_LENGTH_META_DESCRIPTION_STAGES, ), AI4SEO_SETTING_METADATA_FACEBOOK_TITLE_GENERATION_LENGTH => array( 'context' => 'metadata', 'field-identifier' => 'facebook-title', 'stages' => AI4SEO_GENERATION_LENGTH_TITLE_STAGES, ), AI4SEO_SETTING_METADATA_FACEBOOK_DESCRIPTION_GENERATION_LENGTH => array( 'context' => 'metadata', 'field-identifier' => 'facebook-description', 'stages' => AI4SEO_GENERATION_LENGTH_FACEBOOK_DESCRIPTION_STAGES, ), AI4SEO_SETTING_METADATA_TWITTER_TITLE_GENERATION_LENGTH => array( 'context' => 'metadata', 'field-identifier' => 'twitter-title', 'stages' => AI4SEO_GENERATION_LENGTH_TITLE_STAGES, ), AI4SEO_SETTING_METADATA_TWITTER_DESCRIPTION_GENERATION_LENGTH => array( 'context' => 'metadata', 'field-identifier' => 'twitter-description', 'stages' => AI4SEO_GENERATION_LENGTH_TWITTER_DESCRIPTION_STAGES, ), AI4SEO_SETTING_ATTACHMENT_ATTRIBUTES_ALT_TEXT_GENERATION_LENGTH => array( 'context' => 'attachment_attributes', 'field-identifier' => 'alt-text', 'stages' => AI4SEO_GENERATION_LENGTH_ALT_TEXT_STAGES, ), ); const AI4SEO_AVAILABLE_GENERATION_LANGUAGE_OPTIONS = array( 'albanian', 'arabic', 'bulgarian', 'chinese', 'simplified chinese', 'traditional chinese', 'croatian', 'czech', 'danish', 'dutch', 'american english', 'british english', 'estonian', 'finnish', 'european french', 'canadian french', 'german', 'greek', 'hebrew', 'hindi', 'hungarian', 'icelandic', 'indonesian', 'italian', 'japanese', 'korean', 'latvian', 'lithuanian', 'macedonian', 'maltese', 'norwegian', 'polish', 'european portuguese', 'brazilian portuguese', 'romanian', 'russian', 'serbian', 'slovak', 'slovenian', 'spanish', 'swedish', 'thai', 'turkish', 'ukrainian', 'vietnamese', ); const AI4SEO_EXPORTABLE_SETTING_PAGE_SETTINGS = array( AI4SEO_SETTING_SHOW_ADVANCED_SETTINGS, AI4SEO_SETTING_GLOBAL_CUSTOM_INSTRUCTIONS, AI4SEO_SETTING_DEFAULT_EDITOR_VIEW_MODE, AI4SEO_SETTING_ENABLE_NATIVE_BULK_ACTIONS, AI4SEO_SETTING_ACTIVE_META_TAGS, AI4SEO_SETTING_METADATA_CUSTOM_INSTRUCTIONS, AI4SEO_SETTING_METADATA_POST_TYPE_CUSTOM_INSTRUCTIONS, AI4SEO_SETTING_METADATA_FALLBACK_META_TITLE, AI4SEO_SETTING_METADATA_FALLBACK_META_DESCRIPTION, AI4SEO_SETTING_METADATA_FALLBACK_FACEBOOK_TITLE, AI4SEO_SETTING_METADATA_FALLBACK_FACEBOOK_DESCRIPTION, AI4SEO_SETTING_METADATA_FALLBACK_TWITTER_TITLE, AI4SEO_SETTING_METADATA_FALLBACK_TWITTER_DESCRIPTION, AI4SEO_SETTING_META_TAG_OUTPUT_MODE, AI4SEO_SETTING_APPLY_CHANGES_TO_THIRD_PARTY_SEO_PLUGINS, AI4SEO_SETTING_ENABLE_EXTERNAL_METADATA_GENERATE_BUTTONS, AI4SEO_SETTING_SYNC_ONLY_THESE_METADATA, AI4SEO_SETTING_METADATA_GENERATION_LANGUAGE, AI4SEO_SETTING_ATTACHMENT_ATTRIBUTES_GENERATION_LANGUAGE, AI4SEO_SETTING_ACTIVE_ATTACHMENT_ATTRIBUTES, AI4SEO_SETTING_ENABLED_ATTACHMENT_FILE_TYPES, AI4SEO_SETTING_OVERWRITE_EXISTING_METADATA, AI4SEO_SETTING_OVERWRITE_EXISTING_ATTACHMENT_ATTRIBUTES, AI4SEO_SETTING_GENERATE_METADATA_FOR_FULLY_COVERED_ENTRIES, AI4SEO_SETTING_GENERATE_ATTACHMENT_ATTRIBUTES_FOR_FULLY_COVERED_ENTRIES, AI4SEO_SETTING_ENABLE_RENDER_LEVEL_ALT_TEXT_INJECTION, AI4SEO_SETTING_ENABLE_JS_ALT_TEXT_INJECTION, AI4SEO_SETTING_IMAGE_TITLE_INJECTION_MODE, AI4SEO_SETTING_METADATA_PREFIXES, AI4SEO_SETTING_METADATA_SUFFIXES, AI4SEO_SETTING_INCLUDE_PRODUCT_PRICE_IN_METADATA, AI4SEO_SETTING_FOCUS_KEYPHRASE_BEHAVIOR_ON_EXISTING_METADATA, AI4SEO_SETTING_USE_EXISTING_METADATA_AS_REFERENCE, // Prompt-control sliders are exportable because they are normal saved settings. AI4SEO_SETTING_METADATA_EXISTING_VALUES_REFERENCE_STRENGTH, AI4SEO_SETTING_METADATA_FOCUS_KEYPHRASE_INFLUENCE, AI4SEO_SETTING_METADATA_SEO_KEYWORD_INTENSITY, AI4SEO_SETTING_METADATA_COMMERCIAL_TONE, AI4SEO_SETTING_METADATA_SOCIAL_VARIATION, AI4SEO_SETTING_METADATA_WEBSITE_BRAND_CONTEXT_INFLUENCE, AI4SEO_SETTING_METADATA_TONE_VARIANT, AI4SEO_SETTING_METADATA_META_TITLE_GENERATION_LENGTH, AI4SEO_SETTING_METADATA_META_DESCRIPTION_GENERATION_LENGTH, AI4SEO_SETTING_METADATA_FACEBOOK_TITLE_GENERATION_LENGTH, AI4SEO_SETTING_METADATA_FACEBOOK_DESCRIPTION_GENERATION_LENGTH, AI4SEO_SETTING_METADATA_TWITTER_TITLE_GENERATION_LENGTH, AI4SEO_SETTING_METADATA_TWITTER_DESCRIPTION_GENERATION_LENGTH, AI4SEO_SETTING_ATTACHMENT_ATTRIBUTES_CUSTOM_INSTRUCTIONS, AI4SEO_SETTING_ATTACHMENT_ATTRIBUTES_PREFIXES, AI4SEO_SETTING_ATTACHMENT_ATTRIBUTES_SUFFIXES, AI4SEO_SETTING_ENABLE_EXTERNAL_MEDIA_GENERATE_BUTTONS, AI4SEO_SETTING_USE_EXISTING_ATTACHMENT_ATTRIBUTES_AS_REFERENCE, // Media prompt controls are exported beside legacy compatibility settings used during migration. AI4SEO_SETTING_ATTACHMENT_ATTRIBUTES_SURROUNDING_CONTEXT_INFLUENCE, AI4SEO_SETTING_ATTACHMENT_ATTRIBUTES_FILE_NAME_INFLUENCE, AI4SEO_SETTING_ATTACHMENT_ATTRIBUTES_EXISTING_VALUES_REFERENCE_STRENGTH, AI4SEO_SETTING_ATTACHMENT_ATTRIBUTES_SEO_KEYWORD_INTENSITY, AI4SEO_SETTING_ATTACHMENT_ATTRIBUTES_RECOGNIZABLE_ENTITY_INCLUSION, AI4SEO_SETTING_ATTACHMENT_ATTRIBUTES_WEBSITE_BRAND_CONTEXT_INFLUENCE, AI4SEO_SETTING_ATTACHMENT_ATTRIBUTES_TONE_VARIANT, AI4SEO_SETTING_ATTACHMENT_ATTRIBUTES_ALT_TEXT_GENERATION_LENGTH, AI4SEO_SETTING_ENABLE_ENHANCED_ENTITY_RECOGNITION, AI4SEO_SETTING_ENABLE_ENHANCED_CELEBRITY_RECOGNITION, AI4SEO_SETTING_IMAGE_UPLOAD_METHOD, AI4SEO_SETTING_ALLOWED_USER_ROLES, AI4SEO_SETTING_DISABLED_POST_TYPES, AI4SEO_SETTING_DISABLED_POST_AUTHORS, AI4SEO_SETTING_DISABLED_ATTACHMENT_POST_AUTHORS, AI4SEO_SETTING_DISABLED_METADATA_WPML_LANGUAGES, AI4SEO_SETTING_DISABLED_ATTACHMENT_ATTRIBUTES_WPML_LANGUAGES, AI4SEO_SETTING_DISABLED_TAXONOMY_TERMS, AI4SEO_SETTING_EXCLUDE_POSTS_IF_ANY_DISABLED_TAXONOMY_TERM, AI4SEO_SETTING_BULK_GENERATION_DURATION, AI4SEO_SETTING_DISABLE_HEAVY_DB_OPERATIONS, AI4SEO_SETTING_ENABLE_FRONTEND_CACHE_PURGE, AI4SEO_SETTING_DEEP_CONTEXT_SEARCH_FOR_IMAGES, AI4SEO_SETTING_DEBUG_OUTPUT_MODE, AI4SEO_SETTING_QUERY_IDS_CHUNK_SIZE, ); const AI4SEO_ALL_SETTING_PAGE_SETTINGS = AI4SEO_EXPORTABLE_SETTING_PAGE_SETTINGS + array(); // SETTINGS FROM THE SEO AUTOPILOT MODAL. const AI4SEO_SETTING_ENABLED_BULK_GENERATION_POST_TYPES = 'enabled_bulk_generation_post_types'; const AI4SEO_SETTING_ENABLED_BULK_GENERATION_TAXONOMIES = 'enabled_bulk_generation_taxonomies'; const AI4SEO_TAXONOMY_GENERATION_INDEX_OPTION = 'ai4seo_taxonomy_generation_index_requested'; const AI4SEO_SETTING_BULK_GENERATION_AUTO_QUEUE_ENTRIES = 'bulk_generation_auto_queue_entries'; const AI4SEO_SETTING_BULK_GENERATION_ORDER = 'bulk_generation_order'; const AI4SEO_SETTING_BULK_GENERATION_NEW_OR_EXISTING_FILTER = 'bulk_generation_new_or_existing_filter'; const AI4SEO_EXPORTABLE_SEO_AUTOPILOT_SETTINGS = array( AI4SEO_SETTING_ENABLED_BULK_GENERATION_TAXONOMIES, AI4SEO_SETTING_ENABLED_BULK_GENERATION_POST_TYPES, AI4SEO_SETTING_BULK_GENERATION_AUTO_QUEUE_ENTRIES, AI4SEO_SETTING_BULK_GENERATION_ORDER, AI4SEO_SETTING_BULK_GENERATION_NEW_OR_EXISTING_FILTER, ); // SETTINGS FROM THE ACCOUNT PAGE. const AI4SEO_SETTING_SHOW_ADMIN_OFFERS = 'show_admin_offers'; const AI4SEO_SETTING_SHOW_PLUGIN_OFFERS = 'show_plugin_offers'; const AI4SEO_SETTING_SHOW_OTHER_NOTIFICATIONS = 'show_other_notifications'; const AI4SEO_SETTING_SHOW_MENU_COUNTERS = 'show_menu_counters'; const AI4SEO_NOTIFICATION_SETTINGS = array( AI4SEO_SETTING_SHOW_ADMIN_OFFERS, AI4SEO_SETTING_SHOW_PLUGIN_OFFERS, AI4SEO_SETTING_SHOW_OTHER_NOTIFICATIONS, AI4SEO_SETTING_SHOW_MENU_COUNTERS, ); const AI4SEO_SETTING_ENABLE_INCOGNITO_MODE = 'enable_incognito_mode'; const AI4SEO_SETTING_INCOGNITO_MODE_USER_ID = 'incognito_mode_user_id'; const AI4SEO_SETTING_ENABLE_WHITE_LABEL = 'enable_white_label'; const AI4SEO_SETTING_INSTALLED_PLUGINS_PLUGIN_NAME = 'installed_plugins_plugin_name'; const AI4SEO_SETTING_INSTALLED_PLUGINS_PLUGIN_DESCRIPTION = 'installed_plugins_plugin_description'; const AI4SEO_SETTING_ADD_GENERATOR_HINTS = 'add_generator_hints'; const AI4SEO_SETTING_META_TAGS_BLOCK_STARTING_HINT = 'meta_tags_block_starting_hint'; const AI4SEO_SETTING_META_TAGS_BLOCK_ENDING_HINT = 'meta_tags_block_ending_hint'; const AI4SEO_EXPORTABLE_ACCOUNT_PAGE_SETTINGS = array( AI4SEO_SETTING_SHOW_ADMIN_OFFERS, AI4SEO_SETTING_SHOW_PLUGIN_OFFERS, AI4SEO_SETTING_SHOW_OTHER_NOTIFICATIONS, AI4SEO_SETTING_SHOW_MENU_COUNTERS, AI4SEO_SETTING_ENABLE_WHITE_LABEL, AI4SEO_SETTING_INSTALLED_PLUGINS_PLUGIN_NAME, AI4SEO_SETTING_INSTALLED_PLUGINS_PLUGIN_DESCRIPTION, AI4SEO_SETTING_ADD_GENERATOR_HINTS, AI4SEO_SETTING_META_TAGS_BLOCK_STARTING_HINT, AI4SEO_SETTING_META_TAGS_BLOCK_ENDING_HINT, ); // SETTINGS FROM THE GET MORE CREDITS MODAL. const AI4SEO_SETTING_PREFERRED_CURRENCY = 'preferred_currency'; const AI4SEO_SETTING_PAYG_ENABLED = 'payg_enabled'; const AI4SEO_SETTING_PAYG_STRIPE_PRICE_ID = 'payg_stripe_price_id'; const AI4SEO_SETTING_PAYG_DAILY_BUDGET = 'payg_daily_budget'; const AI4SEO_SETTING_PAYG_MONTHLY_BUDGET = 'payg_monthly_budget'; const AI4SEO_EXPORTABLE_GET_MORE_CREDITS_MODAL_SETTINGS = array( AI4SEO_SETTING_PREFERRED_CURRENCY, AI4SEO_SETTING_PAYG_STRIPE_PRICE_ID, AI4SEO_SETTING_PAYG_DAILY_BUDGET, AI4SEO_SETTING_PAYG_MONTHLY_BUDGET, ); // NOT IMPORTABLE SETTINGS. const AI4SEO_NOT_IMPORTABLE_SETTINGS = array( AI4SEO_SETTING_INCOGNITO_MODE_USER_ID, ); // DEFAULT SETTINGS. const AI4SEO_DEFAULT_SETTINGS = array( AI4SEO_SETTING_SHOW_ADMIN_OFFERS => true, AI4SEO_SETTING_SHOW_PLUGIN_OFFERS => true, AI4SEO_SETTING_SHOW_OTHER_NOTIFICATIONS => true, AI4SEO_SETTING_SHOW_MENU_COUNTERS => true, AI4SEO_SETTING_SHOW_ADVANCED_SETTINGS => 'hide', AI4SEO_SETTING_GLOBAL_CUSTOM_INSTRUCTIONS => '', AI4SEO_SETTING_DEFAULT_EDITOR_VIEW_MODE => AI4SEO_EDITOR_VIEW_MODE_PREVIEW, AI4SEO_SETTING_ENABLE_NATIVE_BULK_ACTIONS => false, AI4SEO_SETTING_BULK_GENERATION_DURATION => 60, AI4SEO_SETTING_META_TAG_OUTPUT_MODE => 'replace', AI4SEO_SETTING_APPLY_CHANGES_TO_THIRD_PARTY_SEO_PLUGINS => array(), AI4SEO_SETTING_ENABLE_EXTERNAL_METADATA_GENERATE_BUTTONS => false, AI4SEO_SETTING_SYNC_ONLY_THESE_METADATA => array( 'focus-keyphrase', 'meta-title', 'meta-description', 'keywords', 'facebook-title', 'facebook-description', 'twitter-title', 'twitter-description' ), AI4SEO_SETTING_ALLOWED_USER_ROLES => array( 'administrator' ), AI4SEO_SETTING_DISABLED_POST_TYPES => array(), AI4SEO_SETTING_DISABLED_POST_AUTHORS => array(), AI4SEO_SETTING_DISABLED_ATTACHMENT_POST_AUTHORS => array(), AI4SEO_SETTING_DISABLED_METADATA_WPML_LANGUAGES => array(), AI4SEO_SETTING_DISABLED_ATTACHMENT_ATTRIBUTES_WPML_LANGUAGES => array(), AI4SEO_SETTING_DISABLED_TAXONOMY_TERMS => array(), AI4SEO_SETTING_EXCLUDE_POSTS_IF_ANY_DISABLED_TAXONOMY_TERM => false, AI4SEO_SETTING_ENABLED_BULK_GENERATION_POST_TYPES => array(), AI4SEO_SETTING_ENABLED_BULK_GENERATION_TAXONOMIES => array(), AI4SEO_SETTING_BULK_GENERATION_AUTO_QUEUE_ENTRIES => true, AI4SEO_SETTING_BULK_GENERATION_ORDER => 'newest', AI4SEO_SETTING_BULK_GENERATION_NEW_OR_EXISTING_FILTER => 'both', AI4SEO_SETTING_METADATA_GENERATION_LANGUAGE => 'auto', AI4SEO_SETTING_ATTACHMENT_ATTRIBUTES_GENERATION_LANGUAGE => 'auto', AI4SEO_SETTING_ACTIVE_META_TAGS => array( 'focus-keyphrase', 'meta-title', 'meta-description', 'keywords', 'facebook-title', 'facebook-description', 'twitter-title', 'twitter-description' ), AI4SEO_SETTING_METADATA_CUSTOM_INSTRUCTIONS => '', AI4SEO_SETTING_METADATA_POST_TYPE_CUSTOM_INSTRUCTIONS => array(), AI4SEO_SETTING_METADATA_FALLBACK_META_TITLE => 'no-fallback', AI4SEO_SETTING_METADATA_FALLBACK_META_DESCRIPTION => 'no-fallback', AI4SEO_SETTING_METADATA_FALLBACK_FACEBOOK_TITLE => 'no-fallback', AI4SEO_SETTING_METADATA_FALLBACK_FACEBOOK_DESCRIPTION => 'no-fallback', AI4SEO_SETTING_METADATA_FALLBACK_TWITTER_TITLE => 'no-fallback', AI4SEO_SETTING_METADATA_FALLBACK_TWITTER_DESCRIPTION => 'no-fallback', AI4SEO_SETTING_ACTIVE_ATTACHMENT_ATTRIBUTES => array( 'title', 'alt-text', 'caption', 'description' ), AI4SEO_SETTING_ENABLED_ATTACHMENT_FILE_TYPES => array( 'jpeg', 'png', 'gif', 'webp', 'avif', 'svg' ), AI4SEO_SETTING_OVERWRITE_EXISTING_METADATA => array(), AI4SEO_SETTING_OVERWRITE_EXISTING_ATTACHMENT_ATTRIBUTES => array(), AI4SEO_SETTING_GENERATE_METADATA_FOR_FULLY_COVERED_ENTRIES => false, AI4SEO_SETTING_INCLUDE_PRODUCT_PRICE_IN_METADATA => 'never', AI4SEO_SETTING_FOCUS_KEYPHRASE_BEHAVIOR_ON_EXISTING_METADATA => AI4SEO_FOCUS_KEYPHRASE_BEHAVIOR_SKIP, AI4SEO_SETTING_USE_EXISTING_METADATA_AS_REFERENCE => false, // New installs use balanced prompt-slider defaults until users choose weaker or stronger generation guidance. AI4SEO_SETTING_METADATA_EXISTING_VALUES_REFERENCE_STRENGTH => '3', AI4SEO_SETTING_METADATA_FOCUS_KEYPHRASE_INFLUENCE => '3', AI4SEO_SETTING_METADATA_SEO_KEYWORD_INTENSITY => '3', AI4SEO_SETTING_METADATA_COMMERCIAL_TONE => '3', AI4SEO_SETTING_METADATA_SOCIAL_VARIATION => '3', AI4SEO_SETTING_METADATA_WEBSITE_BRAND_CONTEXT_INFLUENCE => '3', AI4SEO_SETTING_METADATA_TONE_VARIANT => '3', // Option 2 preserves the generation windows used before per-field length controls were introduced. AI4SEO_SETTING_METADATA_META_TITLE_GENERATION_LENGTH => '2', AI4SEO_SETTING_METADATA_META_DESCRIPTION_GENERATION_LENGTH => '2', AI4SEO_SETTING_METADATA_FACEBOOK_TITLE_GENERATION_LENGTH => '2', AI4SEO_SETTING_METADATA_FACEBOOK_DESCRIPTION_GENERATION_LENGTH => '2', AI4SEO_SETTING_METADATA_TWITTER_TITLE_GENERATION_LENGTH => '2', AI4SEO_SETTING_METADATA_TWITTER_DESCRIPTION_GENERATION_LENGTH => '2', AI4SEO_SETTING_GENERATE_ATTACHMENT_ATTRIBUTES_FOR_FULLY_COVERED_ENTRIES => false, AI4SEO_SETTING_ATTACHMENT_ATTRIBUTES_CUSTOM_INSTRUCTIONS => '', AI4SEO_SETTING_ENABLE_RENDER_LEVEL_ALT_TEXT_INJECTION => false, AI4SEO_SETTING_ENABLE_JS_ALT_TEXT_INJECTION => false, AI4SEO_SETTING_DISABLE_HEAVY_DB_OPERATIONS => false, AI4SEO_SETTING_ENABLE_FRONTEND_CACHE_PURGE => false, AI4SEO_SETTING_DEEP_CONTEXT_SEARCH_FOR_IMAGES => false, AI4SEO_SETTING_DEBUG_OUTPUT_MODE => 'none', AI4SEO_SETTING_QUERY_IDS_CHUNK_SIZE => 1000, AI4SEO_SETTING_USE_EXISTING_ATTACHMENT_ATTRIBUTES_AS_REFERENCE => false, // Media prompt-slider defaults mirror the same balanced baseline as metadata. AI4SEO_SETTING_ATTACHMENT_ATTRIBUTES_SURROUNDING_CONTEXT_INFLUENCE => '3', AI4SEO_SETTING_ATTACHMENT_ATTRIBUTES_FILE_NAME_INFLUENCE => '3', AI4SEO_SETTING_ATTACHMENT_ATTRIBUTES_EXISTING_VALUES_REFERENCE_STRENGTH => '3', AI4SEO_SETTING_ATTACHMENT_ATTRIBUTES_SEO_KEYWORD_INTENSITY => '3', AI4SEO_SETTING_ATTACHMENT_ATTRIBUTES_RECOGNIZABLE_ENTITY_INCLUSION => '3', AI4SEO_SETTING_ATTACHMENT_ATTRIBUTES_WEBSITE_BRAND_CONTEXT_INFLUENCE => '3', AI4SEO_SETTING_ATTACHMENT_ATTRIBUTES_TONE_VARIANT => '3', AI4SEO_SETTING_ATTACHMENT_ATTRIBUTES_ALT_TEXT_GENERATION_LENGTH => '2', AI4SEO_SETTING_ENABLE_ENHANCED_ENTITY_RECOGNITION => true, AI4SEO_SETTING_ENABLE_ENHANCED_CELEBRITY_RECOGNITION => false, AI4SEO_SETTING_IMAGE_TITLE_INJECTION_MODE => 'disabled', AI4SEO_SETTING_METADATA_PREFIXES => array( 'meta-title' => '', 'meta-description' => '', 'facebook-title' => '', 'facebook-description' => '', 'twitter-title' => '', 'twitter-description' => '', ), AI4SEO_SETTING_METADATA_SUFFIXES => array( 'meta-title' => '', 'meta-description' => '', 'facebook-title' => '', 'facebook-description' => '', 'twitter-title' => '', 'twitter-description' => '', ), AI4SEO_SETTING_ATTACHMENT_ATTRIBUTES_PREFIXES => array( 'title' => '', 'alt-text' => '', 'caption' => '', 'description' => '', ), AI4SEO_SETTING_ATTACHMENT_ATTRIBUTES_SUFFIXES => array( 'title' => '', 'alt-text' => '', 'caption' => '', 'description' => '', ), AI4SEO_SETTING_ENABLE_EXTERNAL_MEDIA_GENERATE_BUTTONS => false, AI4SEO_SETTING_IMAGE_UPLOAD_METHOD => 'auto', AI4SEO_SETTING_ENABLE_INCOGNITO_MODE => false, AI4SEO_SETTING_INCOGNITO_MODE_USER_ID => '0', AI4SEO_SETTING_ENABLE_WHITE_LABEL => false, AI4SEO_SETTING_INSTALLED_PLUGINS_PLUGIN_NAME => AI4SEO_PLUGIN_NAME, AI4SEO_SETTING_INSTALLED_PLUGINS_PLUGIN_DESCRIPTION => AI4SEO_PLUGIN_DESCRIPTION, AI4SEO_SETTING_ADD_GENERATOR_HINTS => true, AI4SEO_SETTING_META_TAGS_BLOCK_STARTING_HINT => '[{NAME}] This site is optimized with the {NAME} plugin v{VERSION} - {WEBSITE}', AI4SEO_SETTING_META_TAGS_BLOCK_ENDING_HINT => '[{NAME}] End', AI4SEO_SETTING_PREFERRED_CURRENCY => 'usd', AI4SEO_SETTING_PAYG_ENABLED => false, AI4SEO_SETTING_PAYG_STRIPE_PRICE_ID => '', // defaults to previously purchased pack. AI4SEO_SETTING_PAYG_DAILY_BUDGET => 0, // defaults to cost credits pack. AI4SEO_SETTING_PAYG_MONTHLY_BUDGET => 0, // defaults to recommended credits pack entry,. ); const AI4SEO_METADATA_FALLBACK_MAPPING = array( 'meta-title' => AI4SEO_SETTING_METADATA_FALLBACK_META_TITLE, 'meta-description' => AI4SEO_SETTING_METADATA_FALLBACK_META_DESCRIPTION, 'facebook-title' => AI4SEO_SETTING_METADATA_FALLBACK_FACEBOOK_TITLE, 'facebook-description' => AI4SEO_SETTING_METADATA_FALLBACK_FACEBOOK_DESCRIPTION, 'twitter-title' => AI4SEO_SETTING_METADATA_FALLBACK_TWITTER_TITLE, 'twitter-description' => AI4SEO_SETTING_METADATA_FALLBACK_TWITTER_DESCRIPTION, ); $ai4seo_settings = AI4SEO_DEFAULT_SETTINGS; $ai4seo_are_settings_initialized = false; $ai4seo_settings_request_cache_scope = ''; $ai4seo_settings_request_cache_by_site = array(); $ai4seo_settings_scopes_being_initialized = array(); $ai4seo_fallback_allowed_user_roles = array( 'administrator' => 'Administrator' ); $ai4seo_forbidden_allowed_user_roles = array( 'subscriber', 'customer' ); $ai4seo_can_manage_this_plugin = null; // cache variable. const AI4SEO_AVAILABLE_BULK_GENERATION_ORDER_OPTIONS = array( 'random', 'oldest', 'newest' ); const AI4SEO_AVAILABLE_BULK_GENERATION_NEW_OR_EXISTING_FILTER_OPTIONS = array( 'both', 'new', 'existing' ); const AI4SEO_AVAILABLE_QUERY_IDS_CHUNK_SIZE_OPTIONS = array( 250, 500, 1000, 2500, 5000, 10000, 20000 ); // init parameters with translation. add_action( 'init', function () { define( 'AI4SEO_BULK_GENERATION_ORDER_TRANSLATED_OPTIONS', array( 'random' => __( 'Random', 'ai-for-seo' ), 'oldest' => __( 'Oldest to newest', 'ai-for-seo' ), 'newest' => __( 'Newest to oldest', 'ai-for-seo' ), ) ); define( 'AI4SEO_BULK_GENERATION_NEW_OR_EXISTING_FILTER_TRANSLATED_OPTIONS', array( 'both' => __( 'New entries and existing entries', 'ai-for-seo' ), 'new' => __( 'New entries only', 'ai-for-seo' ), 'existing' => __( 'Existing entries only', 'ai-for-seo' ), ) ); }, 9 ); const AI4SEO_NOTIFICATION_AUTO_DISMISS_DAYS = 7; const AI4SEO_WELCOME_NOTIFICATION_INDEX = 'first-install-welcome'; const AI4SEO_ENVIRONMENTAL_VARIABLE_CACHE_TTL_SUFFIX = '__ttl_time'; // These exact legacy options are recognized by tidy-up; their presence rules out fresh enrollment. const AI4SEO_WELCOME_LEGACY_HISTORY_OPTIONS = array( '_ai4seo_current_credits_balance', '_ai4seo_last_cronjob_call', '_ai4seo_last_cronjob_call_for_ai4seo_automated_generation_cron_job', '_ai4seo_last_cronjob_call_for_ai4seo_automated_metadata_generation', '_ai4seo_licence_key_shown', '_ai4seo_num_already_filled_post_ids_by_post_type', '_ai4seo_num_existing_going_to_fill_this_post_ids_by_post_type', '_ai4seo_num_failed_to_fill_post_ids_by_post_type', '_ai4seo_num_posts_not_filled_by_post_type', '_ai4seo_num_processing_metadata_post_ids_by_post_type', '_ai4seo_performance_notice_dismissed_timestamp', '_ai4seo_plugin_activation_time', '_ai4seo_robhub_credits_balance', '_ai4seo_robhub_last_credit_balance_check', '_ai4seo_version', 'ai4seo_already_filled_attributes_attachment_post_ids', 'ai4seo_already_filled_metadata_post_ids', 'ai4seo_already_filled_post_ids', 'ai4seo_failed_to_fill_attributes_attachment_post_ids', 'ai4seo_failed_to_fill_metadata_post_ids', 'ai4seo_failed_to_fill_post_ids', 'ai4seo_is_automation_activated_for_pages', 'ai4seo_is_automation_activated_for_posts', 'ai4seo_is_automation_activated_for_products', 'ai4seo_missing_seo_data_post_ids', 'ai4seo_robhub_auth_data', ); // endregion _________________________________________________________________________________ \\ // region ENVIRONMENTAL VARIABLES ==============================================================================. const AI4SEO_ENVIRONMENTAL_VARIABLE_LAST_KNOWN_PLUGIN_VERSION = 'last_known_plugin_version'; const AI4SEO_ENVIRONMENTAL_VARIABLE_WELCOME_NOTIFICATION_STATE = 'welcome_notification_state'; const AI4SEO_ENVIRONMENTAL_VARIABLE_ATTACHMENT_BASE64_RECOVERY_STREAK = 'attachment_base64_recovery_streak'; const AI4SEO_ENVIRONMENTAL_VARIABLE_LAST_CRON_JOB_CALL = 'last_cronjob_call'; const AI4SEO_ENVIRONMENTAL_VARIABLE_LAST_SPECIFIC_CRON_JOB_CALLS = 'last_specific_cronjob_call'; const AI4SEO_ENVIRONMENTAL_VARIABLE_CRON_JOB_STATUS_LIST = 'cron_job_status_list'; const AI4SEO_ENVIRONMENTAL_VARIABLE_CRON_JOB_STATUS_LAST_UPDATE_TIMES = 'cron_job_status_last_update_times'; const AI4SEO_ENVIRONMENTAL_VARIABLE_TOS_TOC_AND_PP_ACCEPTED_TIME = 'tos_toc_and_pp_accepted_time'; const AI4SEO_ENVIRONMENTAL_VARIABLE_LAST_TOS_DETAILS_CHECKSUM = 'last_tos_details_checksum'; const AI4SEO_ENVIRONMENTAL_VARIABLE_TOS_LAST_MODAL_OPEN_TIME = 'tos_last_modal_open_time'; const AI4SEO_ENVIRONMENTAL_VARIABLE_ENHANCED_REPORTING_ACCEPTED = 'enhanced_reporting_accepted'; const AI4SEO_ENVIRONMENTAL_VARIABLE_ENHANCED_REPORTING_ACCEPTED_TIME = 'enhanced_reporting_accepted_time'; const AI4SEO_ENVIRONMENTAL_VARIABLE_ENHANCED_REPORTING_REVOKED_TIME = 'enhanced_reporting_revoke_time'; const AI4SEO_ENVIRONMENTAL_VARIABLE_LAST_WEBSITE_TOC_AND_PP_UPDATE_TIME = 'last_website_toc_and_pp_update_time'; const AI4SEO_ENVIRONMENTAL_VARIABLE_BULK_GENERATION_NEW_OR_EXISTING_FILTER_REFERENCE_TIME = 'bulk_generation_new_or_existing_filter_reference_time'; const AI4SEO_ENVIRONMENTAL_VARIABLE_HAS_PURCHASED_SOMETHING = 'has_purchased_something'; const AI4SEO_ENVIRONMENTAL_VARIABLE_LAST_SEO_AUTOPILOT_SET_UP_TIME = 'last_seo_autopilot_set_up_time'; const AI4SEO_ENVIRONMENTAL_VARIABLE_UNREAD_NOTIFICATIONS_COUNT = 'unread_notifications_count'; const AI4SEO_ENVIRONMENTAL_VARIABLE_NUM_LAST_KNOWN_POSTS_TABLE_ENTRIES = 'num_last_known_posts_table_entries'; const AI4SEO_ENVIRONMENTAL_VARIABLE_NUM_CURRENT_POSTS_TABLE_ENTRIES = 'num_current_posts_table_entries'; const AI4SEO_ENVIRONMENTAL_VARIABLE_NUM_CURRENT_POSTMETA_TABLE_ENTRIES = 'num_current_postmeta_table_entries'; const AI4SEO_ENVIRONMENTAL_VARIABLE_CURRENT_DISCOUNT = 'current_discount'; const AI4SEO_ENVIRONMENTAL_VARIABLE_ACTIVATION = 'activation_offers'; const AI4SEO_ENVIRONMENTAL_VARIABLE_NOTIFICATION_PREFERENCES = 'notification_preferences'; const AI4SEO_ENVIRONMENTAL_VARIABLE_PLUGIN_ACTIVATION_TIME = 'plugin_activation_time'; const AI4SEO_ENVIRONMENTAL_VARIABLE_LAST_PERFORMANCE_ANALYSIS_TIME = 'last_performance_analysis_time'; const AI4SEO_ENVIRONMENTAL_VARIABLE_PAYG_STATUS = 'payg_status'; const AI4SEO_ENVIRONMENTAL_VARIABLE_PAYG_FAILURE_REASON = 'payg_failure_reason'; const AI4SEO_ENVIRONMENTAL_VARIABLE_JUST_PURCHASED_SOMETHING_TIME = 'just_purchased_something_time'; const AI4SEO_ENVIRONMENTAL_VARIABLE_PAYG_LOW_CREDITS_FIRST_OCCURRENCE_TIME = 'payg_low_credits_first_occurrence_time'; const AI4SEO_ENVIRONMENTAL_VARIABLE_PAYG_LOW_CREDITS_LAST_SYNC_TIME = 'payg_low_credits_last_sync_time'; const AI4SEO_ENVIRONMENTAL_VARIABLE_POSTS_TABLE_ANALYSIS_LAST_POST_ID = 'posts_table_analysis_last_post_id'; const AI4SEO_ENVIRONMENTAL_VARIABLE_ANALYSIS_REFRESH = 'analysis_refresh'; const AI4SEO_ENVIRONMENTAL_VARIABLE_POSTS_TABLE_ANALYSIS_PROGRESS = 'posts_table_analysis_progress'; const AI4SEO_ENVIRONMENTAL_VARIABLE_POSTS_TABLE_ANALYSIS_TIMING = 'posts_table_analysis_timing'; const AI4SEO_ENVIRONMENTAL_VARIABLE_POST_STATE_STORAGE = 'post_state_storage'; const AI4SEO_ENVIRONMENTAL_VARIABLE_POST_STATE_LIST_CACHE = 'post_state_list_cache'; const AI4SEO_ENVIRONMENTAL_VARIABLE_TITLE_ORDER = 'title_order'; const AI4SEO_ENVIRONMENTAL_VARIABLE_AUTO_QUEUE_SELECTION_PROGRESS = 'auto_queue_selection_progress'; const AI4SEO_AUTO_QUEUE_SELECTION_CHUNK_SIZE = 100; const AI4SEO_AUTO_QUEUE_SELECTION_CHUNKS_PER_RUN = 4; const AI4SEO_AUTO_QUEUE_RETAINED_BLOCKER_LIMIT = 10; const AI4SEO_ENVIRONMENTAL_VARIABLE_POSTS_TABLE_ANALYSIS_STATE = 'posts_table_analysis_state'; const AI4SEO_ENVIRONMENTAL_VARIABLE_POSTS_TABLE_ANALYSIS_START_TIME = 'posts_table_analysis_start_time'; const AI4SEO_ENVIRONMENTAL_VARIABLE_POSTS_TABLE_ANALYSIS_LAST_CORE_RUN_TIME = 'posts_table_analysis_last_core_run_time'; const AI4SEO_ENVIRONMENTAL_VARIABLE_GENERATION_STATUS_SUMMARY_REBUILD_STATE = 'generation_status_summary_rebuild_state'; const AI4SEO_ENVIRONMENTAL_VARIABLE_AUTO_RETRY_FAILED_REQUIRED = 'auto_retry_failed_required'; const AI4SEO_ENVIRONMENTAL_VARIABLE_ACTIVE_METADATA_MIGRATION_V235_STATE = 'active_metadata_migration_v235_state'; const AI4SEO_ENVIRONMENTAL_VARIABLE_ACTIVE_METADATA_MIGRATION_V235_STARTED_TIME = 'active_metadata_migration_v235_started_time'; const AI4SEO_ENVIRONMENTAL_VARIABLE_ACTIVE_METADATA_MIGRATION_V235_LAST_RUN_TIME = 'active_metadata_migration_v235_last_run_time'; const AI4SEO_ENVIRONMENTAL_VARIABLE_ACTIVE_METADATA_MIGRATION_V235_PROCESSED_ENTRIES = 'active_metadata_migration_v235_processed_entries'; const AI4SEO_ENVIRONMENTAL_VARIABLE_SUPPORTED_POST_TYPES_CACHE = 'supported_post_types_cache'; const AI4SEO_ENVIRONMENTAL_VARIABLE_AVAILABLE_POST_AUTHORS_CACHE = 'available_post_authors_cache'; const AI4SEO_ENVIRONMENTAL_VARIABLE_SUPPORTED_TAXONOMY_TERMS_CACHE = 'supported_taxonomy_terms_cache'; const AI4SEO_SUPPORTED_TAXONOMY_TERMS_CACHE_OPTION_NAME = 'ai4seo_supported_taxonomy_terms_cache_v1'; const AI4SEO_ENVIRONMENTAL_VARIABLE_ATTACHMENT_ID_LOOKUP_CACHE = 'attachment_id_lookup_cache_v2'; // Retire keys altered by legacy filename sanitization. const AI4SEO_ENVIRONMENTAL_VARIABLE_NEXTGEN_PICTURE_PIDS_CACHE = 'nextgen_picture_pids_cache'; const AI4SEO_ENVIRONMENTAL_VARIABLE_NEXTGEN_IMPORTED_IMAGES_COUNT_CACHE = 'nextgen_imported_images_count_cache'; const AI4SEO_ENVIRONMENTAL_VARIABLE_MAX_POST_ID_CACHE = 'max_post_id_cache'; const AI4SEO_ENVIRONMENTAL_VARIABLE_CLAIMED_FEEDBACK_OFFER = 'claimed_feedback_offer'; const AI4SEO_DEFAULT_ENVIRONMENTAL_VARIABLES = array( AI4SEO_ENVIRONMENTAL_VARIABLE_AUTO_QUEUE_SELECTION_PROGRESS => array(), AI4SEO_ENVIRONMENTAL_VARIABLE_WELCOME_NOTIFICATION_STATE => 'unknown', AI4SEO_ENVIRONMENTAL_VARIABLE_ATTACHMENT_BASE64_RECOVERY_STREAK => 0, AI4SEO_ENVIRONMENTAL_VARIABLE_LAST_KNOWN_PLUGIN_VERSION => '0.0.0', AI4SEO_ENVIRONMENTAL_VARIABLE_LAST_CRON_JOB_CALL => 0, AI4SEO_ENVIRONMENTAL_VARIABLE_CRON_JOB_STATUS_LIST => array(), AI4SEO_ENVIRONMENTAL_VARIABLE_CRON_JOB_STATUS_LAST_UPDATE_TIMES => array(), AI4SEO_ENVIRONMENTAL_VARIABLE_TOS_LAST_MODAL_OPEN_TIME => 0, AI4SEO_ENVIRONMENTAL_VARIABLE_LAST_SPECIFIC_CRON_JOB_CALLS => array(), AI4SEO_ENVIRONMENTAL_VARIABLE_TOS_TOC_AND_PP_ACCEPTED_TIME => 0, AI4SEO_ENVIRONMENTAL_VARIABLE_ENHANCED_REPORTING_ACCEPTED => false, AI4SEO_ENVIRONMENTAL_VARIABLE_ENHANCED_REPORTING_ACCEPTED_TIME => 0, AI4SEO_ENVIRONMENTAL_VARIABLE_ENHANCED_REPORTING_REVOKED_TIME => 0, AI4SEO_ENVIRONMENTAL_VARIABLE_LAST_WEBSITE_TOC_AND_PP_UPDATE_TIME => 0, AI4SEO_ENVIRONMENTAL_VARIABLE_LAST_TOS_DETAILS_CHECKSUM => 0, AI4SEO_ENVIRONMENTAL_VARIABLE_BULK_GENERATION_NEW_OR_EXISTING_FILTER_REFERENCE_TIME => 0, AI4SEO_ENVIRONMENTAL_VARIABLE_HAS_PURCHASED_SOMETHING => false, AI4SEO_ENVIRONMENTAL_VARIABLE_LAST_SEO_AUTOPILOT_SET_UP_TIME => 0, AI4SEO_ENVIRONMENTAL_VARIABLE_UNREAD_NOTIFICATIONS_COUNT => 0, AI4SEO_ENVIRONMENTAL_VARIABLE_NUM_LAST_KNOWN_POSTS_TABLE_ENTRIES => 0, AI4SEO_ENVIRONMENTAL_VARIABLE_NUM_CURRENT_POSTS_TABLE_ENTRIES => 0, AI4SEO_ENVIRONMENTAL_VARIABLE_NUM_CURRENT_POSTMETA_TABLE_ENTRIES => 0, AI4SEO_ENVIRONMENTAL_VARIABLE_CURRENT_DISCOUNT => array(), AI4SEO_ENVIRONMENTAL_VARIABLE_ACTIVATION => array(), AI4SEO_ENVIRONMENTAL_VARIABLE_NOTIFICATION_PREFERENCES => array(), AI4SEO_ENVIRONMENTAL_VARIABLE_PLUGIN_ACTIVATION_TIME => 0, AI4SEO_ENVIRONMENTAL_VARIABLE_LAST_PERFORMANCE_ANALYSIS_TIME => 0, AI4SEO_ENVIRONMENTAL_VARIABLE_PAYG_STATUS => 'idle', AI4SEO_ENVIRONMENTAL_VARIABLE_PAYG_FAILURE_REASON => '', AI4SEO_ENVIRONMENTAL_VARIABLE_JUST_PURCHASED_SOMETHING_TIME => 0, AI4SEO_ENVIRONMENTAL_VARIABLE_PAYG_LOW_CREDITS_FIRST_OCCURRENCE_TIME => 0, AI4SEO_ENVIRONMENTAL_VARIABLE_PAYG_LOW_CREDITS_LAST_SYNC_TIME => 0, AI4SEO_ENVIRONMENTAL_VARIABLE_POSTS_TABLE_ANALYSIS_LAST_POST_ID => 0, AI4SEO_ENVIRONMENTAL_VARIABLE_ANALYSIS_REFRESH => array(), AI4SEO_ENVIRONMENTAL_VARIABLE_POSTS_TABLE_ANALYSIS_PROGRESS => array(), AI4SEO_ENVIRONMENTAL_VARIABLE_POSTS_TABLE_ANALYSIS_TIMING => array(), AI4SEO_ENVIRONMENTAL_VARIABLE_POST_STATE_STORAGE => array(), AI4SEO_ENVIRONMENTAL_VARIABLE_POST_STATE_LIST_CACHE => array(), AI4SEO_ENVIRONMENTAL_VARIABLE_TITLE_ORDER => array(), AI4SEO_ENVIRONMENTAL_VARIABLE_POSTS_TABLE_ANALYSIS_STATE => 'idle', AI4SEO_ENVIRONMENTAL_VARIABLE_POSTS_TABLE_ANALYSIS_START_TIME => 0, AI4SEO_ENVIRONMENTAL_VARIABLE_POSTS_TABLE_ANALYSIS_LAST_CORE_RUN_TIME => 0, AI4SEO_ENVIRONMENTAL_VARIABLE_GENERATION_STATUS_SUMMARY_REBUILD_STATE => 'idle', AI4SEO_ENVIRONMENTAL_VARIABLE_AUTO_RETRY_FAILED_REQUIRED => false, AI4SEO_ENVIRONMENTAL_VARIABLE_ACTIVE_METADATA_MIGRATION_V235_STATE => 'completed', AI4SEO_ENVIRONMENTAL_VARIABLE_ACTIVE_METADATA_MIGRATION_V235_STARTED_TIME => 0, AI4SEO_ENVIRONMENTAL_VARIABLE_ACTIVE_METADATA_MIGRATION_V235_LAST_RUN_TIME => 0, AI4SEO_ENVIRONMENTAL_VARIABLE_ACTIVE_METADATA_MIGRATION_V235_PROCESSED_ENTRIES => 0, AI4SEO_ENVIRONMENTAL_VARIABLE_SUPPORTED_POST_TYPES_CACHE => array(), AI4SEO_ENVIRONMENTAL_VARIABLE_AVAILABLE_POST_AUTHORS_CACHE => array(), AI4SEO_ENVIRONMENTAL_VARIABLE_SUPPORTED_TAXONOMY_TERMS_CACHE => array(), AI4SEO_ENVIRONMENTAL_VARIABLE_ATTACHMENT_ID_LOOKUP_CACHE => array(), AI4SEO_ENVIRONMENTAL_VARIABLE_NEXTGEN_PICTURE_PIDS_CACHE => array(), AI4SEO_ENVIRONMENTAL_VARIABLE_NEXTGEN_IMPORTED_IMAGES_COUNT_CACHE => 0, AI4SEO_ENVIRONMENTAL_VARIABLE_MAX_POST_ID_CACHE => 0, AI4SEO_ENVIRONMENTAL_VARIABLE_CLAIMED_FEEDBACK_OFFER => false, ); $ai4seo_environmental_variables = AI4SEO_DEFAULT_ENVIRONMENTAL_VARIABLES; $ai4seo_environmental_variables_are_loaded = false; // cache variable. // Debug cache-busting remains request-wide; update refreshes are scoped later to authorized plugin admin pages. // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only cache-busting flag; no state is changed. $ai4seo_scripts_version_number = isset( $_GET['ai4seo_debug_uncached_assets'] ) && sanitize_text_field( wp_unslash( $_GET['ai4seo_debug_uncached_assets'] ) ) ? time() : AI4SEO_PLUGIN_VERSION_NUMBER; $ai4seo_user_has_at_least_plan = array(); // cache variable to store if user has at least a specific plan. $ai4seo_user_has_active_generation_length_subscription = array(); // Request cache for expiry-aware length-stage entitlement. // used to store various details about all supported metadata fields to use it on many places throughout the plugin. add_action( 'init', function () { define( 'AI4SEO_METADATA_DETAILS', array( 'focus-keyphrase' => array( 'name' => esc_html__( 'Focus Keyphrase', 'ai-for-seo' ), 'icon' => 'flag', 'input' => 'textfield', 'hint' => __( 'Best Practice: A primary SEO keyword or keyphrase that best represents the main topic of this entry. It should be specific, relevant, and reflect the content accurately to help improve search engine rankings.

Enter your own focus keyphrase or generate one with SOOZ before generating the meta title and meta description. SOOZ uses the keyphrase as guidance when generating those fields. Changing the keyphrase alone does not update existing titles or descriptions. You can also generate all fields together.', 'ai-for-seo' ), 'api-identifier' => 'focus_keyphrase', 'flat-credits-cost' => 2, ), 'meta-title' => array( 'name' => esc_html__( 'Meta Title', 'ai-for-seo' ), 'icon' => 'globe', 'input' => 'textarea', 'hint' => __( 'Best Practice: A unique and concise title for this entry, used for the browser tab and as your preferred search-result title. Search engines such as Google may display a different title based on page content and other signals. A clear title helps users understand your content.

The recommended natural quality window is 45 to 65 characters. Your active Generation Length setting may use a different target.

The meta title is added to the title tag of your website.', 'ai-for-seo' ), 'api-identifier' => 'meta_title', 'output-tag-type' => 'title', 'output-tag-identifier' => '', 'meta-tag-regex' => '/(.*?)<\/title>/is', 'meta-tag-regex-match-index' => 1, 'flat-credits-cost' => 1, ), 'meta-description' => array( 'name' => esc_html__( 'Meta Description', 'ai-for-seo' ), 'icon' => 'globe', 'input' => 'textarea', 'hint' => __( '<strong>Best Practice:</strong> A compelling and relevant summary of your page or post, which search engines may use as the description beneath the search-result title. Google may show a different snippet from your page content depending on the search query. A clear description helps users understand your content and decide whether to visit your page.<br><br>The recommended natural quality window is <strong>130 to 160</strong> characters. Your active Generation Length setting may use a different target.<br><br>The meta description is added to the <strong>meta description tag</strong> of your website.', 'ai-for-seo' ), 'api-identifier' => 'meta_description', 'output-tag-type' => 'meta name', 'output-tag-identifier' => 'description', 'meta-tag-regex' => '/<meta\s+[^>]*name=(["\'])description\1[^>]*content=(["\'])(.*?)\2[^>]*>/i', 'meta-tag-regex-match-index' => 3, 'flat-credits-cost' => 2, ), 'keywords' => array( 'name' => esc_html__( 'Keywords', 'ai-for-seo' ), 'icon' => 'hashtag', 'input' => 'textarea', 'hint' => __( '<strong>Best Practice:</strong> Provide a concise, comma-separated list of up to <strong>10</strong> relevant keywords for compatibility with tools and content consumers that still read this field. Focus on specific phrases that describe the content and avoid duplicates or keyword stuffing.<br><br>Google Search does not use the <strong>meta keywords tag</strong> for web ranking.', 'ai-for-seo' ), 'api-identifier' => 'meta_keywords', 'output-tag-type' => 'meta name', 'output-tag-identifier' => 'keywords', 'meta-tag-regex' => '/<meta\s+[^>]*name=(["\'])keywords\1[^>]*content=(["\'])(.*?)\2[^>]*>/i', 'meta-tag-regex-match-index' => 3, 'flat-credits-cost' => 1, ), 'facebook-title' => array( 'name' => esc_html__( 'Facebook Title', 'ai-for-seo' ), 'icon' => 'square-facebook', 'input' => 'textarea', 'hint' => __( '<strong>Best Practice:</strong> This field provides the preferred headline for link previews when your content is shared on Facebook. The final preview may differ.<br><br>The recommended natural quality window is <strong>45 to 65</strong> characters. Your active Generation Length setting may use a different target.<br><br>The Facebook title is added to the <strong>og:title tag</strong> of your website.', 'ai-for-seo' ), 'api-identifier' => 'meta_facebook_title', 'output-tag-type' => 'meta property', 'output-tag-identifier' => 'og:title', 'meta-tag-regex' => '/<meta\s+[^>]*property=(["\'])og:title\1[^>]*content=(["\'])(.*?)\2[^>]*>/i', 'meta-tag-regex-match-index' => 3, 'flat-credits-cost' => 1, ), 'facebook-description' => array( 'name' => esc_html__( 'Facebook Description', 'ai-for-seo' ), 'icon' => 'square-facebook', 'input' => 'textarea', 'hint' => __( '<strong>Best Practice:</strong> This field provides the preferred summary for link previews when your content is shared on Facebook. The final preview may differ.<br><br>The recommended natural quality window is <strong>90 to 120</strong> characters. Your active Generation Length setting may use a different target.<br><br>The Facebook description is added to the <strong>og:description tag</strong> of your website.', 'ai-for-seo' ), 'api-identifier' => 'meta_facebook_description', 'output-tag-type' => 'meta property', 'output-tag-identifier' => 'og:description', 'meta-tag-regex' => '/<meta\s+[^>]*property=(["\'])og:description\1[^>]*content=(["\'])(.*?)\2[^>]*>/i', 'meta-tag-regex-match-index' => 3, 'flat-credits-cost' => 1, ), 'twitter-title' => array( 'name' => esc_html__( 'Twitter/X Title', 'ai-for-seo' ), 'icon' => 'square-twitter-x', 'input' => 'textarea', 'hint' => __( '<strong>Best Practice:</strong> This field provides the preferred headline for link previews when your content is shared on Twitter/X. The final preview may differ.<br><br>The recommended natural quality window is <strong>45 to 65</strong> characters. Your active Generation Length setting may use a different target.<br><br>The Twitter/X title is added to the <strong>twitter:title tag</strong> of your website.', 'ai-for-seo' ), 'api-identifier' => 'meta_twitter_title', 'output-tag-type' => 'meta name', 'output-tag-identifier' => 'twitter:title', 'meta-tag-regex' => '/<meta\s+[^>]*name=(["\'])twitter:title\1[^>]*content=(["\'])(.*?)\2[^>]*>/i', 'meta-tag-regex-match-index' => 3, 'flat-credits-cost' => 1, ), 'twitter-description' => array( 'name' => esc_html__( 'Twitter/X Description', 'ai-for-seo' ), 'icon' => 'square-twitter-x', 'input' => 'textarea', 'hint' => __( '<strong>Best Practice:</strong> This field provides the preferred summary for link previews when your content is shared on Twitter/X. The final preview may differ.<br><br>The recommended natural quality window is <strong>90 to 125</strong> characters. Your active Generation Length setting may use a different target.<br><br>The Twitter/X description is added to the <strong>twitter:description tag</strong> of your website.', 'ai-for-seo' ), 'api-identifier' => 'meta_twitter_description', 'output-tag-type' => 'meta name', 'output-tag-identifier' => 'twitter:description', 'meta-tag-regex' => '/<meta\s+[^>]*name=(["\'])twitter:description\1[^>]*content=(["\'])(.*?)\2[^>]*>/i', 'meta-tag-regex-match-index' => 3, 'flat-credits-cost' => 1, ), ) ); define( 'AI4SEO_ATTACHMENT_ATTRIBUTES_DETAILS', array( 'title' => array( 'name' => esc_html__( 'Title', 'ai-for-seo' ), 'icon' => 'headline', 'mime-type-restrictions' => array(), 'input-type' => 'textarea', 'hint' => __( '<strong>Best Practice:</strong> Use a descriptive title to organize and find this item in the <strong>WordPress Media Library</strong>. This field changes the WordPress media title. The image filename stays unchanged.<br><br>Whether the title appears on your website depends on your theme and plugins. SOOZ - AI for SEO can also use it for the HTML image title attribute according to your image-title injection setting.<br><br>The AI aims to generate an image title within a natural quality window of <strong>50 to 80</strong> characters.', 'ai-for-seo' ), 'api-identifier' => 'image_title', 'flat-credits-cost' => 1, ), 'alt-text' => array( 'name' => esc_html__( 'Alt Text', 'ai-for-seo' ), 'icon' => 'code', 'mime-type-restrictions' => array( 'image/jpeg', 'image/gif', 'image/png', 'image/bmp', 'image/tiff', 'image/webp', 'image/avif', 'image/x-icon', 'image/heic', ), 'input-type' => 'textarea', // phpcs:ignore -- Keep this translated registry entry aligned with the neighboring field declarations. 'hint' => __( '<strong>Best Practice:</strong> An informative and clear alt text for your image that describes its content and function. This text is used by screen readers to assist visually impaired users and is displayed in place of the image if it cannot be loaded. It also contributes to SEO by providing context to search engines.<br><br>This field saves alt text in the WordPress Media Library. Whether it appears in an image\'s HTML alt attribute depends on how your theme or page builder uses that value. Images already inserted into pages can keep their own alt text. If the saved text is missing on your website, review the Alt Text Injection setting under Troubleshooting & Experimental.', 'ai-for-seo' ), 'api-identifier' => 'image_alt_text', 'flat-credits-cost' => 2, ), 'caption' => array( 'name' => esc_html__( 'Caption', 'ai-for-seo' ), 'icon' => 'subtitle', 'mime-type-restrictions' => array(), 'input-type' => 'textarea', 'hint' => __( '<strong>Best Practice:</strong> A brief and engaging caption for your image that provides additional context or credit information. Captions are typically displayed below the image on your website and can help enhance user engagement and provide useful information.<br><br>The AI aims to generate a caption within a natural quality window of <strong>130 to 160</strong> characters.<br><br>This field saves the caption in the <strong>WordPress Media Library</strong>. Whether it appears below an image on your website depends on the image block, theme or plugin using the caption. Saving it here does not guarantee a visible caption wherever the image appears.', 'ai-for-seo' ), 'api-identifier' => 'image_caption', 'flat-credits-cost' => 1, ), 'description' => array( 'name' => esc_html__( 'Description', 'ai-for-seo' ), 'icon' => 'subtitles', 'mime-type-restrictions' => array(), 'input-type' => 'textarea', 'hint' => __( "<strong>Best Practice:</strong> A detailed and informative description of your image, which helps users understand the image's content and context. This description is particularly useful for internal reference and can aid in organizing and managing your media library.<br><br>The AI aims to generate a description within a natural quality window of <strong>180 to 240</strong> characters.<br><br>This field saves the description in the WordPress Media Library; it does not change metadata embedded in the image file. Website display depends on your theme or plugins. SOOZ can also use this text as the image title attribute when selected in the image-title injection setting.", 'ai-for-seo' ), 'api-identifier' => 'image_description', 'flat-credits-cost' => 1, ), ) ); define( 'AI4SEO_TRANSLATED_LANGUAGE_NAMES', array( 'en' => __( 'English', 'ai-for-seo' ), 'de' => __( 'German', 'ai-for-seo' ), 'fr' => __( 'French', 'ai-for-seo' ), 'es' => __( 'Spanish', 'ai-for-seo' ), 'it' => __( 'Italian', 'ai-for-seo' ), 'nl' => __( 'Dutch', 'ai-for-seo' ), 'pt' => __( 'Portuguese', 'ai-for-seo' ), 'ru' => __( 'Russian', 'ai-for-seo' ), 'zh' => __( 'Chinese', 'ai-for-seo' ), 'ja' => __( 'Japanese', 'ai-for-seo' ), 'ko' => __( 'Korean', 'ai-for-seo' ), ) ); }, 7 ); /** * Return the narrow HTML policy for API-provided notification messages. * * @return array Allowed remote-message tags mapped to their permitted attributes. */ function ai4seo_get_remote_notification_allowed_html_tags_and_attributes(): array { // Keep API-controlled markup isolated so future changes to the rich local allowlist cannot widen this policy. static $ai4seo_remote_notification_allowed_html_tags_and_attributes = array( 'a' => array( 'href' => array(), ), 'p' => array(), 'br' => array(), 'strong' => array(), 'em' => array(), 'ul' => array(), 'ol' => array(), 'li' => array(), ); return $ai4seo_remote_notification_allowed_html_tags_and_attributes; } /** * Return the HTML tags and attributes allowed in plugin output. * * @return array Allowed tags mapped to their permitted attributes. */ function ai4seo_get_allowed_html_tags_and_attributes(): array { static $ai4seo_allowed_html_tags_and_attributes = array( 'div' => array( 'data-page' => array(), 'data-offer-surface' => array(), 'data-offer-id' => array(), 'data-notification-index' => array(), 'id' => array(), 'class' => array(), 'hidden' => array(), 'onclick' => array(), 'style' => array(), 'title' => array(), 'role' => array(), 'aria-describedby' => array(), 'aria-labelledby' => array(), 'aria-hidden' => array(), 'aria-live' => array(), 'aria-label' => array(), 'data-ai4seo-non-persistent' => array(), 'data-ai4seo-read-state' => array(), 'data-ai4seo-settings-state' => array(), 'data-ai4seo-slider-input' => array(), 'data-ai4seo-preview-card' => array(), 'data-ai4seo-preview-field' => array(), 'data-ai4seo-editor-field-source-identifier' => array(), // Keep evaluation labels available after modal output passes through the plugin KSES allowlist. 'data-ai4seo-editor-evaluation-label' => array(), ), 'progress' => array( 'class' => array(), 'value' => array(), 'max' => array(), 'aria-label' => array(), ), 'details' => array( 'class' => array(), 'open' => array(), ), 'summary' => array( 'class' => array(), ), 'img' => array( 'class' => array(), 'src' => array(), 'alt' => array(), 'onclick' => array(), 'style' => array(), 'data-ai4seo-image-fallback-src' => array(), 'data-ai4seo-image-fallback-alt' => array(), ), 'meta' => array( 'name' => array(), 'content' => array(), 'property' => array(), ), 'title' => array(), 'svg' => array( 'xmlns' => true, 'xmlns:xlink' => true, 'version' => true, 'width' => true, 'height' => true, 'viewbox' => true, 'preserveaspectratio' => true, 'role' => true, 'aria-hidden' => true, 'aria-label' => true, 'class' => true, 'id' => true, 'focusable' => true, ), 'defs' => array(), 'style' => array( 'type' => array(), 'media' => true, ), 'desc' => array(), 'path' => array( 'class' => array(), 'd' => array(), 'fill-rule' => array(), 'fill' => array(), 'clip-rule' => array(), 'id' => true, 'transform' => true, 'fill-opacity' => true, 'stroke' => true, 'stroke-width' => true, 'stroke-linecap' => true, 'stroke-linejoin' => true, 'stroke-miterlimit' => true, 'stroke-opacity' => true, 'opacity' => true, ), 'g' => array( 'class' => true, 'id' => true, 'transform' => true, ), 'circle' => array( 'cx' => array(), 'cy' => array(), 'r' => array(), 'fill' => array(), 'class' => true, 'id' => true, 'stroke' => true, 'stroke-width' => true, 'opacity' => true, 'transform' => true, ), 'rect' => array( 'width' => array(), 'height' => array(), 'rx' => array(), 'ry' => array(), 'x' => array(), 'y' => array(), 'fill' => array(), 'class' => true, 'id' => true, 'stroke' => true, 'stroke-width' => true, 'opacity' => true, 'transform' => true, ), 'line' => array( 'x1' => array(), 'y1' => array(), 'x2' => array(), 'y2' => array(), 'stroke' => array(), 'stroke-width' => array(), 'class' => true, 'id' => true, 'opacity' => true, 'transform' => true, ), 'polygon' => array( 'points' => array(), 'fill' => array(), 'class' => true, 'id' => true, 'stroke' => true, 'stroke-width' => true, 'opacity' => true, 'transform' => true, ), 'polyline' => array( 'class' => true, 'id' => true, 'points' => true, 'fill' => true, 'stroke' => true, 'stroke-width' => true, 'opacity' => true, 'transform' => true, ), 'ellipse' => array( 'class' => true, 'id' => true, 'cx' => true, 'cy' => true, 'rx' => true, 'ry' => true, 'fill' => true, 'stroke' => true, 'stroke-width' => true, 'opacity' => true, 'transform' => true, ), 'use' => array( 'xlink:href' => true, 'href' => true, 'x' => true, 'y' => true, 'width' => true, 'height' => true, ), 'text' => array( 'x' => array(), 'y' => array(), 'font-size' => array(), 'font-family' => array(), 'font-weight' => array(), 'fill' => array(), ), 'button' => array( 'data-page' => array(), 'data-offer-setting' => array(), 'data-preferences' => array(), 'data-signature' => array(), 'data-notification-index' => array(), 'data-offer-id' => array(), 'data-activation-action' => array(), 'type' => array(), 'onclick' => array(), 'class' => array(), 'id' => array(), 'disabled' => array(), 'style' => array(), 'title' => array(), 'data-clipboard-text' => array(), 'data-time-left' => array(), 'data-ai4seo-generation-action' => array(), 'data-ai4seo-generation-fields' => array(), 'data-ai4seo-editor-view-mode' => array(), 'data-ai4seo-preview-edit-target' => array(), 'aria-controls' => array(), 'aria-expanded' => array(), 'aria-label' => array(), 'aria-pressed' => array(), ), 'span' => array( 'id' => array(), 'class' => array(), 'style' => array(), 'data-trigger' => array(), 'data-time-left' => array(), 'data-post-type' => array(), 'data-label' => array(), 'data-ai4seo-editor-source-message-default' => array(), 'aria-hidden' => array(), 'hidden' => array(), 'onclick' => array(), ), 'h1' => array( 'class' => array(), 'style' => array(), ), 'h2' => array( 'class' => array(), 'style' => array(), ), // Preview-card headings use the next semantic level below the modal workspace title. 'h3' => array( 'class' => array(), 'style' => array(), ), // FAQ accordions retain their semantic heading and shared card styling after sanitization. 'h4' => array( 'class' => array(), ), 'p' => array( 'id' => array(), 'class' => array(), 'style' => array(), 'data-input-id' => array(), 'data-max-length' => array(), 'hidden' => array(), 'aria-live' => array(), 'aria-atomic' => array(), ), 'b' => array(), 'u' => array(), 'a' => array( 'href' => array(), 'target' => array(), 'rel' => array(), 'title' => array(), 'class' => array(), 'onclick' => array(), 'data-time-left' => array(), 'aria-label' => array(), 'aria-current' => array(), ), 'i' => array( 'onclick' => array(), 'class' => array(), 'id' => array(), 'style' => array(), 'aria-hidden' => array(), ), 'select' => array( 'id' => array(), 'name' => array(), 'class' => array(), 'style' => array(), 'onchange' => array(), 'aria-describedby' => array(), ), 'option' => array( 'value' => array(), 'selected' => array(), 'data-ai4seo-description' => array(), ), 'br' => array(), 'hr' => array( 'class' => array(), ), 'strong' => array( 'class' => array(), ), 'input' => array( 'readonly' => array(), 'aria-readonly' => array(), 'data-ai4seo-generation-unavailable' => array(), 'type' => array(), 'id' => array(), 'class' => array(), 'style' => array(), 'value' => array(), 'name' => array(), 'placeholder' => array(), 'checked' => array(), 'onchange' => array(), 'onclick' => array(), 'disabled' => array(), 'data-target' => array(), 'data-ai4seo-non-persistent' => array(), 'data-ai4seo-slider-description' => array(), 'data-ai4seo-slider-note' => array(), 'aria-describedby' => array(), 'data-ai4seo-min-length' => array(), 'data-ai4seo-max-length' => array(), ), 'textarea' => array( 'readonly' => array(), 'aria-readonly' => array(), 'data-ai4seo-non-persistent' => array(), 'data-ai4seo-generation-unavailable' => array(), 'id' => array(), 'name' => array(), 'class' => array(), 'placeholder' => array(), 'style' => array(), 'onchange' => array(), 'onclick' => array(), 'disabled' => array(), 'aria-describedby' => array(), 'data-ai4seo-min-length' => array(), 'data-ai4seo-max-length' => array(), 'data-ai4seo-custom-instructions-limit' => array(), 'data-ai4seo-custom-instructions-label' => array(), ), 'label' => array( 'for' => array(), 'class' => array(), 'style' => array(), 'title' => array(), ), 'center' => array(), 'ol' => array( 'class' => array(), 'style' => array(), ), 'ul' => array( 'class' => array(), 'style' => array(), 'data-ai4seo-defer-status-filters' => array(), 'data-ai4seo-content-context' => array(), 'data-ai4seo-post-type' => array(), 'data-ai4seo-filter-text' => array(), 'data-ai4seo-filter-status' => array(), 'data-ai4seo-orderby' => array(), 'data-ai4seo-order' => array(), 'data-ai4seo-retry-all-failed-button-target' => array(), 'data-ai4seo-hydration-hidden-fields' => array(), ), 'li' => array( 'class' => array(), 'style' => array(), 'aria-live' => array(), ), 'em' => array(), 'form' => array( 'id' => array(), 'class' => array(), 'style' => array(), 'method' => array(), 'action' => array(), ), 'pre' => array( 'class' => array(), 'style' => array(), ), 'code' => array( 'class' => array(), 'style' => array(), ), 'small' => array( 'class' => array(), 'style' => array(), ), ); return $ai4seo_allowed_html_tags_and_attributes; } $ai4seo_cached_active_plugins_and_themes = array(); $ai4seo_active_plugins_and_themes_request_cache_scope = ''; $ai4seo_active_plugins_and_themes_request_cache_by_site = array(); $ai4seo_cached_supported_post_types = array(); $ai4seo_checked_supported_post_types = array(); $ai4seo_allowed_image_mime_types = array( 'image/jpeg', 'image/png', 'image/gif', 'image/webp', 'image/avif' ); $ai4seo_allowed_image_file_type_names = array( 'jpg', 'jpeg', 'png', 'gif', 'webp', 'avif' ); /** * Returns allowed attachment mime types. * * @return array */ function ai4seo_get_allowed_attachment_mime_types(): array { return array_values( AI4SEO_ATTACHMENT_FILE_TYPE_MIME_TYPES ); } // Define the constants for full and base language code mappings. const AI4SEO_FULL_LANGUAGE_CODE_MAPPING = array( 'zh_cn' => 'simplified chinese', 'zh_tw' => 'traditional chinese', 'pt_br' => 'brazilian portuguese', 'pt_pt' => 'european portuguese', 'fr_ca' => 'canadian french', 'en_us' => 'american english', 'en_gb' => 'british english', ); const AI4SEO_BASE_LANGUAGE_CODE_MAPPING = array( 'sq' => 'albanian', 'ar' => 'arabic', 'bg' => 'bulgarian', 'zh' => 'chinese', // General Chinese fallback. 'hr' => 'croatian', 'cs' => 'czech', 'da' => 'danish', 'nl' => 'dutch', 'en' => 'english', // General English fallback. 'et' => 'estonian', 'fi' => 'finnish', 'fr' => 'french', // General French fallback. 'de' => 'german', 'el' => 'greek', 'he' => 'hebrew', 'hi' => 'hindi', 'hu' => 'hungarian', 'is' => 'icelandic', 'id' => 'indonesian', 'it' => 'italian', 'ja' => 'japanese', 'ko' => 'korean', 'lv' => 'latvian', 'lt' => 'lithuanian', 'mk' => 'macedonian', 'mt' => 'maltese', 'no' => 'norwegian', 'pl' => 'polish', 'pt' => 'portuguese', // General Portuguese fallback. 'ro' => 'romanian', 'ru' => 'russian', 'sr' => 'serbian', 'sk' => 'slovak', 'sl' => 'slovenian', 'es' => 'spanish', // General Spanish fallback. 'sv' => 'swedish', 'th' => 'thai', 'tr' => 'turkish', 'uk' => 'ukrainian', 'vi' => 'vietnamese', ); // Allowed logged-in AJAX function names. Localized to JavaScript from this canonical list. const AI4SEO_ALLOWED_AJAX_FUNCTIONS = array( 'ai4seo_save_anything', 'ai4seo_show_metadata_editor', 'ai4seo_show_get_more_credits_modal', 'ai4seo_show_attachment_attributes_editor', 'ai4seo_show_related_attachments', 'ai4seo_check_attachment_usage_context', 'ai4seo_save_attachment_context_policy', 'ai4seo_generate_metadata', 'ai4seo_generate_attachment_attributes', 'ai4seo_reject_tos', 'ai4seo_accept_tos', 'ai4seo_show_terms_of_service', 'ai4seo_dismiss_notification', 'ai4seo_dismiss_recent_activity_error', 'ai4seo_dismiss_autopilot_item_notice', 'ai4seo_get_dashboard_html', 'ai4seo_get_analysis_issues_html', 'ai4seo_reset_plugin_data', 'ai4seo_clear_debug_message_log', 'ai4seo_stop_bulk_generation', 'ai4seo_clear_bulk_generation_queue', 'ai4seo_apply_bulk_generation_queue_action', 'ai4seo_show_bulk_custom_instructions_modal', 'ai4seo_apply_bulk_custom_instructions_action', 'ai4seo_get_content_type_status_filters', 'ai4seo_retry_all_failed_attachment_attributes', 'ai4seo_retry_all_failed_metadata', 'ai4seo_disable_payg', 'ai4seo_init_purchase', 'ai4seo_update_activation_preferences', 'ai4seo_init_subscription_pricing', 'ai4seo_import_nextgen_gallery_images', 'ai4seo_export_settings', 'ai4seo_show_import_settings_preview', 'ai4seo_import_settings', 'ai4seo_restore_default_settings', 'ai4seo_refresh_dashboard_statistics', 'ai4seo_refresh_robhub_account', 'ai4seo_request_license_change', 'ai4seo_confirm_license_change', 'ai4seo_get_license_change_status', 'ai4seo_submit_feedback', 'ai4seo_request_lost_licence_data', ); // AJAX actions that change or expose site-wide account, configuration, or operational state. const AI4SEO_ADMINISTRATIVE_AJAX_FUNCTIONS = array( 'ai4seo_get_analysis_issues_html', 'ai4seo_show_get_more_credits_modal', 'ai4seo_reject_tos', 'ai4seo_accept_tos', 'ai4seo_show_terms_of_service', 'ai4seo_dismiss_notification', 'ai4seo_reset_plugin_data', 'ai4seo_clear_debug_message_log', 'ai4seo_stop_bulk_generation', 'ai4seo_clear_bulk_generation_queue', 'ai4seo_retry_all_failed_attachment_attributes', 'ai4seo_retry_all_failed_metadata', 'ai4seo_disable_payg', 'ai4seo_init_purchase', 'ai4seo_update_activation_preferences', 'ai4seo_init_subscription_pricing', 'ai4seo_import_nextgen_gallery_images', 'ai4seo_export_settings', 'ai4seo_show_import_settings_preview', 'ai4seo_import_settings', 'ai4seo_restore_default_settings', 'ai4seo_refresh_dashboard_statistics', 'ai4seo_refresh_robhub_account', 'ai4seo_request_license_change', 'ai4seo_confirm_license_change', 'ai4seo_get_license_change_status', 'ai4seo_submit_feedback', 'ai4seo_request_lost_licence_data', ); // the robhub api communicator is used to communicate with the robhub api which handles all the AI operations. $ai4seo_robhub_api = null; // endregion _________________________________________________________________________________ \\ // endregion _________________________________________________________________________________.