new SettingsService(new SettingsStorage()); $currentLicense = $settingsService->getSetting('activation', 'licence'); $currentLicense = !empty($currentLicense) ? $currentLicense : LicenceConstants::DEVELOPER; // Return the appropriate license class return self::$licenseClassMap[$currentLicense] ?? 'AmeliaBooking\Infrastructure\Licence\Lite\Licence'; } /** * Checks if the current license grants access to the specified license level. * * For example, if the user's license is 'Lite' and the function is called with 'Basic', it will return true. * If the user's license is 'Lite' and the function is called with 'Lite', it will return false. * * Usage examples: * - Licence::hasLicenseAccess(LicenseConstants::BASIC) * - Licence::hasLicenseAccess(LicenseConstants::PRO) * * @param string $requiredLicense The license level required for access (use class constants) * @return bool True if current license has access, false otherwise */ public static function hasLicenseAccess($requiredLicense) { $currentLicense = self::$licence; if (AMELIA_DEV) { /** @var SettingsService $settingsService */ $settingsService = new SettingsService(new SettingsStorage()); $currentLicense = $settingsService->getSetting('activation', 'licence'); $currentLicense = !empty($currentLicense) ? $currentLicense : 'Lite'; } // If either license is not in our hierarchy, fall back to exact match if (!isset(self::$licenseHierarchy[$currentLicense]) || !isset(self::$licenseHierarchy[$requiredLicense])) { return $currentLicense === $requiredLicense; } // Check if current license level is greater than or equal to required level return self::$licenseHierarchy[$currentLicense] >= self::$licenseHierarchy[$requiredLicense]; } /** * Checks if the current license has access to a specific feature. * * @param string $featureCode The feature code (e.g., 'googleCalendar', 'packages', etc.) * @return bool True if current license has access to the feature, false otherwise */ public static function hasFeatureAccess($featureCode) { // If feature doesn't have a license requirement, assume it's available to all if (!isset(self::$featureLicenseRequirements[$featureCode])) { return true; } $requiredLicense = self::$featureLicenseRequirements[$featureCode]; return self::hasLicenseAccess($requiredLicense); } /** * Features that are not stored in featuresIntegrations settings * These are license-gated features without an on/off toggle */ private static $nonToggleableFeatures = ['locations']; /** * Checks if a feature should be shown in the UI (menu, pages, etc.) * * Logic for regular features: * 1. If feature is enabled in settings → show it * 2. If feature is disabled in settings: * - If user has license access → hide it (don't show disabled features they can enable) * - If user doesn't have license access → check hideUnavailableFeatures setting * * Logic for non-toggleable features (locations): * - These don't have an on/off toggle in featuresIntegrations * - If user has license access → always show * - If user doesn't have license access → check hideUnavailableFeatures setting * * Note: For Lite license, unavailable features are always shown (hideUnavailableFeatures is ignored) * * @param string $featureCode The feature code (e.g., 'packages', 'customFields', 'locations') * @return bool True if feature should be shown, false if it should be hidden */ public static function shouldShowFeature($featureCode) { $settingsService = new SettingsService(new SettingsStorage()); // Special handling for non-toggleable features (locations) // These are not stored in featuresIntegrations and only have license restrictions if (in_array($featureCode, self::$nonToggleableFeatures)) { // If user has license access, always show it if (self::hasFeatureAccess($featureCode)) { return true; } // User doesn't have license access // Check hideUnavailableFeatures setting (Lite always shows unavailable features) $hideUnavailableFeatures = self::getHideUnavailableFeatures($settingsService); return !$hideUnavailableFeatures; } // Standard feature handling (stored in featuresIntegrations) // Check if feature is enabled in settings $isFeatureEnabled = $settingsService->isFeatureEnabled($featureCode); // If feature is enabled in settings, always show it if ($isFeatureEnabled) { return true; } // Feature is disabled in settings // Check if user has license access to this feature $hasLicenseAccess = self::hasFeatureAccess($featureCode); // If user has license access, don't show disabled features if ($hasLicenseAccess) { return false; } // User doesn't have license access to this disabled feature // Check hideUnavailableFeatures setting (Lite always shows unavailable features) $hideUnavailableFeatures = self::getHideUnavailableFeatures($settingsService); // If hideUnavailableFeatures is false/null, show locked features // If hideUnavailableFeatures is true, hide locked features return !$hideUnavailableFeatures; } /** * Get the effective hideUnavailableFeatures value * For Lite license, always return false (always show unavailable features) * For other licenses, use the setting value * * @param SettingsService $settingsService * @return bool */ private static function getHideUnavailableFeatures($settingsService) { // For Lite license, always show unavailable features if (self::getLicence() === LicenceConstants::LITE) { return false; } return (bool) $settingsService->getSetting('activation', 'hideUnavailableFeatures'); } /** * Checks if a feature is locked (visible but not accessible due to license restrictions) * * A feature is locked when: * - It should be shown (shouldShowFeature returns true) * - BUT the user doesn't have license access to it * * For non-toggleable features (locations): * - Locked = visible but no license access * * For regular features: * - Locked = visible but not enabled (which implies no license access since * users with license access who have disabled features won't see them) * * @param string $featureCode The feature code (e.g., 'packages', 'customFields', 'locations') * @return bool True if feature is locked, false otherwise */ public static function isFeatureLocked($featureCode) { // Feature must be visible to be considered "locked" if (!self::shouldShowFeature($featureCode)) { return false; } // Feature is locked if user doesn't have license access return !self::hasFeatureAccess($featureCode); } /** * Filters feature settings array to disable features not available in current license. * Preserves the original structure but sets 'enabled' to false for unavailable features. * * @param array $featuresIntegrations Array of feature settings * @return array Filtered feature settings with license checks applied */ public static function filterFeaturesByLicense($featuresIntegrations) { if (!is_array($featuresIntegrations)) { return $featuresIntegrations; } foreach ($featuresIntegrations as $featureCode => $featureSettings) { // Check if this feature has license restrictions and if user has access if (!self::hasFeatureAccess($featureCode)) { // Feature is not available in current license - force disable it if (is_array($featureSettings) && isset($featureSettings['enabled'])) { $featuresIntegrations[$featureCode]['enabled'] = false; } } } return $featuresIntegrations; } /** * Checks if a feature is both enabled in settings AND available in current license. * This is a convenience method for use in SettingsStorage and other places. * * @param string $featureCode The feature code (e.g., 'googleCalendar', 'packages') * @param array|null $featureSettings The feature settings array from database * @return bool True if feature is enabled and license has access, false otherwise */ public static function isFeatureEnabledWithLicense($featureCode, $featureSettings) { // Check if feature settings exist and are valid if (!is_array($featureSettings) || !isset($featureSettings['enabled'])) { return false; } // Feature must be enabled in settings AND license must have access to it return $featureSettings['enabled'] && self::hasFeatureAccess($featureCode); } /** * Override getCommands to delegate to the appropriate license class in development mode * * @param \AmeliaBooking\Infrastructure\Common\Container $c * @return array */ public static function getCommands($c) { // In production, use normal inheritance (class extends were changed by build scripts) if (!AMELIA_DEV) { return parent::getCommands($c); } // In development, dynamically load the appropriate license class $licenseClass = self::getLicenseClass(); return $licenseClass::getCommands($c); } /** * Override setRoutes to delegate to the appropriate license class in development mode * * @param \Slim\App $app * @param \AmeliaBooking\Infrastructure\Common\Container $container * @return void */ public static function setRoutes($app, $container) { // In production, use normal inheritance (class extends were changed by build scripts) if (!AMELIA_DEV) { parent::setRoutes($app, $container); return; } // In development, dynamically load the appropriate license class $licenseClass = self::getLicenseClass(); $licenseClass::setRoutes($app, $container); } /** * Override getPaddleUrl to delegate to the appropriate license class in development mode * * @return string */ public static function getPaddleUrl() { // In production, use normal inheritance (class extends were changed by build scripts) if (!AMELIA_DEV) { return parent::getPaddleUrl(); } // In development, dynamically load the appropriate license class $licenseClass = self::getLicenseClass(); return $licenseClass::getPaddleUrl(); } /** * Get the current license name * This is kept for backward compatibility but should use hasLicenseAccess() instead * * @return string */ public static function getLicence() { if (AMELIA_DEV) { $settingsService = new SettingsService(new SettingsStorage()); $currentLicense = $settingsService->getSetting('activation', 'licence'); return !empty($currentLicense) ? $currentLicense : LicenceConstants::DEVELOPER; } return self::$licence; } /** * Check if current license is premium * This is kept for backward compatibility but should use hasLicenseAccess() instead * * @return bool */ public static function isPremium() { if (AMELIA_DEV) { $settingsService = new SettingsService(new SettingsStorage()); $currentLicense = $settingsService->getSetting('activation', 'licence'); $currentLicense = !empty($currentLicense) ? $currentLicense : LicenceConstants::DEVELOPER; // Lite is the only non-premium license return $currentLicense !== LicenceConstants::LITE; } return self::$premium; } }