$enabled IDs the migration enabled */ do_action('easy_invoice_pro_addon_migration_completed', $enabled); } /** * Dispatch to per-addon detector. * * Two categories of addon: * * FEATURE-SPECIFIC DETECTION — the feature had its own user-facing * enable/disable setting OR produced its own data signal. We can * tell precisely whether this merchant was using it. * • recurring_invoices — meta on actual recurring invoices * • partial_payments — own enable option * • client_portal — settings option present * • custom_templates — saved templates option non-empty * * PRO-INSTALL DETECTION — the feature had NO user toggle in old Pro * (it always loaded, every site got it). To preserve "old users * keep working", we enable for ANY install that we can prove * previously ran Pro. Fresh installs leave them off. * • pdf_toolkit * • bulk_operations * • item_library * • additional_tax * • email_enhancements * • privacy_tools */ public static function isFeatureInUse(string $addon_id): bool { switch ($addon_id) { // Feature-specific signals. case 'recurring_invoices': return self::detectRecurring(); case 'partial_payments': return self::detectPartialPayments(); case 'client_portal': return self::detectClientPortal(); case 'custom_templates': return self::detectCustomTemplates(); case 'secure_links': return self::detectSecureLinks(); // Always-on Pro features — enable for any existing Pro install. case 'pdf_toolkit': case 'bulk_operations': case 'item_library': case 'additional_tax': case 'email_enhancements': case 'privacy_tools': // Reports was an always-on Pro feature gated only by // easy_invoice_has_pro(); existing Pro sites were using it. case 'reports': return self::isExistingProInstall(); default: return false; } } /** * "Did this site ever run Pro before the addon-ization migration?" * * Verified signals (every entry is an option I have read the Pro * source for and confirmed gets written during normal Pro lifecycle): * * easy_invoice_pro_license_details — written by License::activate() * in includes/Updater/License.php line 97 the first time the * admin saves a license key. * easy_invoice_pro_license_key — written by the same call, * line 101. * * Both are absent on a fresh install that has never activated Pro. * We deliberately stick to license-flow signals because they're the * least ambiguous "Pro was here" indicator — other options can be * written by tools that don't imply real Pro use. */ public static function isExistingProInstall(): bool { if (get_option('easy_invoice_pro_license_details', null) !== null) return true; if (get_option('easy_invoice_pro_license_key', null) !== null) return true; /** * Override hook for staging clones / unusual deploys where the * heuristic mis-classifies. Return true to force "existing", * false to force "fresh", null (default) to use the heuristic. * * @param bool|null $resolved */ $override = apply_filters('easy_invoice_pro_addon_migration_is_existing', null); return is_bool($override) ? $override : false; } // ── Verified per-feature detectors ────────────────────────────────── private static function detectRecurring(): bool { global $wpdb; $found = $wpdb->get_var($wpdb->prepare( "SELECT 1 FROM {$wpdb->postmeta} WHERE meta_key = %s AND meta_value = %s LIMIT 1", '_easy_invoice_recurring_enabled', '1' )); return !empty($found); } private static function detectPartialPayments(): bool { // The PartialPayments extension stores '1' / '0' as string. We // count any truthy value (including the literal '1' or a future // 'true') as "in use". $value = get_option('easy_invoice_pro_partial_payments_enable', null); if ($value === null) return false; return !empty($value) && $value !== '0'; } private static function detectClientPortal(): bool { return get_option('easy_invoice_pro_client_portal_settings', null) !== null; } private static function detectCustomTemplates(): bool { $templates = get_option('easy_invoice_pro_templates', null); return is_array($templates) && !empty($templates); } /** * Secure Links: the legacy Pro setting was a Yes/No checkbox in the * Pro Settings page (see templates/admin/settings.php line 237 — * checkbox writing easy_invoice_pro_enable_secure_links). Treat any * truthy value as "in use" so we don't 404 secure URLs already * mailed out. Sites that never enabled it stay disabled. */ private static function detectSecureLinks(): bool { $value = get_option('easy_invoice_pro_enable_secure_links', null); if ($value === null) return false; return $value === 'yes' || $value === '1' || $value === 1 || $value === true; } // detectItemLibrary() and detectEmailEnhancements() used to live here // but were never called from isFeatureInUse(). Those addons are // treated as "always-on Pro" features and fall through to // isExistingProInstall() — the safer migration default (any site // that ran Pro gets the feature, even if they hadn't actively used // it yet). The per-feature detectors were removed to eliminate the // dead-code path; reintroduce them only if a future product // decision wants the stricter "must have used it" rule. }