Newsletter - Version 5.4.9

Version Description

  • Lists management in APIs
  • Code cleanup
  • New subscribers data export controls
  • New global check and notice if the dedicated page is misconfigured
  • Fix privacy note display on profile page even without a privacy url set
Download this release

Release Info

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

Code changes from version 5.4.8 to 5.4.9

admin.css CHANGED
@@ -724,7 +724,7 @@ p.description {
724
  border-color: #28313C;
725
  border-width: 10px;
726
  border-style: solid;
727
- overflow: auto;
728
  }
729
 
730
  .tnp-darkbg {
@@ -791,6 +791,12 @@ p.description {
791
  color: #ccc;
792
  }
793
 
 
 
 
 
 
 
794
  #tnp-heading .tnp-btn-h1 {
795
  color: #fff;
796
  background-color: #3498db;
724
  border-color: #28313C;
725
  border-width: 10px;
726
  border-style: solid;
727
+ /*overflow: auto;*/
728
  }
729
 
730
  .tnp-darkbg {
791
  color: #ccc;
792
  }
793
 
794
+ /* Style for WP global notices */
795
+ #tnp-heading .notice p {
796
+ margin: 0.5em 0;
797
+ padding: 2px;
798
+ }
799
+
800
  #tnp-heading .tnp-btn-h1 {
801
  color: #fff;
802
  background-color: #3498db;
emails/edit.php CHANGED
@@ -258,6 +258,11 @@ if (isset($controls->data['options_status']) && $controls->data['options_status'
258
  $controls->warnings[] = __('This newsletter will be sent to not confirmed subscribers.', 'newsletter');
259
  }
260
 
 
 
 
 
 
261
  /*
262
  $host = parse_url(home_url(), PHP_URL_HOST);
263
  $parts = array_reverse(explode('.', $host));
258
  $controls->warnings[] = __('This newsletter will be sent to not confirmed subscribers.', 'newsletter');
259
  }
260
 
261
+ if (strpos($controls->data['message'], '{profile_url}') === false && strpos($controls->data['message'], '{unsubscription_url}') === false
262
+ && strpos($controls->data['message'], '{unsubscription_confirm_url}') === false) {
263
+ $controls->warnings[] = __('The message is missing the subscriber profile or cancellation link.', 'newsletter');
264
+ }
265
+
266
  /*
267
  $host = parse_url(home_url(), PHP_URL_HOST);
268
  $parts = array_reverse(explode('.', $host));
emails/tnp-composer/_css/newsletter-builder.css CHANGED
@@ -479,6 +479,13 @@
479
  margin-top: 10px;
480
  }
481
 
 
 
 
 
 
 
 
482
 
483
  .fake-browser-ui {
484
  padding: 30px 0 0;
479
  margin-top: 10px;
480
  }
481
 
482
+ .tnpc-preview .fake-browser-ui iframe {
483
+ width: 700px;
484
+ }
485
+
486
+ .tnpc-preview .fake-mobile-browser-ui iframe {
487
+ width: 320px;
488
+ }
489
 
490
  .fake-browser-ui {
491
  padding: 30px 0 0;
includes/TNP.php CHANGED
@@ -21,20 +21,20 @@ class TNP {
21
  */
22
 
23
  public static function subscribe($params) {
24
-
25
  // error_reporting(E_ALL);
26
-
27
  $newsletter = Newsletter::instance();
28
  $subscription = NewsletterSubscription::instance();
29
-
30
  // Messages
31
  $options = get_option('newsletter', array());
32
 
33
  // Form field configuration
34
  $options_profile = get_option('newsletter_profile', array());
35
-
36
  $optin = (int) $options['noconfirmation']; // 0 - double, 1 - single
37
-
38
  $email = $newsletter->normalize_email(stripslashes($params['email']));
39
 
40
  // Should never reach this point without a valid email address
@@ -43,9 +43,9 @@ class TNP {
43
  }
44
 
45
  $user = $newsletter->get_user($email);
46
-
47
  if ($user != null) {
48
-
49
  $newsletter->logger->info('Subscription of an address with status ' . $user->status);
50
 
51
  // Bounced
@@ -69,7 +69,7 @@ class TNP {
69
  $prefix = 'confirmation_';
70
 
71
  if (empty($options[$prefix . 'disabled'])) {
72
-
73
  $message = $options[$prefix . 'message'];
74
  // TODO: This is always empty!
75
  //$message_text = $options[$prefix . 'message_text'];
@@ -82,7 +82,7 @@ class TNP {
82
  }
83
  }
84
  }
85
-
86
  if ($user != null) {
87
  $newsletter->logger->info("Email address subscribed but not confirmed");
88
  $user = array('id' => $user->id);
@@ -96,15 +96,41 @@ class TNP {
96
  $params['status'] = 'S';
97
  }
98
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
99
  $user = TNP::add_subscriber($params);
100
 
101
- // TODO: decidere se applicare i filtri sulle API
102
- // $user = apply_filters('newsletter_user_subscribe', $user);
103
 
104
  if (is_wp_error($user)) {
105
  return ($user);
106
  }
107
-
108
  // Notification to admin (only for new confirmed subscriptions)
109
  if ($user->status == 'C') {
110
  do_action('newsletter_user_confirmed', $user);
@@ -117,7 +143,7 @@ class TNP {
117
  }
118
 
119
  $prefix = ($user->status == 'C') ? 'confirmed_' : 'confirmation_';
120
-
121
  if (empty($options[$prefix . 'disabled'])) {
122
  $message = $options[$prefix . 'message'];
123
 
@@ -132,7 +158,6 @@ class TNP {
132
  $newsletter->mail($user->email, $newsletter->replace($subject, $user), $newsletter->replace($message, $user));
133
  }
134
  return $user;
135
-
136
  }
137
 
138
  /*
@@ -154,12 +179,12 @@ class TNP {
154
  return $user;
155
  }
156
 
157
- $newsletter->set_user_status($user->id, 'U');
158
 
159
- if (empty($newsletter->options['unsubscribed_disabled'])) {
160
  $newsletter->mail($user->email, $newsletter->replace($newsletter->options['unsubscribed_subject'], $user), $newsletter->replace($newsletter->options['unsubscribed_message'], $user));
161
  }
162
- $newsletter->notify_admin($user, 'Newsletter unsubscription');
163
 
164
  return $user;
165
  }
@@ -198,12 +223,21 @@ class TNP {
198
  $user['sex'] = $newsletter->normalize_sex($params['gender']);
199
  }
200
 
201
- if (is_array($params['profile'])) {
202
  foreach ($params['profile'] as $key => $value) {
203
  $user['profile_' . $key] = trim(stripslashes($value));
204
  }
205
  }
206
 
 
 
 
 
 
 
 
 
 
207
  if (!empty($params['status'])) {
208
  $user['status'] = $params['status'];
209
  } else {
@@ -217,7 +251,7 @@ class TNP {
217
 
218
  return $user;
219
  }
220
-
221
  /*
222
  * Subscribers list
223
  */
@@ -226,10 +260,10 @@ class TNP {
226
 
227
  global $wpdb;
228
  $newsletter = Newsletter::instance();
229
-
230
  $items_per_page = 20;
231
  $where = "";
232
-
233
  $query = "select name, email from " . NEWSLETTER_USERS_TABLE . ' ' . $where . " order by id desc";
234
  $query .= " limit 0," . $items_per_page;
235
  $list = $wpdb->get_results($query);
@@ -259,7 +293,7 @@ class TNP {
259
  return new WP_Error('-1', $wpdb->last_error, array('status' => 400));
260
  }
261
  }
262
-
263
  /*
264
  * Newsletters list
265
  */
@@ -267,20 +301,19 @@ class TNP {
267
  public static function newsletters($params) {
268
 
269
  global $wpdb;
270
-
271
  $list = $wpdb->get_results("SELECT id, subject, created, status, total, sent, send_on FROM " . NEWSLETTER_EMAILS_TABLE . " ORDER BY id DESC LIMIT 10", OBJECT);
272
-
273
  if ($wpdb->last_error) {
274
  $this->logger->error($wpdb->last_error);
275
  return false;
276
  }
277
-
278
  if (empty($list)) {
279
  return array();
280
  }
281
-
282
  return $list;
283
-
284
  }
285
 
286
  }
21
  */
22
 
23
  public static function subscribe($params) {
24
+
25
  // error_reporting(E_ALL);
26
+
27
  $newsletter = Newsletter::instance();
28
  $subscription = NewsletterSubscription::instance();
29
+
30
  // Messages
31
  $options = get_option('newsletter', array());
32
 
33
  // Form field configuration
34
  $options_profile = get_option('newsletter_profile', array());
35
+
36
  $optin = (int) $options['noconfirmation']; // 0 - double, 1 - single
37
+
38
  $email = $newsletter->normalize_email(stripslashes($params['email']));
39
 
40
  // Should never reach this point without a valid email address
43
  }
44
 
45
  $user = $newsletter->get_user($email);
46
+
47
  if ($user != null) {
48
+
49
  $newsletter->logger->info('Subscription of an address with status ' . $user->status);
50
 
51
  // Bounced
69
  $prefix = 'confirmation_';
70
 
71
  if (empty($options[$prefix . 'disabled'])) {
72
+
73
  $message = $options[$prefix . 'message'];
74
  // TODO: This is always empty!
75
  //$message_text = $options[$prefix . 'message_text'];
82
  }
83
  }
84
  }
85
+
86
  if ($user != null) {
87
  $newsletter->logger->info("Email address subscribed but not confirmed");
88
  $user = array('id' => $user->id);
96
  $params['status'] = 'S';
97
  }
98
 
99
+ // Lists
100
+
101
+ if (!isset($params['lists']) || !is_array($params['lists'])) {
102
+ $params['lists'] = array();
103
+ }
104
+
105
+ // Public lists: rebuild the array keeping only the valid lists
106
+ $lists = $newsletter->get_lists_public();
107
+
108
+ // Public list IDs
109
+ $public_lists = array();
110
+ foreach ($lists as $list) {
111
+ $public_lists[] = $list->id;
112
+ }
113
+
114
+ // Keep only the public lists
115
+ $params['lists'] = array_intersect($public_lists, $params['lists']);
116
+
117
+ // Pre assigned lists
118
+ $lists = $newsletter->get_lists();
119
+ foreach ($lists as $list) {
120
+ if ($list->forced) {
121
+ $params['lists'][] = $list->id;
122
+ }
123
+ }
124
+
125
+ apply_filters('newsletter_api_subscribe', $params);
126
+
127
  $user = TNP::add_subscriber($params);
128
 
 
 
129
 
130
  if (is_wp_error($user)) {
131
  return ($user);
132
  }
133
+
134
  // Notification to admin (only for new confirmed subscriptions)
135
  if ($user->status == 'C') {
136
  do_action('newsletter_user_confirmed', $user);
143
  }
144
 
145
  $prefix = ($user->status == 'C') ? 'confirmed_' : 'confirmation_';
146
+
147
  if (empty($options[$prefix . 'disabled'])) {
148
  $message = $options[$prefix . 'message'];
149
 
158
  $newsletter->mail($user->email, $newsletter->replace($subject, $user), $newsletter->replace($message, $user));
159
  }
160
  return $user;
 
161
  }
162
 
163
  /*
179
  return $user;
180
  }
181
 
182
+ $user = $newsletter->set_user_status($user, 'U');
183
 
184
+ if (empty(NewsletterSubscription::instance()->options['unsubscribed_disabled'])) {
185
  $newsletter->mail($user->email, $newsletter->replace($newsletter->options['unsubscribed_subject'], $user), $newsletter->replace($newsletter->options['unsubscribed_message'], $user));
186
  }
187
+ NewsletterSubscription::instance()->notify_admin($user, 'Newsletter unsubscription');
188
 
189
  return $user;
190
  }
223
  $user['sex'] = $newsletter->normalize_sex($params['gender']);
224
  }
225
 
226
+ if (isset($params['profile']) && is_array($params['profile'])) {
227
  foreach ($params['profile'] as $key => $value) {
228
  $user['profile_' . $key] = trim(stripslashes($value));
229
  }
230
  }
231
 
232
+ // Lists (an arrayunder the key "lists")
233
+ // Preferences (field names are nl[] and values the list number so special forms with radio button can work)
234
+ if (isset($params['lists']) && is_array($params['lists'])) {
235
+ foreach ($params['lists'] as $list_id) {
236
+ $user['list_' . ((int)$list_id)] = 1;
237
+ }
238
+ }
239
+
240
+
241
  if (!empty($params['status'])) {
242
  $user['status'] = $params['status'];
243
  } else {
251
 
252
  return $user;
253
  }
254
+
255
  /*
256
  * Subscribers list
257
  */
260
 
261
  global $wpdb;
262
  $newsletter = Newsletter::instance();
263
+
264
  $items_per_page = 20;
265
  $where = "";
266
+
267
  $query = "select name, email from " . NEWSLETTER_USERS_TABLE . ' ' . $where . " order by id desc";
268
  $query .= " limit 0," . $items_per_page;
269
  $list = $wpdb->get_results($query);
293
  return new WP_Error('-1', $wpdb->last_error, array('status' => 400));
294
  }
295
  }
296
+
297
  /*
298
  * Newsletters list
299
  */
301
  public static function newsletters($params) {
302
 
303
  global $wpdb;
304
+
305
  $list = $wpdb->get_results("SELECT id, subject, created, status, total, sent, send_on FROM " . NEWSLETTER_EMAILS_TABLE . " ORDER BY id DESC LIMIT 10", OBJECT);
306
+
307
  if ($wpdb->last_error) {
308
  $this->logger->error($wpdb->last_error);
309
  return false;
310
  }
311
+
312
  if (empty($list)) {
313
  return array();
314
  }
315
+
316
  return $list;
 
317
  }
318
 
319
  }
includes/controls.php CHANGED
@@ -414,12 +414,12 @@ class NewsletterControls {
414
  if ($value == 0) {
415
  echo ' selected';
416
  }
417
- echo '>No</option>';
418
  echo '<option value="1"';
419
  if ($value == 1) {
420
  echo ' selected';
421
  }
422
- echo '>Yes</option>';
423
  echo '</select>&nbsp;&nbsp;&nbsp;';
424
  }
425
 
@@ -431,12 +431,12 @@ class NewsletterControls {
431
  if ($value == 0) {
432
  echo ' selected';
433
  }
434
- echo '>Disabled</option>';
435
  echo '<option value="1"';
436
  if ($value == 1) {
437
  echo ' selected';
438
  }
439
- echo '>Enabled</option>';
440
  echo '</select>';
441
  }
442
 
414
  if ($value == 0) {
415
  echo ' selected';
416
  }
417
+ echo '>', __('No', 'newsletter'), '</option>';
418
  echo '<option value="1"';
419
  if ($value == 1) {
420
  echo ' selected';
421
  }
422
+ echo '>', __('Yes', 'newsletter'), '</option>';
423
  echo '</select>&nbsp;&nbsp;&nbsp;';
424
  }
425
 
431
  if ($value == 0) {
432
  echo ' selected';
433
  }
434
+ echo '>', __('Disabled', 'newsletter'), '</option>';
435
  echo '<option value="1"';
436
  if ($value == 1) {
437
  echo ' selected';
438
  }
439
+ echo '>', __('Enabled', 'newsletter'), '</option>';
440
  echo '</select>';
441
  }
442
 
includes/module.php CHANGED
@@ -11,10 +11,10 @@ defined('ABSPATH') || exit;
11
  * */
12
  abstract class TNP_List {
13
 
14
- const STAUTS_PRIVATE = 0;
15
- const STAUTS_PUBLIC = 1;
16
- const STAUTS_PROFILE_ONLY = 2;
17
- const STAUTS_HIDDEN = 3; // Public but never show (can be set with a hidden form field)
18
 
19
  }
20
 
@@ -27,10 +27,10 @@ abstract class TNP_List {
27
  * */
28
  abstract class TNP_User {
29
 
30
- const STAUTS_CONFIRMED = 'C';
31
- const STAUTS_NOT_CONFIRMED = 'S';
32
- const STAUTS_UNSUBSCRIBED = 'U';
33
- const STAUTS_BOUNCED = 'B';
34
 
35
  }
36
 
@@ -816,6 +816,15 @@ class NewsletterModule {
816
  return '-';
817
  }
818
 
 
 
 
 
 
 
 
 
 
819
  /** Searches for a user using the nk parameter or the ni and nt parameters. Tries even with the newsletter cookie.
820
  * If found, the user object is returned or null.
821
  * The user is returned without regards to his status that should be checked by caller.
@@ -869,7 +878,7 @@ class NewsletterModule {
869
 
870
  if ($wpdb->last_error) {
871
  $this->logger->error($wpdb->last_error);
872
- return false;
873
  }
874
  return $r;
875
  }
@@ -900,7 +909,7 @@ class NewsletterModule {
900
 
901
  if ($user == null || $token != $user->token) {
902
  if ($die_on_fail) {
903
- die('No subscriber found.');
904
  } else {
905
  return null;
906
  }
@@ -914,8 +923,9 @@ class NewsletterModule {
914
  */
915
  function get_lists() {
916
  static $lists = null;
917
- if (is_array($lists))
918
  return $lists;
 
919
 
920
  $lists = array();
921
  $data = NewsletterSubscription::instance()->options_lists;
@@ -947,7 +957,7 @@ class NewsletterModule {
947
  $lists = array();
948
  $all = $this->get_lists();
949
  foreach ($all as $list) {
950
- if ($list->status == 0) {
951
  continue;
952
  }
953
  $lists[] = $list;
@@ -969,7 +979,7 @@ class NewsletterModule {
969
  $lists = array();
970
  $all = $this->get_lists();
971
  foreach ($all as $list) {
972
- if ($list->status != 2 || $list->forced) {
973
  continue;
974
  }
975
  $lists[] = $list;
@@ -978,6 +988,7 @@ class NewsletterModule {
978
  }
979
 
980
  /**
 
981
  *
982
  * @return TNP_List[]
983
  */
@@ -990,7 +1001,7 @@ class NewsletterModule {
990
  $lists = array();
991
  $all = $this->get_lists();
992
  foreach ($all as $list) {
993
- if ($list->status == 0 || $list->status == 3) {
994
  continue;
995
  }
996
  $lists[] = $list;
@@ -999,13 +1010,12 @@ class NewsletterModule {
999
  }
1000
 
1001
  /**
 
1002
  *
1003
- * @global wpdb $wpdb
1004
  * @param int $id
1005
  * @return TNP_List
1006
  */
1007
  function get_list($id) {
1008
- global $wpdb;
1009
  $id = (int) $id;
1010
  if (!$id) {
1011
  return null;
@@ -1026,7 +1036,8 @@ class NewsletterModule {
1026
  * Saves a new user on the database. Return false if the email (that must be unique) is already
1027
  * there. For a new users set the token and creation time if not passed.
1028
  *
1029
- * @param TNP_User $user
 
1030
  */
1031
  function save_user($user, $return_format = OBJECT) {
1032
  if (is_object($user)) {
@@ -1040,19 +1051,30 @@ class NewsletterModule {
1040
  if (empty($user['token'])) {
1041
  $user['token'] = NewsletterModule::get_token();
1042
  }
1043
- //if (empty($user['created'])) $user['created'] = time();
1044
- // Database default
1045
- //if (empty($user['status'])) $user['status'] = 'S';
1046
  }
1047
  // Due to the unique index on email field, this can fail.
1048
  return $this->store->save(NEWSLETTER_USERS_TABLE, $user, $return_format);
1049
  }
1050
 
 
 
 
 
 
 
1051
  function update_user_last_activity($user) {
1052
  global $wpdb;
1053
  $this->query($wpdb->prepare("update " . NEWSLETTER_USERS_TABLE . " set last_activity=%d where id=%d limit 1", time(), $user->id));
1054
  }
1055
 
 
 
 
 
 
 
 
 
1056
  function inline_css($content, $strip_style_blocks = false) {
1057
  // CSS
1058
  $matches = array();
@@ -1087,6 +1109,12 @@ class NewsletterModule {
1087
  return $this->store->get_all(NEWSLETTER_USERS_TABLE, "where test=1");
1088
  }
1089
 
 
 
 
 
 
 
1090
  function delete_user($id) {
1091
  global $wpdb;
1092
  $user = $this->get_user($id);
@@ -1110,34 +1138,40 @@ class NewsletterModule {
1110
  * @param string $alert
1111
  * @return string The final url
1112
  */
1113
- function build_message_url($url = '', $message_key='', $user = false, $email = false, $alert = '') {
1114
  $params = 'nm=' . urlencode($message_key);
1115
 
1116
  if ($user) {
1117
- if (!is_object($user)) $user = $this->get_user($user);
 
1118
  $params .= '&nk=' . urlencode($this->get_user_key($user));
1119
  }
1120
 
1121
  if ($email) {
1122
- if (!is_object($email)) $email = $this->get_email($email);
1123
- $params .= '&nek=' . urlencode($email->id . '-' . $email->token);
 
 
1124
  }
1125
 
1126
  if ($alert) {
1127
  $params .= '&alert=' . urlencode($alert);
1128
  }
1129
-
1130
  if (empty($url)) {
1131
  $url = Newsletter::instance()->get_newsletter_page_url();
1132
  }
1133
 
1134
  return self::add_qs($url, $params, false);
1135
  }
1136
-
1137
  function build_action_url($action, $user = null, $email = null) {
1138
- $url = $this->get_home_url() . '?na=' . urlencode($action) . '&nk=' . urlencode($this->get_user_key($user));
 
 
 
1139
  if ($email) {
1140
- $url .= '&nek=' . $email->id . '-' . $email->token;
1141
  }
1142
  return $url;
1143
  }
@@ -1191,7 +1225,7 @@ class NewsletterModule {
1191
  }
1192
 
1193
  // [TODO] Status?
1194
- $user->status = 'U';
1195
  $user->email = $user->id . '@anonymi.zed';
1196
 
1197
  $user = $this->save_user($user);
@@ -1202,31 +1236,18 @@ class NewsletterModule {
1202
  /**
1203
  * Changes a user status. Accept a user object, user id or user email.
1204
  *
1205
- * @param int|string $id_or_email
1206
  * @param string $status
1207
- * @return boolean
1208
  */
1209
  function set_user_status($user, $status) {
1210
  global $wpdb;
1211
- $status = (string) $status;
1212
- $this->logger->debug('Status change to ' . $status . ' of subscriber ' . $id_or_email . ' from ' . $_SERVER['REQUEST_URI']);
1213
 
1214
- if (is_object($user)) {
1215
- $r = $wpdb->query($wpdb->prepare("update " . NEWSLETTER_USERS_TABLE . " set status=%s where id=%d limit 1", $status, $user->id));
1216
- } else {
1217
- $user = strtolower(trim($user));
1218
- if (is_numeric($user)) {
1219
- $r = $wpdb->query($wpdb->prepare("update " . NEWSLETTER_USERS_TABLE . " set status=%s where id=%d limit 1", $status, $user));
1220
- } else {
1221
- $r = $wpdb->query($wpdb->prepare("update " . NEWSLETTER_USERS_TABLE . " set status=%s where email=%s limit 1", $status, $user));
1222
- }
1223
- }
1224
 
1225
- if ($wpdb->last_error) {
1226
- $this->logger->error($wpdb->last_error);
1227
- return false;
1228
- }
1229
- return $r;
1230
  }
1231
 
1232
  /**
@@ -1239,9 +1260,6 @@ class NewsletterModule {
1239
  */
1240
  function add_user_log($user, $source = '') {
1241
  global $wpdb;
1242
- if (!is_object($user)) {
1243
- return;
1244
- }
1245
 
1246
  $lists = $this->get_lists_public();
1247
  foreach ($lists as $list) {
@@ -1559,7 +1577,7 @@ class NewsletterModule {
1559
  }
1560
  echo '<input type="hidden" name="ts" value="' . time() . '">';
1561
  echo '</div>';
1562
-
1563
  if ($captcha) {
1564
  echo '<div class="tnp-captcha">';
1565
  echo '<p>', __('Math question', 'newsletter'), '</p>';
11
  * */
12
  abstract class TNP_List {
13
 
14
+ const STATUS_PRIVATE = 0;
15
+ const STATUS_PUBLIC = 2;
16
+ const STATUS_PROFILE_ONLY = 1;
17
+ const STATUS_HIDDEN = 3; // Public but never show (can be set with a hidden form field)
18
 
19
  }
20
 
27
  * */
28
  abstract class TNP_User {
29
 
30
+ const STATUS_CONFIRMED = 'C';
31
+ const STATUS_NOT_CONFIRMED = 'S';
32
+ const STATUS_UNSUBSCRIBED = 'U';
33
+ const STATUS_BOUNCED = 'B';
34
 
35
  }
36
 
816
  return '-';
817
  }
818
 
819
+ /**
820
+ * Returns the email unique key
821
+ * @param TNP_User $user
822
+ * @return string
823
+ */
824
+ function get_email_key($email) {
825
+ return $email->id . '-' . $email->token;
826
+ }
827
+
828
  /** Searches for a user using the nk parameter or the ni and nt parameters. Tries even with the newsletter cookie.
829
  * If found, the user object is returned or null.
830
  * The user is returned without regards to his status that should be checked by caller.
878
 
879
  if ($wpdb->last_error) {
880
  $this->logger->error($wpdb->last_error);
881
+ return null;
882
  }
883
  return $r;
884
  }
909
 
910
  if ($user == null || $token != $user->token) {
911
  if ($die_on_fail) {
912
+ die(__('No subscriber found.', 'newsletter'));
913
  } else {
914
  return null;
915
  }
923
  */
924
  function get_lists() {
925
  static $lists = null;
926
+ if (is_array($lists)) {
927
  return $lists;
928
+ }
929
 
930
  $lists = array();
931
  $data = NewsletterSubscription::instance()->options_lists;
957
  $lists = array();
958
  $all = $this->get_lists();
959
  foreach ($all as $list) {
960
+ if ($list->status == TNP_List::STATUS_PRIVATE) {
961
  continue;
962
  }
963
  $lists[] = $list;
979
  $lists = array();
980
  $all = $this->get_lists();
981
  foreach ($all as $list) {
982
+ if ($list->status != TNP_List::STATUS_PUBLIC || $list->forced) {
983
  continue;
984
  }
985
  $lists[] = $list;
988
  }
989
 
990
  /**
991
+ * Returns the lists to be shown in the profile page.
992
  *
993
  * @return TNP_List[]
994
  */
1001
  $lists = array();
1002
  $all = $this->get_lists();
1003
  foreach ($all as $list) {
1004
+ if ($list->status == TNP_List::STATUS_PRIVATE || $list->status == TNP_List::STATUS_HIDDEN) {
1005
  continue;
1006
  }
1007
  $lists[] = $list;
1010
  }
1011
 
1012
  /**
1013
+ * Returns a list as an object (with the same signature of TNP_List)
1014
  *
 
1015
  * @param int $id
1016
  * @return TNP_List
1017
  */
1018
  function get_list($id) {
 
1019
  $id = (int) $id;
1020
  if (!$id) {
1021
  return null;
1036
  * Saves a new user on the database. Return false if the email (that must be unique) is already
1037
  * there. For a new users set the token and creation time if not passed.
1038
  *
1039
+ * @param array $user
1040
+ * @return TNP_User|array|boolean Returns the subscriber reloaded from DB in the specified format. Flase on failure (duplicate email).
1041
  */
1042
  function save_user($user, $return_format = OBJECT) {
1043
  if (is_object($user)) {
1051
  if (empty($user['token'])) {
1052
  $user['token'] = NewsletterModule::get_token();
1053
  }
 
 
 
1054
  }
1055
  // Due to the unique index on email field, this can fail.
1056
  return $this->store->save(NEWSLETTER_USERS_TABLE, $user, $return_format);
1057
  }
1058
 
1059
+ /**
1060
+ * Updates the user last activity timestamp.
1061
+ *
1062
+ * @global wpdb $wpdb
1063
+ * @param TNP_User $user
1064
+ */
1065
  function update_user_last_activity($user) {
1066
  global $wpdb;
1067
  $this->query($wpdb->prepare("update " . NEWSLETTER_USERS_TABLE . " set last_activity=%d where id=%d limit 1", time(), $user->id));
1068
  }
1069
 
1070
+ /**
1071
+ * Finds single style blocks and adds a style attribute to every HTML tag with a class exactly matching the rules in the style
1072
+ * block. HTML tags can use the attribute "inline-class" to exact match a style rules if they need a composite class definition.
1073
+ *
1074
+ * @param string $content
1075
+ * @param boolean $strip_style_blocks
1076
+ * @return string
1077
+ */
1078
  function inline_css($content, $strip_style_blocks = false) {
1079
  // CSS
1080
  $matches = array();
1109
  return $this->store->get_all(NEWSLETTER_USERS_TABLE, "where test=1");
1110
  }
1111
 
1112
+ /**
1113
+ * Deletes a subscriber and cleans up all the stats table with his correlated data.
1114
+ *
1115
+ * @global wpdb $wpdb
1116
+ * @param int $id
1117
+ */
1118
  function delete_user($id) {
1119
  global $wpdb;
1120
  $user = $this->get_user($id);
1138
  * @param string $alert
1139
  * @return string The final url
1140
  */
1141
+ function build_message_url($url = '', $message_key = '', $user = null, $email = null, $alert = '') {
1142
  $params = 'nm=' . urlencode($message_key);
1143
 
1144
  if ($user) {
1145
+ if (!is_object($user))
1146
+ $user = $this->get_user($user);
1147
  $params .= '&nk=' . urlencode($this->get_user_key($user));
1148
  }
1149
 
1150
  if ($email) {
1151
+ if (!is_object($email)) {
1152
+ $email = $this->get_email($email);
1153
+ }
1154
+ $params .= '&nek=' . urlencode($this->get_email_key($email));
1155
  }
1156
 
1157
  if ($alert) {
1158
  $params .= '&alert=' . urlencode($alert);
1159
  }
1160
+
1161
  if (empty($url)) {
1162
  $url = Newsletter::instance()->get_newsletter_page_url();
1163
  }
1164
 
1165
  return self::add_qs($url, $params, false);
1166
  }
1167
+
1168
  function build_action_url($action, $user = null, $email = null) {
1169
+ $url = $this->get_home_url() . '?na=' . urlencode($action);
1170
+ if ($user) {
1171
+ $url .= '&nk=' . urlencode($this->get_user_key($user));
1172
+ }
1173
  if ($email) {
1174
+ $url .= '&nek=' . urlencode($this->get_email_key($email));
1175
  }
1176
  return $url;
1177
  }
1225
  }
1226
 
1227
  // [TODO] Status?
1228
+ $user->status = TNP_User::STATUS_UNSUBSCRIBED;
1229
  $user->email = $user->id . '@anonymi.zed';
1230
 
1231
  $user = $this->save_user($user);
1236
  /**
1237
  * Changes a user status. Accept a user object, user id or user email.
1238
  *
1239
+ * @param TNP_User $user
1240
  * @param string $status
1241
+ * @return TNP_User
1242
  */
1243
  function set_user_status($user, $status) {
1244
  global $wpdb;
 
 
1245
 
1246
+ $this->logger->debug('Status change to ' . $status . ' of subscriber ' . $user->id . ' from ' . $_SERVER['REQUEST_URI']);
 
 
 
 
 
 
 
 
 
1247
 
1248
+ $this->query($wpdb->prepare("update " . NEWSLETTER_USERS_TABLE . " set status=%s where id=%d limit 1", $status, $user->id));
1249
+
1250
+ return $this->get_user($user);
 
 
1251
  }
1252
 
1253
  /**
1260
  */
1261
  function add_user_log($user, $source = '') {
1262
  global $wpdb;
 
 
 
1263
 
1264
  $lists = $this->get_lists_public();
1265
  foreach ($lists as $list) {
1577
  }
1578
  echo '<input type="hidden" name="ts" value="' . time() . '">';
1579
  echo '</div>';
1580
+
1581
  if ($captcha) {
1582
  echo '<div class="tnp-captcha">';
1583
  echo '<p>', __('Math question', 'newsletter'), '</p>';
main/main.php CHANGED
@@ -108,11 +108,6 @@ if (!empty($return_path)) {
108
  }
109
  }
110
 
111
- if (empty($controls->data['page'])) {
112
- $controls->messages .= '<p>You should set a dedicated page for Newsletter which used to interact with your subscribers.</p>';
113
- } else {
114
-
115
- }
116
  ?>
117
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.37.0/codemirror.css" type="text/css">
118
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.37.0/addon/hint/show-hint.css">
108
  }
109
  }
110
 
 
 
 
 
 
111
  ?>
112
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.37.0/codemirror.css" type="text/css">
113
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.37.0/addon/hint/show-hint.css">
plugin.php CHANGED
@@ -4,7 +4,7 @@
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: 5.4.8
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.
@@ -14,7 +14,7 @@
14
  */
15
 
16
  // Used as dummy parameter on css and js links
17
- define('NEWSLETTER_VERSION', '5.4.8');
18
 
19
  global $wpdb, $newsletter;
20
 
@@ -308,7 +308,7 @@ class Newsletter extends NewsletterModule {
308
  $this->options['css'] = $subscription_options['css'];
309
  $this->save_options($this->options);
310
  }
311
-
312
  // Migration of "info" options
313
  $info_options = $this->get_options('info');
314
  if (!empty($this->options['header_logo']) && empty($info_options['header_logo'])) {
@@ -375,6 +375,8 @@ class Newsletter extends NewsletterModule {
375
  add_shortcode('newsletter_replace', array($this, 'shortcode_newsletter_replace'));
376
 
377
  if (is_admin()) {
 
 
378
  if ($this->is_admin_page()) {
379
  wp_enqueue_script('jquery-ui-tabs');
380
  wp_enqueue_script('jquery-ui-tooltip');
@@ -431,6 +433,22 @@ class Newsletter extends NewsletterModule {
431
  }
432
  }
433
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
434
  function hook_wp_enqueue_scripts() {
435
  if (empty($this->options['css_disabled']) && apply_filters('newsletter_enqueue_style', true)) {
436
  wp_enqueue_style('newsletter', plugins_url('newsletter') . '/style.css', array(), NEWSLETTER_VERSION);
@@ -1189,18 +1207,18 @@ class Newsletter extends NewsletterModule {
1189
  * @staticvar string $url
1190
  * @return string
1191
  */
 
1192
  function get_newsletter_page_url() {
1193
- static $url = false;
1194
 
1195
- if (!$url) {
1196
  if (!empty($this->options['page'])) {
1197
- $url = get_permalink($this->options['page']);
1198
  }
1199
- if (!$url) {
1200
- $url = self::get_home_url() . '?na=m';
1201
  }
1202
  }
1203
- return $url;
1204
  }
1205
 
1206
  }
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: 5.4.9
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.
14
  */
15
 
16
  // Used as dummy parameter on css and js links
17
+ define('NEWSLETTER_VERSION', '5.4.9');
18
 
19
  global $wpdb, $newsletter;
20
 
308
  $this->options['css'] = $subscription_options['css'];
309
  $this->save_options($this->options);
310
  }
311
+
312
  // Migration of "info" options
313
  $info_options = $this->get_options('info');
314
  if (!empty($this->options['header_logo']) && empty($info_options['header_logo'])) {
375
  add_shortcode('newsletter_replace', array($this, 'shortcode_newsletter_replace'));
376
 
377
  if (is_admin()) {
378
+ add_action('admin_notices', array($this, 'hook_admin_notices'));
379
+
380
  if ($this->is_admin_page()) {
381
  wp_enqueue_script('jquery-ui-tabs');
382
  wp_enqueue_script('jquery-ui-tooltip');
433
  }
434
  }
435
 
436
+ function hook_admin_notices() {
437
+ // Check of Newsletter dedicated page
438
+ if ($this->options['page']) {
439
+ if (get_post_status($this->options['page']) !== 'publish') {
440
+
441
+ 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">Edit the page</a>.</p></div>';
442
+ } else {
443
+ $content = get_post_field('post_content', $this->options['page']);
444
+ // With and without attributes
445
+ if (strpos($content, '[newsletter]') === false && strpos($content, '[newsletter ') === false) {
446
+ echo '<div class="notice notice-error"><p>The Newsletter dedicated page does not contain the [newsletter] shortcode. <a href="', site_url('/wp-admin/post.php'), '?post=', $this->options['page'], '&action=edit">Edit the page</a>.</p></div>';
447
+ }
448
+ }
449
+ }
450
+ }
451
+
452
  function hook_wp_enqueue_scripts() {
453
  if (empty($this->options['css_disabled']) && apply_filters('newsletter_enqueue_style', true)) {
454
  wp_enqueue_style('newsletter', plugins_url('newsletter') . '/style.css', array(), NEWSLETTER_VERSION);
1207
  * @staticvar string $url
1208
  * @return string
1209
  */
1210
+ var $newsletter_page_url = false;
1211
  function get_newsletter_page_url() {
 
1212
 
1213
+ if (!$this->newsletter_page_url) {
1214
  if (!empty($this->options['page'])) {
1215
+ $this->newsletter_page_url = get_permalink($this->options['page']);
1216
  }
1217
+ if (!$this->newsletter_page_url) {
1218
+ $this->newsletter_page_url = self::get_home_url() . '?na=m';
1219
  }
1220
  }
1221
+ return $this->newsletter_page_url;
1222
  }
1223
 
1224
  }
profile/defaults.php CHANGED
@@ -13,4 +13,5 @@ $options['error'] = __("Your email is not valid or already in use.", 'newsletter
13
  $options['save_label'] = __('Save', 'newsletter');
14
  $options['privacy_label'] = __('Read our privacy note', 'newsletter');
15
  $options['saved'] = __('Profile saved.', 'newsletter');
 
16
 
13
  $options['save_label'] = __('Save', 'newsletter');
14
  $options['privacy_label'] = __('Read our privacy note', 'newsletter');
15
  $options['saved'] = __('Profile saved.', 'newsletter');
16
+ $options['export_newsletters'] = 0;
17
 
profile/index.php CHANGED
@@ -40,6 +40,7 @@ if ($controls->is_action()) {
40
  <div id="tabs">
41
  <ul>
42
  <li><a href="#tabs-general"><?php _e('General', 'newsletter') ?></a></li>
 
43
 
44
  </ul>
45
 
@@ -116,6 +117,22 @@ if ($controls->is_action()) {
116
 
117
  </table>
118
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
119
 
120
  </div>
121
 
40
  <div id="tabs">
41
  <ul>
42
  <li><a href="#tabs-general"><?php _e('General', 'newsletter') ?></a></li>
43
+ <li><a href="#tabs-export"><?php _e('Subscriber data export', 'newsletter') ?></a></li>
44
 
45
  </ul>
46
 
117
 
118
  </table>
119
  </div>
120
+
121
+ <div id="tabs-general">
122
+
123
+
124
+ <table class="form-table">
125
+
126
+ <tr>
127
+ <th>
128
+ <?php _e('Log of sent newsletters', 'newsletter') ?>
129
+ </th>
130
+ <td>
131
+ <?php $controls->yesno('export_newsletters'); ?>
132
+ </td>
133
+ </tr>
134
+ </table>
135
+ </div>
136
 
137
  </div>
138
 
profile/profile.php CHANGED
@@ -126,60 +126,60 @@ class NewsletterProfile extends NewsletterModule {
126
  function to_json($user) {
127
  global $wpdb;
128
 
129
- $user = (array) $user;
130
- $fields = array('id', 'name', 'surname', 'sex', 'created', 'ip');
131
- $data = array();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
132
  $options_profile = get_option('newsletter_profile', array());
133
- $lists = array();
134
- $profiles = array();
135
- foreach ($user as $key => $value) {
136
- if (strpos($key, 'list_') === 0) {
137
- if (empty($value))
138
- continue;
139
- if ($options_profile[$key . '_status'] != 1 && $options_profile[$key . '_status'] != 2) {
140
- continue;
141
- }
142
- $lists[] = $options_profile[$key];
143
  }
 
 
144
 
145
- // Check if disabled
146
- if (strpos($key, 'profile_') === 0) {
147
- if (empty($value))
148
- continue;
149
- if ($options_profile[$key . '_status'] == 0) {
 
 
 
 
 
 
 
 
150
  continue;
151
- }
152
- $profiles[$key] = array('name' => $options_profile[$key], 'value' => $value);
153
  }
154
 
155
-
156
- if (in_array($key, $fields))
157
- $data[$key] = $value;
158
  }
159
 
160
- $data['lists'] = $lists;
161
- $data['profiles'] = $profiles;
162
-
163
-
164
- // Newsletters
165
- $sent = $wpdb->get_results($wpdb->prepare("select * from {$wpdb->prefix}newsletter_sent where user_id=%d order by email_id asc", $user['id']));
166
- $newsletters = array();
167
- foreach ($sent as $item) {
168
- $action = 'none';
169
- if ($item->open == 1)
170
- $action = 'read';
171
- else if ($item->open == 2)
172
- $action = 'click';
173
-
174
- $email = $this->get_email($item->email_id);
175
- if (!$email)
176
- continue;
177
- // 'id'=>$item->email_id,
178
- $newsletters[] = array('subject' => $email->subject, 'action' => $action, 'sent' => date('Y-m-d h:i:s', $email->send_on));
179
- }
180
-
181
- $data['newsletters'] = $newsletters;
182
-
183
  $extra = apply_filters('newsletter_profile_export_extra', array());
184
 
185
  $data = array_merge($extra, $data);
@@ -290,15 +290,16 @@ class NewsletterProfile extends NewsletterModule {
290
  }
291
 
292
  // Privacy
293
- if (!empty($this->options['privacy_label'])) {
 
294
  $buffer .= '<div class="tnp-field tnp-field-privacy">';
295
- if (!empty($options['privacy_url'])) {
296
- $buffer .= '<a href="' . $options['privacy_url'] . '" target="_blank">';
297
  }
298
 
299
  $buffer .= $this->options['privacy_label'];
300
 
301
- if (!empty($options['privacy_url'])) {
302
  $buffer .= '</a>';
303
  }
304
  $buffer .= "</div>\n";
@@ -390,9 +391,9 @@ class NewsletterProfile extends NewsletterModule {
390
 
391
  // Feed by Mail service is saved here
392
  $data = apply_filters('newsletter_profile_save', $data);
393
-
394
- if ($user->status == TNP_User::STAUTS_NOT_CONFIRMED) {
395
- $data['status'] = TNP_User::STAUTS_CONFIRMED;
396
  }
397
 
398
  $user = $this->save_user($data);
@@ -419,7 +420,7 @@ class NewsletterProfile extends NewsletterModule {
419
  global $wpdb, $charset_collate;
420
 
421
  parent::upgrade();
422
-
423
  // Migration code
424
  if (empty($this->options) || empty($this->options['email_changed'])) {
425
  // Options of the subscription module (worng name, I know)
@@ -437,12 +438,9 @@ class NewsletterProfile extends NewsletterModule {
437
  $this->options['save_label'] = $options['save'];
438
  $this->save_options($this->options);
439
  }
440
-
441
-
442
  }
443
 
444
  function admin_menu() {
445
- //$this->add_menu_page('index', 'Subscribers');
446
  $this->add_admin_page('index', 'Profile');
447
  }
448
 
126
  function to_json($user) {
127
  global $wpdb;
128
 
129
+
130
+ $fields = array('name', 'surname', 'sex', 'created', 'ip', 'email');
131
+ $data = array(
132
+ 'email'=>$user->email,
133
+ 'name'=>$user->name,
134
+ 'last_name'=>$user->surname,
135
+ 'gender'=>$user->sex,
136
+ 'created'=>$user->created,
137
+ 'ip'=>$user->ip,
138
+ );
139
+
140
+ // Lists
141
+ $data['lists'] = array();
142
+
143
+ $lists = $this->get_lists_public();
144
+ foreach ($lists as $list) {
145
+ $field = 'list_' . $list->id;
146
+ if ($user->$field == 1) {
147
+ $data['lists'][] = $list->name;
148
+ }
149
+ }
150
+
151
+ // Profile
152
  $options_profile = get_option('newsletter_profile', array());
153
+ $data['profiles'] = array();
154
+ for ($i=1; $i<NEWSLETTER_PROFILE_MAX; $i++) {
155
+ $field = 'profile_' . $i;
156
+ if ($options_profile[$field . '_status'] != 1 && $options_profile[$field . '_status'] != 2) {
157
+ continue;
 
 
 
 
 
158
  }
159
+ $data['profiles'][] = array('name' => $options_profile[$field], 'value' => $user->$field);
160
+ }
161
 
162
+ // Newsletters
163
+ if ($this->options['export_newsletters']) {
164
+ $sent = $wpdb->get_results($wpdb->prepare("select * from {$wpdb->prefix}newsletter_sent where user_id=%d order by email_id asc", $user->id));
165
+ $newsletters = array();
166
+ foreach ($sent as $item) {
167
+ $action = 'none';
168
+ if ($item->open == 1)
169
+ $action = 'read';
170
+ else if ($item->open == 2)
171
+ $action = 'click';
172
+
173
+ $email = $this->get_email($item->email_id);
174
+ if (!$email)
175
  continue;
176
+ // 'id'=>$item->email_id,
177
+ $newsletters[] = array('subject' => $email->subject, 'action' => $action, 'sent' => date('Y-m-d h:i:s', $email->send_on));
178
  }
179
 
180
+ $data['newsletters'] = $newsletters;
 
 
181
  }
182
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
183
  $extra = apply_filters('newsletter_profile_export_extra', array());
184
 
185
  $data = array_merge($extra, $data);
290
  }
291
 
292
  // Privacy
293
+ $privacy_url = NewsletterSubscription::instance()->get_privacy_url();
294
+ if (!empty($this->options['privacy_label']) && !empty($privacy_url)) {
295
  $buffer .= '<div class="tnp-field tnp-field-privacy">';
296
+ if ($privacy_url) {
297
+ $buffer .= '<a href="' . $privacy_url . '" target="_blank">';
298
  }
299
 
300
  $buffer .= $this->options['privacy_label'];
301
 
302
+ if ($privacy_url) {
303
  $buffer .= '</a>';
304
  }
305
  $buffer .= "</div>\n";
391
 
392
  // Feed by Mail service is saved here
393
  $data = apply_filters('newsletter_profile_save', $data);
394
+
395
+ if ($user->status == TNP_User::STATUS_NOT_CONFIRMED) {
396
+ $data['status'] = TNP_User::STATUS_CONFIRMED;
397
  }
398
 
399
  $user = $this->save_user($data);
420
  global $wpdb, $charset_collate;
421
 
422
  parent::upgrade();
423
+
424
  // Migration code
425
  if (empty($this->options) || empty($this->options['email_changed'])) {
426
  // Options of the subscription module (worng name, I know)
438
  $this->options['save_label'] = $options['save'];
439
  $this->save_options($this->options);
440
  }
 
 
441
  }
442
 
443
  function admin_menu() {
 
444
  $this->add_admin_page('index', 'Profile');
445
  }
446
 
readme.txt CHANGED
@@ -2,7 +2,7 @@
2
  Tags: newsletter,email,subscription,mass mail,list build,email marketing,direct mailing,automation,automated
3
  Requires at least: 3.4.0
4
  Tested up to: 4.9.6
5
- Stable tag: 5.4.8
6
  Contributors: satollo,webagile,michael-travan
7
 
8
  Add a real newsletter system to your blog. For free. With unlimited newsletters and subscribers.
@@ -102,6 +102,14 @@ Thank you, The Newsletter Team
102
 
103
  == Changelog ==
104
 
 
 
 
 
 
 
 
 
105
  = 5.4.8 =
106
 
107
  * Fixed the (duplicated) style.css reference
2
  Tags: newsletter,email,subscription,mass mail,list build,email marketing,direct mailing,automation,automated
3
  Requires at least: 3.4.0
4
  Tested up to: 4.9.6
5
+ Stable tag: 5.4.9
6
  Contributors: satollo,webagile,michael-travan
7
 
8
  Add a real newsletter system to your blog. For free. With unlimited newsletters and subscribers.
102
 
103
  == Changelog ==
104
 
105
+ = 5.4.9 =
106
+
107
+ * Lists management in APIs
108
+ * Code cleanup
109
+ * New subscribers data export controls
110
+ * New global check and notice if the dedicated page is misconfigured
111
+ * Fix privacy note display on profile page even without a privacy url set
112
+
113
  = 5.4.8 =
114
 
115
  * Fixed the (duplicated) style.css reference
subscription/antibot.php CHANGED
@@ -1,6 +1,5 @@
1
  <?php
2
- if (!defined('ABSPATH'))
3
- exit;
4
 
5
  @include_once NEWSLETTER_INCLUDES_DIR . '/controls.php';
6
  $controls = new NewsletterControls();
1
  <?php
2
+ defined('ABSPATH') || exit;
 
3
 
4
  @include_once NEWSLETTER_INCLUDES_DIR . '/controls.php';
5
  $controls = new NewsletterControls();
subscription/lists.php CHANGED
@@ -12,7 +12,7 @@ if (!$controls->is_action()) {
12
  $module->save_options($controls->data, 'lists');
13
  $controls->add_message_saved();
14
  }
15
- if ($controls->is_action('dissociate')) {
16
  $wpdb->query("update " . NEWSLETTER_USERS_TABLE . " set list_" . ((int)$controls->button_data) . "=0");
17
  $controls->add_message_done();
18
  }
@@ -75,7 +75,9 @@ $status = array(0 => 'Disabled/Private use', 1 => 'Only on profile page', 2 => '
75
  <td><?php $controls->select('list_' . $i . '_checked', array(0 => 'No', 1 => 'Yes')); ?></td>
76
  <td><?php $controls->select('list_' . $i . '_forced', array(0 => 'No', 1 => 'Yes')); ?></td>
77
  <td><?php echo $wpdb->get_var("select count(*) from " . NEWSLETTER_USERS_TABLE . " where list_" . $i . "=1 and status='C'"); ?></td>
78
- <td><?php $controls->button_confirm('dissociate', __('Dissociate everyone', 'newsletter'), '', $i); ?></td>
 
 
79
  <td>
80
  <?php $notes = apply_filters('newsletter_lists_notes', array(), $i); ?>
81
  <?php
12
  $module->save_options($controls->data, 'lists');
13
  $controls->add_message_saved();
14
  }
15
+ if ($controls->is_action('unlink')) {
16
  $wpdb->query("update " . NEWSLETTER_USERS_TABLE . " set list_" . ((int)$controls->button_data) . "=0");
17
  $controls->add_message_done();
18
  }
75
  <td><?php $controls->select('list_' . $i . '_checked', array(0 => 'No', 1 => 'Yes')); ?></td>
76
  <td><?php $controls->select('list_' . $i . '_forced', array(0 => 'No', 1 => 'Yes')); ?></td>
77
  <td><?php echo $wpdb->get_var("select count(*) from " . NEWSLETTER_USERS_TABLE . " where list_" . $i . "=1 and status='C'"); ?></td>
78
+
79
+ <td><?php $controls->button_confirm('unlink', __('Unlink everyone', 'newsletter'), '', $i); ?></td>
80
+
81
  <td>
82
  <?php $notes = apply_filters('newsletter_lists_notes', array(), $i); ?>
83
  <?php
subscription/subscription.php CHANGED
@@ -12,7 +12,15 @@ class NewsletterSubscription extends NewsletterModule {
12
  const OPTIN_SINGLE = 1;
13
 
14
  static $instance;
 
 
 
 
15
  var $options_profile;
 
 
 
 
16
  var $options_lists;
17
 
18
  /**
@@ -27,7 +35,7 @@ class NewsletterSubscription extends NewsletterModule {
27
 
28
  function __construct() {
29
 
30
- parent::__construct('subscription', '2.1.5', null, array('lists', 'template', 'profile'));
31
  $this->options_profile = $this->get_options('profile');
32
  $this->options_lists = $this->get_options('lists');
33
 
@@ -548,8 +556,9 @@ class NewsletterSubscription extends NewsletterModule {
548
  if ($sub == '') {
549
  // For compatibility the options are wrongly named
550
  $options = get_option('newsletter', array());
551
- if (!is_array($options))
552
  $options = array();
 
553
  return $options;
554
  }
555
  if ($sub == 'profile') {
@@ -588,10 +597,6 @@ class NewsletterSubscription extends NewsletterModule {
588
  * @global Newsletter $newsletter
589
  */
590
  function subscribe($status = null, $emails = true) {
591
- $newsletter = Newsletter::instance();
592
-
593
- // Messages
594
- $options = get_option('newsletter', array());
595
 
596
  $opt_in = (int) $this->options['noconfirmation']; // 0 - double, 1 - single
597
  if (!empty($this->options['optin_override']) && isset($_REQUEST['optin'])) {
@@ -614,7 +619,7 @@ class NewsletterSubscription extends NewsletterModule {
614
  }
615
  }
616
 
617
- $email = $newsletter->normalize_email(stripslashes($_REQUEST['ne']));
618
 
619
  // Shound never reach this point without a valid email address
620
  if ($email == null) {
@@ -670,7 +675,7 @@ class NewsletterSubscription extends NewsletterModule {
670
 
671
  set_transient($this->get_user_key($user), $_REQUEST, 3600 * 48);
672
 
673
- // This status is not stored it indicate a temporary status to show the correct messages
674
  $user->status = 'S';
675
 
676
  $this->send_message('confirmation', $user);
@@ -699,13 +704,6 @@ class NewsletterSubscription extends NewsletterModule {
699
  $user['updated'] = time();
700
 
701
  $user = apply_filters('newsletter_user_subscribe', $user);
702
- // TODO: should be removed!!!
703
- if (defined('NEWSLETTER_FEED_VERSION')) {
704
- $options_feed = get_option('newsletter_feed', array());
705
- if ($options_feed['add_new'] == 1) {
706
- $user['feed'] = 1;
707
- }
708
- }
709
 
710
  $user = $this->save_user($user);
711
 
@@ -729,11 +727,15 @@ class NewsletterSubscription extends NewsletterModule {
729
  return $message . '<span itemscope itemtype="http://schema.org/EmailMessage"><span itemprop="description" content="Email address confirmation"></span><span itemprop="action" itemscope itemtype="http://schema.org/ConfirmAction"><meta itemprop="name" content="Confirm Subscription"><span itemprop="handler" itemscope itemtype="http://schema.org/HttpActionHandler"><meta itemprop="url" content="{subscription_confirm_url}"><link itemprop="method" href="http://schema.org/HttpRequestMethod/POST"></span></span></span>';
730
  }
731
 
 
 
 
 
 
 
 
732
  function update_user_from_request($user) {
733
- $newsletter = Newsletter::instance();
734
- $options = get_option('newsletter', array());
735
-
736
- $options_profile = get_option('newsletter_profile', array());
737
  if (isset($_REQUEST['nn'])) {
738
  $user['name'] = $this->normalize_name(stripslashes($_REQUEST['nn']));
739
  }
@@ -774,18 +776,17 @@ class NewsletterSubscription extends NewsletterModule {
774
  }
775
  }
776
 
777
- $lists = $this->get_lists_public();
778
-
779
  // Preferences (field names are nl[] and values the list number so special forms with radio button can work)
780
  if (isset($_REQUEST['nl']) && is_array($_REQUEST['nl'])) {
781
- $this->logger->debug($_REQUEST['nl']);
 
782
  foreach ($lists as $list) {
783
  if (in_array('' . $list->id, $_REQUEST['nl'])) {
784
  $user['list_' . $list->id] = 1;
785
  }
786
  }
787
  } else {
788
- $this->logger->debug('No preferences received');
789
  }
790
 
791
  // Forced lists
@@ -820,29 +821,34 @@ class NewsletterSubscription extends NewsletterModule {
820
  }
821
  $message = str_replace('{message}', $message, $template);
822
 
823
-
824
  $headers = array('Auto-Submitted' => 'auto-generated');
825
 
 
826
  $message = $this->replace($message);
827
  return Newsletter::instance()->mail($to, $subject, $message, $headers);
828
  }
829
 
 
 
 
 
 
 
830
  function reactivate() {
831
- $user = $this->get_user_from_request();
832
- if (!$user) {
833
- die('No subscriber found.');
834
- }
835
 
836
- $this->set_user_status($user, Newsletter::STATUS_CONFIRMED);
837
  $this->add_user_log($user, 'reactivate');
838
 
839
  return $user;
840
  }
841
 
842
  /**
843
- *
844
- * @param type $user
845
- * @return stdClass
 
 
846
  */
847
  function confirm($user_id = null, $emails = true) {
848
 
@@ -870,15 +876,15 @@ class NewsletterSubscription extends NewsletterModule {
870
 
871
  setcookie('newsletter', $user->id . '-' . $user->token, time() + 60 * 60 * 24 * 365, '/');
872
 
873
- if ($user->status == Newsletter::STATUS_CONFIRMED) {
874
  $this->add_user_log($user, 'activate');
875
  do_action('newsletter_user_confirmed', $user);
876
  return $user;
877
  }
878
 
879
- $this->set_user_status($user->id, Newsletter::STATUS_CONFIRMED);
880
 
881
- $user = $this->get_user($user->id);
882
 
883
  $this->add_user_log($user, 'activate');
884
 
@@ -913,22 +919,21 @@ class NewsletterSubscription extends NewsletterModule {
913
  }
914
 
915
  /**
916
- * Returns the unsubscribed user.
917
  *
918
- * @global type $newsletter
919
- * @return type
920
  */
921
  function unsubscribe() {
922
- $newsletter = Newsletter::instance();
923
  $user = $this->get_user_from_request(true);
924
 
925
  if ($user->status == 'U') {
926
  return $user;
927
  }
928
 
929
- $this->set_user_status($user->id, 'U');
930
 
931
- $user->status = 'U';
 
932
  $this->add_user_log($user, 'unsubscribe');
933
 
934
  do_action('newsletter_unsubscribed', $user);
@@ -1008,17 +1013,16 @@ class NewsletterSubscription extends NewsletterModule {
1008
  }
1009
  }
1010
 
 
1011
  function get_privacy_url() {
1012
- static $url = false;
1013
-
1014
- if (!$url) {
1015
  if ($this->options_profile['privacy_use_wp_url'] && function_exists('get_privacy_policy_url')) {
1016
- $url = get_privacy_policy_url();
1017
  } else {
1018
- $url = $this->options_profile['privacy_url'];
1019
  }
1020
  }
1021
- return $url;
1022
  }
1023
 
1024
  function get_form_javascript() {
12
  const OPTIN_SINGLE = 1;
13
 
14
  static $instance;
15
+
16
+ /**
17
+ * @var array
18
+ */
19
  var $options_profile;
20
+
21
+ /**
22
+ * @var array
23
+ */
24
  var $options_lists;
25
 
26
  /**
35
 
36
  function __construct() {
37
 
38
+ parent::__construct('subscription', '2.1.6', null, array('lists', 'template', 'profile'));
39
  $this->options_profile = $this->get_options('profile');
40
  $this->options_lists = $this->get_options('lists');
41
 
556
  if ($sub == '') {
557
  // For compatibility the options are wrongly named
558
  $options = get_option('newsletter', array());
559
+ if (!is_array($options)) {
560
  $options = array();
561
+ }
562
  return $options;
563
  }
564
  if ($sub == 'profile') {
597
  * @global Newsletter $newsletter
598
  */
599
  function subscribe($status = null, $emails = true) {
 
 
 
 
600
 
601
  $opt_in = (int) $this->options['noconfirmation']; // 0 - double, 1 - single
602
  if (!empty($this->options['optin_override']) && isset($_REQUEST['optin'])) {
619
  }
620
  }
621
 
622
+ $email = $this->normalize_email(stripslashes($_REQUEST['ne']));
623
 
624
  // Shound never reach this point without a valid email address
625
  if ($email == null) {
675
 
676
  set_transient($this->get_user_key($user), $_REQUEST, 3600 * 48);
677
 
678
+ // This status is *not* stored it indicate a temporary status to show the correct messages
679
  $user->status = 'S';
680
 
681
  $this->send_message('confirmation', $user);
704
  $user['updated'] = time();
705
 
706
  $user = apply_filters('newsletter_user_subscribe', $user);
 
 
 
 
 
 
 
707
 
708
  $user = $this->save_user($user);
709
 
727
  return $message . '<span itemscope itemtype="http://schema.org/EmailMessage"><span itemprop="description" content="Email address confirmation"></span><span itemprop="action" itemscope itemtype="http://schema.org/ConfirmAction"><meta itemprop="name" content="Confirm Subscription"><span itemprop="handler" itemscope itemtype="http://schema.org/HttpActionHandler"><meta itemprop="url" content="{subscription_confirm_url}"><link itemprop="method" href="http://schema.org/HttpRequestMethod/POST"></span></span></span>';
728
  }
729
 
730
+ /**
731
+ * Processes the request and fill in the *array* representing a subscriber with submitted values
732
+ * (filtering when necessary).
733
+ *
734
+ * @param array $user An array partially filled with subscriber data
735
+ * @return array The filled array representing a subscriber
736
+ */
737
  function update_user_from_request($user) {
738
+
 
 
 
739
  if (isset($_REQUEST['nn'])) {
740
  $user['name'] = $this->normalize_name(stripslashes($_REQUEST['nn']));
741
  }
776
  }
777
  }
778
 
 
 
779
  // Preferences (field names are nl[] and values the list number so special forms with radio button can work)
780
  if (isset($_REQUEST['nl']) && is_array($_REQUEST['nl'])) {
781
+ $lists = $this->get_lists_public();
782
+ //$this->logger->debug($_REQUEST['nl']);
783
  foreach ($lists as $list) {
784
  if (in_array('' . $list->id, $_REQUEST['nl'])) {
785
  $user['list_' . $list->id] = 1;
786
  }
787
  }
788
  } else {
789
+ $this->logger->debug('No lists received');
790
  }
791
 
792
  // Forced lists
821
  }
822
  $message = str_replace('{message}', $message, $template);
823
 
 
824
  $headers = array('Auto-Submitted' => 'auto-generated');
825
 
826
+ // Replaces tags from the template
827
  $message = $this->replace($message);
828
  return Newsletter::instance()->mail($to, $subject, $message, $headers);
829
  }
830
 
831
+ /**
832
+ * Reactivate the subscriber extracted from the request setting his status
833
+ * to confirmed and logging. No email are sent. Dies on subscriber extraction failure.
834
+ *
835
+ * @return TNP_User
836
+ */
837
  function reactivate() {
838
+ $user = $this->get_user_from_request(true);
 
 
 
839
 
840
+ $user = $this->set_user_status($user, TNP_User::STATUS_CONFIRMED);
841
  $this->add_user_log($user, 'reactivate');
842
 
843
  return $user;
844
  }
845
 
846
  /**
847
+ * Confirms a subscription changing the user status and, possibly, merging the
848
+ * temporary data if present.
849
+ *
850
+ * @param int $user_id Optional. If null the user is extracted from the request.
851
+ * @return TNP_User
852
  */
853
  function confirm($user_id = null, $emails = true) {
854
 
876
 
877
  setcookie('newsletter', $user->id . '-' . $user->token, time() + 60 * 60 * 24 * 365, '/');
878
 
879
+ if ($user->status == TNP_User::STATUS_CONFIRMED) {
880
  $this->add_user_log($user, 'activate');
881
  do_action('newsletter_user_confirmed', $user);
882
  return $user;
883
  }
884
 
885
+ $this->set_user_status($user, TNP_User::STATUS_CONFIRMED);
886
 
887
+ $user = $this->get_user($user);
888
 
889
  $this->add_user_log($user, 'activate');
890
 
919
  }
920
 
921
  /**
922
+ * Unsubscribes the subscriber from the request. Die on subscriber extraction failure.
923
  *
924
+ * @return TNP_User
 
925
  */
926
  function unsubscribe() {
 
927
  $user = $this->get_user_from_request(true);
928
 
929
  if ($user->status == 'U') {
930
  return $user;
931
  }
932
 
933
+ $this->set_user_status($user, TNP_User::STATUS_UNSUBSCRIBED);
934
 
935
+ $user = $this->get_user($user);
936
+
937
  $this->add_user_log($user, 'unsubscribe');
938
 
939
  do_action('newsletter_unsubscribed', $user);
1013
  }
1014
  }
1015
 
1016
+ var $privacy_url = false;
1017
  function get_privacy_url() {
1018
+ if (!$this->privacy_url) {
 
 
1019
  if ($this->options_profile['privacy_use_wp_url'] && function_exists('get_privacy_policy_url')) {
1020
+ $this->privacy_url = get_privacy_policy_url();
1021
  } else {
1022
+ $this->privacy_url = $this->options_profile['privacy_url'];
1023
  }
1024
  }
1025
+ return $this->privacy_url;
1026
  }
1027
 
1028
  function get_form_javascript() {
tnp-header.php CHANGED
@@ -154,13 +154,6 @@ $warning |= empty($status_options['mail']);
154
  </ul>
155
  </div>
156
 
157
- <?php if (!empty(Newsletter::instance()->options['page']) && (!get_permalink(Newsletter::instance()->options['page']) || get_post_status(Newsletter::instance()->options['page']) != 'publish')) { ?>
158
- <div class="tnp-error">
159
- <?php _e('The Newsletter dedicated page is wrongly configured.', 'newsletter') ?>
160
- <a href="admin.php?page=newsletter_main_main"><?php _e('Check it now', 'newsletter')?></a>.
161
- </div>
162
- <?php } ?>
163
-
164
 
165
  <?php if (isset($_GET['debug']) || !isset($dismissed['rate']) && $user_count > 300) { ?>
166
  <div class="tnp-notice">
154
  </ul>
155
  </div>
156
 
 
 
 
 
 
 
 
157
 
158
  <?php if (isset($_GET['debug']) || !isset($dismissed['rate']) && $user_count > 300) { ?>
159
  <div class="tnp-notice">
users/massive.php CHANGED
@@ -128,8 +128,8 @@ if ($controls->is_action('bounces')) {
128
  continue;
129
  }
130
 
131
- $r = NewsletterUsers::instance()->set_user_status($email, 'B');
132
- if ($r === 0) {
133
  $results .= '[BOUNCED] ' . $email . "\n";
134
  $marked++;
135
  continue;
128
  continue;
129
  }
130
 
131
+ $r = $wpdb->query($wpdb->prepare('update ' . NEWSLETTER_USERS_TABLE . " set status='B' where email=%s limit 1", $email));
132
+ if ($r === 1) {
133
  $results .= '[BOUNCED] ' . $email . "\n";
134
  $marked++;
135
  continue;
users/users.php CHANGED
@@ -19,7 +19,7 @@ class NewsletterUsers extends NewsletterModule {
19
  }
20
 
21
  function __construct() {
22
- parent::__construct('users', '1.2.2');
23
  add_action('init', array($this, 'hook_init'));
24
  }
25
 
19
  }
20
 
21
  function __construct() {
22
+ parent::__construct('users', '1.2.3');
23
  add_action('init', array($this, 'hook_init'));
24
  }
25
 
widget/minimal.php CHANGED
@@ -105,4 +105,3 @@ class NewsletterWidgetMinimal extends WP_Widget {
105
  add_action('widgets_init', function() {
106
  return register_widget("NewsletterWidgetMinimal");
107
  });
108
- ?>
105
  add_action('widgets_init', function() {
106
  return register_widget("NewsletterWidgetMinimal");
107
  });
 
widget/standard.php CHANGED
@@ -25,11 +25,10 @@ class NewsletterWidget extends WP_Widget {
25
  'lists_field_label' => ''), $instance);
26
 
27
  $options_profile = get_option('newsletter_profile');
28
- //$form = NewsletterSubscription::instance()->get_form_javascript();
29
  $form = '';
30
 
31
  $form .= '<div class="tnp tnp-widget">';
32
- $form .= NewsletterSubscription::instance()->get_subscription_form_html5('widget', null, array(
33
  'list' => implode(',', $instance['nl']),
34
  'lists_field_layout' => $instance['lists_layout'],
35
  'lists_field_empty_label' => $instance['lists_empty_label'],
@@ -160,4 +159,3 @@ class NewsletterWidget extends WP_Widget {
160
  add_action('widgets_init', function () {
161
  return register_widget("NewsletterWidget");
162
  });
163
- ?>
25
  'lists_field_label' => ''), $instance);
26
 
27
  $options_profile = get_option('newsletter_profile');
 
28
  $form = '';
29
 
30
  $form .= '<div class="tnp tnp-widget">';
31
+ $form .= NewsletterSubscription::instance()->get_subscription_form('widget', null, array(
32
  'list' => implode(',', $instance['nl']),
33
  'lists_field_layout' => $instance['lists_layout'],
34
  'lists_field_empty_label' => $instance['lists_empty_label'],
159
  add_action('widgets_init', function () {
160
  return register_widget("NewsletterWidget");
161
  });