PluginProbe ʕ •ᴥ•ʔ
MailPoet – Newsletters, Email Marketing, and Automation / 5.33.1
MailPoet – Newsletters, Email Marketing, and Automation v5.33.1
5.37.0 5.36.1 5.36.0 5.35.1 5.35.0 5.34.3 5.34.2 5.34.1 5.34.0 5.33.1 5.33.0 5.32.0 5.31.0 5.30.0 5.29.0 5.28.1 5.28.0 5.27.0 5.26.0 5.26.1 5.25.0 5.24.0 4.43.0 4.43.1 4.44.0 4.44.1 4.45.0 4.46.0 4.47.0 4.48.0 4.48.1 4.48.2 4.49.0 4.49.1 4.5.0 4.5.1 4.5.2 4.50.0 4.50.1 4.51.0 4.51.1 4.51.2 4.52.0 4.53.0 4.54.0 4.55.0 4.56.0 4.57.0 4.58.0 4.58.1 4.58.2 4.6.0 4.6.1 4.6.2 4.7.0 4.7.1 4.8.0 4.8.1 4.9.0 5.0.0 5.0.1 5.0.2 5.1.0 5.1.1 5.10.0 5.10.1 5.11.0 5.12.0 5.12.1 5.12.10 5.12.11 5.12.12 5.12.13 5.12.2 5.12.3 5.12.4 5.12.5 5.12.6 5.12.7 5.12.8 5.12.9 5.13.0 5.13.1 5.13.2 5.14.0 5.14.1 5.14.2 5.14.3 5.15.0 5.15.1 5.16.0 5.16.1 5.16.2 5.16.3 5.16.4 5.17.0 5.17.1 5.17.2 5.17.3 5.17.4 5.17.5 5.17.6 5.18.0 5.19.0 5.2.0 5.2.1 5.2.2 5.2.3 5.20.0 5.21.0 5.21.1 5.21.2 5.21.3 5.22.0 5.22.1 5.22.2 5.22.3 5.22.4 5.23.0 5.23.1 5.23.2 5.3.0 5.3.1 5.3.2 5.3.3 5.3.4 5.3.5 5.3.6 5.3.7 5.4.0 5.4.1 5.4.2 5.5.0 5.5.1 5.5.2 5.6.0 5.6.1 5.6.2 5.6.3 5.6.4 5.7.0 5.7.1 5.8.0 5.8.1 5.9.0 3.0.0-beta.15 3.7.1 3.0.0-beta.16 3.7.2 3.0.0-beta.17 3.7.3 3.0.0-beta.18 3.7.4 3.0.0-beta.19 3.7.5 3.0.0-beta.2 3.7.6 3.0.0-beta.20 3.7.8 3.0.0-beta.21 3.70.0 3.0.0-beta.22 3.71.0 3.0.0-beta.23 3.71.1 3.0.0-beta.23.1 3.71.2 3.0.0-beta.23.2 3.71.3 3.0.0-beta.24 3.72.0 3.0.0-beta.25 3.73.0 3.0.0-beta.26 3.73.1 3.0.0-beta.27 3.73.2 3.0.0-beta.28 3.74.0 3.0.0-beta.29 3.74.1 3.0.0-beta.3 3.74.2 3.0.0-beta.30 3.74.3 3.0.0-beta.31 3.75.0 3.0.0-beta.32 3.75.1 3.0.0-beta.33 3.76.0 3.0.0-beta.33.1 3.77.0 3.0.0-beta.34.0.0 3.77.1 3.0.0-beta.36.0.0 3.78.0 3.0.0-beta.36.0.1 3.79.0 3.0.0-beta.36.2.0 3.8 3.0.0-beta.36.3.0 3.8.1 3.0.0-beta.36.3.1 3.8.2 3.0.0-beta.37.0.0 3.8.3 3.0.0-beta.4 3.8.4 3.0.0-beta.5 3.8.5 3.0.0-beta.6 3.8.6 3.0.0-beta.7 3.80.0 3.0.0-beta.7.1 3.81.0 3.0.0-beta.8 3.82.0 3.0.0-beta.9 3.83.0 3.0.0-rc.1.0.0 3.84.0 3.0.0-rc.1.0.1 3.84.1 3.0.0-rc.1.0.2 3.85.0 3.0.0-rc.1.0.3 3.85.1 3.0.0-rc.1.0.4 3.86.0 3.0.0-rc.2.0.0 3.87.0 3.0.0-rc.2.0.1 3.87.1 3.0.0-rc.2.0.2 3.87.2 3.0.0-rc.2.0.3 3.88.0 3.0.1 3.88.1 3.0.2 3.88.2 3.0.3 3.89.0 3.0.4 3.89.1 3.0.5 3.89.2 3.0.6 3.89.3 3.0.7 3.89.4 3.0.8 3.9.0 3.0.9 3.9.1 3.1.0 3.90.0 3.10 3.90.1 3.10.1 3.90.2 3.100.0 3.91.0 3.100.1 3.91.1 3.100.2 3.92.0 3.101.0 3.92.1 3.101.1 3.93.0 3.102.0 3.93.1 3.102.1 3.94.0 3.103.0 3.95.0 3.103.1 3.95.1 3.11.0 3.96.0 3.11.1 3.96.1 3.11.2 3.97.0 3.11.3 3.98.0 3.11.4 3.98.1 3.11.5 3.99.0 3.12.0 3.99.1 3.12.1 4.0.0 3.13.0 4.0.1 3.14.0 4.1.0 3.14.1 4.1.1 3.15.0 4.10.0 3.16.0 4.11.0 3.16.1 4.11.1 3.16.2 4.12.0 3.16.3 4.12.1 3.17.0 4.12.2 3.17.1 4.13.0 3.17.2 4.14.0 3.18.0 4.15.0 3.18.1 4.16.0 3.18.2 4.17.0 3.19.0 4.17.1 3.19.1 4.18.0 3.19.2 4.18.1 3.19.3 4.19.0 3.2.0 4.2.0 3.2.1 4.20.0 3.2.2 4.20.1 3.2.3 4.20.2 3.2.4 4.21.0 3.2.5 4.22.0 3.20.0 4.22.1 3.21.0 4.22.2 3.21.1 4.23.0 3.22.0 4.24.0 3.23.0 4.25.0 3.23.1 4.26.0 3.23.2 4.26.1 3.24.0 4.27.0 3.25.0 4.28.0 3.25.1 4.29.0 3.26.0 4.3.0 3.26.1 4.3.1 3.27.0 4.30.0 3.28.0 4.31.0 3.29.0 4.31.1 3.3.0 4.32.0 3.3.1 4.33.0 3.3.2 4.34.0 3.3.3 4.35.0 3.3.4 4.35.1 3.3.5 4.36.0 3.3.6 4.37.0 3.30.0 4.38.0 3.31.0 4.39.0 3.31.1 4.4.0 3.32.0 4.40.0 3.32.1 4.41.0 3.32.2 4.41.1 3.33.0 4.41.2 3.34.0 4.41.3 3.34.1 4.42.0 3.34.2 4.42.1 3.34.3 3.34.4 3.35.0 3.35.1 3.35.3 3.35.4 3.36.0 3.37.0 3.37.1 3.37.2 3.37.3 3.38.0 3.38.1 3.39.0 3.39.1 3.39.2 3.4.0 3.4.1 3.4.2 3.4.3 3.4.4 3.40.0 3.40.1 3.41.0 3.41.1 3.41.2 3.42.0 3.42.1 3.42.2 3.42.3 3.43.0 3.43.1 3.44.0 3.45.0 3.45.1 3.46.0 3.46.1 3.46.10 3.46.11 3.46.12 3.46.13 3.46.14 3.46.2 3.46.3 3.46.4 3.46.5 3.46.6 3.46.7 3.46.8 3.46.9 3.47.0 3.47.1 3.47.10 3.47.11 3.47.2 3.47.3 3.47.5 3.47.6 3.47.7 3.47.9 3.48.0 3.48.1 3.49.0 3.49.1 3.5.0 3.5.1 3.50.0 3.51.0 3.51.1 3.51.2 3.52.0 3.53.0 3.54.0 3.54.1 3.54.2 3.54.3 3.55.0 3.55.1 3.56.0 3.56.1 3.56.2 3.57.0 3.57.1 3.58.0 3.59.0 3.59.1 3.59.2 3.6.0 3.6.1 3.6.2 3.6.3 3.6.4 3.6.5 3.6.6 3.6.7 3.60.0 3.60.1 3.60.10 3.60.11 3.60.12 3.60.2 3.60.3 3.60.4 3.60.6 3.60.7 3.60.8 3.60.9 3.61.0 3.62.0 3.62.1 3.63.0 3.64.0 3.64.1 3.64.2 3.64.3 3.65.0 trunk 3.65.1 3.0.0 3.66.0 3.0.0-beta.1 3.67.0 3.0.0-beta.10 3.67.1 3.0.0-beta.11 3.68.0 3.0.0-beta.12 3.69.0 3.0.0-beta.13 3.69.1 3.0.0-beta.14 3.7.0
mailpoet / lib / Settings / SettingsController.php
mailpoet / lib / Settings Last commit date
Charsets.php 3 years ago Hosts.php 3 months ago Pages.php 10 months ago SettingsChangeHandler.php 1 month ago SettingsController.php 3 months ago SettingsRepository.php 2 years ago TrackingConfig.php 7 months ago UserFlagsController.php 1 year ago UserFlagsRepository.php 3 years ago index.php 3 years ago
SettingsController.php
285 lines
1 <?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
2
3 namespace MailPoet\Settings;
4
5 if (!defined('ABSPATH')) exit;
6
7
8 use MailPoet\Cron\CronTrigger;
9 use MailPoet\DI\ContainerWrapper;
10
11 class SettingsController {
12
13 const DEFAULT_SENDING_METHOD_GROUP = 'website';
14 const DEFAULT_SENDING_METHOD = 'PHPMail';
15 const DEFAULT_SENDING_FREQUENCY_EMAILS = 25;
16 const DEFAULT_SENDING_FREQUENCY_INTERVAL = 5; // in minutes
17 const DEFAULT_DEACTIVATE_SUBSCRIBER_AFTER_INACTIVE_DAYS = 365;
18 const DEFAULT_SENDING_STATUS_RETENTION_DAYS = '';
19 const DEFAULT_SENDING_QUEUE_BODY_RETENTION_DAYS = 30;
20 const DEFAULT_DELETE_UNCONFIRMED_SUBSCRIBERS_AFTER_DAYS = '';
21 const ALLOWED_DELETE_UNCONFIRMED_SUBSCRIBERS_AFTER_DAYS = ['', '30'];
22 const MANAGE_SUBSCRIPTION_PAGE_STYLE_CLASSIC = 'classic';
23 const MANAGE_SUBSCRIPTION_PAGE_STYLE_MODERN = 'modern';
24
25 private $loaded = false;
26
27 private $settings = [];
28
29 private $defaults = null;
30
31 /** @var SettingsRepository */
32 private $settingsRepository;
33
34 private static $instance;
35
36 public function __construct(
37 SettingsRepository $settingsRepository
38 ) {
39 $this->settingsRepository = $settingsRepository;
40 }
41
42 public function get($key, $default = null) {
43 $this->ensureLoaded();
44 $keyParts = explode('.', $key);
45 $setting = $this->settings;
46 if ($default === null) {
47 $default = $this->getDefaultValue($keyParts);
48 }
49 foreach ($keyParts as $keyPart) {
50 if (is_array($setting) && array_key_exists($keyPart, $setting)) {
51 $setting = $setting[$keyPart];
52 } else {
53 return $default;
54 }
55 }
56 if (is_array($setting) && is_array($default)) {
57 return array_replace_recursive($default, $setting);
58 }
59 if ($key === 'delete_unconfirmed_subscribers_after_days') {
60 return $this->normalizeDeleteUnconfirmedSubscribersAfterDays($setting);
61 }
62 return $setting;
63 }
64
65 public function getAllDefaults() {
66 if ($this->defaults === null) {
67 $this->defaults = [
68 'mta_group' => self::DEFAULT_SENDING_METHOD_GROUP,
69 'mta' => [
70 'method' => self::DEFAULT_SENDING_METHOD,
71 'frequency' => [
72 'emails' => self::DEFAULT_SENDING_FREQUENCY_EMAILS,
73 'interval' => self::DEFAULT_SENDING_FREQUENCY_INTERVAL,
74 ],
75 ],
76 CronTrigger::SETTING_NAME => [
77 'method' => CronTrigger::DEFAULT_METHOD,
78 ],
79 'signup_confirmation' => [
80 'enabled' => true,
81 'use_mailpoet_editor' => true,
82 ],
83 'tracking' => [
84 'level' => TrackingConfig::LEVEL_FULL,
85 ],
86 'subscription' => [
87 'manage_subscription_page_style' => self::MANAGE_SUBSCRIPTION_PAGE_STYLE_MODERN,
88 'unsubscribe_survey' => [
89 'enabled' => true,
90 'allow_other_text' => false,
91 ],
92 ],
93 'analytics' => [
94 'enabled' => false,
95 ],
96 'collect_subscriber_timezones' => [
97 'enabled' => true,
98 ],
99 'display_nps_poll' => true,
100 'deactivate_subscriber_after_inactive_days' => self::DEFAULT_DEACTIVATE_SUBSCRIBER_AFTER_INACTIVE_DAYS,
101 'delete_unconfirmed_subscribers_after_days' => self::DEFAULT_DELETE_UNCONFIRMED_SUBSCRIBERS_AFTER_DAYS,
102 'sending_status_retention_days' => self::DEFAULT_SENDING_STATUS_RETENTION_DAYS,
103 'sending_queue_body_retention_days' => self::DEFAULT_SENDING_QUEUE_BODY_RETENTION_DAYS,
104 'sharing' => [
105 'default_visibility' => 'public',
106 ],
107 ];
108 }
109
110 // Always include subject and body in defaults, using translated versions when available
111 $this->defaults['signup_confirmation']['subject'] = $this->getTranslatedDefaultSubject();
112 $this->defaults['signup_confirmation']['body'] = $this->getTranslatedDefaultBody();
113
114 return $this->defaults;
115 }
116
117 /**
118 * Get translated default subject with fallback
119 *
120 * @return string
121 */
122 private function getTranslatedDefaultSubject(): string {
123 if ($this->isTranslationReady()) {
124 return __('Confirm your subscription to [site:title]', 'mailpoet');
125 }
126 return 'Confirm your subscription to [site:title]';
127 }
128
129 /**
130 * Get translated default body with fallback
131 *
132 * @return string
133 */
134 private function getTranslatedDefaultBody(): string {
135 if ($this->isTranslationReady()) {
136 return __("Hello [subscriber:firstname | default:there],\n\nYou've received this message because you subscribed to [site:title]. Please confirm your subscription to receive emails from us:\n\n[activation_link]Click here to confirm your subscription.[/activation_link] \n\nIf you received this email by mistake, simply delete it. You won't receive any more emails from us unless you confirm your subscription using the link above.\n\nThank you,\n\n<a target=\"_blank\" href=\"[site:homepage_url]\">[site:title]</a>", 'mailpoet');
137 }
138 return "Hello [subscriber:firstname | default:there],\n\nYou've received this message because you subscribed to [site:title]. Please confirm your subscription to receive emails from us:\n\n[activation_link]Click here to confirm your subscription.[/activation_link] \n\nIf you received this email by mistake, simply delete it. You won't receive any more emails from us unless you confirm your subscription using the link above.\n\nThank you,\n\n<a target=\"_blank\" href=\"[site:homepage_url]\">[site:title]</a>";
139 }
140
141 /**
142 * Check if translations are ready to be used
143 *
144 * @return bool
145 */
146 private function isTranslationReady(): bool {
147 if (!function_exists('__')) {
148 return false;
149 }
150
151 // Prefer using is_textdomain_loaded to confirm the 'mailpoet' text domain is present
152 if (function_exists('is_textdomain_loaded')) {
153 return (bool)is_textdomain_loaded('mailpoet');
154 }
155
156 // Fall back to did_action('init') if is_textdomain_loaded is unavailable
157 // Guard did_action() with function_exists to avoid fatals in non-WP contexts
158 if (function_exists('did_action')) {
159 return (bool)did_action('init');
160 }
161
162 return false;
163 }
164
165 /**
166 * Fetches the value from DB and update in cache
167 * This is required for sync settings between parallel processes e.g. cron
168 */
169 public function fetch($key, $default = null) {
170 $keys = explode('.', $key);
171 $mainKey = $keys[0];
172 $this->settings[$mainKey] = $this->fetchValue($mainKey);
173 return $this->get($key, $default);
174 }
175
176 public function getAll() {
177 $this->ensureLoaded();
178 $settings = array_replace_recursive($this->getAllDefaults(), $this->settings);
179 $settings['delete_unconfirmed_subscribers_after_days'] = $this->normalizeDeleteUnconfirmedSubscribersAfterDays($settings['delete_unconfirmed_subscribers_after_days']);
180 return $settings;
181 }
182
183 public function set($key, $value) {
184 $this->ensureLoaded();
185 $keyParts = explode('.', $key);
186 $mainKey = $keyParts[0];
187 $hadKey = array_key_exists($mainKey, $this->settings);
188 $previousValue = $hadKey ? $this->settings[$mainKey] : null;
189 $lastKey = array_pop($keyParts);
190 $setting =& $this->settings;
191 foreach ($keyParts as $keyPart) {
192 $setting =& $setting[$keyPart];
193 if (!is_array($setting)) {
194 $setting = [];
195 }
196 }
197 $setting[$lastKey] = $value;
198 if (!$hadKey || $this->settings[$mainKey] !== $previousValue) {
199 $this->settingsRepository->createOrUpdateByName($mainKey, $this->settings[$mainKey]);
200 }
201 }
202
203 public function delete($key) {
204 $setting = $this->settingsRepository->findOneByName($key);
205 if ($setting) {
206 $this->settingsRepository->remove($setting);
207 $this->settingsRepository->flush();
208 }
209 unset($this->settings[$key]);
210 }
211
212 /**
213 * Returns true if a value is stored in the database for the given key
214 *
215 * @param string $key
216 *
217 * @return bool
218 */
219 public function hasSavedValue(string $key): bool {
220 return $this->get($key, 'unset') !== 'unset';
221 }
222
223 /**
224 * Returns true if the setting value is a truthy boolean. Settings are persisted as
225 * strings, integers, or booleans depending on how they were written, so this helper
226 * normalizes the check across all three representations.
227 */
228 public function isSettingEnabled(string $key): bool {
229 $value = $this->get($key);
230 return $value === true || $value === '1' || $value === 1;
231 }
232
233 private function ensureLoaded() {
234 if ($this->loaded) {
235 return;
236 }
237
238 $this->settings = [];
239 foreach ($this->settingsRepository->findAll() as $setting) {
240 $this->settings[$setting->getName()] = $setting->getValue();
241 }
242 $this->loaded = true;
243 }
244
245 private function getDefaultValue($keys) {
246 $default = $this->getAllDefaults();
247 foreach ($keys as $key) {
248 if (array_key_exists($key, $default)) {
249 $default = $default[$key];
250 } else {
251 return null;
252 }
253 }
254
255 return $default;
256 }
257
258 private function normalizeDeleteUnconfirmedSubscribersAfterDays($value): string {
259 if (in_array($value, self::ALLOWED_DELETE_UNCONFIRMED_SUBSCRIBERS_AFTER_DAYS, true)) {
260 return $value;
261 }
262 return self::DEFAULT_DELETE_UNCONFIRMED_SUBSCRIBERS_AFTER_DAYS;
263 }
264
265 private function fetchValue($key) {
266 $setting = $this->settingsRepository->findOneByName($key);
267 return $setting ? $setting->getValue() : null;
268 }
269
270 public function resetCache() {
271 $this->settings = [];
272 $this->loaded = false;
273 }
274
275 public static function setInstance($instance) {
276 self::$instance = $instance;
277 }
278
279 /** @return SettingsController */
280 public static function getInstance() {
281 if (isset(self::$instance)) return self::$instance;
282 return ContainerWrapper::getInstance()->get(SettingsController::class);
283 }
284 }
285