PluginProbe ʕ •ᴥ•ʔ
MailPoet – Newsletters, Email Marketing, and Automation / 5.34.2
MailPoet – Newsletters, Email Marketing, and Automation v5.34.2
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 2 months ago Pages.php 9 months ago SettingsChangeHandler.php 2 weeks ago SettingsController.php 5 days ago SettingsRepository.php 1 year ago TrackingConfig.php 5 months ago UserFlagsController.php 1 year ago UserFlagsRepository.php 3 years ago index.php 3 years ago
SettingsController.php
288 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 'consent' => [
86 'track_unknown' => true,
87 ],
88 ],
89 'subscription' => [
90 'manage_subscription_page_style' => self::MANAGE_SUBSCRIPTION_PAGE_STYLE_MODERN,
91 'unsubscribe_survey' => [
92 'enabled' => true,
93 'allow_other_text' => false,
94 ],
95 ],
96 'analytics' => [
97 'enabled' => false,
98 ],
99 'collect_subscriber_timezones' => [
100 'enabled' => true,
101 ],
102 'display_nps_poll' => true,
103 'deactivate_subscriber_after_inactive_days' => self::DEFAULT_DEACTIVATE_SUBSCRIBER_AFTER_INACTIVE_DAYS,
104 'delete_unconfirmed_subscribers_after_days' => self::DEFAULT_DELETE_UNCONFIRMED_SUBSCRIBERS_AFTER_DAYS,
105 'sending_status_retention_days' => self::DEFAULT_SENDING_STATUS_RETENTION_DAYS,
106 'sending_queue_body_retention_days' => self::DEFAULT_SENDING_QUEUE_BODY_RETENTION_DAYS,
107 'sharing' => [
108 'default_visibility' => 'public',
109 ],
110 ];
111 }
112
113 // Always include subject and body in defaults, using translated versions when available
114 $this->defaults['signup_confirmation']['subject'] = $this->getTranslatedDefaultSubject();
115 $this->defaults['signup_confirmation']['body'] = $this->getTranslatedDefaultBody();
116
117 return $this->defaults;
118 }
119
120 /**
121 * Get translated default subject with fallback
122 *
123 * @return string
124 */
125 private function getTranslatedDefaultSubject(): string {
126 if ($this->isTranslationReady()) {
127 return __('Confirm your subscription to [site:title]', 'mailpoet');
128 }
129 return 'Confirm your subscription to [site:title]';
130 }
131
132 /**
133 * Get translated default body with fallback
134 *
135 * @return string
136 */
137 private function getTranslatedDefaultBody(): string {
138 if ($this->isTranslationReady()) {
139 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');
140 }
141 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>";
142 }
143
144 /**
145 * Check if translations are ready to be used
146 *
147 * @return bool
148 */
149 private function isTranslationReady(): bool {
150 if (!function_exists('__')) {
151 return false;
152 }
153
154 // Prefer using is_textdomain_loaded to confirm the 'mailpoet' text domain is present
155 if (function_exists('is_textdomain_loaded')) {
156 return (bool)is_textdomain_loaded('mailpoet');
157 }
158
159 // Fall back to did_action('init') if is_textdomain_loaded is unavailable
160 // Guard did_action() with function_exists to avoid fatals in non-WP contexts
161 if (function_exists('did_action')) {
162 return (bool)did_action('init');
163 }
164
165 return false;
166 }
167
168 /**
169 * Fetches the value from DB and update in cache
170 * This is required for sync settings between parallel processes e.g. cron
171 */
172 public function fetch($key, $default = null) {
173 $keys = explode('.', $key);
174 $mainKey = $keys[0];
175 $this->settings[$mainKey] = $this->fetchValue($mainKey);
176 return $this->get($key, $default);
177 }
178
179 public function getAll() {
180 $this->ensureLoaded();
181 $settings = array_replace_recursive($this->getAllDefaults(), $this->settings);
182 $settings['delete_unconfirmed_subscribers_after_days'] = $this->normalizeDeleteUnconfirmedSubscribersAfterDays($settings['delete_unconfirmed_subscribers_after_days']);
183 return $settings;
184 }
185
186 public function set($key, $value) {
187 $this->ensureLoaded();
188 $keyParts = explode('.', $key);
189 $mainKey = $keyParts[0];
190 $hadKey = array_key_exists($mainKey, $this->settings);
191 $previousValue = $hadKey ? $this->settings[$mainKey] : null;
192 $lastKey = array_pop($keyParts);
193 $setting =& $this->settings;
194 foreach ($keyParts as $keyPart) {
195 $setting =& $setting[$keyPart];
196 if (!is_array($setting)) {
197 $setting = [];
198 }
199 }
200 $setting[$lastKey] = $value;
201 if (!$hadKey || $this->settings[$mainKey] !== $previousValue) {
202 $this->settingsRepository->createOrUpdateByName($mainKey, $this->settings[$mainKey]);
203 }
204 }
205
206 public function delete($key) {
207 $setting = $this->settingsRepository->findOneByName($key);
208 if ($setting) {
209 $this->settingsRepository->remove($setting);
210 $this->settingsRepository->flush();
211 }
212 unset($this->settings[$key]);
213 }
214
215 /**
216 * Returns true if a value is stored in the database for the given key
217 *
218 * @param string $key
219 *
220 * @return bool
221 */
222 public function hasSavedValue(string $key): bool {
223 return $this->get($key, 'unset') !== 'unset';
224 }
225
226 /**
227 * Returns true if the setting value is a truthy boolean. Settings are persisted as
228 * strings, integers, or booleans depending on how they were written, so this helper
229 * normalizes the check across all three representations.
230 */
231 public function isSettingEnabled(string $key): bool {
232 $value = $this->get($key);
233 return $value === true || $value === '1' || $value === 1;
234 }
235
236 private function ensureLoaded() {
237 if ($this->loaded) {
238 return;
239 }
240
241 $this->settings = [];
242 foreach ($this->settingsRepository->findAll() as $setting) {
243 $this->settings[$setting->getName()] = $setting->getValue();
244 }
245 $this->loaded = true;
246 }
247
248 private function getDefaultValue($keys) {
249 $default = $this->getAllDefaults();
250 foreach ($keys as $key) {
251 if (array_key_exists($key, $default)) {
252 $default = $default[$key];
253 } else {
254 return null;
255 }
256 }
257
258 return $default;
259 }
260
261 private function normalizeDeleteUnconfirmedSubscribersAfterDays($value): string {
262 if (in_array($value, self::ALLOWED_DELETE_UNCONFIRMED_SUBSCRIBERS_AFTER_DAYS, true)) {
263 return $value;
264 }
265 return self::DEFAULT_DELETE_UNCONFIRMED_SUBSCRIBERS_AFTER_DAYS;
266 }
267
268 private function fetchValue($key) {
269 $setting = $this->settingsRepository->findOneByName($key);
270 return $setting ? $setting->getValue() : null;
271 }
272
273 public function resetCache() {
274 $this->settings = [];
275 $this->loaded = false;
276 }
277
278 public static function setInstance($instance) {
279 self::$instance = $instance;
280 }
281
282 /** @return SettingsController */
283 public static function getInstance() {
284 if (isset(self::$instance)) return self::$instance;
285 return ContainerWrapper::getInstance()->get(SettingsController::class);
286 }
287 }
288