PluginProbe ʕ •ᴥ•ʔ
MailPoet – Newsletters, Email Marketing, and Automation / 5.35.1
MailPoet – Newsletters, Email Marketing, and Automation v5.35.1
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 9 months ago SettingsChangeHandler.php 1 month ago SettingsController.php 2 days ago SettingsRepository.php 1 year ago TrackingConfig.php 6 months ago UserFlagsController.php 1 year ago UserFlagsRepository.php 3 years ago index.php 3 years ago
SettingsController.php
289 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 use MailPoet\Subscribers\TrackingConsentController;
11
12 class SettingsController {
13
14 const DEFAULT_SENDING_METHOD_GROUP = 'website';
15 const DEFAULT_SENDING_METHOD = 'PHPMail';
16 const DEFAULT_SENDING_FREQUENCY_EMAILS = 25;
17 const DEFAULT_SENDING_FREQUENCY_INTERVAL = 5; // in minutes
18 const DEFAULT_DEACTIVATE_SUBSCRIBER_AFTER_INACTIVE_DAYS = 365;
19 const DEFAULT_SENDING_STATUS_RETENTION_DAYS = '';
20 const DEFAULT_SENDING_QUEUE_BODY_RETENTION_DAYS = 30;
21 const DEFAULT_DELETE_UNCONFIRMED_SUBSCRIBERS_AFTER_DAYS = '';
22 const ALLOWED_DELETE_UNCONFIRMED_SUBSCRIBERS_AFTER_DAYS = ['', '30'];
23 const MANAGE_SUBSCRIPTION_PAGE_STYLE_CLASSIC = 'classic';
24 const MANAGE_SUBSCRIPTION_PAGE_STYLE_MODERN = 'modern';
25
26 private $loaded = false;
27
28 private $settings = [];
29
30 private $defaults = null;
31
32 /** @var SettingsRepository */
33 private $settingsRepository;
34
35 private static $instance;
36
37 public function __construct(
38 SettingsRepository $settingsRepository
39 ) {
40 $this->settingsRepository = $settingsRepository;
41 }
42
43 public function get($key, $default = null) {
44 $this->ensureLoaded();
45 $keyParts = explode('.', $key);
46 $setting = $this->settings;
47 if ($default === null) {
48 $default = $this->getDefaultValue($keyParts);
49 }
50 foreach ($keyParts as $keyPart) {
51 if (is_array($setting) && array_key_exists($keyPart, $setting)) {
52 $setting = $setting[$keyPart];
53 } else {
54 return $default;
55 }
56 }
57 if (is_array($setting) && is_array($default)) {
58 return array_replace_recursive($default, $setting);
59 }
60 if ($key === 'delete_unconfirmed_subscribers_after_days') {
61 return $this->normalizeDeleteUnconfirmedSubscribersAfterDays($setting);
62 }
63 return $setting;
64 }
65
66 public function getAllDefaults() {
67 if ($this->defaults === null) {
68 $this->defaults = [
69 'mta_group' => self::DEFAULT_SENDING_METHOD_GROUP,
70 'mta' => [
71 'method' => self::DEFAULT_SENDING_METHOD,
72 'frequency' => [
73 'emails' => self::DEFAULT_SENDING_FREQUENCY_EMAILS,
74 'interval' => self::DEFAULT_SENDING_FREQUENCY_INTERVAL,
75 ],
76 ],
77 CronTrigger::SETTING_NAME => [
78 'method' => CronTrigger::DEFAULT_METHOD,
79 ],
80 'signup_confirmation' => [
81 'enabled' => true,
82 'use_mailpoet_editor' => true,
83 ],
84 'tracking' => [
85 'level' => TrackingConfig::LEVEL_FULL,
86 'consent' => [
87 'subscriber_choice' => TrackingConsentController::CHOICE_TRACK_ALL,
88 ],
89 ],
90 'subscription' => [
91 'manage_subscription_page_style' => self::MANAGE_SUBSCRIPTION_PAGE_STYLE_MODERN,
92 'unsubscribe_survey' => [
93 'enabled' => true,
94 'allow_other_text' => false,
95 ],
96 ],
97 'analytics' => [
98 'enabled' => false,
99 ],
100 'collect_subscriber_timezones' => [
101 'enabled' => true,
102 ],
103 'display_nps_poll' => true,
104 'deactivate_subscriber_after_inactive_days' => self::DEFAULT_DEACTIVATE_SUBSCRIBER_AFTER_INACTIVE_DAYS,
105 'delete_unconfirmed_subscribers_after_days' => self::DEFAULT_DELETE_UNCONFIRMED_SUBSCRIBERS_AFTER_DAYS,
106 'sending_status_retention_days' => self::DEFAULT_SENDING_STATUS_RETENTION_DAYS,
107 'sending_queue_body_retention_days' => self::DEFAULT_SENDING_QUEUE_BODY_RETENTION_DAYS,
108 'sharing' => [
109 'default_visibility' => 'public',
110 ],
111 ];
112 }
113
114 // Always include subject and body in defaults, using translated versions when available
115 $this->defaults['signup_confirmation']['subject'] = $this->getTranslatedDefaultSubject();
116 $this->defaults['signup_confirmation']['body'] = $this->getTranslatedDefaultBody();
117
118 return $this->defaults;
119 }
120
121 /**
122 * Get translated default subject with fallback
123 *
124 * @return string
125 */
126 private function getTranslatedDefaultSubject(): string {
127 if ($this->isTranslationReady()) {
128 return __('Confirm your subscription to [site:title]', 'mailpoet');
129 }
130 return 'Confirm your subscription to [site:title]';
131 }
132
133 /**
134 * Get translated default body with fallback
135 *
136 * @return string
137 */
138 private function getTranslatedDefaultBody(): string {
139 if ($this->isTranslationReady()) {
140 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');
141 }
142 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>";
143 }
144
145 /**
146 * Check if translations are ready to be used
147 *
148 * @return bool
149 */
150 private function isTranslationReady(): bool {
151 if (!function_exists('__')) {
152 return false;
153 }
154
155 // Prefer using is_textdomain_loaded to confirm the 'mailpoet' text domain is present
156 if (function_exists('is_textdomain_loaded')) {
157 return (bool)is_textdomain_loaded('mailpoet');
158 }
159
160 // Fall back to did_action('init') if is_textdomain_loaded is unavailable
161 // Guard did_action() with function_exists to avoid fatals in non-WP contexts
162 if (function_exists('did_action')) {
163 return (bool)did_action('init');
164 }
165
166 return false;
167 }
168
169 /**
170 * Fetches the value from DB and update in cache
171 * This is required for sync settings between parallel processes e.g. cron
172 */
173 public function fetch($key, $default = null) {
174 $keys = explode('.', $key);
175 $mainKey = $keys[0];
176 $this->settings[$mainKey] = $this->fetchValue($mainKey);
177 return $this->get($key, $default);
178 }
179
180 public function getAll() {
181 $this->ensureLoaded();
182 $settings = array_replace_recursive($this->getAllDefaults(), $this->settings);
183 $settings['delete_unconfirmed_subscribers_after_days'] = $this->normalizeDeleteUnconfirmedSubscribersAfterDays($settings['delete_unconfirmed_subscribers_after_days']);
184 return $settings;
185 }
186
187 public function set($key, $value) {
188 $this->ensureLoaded();
189 $keyParts = explode('.', $key);
190 $mainKey = $keyParts[0];
191 $hadKey = array_key_exists($mainKey, $this->settings);
192 $previousValue = $hadKey ? $this->settings[$mainKey] : null;
193 $lastKey = array_pop($keyParts);
194 $setting =& $this->settings;
195 foreach ($keyParts as $keyPart) {
196 $setting =& $setting[$keyPart];
197 if (!is_array($setting)) {
198 $setting = [];
199 }
200 }
201 $setting[$lastKey] = $value;
202 if (!$hadKey || $this->settings[$mainKey] !== $previousValue) {
203 $this->settingsRepository->createOrUpdateByName($mainKey, $this->settings[$mainKey]);
204 }
205 }
206
207 public function delete($key) {
208 $setting = $this->settingsRepository->findOneByName($key);
209 if ($setting) {
210 $this->settingsRepository->remove($setting);
211 $this->settingsRepository->flush();
212 }
213 unset($this->settings[$key]);
214 }
215
216 /**
217 * Returns true if a value is stored in the database for the given key
218 *
219 * @param string $key
220 *
221 * @return bool
222 */
223 public function hasSavedValue(string $key): bool {
224 return $this->get($key, 'unset') !== 'unset';
225 }
226
227 /**
228 * Returns true if the setting value is a truthy boolean. Settings are persisted as
229 * strings, integers, or booleans depending on how they were written, so this helper
230 * normalizes the check across all three representations.
231 */
232 public function isSettingEnabled(string $key): bool {
233 $value = $this->get($key);
234 return $value === true || $value === '1' || $value === 1;
235 }
236
237 private function ensureLoaded() {
238 if ($this->loaded) {
239 return;
240 }
241
242 $this->settings = [];
243 foreach ($this->settingsRepository->findAll() as $setting) {
244 $this->settings[$setting->getName()] = $setting->getValue();
245 }
246 $this->loaded = true;
247 }
248
249 private function getDefaultValue($keys) {
250 $default = $this->getAllDefaults();
251 foreach ($keys as $key) {
252 if (array_key_exists($key, $default)) {
253 $default = $default[$key];
254 } else {
255 return null;
256 }
257 }
258
259 return $default;
260 }
261
262 private function normalizeDeleteUnconfirmedSubscribersAfterDays($value): string {
263 if (in_array($value, self::ALLOWED_DELETE_UNCONFIRMED_SUBSCRIBERS_AFTER_DAYS, true)) {
264 return $value;
265 }
266 return self::DEFAULT_DELETE_UNCONFIRMED_SUBSCRIBERS_AFTER_DAYS;
267 }
268
269 private function fetchValue($key) {
270 $setting = $this->settingsRepository->findOneByName($key);
271 return $setting ? $setting->getValue() : null;
272 }
273
274 public function resetCache() {
275 $this->settings = [];
276 $this->loaded = false;
277 }
278
279 public static function setInstance($instance) {
280 self::$instance = $instance;
281 }
282
283 /** @return SettingsController */
284 public static function getInstance() {
285 if (isset(self::$instance)) return self::$instance;
286 return ContainerWrapper::getInstance()->get(SettingsController::class);
287 }
288 }
289