Newsletter - Version 7.3.2

Version Description

  • Fixed the remote ip retrieval and clean up
  • Fixed header link to status page
  • Fixed database error with too long IPs
  • Fixed the subscription of cancelled addresses
  • Fixed sender and name customization
Download this release

Release Info

Developer satollo
Plugin Icon 128x128 Newsletter
Version 7.3.2
Comparing to
See all releases

Code changes from version 7.3.1 to 7.3.2

Files changed (6) hide show
  1. includes/module.php +18 -4
  2. main/main.php +1 -1
  3. plugin.php +1420 -1419
  4. readme.txt +359 -351
  5. subscription/subscription.php +26 -19
  6. tnp-header.php +0 -1
includes/module.php CHANGED
@@ -310,6 +310,14 @@ class TNP_Subscription {
310
  public function __construct() {
311
  $this->data = new TNP_Subscription_Data();
312
  }
 
 
 
 
 
 
 
 
313
 
314
  }
315
 
@@ -1826,6 +1834,7 @@ class NewsletterModule {
1826
  }
1827
 
1828
  function process_ip($ip) {
 
1829
  $option = Newsletter::instance()->options['ip'];
1830
  if (empty($option)) {
1831
  return $ip;
@@ -2317,7 +2326,14 @@ class NewsletterModule {
2317
  if (empty($ip)) {
2318
  return '';
2319
  }
2320
- return preg_replace('/[^0-9a-fA-F:., ]/', '', trim($ip));
 
 
 
 
 
 
 
2321
  }
2322
 
2323
  static function get_remote_ip() {
@@ -2325,9 +2341,7 @@ class NewsletterModule {
2325
  if (!empty($_SERVER['HTTP_X_REAL_IP'])) {
2326
  $ip = $_SERVER['HTTP_X_REAL_IP'];
2327
  } elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
2328
- // Sometimes this is a list of IPs representing the chain of proxies
2329
- $ip = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR'], 1);
2330
- $ip = $ip[0];
2331
  } elseif (isset($_SERVER['REMOTE_ADDR'])) {
2332
  $ip = $_SERVER['REMOTE_ADDR'];
2333
  }
310
  public function __construct() {
311
  $this->data = new TNP_Subscription_Data();
312
  }
313
+
314
+ public function is_single_optin() {
315
+ return $this->optin == 'single';
316
+ }
317
+
318
+ public function is_double_optin() {
319
+ return $this->optin == 'double';
320
+ }
321
 
322
  }
323
 
1834
  }
1835
 
1836
  function process_ip($ip) {
1837
+
1838
  $option = Newsletter::instance()->options['ip'];
1839
  if (empty($option)) {
1840
  return $ip;
2326
  if (empty($ip)) {
2327
  return '';
2328
  }
2329
+ $ip = preg_replace('/[^0-9a-fA-F:., ]/', '', trim($ip));
2330
+ if (strlen($ip) > 50) $ip = substr($ip, 0, 50);
2331
+
2332
+ // When more than one IP is present due to firewalls, proxies, and so on. The first one should be the origin.
2333
+ if (strpos($ip, ',') !== false) {
2334
+ list($ip, $tail) = explode(',', $ip, 2);
2335
+ }
2336
+ return $ip;
2337
  }
2338
 
2339
  static function get_remote_ip() {
2341
  if (!empty($_SERVER['HTTP_X_REAL_IP'])) {
2342
  $ip = $_SERVER['HTTP_X_REAL_IP'];
2343
  } elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
2344
+ $ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
 
 
2345
  } elseif (isset($_SERVER['REMOTE_ADDR'])) {
2346
  $ip = $_SERVER['REMOTE_ADDR'];
2347
  }
main/main.php CHANGED
@@ -246,7 +246,7 @@ if (!empty($return_path)) {
246
  <td>
247
  <?php $controls->text('scheduler_max', 5); ?> (min. 10)
248
  <p class="description">
249
- <a href="?page=newsletter_main_status#tnp-speed">See the collected statistics</a>
250
  </td>
251
  </tr>
252
  </table>
246
  <td>
247
  <?php $controls->text('scheduler_max', 5); ?> (min. 10)
248
  <p class="description">
249
+ <a href="?page=newsletter_system_status#tnp-speed">See the collected statistics</a>
250
  </td>
251
  </tr>
252
  </table>
plugin.php CHANGED
@@ -1,1419 +1,1420 @@
1
- <?php
2
-
3
- /*
4
- Plugin Name: Newsletter
5
- Plugin URI: https://www.thenewsletterplugin.com/plugins/newsletter
6
- Description: Newsletter is a cool plugin to create your own subscriber list, to send newsletters, to build your business. <strong>Before update give a look to <a href="https://www.thenewsletterplugin.com/category/release">this page</a> to know what's changed.</strong>
7
- Version: 7.3.1
8
- Author: Stefano Lissa & The Newsletter Team
9
- Author URI: https://www.thenewsletterplugin.com
10
- Disclaimer: Use at your own risk. No warranty expressed or implied is provided.
11
- Text Domain: newsletter
12
- License: GPLv2 or later
13
- Requires at least: 4.6
14
- Requires PHP: 5.6
15
-
16
- Copyright 2009-2021 The Newsletter Team (email: info@thenewsletterplugin.com, web: https://www.thenewsletterplugin.com)
17
-
18
- Newsletter is free software: you can redistribute it and/or modify
19
- it under the terms of the GNU General Public License as published by
20
- the Free Software Foundation, either version 2 of the License, or
21
- any later version.
22
-
23
- Newsletter is distributed in the hope that it will be useful,
24
- but WITHOUT ANY WARRANTY; without even the implied warranty of
25
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26
- GNU General Public License for more details.
27
-
28
- You should have received a copy of the GNU General Public License
29
- along with Newsletter. If not, see https://www.gnu.org/licenses/gpl-2.0.html.
30
-
31
- */
32
-
33
- if (version_compare(phpversion(), '5.6', '<')) {
34
- add_action('admin_notices', function () {
35
- echo '<div class="notice notice-error"><p>PHP version 5.6 or greater is required for Newsletter. Ask your provider to upgrade. <a href="https://www.php.net/supported-versions.php" target="_blank">Read more on PHP versions</a></p></div>';
36
- });
37
- return;
38
- }
39
-
40
- define('NEWSLETTER_VERSION', '7.3.1');
41
-
42
- global $newsletter, $wpdb;
43
-
44
- // For acceptance tests, DO NOT CHANGE
45
- if (!defined('NEWSLETTER_DEBUG'))
46
- define('NEWSLETTER_DEBUG', false);
47
-
48
- if (!defined('NEWSLETTER_EXTENSION_UPDATE'))
49
- define('NEWSLETTER_EXTENSION_UPDATE', true);
50
-
51
- if (!defined('NEWSLETTER_EMAILS_TABLE'))
52
- define('NEWSLETTER_EMAILS_TABLE', $wpdb->prefix . 'newsletter_emails');
53
-
54
- if (!defined('NEWSLETTER_USERS_TABLE'))
55
- define('NEWSLETTER_USERS_TABLE', $wpdb->prefix . 'newsletter');
56
-
57
- if (!defined('NEWSLETTER_STATS_TABLE'))
58
- define('NEWSLETTER_STATS_TABLE', $wpdb->prefix . 'newsletter_stats');
59
-
60
- if (!defined('NEWSLETTER_SENT_TABLE'))
61
- define('NEWSLETTER_SENT_TABLE', $wpdb->prefix . 'newsletter_sent');
62
-
63
- define('NEWSLETTER_SLUG', 'newsletter');
64
-
65
- define('NEWSLETTER_DIR', __DIR__);
66
- define('NEWSLETTER_INCLUDES_DIR', __DIR__ . '/includes');
67
-
68
- // Deperacted since plugin can change the plugins_url()
69
- define('NEWSLETTER_URL', WP_PLUGIN_URL . '/newsletter');
70
-
71
- if (!defined('NEWSLETTER_LIST_MAX'))
72
- define('NEWSLETTER_LIST_MAX', 40);
73
-
74
- if (!defined('NEWSLETTER_PROFILE_MAX'))
75
- define('NEWSLETTER_PROFILE_MAX', 20);
76
-
77
- if (!defined('NEWSLETTER_FORMS_MAX'))
78
- define('NEWSLETTER_FORMS_MAX', 10);
79
-
80
- if (!defined('NEWSLETTER_HEADER'))
81
- define('NEWSLETTER_HEADER', true);
82
-
83
- require_once NEWSLETTER_INCLUDES_DIR . '/module.php';
84
- require_once NEWSLETTER_INCLUDES_DIR . '/TNP.php';
85
- require_once NEWSLETTER_INCLUDES_DIR . '/cron.php';
86
-
87
- class Newsletter extends NewsletterModule {
88
-
89
- // Limits to respect to avoid memory, time or provider limits
90
- var $time_start;
91
- var $time_limit = 0;
92
- var $max_emails = null;
93
-
94
- var $mailer = null;
95
-
96
- var $action = '';
97
-
98
- /** @var Newsletter */
99
- static $instance;
100
-
101
- const STATUS_NOT_CONFIRMED = 'S';
102
- const STATUS_CONFIRMED = 'C';
103
-
104
- /**
105
- * @return Newsletter
106
- */
107
- static function instance() {
108
- if (self::$instance == null) {
109
- self::$instance = new Newsletter();
110
- }
111
- return self::$instance;
112
- }
113
-
114
- function __construct() {
115
-
116
- // Grab it before a plugin decides to remove it.
117
- if (isset($_GET['na'])) {
118
- $this->action = $_GET['na'];
119
- }
120
- if (isset($_POST['na'])) {
121
- $this->action = $_POST['na'];
122
- }
123
-
124
- $this->time_start = time();
125
-
126
- parent::__construct('main', '1.6.7', null, ['info', 'smtp']);
127
-
128
- add_action('plugins_loaded', [$this, 'hook_plugins_loaded']);
129
- add_action('init', [$this, 'hook_init'], 1);
130
- add_action('wp_loaded', [$this, 'hook_wp_loaded'], 1);
131
-
132
- add_action('newsletter', [$this, 'hook_newsletter'], 1);
133
-
134
- register_activation_hook(__FILE__, [$this, 'hook_activate']);
135
- register_deactivation_hook(__FILE__, [$this, 'hook_deactivate']);
136
-
137
- add_action('admin_init', [$this, 'hook_admin_init']);
138
-
139
- if (is_admin()) {
140
- add_action('admin_head', [$this, 'hook_admin_head']);
141
-
142
- // Protection against strange schedule removal on some installations
143
- if (!wp_next_scheduled('newsletter') && (!defined('WP_INSTALLING') || !WP_INSTALLING)) {
144
- wp_schedule_event(time() + 30, 'newsletter', 'newsletter');
145
- }
146
-
147
- add_action('admin_menu', [$this, 'add_extensions_menu'], 90);
148
-
149
- add_filter('display_post_states', [$this, 'add_notice_to_chosen_profile_page_hook'], 10, 2);
150
-
151
- if ($this->is_admin_page()) {
152
- // Remove the emoji replacer to save to database the original emoji characters (see even woocommerce for the same problem)
153
- remove_action('admin_print_scripts', 'print_emoji_detection_script');
154
- add_action('admin_enqueue_scripts', [$this, 'hook_admin_enqueue_scripts']);
155
- }
156
-
157
- }
158
- }
159
-
160
- function hook_plugins_loaded() {
161
- // Used to load dependant modules
162
- do_action('newsletter_loaded', NEWSLETTER_VERSION);
163
-
164
- if (function_exists('load_plugin_textdomain')) {
165
- load_plugin_textdomain('newsletter', false, plugin_basename(__DIR__) . '/languages');
166
- }
167
- }
168
-
169
- function hook_init() {
170
- global $wpdb;
171
-
172
- if (isset($this->options['debug']) && $this->options['debug'] == 1) {
173
- ini_set('log_errors', 1);
174
- ini_set('error_log', WP_CONTENT_DIR . '/logs/newsletter/php-' . date('Y-m') . '-' . get_option('newsletter_logger_secret') . '.txt');
175
- }
176
-
177
- add_shortcode('newsletter_replace', [$this, 'shortcode_newsletter_replace']);
178
-
179
- add_filter('site_transient_update_plugins', [$this, 'hook_site_transient_update_plugins']);
180
-
181
- if (is_admin()) {
182
- if (!class_exists('NewsletterExtensions')) {
183
-
184
- add_filter('plugin_row_meta', function ($plugin_meta, $plugin_file) {
185
-
186
- static $slugs = array();
187
- if (empty($slugs)) {
188
- $addons = $this->getTnpExtensions();
189
- if ($addons) {
190
- foreach ($addons as $addon) {
191
- $slugs[] = $addon->wp_slug;
192
- }
193
- }
194
- }
195
- if (array_search($plugin_file, $slugs) !== false) {
196
-
197
- $plugin_meta[] = '<a href="admin.php?page=newsletter_main_extensions" style="font-weight: bold">Newsletter Addons Manager required</a>';
198
- }
199
- return $plugin_meta;
200
- }, 10, 2);
201
- }
202
-
203
- add_action('in_admin_header', array($this, 'hook_in_admin_header'), 1000);
204
-
205
- if ($this->is_admin_page()) {
206
-
207
- $dismissed = get_option('newsletter_dismissed', []);
208
-
209
- if (isset($_GET['dismiss'])) {
210
- $dismissed[$_GET['dismiss']] = 1;
211
- update_option('newsletter_dismissed', $dismissed);
212
- wp_redirect($_SERVER['HTTP_REFERER']);
213
- exit();
214
- }
215
- }
216
- } else {
217
- add_action('wp_enqueue_scripts', [$this, 'hook_wp_enqueue_scripts']);
218
- }
219
-
220
- do_action('newsletter_init');
221
- }
222
-
223
- function hook_wp_loaded() {
224
- if (empty($this->action)) {
225
- return;
226
- }
227
-
228
- if ($this->action == 'test') {
229
- // This response is tested, do not change it!
230
- echo 'ok';
231
- die();
232
- }
233
-
234
- if ($this->action === 'nul') {
235
- $this->dienow('This link is not active on newsletter preview', 'You can send a test message to test subscriber to have the real working link.');
236
- }
237
-
238
- $user = $this->get_user_from_request();
239
- $email = $this->get_email_from_request();
240
- do_action('newsletter_action', $this->action, $user, $email);
241
- }
242
-
243
- function hook_activate() {
244
- // Ok, why? When the plugin is not active WordPress may remove the scheduled "newsletter" action because
245
- // the every-five-minutes schedule named "newsletter" is not present.
246
- // Since the activation does not forces an upgrade, that schedule must be reactivated here. It is activated on
247
- // the upgrade method as well for the user which upgrade the plugin without deactivte it (many).
248
- if (!wp_next_scheduled('newsletter')) {
249
- wp_schedule_event(time() + 30, 'newsletter', 'newsletter');
250
- }
251
-
252
- $install_time = get_option('newsletter_install_time');
253
- if (!$install_time) {
254
- update_option('newsletter_install_time', time(), false);
255
- }
256
-
257
- touch(NEWSLETTER_LOG_DIR . '/index.html');
258
-
259
- Newsletter::instance()->upgrade();
260
- NewsletterUsers::instance()->upgrade();
261
- NewsletterEmails::instance()->upgrade();
262
- NewsletterSubscription::instance()->upgrade();
263
- NewsletterStatistics::instance()->upgrade();
264
- NewsletterProfile::instance()->upgrade();
265
- }
266
-
267
- function first_install() {
268
- parent::first_install();
269
- update_option('newsletter_show_welcome', '1', false);
270
- }
271
-
272
- function upgrade() {
273
- global $wpdb, $charset_collate;
274
-
275
- require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
276
-
277
- parent::upgrade();
278
-
279
- $sql = "CREATE TABLE `" . $wpdb->prefix . "newsletter_emails` (
280
- `id` int(11) NOT NULL AUTO_INCREMENT,
281
- `language` varchar(10) NOT NULL DEFAULT '',
282
- `subject` varchar(255) NOT NULL DEFAULT '',
283
- `message` longtext,
284
- `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
285
- `status` enum('new','sending','sent','paused','error') NOT NULL DEFAULT 'new',
286
- `total` int(11) NOT NULL DEFAULT '0',
287
- `last_id` int(11) NOT NULL DEFAULT '0',
288
- `sent` int(11) NOT NULL DEFAULT '0',
289
- `track` int(11) NOT NULL DEFAULT '1',
290
- `list` int(11) NOT NULL DEFAULT '0',
291
- `type` varchar(50) NOT NULL DEFAULT '',
292
- `query` longtext,
293
- `editor` tinyint(4) NOT NULL DEFAULT '0',
294
- `sex` varchar(20) NOT NULL DEFAULT '',
295
- `theme` varchar(50) NOT NULL DEFAULT '',
296
- `message_text` longtext,
297
- `preferences` longtext,
298
- `send_on` int(11) NOT NULL DEFAULT '0',
299
- `token` varchar(10) NOT NULL DEFAULT '',
300
- `options` longtext,
301
- `private` tinyint(1) NOT NULL DEFAULT '0',
302
- `click_count` int(10) unsigned NOT NULL DEFAULT '0',
303
- `version` varchar(10) NOT NULL DEFAULT '',
304
- `open_count` int(10) unsigned NOT NULL DEFAULT '0',
305
- `unsub_count` int(10) unsigned NOT NULL DEFAULT '0',
306
- `error_count` int(10) unsigned NOT NULL DEFAULT '0',
307
- `stats_time` int(10) unsigned NOT NULL DEFAULT '0',
308
- `updated` int(10) unsigned NOT NULL DEFAULT '0',
309
- PRIMARY KEY (`id`)
310
- ) $charset_collate;";
311
-
312
- dbDelta($sql);
313
-
314
- // WP does not manage composite primary key when it tries to upgrade a table...
315
- $suppress_errors = $wpdb->suppress_errors(true);
316
-
317
- dbDelta("CREATE TABLE " . $wpdb->prefix . "newsletter_sent (
318
- email_id int(10) unsigned NOT NULL DEFAULT '0',
319
- user_id int(10) unsigned NOT NULL DEFAULT '0',
320
- status tinyint(1) unsigned NOT NULL DEFAULT '0',
321
- open tinyint(1) unsigned NOT NULL DEFAULT '0',
322
- time int(10) unsigned NOT NULL DEFAULT '0',
323
- error varchar(255) NOT NULL DEFAULT '',
324
- ip varchar(100) NOT NULL DEFAULT '',
325
- PRIMARY KEY (email_id,user_id),
326
- KEY user_id (user_id),
327
- KEY email_id (email_id)
328
- ) $charset_collate;");
329
- $wpdb->suppress_errors($suppress_errors);
330
-
331
- // Some setting check to avoid the common support request for mis-configurations
332
- $options = $this->get_options();
333
-
334
- if (empty($options['scheduler_max']) || !is_numeric($options['scheduler_max'])) {
335
- $options['scheduler_max'] = 100;
336
- $this->save_options($options);
337
- }
338
-
339
- wp_clear_scheduled_hook('newsletter');
340
- wp_schedule_event(time() + 30, 'newsletter', 'newsletter');
341
-
342
- if (!empty($this->options['editor'])) {
343
- if (empty($this->options['roles'])) {
344
- $this->options['roles'] = array('editor');
345
- unset($this->options['editor']);
346
- }
347
- $this->save_options($this->options);
348
- }
349
-
350
- // Clear the addons and license caches
351
- delete_transient('newsletter_license_data');
352
- delete_transient("tnp_extensions_json");
353
-
354
- touch(NEWSLETTER_LOG_DIR . '/index.html');
355
-
356
- return true;
357
- }
358
-
359
- function is_allowed() {
360
- if (current_user_can('administrator')) {
361
- return true;
362
- }
363
- if (!empty($this->options['roles'])) {
364
- foreach ($this->options['roles'] as $role) {
365
- if (current_user_can($role)) {
366
- return true;
367
- }
368
- }
369
- }
370
- return false;
371
- }
372
-
373
- function admin_menu() {
374
- if (!$this->is_allowed()) {
375
- return;
376
- }
377
-
378
- add_menu_page('Newsletter', 'Newsletter', 'exist', 'newsletter_main_index', '', plugins_url('newsletter') . '/admin/images/menu-icon.png', '30.333');
379
-
380
- $this->add_menu_page('index', __('Dashboard', 'newsletter'));
381
- $this->add_admin_page('info', __('Company info', 'newsletter'));
382
-
383
- if (current_user_can('administrator')) {
384
- $this->add_menu_page('welcome', __('Welcome', 'newsletter'));
385
- $this->add_menu_page('main', __('Settings', 'newsletter'));
386
-
387
- // Pages not on menu
388
- $this->add_admin_page('smtp', 'SMTP');
389
- $this->add_admin_page('diagnostic', __('Diagnostic', 'newsletter'));
390
- $this->add_admin_page('test', __('Test', 'newsletter'));
391
- }
392
- }
393
-
394
- function add_extensions_menu() {
395
- if (!class_exists('NewsletterExtensions')) {
396
- $this->add_menu_page('extensions', '<span style="color:#27AE60; font-weight: bold;">' . __('Addons', 'newsletter') . '</span>');
397
- }
398
- }
399
-
400
- function hook_in_admin_header() {
401
- if (!$this->is_admin_page()) {
402
- add_action('admin_notices', array($this, 'hook_admin_notices'));
403
- return;
404
- }
405
- remove_all_actions('admin_notices');
406
- remove_all_actions('all_admin_notices');
407
- add_action('admin_notices', array($this, 'hook_admin_notices'));
408
- }
409
-
410
- function hook_admin_notices() {
411
- // Check of Newsletter dedicated page
412
- if (!empty($this->options['page'])) {
413
- if (get_post_status($this->options['page']) !== 'publish') {
414
- echo '<div class="notice notice-error"><p>The Newsletter dedicated page is not published. <a href="', site_url('/wp-admin/post.php') . '?post=', $this->options['page'], '&action=edit"><strong>Edit the page</strong></a> or <a href="admin.php?page=newsletter_main_main"><strong>review the main settings</strong></a>.</p></div>';
415
- }
416
- }
417
-
418
- if (isset($this->options['debug']) && $this->options['debug'] == 1) {
419
- echo '<div class="notice notice-warning"><p>The Newsletter plugin is in <strong>debug mode</strong>. When done change it on Newsletter <a href="admin.php?page=newsletter_main_main"><strong>main settings</strong></a>. Do not keep the debug mode active on production sites.</p></div>';
420
- }
421
- }
422
-
423
- function hook_wp_enqueue_scripts() {
424
- if (empty($this->options['css_disabled']) && apply_filters('newsletter_enqueue_style', true)) {
425
- wp_enqueue_style('newsletter', plugins_url('newsletter') . '/style.css', [], NEWSLETTER_VERSION);
426
- if (!empty($this->options['css'])) {
427
- wp_add_inline_style('newsletter', $this->options['css']);
428
- }
429
- } else {
430
- if (!empty($this->options['css'])) {
431
- add_action('wp_head', function () {
432
- echo '<style>', $this->options['css'], '</style>';
433
- });
434
- }
435
- }
436
- }
437
-
438
- function hook_admin_enqueue_scripts() {
439
-
440
- $newsletter_url = plugins_url('newsletter');
441
- wp_enqueue_script('jquery-ui-tabs');
442
- wp_enqueue_script('jquery-ui-tooltip');
443
- wp_enqueue_script('jquery-ui-draggable');
444
- wp_enqueue_media();
445
-
446
- wp_enqueue_script('tnp-admin', $newsletter_url . '/admin/admin.js', ['jquery'], NEWSLETTER_VERSION);
447
-
448
- wp_enqueue_style('tnp-select2', $newsletter_url . '/vendor/select2/css/select2.min.css', [], NEWSLETTER_VERSION);
449
- wp_enqueue_script('tnp-select2', $newsletter_url . '/vendor/select2/js/select2.min.js', ['jquery'], NEWSLETTER_VERSION);
450
-
451
- wp_enqueue_style('tnp-modal', $newsletter_url . '/admin/modal.css', [], NEWSLETTER_VERSION);
452
- wp_enqueue_script('tnp-modal', $newsletter_url . '/admin/modal.js', ['jquery'], NEWSLETTER_VERSION, true);
453
-
454
- wp_enqueue_style('tnp-toast', $newsletter_url . '/admin/toast.css', [], NEWSLETTER_VERSION);
455
- wp_enqueue_script('tnp-toast', $newsletter_url . '/admin/toast.js', ['jquery'], NEWSLETTER_VERSION);
456
-
457
- wp_enqueue_style('tnp-admin-font', 'https://use.typekit.net/jlj2wjy.css');
458
- wp_enqueue_style('tnp-admin-fontawesome', $newsletter_url . '/vendor/fa/css/all.min.css', [], NEWSLETTER_VERSION);
459
- wp_enqueue_style('tnp-admin-jquery-ui', $newsletter_url . '/vendor/jquery-ui/jquery-ui.min.css', [], NEWSLETTER_VERSION);
460
- wp_enqueue_style('tnp-admin', $newsletter_url . '/admin/admin.css', [], NEWSLETTER_VERSION);
461
- wp_enqueue_style('tnp-admin-dropdown', $newsletter_url . '/admin/dropdown.css', [], NEWSLETTER_VERSION);
462
- wp_enqueue_style('tnp-admin-tabs', $newsletter_url . '/admin/tabs.css', [], NEWSLETTER_VERSION);
463
- wp_enqueue_style('tnp-admin-controls', $newsletter_url . '/admin/controls.css', [], NEWSLETTER_VERSION);
464
- wp_enqueue_style('tnp-admin-fields', $newsletter_url . '/admin/fields.css', [], NEWSLETTER_VERSION);
465
- wp_enqueue_style('tnp-admin-widgets', $newsletter_url . '/admin/widgets.css', [], NEWSLETTER_VERSION);
466
- wp_enqueue_style('tnp-admin-extensions', $newsletter_url . '/admin/extensions.css', [], NEWSLETTER_VERSION);
467
-
468
- $translations_array = array(
469
- 'save_to_update_counter' => __('Save the newsletter to update the counter!', 'newsletter')
470
- );
471
- wp_localize_script('tnp-admin', 'tnp_translations', $translations_array);
472
-
473
- wp_enqueue_script('tnp-jquery-vmap', $newsletter_url . '/vendor/jqvmap/jquery.vmap.min.js', ['jquery'], NEWSLETTER_VERSION);
474
- wp_enqueue_script('tnp-jquery-vmap-world', $newsletter_url . '/vendor/jqvmap/jquery.vmap.world.js', ['tnp-jquery-vmap'], NEWSLETTER_VERSION);
475
- wp_enqueue_style('tnp-jquery-vmap', $newsletter_url . '/vendor/jqvmap/jqvmap.min.css', [], NEWSLETTER_VERSION);
476
-
477
- wp_register_script('tnp-chart', $newsletter_url . '/vendor/chartjs/Chart.min.js', ['jquery'], NEWSLETTER_VERSION);
478
-
479
- wp_enqueue_script('tnp-color-picker', $newsletter_url . '/vendor/spectrum/spectrum.min.js', ['jquery']);
480
- wp_enqueue_style('tnp-color-picker', $newsletter_url . '/vendor/spectrum/spectrum.min.css', [], NEWSLETTER_VERSION);
481
- }
482
-
483
- function shortcode_newsletter_replace($attrs, $content) {
484
- $content = do_shortcode($content);
485
- $content = $this->replace($content, $this->get_user_from_request(), $this->get_email_from_request());
486
- return $content;
487
- }
488
-
489
- function is_admin_page() {
490
- if (!isset($_GET['page'])) {
491
- return false;
492
- }
493
- $page = $_GET['page'];
494
- return strpos($page, 'newsletter_') === 0;
495
- }
496
-
497
- function hook_admin_init() {
498
- // Verificare il contesto
499
- if (isset($_GET['page']) && $_GET['page'] === 'newsletter_main_welcome')
500
- return;
501
- if (get_option('newsletter_show_welcome')) {
502
- delete_option('newsletter_show_welcome');
503
- wp_redirect(admin_url('admin.php?page=newsletter_main_welcome'));
504
- }
505
- }
506
-
507
- function hook_admin_head() {
508
- // Small global rule for sidebar menu entries
509
- echo '<style>';
510
- echo '.tnp-side-menu { color: #E67E22!important; }';
511
- echo '</style>';
512
- }
513
-
514
- function relink($text, $email_id, $user_id, $email_token = '') {
515
- return NewsletterStatistics::instance()->relink($text, $email_id, $user_id, $email_token);
516
- }
517
-
518
- /**
519
- * Runs every 5 minutes and look for emails that need to be processed.
520
- */
521
- function hook_newsletter() {
522
-
523
- $this->logger->debug(__METHOD__ . '> Start');
524
-
525
- if (!$this->check_transient('engine', NEWSLETTER_CRON_INTERVAL)) {
526
- $this->logger->debug(__METHOD__ . '> Engine already active, exit');
527
- return;
528
- }
529
-
530
- $emails = $this->get_results("select * from " . NEWSLETTER_EMAILS_TABLE . " where status='sending' and send_on<" . time() . " order by id asc");
531
- $this->logger->debug(__METHOD__ . '> Emails found in sending status: ' . count($emails));
532
-
533
- foreach ($emails as $email) {
534
- $this->logger->debug(__METHOD__ . '> Start newsletter ' . $email->id);
535
- $r = $this->send($email);
536
- $this->logger->debug(__METHOD__ . '> End newsletter ' . $email->id);
537
- if (!$r) {
538
- $this->logger->debug(__METHOD__ . '> Engine returned false, there is no more capacity');
539
- break;
540
- }
541
- }
542
- // Remove the semaphore so the delivery engine can be activated again
543
- $this->delete_transient('engine');
544
-
545
- $this->logger->debug(__METHOD__ . '> End');
546
- }
547
-
548
- function get_send_speed($email = null) {
549
- $this->logger->debug(__METHOD__ . '> Computing delivery speed');
550
- $mailer = $this->get_mailer();
551
- $speed = (int) $mailer->get_speed();
552
- if (!$speed) {
553
- $this->logger->debug(__METHOD__ . '> Speed not set by mailer, use the default');
554
- $speed = (int) $this->options['scheduler_max'];
555
- } else {
556
- $this->logger->debug(__METHOD__ . '> Speed set by mailer');
557
- }
558
-
559
- //$speed = (int) apply_filters('newsletter_send_speed', $speed, $email);
560
- // Fail safe setting
561
- $runs_per_hour = $this->get_runs_per_hour();
562
- if (!$speed || $speed < $runs_per_hour) {
563
- return $runs_per_hour;
564
- }
565
-
566
- $this->logger->debug(__METHOD__ . '> Speed: ' . $speed);
567
- return $speed;
568
- }
569
-
570
- function get_send_delay() {
571
- return 0;
572
- }
573
-
574
- function skip_this_run($email = null) {
575
- return (boolean) apply_filters('newsletter_send_skip', false, $email);
576
- }
577
-
578
- function get_runs_per_hour() {
579
- return (int) (3600 / NEWSLETTER_CRON_INTERVAL);
580
- }
581
-
582
- function get_emails_per_run() {
583
- $speed = $this->get_send_speed();
584
- $max = (int)($speed / $this->get_runs_per_hour());
585
-
586
- return $max;
587
- }
588
-
589
- function get_max_emails($email) {
590
- // Obsolete, here from Speed Control Addon
591
- $max = (int) apply_filters('newsletter_send_max_emails', $this->max_emails, $email);
592
-
593
- return min($max, $this->max_emails);
594
- }
595
-
596
- function fix_email($email) {
597
- if (empty($email->query)) {
598
- $email->query = "select * from " . NEWSLETTER_USERS_TABLE . " where status='C'";
599
- }
600
- if (empty($email->id)) {
601
- $email->id = 0;
602
- }
603
- }
604
-
605
- function send_setup() {
606
- $this->logger->debug(__METHOD__ . '> Setup delivery engine');
607
- if (is_null($this->max_emails)) {
608
- $this->max_emails = $this->get_emails_per_run();
609
- $this->logger->debug(__METHOD__ . '> Max emails: ' . $this->max_emails);
610
- ignore_user_abort(true);
611
-
612
- @set_time_limit(NEWSLETTER_CRON_INTERVAL + 30);
613
-
614
- $max_time = (int) (@ini_get('max_execution_time') * 0.95);
615
- if ($max_time == 0 || $max_time > NEWSLETTER_CRON_INTERVAL) {
616
- $max_time = (int) (NEWSLETTER_CRON_INTERVAL * 0.95);
617
- }
618
-
619
- $this->time_limit = $this->time_start + $max_time;
620
-
621
- $this->logger->debug(__METHOD__ . '> Max time set to ' . $max_time);
622
- } else {
623
- $this->logger->debug(__METHOD__ . '> Already setup');
624
- }
625
- }
626
-
627
- function time_exceeded() {
628
- if ($this->time_limit && time() > $this->time_limit) {
629
- $this->logger->info(__METHOD__ . '> Max execution time limit reached');
630
- return true;
631
- }
632
- }
633
-
634
- /**
635
- * Sends an email to targeted users or to given users. If a list of users is given (usually a list of test users)
636
- * the query inside the email to retrieve users is not used.
637
- *
638
- * @global wpdb $wpdb
639
- * @global type $newsletter_feed
640
- * @param TNP_Email $email
641
- * @param array $users
642
- * @return boolean|WP_Error True if the process completed, false if limits was reached. On false the caller should no continue to call it with other emails.
643
- */
644
- function send($email, $users = null, $test = false) {
645
- global $wpdb;
646
-
647
- $this->logger->info(__METHOD__ . '> Send email ' . $email->id);
648
-
649
- $this->send_setup();
650
-
651
- if ($this->max_emails <= 0) {
652
- $this->logger->info(__METHOD__ . '> No more capacity');
653
- return false;
654
- }
655
-
656
- if (is_array($email)) {
657
- $email = (object) $email;
658
- }
659
-
660
- $this->fix_email($email);
661
-
662
- // This stops the update of last_id and sent fields since
663
- // it's not a scheduled delivery but a test or something else (like an autoresponder)
664
- $supplied_users = $users != null;
665
-
666
- if (!$supplied_users) {
667
-
668
- if ($this->skip_this_run($email)) {
669
- return true;
670
- }
671
-
672
- // Speed change for specific email by Speed Control Addon
673
- $max_emails = $this->get_max_emails($email);
674
- if ($max_emails <= 0) {
675
- return true;
676
- }
677
-
678
- $query = $email->query;
679
- $query .= " and id>" . $email->last_id . " order by id limit " . $max_emails;
680
-
681
- $this->logger->debug(__METHOD__ . '> Query: ' . $query);
682
-
683
- //Retrieve subscribers
684
- $users = $this->get_results($query);
685
-
686
- $this->logger->debug(__METHOD__ . '> Loaded subscribers: ' . count($users));
687
-
688
- // If there was a database error, return error
689
- if ($users === false) {
690
- return new WP_Error('1', 'Unable to query subscribers, check the logs');
691
- }
692
-
693
- if (empty($users)) {
694
- $this->logger->info(__METHOD__ . '> No more users, set as sent');
695
- $wpdb->query("update " . NEWSLETTER_EMAILS_TABLE . " set status='sent', total=sent where id=" . $email->id . " limit 1");
696
- do_action('newsletter_ended_sending_newsletter', $email);
697
- return true;
698
- }
699
- } else {
700
- $this->logger->info(__METHOD__ . '> Subscribers supplied');
701
- }
702
-
703
- $start_time = microtime(true);
704
- $count = 0;
705
- $result = true;
706
-
707
- $mailer = $this->get_mailer();
708
-
709
- $batch_size = $mailer->get_batch_size();
710
-
711
- $this->logger->debug(__METHOD__ . '> Batch size: ' . $batch_size);
712
-
713
- // For batch size == 1 (normal condition) we optimize
714
- if ($batch_size == 1) {
715
-
716
- foreach ($users as $user) {
717
-
718
- $this->logger->debug(__METHOD__ . '> Processing user ID: ' . $user->id);
719
- $user = apply_filters('newsletter_send_user', $user);
720
- $message = $this->build_message($email, $user);
721
-
722
- // Save even test emails since people wants to see some stats even for test emails. Stats are reset upon the real "send" of a newsletter
723
- $this->save_sent_message($message);
724
-
725
- //Se non è un test incremento il contatore delle email spedite. Perchè incremento prima di spedire??
726
- if (!$test) {
727
- $wpdb->query("update " . NEWSLETTER_EMAILS_TABLE . " set sent=sent+1, last_id=" . $user->id . " where id=" . $email->id . " limit 1");
728
- }
729
-
730
- $r = $mailer->send($message);
731
-
732
- if (!empty($message->error)) {
733
- $this->logger->error($message);
734
- $this->save_sent_message($message);
735
- }
736
-
737
- if (is_wp_error($r)) {
738
- $this->logger->error($r);
739
-
740
- // For fatal error, the newsletter status i changed to error (and the delivery stopped)
741
- if (!$test && $r->get_error_code() == NewsletterMailer::ERROR_FATAL) {
742
- $this->set_error_state_of_email($email, $r->get_error_message());
743
- }
744
-
745
- return $r;
746
- }
747
-
748
- if (!$supplied_users && !$test && $this->time_exceeded()) {
749
- $result = false;
750
- break;
751
- }
752
- }
753
-
754
- $this->max_emails--;
755
- $count++;
756
- } else {
757
-
758
- $chunks = array_chunk($users, $batch_size);
759
-
760
- foreach ($chunks as $chunk) {
761
-
762
- $messages = [];
763
-
764
- // Peeparing a batch of messages
765
- foreach ($chunk as $user) {
766
- $this->logger->debug(__METHOD__ . '> Processing user ID: ' . $user->id);
767
- $user = apply_filters('newsletter_send_user', $user);
768
- $message = $this->build_message($email, $user);
769
- $this->save_sent_message($message);
770
- $messages[] = $message;
771
-
772
- if (!$test) {
773
- $wpdb->query("update " . NEWSLETTER_EMAILS_TABLE . " set sent=sent+1, last_id=" . $user->id . " where id=" . $email->id . " limit 1");
774
- }
775
- $this->max_emails--;
776
- $count++;
777
- }
778
-
779
- $r = $mailer->send_batch($messages);
780
-
781
- // Updating the status of the sent messages
782
- foreach ($messages as $message) {
783
- if (!empty($message->error)) {
784
- $this->save_sent_message($message);
785
- }
786
- }
787
-
788
- // The batch went in error
789
- if (is_wp_error($r)) {
790
-
791
- if (!$test && $r->get_error_code() == NewsletterMailer::ERROR_FATAL) {
792
- $this->set_error_state_of_email($email, $r->get_error_message());
793
- }
794
-
795
- $this->logger->error($r);
796
- return $r;
797
- }
798
-
799
- if (!$supplied_users && !$test && $this->time_exceeded()) {
800
- $result = false;
801
- break;
802
- }
803
- }
804
- }
805
-
806
- $end_time = microtime(true);
807
-
808
- // Stats only for newsletter with enough emails in a batch (we exclude the Autoresponder since it send one email per call)
809
- if (!$test && !$supplied_users && $count > 5) {
810
- $this->update_send_stats($start_time, $end_time, $count, $result);
811
- }
812
-
813
- // Cached general statistics are reset
814
- if (!$test) {
815
- NewsletterStatistics::instance()->reset_stats_time($email->id);
816
- }
817
-
818
- $this->logger->info(__METHOD__ . '> End run for email ' . $email->id);
819
-
820
- return $result;
821
- }
822
-
823
- function update_send_stats($start_time, $end_time, $count, $result) {
824
- $send_calls = get_option('newsletter_diagnostic_send_calls', []);
825
- $send_calls[] = [$start_time, $end_time, $count, $result];
826
-
827
- if (count($send_calls) > 100) {
828
- array_shift($send_calls);
829
- }
830
-
831
- update_option('newsletter_diagnostic_send_calls', $send_calls, false);
832
- }
833
-
834
- /**
835
- * @param TNP_Email $email
836
- */
837
- private function set_error_state_of_email($email, $message = '') {
838
- // Handle only message type at the moment
839
- if ($email->type !== 'message') {
840
- return;
841
- }
842
-
843
- do_action('newsletter_error_on_sending', $email, $message);
844
-
845
- $edited_email = new TNP_Email();
846
- $edited_email->id = $email->id;
847
- $edited_email->status = TNP_Email::STATUS_ERROR;
848
- $edited_email->options = $email->options;
849
- $edited_email->options['error_message'] = $message;
850
-
851
- $this->save_email($edited_email);
852
- }
853
-
854
- /**
855
- *
856
- * @param TNP_Email $email
857
- * @param TNP_User $user
858
- * @return \TNP_Mailer_Message
859
- */
860
- function build_message($email, $user) {
861
-
862
- $message = new TNP_Mailer_Message();
863
-
864
- $message->to = $user->email;
865
-
866
- $message->headers = [];
867
- $message->headers['Precedence'] = 'bulk';
868
- $message->headers['X-Newsletter-Email-Id'] = $email->id;
869
- $message->headers['X-Auto-Response-Suppress'] = 'OOF, AutoReply';
870
- $message->headers = apply_filters('newsletter_message_headers', $message->headers, $email, $user);
871
-
872
- $message->body = preg_replace('/data-json=".*?"/is', '', $email->message);
873
- $message->body = preg_replace('/ +/s', ' ', $message->body);
874
- $message->body = $this->replace($message->body, $user, $email);
875
- if ($this->options['do_shortcodes']) {
876
- $message->body = do_shortcode($message->body);
877
- }
878
- $message->body = apply_filters('newsletter_message_html', $message->body, $email, $user);
879
-
880
- $message->body_text = $this->replace($email->message_text, $user, $email);
881
- $message->body_text = apply_filters('newsletter_message_text', $message->body_text, $email, $user);
882
-
883
- if ($email->track == 1) {
884
- $message->body = $this->relink($message->body, $email->id, $user->id, $email->token);
885
- }
886
-
887
- $message->subject = $this->replace($email->subject, $user);
888
- $message->subject = apply_filters('newsletter_message_subject', $message->subject, $email, $user);
889
-
890
- if (!empty($email->options['sender_email'])) {
891
- $message->from = $email->options['sender_email'];
892
- } else {
893
- $message->from = $this->options['sender_email'];
894
- }
895
-
896
- if (!empty($email->options['sender_name'])) {
897
- $message->from_name = $email->options['sender_name'];
898
- } else {
899
- $message->from_name = $this->options['sender_name'];
900
- }
901
-
902
- $message->email_id = $email->id;
903
- $message->user_id = $user->id;
904
-
905
- return apply_filters('newsletter_message', $message, $email, $user);
906
- }
907
-
908
- /**
909
- *
910
- * @param TNP_Mailer_Message $message
911
- * @param int $status
912
- * @param string $error
913
- */
914
- function save_sent_message($message) {
915
- global $wpdb;
916
-
917
- if (!$message->user_id || !$message->email_id) {
918
- return;
919
- }
920
- $status = empty($message->error) ? 0 : 1;
921
-
922
- $error = mb_substr($message->error, 0, 250);
923
-
924
- $this->query($wpdb->prepare("insert into " . $wpdb->prefix . 'newsletter_sent (user_id, email_id, time, status, error) values (%d, %d, %d, %d, %s) on duplicate key update time=%d, status=%d, error=%s', $message->user_id, $message->email_id, time(), $status, $error, time(), $status, $error));
925
- }
926
-
927
- /**
928
- * @deprecated since version 7.3.0
929
- */
930
- function limits_exceeded() {
931
- return false;
932
- }
933
-
934
- /**
935
- * @deprecated since version 6.0.0
936
- */
937
- function register_mail_method($callable) {
938
- }
939
-
940
- function register_mailer($mailer) {
941
- if ($mailer instanceof NewsletterMailer) {
942
- $this->mailer = $mailer;
943
- }
944
- }
945
-
946
- /**
947
- * Returns the current registered mailer which must be used to send emails.
948
- *
949
- * @return NewsletterMailer
950
- */
951
- function get_mailer() {
952
- if ($this->mailer) {
953
- return $this->mailer;
954
- }
955
-
956
- do_action('newsletter_register_mailer');
957
-
958
- if (!$this->mailer) {
959
- // Compatibility
960
- $smtp = $this->get_options('smtp');
961
- if (!empty($smtp['enabled'])) {
962
- $this->mailer = new NewsletterDefaultSMTPMailer($smtp);
963
- } else {
964
- $this->mailer = new NewsletterDefaultMailer();
965
- }
966
- }
967
- return $this->mailer;
968
- }
969
-
970
- /**
971
- *
972
- * @param TNP_Mailer_Message $message
973
- * @return type
974
- */
975
- function deliver($message) {
976
- $mailer = $this->get_mailer();
977
- if (empty($message->from))
978
- $message->from = $this->options['sender_email'];
979
- if (empty($message->from_name))
980
- $mailer->from_name = $this->options['sender_name'];
981
- return $mailer->send($message);
982
- }
983
-
984
- /**
985
- *
986
- * @param type $to
987
- * @param type $subject
988
- * @param string|array $message If string is considered HTML, is array should contains the keys "html" and "text"
989
- * @param type $headers
990
- * @param type $enqueue
991
- * @param type $from
992
- * @return boolean
993
- */
994
- function mail($to, $subject, $message, $headers = array(), $enqueue = false, $from = false) {
995
-
996
- if (empty($subject)) {
997
- $this->logger->error('mail> Subject empty, skipped');
998
- return true;
999
- }
1000
-
1001
- $mailer_message = new TNP_Mailer_Message();
1002
- $mailer_message->to = $to;
1003
- $mailer_message->subject = $subject;
1004
- $mailer_message->from = $this->options['sender_email'];
1005
- $mailer_message->from_name = $this->options['sender_name'];
1006
-
1007
- if (!empty($headers)) {
1008
- $mailer_message->headers = $headers;
1009
- }
1010
- $mailer_message->headers['X-Auto-Response-Suppress'] = 'OOF, AutoReply';
1011
-
1012
- // Message carrige returns and line feeds clean up
1013
- if (!is_array($message)) {
1014
- $mailer_message->body = $this->clean_eol($message);
1015
- } else {
1016
- if (!empty($message['text'])) {
1017
- $mailer_message->body_text = $this->clean_eol($message['text']);
1018
- }
1019
-
1020
- if (!empty($message['html'])) {
1021
- $mailer_message->body = $this->clean_eol($message['html']);
1022
- }
1023
- }
1024
-
1025
- $this->logger->debug($mailer_message);
1026
-
1027
- $mailer = $this->get_mailer();
1028
-
1029
- $r = $mailer->send($mailer_message);
1030
-
1031
- return !is_wp_error($r);
1032
- }
1033
-
1034
- function hook_deactivate() {
1035
- wp_clear_scheduled_hook('newsletter');
1036
- }
1037
-
1038
- function find_file($file1, $file2) {
1039
- if (is_file($file1))
1040
- return $file1;
1041
- return $file2;
1042
- }
1043
-
1044
- function hook_site_transient_update_plugins($value) {
1045
- static $extra_response = array();
1046
-
1047
- //$this->logger->debug('Update plugins transient called');
1048
-
1049
- if (!$value || !is_object($value)) {
1050
- //$this->logger->info('Empty object');
1051
- return $value;
1052
- }
1053
-
1054
- if (!isset($value->response) || !is_array($value->response)) {
1055
- $value->response = array();
1056
- }
1057
-
1058
- // Already computed? Use it! (this filter is called many times in a single request)
1059
- if ($extra_response) {
1060
- //$this->logger->debug('Already updated');
1061
- $value->response = array_merge($value->response, $extra_response);
1062
- return $value;
1063
- }
1064
-
1065
- $extensions = $this->getTnpExtensions();
1066
-
1067
- // Ops...
1068
- if (!$extensions) {
1069
- return $value;
1070
- }
1071
-
1072
- foreach ($extensions as $extension) {
1073
- unset($value->response[$extension->wp_slug]);
1074
- unset($value->no_update[$extension->wp_slug]);
1075
- }
1076
-
1077
- // Someone doesn't want our addons updated, let respect it (this constant should be defined in wp-config.php)
1078
- if (!NEWSLETTER_EXTENSION_UPDATE) {
1079
- //$this->logger->info('Updates disabled');
1080
- return $value;
1081
- }
1082
-
1083
- include_once(ABSPATH . 'wp-admin/includes/plugin.php');
1084
-
1085
- // Ok, that is really bad (should we remove it? is there a minimum WP version?)
1086
- if (!function_exists('get_plugin_data')) {
1087
- //$this->logger->error('No get_plugin_data function available!');
1088
- return $value;
1089
- }
1090
-
1091
- $license_key = $this->get_license_key();
1092
-
1093
- // Here we prepare the update information BUT do not add the link to the package which is privided
1094
- // by our Addons Manager (due to WP policies)
1095
- foreach ($extensions as $extension) {
1096
-
1097
- // Patch for names convention
1098
- $extension->plugin = $extension->wp_slug;
1099
-
1100
- //$this->logger->debug('Processing ' . $extension->plugin);
1101
- //$this->logger->debug($extension);
1102
-
1103
- $plugin_data = false;
1104
- if (file_exists(WP_PLUGIN_DIR . '/' . $extension->plugin)) {
1105
- $plugin_data = get_plugin_data(WP_PLUGIN_DIR . '/' . $extension->plugin, false, false);
1106
- } else if (file_exists(WPMU_PLUGIN_DIR . '/' . $extension->plugin)) {
1107
- $plugin_data = get_plugin_data(WPMU_PLUGIN_DIR . '/' . $extension->plugin, false, false);
1108
- }
1109
-
1110
- if (!$plugin_data) {
1111
- //$this->logger->debug('Seems not installed');
1112
- continue;
1113
- }
1114
-
1115
- $plugin = new stdClass();
1116
- $plugin->id = $extension->id;
1117
- $plugin->slug = $extension->slug;
1118
- $plugin->plugin = $extension->plugin;
1119
- $plugin->new_version = $extension->version;
1120
- $plugin->url = $extension->url;
1121
- if (class_exists('NewsletterExtensions')) {
1122
- // NO filters here!
1123
- $plugin->package = NewsletterExtensions::$instance->get_package($extension->id, $license_key);
1124
- } else {
1125
- $plugin->package = '';
1126
- }
1127
- // [banners] => Array
1128
- // (
1129
- // [2x] => https://ps.w.org/wp-rss-aggregator/assets/banner-1544x500.png?rev=2040548
1130
- // [1x] => https://ps.w.org/wp-rss-aggregator/assets/banner-772x250.png?rev=2040548
1131
- // )
1132
- // [icons] => Array
1133
- // (
1134
- // [2x] => https://ps.w.org/advanced-custom-fields/assets/icon-256x256.png?rev=1082746
1135
- // [1x] => https://ps.w.org/advanced-custom-fields/assets/icon-128x128.png?rev=1082746
1136
- // )
1137
- if (version_compare($extension->version, $plugin_data['Version']) > 0) {
1138
- //$this->logger->debug('There is a new version');
1139
- $extra_response[$extension->plugin] = $plugin;
1140
- } else {
1141
- // Maybe useless...
1142
- //$this->logger->debug('There is NOT a new version');
1143
- $value->no_update[$extension->plugin] = $plugin;
1144
- }
1145
- //$this->logger->debug('Added');
1146
- }
1147
-
1148
- $value->response = array_merge($value->response, $extra_response);
1149
-
1150
- return $value;
1151
- }
1152
-
1153
- /**
1154
- * @deprecated since version 6.1.9
1155
- */
1156
- function get_extension_version($extension_id) {
1157
- return null;
1158
- }
1159
-
1160
- /**
1161
- * @deprecated since version 6.1.9
1162
- */
1163
- function set_extension_update_data($value, $extension) {
1164
- return $value;
1165
- }
1166
-
1167
- /**
1168
- * Retrieve the extensions form the tnp site
1169
- * @return array
1170
- */
1171
- function getTnpExtensions() {
1172
-
1173
- $extensions_json = get_transient('tnp_extensions_json');
1174
-
1175
- if (empty($extensions_json)) {
1176
- $url = "http://www.thenewsletterplugin.com/wp-content/extensions.json?ver=" . NEWSLETTER_VERSION;
1177
- $extensions_response = wp_remote_get($url);
1178
-
1179
- if (is_wp_error($extensions_response)) {
1180
- // Cache anyway for blogs which cannot connect outside
1181
- $extensions_json = '[]';
1182
- set_transient('tnp_extensions_json', $extensions_json, 72 * 60 * 60);
1183
- $this->logger->error($extensions_response);
1184
- } else {
1185
-
1186
- $extensions_json = wp_remote_retrieve_body($extensions_response);
1187
-
1188
- // Not clear cases
1189
- if (empty($extensions_json) || !json_decode($extensions_json)) {
1190
- $this->logger->error('Invalid json from thenewsletterplugin.com: retrying in 72 hours');
1191
- $this->logger->error('JSON: ' . $extensions_json);
1192
- $extensions_json = '[]';
1193
- }
1194
- set_transient('tnp_extensions_json', $extensions_json, 72 * 60 * 60);
1195
- }
1196
- }
1197
-
1198
- $extensions = json_decode($extensions_json);
1199
-
1200
- return $extensions;
1201
- }
1202
-
1203
- function clear_extensions_cache() {
1204
- delete_transient('tnp_extensions_json');
1205
- }
1206
-
1207
- var $panels = array();
1208
-
1209
- function add_panel($key, $panel) {
1210
- if (!isset($this->panels[$key]))
1211
- $this->panels[$key] = array();
1212
- if (!isset($panel['id']))
1213
- $panel['id'] = sanitize_key($panel['label']);
1214
- $this->panels[$key][] = $panel;
1215
- }
1216
-
1217
- function has_license() {
1218
- return !empty($this->options['contract_key']);
1219
- }
1220
-
1221
- function get_sender_name() {
1222
- return $this->options['sender_name'];
1223
- }
1224
-
1225
- function get_sender_email() {
1226
- return $this->options['sender_email'];
1227
- }
1228
-
1229
- /**
1230
- *
1231
- * @return int
1232
- */
1233
- function get_newsletter_page_id() {
1234
- return (int) $this->options['page'];
1235
- }
1236
-
1237
- /**
1238
- *
1239
- * @return WP_Post
1240
- */
1241
- function get_newsletter_page() {
1242
- return get_post($this->get_newsletter_page_id());
1243
- }
1244
-
1245
- /**
1246
- * Returns the Newsletter dedicated page URL or an alternative URL if that page if not
1247
- * configured or not available.
1248
- *
1249
- * @staticvar string $url
1250
- * @return string
1251
- */
1252
- function get_newsletter_page_url($language = '') {
1253
-
1254
- $page = $this->get_newsletter_page();
1255
-
1256
- if (!$page || $page->post_status !== 'publish') {
1257
- return $this->build_action_url('m');
1258
- }
1259
-
1260
- $newsletter_page_url = get_permalink($page->ID);
1261
- if ($language && $newsletter_page_url) {
1262
- if (class_exists('SitePress')) {
1263
- $newsletter_page_url = apply_filters('wpml_permalink', $newsletter_page_url, $language, true);
1264
- }
1265
- if (function_exists('pll_get_post')) {
1266
- $translated_page = get_permalink(pll_get_post($page->ID, $language));
1267
- if ($translated_page) {
1268
- $newsletter_page_url = $translated_page;
1269
- }
1270
- }
1271
- }
1272
-
1273
- return $newsletter_page_url;
1274
- }
1275
-
1276
- function get_license_key() {
1277
- if (defined('NEWSLETTER_LICENSE_KEY')) {
1278
- return NEWSLETTER_LICENSE_KEY;
1279
- } else {
1280
- if (!empty($this->options['contract_key'])) {
1281
- return trim($this->options['contract_key']);
1282
- }
1283
- }
1284
- return false;
1285
- }
1286
-
1287
- /**
1288
- * Get the data connected to the specified license code on man settings.
1289
- *
1290
- * - false if no license is present
1291
- * - WP_Error if something went wrong if getting the license data
1292
- * - object with expiration and addons list
1293
- *
1294
- * @param boolean $refresh
1295
- * @return \WP_Error|boolean|object
1296
- */
1297
- function get_license_data($refresh = false) {
1298
-
1299
- $this->logger->debug('Getting license data');
1300
-
1301
- $license_key = $this->get_license_key();
1302
- if (empty($license_key)) {
1303
- $this->logger->debug('License was empty');
1304
- delete_transient('newsletter_license_data');
1305
- return false;
1306
- }
1307
-
1308
- if (!$refresh) {
1309
- $license_data = get_transient('newsletter_license_data');
1310
- if ($license_data !== false && is_object($license_data)) {
1311
- $this->logger->debug('License data found on cache');
1312
- return $license_data;
1313
- }
1314
- }
1315
-
1316
- $this->logger->debug('Refreshing the license data');
1317
-
1318
- $license_data_url = 'https://www.thenewsletterplugin.com/wp-content/plugins/file-commerce-pro/get-license-data.php';
1319
-
1320
- $response = wp_remote_post($license_data_url, array(
1321
- 'body' => array('k' => $license_key)
1322
- ));
1323
-
1324
- // Fall back to http...
1325
- if (is_wp_error($response)) {
1326
- $this->logger->error($response);
1327
- $this->logger->error('Falling back to http');
1328
- $license_data_url = str_replace('https', 'http', $license_data_url);
1329
- $response = wp_remote_post($license_data_url, array(
1330
- 'body' => array('k' => $license_key)
1331
- ));
1332
- if (is_wp_error($response)) {
1333
- $this->logger->error($response);
1334
- set_transient('newsletter_license_data', $response, DAY_IN_SECONDS);
1335
- return $response;
1336
- }
1337
- }
1338
-
1339
- $download_message = 'You can download all addons from www.thenewsletterplugin.com if your license is valid.';
1340
-
1341
- if (wp_remote_retrieve_response_code($response) != '200') {
1342
- $this->logger->error('license data error: ' . wp_remote_retrieve_response_code($response));
1343
- $data = new WP_Error(wp_remote_retrieve_response_code($response), 'License validation service error. <br>' . $download_message);
1344
- set_transient('newsletter_license_data', $data, DAY_IN_SECONDS);
1345
- return $data;
1346
- }
1347
-
1348
- $json = wp_remote_retrieve_body($response);
1349
- $data = json_decode($json);
1350
-
1351
- if (!is_object($data)) {
1352
- $this->logger->error($json);
1353
- $data = new WP_Error(1, 'License validation service error. <br>' . $download_message);
1354
- set_transient('newsletter_license_data', $data, DAY_IN_SECONDS);
1355
- return $data;
1356
- }
1357
-
1358
- if (isset($data->message)) {
1359
- $data = new WP_Error(1, $data->message . ' (check the license on Newsletter main settings)');
1360
- set_transient('newsletter_license_data', $data, DAY_IN_SECONDS);
1361
- return $data;
1362
- }
1363
-
1364
- $expiration = WEEK_IN_SECONDS;
1365
- // If the license expires in few days, make the transient live only few days, so it will be refreshed
1366
- if ($data->expire > time() && $data->expire - time() < WEEK_IN_SECONDS) {
1367
- $expiration = $data->expire - time();
1368
- }
1369
- set_transient('newsletter_license_data', $data, $expiration);
1370
-
1371
- return $data;
1372
- }
1373
-
1374
- /**
1375
- * @deprecated
1376
- * @param type $license_key
1377
- * @return \WP_Error
1378
- */
1379
- public static function check_license($license_key) {
1380
- $response = wp_remote_get('http://www.thenewsletterplugin.com/wp-content/plugins/file-commerce-pro/check.php?k=' . urlencode($license_key), array('sslverify' => false));
1381
- if (is_wp_error($response)) {
1382
- /* @var $response WP_Error */
1383
- return new WP_Error(-1, 'It seems that your blog cannot contact the license validator. Ask your provider to unlock the HTTP/HTTPS connections to www.thenewsletterplugin.com<br>'
1384
- . esc_html($response->get_error_code()) . ' - ' . esc_html($response->get_error_message()));
1385
- } else if ($response['response']['code'] != 200) {
1386
- return new WP_Error(-1, '[' . $response['response']['code'] . '] The license seems expired or not valid, please check your <a href="https://www.thenewsletterplugin.com/account">license code and status</a>, thank you.'
1387
- . '<br>You can anyway download the professional extension from https://www.thenewsletterplugin.com.');
1388
- } elseif ($expires = json_decode(wp_remote_retrieve_body($response))) {
1389
- return array('expires' => $expires->expire, 'message' => 'Your license is valid and expires on ' . esc_html(date('Y-m-d', $expires->expire)));
1390
- } else {
1391
- return new WP_Error(-1, 'Unable to detect the license expiration. Debug data to report to the support: <code>' . esc_html(wp_remote_retrieve_body($response)) . '</code>');
1392
- }
1393
- }
1394
-
1395
- function add_notice_to_chosen_profile_page_hook($post_states, $post) {
1396
-
1397
- if ($post->ID == $this->options['page']) {
1398
- $post_states[] = __('Newsletter plugin page, do not delete', 'newsletter');
1399
- }
1400
-
1401
- return $post_states;
1402
- }
1403
-
1404
- }
1405
-
1406
- $newsletter = Newsletter::instance();
1407
-
1408
- if (is_admin()) {
1409
- require_once NEWSLETTER_DIR . '/system/system.php';
1410
- }
1411
-
1412
- require_once NEWSLETTER_DIR . '/subscription/subscription.php';
1413
- require_once NEWSLETTER_DIR . '/unsubscription/unsubscription.php';
1414
- require_once NEWSLETTER_DIR . '/profile/profile.php';
1415
- require_once NEWSLETTER_DIR . '/emails/emails.php';
1416
- require_once NEWSLETTER_DIR . '/users/users.php';
1417
- require_once NEWSLETTER_DIR . '/statistics/statistics.php';
1418
- require_once NEWSLETTER_DIR . '/widget/standard.php';
1419
- require_once NEWSLETTER_DIR . '/widget/minimal.php';
 
1
+ <?php
2
+
3
+ /*
4
+ Plugin Name: Newsletter
5
+ Plugin URI: https://www.thenewsletterplugin.com/plugins/newsletter
6
+ Description: Newsletter is a cool plugin to create your own subscriber list, to send newsletters, to build your business. <strong>Before update give a look to <a href="https://www.thenewsletterplugin.com/category/release">this page</a> to know what's changed.</strong>
7
+ Version: 7.3.2
8
+ Author: Stefano Lissa & The Newsletter Team
9
+ Author URI: https://www.thenewsletterplugin.com
10
+ Disclaimer: Use at your own risk. No warranty expressed or implied is provided.
11
+ Text Domain: newsletter
12
+ License: GPLv2 or later
13
+ Requires at least: 4.6
14
+ Requires PHP: 5.6
15
+
16
+ Copyright 2009-2021 The Newsletter Team (email: info@thenewsletterplugin.com, web: https://www.thenewsletterplugin.com)
17
+
18
+ Newsletter is free software: you can redistribute it and/or modify
19
+ it under the terms of the GNU General Public License as published by
20
+ the Free Software Foundation, either version 2 of the License, or
21
+ any later version.
22
+
23
+ Newsletter is distributed in the hope that it will be useful,
24
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
25
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26
+ GNU General Public License for more details.
27
+
28
+ You should have received a copy of the GNU General Public License
29
+ along with Newsletter. If not, see https://www.gnu.org/licenses/gpl-2.0.html.
30
+
31
+ */
32
+
33
+ if (version_compare(phpversion(), '5.6', '<')) {
34
+ add_action('admin_notices', function () {
35
+ echo '<div class="notice notice-error"><p>PHP version 5.6 or greater is required for Newsletter. Ask your provider to upgrade. <a href="https://www.php.net/supported-versions.php" target="_blank">Read more on PHP versions</a></p></div>';
36
+ });
37
+ return;
38
+ }
39
+
40
+ define('NEWSLETTER_VERSION', '7.3.2');
41
+
42
+ global $newsletter, $wpdb;
43
+
44
+ // For acceptance tests, DO NOT CHANGE
45
+ if (!defined('NEWSLETTER_DEBUG'))
46
+ define('NEWSLETTER_DEBUG', false);
47
+
48
+ if (!defined('NEWSLETTER_EXTENSION_UPDATE'))
49
+ define('NEWSLETTER_EXTENSION_UPDATE', true);
50
+
51
+ if (!defined('NEWSLETTER_EMAILS_TABLE'))
52
+ define('NEWSLETTER_EMAILS_TABLE', $wpdb->prefix . 'newsletter_emails');
53
+
54
+ if (!defined('NEWSLETTER_USERS_TABLE'))
55
+ define('NEWSLETTER_USERS_TABLE', $wpdb->prefix . 'newsletter');
56
+
57
+ if (!defined('NEWSLETTER_STATS_TABLE'))
58
+ define('NEWSLETTER_STATS_TABLE', $wpdb->prefix . 'newsletter_stats');
59
+
60
+ if (!defined('NEWSLETTER_SENT_TABLE'))
61
+ define('NEWSLETTER_SENT_TABLE', $wpdb->prefix . 'newsletter_sent');
62
+
63
+ define('NEWSLETTER_SLUG', 'newsletter');
64
+
65
+ define('NEWSLETTER_DIR', __DIR__);
66
+ define('NEWSLETTER_INCLUDES_DIR', __DIR__ . '/includes');
67
+
68
+ // Deperacted since plugin can change the plugins_url()
69
+ define('NEWSLETTER_URL', WP_PLUGIN_URL . '/newsletter');
70
+
71
+ if (!defined('NEWSLETTER_LIST_MAX'))
72
+ define('NEWSLETTER_LIST_MAX', 40);
73
+
74
+ if (!defined('NEWSLETTER_PROFILE_MAX'))
75
+ define('NEWSLETTER_PROFILE_MAX', 20);
76
+
77
+ if (!defined('NEWSLETTER_FORMS_MAX'))
78
+ define('NEWSLETTER_FORMS_MAX', 10);
79
+
80
+ if (!defined('NEWSLETTER_HEADER'))
81
+ define('NEWSLETTER_HEADER', true);
82
+
83
+ require_once NEWSLETTER_INCLUDES_DIR . '/module.php';
84
+ require_once NEWSLETTER_INCLUDES_DIR . '/TNP.php';
85
+ require_once NEWSLETTER_INCLUDES_DIR . '/cron.php';
86
+
87
+ class Newsletter extends NewsletterModule {
88
+
89
+ // Limits to respect to avoid memory, time or provider limits
90
+ var $time_start;
91
+ var $time_limit = 0;
92
+ var $max_emails = null;
93
+
94
+ var $mailer = null;
95
+
96
+ var $action = '';
97
+
98
+ /** @var Newsletter */
99
+ static $instance;
100
+
101
+ const STATUS_NOT_CONFIRMED = 'S';
102
+ const STATUS_CONFIRMED = 'C';
103
+
104
+ /**
105
+ * @return Newsletter
106
+ */
107
+ static function instance() {
108
+ if (self::$instance == null) {
109
+ self::$instance = new Newsletter();
110
+ }
111
+ return self::$instance;
112
+ }
113
+
114
+ function __construct() {
115
+
116
+ // Grab it before a plugin decides to remove it.
117
+ if (isset($_GET['na'])) {
118
+ $this->action = $_GET['na'];
119
+ }
120
+ if (isset($_POST['na'])) {
121
+ $this->action = $_POST['na'];
122
+ }
123
+
124
+ $this->time_start = time();
125
+
126
+ parent::__construct('main', '1.6.7', null, ['info', 'smtp']);
127
+
128
+ add_action('plugins_loaded', [$this, 'hook_plugins_loaded']);
129
+ add_action('init', [$this, 'hook_init'], 1);
130
+ add_action('wp_loaded', [$this, 'hook_wp_loaded'], 1);
131
+
132
+ add_action('newsletter', [$this, 'hook_newsletter'], 1);
133
+
134
+ register_activation_hook(__FILE__, [$this, 'hook_activate']);
135
+ register_deactivation_hook(__FILE__, [$this, 'hook_deactivate']);
136
+
137
+ add_action('admin_init', [$this, 'hook_admin_init']);
138
+
139
+ if (is_admin()) {
140
+ add_action('admin_head', [$this, 'hook_admin_head']);
141
+
142
+ // Protection against strange schedule removal on some installations
143
+ if (!wp_next_scheduled('newsletter') && (!defined('WP_INSTALLING') || !WP_INSTALLING)) {
144
+ wp_schedule_event(time() + 30, 'newsletter', 'newsletter');
145
+ }
146
+
147
+ add_action('admin_menu', [$this, 'add_extensions_menu'], 90);
148
+
149
+ add_filter('display_post_states', [$this, 'add_notice_to_chosen_profile_page_hook'], 10, 2);
150
+
151
+ if ($this->is_admin_page()) {
152
+ // Remove the emoji replacer to save to database the original emoji characters (see even woocommerce for the same problem)
153
+ remove_action('admin_print_scripts', 'print_emoji_detection_script');
154
+ add_action('admin_enqueue_scripts', [$this, 'hook_admin_enqueue_scripts']);
155
+ }
156
+
157
+ }
158
+ }
159
+
160
+ function hook_plugins_loaded() {
161
+ // Used to load dependant modules
162
+ do_action('newsletter_loaded', NEWSLETTER_VERSION);
163
+
164
+ if (function_exists('load_plugin_textdomain')) {
165
+ load_plugin_textdomain('newsletter', false, plugin_basename(__DIR__) . '/languages');
166
+ }
167
+ }
168
+
169
+ function hook_init() {
170
+ global $wpdb;
171
+
172
+ if (isset($this->options['debug']) && $this->options['debug'] == 1) {
173
+ ini_set('log_errors', 1);
174
+ ini_set('error_log', WP_CONTENT_DIR . '/logs/newsletter/php-' . date('Y-m') . '-' . get_option('newsletter_logger_secret') . '.txt');
175
+ }
176
+
177
+ add_shortcode('newsletter_replace', [$this, 'shortcode_newsletter_replace']);
178
+
179
+ add_filter('site_transient_update_plugins', [$this, 'hook_site_transient_update_plugins']);
180
+
181
+ if (is_admin()) {
182
+ if (!class_exists('NewsletterExtensions')) {
183
+
184
+ add_filter('plugin_row_meta', function ($plugin_meta, $plugin_file) {
185
+
186
+ static $slugs = array();
187
+ if (empty($slugs)) {
188
+ $addons = $this->getTnpExtensions();
189
+ if ($addons) {
190
+ foreach ($addons as $addon) {
191
+ $slugs[] = $addon->wp_slug;
192
+ }
193
+ }
194
+ }
195
+ if (array_search($plugin_file, $slugs) !== false) {
196
+
197
+ $plugin_meta[] = '<a href="admin.php?page=newsletter_main_extensions" style="font-weight: bold">Newsletter Addons Manager required</a>';
198
+ }
199
+ return $plugin_meta;
200
+ }, 10, 2);
201
+ }
202
+
203
+ add_action('in_admin_header', array($this, 'hook_in_admin_header'), 1000);
204
+
205
+ if ($this->is_admin_page()) {
206
+
207
+ $dismissed = get_option('newsletter_dismissed', []);
208
+
209
+ if (isset($_GET['dismiss'])) {
210
+ $dismissed[$_GET['dismiss']] = 1;
211
+ update_option('newsletter_dismissed', $dismissed);
212
+ wp_redirect($_SERVER['HTTP_REFERER']);
213
+ exit();
214
+ }
215
+ }
216
+ } else {
217
+ add_action('wp_enqueue_scripts', [$this, 'hook_wp_enqueue_scripts']);
218
+ }
219
+
220
+ do_action('newsletter_init');
221
+ }
222
+
223
+ function hook_wp_loaded() {
224
+ if (empty($this->action)) {
225
+ return;
226
+ }
227
+
228
+ if ($this->action == 'test') {
229
+ // This response is tested, do not change it!
230
+ echo 'ok';
231
+ die();
232
+ }
233
+
234
+ if ($this->action === 'nul') {
235
+ $this->dienow('This link is not active on newsletter preview', 'You can send a test message to test subscriber to have the real working link.');
236
+ }
237
+
238
+ $user = $this->get_user_from_request();
239
+ $email = $this->get_email_from_request();
240
+ do_action('newsletter_action', $this->action, $user, $email);
241
+ }
242
+
243
+ function hook_activate() {
244
+ // Ok, why? When the plugin is not active WordPress may remove the scheduled "newsletter" action because
245
+ // the every-five-minutes schedule named "newsletter" is not present.
246
+ // Since the activation does not forces an upgrade, that schedule must be reactivated here. It is activated on
247
+ // the upgrade method as well for the user which upgrade the plugin without deactivte it (many).
248
+ if (!wp_next_scheduled('newsletter')) {
249
+ wp_schedule_event(time() + 30, 'newsletter', 'newsletter');
250
+ }
251
+
252
+ $install_time = get_option('newsletter_install_time');
253
+ if (!$install_time) {
254
+ update_option('newsletter_install_time', time(), false);
255
+ }
256
+
257
+ touch(NEWSLETTER_LOG_DIR . '/index.html');
258
+
259
+ Newsletter::instance()->upgrade();
260
+ NewsletterUsers::instance()->upgrade();
261
+ NewsletterEmails::instance()->upgrade();
262
+ NewsletterSubscription::instance()->upgrade();
263
+ NewsletterStatistics::instance()->upgrade();
264
+ NewsletterProfile::instance()->upgrade();
265
+ }
266
+
267
+ function first_install() {
268
+ parent::first_install();
269
+ update_option('newsletter_show_welcome', '1', false);
270
+ }
271
+
272
+ function upgrade() {
273
+ global $wpdb, $charset_collate;
274
+
275
+ require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
276
+
277
+ parent::upgrade();
278
+
279
+ $sql = "CREATE TABLE `" . $wpdb->prefix . "newsletter_emails` (
280
+ `id` int(11) NOT NULL AUTO_INCREMENT,
281
+ `language` varchar(10) NOT NULL DEFAULT '',
282
+ `subject` varchar(255) NOT NULL DEFAULT '',
283
+ `message` longtext,
284
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
285
+ `status` enum('new','sending','sent','paused','error') NOT NULL DEFAULT 'new',
286
+ `total` int(11) NOT NULL DEFAULT '0',
287
+ `last_id` int(11) NOT NULL DEFAULT '0',
288
+ `sent` int(11) NOT NULL DEFAULT '0',
289
+ `track` int(11) NOT NULL DEFAULT '1',
290
+ `list` int(11) NOT NULL DEFAULT '0',
291
+ `type` varchar(50) NOT NULL DEFAULT '',
292
+ `query` longtext,
293
+ `editor` tinyint(4) NOT NULL DEFAULT '0',
294
+ `sex` varchar(20) NOT NULL DEFAULT '',
295
+ `theme` varchar(50) NOT NULL DEFAULT '',
296
+ `message_text` longtext,
297
+ `preferences` longtext,
298
+ `send_on` int(11) NOT NULL DEFAULT '0',
299
+ `token` varchar(10) NOT NULL DEFAULT '',
300
+ `options` longtext,
301
+ `private` tinyint(1) NOT NULL DEFAULT '0',
302
+ `click_count` int(10) unsigned NOT NULL DEFAULT '0',
303
+ `version` varchar(10) NOT NULL DEFAULT '',
304
+ `open_count` int(10) unsigned NOT NULL DEFAULT '0',
305
+ `unsub_count` int(10) unsigned NOT NULL DEFAULT '0',
306
+ `error_count` int(10) unsigned NOT NULL DEFAULT '0',
307
+ `stats_time` int(10) unsigned NOT NULL DEFAULT '0',
308
+ `updated` int(10) unsigned NOT NULL DEFAULT '0',
309
+ PRIMARY KEY (`id`)
310
+ ) $charset_collate;";
311
+
312
+ dbDelta($sql);
313
+
314
+ // WP does not manage composite primary key when it tries to upgrade a table...
315
+ $suppress_errors = $wpdb->suppress_errors(true);
316
+
317
+ dbDelta("CREATE TABLE " . $wpdb->prefix . "newsletter_sent (
318
+ email_id int(10) unsigned NOT NULL DEFAULT '0',
319
+ user_id int(10) unsigned NOT NULL DEFAULT '0',
320
+ status tinyint(1) unsigned NOT NULL DEFAULT '0',
321
+ open tinyint(1) unsigned NOT NULL DEFAULT '0',
322
+ time int(10) unsigned NOT NULL DEFAULT '0',
323
+ error varchar(255) NOT NULL DEFAULT '',
324
+ ip varchar(100) NOT NULL DEFAULT '',
325
+ PRIMARY KEY (email_id,user_id),
326
+ KEY user_id (user_id),
327
+ KEY email_id (email_id)
328
+ ) $charset_collate;");
329
+ $wpdb->suppress_errors($suppress_errors);
330
+
331
+ // Some setting check to avoid the common support request for mis-configurations
332
+ $options = $this->get_options();
333
+
334
+ if (empty($options['scheduler_max']) || !is_numeric($options['scheduler_max'])) {
335
+ $options['scheduler_max'] = 100;
336
+ $this->save_options($options);
337
+ }
338
+
339
+ wp_clear_scheduled_hook('newsletter');
340
+ wp_schedule_event(time() + 30, 'newsletter', 'newsletter');
341
+
342
+ if (!empty($this->options['editor'])) {
343
+ if (empty($this->options['roles'])) {
344
+ $this->options['roles'] = array('editor');
345
+ unset($this->options['editor']);
346
+ }
347
+ $this->save_options($this->options);
348
+ }
349
+
350
+ // Clear the addons and license caches
351
+ delete_transient('newsletter_license_data');
352
+ $this->clear_extensions_cache();
353
+
354
+ touch(NEWSLETTER_LOG_DIR . '/index.html');
355
+
356
+ return true;
357
+ }
358
+
359
+ function is_allowed() {
360
+ if (current_user_can('administrator')) {
361
+ return true;
362
+ }
363
+ if (!empty($this->options['roles'])) {
364
+ foreach ($this->options['roles'] as $role) {
365
+ if (current_user_can($role)) {
366
+ return true;
367
+ }
368
+ }
369
+ }
370
+ return false;
371
+ }
372
+
373
+ function admin_menu() {
374
+ if (!$this->is_allowed()) {
375
+ return;
376
+ }
377
+
378
+ add_menu_page('Newsletter', 'Newsletter', 'exist', 'newsletter_main_index', '', plugins_url('newsletter') . '/admin/images/menu-icon.png', '30.333');
379
+
380
+ $this->add_menu_page('index', __('Dashboard', 'newsletter'));
381
+ $this->add_admin_page('info', __('Company info', 'newsletter'));
382
+
383
+ if (current_user_can('administrator')) {
384
+ $this->add_menu_page('welcome', __('Welcome', 'newsletter'));
385
+ $this->add_menu_page('main', __('Settings', 'newsletter'));
386
+
387
+ // Pages not on menu
388
+ $this->add_admin_page('smtp', 'SMTP');
389
+ $this->add_admin_page('diagnostic', __('Diagnostic', 'newsletter'));
390
+ $this->add_admin_page('test', __('Test', 'newsletter'));
391
+ }
392
+ }
393
+
394
+ function add_extensions_menu() {
395
+ if (!class_exists('NewsletterExtensions')) {
396
+ $this->add_menu_page('extensions', '<span style="color:#27AE60; font-weight: bold;">' . __('Addons', 'newsletter') . '</span>');
397
+ }
398
+ }
399
+
400
+ function hook_in_admin_header() {
401
+ if (!$this->is_admin_page()) {
402
+ add_action('admin_notices', array($this, 'hook_admin_notices'));
403
+ return;
404
+ }
405
+ remove_all_actions('admin_notices');
406
+ remove_all_actions('all_admin_notices');
407
+ add_action('admin_notices', array($this, 'hook_admin_notices'));
408
+ }
409
+
410
+ function hook_admin_notices() {
411
+ // Check of Newsletter dedicated page
412
+ if (!empty($this->options['page'])) {
413
+ if (get_post_status($this->options['page']) !== 'publish') {
414
+ echo '<div class="notice notice-error"><p>The Newsletter dedicated page is not published. <a href="', site_url('/wp-admin/post.php') . '?post=', $this->options['page'], '&action=edit"><strong>Edit the page</strong></a> or <a href="admin.php?page=newsletter_main_main"><strong>review the main settings</strong></a>.</p></div>';
415
+ }
416
+ }
417
+
418
+ if (isset($this->options['debug']) && $this->options['debug'] == 1) {
419
+ echo '<div class="notice notice-warning"><p>The Newsletter plugin is in <strong>debug mode</strong>. When done change it on Newsletter <a href="admin.php?page=newsletter_main_main"><strong>main settings</strong></a>. Do not keep the debug mode active on production sites.</p></div>';
420
+ }
421
+ }
422
+
423
+ function hook_wp_enqueue_scripts() {
424
+ if (empty($this->options['css_disabled']) && apply_filters('newsletter_enqueue_style', true)) {
425
+ wp_enqueue_style('newsletter', plugins_url('newsletter') . '/style.css', [], NEWSLETTER_VERSION);
426
+ if (!empty($this->options['css'])) {
427
+ wp_add_inline_style('newsletter', $this->options['css']);
428
+ }
429
+ } else {
430
+ if (!empty($this->options['css'])) {
431
+ add_action('wp_head', function () {
432
+ echo '<style>', $this->options['css'], '</style>';
433
+ });
434
+ }
435
+ }
436
+ }
437
+
438
+ function hook_admin_enqueue_scripts() {
439
+
440
+ $newsletter_url = plugins_url('newsletter');
441
+ wp_enqueue_script('jquery-ui-tabs');
442
+ wp_enqueue_script('jquery-ui-tooltip');
443
+ wp_enqueue_script('jquery-ui-draggable');
444
+ wp_enqueue_media();
445
+
446
+ wp_enqueue_script('tnp-admin', $newsletter_url . '/admin/admin.js', ['jquery'], NEWSLETTER_VERSION);
447
+
448
+ wp_enqueue_style('tnp-select2', $newsletter_url . '/vendor/select2/css/select2.min.css', [], NEWSLETTER_VERSION);
449
+ wp_enqueue_script('tnp-select2', $newsletter_url . '/vendor/select2/js/select2.min.js', ['jquery'], NEWSLETTER_VERSION);
450
+
451
+ wp_enqueue_style('tnp-modal', $newsletter_url . '/admin/modal.css', [], NEWSLETTER_VERSION);
452
+ wp_enqueue_script('tnp-modal', $newsletter_url . '/admin/modal.js', ['jquery'], NEWSLETTER_VERSION, true);
453
+
454
+ wp_enqueue_style('tnp-toast', $newsletter_url . '/admin/toast.css', [], NEWSLETTER_VERSION);
455
+ wp_enqueue_script('tnp-toast', $newsletter_url . '/admin/toast.js', ['jquery'], NEWSLETTER_VERSION);
456
+
457
+ wp_enqueue_style('tnp-admin-font', 'https://use.typekit.net/jlj2wjy.css');
458
+ wp_enqueue_style('tnp-admin-fontawesome', $newsletter_url . '/vendor/fa/css/all.min.css', [], NEWSLETTER_VERSION);
459
+ wp_enqueue_style('tnp-admin-jquery-ui', $newsletter_url . '/vendor/jquery-ui/jquery-ui.min.css', [], NEWSLETTER_VERSION);
460
+ wp_enqueue_style('tnp-admin', $newsletter_url . '/admin/admin.css', [], NEWSLETTER_VERSION);
461
+ wp_enqueue_style('tnp-admin-dropdown', $newsletter_url . '/admin/dropdown.css', [], NEWSLETTER_VERSION);
462
+ wp_enqueue_style('tnp-admin-tabs', $newsletter_url . '/admin/tabs.css', [], NEWSLETTER_VERSION);
463
+ wp_enqueue_style('tnp-admin-controls', $newsletter_url . '/admin/controls.css', [], NEWSLETTER_VERSION);
464
+ wp_enqueue_style('tnp-admin-fields', $newsletter_url . '/admin/fields.css', [], NEWSLETTER_VERSION);
465
+ wp_enqueue_style('tnp-admin-widgets', $newsletter_url . '/admin/widgets.css', [], NEWSLETTER_VERSION);
466
+ wp_enqueue_style('tnp-admin-extensions', $newsletter_url . '/admin/extensions.css', [], NEWSLETTER_VERSION);
467
+
468
+ $translations_array = array(
469
+ 'save_to_update_counter' => __('Save the newsletter to update the counter!', 'newsletter')
470
+ );
471
+ wp_localize_script('tnp-admin', 'tnp_translations', $translations_array);
472
+
473
+ wp_enqueue_script('tnp-jquery-vmap', $newsletter_url . '/vendor/jqvmap/jquery.vmap.min.js', ['jquery'], NEWSLETTER_VERSION);
474
+ wp_enqueue_script('tnp-jquery-vmap-world', $newsletter_url . '/vendor/jqvmap/jquery.vmap.world.js', ['tnp-jquery-vmap'], NEWSLETTER_VERSION);
475
+ wp_enqueue_style('tnp-jquery-vmap', $newsletter_url . '/vendor/jqvmap/jqvmap.min.css', [], NEWSLETTER_VERSION);
476
+
477
+ wp_register_script('tnp-chart', $newsletter_url . '/vendor/chartjs/Chart.min.js', ['jquery'], NEWSLETTER_VERSION);
478
+
479
+ wp_enqueue_script('tnp-color-picker', $newsletter_url . '/vendor/spectrum/spectrum.min.js', ['jquery']);
480
+ wp_enqueue_style('tnp-color-picker', $newsletter_url . '/vendor/spectrum/spectrum.min.css', [], NEWSLETTER_VERSION);
481
+ }
482
+
483
+ function shortcode_newsletter_replace($attrs, $content) {
484
+ $content = do_shortcode($content);
485
+ $content = $this->replace($content, $this->get_user_from_request(), $this->get_email_from_request());
486
+ return $content;
487
+ }
488
+
489
+ function is_admin_page() {
490
+ if (!isset($_GET['page'])) {
491
+ return false;
492
+ }
493
+ $page = $_GET['page'];
494
+ return strpos($page, 'newsletter_') === 0;
495
+ }
496
+
497
+ function hook_admin_init() {
498
+ // Verificare il contesto
499
+ if (isset($_GET['page']) && $_GET['page'] === 'newsletter_main_welcome')
500
+ return;
501
+ if (get_option('newsletter_show_welcome')) {
502
+ delete_option('newsletter_show_welcome');
503
+ wp_redirect(admin_url('admin.php?page=newsletter_main_welcome'));
504
+ }
505
+ }
506
+
507
+ function hook_admin_head() {
508
+ // Small global rule for sidebar menu entries
509
+ echo '<style>';
510
+ echo '.tnp-side-menu { color: #E67E22!important; }';
511
+ echo '</style>';
512
+ }
513
+
514
+ function relink($text, $email_id, $user_id, $email_token = '') {
515
+ return NewsletterStatistics::instance()->relink($text, $email_id, $user_id, $email_token);
516
+ }
517
+
518
+ /**
519
+ * Runs every 5 minutes and look for emails that need to be processed.
520
+ */
521
+ function hook_newsletter() {
522
+
523
+ $this->logger->debug(__METHOD__ . '> Start');
524
+
525
+ if (!$this->check_transient('engine', NEWSLETTER_CRON_INTERVAL)) {
526
+ $this->logger->debug(__METHOD__ . '> Engine already active, exit');
527
+ return;
528
+ }
529
+
530
+ $emails = $this->get_results("select * from " . NEWSLETTER_EMAILS_TABLE . " where status='sending' and send_on<" . time() . " order by id asc");
531
+ $this->logger->debug(__METHOD__ . '> Emails found in sending status: ' . count($emails));
532
+
533
+ foreach ($emails as $email) {
534
+ $this->logger->debug(__METHOD__ . '> Start newsletter ' . $email->id);
535
+ $email->options = maybe_unserialize($email->options);
536
+ $r = $this->send($email);
537
+ $this->logger->debug(__METHOD__ . '> End newsletter ' . $email->id);
538
+ if (!$r) {
539
+ $this->logger->debug(__METHOD__ . '> Engine returned false, there is no more capacity');
540
+ break;
541
+ }
542
+ }
543
+ // Remove the semaphore so the delivery engine can be activated again
544
+ $this->delete_transient('engine');
545
+
546
+ $this->logger->debug(__METHOD__ . '> End');
547
+ }
548
+
549
+ function get_send_speed($email = null) {
550
+ $this->logger->debug(__METHOD__ . '> Computing delivery speed');
551
+ $mailer = $this->get_mailer();
552
+ $speed = (int) $mailer->get_speed();
553
+ if (!$speed) {
554
+ $this->logger->debug(__METHOD__ . '> Speed not set by mailer, use the default');
555
+ $speed = (int) $this->options['scheduler_max'];
556
+ } else {
557
+ $this->logger->debug(__METHOD__ . '> Speed set by mailer');
558
+ }
559
+
560
+ //$speed = (int) apply_filters('newsletter_send_speed', $speed, $email);
561
+ // Fail safe setting
562
+ $runs_per_hour = $this->get_runs_per_hour();
563
+ if (!$speed || $speed < $runs_per_hour) {
564
+ return $runs_per_hour;
565
+ }
566
+
567
+ $this->logger->debug(__METHOD__ . '> Speed: ' . $speed);
568
+ return $speed;
569
+ }
570
+
571
+ function get_send_delay() {
572
+ return 0;
573
+ }
574
+
575
+ function skip_this_run($email = null) {
576
+ return (boolean) apply_filters('newsletter_send_skip', false, $email);
577
+ }
578
+
579
+ function get_runs_per_hour() {
580
+ return (int) (3600 / NEWSLETTER_CRON_INTERVAL);
581
+ }
582
+
583
+ function get_emails_per_run() {
584
+ $speed = $this->get_send_speed();
585
+ $max = (int)($speed / $this->get_runs_per_hour());
586
+
587
+ return $max;
588
+ }
589
+
590
+ function get_max_emails($email) {
591
+ // Obsolete, here from Speed Control Addon
592
+ $max = (int) apply_filters('newsletter_send_max_emails', $this->max_emails, $email);
593
+
594
+ return min($max, $this->max_emails);
595
+ }
596
+
597
+ function fix_email($email) {
598
+ if (empty($email->query)) {
599
+ $email->query = "select * from " . NEWSLETTER_USERS_TABLE . " where status='C'";
600
+ }
601
+ if (empty($email->id)) {
602
+ $email->id = 0;
603
+ }
604
+ }
605
+
606
+ function send_setup() {
607
+ $this->logger->debug(__METHOD__ . '> Setup delivery engine');
608
+ if (is_null($this->max_emails)) {
609
+ $this->max_emails = $this->get_emails_per_run();
610
+ $this->logger->debug(__METHOD__ . '> Max emails: ' . $this->max_emails);
611
+ ignore_user_abort(true);
612
+
613
+ @set_time_limit(NEWSLETTER_CRON_INTERVAL + 30);
614
+
615
+ $max_time = (int) (@ini_get('max_execution_time') * 0.95);
616
+ if ($max_time == 0 || $max_time > NEWSLETTER_CRON_INTERVAL) {
617
+ $max_time = (int) (NEWSLETTER_CRON_INTERVAL * 0.95);
618
+ }
619
+
620
+ $this->time_limit = $this->time_start + $max_time;
621
+
622
+ $this->logger->debug(__METHOD__ . '> Max time set to ' . $max_time);
623
+ } else {
624
+ $this->logger->debug(__METHOD__ . '> Already setup');
625
+ }
626
+ }
627
+
628
+ function time_exceeded() {
629
+ if ($this->time_limit && time() > $this->time_limit) {
630
+ $this->logger->info(__METHOD__ . '> Max execution time limit reached');
631
+ return true;
632
+ }
633
+ }
634
+
635
+ /**
636
+ * Sends an email to targeted users or to given users. If a list of users is given (usually a list of test users)
637
+ * the query inside the email to retrieve users is not used.
638
+ *
639
+ * @global wpdb $wpdb
640
+ * @global type $newsletter_feed
641
+ * @param TNP_Email $email
642
+ * @param array $users
643
+ * @return boolean|WP_Error True if the process completed, false if limits was reached. On false the caller should no continue to call it with other emails.
644
+ */
645
+ function send($email, $users = null, $test = false) {
646
+ global $wpdb;
647
+
648
+ $this->logger->info(__METHOD__ . '> Send email ' . $email->id);
649
+
650
+ $this->send_setup();
651
+
652
+ if ($this->max_emails <= 0) {
653
+ $this->logger->info(__METHOD__ . '> No more capacity');
654
+ return false;
655
+ }
656
+
657
+ if (is_array($email)) {
658
+ $email = (object) $email;
659
+ }
660
+
661
+ $this->fix_email($email);
662
+
663
+ // This stops the update of last_id and sent fields since
664
+ // it's not a scheduled delivery but a test or something else (like an autoresponder)
665
+ $supplied_users = $users != null;
666
+
667
+ if (!$supplied_users) {
668
+
669
+ if ($this->skip_this_run($email)) {
670
+ return true;
671
+ }
672
+
673
+ // Speed change for specific email by Speed Control Addon
674
+ $max_emails = $this->get_max_emails($email);
675
+ if ($max_emails <= 0) {
676
+ return true;
677
+ }
678
+
679
+ $query = $email->query;
680
+ $query .= " and id>" . $email->last_id . " order by id limit " . $max_emails;
681
+
682
+ $this->logger->debug(__METHOD__ . '> Query: ' . $query);
683
+
684
+ //Retrieve subscribers
685
+ $users = $this->get_results($query);
686
+
687
+ $this->logger->debug(__METHOD__ . '> Loaded subscribers: ' . count($users));
688
+
689
+ // If there was a database error, return error
690
+ if ($users === false) {
691
+ return new WP_Error('1', 'Unable to query subscribers, check the logs');
692
+ }
693
+
694
+ if (empty($users)) {
695
+ $this->logger->info(__METHOD__ . '> No more users, set as sent');
696
+ $wpdb->query("update " . NEWSLETTER_EMAILS_TABLE . " set status='sent', total=sent where id=" . $email->id . " limit 1");
697
+ do_action('newsletter_ended_sending_newsletter', $email);
698
+ return true;
699
+ }
700
+ } else {
701
+ $this->logger->info(__METHOD__ . '> Subscribers supplied');
702
+ }
703
+
704
+ $start_time = microtime(true);
705
+ $count = 0;
706
+ $result = true;
707
+
708
+ $mailer = $this->get_mailer();
709
+
710
+ $batch_size = $mailer->get_batch_size();
711
+
712
+ $this->logger->debug(__METHOD__ . '> Batch size: ' . $batch_size);
713
+
714
+ // For batch size == 1 (normal condition) we optimize
715
+ if ($batch_size == 1) {
716
+
717
+ foreach ($users as $user) {
718
+
719
+ $this->logger->debug(__METHOD__ . '> Processing user ID: ' . $user->id);
720
+ $user = apply_filters('newsletter_send_user', $user);
721
+ $message = $this->build_message($email, $user);
722
+
723
+ // Save even test emails since people wants to see some stats even for test emails. Stats are reset upon the real "send" of a newsletter
724
+ $this->save_sent_message($message);
725
+
726
+ //Se non è un test incremento il contatore delle email spedite. Perchè incremento prima di spedire??
727
+ if (!$test) {
728
+ $wpdb->query("update " . NEWSLETTER_EMAILS_TABLE . " set sent=sent+1, last_id=" . $user->id . " where id=" . $email->id . " limit 1");
729
+ }
730
+
731
+ $r = $mailer->send($message);
732
+
733
+ if (!empty($message->error)) {
734
+ $this->logger->error($message);
735
+ $this->save_sent_message($message);
736
+ }
737
+
738
+ if (is_wp_error($r)) {
739
+ $this->logger->error($r);
740
+
741
+ // For fatal error, the newsletter status i changed to error (and the delivery stopped)
742
+ if (!$test && $r->get_error_code() == NewsletterMailer::ERROR_FATAL) {
743
+ $this->set_error_state_of_email($email, $r->get_error_message());
744
+ }
745
+
746
+ return $r;
747
+ }
748
+
749
+ if (!$supplied_users && !$test && $this->time_exceeded()) {
750
+ $result = false;
751
+ break;
752
+ }
753
+ }
754
+
755
+ $this->max_emails--;
756
+ $count++;
757
+ } else {
758
+
759
+ $chunks = array_chunk($users, $batch_size);
760
+
761
+ foreach ($chunks as $chunk) {
762
+
763
+ $messages = [];
764
+
765
+ // Peeparing a batch of messages
766
+ foreach ($chunk as $user) {
767
+ $this->logger->debug(__METHOD__ . '> Processing user ID: ' . $user->id);
768
+ $user = apply_filters('newsletter_send_user', $user);
769
+ $message = $this->build_message($email, $user);
770
+ $this->save_sent_message($message);
771
+ $messages[] = $message;
772
+
773
+ if (!$test) {
774
+ $wpdb->query("update " . NEWSLETTER_EMAILS_TABLE . " set sent=sent+1, last_id=" . $user->id . " where id=" . $email->id . " limit 1");
775
+ }
776
+ $this->max_emails--;
777
+ $count++;
778
+ }
779
+
780
+ $r = $mailer->send_batch($messages);
781
+
782
+ // Updating the status of the sent messages
783
+ foreach ($messages as $message) {
784
+ if (!empty($message->error)) {
785
+ $this->save_sent_message($message);
786
+ }
787
+ }
788
+
789
+ // The batch went in error
790
+ if (is_wp_error($r)) {
791
+
792
+ if (!$test && $r->get_error_code() == NewsletterMailer::ERROR_FATAL) {
793
+ $this->set_error_state_of_email($email, $r->get_error_message());
794
+ }
795
+
796
+ $this->logger->error($r);
797
+ return $r;
798
+ }
799
+
800
+ if (!$supplied_users && !$test && $this->time_exceeded()) {
801
+ $result = false;
802
+ break;
803
+ }
804
+ }
805
+ }
806
+
807
+ $end_time = microtime(true);
808
+
809
+ // Stats only for newsletter with enough emails in a batch (we exclude the Autoresponder since it send one email per call)
810
+ if (!$test && !$supplied_users && $count > 5) {
811
+ $this->update_send_stats($start_time, $end_time, $count, $result);
812
+ }
813
+
814
+ // Cached general statistics are reset
815
+ if (!$test) {
816
+ NewsletterStatistics::instance()->reset_stats_time($email->id);
817
+ }
818
+
819
+ $this->logger->info(__METHOD__ . '> End run for email ' . $email->id);
820
+
821
+ return $result;
822
+ }
823
+
824
+ function update_send_stats($start_time, $end_time, $count, $result) {
825
+ $send_calls = get_option('newsletter_diagnostic_send_calls', []);
826
+ $send_calls[] = [$start_time, $end_time, $count, $result];
827
+
828
+ if (count($send_calls) > 100) {
829
+ array_shift($send_calls);
830
+ }
831
+
832
+ update_option('newsletter_diagnostic_send_calls', $send_calls, false);
833
+ }
834
+
835
+ /**
836
+ * @param TNP_Email $email
837
+ */
838
+ private function set_error_state_of_email($email, $message = '') {
839
+ // Handle only message type at the moment
840
+ if ($email->type !== 'message') {
841
+ return;
842
+ }
843
+
844
+ do_action('newsletter_error_on_sending', $email, $message);
845
+
846
+ $edited_email = new TNP_Email();
847
+ $edited_email->id = $email->id;
848
+ $edited_email->status = TNP_Email::STATUS_ERROR;
849
+ $edited_email->options = $email->options;
850
+ $edited_email->options['error_message'] = $message;
851
+
852
+ $this->save_email($edited_email);
853
+ }
854
+
855
+ /**
856
+ *
857
+ * @param TNP_Email $email
858
+ * @param TNP_User $user
859
+ * @return \TNP_Mailer_Message
860
+ */
861
+ function build_message($email, $user) {
862
+
863
+ $message = new TNP_Mailer_Message();
864
+
865
+ $message->to = $user->email;
866
+
867
+ $message->headers = [];
868
+ $message->headers['Precedence'] = 'bulk';
869
+ $message->headers['X-Newsletter-Email-Id'] = $email->id;
870
+ $message->headers['X-Auto-Response-Suppress'] = 'OOF, AutoReply';
871
+ $message->headers = apply_filters('newsletter_message_headers', $message->headers, $email, $user);
872
+
873
+ $message->body = preg_replace('/data-json=".*?"/is', '', $email->message);
874
+ $message->body = preg_replace('/ +/s', ' ', $message->body);
875
+ $message->body = $this->replace($message->body, $user, $email);
876
+ if ($this->options['do_shortcodes']) {
877
+ $message->body = do_shortcode($message->body);
878
+ }
879
+ $message->body = apply_filters('newsletter_message_html', $message->body, $email, $user);
880
+
881
+ $message->body_text = $this->replace($email->message_text, $user, $email);
882
+ $message->body_text = apply_filters('newsletter_message_text', $message->body_text, $email, $user);
883
+
884
+ if ($email->track == 1) {
885
+ $message->body = $this->relink($message->body, $email->id, $user->id, $email->token);
886
+ }
887
+
888
+ $message->subject = $this->replace($email->subject, $user);
889
+ $message->subject = apply_filters('newsletter_message_subject', $message->subject, $email, $user);
890
+
891
+ if (!empty($email->options['sender_email'])) {
892
+ $message->from = $email->options['sender_email'];
893
+ } else {
894
+ $message->from = $this->options['sender_email'];
895
+ }
896
+
897
+ if (!empty($email->options['sender_name'])) {
898
+ $message->from_name = $email->options['sender_name'];
899
+ } else {
900
+ $message->from_name = $this->options['sender_name'];
901
+ }
902
+
903
+ $message->email_id = $email->id;
904
+ $message->user_id = $user->id;
905
+
906
+ return apply_filters('newsletter_message', $message, $email, $user);
907
+ }
908
+
909
+ /**
910
+ *
911
+ * @param TNP_Mailer_Message $message
912
+ * @param int $status
913
+ * @param string $error
914
+ */
915
+ function save_sent_message($message) {
916
+ global $wpdb;
917
+
918
+ if (!$message->user_id || !$message->email_id) {
919
+ return;
920
+ }
921
+ $status = empty($message->error) ? 0 : 1;
922
+
923
+ $error = mb_substr($message->error, 0, 250);
924
+
925
+ $this->query($wpdb->prepare("insert into " . $wpdb->prefix . 'newsletter_sent (user_id, email_id, time, status, error) values (%d, %d, %d, %d, %s) on duplicate key update time=%d, status=%d, error=%s', $message->user_id, $message->email_id, time(), $status, $error, time(), $status, $error));
926
+ }
927
+
928
+ /**
929
+ * @deprecated since version 7.3.0
930
+ */
931
+ function limits_exceeded() {
932
+ return false;
933
+ }
934
+
935
+ /**
936
+ * @deprecated since version 6.0.0
937
+ */
938
+ function register_mail_method($callable) {
939
+ }
940
+
941
+ function register_mailer($mailer) {
942
+ if ($mailer instanceof NewsletterMailer) {
943
+ $this->mailer = $mailer;
944
+ }
945
+ }
946
+
947
+ /**
948
+ * Returns the current registered mailer which must be used to send emails.
949
+ *
950
+ * @return NewsletterMailer
951
+ */
952
+ function get_mailer() {
953
+ if ($this->mailer) {
954
+ return $this->mailer;
955
+ }
956
+
957
+ do_action('newsletter_register_mailer');
958
+
959
+ if (!$this->mailer) {
960
+ // Compatibility
961
+ $smtp = $this->get_options('smtp');
962
+ if (!empty($smtp['enabled'])) {
963
+ $this->mailer = new NewsletterDefaultSMTPMailer($smtp);
964
+ } else {
965
+ $this->mailer = new NewsletterDefaultMailer();
966
+ }
967
+ }
968
+ return $this->mailer;
969
+ }
970
+
971
+ /**
972
+ *
973
+ * @param TNP_Mailer_Message $message
974
+ * @return type
975
+ */
976
+ function deliver($message) {
977
+ $mailer = $this->get_mailer();
978
+ if (empty($message->from))
979
+ $message->from = $this->options['sender_email'];
980
+ if (empty($message->from_name))
981
+ $mailer->from_name = $this->options['sender_name'];
982
+ return $mailer->send($message);
983
+ }
984
+
985
+ /**
986
+ *
987
+ * @param type $to
988
+ * @param type $subject
989
+ * @param string|array $message If string is considered HTML, is array should contains the keys "html" and "text"
990
+ * @param type $headers
991
+ * @param type $enqueue
992
+ * @param type $from
993
+ * @return boolean
994
+ */
995
+ function mail($to, $subject, $message, $headers = array(), $enqueue = false, $from = false) {
996
+
997
+ if (empty($subject)) {
998
+ $this->logger->error('mail> Subject empty, skipped');
999
+ return true;
1000
+ }
1001
+
1002
+ $mailer_message = new TNP_Mailer_Message();
1003
+ $mailer_message->to = $to;
1004
+ $mailer_message->subject = $subject;
1005
+ $mailer_message->from = $this->options['sender_email'];
1006
+ $mailer_message->from_name = $this->options['sender_name'];
1007
+
1008
+ if (!empty($headers)) {
1009
+ $mailer_message->headers = $headers;
1010
+ }
1011
+ $mailer_message->headers['X-Auto-Response-Suppress'] = 'OOF, AutoReply';
1012
+
1013
+ // Message carrige returns and line feeds clean up
1014
+ if (!is_array($message)) {
1015
+ $mailer_message->body = $this->clean_eol($message);
1016
+ } else {
1017
+ if (!empty($message['text'])) {
1018
+ $mailer_message->body_text = $this->clean_eol($message['text']);
1019
+ }
1020
+
1021
+ if (!empty($message['html'])) {
1022
+ $mailer_message->body = $this->clean_eol($message['html']);
1023
+ }
1024
+ }
1025
+
1026
+ $this->logger->debug($mailer_message);
1027
+
1028
+ $mailer = $this->get_mailer();
1029
+
1030
+ $r = $mailer->send($mailer_message);
1031
+
1032
+ return !is_wp_error($r);
1033
+ }
1034
+
1035
+ function hook_deactivate() {
1036
+ wp_clear_scheduled_hook('newsletter');
1037
+ }
1038
+
1039
+ function find_file($file1, $file2) {
1040
+ if (is_file($file1))
1041
+ return $file1;
1042
+ return $file2;
1043
+ }
1044
+
1045
+ function hook_site_transient_update_plugins($value) {
1046
+ static $extra_response = array();
1047
+
1048
+ //$this->logger->debug('Update plugins transient called');
1049
+
1050
+ if (!$value || !is_object($value)) {
1051
+ //$this->logger->info('Empty object');
1052
+ return $value;
1053
+ }
1054
+
1055
+ if (!isset($value->response) || !is_array($value->response)) {
1056
+ $value->response = array();
1057
+ }
1058
+
1059
+ // Already computed? Use it! (this filter is called many times in a single request)
1060
+ if ($extra_response) {
1061
+ //$this->logger->debug('Already updated');
1062
+ $value->response = array_merge($value->response, $extra_response);
1063
+ return $value;
1064
+ }
1065
+
1066
+ $extensions = $this->getTnpExtensions();
1067
+
1068
+ // Ops...
1069
+ if (!$extensions) {
1070
+ return $value;
1071
+ }
1072
+
1073
+ foreach ($extensions as $extension) {
1074
+ unset($value->response[$extension->wp_slug]);
1075
+ unset($value->no_update[$extension->wp_slug]);
1076
+ }
1077
+
1078
+ // Someone doesn't want our addons updated, let respect it (this constant should be defined in wp-config.php)
1079
+ if (!NEWSLETTER_EXTENSION_UPDATE) {
1080
+ //$this->logger->info('Updates disabled');
1081
+ return $value;
1082
+ }
1083
+
1084
+ include_once(ABSPATH . 'wp-admin/includes/plugin.php');
1085
+
1086
+ // Ok, that is really bad (should we remove it? is there a minimum WP version?)
1087
+ if (!function_exists('get_plugin_data')) {
1088
+ //$this->logger->error('No get_plugin_data function available!');
1089
+ return $value;
1090
+ }
1091
+
1092
+ $license_key = $this->get_license_key();
1093
+
1094
+ // Here we prepare the update information BUT do not add the link to the package which is privided
1095
+ // by our Addons Manager (due to WP policies)
1096
+ foreach ($extensions as $extension) {
1097
+
1098
+ // Patch for names convention
1099
+ $extension->plugin = $extension->wp_slug;
1100
+
1101
+ //$this->logger->debug('Processing ' . $extension->plugin);
1102
+ //$this->logger->debug($extension);
1103
+
1104
+ $plugin_data = false;
1105
+ if (file_exists(WP_PLUGIN_DIR . '/' . $extension->plugin)) {
1106
+ $plugin_data = get_plugin_data(WP_PLUGIN_DIR . '/' . $extension->plugin, false, false);
1107
+ } else if (file_exists(WPMU_PLUGIN_DIR . '/' . $extension->plugin)) {
1108
+ $plugin_data = get_plugin_data(WPMU_PLUGIN_DIR . '/' . $extension->plugin, false, false);
1109
+ }
1110
+
1111
+ if (!$plugin_data) {
1112
+ //$this->logger->debug('Seems not installed');
1113
+ continue;
1114
+ }
1115
+
1116
+ $plugin = new stdClass();
1117
+ $plugin->id = $extension->id;
1118
+ $plugin->slug = $extension->slug;
1119
+ $plugin->plugin = $extension->plugin;
1120
+ $plugin->new_version = $extension->version;
1121
+ $plugin->url = $extension->url;
1122
+ if (class_exists('NewsletterExtensions')) {
1123
+ // NO filters here!
1124
+ $plugin->package = NewsletterExtensions::$instance->get_package($extension->id, $license_key);
1125
+ } else {
1126
+ $plugin->package = '';
1127
+ }
1128
+ // [banners] => Array
1129
+ // (
1130
+ // [2x] => https://ps.w.org/wp-rss-aggregator/assets/banner-1544x500.png?rev=2040548
1131
+ // [1x] => https://ps.w.org/wp-rss-aggregator/assets/banner-772x250.png?rev=2040548
1132
+ // )
1133
+ // [icons] => Array
1134
+ // (
1135
+ // [2x] => https://ps.w.org/advanced-custom-fields/assets/icon-256x256.png?rev=1082746
1136
+ // [1x] => https://ps.w.org/advanced-custom-fields/assets/icon-128x128.png?rev=1082746
1137
+ // )
1138
+ if (version_compare($extension->version, $plugin_data['Version']) > 0) {
1139
+ //$this->logger->debug('There is a new version');
1140
+ $extra_response[$extension->plugin] = $plugin;
1141
+ } else {
1142
+ // Maybe useless...
1143
+ //$this->logger->debug('There is NOT a new version');
1144
+ $value->no_update[$extension->plugin] = $plugin;
1145
+ }
1146
+ //$this->logger->debug('Added');
1147
+ }
1148
+
1149
+ $value->response = array_merge($value->response, $extra_response);
1150
+
1151
+ return $value;
1152
+ }
1153
+
1154
+ /**
1155
+ * @deprecated since version 6.1.9
1156
+ */
1157
+ function get_extension_version($extension_id) {
1158
+ return null;
1159
+ }
1160
+
1161
+ /**
1162
+ * @deprecated since version 6.1.9
1163
+ */
1164
+ function set_extension_update_data($value, $extension) {
1165
+ return $value;
1166
+ }
1167
+
1168
+ /**
1169
+ * Retrieve the extensions form the tnp site
1170
+ * @return array
1171
+ */
1172
+ function getTnpExtensions() {
1173
+
1174
+ $extensions_json = get_transient('tnp_extensions_json');
1175
+
1176
+ if (empty($extensions_json)) {
1177
+ $url = "http://www.thenewsletterplugin.com/wp-content/extensions.json?ver=" . NEWSLETTER_VERSION;
1178
+ $extensions_response = wp_remote_get($url);
1179
+
1180
+ if (is_wp_error($extensions_response)) {
1181
+ // Cache anyway for blogs which cannot connect outside
1182
+ $extensions_json = '[]';
1183
+ set_transient('tnp_extensions_json', $extensions_json, 72 * 60 * 60);
1184
+ $this->logger->error($extensions_response);
1185
+ } else {
1186
+
1187
+ $extensions_json = wp_remote_retrieve_body($extensions_response);
1188
+
1189
+ // Not clear cases
1190
+ if (empty($extensions_json) || !json_decode($extensions_json)) {
1191
+ $this->logger->error('Invalid json from thenewsletterplugin.com: retrying in 72 hours');
1192
+ $this->logger->error('JSON: ' . $extensions_json);
1193
+ $extensions_json = '[]';
1194
+ }
1195
+ set_transient('tnp_extensions_json', $extensions_json, 72 * 60 * 60);
1196
+ }
1197
+ }
1198
+
1199
+ $extensions = json_decode($extensions_json);
1200
+
1201
+ return $extensions;
1202
+ }
1203
+
1204
+ function clear_extensions_cache() {
1205
+ delete_transient('tnp_extensions_json');
1206
+ }
1207
+
1208
+ var $panels = array();
1209
+
1210
+ function add_panel($key, $panel) {
1211
+ if (!isset($this->panels[$key]))
1212
+ $this->panels[$key] = array();
1213
+ if (!isset($panel['id']))
1214
+ $panel['id'] = sanitize_key($panel['label']);
1215
+ $this->panels[$key][] = $panel;
1216
+ }
1217
+
1218
+ function has_license() {
1219
+ return !empty($this->options['contract_key']);
1220
+ }
1221
+
1222
+ function get_sender_name() {
1223
+ return $this->options['sender_name'];
1224
+ }
1225
+
1226
+ function get_sender_email() {
1227
+ return $this->options['sender_email'];
1228
+ }
1229
+
1230
+ /**
1231
+ *
1232
+ * @return int
1233
+ */
1234
+ function get_newsletter_page_id() {
1235
+ return (int) $this->options['page'];
1236
+ }
1237
+
1238
+ /**
1239
+ *
1240
+ * @return WP_Post
1241
+ */
1242
+ function get_newsletter_page() {
1243
+ return get_post($this->get_newsletter_page_id());
1244
+ }
1245
+
1246
+ /**
1247
+ * Returns the Newsletter dedicated page URL or an alternative URL if that page if not
1248
+ * configured or not available.
1249
+ *
1250
+ * @staticvar string $url
1251
+ * @return string
1252
+ */
1253
+ function get_newsletter_page_url($language = '') {
1254
+
1255
+ $page = $this->get_newsletter_page();
1256
+
1257
+ if (!$page || $page->post_status !== 'publish') {
1258
+ return $this->build_action_url('m');
1259
+ }
1260
+
1261
+ $newsletter_page_url = get_permalink($page->ID);
1262
+ if ($language && $newsletter_page_url) {
1263
+ if (class_exists('SitePress')) {
1264
+ $newsletter_page_url = apply_filters('wpml_permalink', $newsletter_page_url, $language, true);
1265
+ }
1266
+ if (function_exists('pll_get_post')) {
1267
+ $translated_page = get_permalink(pll_get_post($page->ID, $language));
1268
+ if ($translated_page) {
1269
+ $newsletter_page_url = $translated_page;
1270
+ }
1271
+ }
1272
+ }
1273
+
1274
+ return $newsletter_page_url;
1275
+ }
1276
+
1277
+ function get_license_key() {
1278
+ if (defined('NEWSLETTER_LICENSE_KEY')) {
1279
+ return NEWSLETTER_LICENSE_KEY;
1280
+ } else {
1281
+ if (!empty($this->options['contract_key'])) {
1282
+ return trim($this->options['contract_key']);
1283
+ }
1284
+ }
1285
+ return false;
1286
+ }
1287
+
1288
+ /**
1289
+ * Get the data connected to the specified license code on man settings.
1290
+ *
1291
+ * - false if no license is present
1292
+ * - WP_Error if something went wrong if getting the license data
1293
+ * - object with expiration and addons list
1294
+ *
1295
+ * @param boolean $refresh
1296
+ * @return \WP_Error|boolean|object
1297
+ */
1298
+ function get_license_data($refresh = false) {
1299
+
1300
+ $this->logger->debug('Getting license data');
1301
+
1302
+ $license_key = $this->get_license_key();
1303
+ if (empty($license_key)) {
1304
+ $this->logger->debug('License was empty');
1305
+ delete_transient('newsletter_license_data');
1306
+ return false;
1307
+ }
1308
+
1309
+ if (!$refresh) {
1310
+ $license_data = get_transient('newsletter_license_data');
1311
+ if ($license_data !== false && is_object($license_data)) {
1312
+ $this->logger->debug('License data found on cache');
1313
+ return $license_data;
1314
+ }
1315
+ }
1316
+
1317
+ $this->logger->debug('Refreshing the license data');
1318
+
1319
+ $license_data_url = 'https://www.thenewsletterplugin.com/wp-content/plugins/file-commerce-pro/get-license-data.php';
1320
+
1321
+ $response = wp_remote_post($license_data_url, array(
1322
+ 'body' => array('k' => $license_key)
1323
+ ));
1324
+
1325
+ // Fall back to http...
1326
+ if (is_wp_error($response)) {
1327
+ $this->logger->error($response);
1328
+ $this->logger->error('Falling back to http');
1329
+ $license_data_url = str_replace('https', 'http', $license_data_url);
1330
+ $response = wp_remote_post($license_data_url, array(
1331
+ 'body' => array('k' => $license_key)
1332
+ ));
1333
+ if (is_wp_error($response)) {
1334
+ $this->logger->error($response);
1335
+ set_transient('newsletter_license_data', $response, DAY_IN_SECONDS);
1336
+ return $response;
1337
+ }
1338
+ }
1339
+
1340
+ $download_message = 'You can download all addons from www.thenewsletterplugin.com if your license is valid.';
1341
+
1342
+ if (wp_remote_retrieve_response_code($response) != '200') {
1343
+ $this->logger->error('license data error: ' . wp_remote_retrieve_response_code($response));
1344
+ $data = new WP_Error(wp_remote_retrieve_response_code($response), 'License validation service error. <br>' . $download_message);
1345
+ set_transient('newsletter_license_data', $data, DAY_IN_SECONDS);
1346
+ return $data;
1347
+ }
1348
+
1349
+ $json = wp_remote_retrieve_body($response);
1350
+ $data = json_decode($json);
1351
+
1352
+ if (!is_object($data)) {
1353
+ $this->logger->error($json);
1354
+ $data = new WP_Error(1, 'License validation service error. <br>' . $download_message);
1355
+ set_transient('newsletter_license_data', $data, DAY_IN_SECONDS);
1356
+ return $data;
1357
+ }
1358
+
1359
+ if (isset($data->message)) {
1360
+ $data = new WP_Error(1, $data->message . ' (check the license on Newsletter main settings)');
1361
+ set_transient('newsletter_license_data', $data, DAY_IN_SECONDS);
1362
+ return $data;
1363
+ }
1364
+
1365
+ $expiration = WEEK_IN_SECONDS;
1366
+ // If the license expires in few days, make the transient live only few days, so it will be refreshed
1367
+ if ($data->expire > time() && $data->expire - time() < WEEK_IN_SECONDS) {
1368
+ $expiration = $data->expire - time();
1369
+ }
1370
+ set_transient('newsletter_license_data', $data, $expiration);
1371
+
1372
+ return $data;
1373
+ }
1374
+
1375
+ /**
1376
+ * @deprecated
1377
+ * @param type $license_key
1378
+ * @return \WP_Error
1379
+ */
1380
+ public static function check_license($license_key) {
1381
+ $response = wp_remote_get('http://www.thenewsletterplugin.com/wp-content/plugins/file-commerce-pro/check.php?k=' . urlencode($license_key), array('sslverify' => false));
1382
+ if (is_wp_error($response)) {
1383
+ /* @var $response WP_Error */
1384
+ return new WP_Error(-1, 'It seems that your blog cannot contact the license validator. Ask your provider to unlock the HTTP/HTTPS connections to www.thenewsletterplugin.com<br>'
1385
+ . esc_html($response->get_error_code()) . ' - ' . esc_html($response->get_error_message()));
1386
+ } else if ($response['response']['code'] != 200) {
1387
+ return new WP_Error(-1, '[' . $response['response']['code'] . '] The license seems expired or not valid, please check your <a href="https://www.thenewsletterplugin.com/account">license code and status</a>, thank you.'
1388
+ . '<br>You can anyway download the professional extension from https://www.thenewsletterplugin.com.');
1389
+ } elseif ($expires = json_decode(wp_remote_retrieve_body($response))) {
1390
+ return array('expires' => $expires->expire, 'message' => 'Your license is valid and expires on ' . esc_html(date('Y-m-d', $expires->expire)));
1391
+ } else {
1392
+ return new WP_Error(-1, 'Unable to detect the license expiration. Debug data to report to the support: <code>' . esc_html(wp_remote_retrieve_body($response)) . '</code>');
1393
+ }
1394
+ }
1395
+
1396
+ function add_notice_to_chosen_profile_page_hook($post_states, $post) {
1397
+
1398
+ if ($post->ID == $this->options['page']) {
1399
+ $post_states[] = __('Newsletter plugin page, do not delete', 'newsletter');
1400
+ }
1401
+
1402
+ return $post_states;
1403
+ }
1404
+
1405
+ }
1406
+
1407
+ $newsletter = Newsletter::instance();
1408
+
1409
+ if (is_admin()) {
1410
+ require_once NEWSLETTER_DIR . '/system/system.php';
1411
+ }
1412
+
1413
+ require_once NEWSLETTER_DIR . '/subscription/subscription.php';
1414
+ require_once NEWSLETTER_DIR . '/unsubscription/unsubscription.php';
1415
+ require_once NEWSLETTER_DIR . '/profile/profile.php';
1416
+ require_once NEWSLETTER_DIR . '/emails/emails.php';
1417
+ require_once NEWSLETTER_DIR . '/users/users.php';
1418
+ require_once NEWSLETTER_DIR . '/statistics/statistics.php';
1419
+ require_once NEWSLETTER_DIR . '/widget/standard.php';
1420
+ require_once NEWSLETTER_DIR . '/widget/minimal.php';
readme.txt CHANGED
@@ -1,351 +1,359 @@
1
- === Newsletter ===
2
- Tags: newsletter, email marketing, welcome email, signup forms, contact, lead generation, marketing automation
3
- Tested up to: 5.8.1
4
- Stable tag: 7.3.1
5
- Contributors: satollo,webagile,michael-travan
6
- License: GPLv2 or later
7
- License URI: https://www.gnu.org/licenses/gpl-2.0.html
8
-
9
- Add a real newsletter system to your blog. For free. With unlimited newsletters and subscribers.
10
-
11
- == Description ==
12
-
13
- Newsletter is a **real newsletter and email marketing system** for your WordPress blog: perfect for list building, you can easily create, send and track e-mails, headache-free. It just works out of box!
14
-
15
- = Discover a completely rewritten composer =
16
-
17
- We redesigned our drag and drop composer to make your campaign creation even easier. Try it!
18
-
19
- = Main Features =
20
-
21
- * **Easy-to-use Drag and drop composer** to build responsive newsletters
22
- * **Unlimited subscribers** with statistics
23
- * **Unlimited newsletters** with tracking
24
- * **Subscription spam check** with domain/ip black lists, Akismet, captcha
25
- * **Delivery speed** fine control (from 12 emails per hour to as much as your blog can manage)
26
- * [WPML ready](https://www.thenewsletterplugin.com/documentation/multilanguage), [Polylang ready](https://www.thenewsletterplugin.com/documentation/multilanguage), [Translatepress ready](https://www.thenewsletterplugin.com/documentation/multilanguage)
27
- * All messages are **fully translatable** from administration panels (no .po/.mo file to edit)
28
- * [GDPR ready](https://www.thenewsletterplugin.com/documentation/gdpr-compliancy)
29
- * **Advanced targeting** with lists combinations like all in, at least one, not in and so on
30
- * Customizable **subscription widget**, **page** or **custom form**
31
- * Wordpress Users registration **seamless integration**
32
- * **Single** And **Double Opt-In** plus privacy checkbox for EU laws compliance
33
- * **Subscribers lists** to fine-target your campaigns
34
- * PHP API and REST API for coders and integrations
35
- * SMTP-Ready (with free addon)
36
- * Customizable Themes
37
- * **Status panel** to check your blog mailing capability and configuration
38
- * **Compatible with every SMTP plugin**: Post SMTP (aka Postman), WP Mail SMTP, Easy WP SMTP, Easy SMTP Mail, WP Mail Bank, ...
39
- * **Subscribers import** from file
40
- * Newsletter with Html and Text message versions
41
-
42
- = Find Us =
43
-
44
- Newsletter is a continuously evolving plugin. Stay tuned following us on [Facebook](https://www.facebook.com/thenewsletterplugin/) or [our site](https://www.thenewsletterplugin.com/).
45
-
46
- = Free Addons =
47
-
48
- Improve The Newsletter Plugin with these free addons:
49
-
50
- * [WP Registration Addon](https://www.thenewsletterplugin.com/documentation/wpusers-extension) - connects the WordPress standard and custom registration with Newsletter subscription. Optionally imports all registered users as subscribers.
51
- * [Archive Addon](https://www.thenewsletterplugin.com/documentation/archive-extension) - creates a simple blog page which lists all your sent newsletters
52
- * [Locked Content Addon](https://www.thenewsletterplugin.com/documentation/locked-content-extension) - open up your premium content only after subscription
53
- * [Newsletter REST API Addon](https://www.thenewsletterplugin.com/documentation/developers/newsletter-api-2/) - adds a tier of REST api to integrate with the Newsletter core services
54
- * [Sendinblue Addon](https://www.thenewsletterplugin.com/documentation/addons/delivery-addons/sendinblue-extension/) - deliver your newsletters with Sendinblue
55
- * [SMTP Addon](https://www.thenewsletterplugin.com/documentation/addons/delivery-addons/smtp-extension/) - deliver your newsletters with external SMTP
56
- * [Advanced Import Addon](https://www.thenewsletterplugin.com/documentation/addons/extended-features/advanced-import/) - import contact from file or copy and paste data with full mapping
57
-
58
- (*easily add them from our [Addons panel](https://www.thenewsletterplugin.com/documentation/install-extensions)*)
59
-
60
- = Addons on WordPress.org =
61
-
62
- * [RSS Composer Block](https://wordpress.org/plugins/newsletter-rss-block/) - (3rd party) a composer block which builds its content from a RSS feed
63
- * [Popup Maker Integration](https://wordpress.org/plugins/newsletter-popupmaker/) - (3rd party) integration of Newsletter forms with Popup Maker plugin
64
- * [BuddyPress integration](https://wordpress.org/plugins/newsletter-buddypress/) - subscription opt-in inside BuddyPress signup form
65
- * [WP User Manager addon for Newsletter](https://wordpress.org/plugins/wpum-newsletter/) - adds the subscription option on registration forms
66
-
67
- = Professional Addons =
68
-
69
- Need *more power*? Feel *something's missing*? The Newsletter Plugin features can be easily extended through our **premium, professional Addons**! Let us introduce just two of them : )
70
-
71
- * [Automated](https://www.thenewsletterplugin.com/automated) - generates and sends your newsletters using your blog last posts, even custom ones like events or products. Just sit and watch!
72
- * [Autoresponder](https://www.thenewsletterplugin.com/autoresponder) - creates email series to follow up your subscribers
73
- * [Extended Composer Blocks](https://www.thenewsletterplugin.com/composer) - adds new blocks to the drag & drop composer
74
- * [WooCommerce Integration](https://www.thenewsletterplugin.com/woocommerce) - subscribe customers to a mailing list and generate product newletters.
75
- * [Reports](https://www.thenewsletterplugin.com/reports) - improves the internal statistics collection system and provides better reports of data collected for each sent email. And retargeting. Neat.
76
- * [Leads](https://www.thenewsletterplugin.com/leads) adds a fancy subscription popup box or a fixed bar to your website that will boost your conversion rate
77
- * [Amazon SES and other mail providers integration](https://www.thenewsletterplugin.com/integrations) - seamlessly integrate Amazon SES and other email service providers with The Newsletter Plugin. Hassle-free.
78
- * [Contact Form 7 Integration](https://www.thenewsletterplugin.com/documentation/contact-form-7-extension) - integrate the subscription on Contact Form 7 forms
79
- * [Ninja Forms Integration](https://www.thenewsletterplugin.com/documentation/ninjaforms-extension) - integrate the subscription on Ninja Forms
80
- * [WP Forms Integration](https://www.thenewsletterplugin.com/documentation/wpforms-extension) - integrate the subscription on WP Forms
81
- * Events Manager and The Events Calendar (By Modern Tribe) integrations - easily add events to your newsletters
82
- * [Google Analytics](https://www.thenewsletterplugin.com/google-analytics) - track newsletter links with Google UTM tracking paramaters
83
- * [Subscribe on Comment](https://www.thenewsletterplugin.com/documentation/comments-extension) - adds the subscription option to your blog comment form
84
- * [Geolocation](https://www.thenewsletterplugin.com/documentation/geolocation-extension) - adds geolocation capability to target subscribers by location
85
-
86
- = GDPR =
87
-
88
- The Newsletter Plugin provides all the technical tools needed to achieve GDPR compliancy and we're continuously working to improve them and to give support even for specific use cases.
89
- The plugin does not collect users' own subscribers data, nor it has any access to those data: hence, we are not a data processor, so a data processing agreement is not needed.
90
- Anyway if you configure the plugin to use external services (usually an external mail delivery service) you should check with that service if some sort of agreement is required.
91
-
92
- = Support =
93
-
94
- We provide support for our plugin on [Wordpress.org forums](https://wordpress.org/support/plugin/newsletter) and through our [official forum](https://www.thenewsletterplugin.com/forums).
95
-
96
- Premium Users with an active license have access to one-to-one support via our [ticketing system](https://www.thenewsletterplugin.com/support-ticket).
97
-
98
- = Follow Us =
99
-
100
- * **Our Official Website** - [https://www.thenewsletterplugin.com/](https://www.thenewsletterplugin.com/)
101
- * **LinkedIn** - [https://www.linkedin.com/company/the-newsletter-plugin](https://www.linkedin.com/company/the-newsletter-plugin)
102
- * **Our Facebook Page** - [https://www.facebook.com/thenewsletterplugin](https://www.facebook.com/thenewsletterplugin)
103
- * **Our Twitter Account** - [https://twitter.com/newsletterwp](https://twitter.com/newsletterwp)
104
-
105
- == Frequently Asked Questions ==
106
-
107
- See the [Newsletter Forum](https://www.thenewsletterplugin.com/forums) to ask for help.
108
-
109
- For documentation start from [Newsletter documentation](https://www.thenewsletterplugin.com/documentation).
110
-
111
- Thank you, The Newsletter Team
112
-
113
- == Screenshots ==
114
-
115
- 1. The responsive email Drag & Drop composer
116
- 2. The plugin dashboard
117
- 3. The Reports extension
118
-
119
- == Changelog ==
120
-
121
- = 7.3.1 =
122
-
123
- * Dropped old mailers support
124
- * Improved sending process and limits checking
125
- * Removed obsolete notifications
126
- * Fixed the wrong report on single email delivery speed
127
- * Added support for custom sending speed by addons
128
- * Improved excerpt generation (but it still depends on plugins and themes...)
129
- * Support for building button option on composer blocks
130
-
131
- = 7.3.0 =
132
-
133
- * Fixed header block layout with (logo only layout)
134
- * Check for conflicts on newsletter saving
135
- * Added the subscriber complained status (actually not managed automatically)
136
-
137
- = 7.2.9 =
138
-
139
- * Fixed generic action button confirmation popup not showing the message
140
- * [SECURITY] Pre parsing of IP addresses on security panel
141
- * [SECURITY] Comments allowed on IP address list on security panel
142
- * [COMPOSER] Fixed preset name/subject
143
- * Improved generated forms for accessibility
144
-
145
- = 7.2.8 =
146
-
147
- * Fixed the print_date() when no time is provided
148
- * Fixed date alignment on posts block
149
- * Folders reorganization
150
- * Social block with a new set of icons
151
- * Boosted performances of the lists management page (for big databases)
152
- * Added a new scheduler diagnostic panel
153
- * Seriously improved the cron statistics and diagnostic panel (check it out!)
154
- * Removed obsolete migration code from ancient versions
155
-
156
- = 7.2.7 =
157
-
158
- * Fixed JS error on composer sometimes preventing the correct initialization
159
-
160
- = 7.2.6 =
161
-
162
- * Fixed links on test emails sent to a free email address
163
- * Added attachment explanation
164
- * Added link to explain the use of the snippet
165
-
166
- = 7.2.5 =
167
-
168
- * Fixed subject not saved under specific circumstance
169
-
170
- = 7.2.4 =
171
-
172
- * Fixed the composer not starting for blog with SSL plugin but still HTTP configured on main WP settings
173
- * Changed labels on subscriber maintenance panel
174
- * Updated requirements for WP version
175
-
176
- = 7.2.3 =
177
-
178
- * [COMPOSER] Added approx. indicators of the subsject visibile part in Apple and Android clients (experimental)
179
- * [COMPOSER] New mobile version view directly while composing (experimental)
180
- * [COMPOSER] New test email to test subscribers or to specific email address
181
- * [COMPOSER] Fixed missing background when creating a new message from a preset
182
- * [COMPOSER] Added media selector to the CTA block
183
- * [COMPOSER] Added reference to the tags on HTML and Text blocks
184
- * [COMPOSER] Move the snippet (preheader) field near the subject
185
- * [COMPOSER] Footer block with three link options: unsubscribe, manage and view online
186
- * [COMPOSER] Improve font coherence between blocks (by default)
187
- * [ANTISPAM] Improved the antispam checks on subscription
188
- * [GENERAL] Removed obsolete folders and code
189
- * [NEWSLETTERS] Refactored subject ideas selector
190
- * [SUBSCRIPTION] Inverted extra profile fields and lists on standard subscription form
191
- * [GENERAL] IP address extracted checking proxy variables
192
- * [GENERAL] Improved sending stats collection and display for the delivery engine (not related to click/open stats)
193
-
194
- = 7.2.2 =
195
-
196
- * [COMPOSER] Posts block excerpt removed when set to 0-length
197
- * [GENERAL]Added special characters on test message
198
- * [GENERAL]Added more specific error for action calls on System/Status panel
199
- * [SUBSCRIPTION] Check for the _wp_amp_action_xhr_converted parameter by AMP plugin
200
-
201
- = 7.2.1 =
202
-
203
- * [GENERAL] Added more detailed admin logging
204
- * [NEWSLETTERS] Fixed scheduled date sometimes reset to 1/1/1970
205
-
206
- = 7.2.0 =
207
-
208
- * [PROFILE] Fixed activation email on profile change
209
- * [NEWSLETTERS] Extended year selection on newsletter scheduling
210
- * [DELIVERY] Breaking change: old enqueue() and flush() methods have been removed
211
- * [GENERAL] Fixed alert message on some buttons
212
- * [SUBSCRIPTION] Fixed error message on multiple subscriptions (when not allowed)
213
- * [GENERAL] Fixed erratic error log line on main log
214
-
215
- = 7.1.9 =
216
-
217
- * [GENERAL] Removed the encodign defatlt to Base 64 when not specified since it seems incompatible with some SMTP plugins
218
- * [GENERAL] Review some controls layout and behavior
219
- * [DEBUG] Improved logging on database errors
220
- * [GENERAL] Added TikTok, Discord and Twitch socials
221
- * [GENERAL] Fixed odd error reported related to the cron call statistics collection
222
- * [DEBUG] Added a delivery diagnostic panel
223
-
224
- = 7.1.8 =
225
-
226
- * [COMPOSER] Fixed alignment of single big image on Outlook Android
227
-
228
- = 7.1.7 =
229
-
230
- * [GENERAL] Fix of permalink onm email with multilanguage plugins
231
-
232
- = 7.1.6 =
233
-
234
- * [COMPOSER] Fixed one column big image Read more links
235
-
236
- = 7.1.5 =
237
-
238
- * [COMPOSER] Improve buttons on posts layout
239
- * [COMPOSER] Improved header layout and logo
240
- * [COMPOSER] Generally improved block spacing
241
-
242
- = 7.1.4 =
243
-
244
- * [COMPOSER] Fixed image block for Outlook
245
- * [GENERAL] Fix undefined values in Gutenberg block
246
-
247
- = 7.1.3 =
248
-
249
- * [COMPOSER] Improvements on blocks layout compatibility
250
- * [COMPOSER] Fixed preset global options saving
251
- * [COMPOSER] Content regeneration on preset selection
252
- * [GENERAL] Added to System menu the Site Health link, a not well known native page of WordPress with system information
253
- * [GENERAL] Added sending statistics reset button on status panel
254
-
255
- = 7.1.2 =
256
-
257
- * [ADDONS] Fixed the addons list
258
-
259
- = 7.1.1 =
260
-
261
- * [GENERAL] Improved profile related functions
262
- * [GENERAL] Fixed check on field names (thanks Peter P.)
263
- * [COMPOSER] Fix on preset selection
264
- * [GENERAL] Fix ajax subscription on error
265
-
266
- = 7.1.0 =
267
-
268
- * [COMPOSER] Added link to tags documentation to inject subscriber's data
269
- * [COMPOSER] Fixed layout of posts block for Outlook 365
270
- * [GENERAL] Improved caching of addons json (even on error)
271
- * [GENERAL] Status menu changed to System/Status and System/Logs
272
- * [API] Fixed the subscriber status management (the API Addon should be updated as well)
273
- * [GENERAL] Fixed management of fatal errors on sending
274
- * [GENERAL] Limited error string per message to 250 chars
275
- * [GENERAL] Fixed check on field names (thanks Peter P.)
276
-
277
-
278
- = 7.0.9 =
279
-
280
- * [CAPTCHA] Fixed button label translation
281
- * [DELIVERY] Mailing fatal error management with newsletter stop and reporting
282
- * [SMTP] Marked obsolete the internal SMTP and made available the free SMTP addon
283
- * [DELIVERY] Better management of delivery fatal errors with a new "error" status for newsletters
284
- * [GENERAL] New log files panel
285
- * [IMPORT] Old import panel replaced by the new (really better) import addon (file, copy and paste, bounced addresses import)
286
- * [NEWSLETTERS] It's now possible to specify the sender name and email per newsletter (thanks to Matthew S.)
287
-
288
- = 7.0.8 =
289
-
290
- * [SUBSCRIBERS] Changed action buttons
291
- * [GENERAL] Reorganization of CSS and removal of unused files
292
- * [DASHBOARD] New window open for links and fix of invalid URLs
293
- * [NEWSLETTERS] New action buttons
294
- * [GENERAL] Minor fixes and optimizations
295
- * [COMPOSER] Fixed rare size error on gif images
296
-
297
- = 7.0.7 =
298
-
299
- * [COMPOSER] Fixed a warning in some inline editable blocks
300
- * [NEWSLETTERS] Fixed style which made list labels badly readable
301
- * [NEWSLETTERS] Fixed style which made bullet lists white (not on delivered newsletters)
302
- * [GENERAL] Added some references to the not working scheduler warning
303
-
304
- = 7.0.6 =
305
-
306
- * [COMPOSER] CTA block not grabbing settings from the "old" blocks
307
- * [COMPOSER] CSS issue on button settings group
308
- * [COMPOSER] Posts block two columns layout (author and padding)
309
- * [IMPORT] Removed the old low-featured import (the free import addon has everything needed!)
310
- * [GENERAL] Compatibility check with WP 5.7
311
-
312
- = 7.0.5 =
313
-
314
- * [COMPOSER] Hero CTA button not working
315
- * Fixed to the hero block button
316
- * Added support for the SMTP addon
317
-
318
- = 7.0.4 =
319
-
320
- * [COMPOSER] Redesigned drag and drop composer
321
- * [COMPOSER] NEW! Save drag and drop composed newsletters as templates to reuse easily
322
- * Redesigned dashboard
323
- * Several bug and fixes
324
-
325
- = 7.0.3 =
326
-
327
- * Option to choose between unsubscription and profile link in the footer block
328
- * Direct image src URL for image block
329
- * New media selector for blocks
330
- * Minor fixes
331
- * Updated codemirror
332
- * Updated default theme
333
- * Fixed tag filter on posts block (when tags have parathesis or like)
334
- * Fixed composer visualization of bullet points
335
-
336
- = 7.0.2 =
337
-
338
- * Fixed media 2x resize
339
-
340
- = 7.0.1 =
341
-
342
- * Fixed enforced lists by language with Polylang
343
-
344
- = 7.0.0 =
345
-
346
- * Added multiple newsletter selection for deletion
347
- * Added text part on welcome and activation email
348
- * Added the attribute "show_form" to "newsletter" shortcode
349
- * Added filter on subscriber saving (from external systems) with wrong list field values
350
- * Added index.html on log folder
351
-
 
 
 
 
 
 
 
 
1
+ === Newsletter ===
2
+ Tags: newsletter, email marketing, welcome email, signup forms, contact, lead generation, marketing automation
3
+ Tested up to: 5.8.2
4
+ Stable tag: 7.3.2
5
+ Contributors: satollo,webagile,michael-travan
6
+ License: GPLv2 or later
7
+ License URI: https://www.gnu.org/licenses/gpl-2.0.html
8
+
9
+ Add a real newsletter system to your blog. For free. With unlimited newsletters and subscribers.
10
+
11
+ == Description ==
12
+
13
+ Newsletter is a **real newsletter and email marketing system** for your WordPress blog: perfect for list building, you can easily create, send and track e-mails, headache-free. It just works out of box!
14
+
15
+ = Discover a completely rewritten composer =
16
+
17
+ We redesigned our drag and drop composer to make your campaign creation even easier. Try it!
18
+
19
+ = Main Features =
20
+
21
+ * **Easy-to-use Drag and drop composer** to build responsive newsletters
22
+ * **Unlimited subscribers** with statistics
23
+ * **Unlimited newsletters** with tracking
24
+ * **Subscription spam check** with domain/ip black lists, Akismet, captcha
25
+ * **Delivery speed** fine control (from 12 emails per hour to as much as your blog can manage)
26
+ * [WPML ready](https://www.thenewsletterplugin.com/documentation/multilanguage), [Polylang ready](https://www.thenewsletterplugin.com/documentation/multilanguage), [Translatepress ready](https://www.thenewsletterplugin.com/documentation/multilanguage)
27
+ * All messages are **fully translatable** from administration panels (no .po/.mo file to edit)
28
+ * [GDPR ready](https://www.thenewsletterplugin.com/documentation/gdpr-compliancy)
29
+ * **Advanced targeting** with lists combinations like all in, at least one, not in and so on
30
+ * Customizable **subscription widget**, **page** or **custom form**
31
+ * Wordpress Users registration **seamless integration**
32
+ * **Single** And **Double Opt-In** plus privacy checkbox for EU laws compliance
33
+ * **Subscribers lists** to fine-target your campaigns
34
+ * PHP API and REST API for coders and integrations
35
+ * SMTP-Ready (with free addon)
36
+ * Customizable Themes
37
+ * **Status panel** to check your blog mailing capability and configuration
38
+ * **Compatible with every SMTP plugin**: Post SMTP (aka Postman), WP Mail SMTP, Easy WP SMTP, Easy SMTP Mail, WP Mail Bank, ...
39
+ * **Subscribers import** from file
40
+ * Newsletter with Html and Text message versions
41
+
42
+ = Find Us =
43
+
44
+ Newsletter is a continuously evolving plugin. Stay tuned following us on [Facebook](https://www.facebook.com/thenewsletterplugin/) or [our site](https://www.thenewsletterplugin.com/).
45
+
46
+ = Free Addons =
47
+
48
+ Improve The Newsletter Plugin with these free addons:
49
+
50
+ * [WP Registration Addon](https://www.thenewsletterplugin.com/documentation/wpusers-extension) - connects the WordPress standard and custom registration with Newsletter subscription. Optionally imports all registered users as subscribers.
51
+ * [Archive Addon](https://www.thenewsletterplugin.com/documentation/archive-extension) - creates a simple blog page which lists all your sent newsletters
52
+ * [Locked Content Addon](https://www.thenewsletterplugin.com/documentation/locked-content-extension) - open up your premium content only after subscription
53
+ * [Newsletter REST API Addon](https://www.thenewsletterplugin.com/documentation/developers/newsletter-api-2/) - adds a tier of REST api to integrate with the Newsletter core services
54
+ * [Sendinblue Addon](https://www.thenewsletterplugin.com/documentation/addons/delivery-addons/sendinblue-extension/) - deliver your newsletters with Sendinblue
55
+ * [SMTP Addon](https://www.thenewsletterplugin.com/documentation/addons/delivery-addons/smtp-extension/) - deliver your newsletters with external SMTP
56
+ * [Advanced Import Addon](https://www.thenewsletterplugin.com/documentation/addons/extended-features/advanced-import/) - import contact from file or copy and paste data with full mapping
57
+
58
+ (*easily add them from our [Addons panel](https://www.thenewsletterplugin.com/documentation/install-extensions)*)
59
+
60
+ = Addons on WordPress.org =
61
+
62
+ * [RSS Composer Block](https://wordpress.org/plugins/newsletter-rss-block/) - (3rd party) a composer block which builds its content from a RSS feed
63
+ * [Popup Maker Integration](https://wordpress.org/plugins/newsletter-popupmaker/) - (3rd party) integration of Newsletter forms with Popup Maker plugin
64
+ * [BuddyPress integration](https://wordpress.org/plugins/newsletter-buddypress/) - subscription opt-in inside BuddyPress signup form
65
+ * [WP User Manager addon for Newsletter](https://wordpress.org/plugins/wpum-newsletter/) - adds the subscription option on registration forms
66
+
67
+ = Professional Addons =
68
+
69
+ Need *more power*? Feel *something's missing*? The Newsletter Plugin features can be easily extended through our **premium, professional Addons**! Let us introduce just two of them : )
70
+
71
+ * [Automated](https://www.thenewsletterplugin.com/automated) - generates and sends your newsletters using your blog last posts, even custom ones like events or products. Just sit and watch!
72
+ * [Autoresponder](https://www.thenewsletterplugin.com/autoresponder) - creates email series to follow up your subscribers
73
+ * [Extended Composer Blocks](https://www.thenewsletterplugin.com/composer) - adds new blocks to the drag & drop composer
74
+ * [WooCommerce Integration](https://www.thenewsletterplugin.com/woocommerce) - subscribe customers to a mailing list and generate product newletters.
75
+ * [Reports](https://www.thenewsletterplugin.com/reports) - improves the internal statistics collection system and provides better reports of data collected for each sent email. And retargeting. Neat.
76
+ * [Leads](https://www.thenewsletterplugin.com/leads) adds a fancy subscription popup box or a fixed bar to your website that will boost your conversion rate
77
+ * [Amazon SES and other mail providers integration](https://www.thenewsletterplugin.com/integrations) - seamlessly integrate Amazon SES and other email service providers with The Newsletter Plugin. Hassle-free.
78
+ * [Contact Form 7 Integration](https://www.thenewsletterplugin.com/documentation/contact-form-7-extension) - integrate the subscription on Contact Form 7 forms
79
+ * [Ninja Forms Integration](https://www.thenewsletterplugin.com/documentation/ninjaforms-extension) - integrate the subscription on Ninja Forms
80
+ * [WP Forms Integration](https://www.thenewsletterplugin.com/documentation/wpforms-extension) - integrate the subscription on WP Forms
81
+ * Events Manager and The Events Calendar (By Modern Tribe) integrations - easily add events to your newsletters
82
+ * [Google Analytics](https://www.thenewsletterplugin.com/google-analytics) - track newsletter links with Google UTM tracking paramaters
83
+ * [Subscribe on Comment](https://www.thenewsletterplugin.com/documentation/comments-extension) - adds the subscription option to your blog comment form
84
+ * [Geolocation](https://www.thenewsletterplugin.com/documentation/geolocation-extension) - adds geolocation capability to target subscribers by location
85
+
86
+ = GDPR =
87
+
88
+ The Newsletter Plugin provides all the technical tools needed to achieve GDPR compliancy and we're continuously working to improve them and to give support even for specific use cases.
89
+ The plugin does not collect users' own subscribers data, nor it has any access to those data: hence, we are not a data processor, so a data processing agreement is not needed.
90
+ Anyway if you configure the plugin to use external services (usually an external mail delivery service) you should check with that service if some sort of agreement is required.
91
+
92
+ = Support =
93
+
94
+ We provide support for our plugin on [Wordpress.org forums](https://wordpress.org/support/plugin/newsletter) and through our [official forum](https://www.thenewsletterplugin.com/forums).
95
+
96
+ Premium Users with an active license have access to one-to-one support via our [ticketing system](https://www.thenewsletterplugin.com/support-ticket).
97
+
98
+ = Follow Us =
99
+
100
+ * **Our Official Website** - [https://www.thenewsletterplugin.com/](https://www.thenewsletterplugin.com/)
101
+ * **LinkedIn** - [https://www.linkedin.com/company/the-newsletter-plugin](https://www.linkedin.com/company/the-newsletter-plugin)
102
+ * **Our Facebook Page** - [https://www.facebook.com/thenewsletterplugin](https://www.facebook.com/thenewsletterplugin)
103
+ * **Our Twitter Account** - [https://twitter.com/newsletterwp](https://twitter.com/newsletterwp)
104
+
105
+ == Frequently Asked Questions ==
106
+
107
+ See the [Newsletter Forum](https://www.thenewsletterplugin.com/forums) to ask for help.
108
+
109
+ For documentation start from [Newsletter documentation](https://www.thenewsletterplugin.com/documentation).
110
+
111
+ Thank you, The Newsletter Team
112
+
113
+ == Screenshots ==
114
+
115
+ 1. The responsive email Drag & Drop composer
116
+ 2. The plugin dashboard
117
+ 3. The Reports extension
118
+
119
+ == Changelog ==
120
+
121
+ = 7.3.2 =
122
+
123
+ * Fixed the remote ip retrieval and clean up
124
+ * Fixed header link to status page
125
+ * Fixed database error with too long IPs
126
+ * Fixed the subscription of cancelled addresses
127
+ * Fixed sender and name customization
128
+
129
+ = 7.3.1 =
130
+
131
+ * Dropped old mailers support
132
+ * Improved sending process and limits checking
133
+ * Removed obsolete notifications
134
+ * Fixed the wrong report on single email delivery speed
135
+ * Added support for custom sending speed by addons
136
+ * Improved excerpt generation (but it still depends on plugins and themes...)
137
+ * Support for building button option on composer blocks
138
+
139
+ = 7.3.0 =
140
+
141
+ * Fixed header block layout with (logo only layout)
142
+ * Check for conflicts on newsletter saving
143
+ * Added the subscriber complained status (actually not managed automatically)
144
+
145
+ = 7.2.9 =
146
+
147
+ * Fixed generic action button confirmation popup not showing the message
148
+ * [SECURITY] Pre parsing of IP addresses on security panel
149
+ * [SECURITY] Comments allowed on IP address list on security panel
150
+ * [COMPOSER] Fixed preset name/subject
151
+ * Improved generated forms for accessibility
152
+
153
+ = 7.2.8 =
154
+
155
+ * Fixed the print_date() when no time is provided
156
+ * Fixed date alignment on posts block
157
+ * Folders reorganization
158
+ * Social block with a new set of icons
159
+ * Boosted performances of the lists management page (for big databases)
160
+ * Added a new scheduler diagnostic panel
161
+ * Seriously improved the cron statistics and diagnostic panel (check it out!)
162
+ * Removed obsolete migration code from ancient versions
163
+
164
+ = 7.2.7 =
165
+
166
+ * Fixed JS error on composer sometimes preventing the correct initialization
167
+
168
+ = 7.2.6 =
169
+
170
+ * Fixed links on test emails sent to a free email address
171
+ * Added attachment explanation
172
+ * Added link to explain the use of the snippet
173
+
174
+ = 7.2.5 =
175
+
176
+ * Fixed subject not saved under specific circumstance
177
+
178
+ = 7.2.4 =
179
+
180
+ * Fixed the composer not starting for blog with SSL plugin but still HTTP configured on main WP settings
181
+ * Changed labels on subscriber maintenance panel
182
+ * Updated requirements for WP version
183
+
184
+ = 7.2.3 =
185
+
186
+ * [COMPOSER] Added approx. indicators of the subsject visibile part in Apple and Android clients (experimental)
187
+ * [COMPOSER] New mobile version view directly while composing (experimental)
188
+ * [COMPOSER] New test email to test subscribers or to specific email address
189
+ * [COMPOSER] Fixed missing background when creating a new message from a preset
190
+ * [COMPOSER] Added media selector to the CTA block
191
+ * [COMPOSER] Added reference to the tags on HTML and Text blocks
192
+ * [COMPOSER] Move the snippet (preheader) field near the subject
193
+ * [COMPOSER] Footer block with three link options: unsubscribe, manage and view online
194
+ * [COMPOSER] Improve font coherence between blocks (by default)
195
+ * [ANTISPAM] Improved the antispam checks on subscription
196
+ * [GENERAL] Removed obsolete folders and code
197
+ * [NEWSLETTERS] Refactored subject ideas selector
198
+ * [SUBSCRIPTION] Inverted extra profile fields and lists on standard subscription form
199
+ * [GENERAL] IP address extracted checking proxy variables
200
+ * [GENERAL] Improved sending stats collection and display for the delivery engine (not related to click/open stats)
201
+
202
+ = 7.2.2 =
203
+
204
+ * [COMPOSER] Posts block excerpt removed when set to 0-length
205
+ * [GENERAL]Added special characters on test message
206
+ * [GENERAL]Added more specific error for action calls on System/Status panel
207
+ * [SUBSCRIPTION] Check for the _wp_amp_action_xhr_converted parameter by AMP plugin
208
+
209
+ = 7.2.1 =
210
+
211
+ * [GENERAL] Added more detailed admin logging
212
+ * [NEWSLETTERS] Fixed scheduled date sometimes reset to 1/1/1970
213
+
214
+ = 7.2.0 =
215
+
216
+ * [PROFILE] Fixed activation email on profile change
217
+ * [NEWSLETTERS] Extended year selection on newsletter scheduling
218
+ * [DELIVERY] Breaking change: old enqueue() and flush() methods have been removed
219
+ * [GENERAL] Fixed alert message on some buttons
220
+ * [SUBSCRIPTION] Fixed error message on multiple subscriptions (when not allowed)
221
+ * [GENERAL] Fixed erratic error log line on main log
222
+
223
+ = 7.1.9 =
224
+
225
+ * [GENERAL] Removed the encodign defatlt to Base 64 when not specified since it seems incompatible with some SMTP plugins
226
+ * [GENERAL] Review some controls layout and behavior
227
+ * [DEBUG] Improved logging on database errors
228
+ * [GENERAL] Added TikTok, Discord and Twitch socials
229
+ * [GENERAL] Fixed odd error reported related to the cron call statistics collection
230
+ * [DEBUG] Added a delivery diagnostic panel
231
+
232
+ = 7.1.8 =
233
+
234
+ * [COMPOSER] Fixed alignment of single big image on Outlook Android
235
+
236
+ = 7.1.7 =
237
+
238
+ * [GENERAL] Fix of permalink onm email with multilanguage plugins
239
+
240
+ = 7.1.6 =
241
+
242
+ * [COMPOSER] Fixed one column big image Read more links
243
+
244
+ = 7.1.5 =
245
+
246
+ * [COMPOSER] Improve buttons on posts layout
247
+ * [COMPOSER] Improved header layout and logo
248
+ * [COMPOSER] Generally improved block spacing
249
+
250
+ = 7.1.4 =
251
+
252
+ * [COMPOSER] Fixed image block for Outlook
253
+ * [GENERAL] Fix undefined values in Gutenberg block
254
+
255
+ = 7.1.3 =
256
+
257
+ * [COMPOSER] Improvements on blocks layout compatibility
258
+ * [COMPOSER] Fixed preset global options saving
259
+ * [COMPOSER] Content regeneration on preset selection
260
+ * [GENERAL] Added to System menu the Site Health link, a not well known native page of WordPress with system information
261
+ * [GENERAL] Added sending statistics reset button on status panel
262
+
263
+ = 7.1.2 =
264
+
265
+ * [ADDONS] Fixed the addons list
266
+
267
+ = 7.1.1 =
268
+
269
+ * [GENERAL] Improved profile related functions
270
+ * [GENERAL] Fixed check on field names (thanks Peter P.)
271
+ * [COMPOSER] Fix on preset selection
272
+ * [GENERAL] Fix ajax subscription on error
273
+
274
+ = 7.1.0 =
275
+
276
+ * [COMPOSER] Added link to tags documentation to inject subscriber's data
277
+ * [COMPOSER] Fixed layout of posts block for Outlook 365
278
+ * [GENERAL] Improved caching of addons json (even on error)
279
+ * [GENERAL] Status menu changed to System/Status and System/Logs
280
+ * [API] Fixed the subscriber status management (the API Addon should be updated as well)
281
+ * [GENERAL] Fixed management of fatal errors on sending
282
+ * [GENERAL] Limited error string per message to 250 chars
283
+ * [GENERAL] Fixed check on field names (thanks Peter P.)
284
+
285
+
286
+ = 7.0.9 =
287
+
288
+ * [CAPTCHA] Fixed button label translation
289
+ * [DELIVERY] Mailing fatal error management with newsletter stop and reporting
290
+ * [SMTP] Marked obsolete the internal SMTP and made available the free SMTP addon
291
+ * [DELIVERY] Better management of delivery fatal errors with a new "error" status for newsletters
292
+ * [GENERAL] New log files panel
293
+ * [IMPORT] Old import panel replaced by the new (really better) import addon (file, copy and paste, bounced addresses import)
294
+ * [NEWSLETTERS] It's now possible to specify the sender name and email per newsletter (thanks to Matthew S.)
295
+
296
+ = 7.0.8 =
297
+
298
+ * [SUBSCRIBERS] Changed action buttons
299
+ * [GENERAL] Reorganization of CSS and removal of unused files
300
+ * [DASHBOARD] New window open for links and fix of invalid URLs
301
+ * [NEWSLETTERS] New action buttons
302
+ * [GENERAL] Minor fixes and optimizations
303
+ * [COMPOSER] Fixed rare size error on gif images
304
+
305
+ = 7.0.7 =
306
+
307
+ * [COMPOSER] Fixed a warning in some inline editable blocks
308
+ * [NEWSLETTERS] Fixed style which made list labels badly readable
309
+ * [NEWSLETTERS] Fixed style which made bullet lists white (not on delivered newsletters)
310
+ * [GENERAL] Added some references to the not working scheduler warning
311
+
312
+ = 7.0.6 =
313
+
314
+ * [COMPOSER] CTA block not grabbing settings from the "old" blocks
315
+ * [COMPOSER] CSS issue on button settings group
316
+ * [COMPOSER] Posts block two columns layout (author and padding)
317
+ * [IMPORT] Removed the old low-featured import (the free import addon has everything needed!)
318
+ * [GENERAL] Compatibility check with WP 5.7
319
+
320
+ = 7.0.5 =
321
+
322
+ * [COMPOSER] Hero CTA button not working
323
+ * Fixed to the hero block button
324
+ * Added support for the SMTP addon
325
+
326
+ = 7.0.4 =
327
+
328
+ * [COMPOSER] Redesigned drag and drop composer
329
+ * [COMPOSER] NEW! Save drag and drop composed newsletters as templates to reuse easily
330
+ * Redesigned dashboard
331
+ * Several bug and fixes
332
+
333
+ = 7.0.3 =
334
+
335
+ * Option to choose between unsubscription and profile link in the footer block
336
+ * Direct image src URL for image block
337
+ * New media selector for blocks
338
+ * Minor fixes
339
+ * Updated codemirror
340
+ * Updated default theme
341
+ * Fixed tag filter on posts block (when tags have parathesis or like)
342
+ * Fixed composer visualization of bullet points
343
+
344
+ = 7.0.2 =
345
+
346
+ * Fixed media 2x resize
347
+
348
+ = 7.0.1 =
349
+
350
+ * Fixed enforced lists by language with Polylang
351
+
352
+ = 7.0.0 =
353
+
354
+ * Added multiple newsletter selection for deletion
355
+ * Added text part on welcome and activation email
356
+ * Added the attribute "show_form" to "newsletter" shortcode
357
+ * Added filter on subscriber saving (from external systems) with wrong list field values
358
+ * Added index.html on log folder
359
+
subscription/subscription.php CHANGED
@@ -262,7 +262,6 @@ class NewsletterSubscription extends NewsletterModule {
262
  $this->save_options($profile_options, 'profile');
263
  $this->save_options($lists_options, 'lists');
264
 
265
-
266
  $default_options = $this->get_default_options();
267
 
268
  if (empty($this->options['error_text'])) {
@@ -542,21 +541,30 @@ class NewsletterSubscription extends NewsletterModule {
542
 
543
  // We cannot communicate with bounced addresses, there is no reason to proceed
544
  // TODO: Evaluate if the bounce status is very old, possible reset it
545
- if ($user->status == TNP_User::STATUS_BOUNCED) {
546
  return new WP_Error('bounced', 'Subscriber present and blocked');
547
  }
 
 
 
 
548
 
549
- // If the existing subscriber esists and is already confirmed, park the data until the new subscription is confirmed itself
550
- if ($user->status == TNP_User::STATUS_CONFIRMED && $subscription->optin == 'double') {
 
 
551
 
552
- set_transient('newsletter_subscription_' . $user->id, $subscription->data, 3600 * 48);
553
 
554
- // This status is *not* stored it indicate a temporary status to show the correct messages
555
- $user->status = TNP_User::STATUS_NOT_CONFIRMED;
556
 
557
- $this->send_message('confirmation', $user);
558
 
559
- return $user;
 
 
 
560
  }
561
 
562
  // Can be updated on the fly?
@@ -572,6 +580,8 @@ class NewsletterSubscription extends NewsletterModule {
572
  $user->status = $subscription->optin == 'single' ? TNP_User::STATUS_CONFIRMED : TNP_User::STATUS_NOT_CONFIRMED;
573
  $user->updated = time();
574
  }
 
 
575
 
576
  $user = apply_filters('newsletter_user_subscribe', $user);
577
 
@@ -644,7 +654,6 @@ class NewsletterSubscription extends NewsletterModule {
644
 
645
  $user = $this->get_user($email);
646
 
647
-
648
  if ($user != null) {
649
  // Email already registered in our database
650
  $this->logger->info('Subscription of an address with status ' . $user->status);
@@ -692,7 +701,6 @@ class NewsletterSubscription extends NewsletterModule {
692
 
693
  $user = $this->update_user_from_request($user);
694
 
695
-
696
  $user['token'] = $this->get_token();
697
  $ip = $this->process_ip($ip);
698
  $user['ip'] = $ip;
@@ -1318,10 +1326,10 @@ class NewsletterSubscription extends NewsletterModule {
1318
  function shortcode_newsletter_field($attrs, $content = '') {
1319
  // Counter to create unique ID for checkbox and labels
1320
  static $idx = 0;
1321
-
1322
  $idx++;
1323
  $attrs['id'] = 'tnp-' . $idx;
1324
-
1325
  $this->setup_form_options();
1326
  $language = $this->get_current_language();
1327
 
@@ -1334,7 +1342,7 @@ class NewsletterSubscription extends NewsletterModule {
1334
 
1335
  $buffer .= $this->_shortcode_label('email', $attrs);
1336
 
1337
- $buffer .= '<input class="tnp-email" type="email" name="ne" id="'. esc_attr($attrs['id']) . '" value=""';
1338
  if (isset($attrs['placeholder'])) {
1339
  $buffer .= ' placeholder="' . esc_attr($attrs['placeholder']) . '"';
1340
  }
@@ -1355,7 +1363,7 @@ class NewsletterSubscription extends NewsletterModule {
1355
  $buffer .= '<div class="tnp-field tnp-field-firstname">';
1356
  $buffer .= $this->_shortcode_label('name', $attrs);
1357
 
1358
- $buffer .= '<input class="tnp-name" type="text" name="nn" id="'. esc_attr($attrs['id']) . '" value=""';
1359
  if (isset($attrs['placeholder'])) {
1360
  $buffer .= ' placeholder="' . esc_attr($attrs['placeholder']) . '"';
1361
  }
@@ -1371,7 +1379,7 @@ class NewsletterSubscription extends NewsletterModule {
1371
  $buffer .= '<div class="tnp-field tnp-field-surname">';
1372
  $buffer .= $this->_shortcode_label('surname', $attrs);
1373
 
1374
- $buffer .= '<input class="tnp-surname" type="text" name="ns" id="'. esc_attr($attrs['id']) . '" value=""';
1375
  if (isset($attrs['placeholder'])) {
1376
  $buffer .= ' placeholder="' . esc_attr($attrs['placeholder']) . '"';
1377
  }
@@ -1645,7 +1653,7 @@ class NewsletterSubscription extends NewsletterModule {
1645
  if (isset($options_profile['sex_status']) && $options_profile['sex_status'] == 2) {
1646
  $buffer .= $this->shortcode_newsletter_field(['name' => 'gender']);
1647
  }
1648
-
1649
  // Extra profile fields
1650
  for ($i = 1; $i <= NEWSLETTER_PROFILE_MAX; $i++) {
1651
  // Not for subscription form
@@ -1670,7 +1678,7 @@ class NewsletterSubscription extends NewsletterModule {
1670
  $buffer .= $this->shortcode_newsletter_field(['name' => 'lists']);
1671
  }
1672
 
1673
-
1674
 
1675
  // Deprecated
1676
  $extra = apply_filters('newsletter_subscription_extra', array());
@@ -1785,7 +1793,6 @@ class NewsletterSubscription extends NewsletterModule {
1785
 
1786
  $form = '';
1787
 
1788
-
1789
  $form .= '<div class="tnp tnp-subscription-minimal ' . $attrs['class'] . '">';
1790
  $form .= '<form action="' . esc_attr($this->build_action_url('s')) . '" method="post">';
1791
 
262
  $this->save_options($profile_options, 'profile');
263
  $this->save_options($lists_options, 'lists');
264
 
 
265
  $default_options = $this->get_default_options();
266
 
267
  if (empty($this->options['error_text'])) {
541
 
542
  // We cannot communicate with bounced addresses, there is no reason to proceed
543
  // TODO: Evaluate if the bounce status is very old, possible reset it
544
+ if ($user->status == TNP_User::STATUS_BOUNCED || $user->status == TNP_User::STATUS_COMPLAINED) {
545
  return new WP_Error('bounced', 'Subscriber present and blocked');
546
  }
547
+
548
+ if ($user->status == TNP_User::STATUS_UNSUBSCRIBED) {
549
+ // Special behavior?
550
+ }
551
 
552
+ if ($subscription->optin == 'single') {
553
+ $user->status = TNP_User::STATUS_CONFIRMED;
554
+ } else {
555
+ if ($user->status == TNP_User::STATUS_CONFIRMED) {
556
 
557
+ set_transient('newsletter_subscription_' . $user->id, $subscription->data, 3600 * 48);
558
 
559
+ // This status is *not* stored it indicate a temporary status to show the correct messages
560
+ $user->status = TNP_User::STATUS_NOT_CONFIRMED;
561
 
562
+ $this->send_message('confirmation', $user);
563
 
564
+ return $user;
565
+ } else {
566
+ $user->status = TNP_User::STATUS_NOT_CONFIRMED;
567
+ }
568
  }
569
 
570
  // Can be updated on the fly?
580
  $user->status = $subscription->optin == 'single' ? TNP_User::STATUS_CONFIRMED : TNP_User::STATUS_NOT_CONFIRMED;
581
  $user->updated = time();
582
  }
583
+
584
+ $user->ip = $this->process_ip($user->ip);
585
 
586
  $user = apply_filters('newsletter_user_subscribe', $user);
587
 
654
 
655
  $user = $this->get_user($email);
656
 
 
657
  if ($user != null) {
658
  // Email already registered in our database
659
  $this->logger->info('Subscription of an address with status ' . $user->status);
701
 
702
  $user = $this->update_user_from_request($user);
703
 
 
704
  $user['token'] = $this->get_token();
705
  $ip = $this->process_ip($ip);
706
  $user['ip'] = $ip;
1326
  function shortcode_newsletter_field($attrs, $content = '') {
1327
  // Counter to create unique ID for checkbox and labels
1328
  static $idx = 0;
1329
+
1330
  $idx++;
1331
  $attrs['id'] = 'tnp-' . $idx;
1332
+
1333
  $this->setup_form_options();
1334
  $language = $this->get_current_language();
1335
 
1342
 
1343
  $buffer .= $this->_shortcode_label('email', $attrs);
1344
 
1345
+ $buffer .= '<input class="tnp-email" type="email" name="ne" id="' . esc_attr($attrs['id']) . '" value=""';
1346
  if (isset($attrs['placeholder'])) {
1347
  $buffer .= ' placeholder="' . esc_attr($attrs['placeholder']) . '"';
1348
  }
1363
  $buffer .= '<div class="tnp-field tnp-field-firstname">';
1364
  $buffer .= $this->_shortcode_label('name', $attrs);
1365
 
1366
+ $buffer .= '<input class="tnp-name" type="text" name="nn" id="' . esc_attr($attrs['id']) . '" value=""';
1367
  if (isset($attrs['placeholder'])) {
1368
  $buffer .= ' placeholder="' . esc_attr($attrs['placeholder']) . '"';
1369
  }
1379
  $buffer .= '<div class="tnp-field tnp-field-surname">';
1380
  $buffer .= $this->_shortcode_label('surname', $attrs);
1381
 
1382
+ $buffer .= '<input class="tnp-surname" type="text" name="ns" id="' . esc_attr($attrs['id']) . '" value=""';
1383
  if (isset($attrs['placeholder'])) {
1384
  $buffer .= ' placeholder="' . esc_attr($attrs['placeholder']) . '"';
1385
  }
1653
  if (isset($options_profile['sex_status']) && $options_profile['sex_status'] == 2) {
1654
  $buffer .= $this->shortcode_newsletter_field(['name' => 'gender']);
1655
  }
1656
+
1657
  // Extra profile fields
1658
  for ($i = 1; $i <= NEWSLETTER_PROFILE_MAX; $i++) {
1659
  // Not for subscription form
1678
  $buffer .= $this->shortcode_newsletter_field(['name' => 'lists']);
1679
  }
1680
 
1681
+
1682
 
1683
  // Deprecated
1684
  $extra = apply_filters('newsletter_subscription_extra', array());
1793
 
1794
  $form = '';
1795
 
 
1796
  $form .= '<div class="tnp tnp-subscription-minimal ' . $attrs['class'] . '">';
1797
  $form .= '<form action="' . esc_attr($this->build_action_url('s')) . '" method="post">';
1798
 
tnp-header.php CHANGED
@@ -226,7 +226,6 @@ $warning |= empty($status_options['mail']);
226
  <a href="?page=<?php echo $p ?>"><i class="fas fa-check-square"></i> <?php _e('License active', 'newsletter') ?></a>
227
  </li>
228
 
229
-
230
  <?php } ?>
231
  </ul>
232
  </div>
226
  <a href="?page=<?php echo $p ?>"><i class="fas fa-check-square"></i> <?php _e('License active', 'newsletter') ?></a>
227
  </li>
228
 
 
229
  <?php } ?>
230
  </ul>
231
  </div>