init
This commit is contained in:
@@ -0,0 +1,163 @@
|
||||
<?php declare(strict_types = 1);
|
||||
|
||||
namespace MailPoet\Util\License\Features;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
use MailPoet\Config\ServicesChecker;
|
||||
use MailPoet\Settings\SettingsController;
|
||||
use MailPoet\Util\License\Features\Data\Capability;
|
||||
|
||||
class CapabilitiesManager {
|
||||
// Settings mapping
|
||||
const MSS_TIER_SETTING_KEY = 'mta.mailpoet_api_key_state.data.tier';
|
||||
const MSS_MAILPOET_LOGO_IN_EMAILS_SETTING_KEY = 'mta.mailpoet_api_key_state.data.mailpoet_logo_in_emails';
|
||||
const MSS_DETAILED_ANALYTICS_SETTING_KEY = 'mta.mailpoet_api_key_state.data.detailed_analytics';
|
||||
const MSS_AUTOMATION_STEPS_SETTING_KEY = 'mta.mailpoet_api_key_state.data.automation_steps';
|
||||
const MSS_SEGMENT_FILTERS_SETTING_KEY = 'mta.mailpoet_api_key_state.data.segment_filters';
|
||||
// Product capabilities mapping
|
||||
const MIN_TIER_LOGO_NOT_REQUIRED = 1;
|
||||
const MIN_TIER_ANALYTICS_ENABLED = 1;
|
||||
const MIN_TIER_NO_UPGRADE_PAGE = 2;
|
||||
const MIN_TIER_UNLIMITED_AUTOMATION_STEPS = 2;
|
||||
const MIN_TIER_UNLIMITED_SEGMENT_FILTERS = 2;
|
||||
|
||||
private SettingsController $settings;
|
||||
private ServicesChecker $servicesChecker;
|
||||
private Subscribers $subscribersFeature;
|
||||
private ?int $tier;
|
||||
private bool $isKeyValid = false;
|
||||
|
||||
/** @var array<string,Capability>|null */
|
||||
private ?array $capabilities = null;
|
||||
|
||||
public function __construct(
|
||||
SettingsController $settings,
|
||||
ServicesChecker $servicesChecker,
|
||||
Subscribers $subscribersFeature
|
||||
) {
|
||||
$this->settings = $settings;
|
||||
$this->servicesChecker = $servicesChecker;
|
||||
$this->subscribersFeature = $subscribersFeature;
|
||||
}
|
||||
|
||||
public function getTier(): ?int {
|
||||
$tier = $this->settings->get(self::MSS_TIER_SETTING_KEY);
|
||||
return isset($tier) ? (int)$tier : null;
|
||||
}
|
||||
|
||||
private function isMailpoetLogoInEmailsRequired(): bool {
|
||||
$mailpoetLogoInEmails = $this->settings->get(self::MSS_MAILPOET_LOGO_IN_EMAILS_SETTING_KEY);
|
||||
|
||||
if (!isset($this->tier) && !isset($mailpoetLogoInEmails)) {
|
||||
return !$this->servicesChecker->isUserActivelyPaying(); // Backward compatibility
|
||||
}
|
||||
|
||||
if (!$this->isKeyValid) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Allow for less restrictive individual capability to take precedence over tier
|
||||
if (isset($mailpoetLogoInEmails) && (bool)$mailpoetLogoInEmails === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !isset($this->tier) || $this->tier < self::MIN_TIER_LOGO_NOT_REQUIRED;
|
||||
}
|
||||
|
||||
private function isDetailedAnalyticsEnabled(): bool {
|
||||
// Preconditions
|
||||
if (!$this->subscribersFeature->hasValidPremiumKey() || $this->subscribersFeature->check() || !$this->servicesChecker->isPremiumPluginActive()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$detailedAnalytics = $this->settings->get(self::MSS_DETAILED_ANALYTICS_SETTING_KEY);
|
||||
|
||||
if (!isset($this->tier) && !isset($detailedAnalytics)) {
|
||||
return true; // Backward compatibility is true when preconditions have been met
|
||||
}
|
||||
|
||||
// Allow for less restrictive individual capability to take precedence
|
||||
if (isset($detailedAnalytics) && (bool)$detailedAnalytics === true) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return (isset($this->tier) && $this->tier >= self::MIN_TIER_ANALYTICS_ENABLED);
|
||||
}
|
||||
|
||||
private function getLimit(string $settingKey, int $minTierForUnlimited): int {
|
||||
$capabilityValue = $this->settings->get($settingKey);
|
||||
|
||||
if (!isset($this->tier) && !isset($capabilityValue)) {
|
||||
return 0; // Backward compatibility
|
||||
}
|
||||
|
||||
$limitFromTier = isset($this->tier) && $this->tier >= $minTierForUnlimited ? 0 : 1; // 0 is unlimited
|
||||
|
||||
if ($limitFromTier === 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Allow for less restrictive individual capability to take precedence
|
||||
return (isset($capabilityValue) && ((int)$capabilityValue === 0 || (int)$capabilityValue > $limitFromTier)) ? (int)$capabilityValue : $limitFromTier;
|
||||
}
|
||||
|
||||
private function getAutomationStepsLimit(): int {
|
||||
return $this->getLimit(self::MSS_AUTOMATION_STEPS_SETTING_KEY, self::MIN_TIER_UNLIMITED_AUTOMATION_STEPS);
|
||||
}
|
||||
|
||||
private function getSegmentFiltersLimit(): int {
|
||||
return $this->getLimit(self::MSS_SEGMENT_FILTERS_SETTING_KEY, self::MIN_TIER_UNLIMITED_SEGMENT_FILTERS);
|
||||
}
|
||||
|
||||
public function initCapabilities(): void {
|
||||
$this->tier = $this->getTier();
|
||||
$isPremiumKeyValid = $this->servicesChecker->isPremiumKeyValid(false);
|
||||
$this->isKeyValid = $isPremiumKeyValid || $this->servicesChecker->isMailPoetAPIKeyValid(false);
|
||||
$automationSteps = $this->getAutomationStepsLimit();
|
||||
$segmentFilters = $this->getSegmentFiltersLimit();
|
||||
$this->capabilities = [
|
||||
'mailpoetLogoInEmails' => new Capability('mailpoetLogoInEmails', Capability::TYPE_BOOLEAN, $this->isMailpoetLogoInEmailsRequired()),
|
||||
'detailedAnalytics' => new Capability('detailedAnalytics', Capability::TYPE_BOOLEAN, !$this->isDetailedAnalyticsEnabled()),
|
||||
'automationSteps' => new Capability('automationSteps', Capability::TYPE_NUMBER, $automationSteps > 0, $automationSteps),
|
||||
'segmentFilters' => new Capability('segmentFilters', Capability::TYPE_NUMBER, $segmentFilters > 0, $segmentFilters),
|
||||
];
|
||||
}
|
||||
|
||||
/** @return array<string,Capability> */
|
||||
public function getCapabilities(): array {
|
||||
if ($this->capabilities === null) {
|
||||
$this->initCapabilities();
|
||||
}
|
||||
return $this->capabilities ?? [];
|
||||
}
|
||||
|
||||
public function getCapability(string $name): ?Capability {
|
||||
return $this->getCapabilities()[$name] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if there is no valid premium key or the product tier can be upgraded
|
||||
*/
|
||||
public function showUpgradePage(): bool {
|
||||
$tier = $this->getTier();
|
||||
if (!$this->subscribersFeature->hasValidPremiumKey() || (isset($tier) && $tier < self::MIN_TIER_NO_UPGRADE_PAGE)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if there is a valid key for a tier that can be upgraded
|
||||
* @todo remove after the a/b test is finished and keep only one page
|
||||
*/
|
||||
public function showNewUpgradePage(): bool {
|
||||
$tier = $this->getTier();
|
||||
$isKeyValid = $this->subscribersFeature->hasValidPremiumKey() || $this->servicesChecker->isMailPoetAPIKeyValid(false);
|
||||
if ($isKeyValid && isset($tier) && $tier < self::MIN_TIER_NO_UPGRADE_PAGE) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php declare(strict_types = 1);
|
||||
|
||||
namespace MailPoet\Util\License\Features\Data;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
class Capability {
|
||||
public const TYPE_BOOLEAN = 'boolean';
|
||||
public const TYPE_NUMBER = 'number';
|
||||
public string $name;
|
||||
public string $type;
|
||||
public ?int $value;
|
||||
public bool $isRestricted;
|
||||
|
||||
public function __construct(
|
||||
string $name,
|
||||
string $type = self::TYPE_BOOLEAN,
|
||||
bool $isRestricted = false,
|
||||
?int $value = null
|
||||
) {
|
||||
$this->name = $name;
|
||||
$this->type = $type;
|
||||
$this->value = ($type === self::TYPE_NUMBER) ? $value : null;
|
||||
$this->isRestricted = $isRestricted;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<?php
|
||||
@@ -0,0 +1,177 @@
|
||||
<?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
|
||||
|
||||
namespace MailPoet\Util\License\Features;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
|
||||
use MailPoet\Services\Bridge;
|
||||
use MailPoet\Settings\SettingsController;
|
||||
use MailPoet\Subscribers\SubscribersRepository;
|
||||
use MailPoet\WP\Functions as WPFunctions;
|
||||
|
||||
class Subscribers {
|
||||
const SUBSCRIBERS_OLD_LIMIT = 2000;
|
||||
const SUBSCRIBERS_NEW_LIMIT = 1000;
|
||||
const NEW_LIMIT_DATE = '2019-11-00';
|
||||
const MSS_KEY_STATE = 'mta.mailpoet_api_key_state.state';
|
||||
const MSS_SUBSCRIBERS_LIMIT_SETTING_KEY = 'mta.mailpoet_api_key_state.data.site_active_subscriber_limit';
|
||||
const MSS_SUPPORT_SETTING_KEY = 'mta.mailpoet_api_key_state.data.support_tier';
|
||||
const PREMIUM_KEY_STATE = 'premium.premium_key_state.state';
|
||||
const PREMIUM_SUBSCRIBERS_LIMIT_SETTING_KEY = 'premium.premium_key_state.data.site_active_subscriber_limit';
|
||||
const MSS_EMAIL_VOLUME_LIMIT_SETTING_KEY = 'mta.mailpoet_api_key_state.data.email_volume_limit';
|
||||
const MSS_EMAILS_SENT_SETTING_KEY = 'mta.mailpoet_api_key_state.data.emails_sent';
|
||||
const PREMIUM_SUPPORT_SETTING_KEY = 'premium.premium_key_state.data.support_tier';
|
||||
const SUBSCRIBERS_COUNT_CACHE_KEY = 'mailpoet_subscribers_count';
|
||||
const SUBSCRIBERS_COUNT_CACHE_EXPIRATION_MINUTES = 60;
|
||||
const SUBSCRIBERS_COUNT_CACHE_MIN_VALUE = 2000;
|
||||
|
||||
/** @var SettingsController */
|
||||
private $settings;
|
||||
|
||||
/** @var SubscribersRepository */
|
||||
private $subscribersRepository;
|
||||
|
||||
/** @var WPFunctions */
|
||||
private $wp;
|
||||
|
||||
public function __construct(
|
||||
SettingsController $settings,
|
||||
SubscribersRepository $subscribersRepository,
|
||||
WPFunctions $wp
|
||||
) {
|
||||
$this->settings = $settings;
|
||||
$this->subscribersRepository = $subscribersRepository;
|
||||
$this->wp = $wp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the subscribers limit is reached
|
||||
* @return bool True if subscribers limit reached or restriction set, false otherwise
|
||||
*/
|
||||
public function check(): bool {
|
||||
$limit = $this->getSubscribersLimit();
|
||||
$subscribersCount = $this->getSubscribersCount();
|
||||
if ($limit && $subscribersCount > $limit) {
|
||||
return true;
|
||||
}
|
||||
// We don't have data from MSS, or they might be outdated so we need to check accessibility restrictions
|
||||
$mssStateData = $this->settings->get(Bridge::API_KEY_STATE_SETTING_NAME);
|
||||
$restriction = $mssStateData['access_restriction'] ?? '';
|
||||
return $restriction === Bridge::KEY_ACCESS_SUBSCRIBERS_LIMIT;
|
||||
}
|
||||
|
||||
public function checkEmailVolumeLimitIsReached(): bool {
|
||||
// We have data from MSS and we can determine based on the data
|
||||
$emailVolumeLimit = $this->getEmailVolumeLimit();
|
||||
$emailsSent = $this->getEmailsSent();
|
||||
if ($emailVolumeLimit && $emailsSent > $emailVolumeLimit) {
|
||||
return true;
|
||||
}
|
||||
// We don't have data from MSS, or they might be outdated so we need to check accessibility restrictions
|
||||
$mssStateData = $this->settings->get(Bridge::API_KEY_STATE_SETTING_NAME);
|
||||
$restriction = $mssStateData['access_restriction'] ?? '';
|
||||
return $restriction === Bridge::KEY_ACCESS_EMAIL_VOLUME_LIMIT;
|
||||
}
|
||||
|
||||
public function getSubscribersCount(): int {
|
||||
$count = $this->wp->getTransient(self::SUBSCRIBERS_COUNT_CACHE_KEY);
|
||||
if (is_numeric($count)) {
|
||||
return (int)$count;
|
||||
}
|
||||
$count = $this->subscribersRepository->getTotalSubscribers();
|
||||
|
||||
// cache only when number of subscribers exceeds minimum value
|
||||
if ($this->isSubscribersCountEnoughForCache($count)) {
|
||||
$this->wp->setTransient(self::SUBSCRIBERS_COUNT_CACHE_KEY, $count, self::SUBSCRIBERS_COUNT_CACHE_EXPIRATION_MINUTES * 60);
|
||||
}
|
||||
return $count;
|
||||
}
|
||||
|
||||
public function isSubscribersCountEnoughForCache(int $count = null): bool {
|
||||
if (is_null($count) && func_num_args() === 0) {
|
||||
$count = $this->getSubscribersCount();
|
||||
}
|
||||
return $count > self::SUBSCRIBERS_COUNT_CACHE_MIN_VALUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if key is valid or valid but underprivileged
|
||||
* Do not use the method to check if key is valid for sending emails or premium
|
||||
* This only means that the Bridge can authenticate the key.
|
||||
* @return bool
|
||||
*/
|
||||
public function hasValidApiKey(): bool {
|
||||
$mssState = $this->settings->get(self::MSS_KEY_STATE);
|
||||
$premiumState = $this->settings->get(self::PREMIUM_KEY_STATE);
|
||||
return $this->hasValidMssKey()
|
||||
|| $this->hasValidPremiumKey()
|
||||
|| $mssState === Bridge::KEY_VALID_UNDERPRIVILEGED
|
||||
|| $premiumState === Bridge::KEY_VALID_UNDERPRIVILEGED;
|
||||
}
|
||||
|
||||
public function getSubscribersLimit() {
|
||||
if (!$this->hasValidApiKey()) {
|
||||
return $this->getFreeSubscribersLimit();
|
||||
}
|
||||
$mssState = $this->settings->get(self::MSS_KEY_STATE);
|
||||
if (($this->hasValidMssKey() || $mssState === Bridge::KEY_VALID_UNDERPRIVILEGED) && $this->hasMssSubscribersLimit()) {
|
||||
return $this->getMssSubscribersLimit();
|
||||
}
|
||||
|
||||
$premiumState = $this->settings->get(self::PREMIUM_KEY_STATE);
|
||||
if (($this->hasValidPremiumKey() || $premiumState === Bridge::KEY_VALID_UNDERPRIVILEGED) && $this->hasPremiumSubscribersLimit()) {
|
||||
return $this->getPremiumSubscribersLimit();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getEmailVolumeLimit(): int {
|
||||
return (int)$this->settings->get(self::MSS_EMAIL_VOLUME_LIMIT_SETTING_KEY);
|
||||
}
|
||||
|
||||
public function getEmailsSent(): int {
|
||||
return (int)$this->settings->get(self::MSS_EMAILS_SENT_SETTING_KEY);
|
||||
}
|
||||
|
||||
public function hasValidMssKey() {
|
||||
$state = $this->settings->get(self::MSS_KEY_STATE);
|
||||
return $state === Bridge::KEY_VALID || $state === Bridge::KEY_EXPIRING;
|
||||
}
|
||||
|
||||
private function hasMssSubscribersLimit() {
|
||||
return !empty($this->settings->get(self::MSS_SUBSCRIBERS_LIMIT_SETTING_KEY));
|
||||
}
|
||||
|
||||
private function getMssSubscribersLimit() {
|
||||
return (int)$this->settings->get(self::MSS_SUBSCRIBERS_LIMIT_SETTING_KEY);
|
||||
}
|
||||
|
||||
public function hasMssPremiumSupport() {
|
||||
return $this->hasValidMssKey() && $this->settings->get(self::MSS_SUPPORT_SETTING_KEY) === 'premium';
|
||||
}
|
||||
|
||||
public function hasValidPremiumKey() {
|
||||
$state = $this->settings->get(self::PREMIUM_KEY_STATE);
|
||||
return $state === Bridge::KEY_VALID || $state === Bridge::KEY_EXPIRING;
|
||||
}
|
||||
|
||||
private function hasPremiumSubscribersLimit() {
|
||||
return !empty($this->settings->get(self::PREMIUM_SUBSCRIBERS_LIMIT_SETTING_KEY));
|
||||
}
|
||||
|
||||
private function getPremiumSubscribersLimit() {
|
||||
return (int)$this->settings->get(self::PREMIUM_SUBSCRIBERS_LIMIT_SETTING_KEY);
|
||||
}
|
||||
|
||||
public function hasPremiumSupport() {
|
||||
return $this->hasValidPremiumKey() && $this->settings->get(self::PREMIUM_SUPPORT_SETTING_KEY) === 'premium';
|
||||
}
|
||||
|
||||
private function getFreeSubscribersLimit() {
|
||||
$installationTime = strtotime((string)$this->settings->get('installed_at'));
|
||||
$oldUser = $installationTime < strtotime(self::NEW_LIMIT_DATE);
|
||||
return $oldUser ? self::SUBSCRIBERS_OLD_LIMIT : self::SUBSCRIBERS_NEW_LIMIT;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<?php
|
||||
Reference in New Issue
Block a user