Newsletter - Version 6.8.3

Version Description

  • Changed the administrative notification of subscription removing the lists and linking the user profile
  • Fixed missing user token on data from some external sources
  • Extra profiles fix and optmization
  • Improved notificatons for usage of private fields on custom forms
  • Better management of opt-in override on forms and notices for administration when used in wrong ways
  • Fixed the posts block with odd numbers of posts on Automated context
  • Added administrative notices on custom form for invalid list usage
  • Added multilanguage support on lists on custom forms
  • Added multilanguage support on extra profiles on custom forms
  • Fixed CSS for extra profile fields
  • Improved antibot/antispam performances
  • Multilanguage on validation JS (but will be removed in favor of pure HTML validation)
  • Fixed label "for" attribute for extra fields on custom forms
  • Better test message from status panel
Download this release

Release Info

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

Code changes from version 6.8.2 to 6.8.3

admin.js CHANGED
@@ -53,6 +53,25 @@ function tnp_select_toggle(s, t) {
53
  }
54
  }
55
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
56
  window.onload = function () {
57
  jQuery('.tnp-counter-animation').each(function () {
58
  var _this = jQuery(this);
53
  }
54
  }
55
 
56
+ /*
57
+ * Used by the date field of NewsletterControls
58
+ */
59
+ function tnp_date_onchange(field) {
60
+ let id = field.id.substring(0, field.id.lastIndexOf('_'));
61
+ let base_field = document.getElementById('options-' + id);
62
+ console.log(base_field);
63
+ //let form = field.form;
64
+ let year = document.getElementById(id + '_year');
65
+ let month = document.getElementById(id + '_month');
66
+ let day = document.getElementById(id + '_day');
67
+ if (year.value === '' || month.value === '' || day.value === '') {
68
+ base_field.value = 0;
69
+ } else {
70
+ base_field.value = new Date(year.value, month.value, day.value, 12, 0, 0).getTime()/1000;
71
+ }
72
+ //this.form.elements['options[" . esc_attr($name) . "]'].value = new Date(document.getElementById('" . esc_attr($name) . "_year').value, document.getElementById('" . esc_attr($name) . "_month').value, document.getElementById('" . esc_attr($name) . "_day').value, 12, 0, 0).getTime()/1000";
73
+ }
74
+
75
  window.onload = function () {
76
  jQuery('.tnp-counter-animation').each(function () {
77
  var _this = jQuery(this);
admin.min.js ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ jQuery.cookie=function(c,b,a){if("undefined"!=typeof b){a=a||{};null===b&&(b="",a.expires=-1);var d="";a.expires&&("number"==typeof a.expires||a.expires.toUTCString)&&("number"==typeof a.expires?(d=new Date,d.setTime(d.getTime()+864E5*a.expires)):d=a.expires,d="; expires="+d.toUTCString());var e=a.path?"; path="+a.path:"",f=a.domain?"; domain="+a.domain:"";a=a.secure?"; secure":"";document.cookie=[c,"=",encodeURIComponent(b),d,e,f,a].join("")}else{b=null;if(document.cookie&&""!=document.cookie)for(a=
2
+ document.cookie.split(";"),d=0;d<a.length;d++)if(e=jQuery.trim(a[d]),e.substring(0,c.length+1)==c+"="){b=decodeURIComponent(e.substring(c.length+1));break}return b}};function tnp_toggle_schedule(){jQuery("#tnp-schedule-button").toggle();jQuery("#tnp-schedule").toggle()}function tnp_select_toggle(c,b){1==c.value?jQuery("#options-"+b).show():jQuery("#options-"+b).hide()}
3
+ function tnp_date_onchange(c){var b=c.id.substring(0,c.id.lastIndexOf("_"));c=document.getElementById("options-"+b);console.log(c);let a=document.getElementById(b+"_year"),d=document.getElementById(b+"_month");b=document.getElementById(b+"_day");c.value=""===a.value||""===d.value||""===b.value?0:(new Date(a.value,d.value,b.value,12,0,0)).getTime()/1E3}
4
+ window.onload=function(){jQuery(".tnp-counter-animation").each(function(){var c=jQuery(this),b=null;(function(a){return!isNaN(Number(a))&&-1!==Number(a).toString().indexOf(".")})(c.text())?b={parsed:parseFloat(c.text()),rounded:function(a){return a.toFixed(1)}}:(b={parsed:parseInt(c.text()),rounded:function(a){return Math.ceil(a)}},c.hasClass("percentage")&&c.css("min-width","60px"));jQuery({counter:0}).animate({counter:b.parsed},{duration:1E3,easing:"swing",step:function(){c.text(b.rounded(this.counter))}})});
5
+ (function(){if(jQuery("#tnp-nl-status").length&&jQuery("#newsletter-form").length){var c=jQuery("#tnp-nl-status .tnp-nl-status-schedule-value");jQuery("#newsletter-form").change(function(b){1===jQuery(b.target).parents("#tabs-options").length&&c.text(tnp_translations.save_to_update_counter)})}})()};
changelog.txt CHANGED
@@ -1,4 +1,252 @@
1
- = 6.3.9 =
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
 
3
  * Improved antibot and/or spam subscription checks (please review your security configuration)
4
 
1
+ = 6.7.9 =
2
+
3
+ * Fixed posts block
4
+
5
+ = 6.7.8 =
6
+
7
+ * Removed old themes (anyway not usable since long time)
8
+
9
+ = 6.7.7 =
10
+
11
+ * Fixed text escape for header and footer blocks (was reported as security required fix)
12
+
13
+ = 6.7.6 =
14
+
15
+ * Fixed error on profile save
16
+
17
+ = 6.7.5 =
18
+
19
+ * Added language selector on subscriber profile page on multilanguage site
20
+ * Fix multilanguage label on profile page
21
+ * Added newsletter_user_post_subscribe hook (just after saving the subscription, before saving you can use newsletter_user_subscribe)
22
+ * Added checks for WP 5.5 breaking changes
23
+ * Added new layout (big images) on posts block
24
+ * Added standard button on post block
25
+ * Minor fix on hero block
26
+ * Added support for Popup Maker (need a free addon)
27
+
28
+ = 6.7.4 =
29
+
30
+ * Improved posts block in the Automated context with more options
31
+ * Improved the unsubscribe header
32
+ * Removed the Auto-Submitted header
33
+
34
+ = 6.7.3 =
35
+
36
+ * Added the company_legal tag
37
+ * Change a column type on statitics table to improve performances on site with big subscription lists (100k+)
38
+ * Fix attempt on posts block for RTL languages
39
+
40
+ = 6.7.2 =
41
+
42
+ * Fix post permalink with WPML on language specific setting for Newsletter themes and blocks
43
+ * Added action newsletter_user_reactivated
44
+
45
+ = 6.7.1 =
46
+
47
+ * Changed the lists configuration to be more clear
48
+
49
+ = 6.7.0 =
50
+
51
+ * Fixed the text part not sent when using the standard mailing function of WP
52
+ * Added warning on statistic panel for email without the tracking active
53
+ * Fixed the hero block align when specified manually with text-align on text part
54
+
55
+ = 6.6.9 =
56
+
57
+ * Fixed the admin email preview not showing the correct email after visiting the online view of a newsletter
58
+ * Fixed possible not "100%" display percentage for sent newsletters (was only a display problem)
59
+
60
+ = 6.6.8 =
61
+
62
+ * Fixed newsletter view returning "not found"
63
+
64
+ = 6.6.7 =
65
+
66
+ * Fixed a debug notice on image resize
67
+
68
+ = 6.6.6 =
69
+
70
+ * Added check on submission for private lists (error shown only to administrator as debug information)
71
+ * Improved right layout on hero block
72
+ * Improved internal actions management (better performances)
73
+ * Added lists, media selector, charmap on text block
74
+ * Absolute path fix form media file with non relative path on database
75
+ * Internal admin pages review for code coherence with our standard
76
+ * Changed the action name from `newsletter_unsubscribed` to `newsletter_user_unsubscribed`. [See our developer documentation](https://www.thenewsletterplugin.com/documentation/developers/).
77
+ * Removed deprecated function `save_profile()` on NewsletterSubscription
78
+
79
+ = 6.6.5 =
80
+
81
+ * Fixed email reference lost on double step cancellation
82
+ * Update to support Instasend
83
+ * Added "optin" support on shortcodes (see the documentation)
84
+
85
+ = 6.6.4 =
86
+
87
+ * Added filter on profile fields on targeting
88
+ * Administrator notice on custom forms using provate lists
89
+ * Added translation code on some words
90
+ * Compatibility check with WP 5.4.1
91
+ * Tip changed on SMTP panel about GMail
92
+
93
+ = 6.6.3 =
94
+
95
+ * Changed the administration script enqueuing
96
+ * Improved the status page
97
+ * Added notice on page list to highlight the Newsletter dedicated page
98
+
99
+ = 6.6.2 =
100
+
101
+ * Fixed incomplete blocks package on previous release
102
+
103
+ = 6.6.1 =
104
+
105
+ * Improved WMPL permalink filter (not working with custom slugs and old WPML releases)
106
+ * Fixed the hero block not rendering correctly with "left" layout
107
+ * Added the one-click list unsubscribe header
108
+
109
+ = 6.6.0 =
110
+
111
+ * Fixed a jQuery bug on drag and drop composer
112
+ * Fix for report number animations
113
+
114
+ = 6.5.9 =
115
+
116
+ * Revisited the posts block
117
+ * Support for the new Reports addon styles
118
+ * Improved the excerpt extraction
119
+ * Cancellation administrative notifications on/off switch
120
+
121
+ = 6.5.8 =
122
+
123
+ * Improvements on controls
124
+ * Delivery test mode fix for Autoresponder
125
+ * Fixed link to documentation on status panel
126
+ * Added link to documentation on SMTP page
127
+ * Reorganized status page with more links to the documentation
128
+ * Improved image block dimensions management for Outlook
129
+ * Fixed the mobile preview in the composer
130
+ * Added "image to the right" in the hero block
131
+ * Fixed dark color schema in hero block
132
+
133
+ = 6.5.7 =
134
+
135
+ * Fixed posts block to stop automated by default when there are no new contents
136
+
137
+ = 6.5.6 =
138
+
139
+ * Improved post image extraction
140
+
141
+ = 6.5.5 =
142
+
143
+ * Revised the posts block for the new Automated features
144
+ * Added action_link to controls
145
+ * Revised the newsletter regeneration and added blocks behaviors
146
+
147
+ = 6.5.4 =
148
+
149
+ * Font awesome now included
150
+ * Spacing fix on hero block
151
+ * Support methods for the WooCommerce addon
152
+ * Security fix on CSV export (reported by Vishnupriya Ilango of Fortinet's FortiGuard Labs)
153
+ * Removed obsolete fonts
154
+ * PHP 5.6 check
155
+
156
+ = 6.5.3 =
157
+
158
+ * Removed a background wrapper for outlook otherwise outlook cut down long emails
159
+ * Added background gradient (experimental, no supported by all mail clients)
160
+ * Added dark color schema to cta and hero blocks
161
+ * Fixed a debug notice
162
+
163
+ = 6.5.2 =
164
+
165
+ * Fixed image block
166
+ * Newsletter editing page with TinyMCE now saves before sending a test
167
+
168
+ = 6.5.1 =
169
+
170
+ * Solved hero block media problem not showing
171
+
172
+ = 6.5.0 =
173
+
174
+ * Hero block fix
175
+
176
+ = 6.4.9 =
177
+
178
+ * Added background selection to the composer
179
+ * Reactivated the Pint theme
180
+ * Improved blocks layout
181
+ * Inline editing for title and excerpt on Posts block
182
+ * Changed the image cropping method on newsletter included images
183
+ * Posts block can now specify an offset to skip the first *n* posts
184
+ * Fixed the display of multiline title on some blocks (was overlapping)
185
+ * Added the excerpt length on Posts block
186
+ * Removed the shortcodes from generated excerpt for blog post composer block
187
+ * New media resize to better fit the email layout standards
188
+ * Updated image block
189
+
190
+ = 6.4.8 =
191
+
192
+ * Antispam on PHP API
193
+ * Improved hero block layout
194
+ * Added color schema to some blocks
195
+ * Added background gradient (experimental)
196
+ * Added image width control on image block
197
+
198
+ = 6.4.7 =
199
+
200
+ * Default theme thumbnail fix
201
+ * Profile saving antispam check
202
+ * Fixed hero block (button)
203
+ * Added logging on spam checking (enable info lov level to see the spam check results in the logs)
204
+
205
+ = 6.4.6 =
206
+
207
+ * Added hook to [register custom theme folders](https://www.thenewsletterplugin.com/developers/newsletter-themes)
208
+ * Removed old themes
209
+ * Removed packages theme folder scan (so locally added themes are not loaded, see site documentation if you need to create a custom theme)
210
+ * Polylang support on default theme
211
+ * Better error check on addon list retrieval
212
+ * Added language on targeting options
213
+ * Fixed user list pagination
214
+ * Subject replacement for online preview
215
+
216
+ = 6.4.5 =
217
+
218
+ * Restored a missing method used by addons
219
+ * Added the changelog.txt file
220
+
221
+ = 6.4.4 =
222
+
223
+ * Automatic language detection on subscription when available
224
+ * New statistics panel
225
+
226
+ = 6.4.3 =
227
+
228
+ * Fixed error reported in log on wrong subscription calls
229
+
230
+ = 6.4.2 =
231
+
232
+ * Improved the antispam check on subscription
233
+
234
+ = 6.4.1 =
235
+
236
+ * Added statistics shortcut for sent newsletters
237
+ * Fixed unsubscription from email tracking
238
+ * Password field in smtp configuration
239
+ * Small fixes to header block
240
+ * Fixed the subject on online newsletter view
241
+ * Fixed the subject ideas popup X button
242
+
243
+ = 6.4.0 =
244
+
245
+ * Fixed extra profile fields management in REST and PHP API
246
+ * Removed the "read more" added by themes on posts excerpt
247
+ * Core improvements
248
+
249
+ 6.3.9 =
250
 
251
  * Improved antibot and/or spam subscription checks (please review your security configuration)
252
 
emails/blocks/header/block.php CHANGED
@@ -14,7 +14,8 @@ $default_options = array(
14
  'block_padding_top' => 15,
15
  'block_padding_bottom' => 15,
16
  'block_padding_left' => 15,
17
- 'block_padding_right' => 15
 
18
  );
19
  $options = array_merge($default_options, $options);
20
 
14
  'block_padding_top' => 15,
15
  'block_padding_bottom' => 15,
16
  'block_padding_left' => 15,
17
+ 'block_padding_right' => 15,
18
+ 'layout' => ''
19
  );
20
  $options = array_merge($default_options, $options);
21
 
emails/blocks/hero/block.php CHANGED
@@ -23,6 +23,7 @@ $defaults = array(
23
  'block_background' => '#ffffff',
24
  'layout' => 'full',
25
  'button_url' => '',
 
26
  'button_label' => 'Click Here',
27
  'button_font_color' => '#ffffff',
28
  'button_font_weight' => 'bold',
23
  'block_background' => '#ffffff',
24
  'layout' => 'full',
25
  'button_url' => '',
26
+ 'button_font_family' => 'Helvetica, Arial, sans-serif',
27
  'button_label' => 'Click Here',
28
  'button_font_color' => '#ffffff',
29
  'button_font_weight' => 'bold',
emails/blocks/posts/layout-two.php CHANGED
@@ -95,6 +95,9 @@ $size = array('width' => 240, 'height' => 160, "crop" => true);
95
  </table>
96
 
97
  <?php
 
 
 
98
  $media = null;
99
  if ($show_image) {
100
  $media = tnp_composer_block_posts_get_media($row[1], $size, $alternative_2);
95
  </table>
96
 
97
  <?php
98
+ if (!isset($row[1])) {
99
+ continue;
100
+ }
101
  $media = null;
102
  if ($show_image) {
103
  $media = tnp_composer_block_posts_get_media($row[1], $size, $alternative_2);
includes/addon.php CHANGED
@@ -181,10 +181,8 @@ class NewsletterMailerAddon extends NewsletterAddon {
181
  $message->to = $to;
182
  $message->to_name = '';
183
  if (empty($type) || $type == 'html') {
184
- $message->body = "<!DOCTYPE html>\n";
185
- $message->body .= "This is the rich text (HTML) version of a test message.</p>\n";
186
- $message->body .= "This is a <strong>bold text</strong></p>\n";
187
- $message->body .= "This is a <a href='http://www.thenewsletterplugin.com'>link to www.thenewsletterplugin.com</a></p>\n";
188
  }
189
 
190
  if (empty($type) || $type == 'text') {
181
  $message->to = $to;
182
  $message->to_name = '';
183
  if (empty($type) || $type == 'html') {
184
+ $message->body = file_get_contents(NEWSLETTER_DIR . '/includes/test-message.html');
185
+ $message->body = str_replace('{plugin_url}', NEWSLETTER_URL, $message->body);
 
 
186
  }
187
 
188
  if (empty($type) || $type == 'text') {
includes/composer.php CHANGED
@@ -90,7 +90,7 @@ class TNP_Composer {
90
  $open .= NewsletterEmails::instance()->get_composer_css();
91
  $open .= "\n</style>\n";
92
  $open .= "</head>\n";
93
- $open .= '<body style="margin: 0; padding: 0;" dir="' . (is_rtl()?'rtl':'ltr') . '">';
94
  $open .= "\n";
95
  return $open;
96
  }
@@ -206,7 +206,7 @@ class TNP_Composer {
206
  static function prepare_controls($controls, $email) {
207
  foreach ($email->options as $name => $value) {
208
  //if (strpos($name, 'composer_') === 0) {
209
- $controls->data['options_' . $name] = $value;
210
  //}
211
  }
212
 
@@ -244,7 +244,8 @@ class TNP_Composer {
244
  * @return bool
245
  */
246
  static function is_post_field_edited_inline($inline_edit_list, $field_type, $post_id) {
247
- if (empty($inline_edit_list)) return false;
 
248
  foreach ($inline_edit_list as $edit) {
249
  if ($edit['type'] == $field_type && $edit['post_id'] == $post_id) {
250
  return true;
@@ -255,6 +256,18 @@ class TNP_Composer {
255
  }
256
 
257
  static function button($options, $prefix = 'button') {
 
 
 
 
 
 
 
 
 
 
 
 
258
  $b = '<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="border-collapse:separate;line-height:100%;">';
259
  $b .= '<tr>';
260
  $b .= '<td align="center" bgcolor="' . $options[$prefix . '_background'] . '" role="presentation" style="border:none;border-radius:3px;cursor:auto;mso-padding-alt:10px 25px;background:' . $options[$prefix . '_background'] . '" valign="middle">';
@@ -266,7 +279,7 @@ class TNP_Composer {
266
  $b .= '</td></tr></table>';
267
  return $b;
268
  }
269
-
270
  /**
271
  *
272
  * @param TNP_Media $media
@@ -277,20 +290,20 @@ class TNP_Composer {
277
  if ($media->link) {
278
  $b .= '<a href="' . $media->link . '" target="_blank" style="text-decoration: none">';
279
  }
280
-
281
- $b .= '<img src="' . $media->url . '" width="' . $media->width . '"'
282
  . ' height="' . $media->height . '"'
283
  . ' alt="' . esc_attr($media->alt) . '"'
284
  . ' border="0" style="max-width: 100%">';
285
-
286
  if ($media->link) {
287
  $b .= '</a>';
288
  }
289
-
290
  return $b;
291
  }
292
 
293
- /**
294
  * Returns a WP media ID for the specified post (or false if nothing can be found)
295
  * looking for the featured image or, if missing, taking the first media in the gallery and
296
  * if again missing, searching the first reference to a media in the post content.
@@ -317,10 +330,10 @@ class TNP_Composer {
317
  }
318
 
319
  $post = get_post($post_id);
320
-
321
  $r = preg_match('/wp-image-(\d+)/', $post->post_content, $matches);
322
  if ($matches) {
323
- return (int)$matches[1];
324
  }
325
 
326
  return false;
@@ -376,7 +389,7 @@ class TNP_Composer_Grid_System {
376
 
377
  $row_idx = (int) floor($this->cells_counter / $this->cells_per_row);
378
  $this->rows[$row_idx]->add_cell($cell);
379
- $this->cells_counter ++;
380
  }
381
 
382
  private function add_row($row) {
@@ -491,11 +504,11 @@ class TNP_Composer_Grid_Cell {
491
  $column_template = $this->get_template();
492
  $column = str_replace(
493
  [
494
- 'TNP_ALIGN_PH',
495
- 'TNP_VALIGN_PH',
496
- 'TNP_WIDTH_PH',
497
- 'TNP_CLASS_PH',
498
- 'TNP_COLUMN_CONTENT_PH'
499
  ], [
500
  $this->args['align'],
501
  $this->args['valign'],
90
  $open .= NewsletterEmails::instance()->get_composer_css();
91
  $open .= "\n</style>\n";
92
  $open .= "</head>\n";
93
+ $open .= '<body style="margin: 0; padding: 0;" dir="' . (is_rtl() ? 'rtl' : 'ltr') . '">';
94
  $open .= "\n";
95
  return $open;
96
  }
206
  static function prepare_controls($controls, $email) {
207
  foreach ($email->options as $name => $value) {
208
  //if (strpos($name, 'composer_') === 0) {
209
+ $controls->data['options_' . $name] = $value;
210
  //}
211
  }
212
 
244
  * @return bool
245
  */
246
  static function is_post_field_edited_inline($inline_edit_list, $field_type, $post_id) {
247
+ if (empty($inline_edit_list))
248
+ return false;
249
  foreach ($inline_edit_list as $edit) {
250
  if ($edit['type'] == $field_type && $edit['post_id'] == $post_id) {
251
  return true;
256
  }
257
 
258
  static function button($options, $prefix = 'button') {
259
+ $defaults = [
260
+ 'button_url' => '#',
261
+ $prefix . '_font_family' => 'Helvetica, Arial, sans-serif',
262
+ $prefix . '_label' => 'Click Here',
263
+ $prefix . '_font_color' => '#ffffff',
264
+ $prefix . '_font_weight' => 'bold',
265
+ $prefix . '_font_size' => 20,
266
+ $prefix . '_background' => '#256F9C',
267
+ ];
268
+
269
+ $options = array_merge($defaults, $options);
270
+
271
  $b = '<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="border-collapse:separate;line-height:100%;">';
272
  $b .= '<tr>';
273
  $b .= '<td align="center" bgcolor="' . $options[$prefix . '_background'] . '" role="presentation" style="border:none;border-radius:3px;cursor:auto;mso-padding-alt:10px 25px;background:' . $options[$prefix . '_background'] . '" valign="middle">';
279
  $b .= '</td></tr></table>';
280
  return $b;
281
  }
282
+
283
  /**
284
  *
285
  * @param TNP_Media $media
290
  if ($media->link) {
291
  $b .= '<a href="' . $media->link . '" target="_blank" style="text-decoration: none">';
292
  }
293
+
294
+ $b .= '<img src="' . $media->url . '" width="' . $media->width . '"'
295
  . ' height="' . $media->height . '"'
296
  . ' alt="' . esc_attr($media->alt) . '"'
297
  . ' border="0" style="max-width: 100%">';
298
+
299
  if ($media->link) {
300
  $b .= '</a>';
301
  }
302
+
303
  return $b;
304
  }
305
 
306
+ /**
307
  * Returns a WP media ID for the specified post (or false if nothing can be found)
308
  * looking for the featured image or, if missing, taking the first media in the gallery and
309
  * if again missing, searching the first reference to a media in the post content.
330
  }
331
 
332
  $post = get_post($post_id);
333
+
334
  $r = preg_match('/wp-image-(\d+)/', $post->post_content, $matches);
335
  if ($matches) {
336
+ return (int) $matches[1];
337
  }
338
 
339
  return false;
389
 
390
  $row_idx = (int) floor($this->cells_counter / $this->cells_per_row);
391
  $this->rows[$row_idx]->add_cell($cell);
392
+ $this->cells_counter++;
393
  }
394
 
395
  private function add_row($row) {
504
  $column_template = $this->get_template();
505
  $column = str_replace(
506
  [
507
+ 'TNP_ALIGN_PH',
508
+ 'TNP_VALIGN_PH',
509
+ 'TNP_WIDTH_PH',
510
+ 'TNP_CLASS_PH',
511
+ 'TNP_COLUMN_CONTENT_PH'
512
  ], [
513
  $this->args['align'],
514
  $this->args['valign'],
includes/controls.php CHANGED
@@ -1263,6 +1263,50 @@ class NewsletterControls {
1263
  echo '</select>';
1264
  }
1265
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1266
  /**
1267
  * Date and time (hour) selector. Timestamp stored.
1268
  */
1263
  echo '</select>';
1264
  }
1265
 
1266
+ /**
1267
+ * Creates a set of fields to collect a date and sends back the triplet year, month and day.
1268
+ *
1269
+ * @param string $name
1270
+ */
1271
+ function date2($name) {
1272
+ $year = $this->get_value($name . '_year');
1273
+ $day = $this->get_value($name . '_day');
1274
+ $month = $this->get_value($name . '_month');
1275
+
1276
+ echo '<select name="options[' . $name . '_month]">';
1277
+ echo '<option value="">-</option>';
1278
+ for ($i = 1; $i <= 12; $i++) {
1279
+ echo '<option value="' . $i . '"';
1280
+ if ($month - 1 == $i) {
1281
+ echo ' selected';
1282
+ }
1283
+ echo '>' . date('F', mktime(0, 0, 0, $i + 1, 1, 2000)) . '</option>';
1284
+ }
1285
+ echo '</select>';
1286
+
1287
+ echo '<select name="options[' . esc_attr($name) . '_day]">';
1288
+ echo '<option value="">-</option>';
1289
+ for ($i = 1; $i <= 31; $i++) {
1290
+ echo '<option value="' . $i . '"';
1291
+ if ($day == $i) {
1292
+ echo ' selected';
1293
+ }
1294
+ echo '>' . $i . '</option>';
1295
+ }
1296
+ echo '</select>';
1297
+
1298
+ echo '<select name="options[' . esc_attr($name) . '_year]">';
1299
+ echo '<option value="">-</option>';
1300
+ for ($i = 2011; $i <= 2021; $i++) {
1301
+ echo '<option value="' . $i . '"';
1302
+ if ($year == $i) {
1303
+ echo ' selected';
1304
+ }
1305
+ echo '>' . $i . '</option>';
1306
+ }
1307
+ echo '</select>';
1308
+ }
1309
+
1310
  /**
1311
  * Date and time (hour) selector. Timestamp stored.
1312
  */
includes/module.php CHANGED
@@ -126,7 +126,7 @@ class TNP_Profile_Service {
126
  }
127
  $profile = self::create_profile_from_options($profile_options, $i);
128
 
129
- if (is_null($type) ||
130
  ( $type == TNP_Profile::TYPE_SELECT && $profile->is_select() ) ||
131
  ( $type == TNP_Profile::TYPE_TEXT && $profile->is_text() )) {
132
  $profiles[$k]['' . $i] = $profile;
@@ -139,16 +139,8 @@ class TNP_Profile_Service {
139
  static function get_profile_by_id($id, $language = '') {
140
 
141
  $profiles = self::get_profiles($language);
142
- return $profiles[$id];
143
- /*
144
- $profile_options = NewsletterSubscription::instance()->get_options( 'profile', $language );
145
-
146
- if ( empty( $profile_options[ 'profile_' . $id ] ) ) {
147
- return null;
148
- }
149
-
150
- return self::create_profile_from_options( $profile_options, $id );
151
- */
152
  }
153
 
154
  /**
@@ -186,6 +178,7 @@ class TNP_Profile_Service {
186
  * @property string $surname The subscriber last name
187
  * @property string $status The subscriber status
188
  * @property string $language The subscriber language code 2 chars lowercase
 
189
  */
190
  abstract class TNP_User {
191
 
@@ -1122,6 +1115,10 @@ class NewsletterModule {
1122
  * @return string
1123
  */
1124
  function get_user_key($user, $context = '') {
 
 
 
 
1125
  if ($context == 'preconfirm') {
1126
  return $user->id . '-' . md5($user->token);
1127
  }
@@ -2191,28 +2188,10 @@ class NewsletterModule {
2191
 
2192
  protected function generate_admin_notification_message($user) {
2193
 
2194
- $message = "Subscriber details:\n\n" .
2195
- "email: " . $user->email . "\n" .
2196
- "first name: " . $user->name . "\n" .
2197
- "last name: " . $user->surname . "\n" .
2198
- "gender: " . $user->sex . "\n";
2199
-
2200
- $lists = $this->get_lists();
2201
- foreach ($lists as $list) {
2202
- $field = 'list_' . $list->id;
2203
- $message .= $list->name . ': ' . ( empty($user->$field) ? "NO" : "YES" ) . "\n";
2204
- }
2205
-
2206
- for ($i = 0; $i < NEWSLETTER_PROFILE_MAX; $i++) {
2207
- if (empty($this->options_profile['profile_' . $i])) {
2208
- continue;
2209
- }
2210
- $field = 'profile_' . $i;
2211
- $message .= $this->options_profile['profile_' . $i] . ': ' . $user->$field . "\n";
2212
- }
2213
 
2214
- $message .= "token: " . $user->token . "\n" .
2215
- "status: " . $user->status . "\n";
2216
 
2217
  return $message;
2218
  }
126
  }
127
  $profile = self::create_profile_from_options($profile_options, $i);
128
 
129
+ if (empty($type) ||
130
  ( $type == TNP_Profile::TYPE_SELECT && $profile->is_select() ) ||
131
  ( $type == TNP_Profile::TYPE_TEXT && $profile->is_text() )) {
132
  $profiles[$k]['' . $i] = $profile;
139
  static function get_profile_by_id($id, $language = '') {
140
 
141
  $profiles = self::get_profiles($language);
142
+ if (isset($profiles[$id])) return $profiles[$id];
143
+ return null;
 
 
 
 
 
 
 
 
144
  }
145
 
146
  /**
178
  * @property string $surname The subscriber last name
179
  * @property string $status The subscriber status
180
  * @property string $language The subscriber language code 2 chars lowercase
181
+ * @property string $token The subscriber secret token
182
  */
183
  abstract class TNP_User {
184
 
1115
  * @return string
1116
  */
1117
  function get_user_key($user, $context = '') {
1118
+ if (empty($user->token)) {
1119
+ $this->refresh_user_token($user);
1120
+ }
1121
+
1122
  if ($context == 'preconfirm') {
1123
  return $user->id . '-' . md5($user->token);
1124
  }
2188
 
2189
  protected function generate_admin_notification_message($user) {
2190
 
2191
+ $message = file_get_contents(__DIR__ . '/notification.html');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2192
 
2193
+ $message = $this->replace($message, $user);
2194
+ $message = str_replace('{user_admin_url}', admin_url('admin.php?page=newsletter_users_edit&id=' . $user->id), $message);
2195
 
2196
  return $message;
2197
  }
includes/notification.html ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>New subscriber</title>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <style>
8
+ body {
9
+ font-family: Arial;
10
+ }
11
+ </style>
12
+ </head>
13
+ <body>
14
+ <p>Hi,</p>
15
+ <p>a new subscriber has just been registered.</p>
16
+
17
+ <table cellpadding="7" cellspacing="0" border="1">
18
+ <tr>
19
+ <th align="right">Email</th>
20
+ <td>{email}</td>
21
+ </tr>
22
+ <tr>
23
+ <th align="right">First name</th>
24
+ <td>{name}</td>
25
+ </tr>
26
+ <tr>
27
+ <th align="right">Last name</th>
28
+ <td>{surname}</td>
29
+ </tr>
30
+ <tr>
31
+ <td colspan="2" align="center">
32
+ <a href="{user_admin_url}">See the full profile</a>
33
+ </td>
34
+ </tr>
35
+ </table>
36
+ <p style="font-size: .9em">*Some fields may have been updated after this notification by intergration addons</p>
37
+ <hr>
38
+ <p>This is an administrative notification sent to the address set on subscription configuration page of the Newsletter plugin.</p>
39
+ </body>
40
+ </html>
includes/test-message.html ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!doctype html>
2
+ <html>
3
+ <head>
4
+ <title>Test Message by The Newsletter Plugin</title>
5
+ <!--[if !mso]><!-- -->
6
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
7
+ <!--<![endif]-->
8
+ <meta charset="UTF-8">
9
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
10
+ <style>
11
+ body {
12
+ margin: 0;
13
+ padding: 0;
14
+ -webkit-text-size-adjust: 100%;
15
+ -ms-text-size-adjust: 100%;
16
+ }
17
+ img {
18
+ max-width: 100%;
19
+ }
20
+ </style>
21
+ </head>
22
+ <body style="margin: 0; padding: 0">
23
+
24
+ <table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width: 100%;">
25
+ <tbody>
26
+ <tr>
27
+ <td style="direction: ltr; font-size: 16px; padding: 20px 0; text-align: center;">
28
+
29
+ <p>
30
+ That is an HTML email with some <strong>bold text</strong>,
31
+ a <a href="https://www.thenewsletterplugin.com">cool link</a> and an image just below.
32
+ </p>
33
+
34
+ <img src="{plugin_url}/images/test.jpg" width="600" alt="Desk">
35
+
36
+ </td>
37
+ </tr>
38
+ </tbody>
39
+ </table>
40
+
41
+ </body>
42
+ </html>
main/status.php CHANGED
@@ -85,13 +85,7 @@ if ($controls->is_action('test')) {
85
  $controls->messages .= '<strong>Warning:</strong> you are using as test email the same address configured as sender in main configuration. Test can fail because of that.<br>';
86
  }
87
 
88
- $message = new TNP_Mailer_Message();
89
- $message->body = '<p>This is an <b>HTML</b> test email sent using the sender data set on Newsletter main setting. <a href="https://www.thenewsletterplugin.com">This is a link to an external site</a>.</p>';
90
- $message->body_text = 'This is a textual test email part sent using the sender data set on Newsletter main setting.';
91
- $message->to = $controls->data['test_email'];
92
- $message->subject = 'Newsletter test email at ' . date(DATE_ISO8601);
93
- $message->from = $this->options['sender_email'];
94
- $message->from_name = $this->options['sender_name'];
95
 
96
  $r = $this->deliver($message);
97
 
85
  $controls->messages .= '<strong>Warning:</strong> you are using as test email the same address configured as sender in main configuration. Test can fail because of that.<br>';
86
  }
87
 
88
+ $message = NewsletterMailerAddon::get_test_message($controls->data['test_email'], 'Newsletter test email at ' . date(DATE_ISO8601));
 
 
 
 
 
 
89
 
90
  $r = $this->deliver($message);
91
 
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: 6.8.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.
@@ -35,7 +35,7 @@ if (version_compare(phpversion(), '5.6', '<')) {
35
  return;
36
  }
37
 
38
- define('NEWSLETTER_VERSION', '6.8.2');
39
 
40
  global $newsletter, $wpdb;
41
 
@@ -135,6 +135,7 @@ class Newsletter extends NewsletterModule {
135
  }
136
 
137
  function __construct() {
 
138
  // Grab it before a plugin decides to remove it.
139
  if (isset($_GET['na'])) {
140
  $this->action = $_GET['na'];
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: 6.8.3
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.
35
  return;
36
  }
37
 
38
+ define('NEWSLETTER_VERSION', '6.8.3');
39
 
40
  global $newsletter, $wpdb;
41
 
135
  }
136
 
137
  function __construct() {
138
+
139
  // Grab it before a plugin decides to remove it.
140
  if (isset($_GET['na'])) {
141
  $this->action = $_GET['na'];
readme.txt CHANGED
@@ -2,7 +2,7 @@
2
  Tags: email, email marketing, newsletter, newsletter subscribers, welcome email, signup forms, contact, lead generation, popup, marketing automation
3
  Requires at least: 3.4.0
4
  Tested up to: 5.4.2
5
- Stable tag: 6.8.2
6
  Requires PHP: 5.6
7
  Contributors: satollo,webagile,michael-travan
8
 
@@ -113,6 +113,23 @@ Thank you, The Newsletter Team
113
 
114
  == Changelog ==
115
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
116
  = 6.8.2 =
117
 
118
  * Added HTML filtering on block rendering
@@ -130,250 +147,3 @@ Thank you, The Newsletter Team
130
  * Improved HTTP responses for invalid profile links (good for users and acceptance tests)
131
  * Fixed newsletter page url generation with Polylang
132
 
133
- = 6.7.9 =
134
-
135
- * Fixed posts block
136
-
137
- = 6.7.8 =
138
-
139
- * Removed old themes (anyway not usable since long time)
140
-
141
- = 6.7.7 =
142
-
143
- * Fixed text escape for header and footer blocks (was reported as security required fix)
144
-
145
- = 6.7.6 =
146
-
147
- * Fixed error on profile save
148
-
149
- = 6.7.5 =
150
-
151
- * Added language selector on subscriber profile page on multilanguage site
152
- * Fix multilanguage label on profile page
153
- * Added newsletter_user_post_subscribe hook (just after saving the subscription, before saving you can use newsletter_user_subscribe)
154
- * Added checks for WP 5.5 breaking changes
155
- * Added new layout (big images) on posts block
156
- * Added standard button on post block
157
- * Minor fix on hero block
158
- * Added support for Popup Maker (need a free addon)
159
-
160
- = 6.7.4 =
161
-
162
- * Improved posts block in the Automated context with more options
163
- * Improved the unsubscribe header
164
- * Removed the Auto-Submitted header
165
-
166
- = 6.7.3 =
167
-
168
- * Added the company_legal tag
169
- * Change a column type on statitics table to improve performances on site with big subscription lists (100k+)
170
- * Fix attempt on posts block for RTL languages
171
-
172
- = 6.7.2 =
173
-
174
- * Fix post permalink with WPML on language specific setting for Newsletter themes and blocks
175
- * Added action newsletter_user_reactivated
176
-
177
- = 6.7.1 =
178
-
179
- * Changed the lists configuration to be more clear
180
-
181
- = 6.7.0 =
182
-
183
- * Fixed the text part not sent when using the standard mailing function of WP
184
- * Added warning on statistic panel for email without the tracking active
185
- * Fixed the hero block align when specified manually with text-align on text part
186
-
187
- = 6.6.9 =
188
-
189
- * Fixed the admin email preview not showing the correct email after visiting the online view of a newsletter
190
- * Fixed possible not "100%" display percentage for sent newsletters (was only a display problem)
191
-
192
- = 6.6.8 =
193
-
194
- * Fixed newsletter view returning "not found"
195
-
196
- = 6.6.7 =
197
-
198
- * Fixed a debug notice on image resize
199
-
200
- = 6.6.6 =
201
-
202
- * Added check on submission for private lists (error shown only to administrator as debug information)
203
- * Improved right layout on hero block
204
- * Improved internal actions management (better performances)
205
- * Added lists, media selector, charmap on text block
206
- * Absolute path fix form media file with non relative path on database
207
- * Internal admin pages review for code coherence with our standard
208
- * Changed the action name from `newsletter_unsubscribed` to `newsletter_user_unsubscribed`. [See our developer documentation](https://www.thenewsletterplugin.com/documentation/developers/).
209
- * Removed deprecated function `save_profile()` on NewsletterSubscription
210
-
211
- = 6.6.5 =
212
-
213
- * Fixed email reference lost on double step cancellation
214
- * Update to support Instasend
215
- * Added "optin" support on shortcodes (see the documentation)
216
-
217
- = 6.6.4 =
218
-
219
- * Added filter on profile fields on targeting
220
- * Administrator notice on custom forms using provate lists
221
- * Added translation code on some words
222
- * Compatibility check with WP 5.4.1
223
- * Tip changed on SMTP panel about GMail
224
-
225
- = 6.6.3 =
226
-
227
- * Changed the administration script enqueuing
228
- * Improved the status page
229
- * Added notice on page list to highlight the Newsletter dedicated page
230
-
231
- = 6.6.2 =
232
-
233
- * Fixed incomplete blocks package on previous release
234
-
235
- = 6.6.1 =
236
-
237
- * Improved WMPL permalink filter (not working with custom slugs and old WPML releases)
238
- * Fixed the hero block not rendering correctly with "left" layout
239
- * Added the one-click list unsubscribe header
240
-
241
- = 6.6.0 =
242
-
243
- * Fixed a jQuery bug on drag and drop composer
244
- * Fix for report number animations
245
-
246
- = 6.5.9 =
247
-
248
- * Revisited the posts block
249
- * Support for the new Reports addon styles
250
- * Improved the excerpt extraction
251
- * Cancellation administrative notifications on/off switch
252
-
253
- = 6.5.8 =
254
-
255
- * Improvements on controls
256
- * Delivery test mode fix for Autoresponder
257
- * Fixed link to documentation on status panel
258
- * Added link to documentation on SMTP page
259
- * Reorganized status page with more links to the documentation
260
- * Improved image block dimensions management for Outlook
261
- * Fixed the mobile preview in the composer
262
- * Added "image to the right" in the hero block
263
- * Fixed dark color schema in hero block
264
-
265
- = 6.5.7 =
266
-
267
- * Fixed posts block to stop automated by default when there are no new contents
268
-
269
- = 6.5.6 =
270
-
271
- * Improved post image extraction
272
-
273
- = 6.5.5 =
274
-
275
- * Revised the posts block for the new Automated features
276
- * Added action_link to controls
277
- * Revised the newsletter regeneration and added blocks behaviors
278
-
279
- = 6.5.4 =
280
-
281
- * Font awesome now included
282
- * Spacing fix on hero block
283
- * Support methods for the WooCommerce addon
284
- * Security fix on CSV export (reported by Vishnupriya Ilango of Fortinet's FortiGuard Labs)
285
- * Removed obsolete fonts
286
- * PHP 5.6 check
287
-
288
- = 6.5.3 =
289
-
290
- * Removed a background wrapper for outlook otherwise outlook cut down long emails
291
- * Added background gradient (experimental, no supported by all mail clients)
292
- * Added dark color schema to cta and hero blocks
293
- * Fixed a debug notice
294
-
295
- = 6.5.2 =
296
-
297
- * Fixed image block
298
- * Newsletter editing page with TinyMCE now saves before sending a test
299
-
300
- = 6.5.1 =
301
-
302
- * Solved hero block media problem not showing
303
-
304
- = 6.5.0 =
305
-
306
- * Hero block fix
307
-
308
- = 6.4.9 =
309
-
310
- * Added background selection to the composer
311
- * Reactivated the Pint theme
312
- * Improved blocks layout
313
- * Inline editing for title and excerpt on Posts block
314
- * Changed the image cropping method on newsletter included images
315
- * Posts block can now specify an offset to skip the first *n* posts
316
- * Fixed the display of multiline title on some blocks (was overlapping)
317
- * Added the excerpt length on Posts block
318
- * Removed the shortcodes from generated excerpt for blog post composer block
319
- * New media resize to better fit the email layout standards
320
- * Updated image block
321
-
322
- = 6.4.8 =
323
-
324
- * Antispam on PHP API
325
- * Improved hero block layout
326
- * Added color schema to some blocks
327
- * Added background gradient (experimental)
328
- * Added image width control on image block
329
-
330
- = 6.4.7 =
331
-
332
- * Default theme thumbnail fix
333
- * Profile saving antispam check
334
- * Fixed hero block (button)
335
- * Added logging on spam checking (enable info lov level to see the spam check results in the logs)
336
-
337
- = 6.4.6 =
338
-
339
- * Added hook to [register custom theme folders](https://www.thenewsletterplugin.com/developers/newsletter-themes)
340
- * Removed old themes
341
- * Removed packages theme folder scan (so locally added themes are not loaded, see site documentation if you need to create a custom theme)
342
- * Polylang support on default theme
343
- * Better error check on addon list retrieval
344
- * Added language on targeting options
345
- * Fixed user list pagination
346
- * Subject replacement for online preview
347
-
348
- = 6.4.5 =
349
-
350
- * Restored a missing method used by addons
351
- * Added the changelog.txt file
352
-
353
- = 6.4.4 =
354
-
355
- * Automatic language detection on subscription when available
356
- * New statistics panel
357
-
358
- = 6.4.3 =
359
-
360
- * Fixed error reported in log on wrong subscription calls
361
-
362
- = 6.4.2 =
363
-
364
- * Improved the antispam check on subscription
365
-
366
- = 6.4.1 =
367
-
368
- * Added statistics shortcut for sent newsletters
369
- * Fixed unsubscription from email tracking
370
- * Password field in smtp configuration
371
- * Small fixes to header block
372
- * Fixed the subject on online newsletter view
373
- * Fixed the subject ideas popup X button
374
-
375
- = 6.4.0 =
376
-
377
- * Fixed extra profile fields management in REST and PHP API
378
- * Removed the "read more" added by themes on posts excerpt
379
- * Core improvements
2
  Tags: email, email marketing, newsletter, newsletter subscribers, welcome email, signup forms, contact, lead generation, popup, marketing automation
3
  Requires at least: 3.4.0
4
  Tested up to: 5.4.2
5
+ Stable tag: 6.8.3
6
  Requires PHP: 5.6
7
  Contributors: satollo,webagile,michael-travan
8
 
113
 
114
  == Changelog ==
115
 
116
+ = 6.8.3 =
117
+
118
+ * Changed the administrative notification of subscription removing the lists and linking the user profile
119
+ * Fixed missing user token on data from some external sources
120
+ * Extra profiles fix and optmization
121
+ * Improved notificatons for usage of private fields on custom forms
122
+ * Better management of opt-in override on forms and notices for administration when used in wrong ways
123
+ * Fixed the posts block with odd numbers of posts on Automated context
124
+ * Added administrative notices on custom form for invalid list usage
125
+ * Added multilanguage support on lists on custom forms
126
+ * Added multilanguage support on extra profiles on custom forms
127
+ * Fixed CSS for extra profile fields
128
+ * Improved antibot/antispam performances
129
+ * Multilanguage on validation JS (but will be removed in favor of pure HTML validation)
130
+ * Fixed label "for" attribute for extra fields on custom forms
131
+ * Better test message from status panel
132
+
133
  = 6.8.2 =
134
 
135
  * Added HTML filtering on block rendering
147
  * Improved HTTP responses for invalid profile links (good for users and acceptance tests)
148
  * Fixed newsletter page url generation with Polylang
149
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
style.css CHANGED
@@ -80,10 +80,6 @@ CUSTOM CSS RULES.
80
 
81
  /* Profile form */
82
 
83
- .tnp-profile {
84
- font-size: 13px;
85
- }
86
-
87
  .tnp-profile form .tnp-field {
88
  margin-bottom: 10px;
89
  border: 0;
@@ -93,7 +89,6 @@ CUSTOM CSS RULES.
93
  .tnp-profile form .tnp-field label {
94
  display: block;
95
  color: #333;
96
- font-size: 14px;
97
  }
98
 
99
  .tnp-profile form .tnp-field input[type=text],
80
 
81
  /* Profile form */
82
 
 
 
 
 
83
  .tnp-profile form .tnp-field {
84
  margin-bottom: 10px;
85
  border: 0;
89
  .tnp-profile form .tnp-field label {
90
  display: block;
91
  color: #333;
 
92
  }
93
 
94
  .tnp-profile form .tnp-field input[type=text],
style.min.css ADDED
@@ -0,0 +1 @@
 
1
+ .tnp-subscription{font-size:13px;display:block;margin:15px auto;max-width:500px;width:100%}.tnp-subscription div.tnp-field{margin-bottom:10px;border:0;padding:0}.tnp-subscription label{display:block;color:inherit;font-size:14px;font-weight:700;line-height:normal;padding:5px;margin:0}.tnp-subscription input[type=text],.tnp-subscription input[type=email],.tnp-subscription input[type=submit],.tnp-subscription select{width:100%;height:50px;padding:10px;display:block;border:1px;border-color:#ddd;background-color:#f4f4f4;background-image:none;text-shadow:none;color:#444;font-size:14px;line-height:20px;margin:0;line-height:normal;box-sizing:border-box}.tnp-subscription input[type=checkbox],.tnp-widget input[type=radio]{max-width:20px;display:inline-block}.tnp-subscription select option{margin-right:10px}.tnp-subscription input.tnp-submit{background-color:#444;color:#fff;width:auto;height:auto;margin:0}@media all and (max-width:480px){.tnp-subscription input[type=submit]{width:100%}}.tnp-profile form .tnp-field{margin-bottom:10px;border:0;padding:0}.tnp-profile form .tnp-field label{display:block;color:#333}.tnp-profile form .tnp-field input[type=text],.tnp-profile form .tnp-field input[type=email],.tnp-profile form .tnp-field input[type=submit],.tnp-profile form .tnp-field textarea,.tnp-profile form .tnp-field select{padding:10px;display:block;border:1px;border-color:#ddd;background-color:#f4f4f4;background-image:none;text-shadow:none;color:#444;font-size:14px;margin:0;line-height:normal;box-sizing:border-box;border-radius:0;height:auto;float:none}.tnp-profile form input[type=checkbox],.tnp-profile input[type=radio]{max-width:20px;display:inline-block}.tnp-profile form .tnp-list-label{margin-left:15px}.tnp-profile form select option{margin-right:10px}.tnp-profile form .tnp-field input[type=submit]{background-color:#444;color:#fff;width:auto;height:auto;margin:0}@media all and (max-width:480px){.tnp-profile input[type=submit]{width:100%;margin:0}}.tnp-widget{width:100%;display:block;box-sizing:border-box}.tnp-widget .tnp-field{margin-bottom:10px;border:0;padding:0}.tnp-widget label{display:block;color:inherit;font-size:14px}.tnp-widget input[type=text],.tnp-widget input[type=email],.tnp-widget input[type=submit],.tnp-widget select{width:100%;padding:10px;display:block;border:1px solid #ddd;border-color:#ddd;background-color:#f4f4f4;background-image:none;text-shadow:none;color:#444;font-size:14px;line-height:normal;box-sizing:border-box;height:auto}.tnp-widget input[type=checkbox],.tnp-widget input[type=radio]{width:auto;display:inline-block}.tnp-widget select option{margin-right:10px}.tnp-widget input.tnp-submit{background-color:#444;background-image:none;text-shadow:none;color:#fff;margin:0}.tnp-field input[type="submit"]{position:inherit}.tnp-widget-minimal{width:100%}.tnp-widget-minimal form{margin:0;padding:0;border:0}.tnp-widget-minimal input.tnp-email{width:100%;box-sizing:border-box;padding:10px;display:inline-block;border:1px solid #ddd;background-color:#f4f4f4;color:#444;font-size:14px}.tnp-widget-minimal input.tnp-submit{width:100%;box-sizing:border-box;padding:10px;display:inline-block;border:1px;border-color:#ddd;background-color:#444;background-image:none;text-shadow:none;color:#fff;font-size:14px;line-height:normal;border-radius:0;height:auto;margin:0}.tnp-subscription-minimal{width:100%;box-sizing:border-box}.tnp-subscription-minimal form{margin:0;padding:0;border:0}.tnp-subscription-minimal input.tnp-email{width:70%;max-width:300px;box-sizing:border-box;padding:10px;display:inline-block;border:1px solid #ddd;background-color:#f4f4f4;color:#444;font-size:14px;line-height:20px;border-radius:0}.tnp-subscription-minimal .tnp-privacy-field{margin-top:10px}.tnp-subscription-minimal input.tnp-submit{width:29%;box-sizing:border-box;display:inline-block;padding:10px;border:1px;border-color:#ddd;background-color:#444;background-image:none;text-shadow:none;color:#fff;font-size:14px;line-height:20px;border-radius:0;margin:0}.tnp-comments{clear:both;margin-top:15px;margin-bottom:15px}.tnp-comments label{display:block}.tnp-comments input[type=checkbox]{display:inline-block;width:auto!important}.tnp-lock{clear:both;display:block;box-sizing:border-box;box-shadow:none;margin:20px;padding:15px;background-color:#fff;border:1px solid #ddd}.tnp-nl-checkout{margin-bottom:1em}
subscription/profile.php CHANGED
@@ -177,8 +177,8 @@ $extra_type = array('text' => __('Text', 'newsletter'), 'select' => __('List', '
177
  <td><?php $controls->text('privacy_error', 50); ?></td></tr>
178
  </table>
179
  <p class="description">
180
- The privacy acceptance checkbox (required in many Europen countries) force the subscriber to
181
- check it before proceeding. If an URL is specified the label become a link.
182
  </p>
183
  </td>
184
  </tr>
177
  <td><?php $controls->text('privacy_error', 50); ?></td></tr>
178
  </table>
179
  <p class="description">
180
+ The privacy acceptance checkbox (required in many Europen countries) forces the subscriber to
181
+ check it before proceeding. If an URL is specified the label becomes a link.
182
  </p>
183
  </td>
184
  </tr>
subscription/subscription.php CHANGED
@@ -14,6 +14,22 @@ class NewsletterSubscription extends NewsletterModule {
14
  * @var array
15
  */
16
  var $options_profile;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
 
18
  /**
19
  * @var array
@@ -47,6 +63,7 @@ class NewsletterSubscription extends NewsletterModule {
47
  add_action('admin_init', array($this, 'hook_admin_init'));
48
  } else {
49
  add_action('wp_enqueue_scripts', array($this, 'hook_wp_enqueue_scripts'));
 
50
  add_shortcode('newsletter', array($this, 'shortcode_newsletter'));
51
  add_shortcode('newsletter_form', array($this, 'shortcode_newsletter_form'));
52
  add_shortcode('newsletter_field', array($this, 'shortcode_newsletter_field'));
@@ -54,6 +71,7 @@ class NewsletterSubscription extends NewsletterModule {
54
  }
55
 
56
  function hook_admin_init() {
 
57
  if (isset($_GET['page']) && $_GET['page'] === 'newsletter_subscription_forms') {
58
  header('X-XSS-Protection: 0');
59
  }
@@ -106,15 +124,15 @@ class NewsletterSubscription extends NewsletterModule {
106
  }
107
 
108
  function is_address_blacklisted($email) {
109
- // TODO: Optimize!
110
- $options = $this->get_options('antibot');
111
- if (empty($options['address_blacklist'])) {
112
  return false;
113
  }
114
 
115
- $this->logger->debug('Address blacklist check');
116
  $rev_email = strrev($email);
117
- foreach ($options['address_blacklist'] as $item) {
118
  if (strpos($rev_email, strrev($item)) === 0) {
119
  return true;
120
  }
@@ -123,13 +141,13 @@ class NewsletterSubscription extends NewsletterModule {
123
  }
124
 
125
  function is_ip_blacklisted($ip) {
126
- // TODO: Optimize!
127
- $options = $this->get_options('antibot');
128
- if (empty($options['ip_blacklist'])) {
129
  return false;
130
  }
131
- $this->logger->debug('IP blacklist check');
132
- foreach ($options['ip_blacklist'] as $item) {
133
  if ($this->ip_match($ip, $item)) {
134
  return true;
135
  }
@@ -157,18 +175,18 @@ class NewsletterSubscription extends NewsletterModule {
157
 
158
  function is_flood($email, $ip) {
159
  global $wpdb;
160
- // TODO: Optimize!
161
- $options = $this->get_options('antibot');
162
-
163
- if (empty($options['antiflood'])) {
164
  return false;
165
  }
166
 
167
- $this->logger->debug('Antiflood check');
168
 
169
  $updated = $wpdb->get_var($wpdb->prepare("select updated from " . NEWSLETTER_USERS_TABLE . " where ip=%s or email=%s order by updated desc limit 1", $ip, $email));
170
 
171
- if ($updated && time() - $updated < $options['antiflood']) {
172
  return true;
173
  }
174
 
@@ -182,24 +200,22 @@ class NewsletterSubscription extends NewsletterModule {
182
  if (stripos($text, 'www.') !== false) {
183
  return true;
184
  }
185
- // Blocks address like patterns
186
- // if (preg_match('/[^\s]+\.[^\s]+/m', $text)) {
187
- // return true;
188
- // }
189
 
190
  return false;
191
  }
192
 
193
  function is_spam_by_akismet($email, $name, $ip, $agent, $referrer) {
194
- // TODO: Optimize!
195
- $options = $this->get_options('antibot');
196
-
197
- if (empty($options['akismet'])) {
198
  return false;
199
  }
200
- if (!class_exists('Akismet')) {
 
 
 
201
  return false;
202
  }
 
203
 
204
  $this->logger->debug('Akismet check');
205
  $request = 'blog=' . urlencode(home_url()) . '&referrer=' . urlencode($referrer) .
@@ -472,7 +488,7 @@ class NewsletterSubscription extends NewsletterModule {
472
  }
473
 
474
  function first_install() {
475
-
476
  }
477
 
478
  function admin_menu() {
@@ -546,9 +562,11 @@ class NewsletterSubscription extends NewsletterModule {
546
  // All that because for unknown reasome, sometime the options are returned as string, maybe a WPML
547
  // interference...
548
  $i18n_options = get_option('newsletter_profile_' . $language, array());
549
- if (!is_array($i18n_options)) $i18n_options = array();
 
550
  $options = get_option('newsletter_profile', array());
551
- if (!is_array($options)) $options = array();
 
552
  $options = array_merge($options, array_filter($i18n_options));
553
  } else {
554
  $options = get_option('newsletter_profile', array());
@@ -562,6 +580,26 @@ class NewsletterSubscription extends NewsletterModule {
562
  }
563
  return parent::get_options($sub, $language);
564
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
565
 
566
  function set_updated($user, $time = 0, $ip = '') {
567
  global $wpdb;
@@ -596,15 +634,15 @@ class NewsletterSubscription extends NewsletterModule {
596
 
597
  $options_profile = $this->get_options('profile');
598
  /*
599
- if ($options_profile['name_status'] == 0 && isset($_REQUEST['nn'])) {
600
- // Name injection
601
- die();
602
- }
603
- if ($options_profile['surname_status'] == 0 && isset($_REQUEST['ns'])) {
604
- // Last name injection
605
- die();
606
- }
607
- */
608
 
609
  // Validation
610
  $ip = $this->get_remote_ip();
@@ -1002,18 +1040,25 @@ class NewsletterSubscription extends NewsletterModule {
1002
 
1003
  var $privacy_url = false;
1004
 
 
 
 
 
 
1005
  function get_privacy_url() {
1006
- if (!$this->privacy_url) {
1007
- if (!empty($this->options_profile['privacy_use_wp_url']) && function_exists('get_privacy_policy_url')) {
 
1008
  $this->privacy_url = get_privacy_policy_url();
1009
  } else {
1010
- $this->privacy_url = $this->options_profile['privacy_url'];
1011
  }
1012
  }
1013
  return $this->privacy_url;
1014
  }
1015
 
1016
  function get_form_javascript() {
 
1017
 
1018
  $buffer = "\n\n";
1019
  $buffer .= '<script type="text/javascript">' . "\n";
@@ -1022,30 +1067,30 @@ class NewsletterSubscription extends NewsletterModule {
1022
  $buffer .= 'window.newsletter_check = function (f) {' . "\n";
1023
  $buffer .= ' var re = /^([a-zA-Z0-9_\.\-\+])+\@(([a-zA-Z0-9\-]{1,})+\.)+([a-zA-Z0-9]{2,})+$/;' . "\n";
1024
  $buffer .= ' if (!re.test(f.elements["ne"].value)) {' . "\n";
1025
- $buffer .= ' alert("' . addslashes($this->options_profile['email_error']) . '");' . "\n";
1026
  $buffer .= ' return false;' . "\n";
1027
  $buffer .= ' }' . "\n";
1028
- if ($this->options_profile['name_status'] == 2 && $this->options_profile['name_rules'] == 1) {
1029
  $buffer .= ' if (f.elements["nn"] && (f.elements["nn"].value == "" || f.elements["nn"].value == f.elements["nn"].defaultValue)) {' . "\n";
1030
- $buffer .= ' alert("' . addslashes($this->options_profile['name_error']) . '");' . "\n";
1031
  $buffer .= ' return false;' . "\n";
1032
  $buffer .= ' }' . "\n";
1033
  }
1034
- if ($this->options_profile['surname_status'] == 2 && $this->options_profile['surname_rules'] == 1) {
1035
  $buffer .= ' if (f.elements["ns"] && (f.elements["ns"].value == "" || f.elements["ns"].value == f.elements["ns"].defaultValue)) {' . "\n";
1036
- $buffer .= ' alert("' . addslashes($this->options_profile['surname_error']) . '");' . "\n";
1037
  $buffer .= ' return false;' . "\n";
1038
  $buffer .= ' }' . "\n";
1039
  }
1040
  $buffer .= ' for (var i=1; i<' . NEWSLETTER_PROFILE_MAX . '; i++) {' . "\n";
1041
  $buffer .= ' if (f.elements["np" + i] && f.elements["np" + i].required && f.elements["np" + i].value == "") {' . "\n";
1042
- $buffer .= ' alert("' . addslashes($this->options_profile['profile_error']) . '");' . "\n";
1043
  $buffer .= ' return false;' . "\n";
1044
  $buffer .= ' }' . "\n";
1045
  $buffer .= ' }' . "\n";
1046
 
1047
  $buffer .= ' if (f.elements["ny"] && !f.elements["ny"].checked) {' . "\n";
1048
- $buffer .= ' alert("' . addslashes($this->options_profile['privacy_error']) . '");' . "\n";
1049
  $buffer .= ' return false;' . "\n";
1050
  $buffer .= ' }' . "\n";
1051
  $buffer .= ' return true;' . "\n";
@@ -1055,13 +1100,22 @@ class NewsletterSubscription extends NewsletterModule {
1055
  $buffer .= '</script>' . "\n\n";
1056
  return $buffer;
1057
  }
1058
-
1059
- function shortcode_subscription($attrs, $content) {
 
 
 
 
 
 
 
1060
  if (!is_array($attrs)) {
1061
- $attrs = array();
1062
  }
 
 
1063
 
1064
- $attrs = array_merge(array('class' => 'newsletter', 'style' => ''), $attrs);
1065
 
1066
  $action = esc_attr($this->build_action_url('s'));
1067
  $class = esc_attr($attrs['class']);
@@ -1077,7 +1131,7 @@ class NewsletterSubscription extends NewsletterModule {
1077
  }
1078
 
1079
  if (isset($attrs['optin'])) {
1080
- $buffer .= '<input type="hidden" name="optin" value="' . esc_attr($attrs['optin']) . '">' . "\n";
1081
  }
1082
 
1083
  if (isset($attrs['confirmation_url'])) {
@@ -1086,21 +1140,25 @@ class NewsletterSubscription extends NewsletterModule {
1086
  }
1087
  $buffer .= "<input type='hidden' name='ncu' value='" . esc_attr($attrs['confirmation_url']) . "'>\n";
1088
  }
1089
-
 
1090
  if (isset($attrs['list'])) {
1091
- $arr = explode(',', $attrs['list']);
 
 
 
 
1092
  foreach ($arr as $a) {
1093
  $buffer .= "<input type='hidden' name='nl[]' value='" . esc_attr(trim($a)) . "'>\n";
1094
  }
1095
  }
1096
 
1097
- //$content = str_replace("\r\n", "", $content);
1098
  $buffer .= do_shortcode($content);
1099
 
1100
  if (isset($attrs['button_label'])) {
1101
  $label = $attrs['button_label'];
1102
  } else {
1103
- $label = $this->options_profile['subscribe'];
1104
  }
1105
 
1106
  if (!empty($label)) {
@@ -1127,7 +1185,7 @@ class NewsletterSubscription extends NewsletterModule {
1127
  * @return string
1128
  */
1129
  function _shortcode_label($name, $attrs, $suffix = null) {
1130
- $options_profile = $this->get_options('profile', $this->get_current_language());
1131
 
1132
  if (!$suffix) {
1133
  $suffix = $name;
@@ -1140,13 +1198,23 @@ class NewsletterSubscription extends NewsletterModule {
1140
  $buffer .= esc_html($attrs['label']);
1141
  }
1142
  } else {
1143
- $buffer .= esc_html($options_profile[$name]);
1144
  }
1145
  $buffer .= "</label>\n";
1146
  return $buffer;
1147
  }
 
 
 
 
 
 
 
1148
 
1149
  function shortcode_newsletter_field($attrs, $content) {
 
 
 
1150
  $name = $attrs['name'];
1151
 
1152
  $buffer = '';
@@ -1158,7 +1226,7 @@ class NewsletterSubscription extends NewsletterModule {
1158
  $buffer .= '<input class="tnp-email" type="email" name="ne" value=""';
1159
  if (isset($attrs['placeholder']))
1160
  $buffer .= ' placeholder="' . esc_attr($attrs['placeholder']) . '"';
1161
- $buffer .= 'required>';
1162
  if (isset($attrs['button_label'])) {
1163
  $label = $attrs['button_label'];
1164
  if (strpos($label, 'http') === 0) {
@@ -1176,9 +1244,10 @@ class NewsletterSubscription extends NewsletterModule {
1176
  $buffer .= $this->_shortcode_label('name', $attrs);
1177
 
1178
  $buffer .= '<input class="tnp-name" type="text" name="nn" value=""';
1179
- if (isset($attrs['placeholder']))
1180
  $buffer .= ' placeholder="' . esc_attr($attrs['placeholder']) . '"';
1181
- if ($this->options_profile['name_rules'] == 1) {
 
1182
  $buffer .= ' required';
1183
  }
1184
  $buffer .= '>';
@@ -1191,9 +1260,10 @@ class NewsletterSubscription extends NewsletterModule {
1191
  $buffer .= $this->_shortcode_label('surname', $attrs);
1192
 
1193
  $buffer .= '<input class="tnp-surname" type="text" name="ns" value=""';
1194
- if (isset($attrs['placeholder']))
1195
  $buffer .= ' placeholder="' . esc_attr($attrs['placeholder']) . '"';
1196
- if ($this->options_profile['surname_rules'] == 1) {
 
1197
  $buffer .= ' required';
1198
  }
1199
  $buffer .= '>';
@@ -1201,14 +1271,25 @@ class NewsletterSubscription extends NewsletterModule {
1201
  return $buffer;
1202
  }
1203
 
 
1204
  if ($name == 'preference' || $name == 'list') {
1205
- $list = $this->get_list($attrs['number']);
1206
- if (!$list || $list->status == 0 || $list->forced) {
1207
- return;
 
 
 
 
 
 
 
 
1208
  }
 
1209
  if (isset($attrs['hidden'])) {
1210
  return '<input type="hidden" name="nl[]" value="' . esc_attr($list->id) . '">';
1211
  }
 
1212
  $buffer .= '<div class="tnp-field tnp-field-checkbox tnp-field-list"><label for="nl' . esc_attr($list->id) . '">';
1213
  $buffer .= '<input type="checkbox" id="nl' . esc_attr($list->id) . '" name="nl[]" value="' . esc_attr($list->id) . '"';
1214
  if (isset($attrs['checked'])) {
@@ -1226,19 +1307,16 @@ class NewsletterSubscription extends NewsletterModule {
1226
  return $buffer;
1227
  }
1228
 
1229
- // All the lists
1230
  if ($name == 'lists' || $name == 'preferences') {
1231
  $tmp = '';
1232
- $lists = $this->get_lists();
1233
  foreach ($lists as $list) {
1234
- if ($list->status != 2 || $list->forced) {
1235
- continue;
1236
- }
1237
- //die('ddd');
1238
  $tmp .= '<div class="tnp-field tnp-field-checkbox tnp-field-list"><label for="nl' . $list->id . '">';
1239
  $tmp .= '<input type="checkbox" id="nl' . $list->id . '" name="nl[]" value="' . $list->id . '"';
1240
- if ($list->checked)
1241
  $tmp .= ' checked';
 
1242
  $tmp .= '>&nbsp;' . esc_html($list->name) . '</label>';
1243
  $tmp .= "</div>\n";
1244
  }
@@ -1248,56 +1326,42 @@ class NewsletterSubscription extends NewsletterModule {
1248
  // TODO: add the "not specified"
1249
  if ($name == 'sex' || $name == 'gender') {
1250
  $buffer .= '<div class="tnp-field tnp-field-gender">';
1251
- if (isset($attrs['label'])) {
1252
- if ($attrs['label'] != '')
1253
- $buffer .= '<label for="">' . esc_html($attrs['label']) . '</label>';
1254
- } else {
1255
- $buffer .= '<label for="">' . esc_html($this->options_profile['sex']) . '</label>';
1256
- }
1257
 
1258
- $buffer .= '<select name="nx" class="tnp-gender">';
1259
- $buffer .= '<option value="m">' . esc_html($this->options_profile['sex_male']) . '</option>';
1260
- $buffer .= '<option value="f">' . esc_html($this->options_profile['sex_female']) . '</option>';
1261
  $buffer .= '</select>';
1262
  $buffer .= "</div>\n";
1263
  return $buffer;
1264
  }
1265
 
1266
- if ($name == 'profile' && isset($attrs['number'])) {
 
 
 
 
1267
  $number = (int) $attrs['number'];
1268
 
1269
- $profile = TNP_Profile_Service::get_profile_by_id($number);
1270
-
1271
- if ( $profile->status == 0 ) {
1272
- if ( current_user_can( 'administrator' ) ) {
1273
- $buffer .= '<div class="tnp-field tnp-field-profile">';
1274
- $buffer .= sprintf( __( 'Extra profile field \'%s\' is set on private status. Please change extra profile status to show it on registration form. This message is visible only for administrator.', 'newsletter' ),
1275
- esc_html( $profile->name ) );
1276
- $buffer .= "</div>";
1277
-
1278
- return $buffer;
1279
- }
1280
 
1281
- return '';
1282
-
1283
- }
1284
 
 
 
 
1285
 
1286
  $size = isset($attrs['size']) ? $attrs['size'] : '';
1287
  $buffer .= '<div class="tnp-field tnp-field-profile">';
1288
- if (isset($attrs['label'])) {
1289
- if ($attrs['label'] != '') {
1290
- $buffer .= '<label>' . esc_html($attrs['label']) . '</label>';
1291
- }
1292
- } else {
1293
- $buffer .= '<label>' . esc_html($profile->name) . '</label>';
1294
- }
1295
 
1296
  $placeholder = isset($attrs['placeholder']) ? $attrs['placeholder'] : $profile->placeholder;
1297
 
1298
  // Text field
1299
  if ($profile->type == TNP_Profile::TYPE_TEXT) {
1300
- $buffer .= '<input class="tnp-profile tnp-profile-' . $number . '" type="text" size="' . esc_attr($size) . '" name="np' . $number . '" placeholder="' . esc_attr($placeholder) . '"';
1301
  if ($profile->is_required()) {
1302
  $buffer .= ' required';
1303
  }
@@ -1306,7 +1370,7 @@ class NewsletterSubscription extends NewsletterModule {
1306
 
1307
  // Select field
1308
  if ($profile->type == TNP_Profile::TYPE_SELECT) {
1309
- $buffer .= '<select class="tnp-profile tnp-profile-' . $number . '" name="np' . $number . '"';
1310
  if ($profile->is_required()) {
1311
  $buffer .= ' required';
1312
  }
@@ -1314,9 +1378,9 @@ class NewsletterSubscription extends NewsletterModule {
1314
  if (!empty($placeholder)) {
1315
  $buffer .= '<option value="" selected disabled>' . esc_html($placeholder) . '</option>';
1316
  }
1317
- foreach ( $profile->options as $option ) {
1318
- $buffer .= '<option>' . esc_html( trim( $option ) ) . '</option>';
1319
- }
1320
  $buffer .= "</select>\n";
1321
  }
1322
 
@@ -1326,13 +1390,12 @@ class NewsletterSubscription extends NewsletterModule {
1326
  }
1327
 
1328
  if (strpos($name, 'privacy') === 0) {
1329
- $options_profile = $this->get_options('profile', $this->get_current_language());
1330
  if (!isset($attrs['url'])) {
1331
  $attrs['url'] = $this->get_privacy_url();
1332
  }
1333
 
1334
  if (!isset($attrs['label'])) {
1335
- $attrs['label'] = $options_profile['privacy'];
1336
  }
1337
 
1338
  $buffer .= '<div class="tnp-field tnp-field-checkbox tnp-field-privacy">';
@@ -1363,11 +1426,17 @@ class NewsletterSubscription extends NewsletterModule {
1363
  return $this->get_subscription_form($referrer, $action, $attrs);
1364
  }
1365
 
1366
- function get_privacy_field() {
1367
- $options_profile = $this->get_options('profile', $this->get_current_language());
1368
- $privacy_status = (int) $options_profile['privacy_status'];
1369
- if (empty($privacy_status))
1370
- return false;
 
 
 
 
 
 
1371
 
1372
  $buffer = '<label>';
1373
  if ($privacy_status === 1) {
@@ -1376,14 +1445,40 @@ class NewsletterSubscription extends NewsletterModule {
1376
  $url = $this->get_privacy_url();
1377
  if (!empty($url)) {
1378
  $buffer .= '<a target="_blank" href="' . esc_attr($url) . '">';
1379
- $buffer .= esc_attr($options_profile['privacy']) . '</a>';
1380
  } else {
1381
- $buffer .= esc_html($options_profile['privacy']);
1382
  }
1383
 
1384
  $buffer .= "</label>";
1385
 
1386
- return $buffer;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1387
  }
1388
 
1389
  /**
@@ -1427,7 +1522,7 @@ class NewsletterSubscription extends NewsletterModule {
1427
  $buffer .= '<input type="hidden" name="nlang" value="' . esc_attr($language) . '">' . "\n";
1428
 
1429
  if (!empty($attrs['optin'])) {
1430
- $buffer .= '<input type="hidden" name="optin" value="' . esc_attr($attrs['optin']) . '">' . "\n";
1431
  }
1432
 
1433
  if (!empty($referrer)) {
@@ -1567,24 +1662,7 @@ class NewsletterSubscription extends NewsletterModule {
1567
  $buffer .= $x['field'] . "</div>\n";
1568
  }
1569
 
1570
- $privacy_status = (int) $options_profile['privacy_status'];
1571
-
1572
- if ($privacy_status === 1 || $privacy_status === 2) {
1573
- $buffer .= '<div class="tnp-field tnp-field-privacy">';
1574
- $buffer .= '<label>';
1575
- if ($privacy_status === 1) {
1576
- $buffer .= '<input type="checkbox" name="ny" required class="tnp-privacy">&nbsp;';
1577
- }
1578
- $url = $this->get_privacy_url();
1579
- if (!empty($url)) {
1580
- $buffer .= '<a target="_blank" href="' . esc_attr($url) . '">';
1581
- $buffer .= esc_attr($options_profile['privacy']) . '</a>';
1582
- } else {
1583
- $buffer .= esc_html($options_profile['privacy']);
1584
- }
1585
-
1586
- $buffer .= "</label></div>\n";
1587
- }
1588
 
1589
  $buffer .= '<div class="tnp-field tnp-field-button">';
1590
 
@@ -1648,30 +1726,31 @@ class NewsletterSubscription extends NewsletterModule {
1648
  return $buffer;
1649
  }
1650
 
1651
- function notify_admin_on_subscription( $user ) {
1652
 
1653
- if ( empty( $this->options['notify'] ) ) {
1654
- return;
1655
- }
1656
-
1657
- $message = $this->generate_admin_notification_message( $user );
1658
- $email = trim( $this->options['notify_email'] );
1659
- $subject = $this->generate_admin_notification_subject( 'Newsletter subscription' );
1660
 
1661
- Newsletter::instance()->mail( $email, $subject, array( 'text' => $message ) );
 
 
1662
 
1663
- }
 
1664
 
1665
  function get_subscription_form_minimal($attrs) {
1666
 
1667
  $language = $this->get_current_language();
 
 
1668
  if (!is_array($attrs)) {
1669
- $attrs = array();
1670
  }
1671
- $options_profile = $this->get_options('profile', $language);
1672
  $attrs = array_merge(array('class' => '', 'referrer' => 'minimal',
1673
- 'button' => $options_profile['subscribe'], 'button_color' => '',
1674
- 'button_radius' => '', 'placeholder' => $options_profile['email']), $attrs);
1675
 
1676
  $form = '';
1677
  $form .= '<div class="tnp tnp-subscription-minimal ' . $attrs['class'] . '">';
@@ -1688,10 +1767,7 @@ class NewsletterSubscription extends NewsletterModule {
1688
  $form .= '<input class="tnp-submit" type="submit" value="' . esc_attr($attrs['button']) . '"'
1689
  . ' style="background-color:' . esc_attr($attrs['button_color']) . '">';
1690
 
1691
- $privacy_field = $this->get_privacy_field();
1692
- if (!empty($privacy_field)) {
1693
- $form .= '<div class="tnp-privacy-field">' . $privacy_field . '</div>';
1694
- }
1695
 
1696
  $form .= "</form></div>\n";
1697
 
@@ -1701,23 +1777,25 @@ class NewsletterSubscription extends NewsletterModule {
1701
  function shortcode_newsletter_form($attrs, $content) {
1702
 
1703
  if (isset($attrs['type']) && $attrs['type'] == 'minimal') {
1704
- return NewsletterSubscription::instance()->get_subscription_form_minimal($attrs);
1705
  }
1706
 
 
1707
  if (!empty($content)) {
1708
- return NewsletterSubscription::instance()->shortcode_subscription($attrs, $content);
1709
  }
 
 
1710
  if (isset($attrs['form'])) {
1711
- return NewsletterSubscription::instance()->get_form((int) $attrs['form']);
1712
- } else if (isset($attrs['number'])) {
1713
- return NewsletterSubscription::instance()->get_form((int) $attrs['number']);
1714
- } else {
1715
- if (isset($attrs['layout']) && $attrs['layout'] == 'table') {
1716
- return NewsletterSubscription::instance()->get_subscription_form(null, null, $attrs);
1717
- } else {
1718
- return NewsletterSubscription::instance()->get_subscription_form_html5(null, null, $attrs);
1719
- }
1720
- }
1721
  }
1722
 
1723
  /**
@@ -1767,7 +1845,7 @@ class NewsletterSubscription extends NewsletterModule {
1767
  if (isset($attrs['form'])) {
1768
  $message = str_replace('{subscription_form}', $this->get_form($attrs['form']), $message);
1769
  } else {
1770
- $message = str_replace('{subscription_form}', $this->get_subscription_form('page'), $message);
1771
  }
1772
  }
1773
  }
@@ -1780,7 +1858,7 @@ class NewsletterSubscription extends NewsletterModule {
1780
 
1781
  if (isset($_REQUEST['alert'])) {
1782
  // slashes are already added by wordpress!
1783
- $message .= '<script>alert("' . strip_tags($_REQUEST['alert']) . '");</script>';
1784
  }
1785
 
1786
  return $message;
@@ -1792,6 +1870,10 @@ NewsletterSubscription::instance();
1792
 
1793
  // Compatibility code
1794
 
 
 
 
 
1795
  function newsletter_form($number = null) {
1796
  if ($number != null) {
1797
  echo NewsletterSubscription::instance()->get_form($number);
14
  * @var array
15
  */
16
  var $options_profile;
17
+
18
+ /**
19
+ * Contains the options for the current language to build a subscription form. Must be initialized with
20
+ * setup_form_options() before use.
21
+ *
22
+ * @var array
23
+ */
24
+ var $form_options = null;
25
+
26
+ /**
27
+ * Contains the antibot/antispam options. Must be initialized with
28
+ * setup_antibot_options() before use.
29
+ *
30
+ * @var array
31
+ */
32
+ var $antibot_options = null;
33
 
34
  /**
35
  * @var array
63
  add_action('admin_init', array($this, 'hook_admin_init'));
64
  } else {
65
  add_action('wp_enqueue_scripts', array($this, 'hook_wp_enqueue_scripts'));
66
+ // Shortcode for the Newsletter page
67
  add_shortcode('newsletter', array($this, 'shortcode_newsletter'));
68
  add_shortcode('newsletter_form', array($this, 'shortcode_newsletter_form'));
69
  add_shortcode('newsletter_field', array($this, 'shortcode_newsletter_field'));
71
  }
72
 
73
  function hook_admin_init() {
74
+ // So the user can add JS and other code
75
  if (isset($_GET['page']) && $_GET['page'] === 'newsletter_subscription_forms') {
76
  header('X-XSS-Protection: 0');
77
  }
124
  }
125
 
126
  function is_address_blacklisted($email) {
127
+ $this->setup_antibot_options();
128
+
129
+ if (empty($this->antibot_options['address_blacklist'])) {
130
  return false;
131
  }
132
 
133
+ //$this->logger->debug('Address blacklist check');
134
  $rev_email = strrev($email);
135
+ foreach ($this->antibot_options['address_blacklist'] as $item) {
136
  if (strpos($rev_email, strrev($item)) === 0) {
137
  return true;
138
  }
141
  }
142
 
143
  function is_ip_blacklisted($ip) {
144
+ $this->setup_antibot_options();
145
+
146
+ if (empty($this->antibot_options['ip_blacklist'])) {
147
  return false;
148
  }
149
+ //$this->logger->debug('IP blacklist check');
150
+ foreach ($this->antibot_options['ip_blacklist'] as $item) {
151
  if ($this->ip_match($ip, $item)) {
152
  return true;
153
  }
175
 
176
  function is_flood($email, $ip) {
177
  global $wpdb;
178
+
179
+ $this->setup_antibot_options();
180
+
181
+ if (empty($this->antibot_options['antiflood'])) {
182
  return false;
183
  }
184
 
185
+ //$this->logger->debug('Antiflood check');
186
 
187
  $updated = $wpdb->get_var($wpdb->prepare("select updated from " . NEWSLETTER_USERS_TABLE . " where ip=%s or email=%s order by updated desc limit 1", $ip, $email));
188
 
189
+ if ($updated && time() - $updated < $this->antibot_options['antiflood']) {
190
  return true;
191
  }
192
 
200
  if (stripos($text, 'www.') !== false) {
201
  return true;
202
  }
 
 
 
 
203
 
204
  return false;
205
  }
206
 
207
  function is_spam_by_akismet($email, $name, $ip, $agent, $referrer) {
208
+
209
+ if (!class_exists('Akismet')) {
 
 
210
  return false;
211
  }
212
+
213
+ $this->setup_antibot_options();
214
+
215
+ if (empty($this->antibot_options['akismet'])) {
216
  return false;
217
  }
218
+
219
 
220
  $this->logger->debug('Akismet check');
221
  $request = 'blog=' . urlencode(home_url()) . '&referrer=' . urlencode($referrer) .
488
  }
489
 
490
  function first_install() {
491
+
492
  }
493
 
494
  function admin_menu() {
562
  // All that because for unknown reasome, sometime the options are returned as string, maybe a WPML
563
  // interference...
564
  $i18n_options = get_option('newsletter_profile_' . $language, array());
565
+ if (!is_array($i18n_options))
566
+ $i18n_options = array();
567
  $options = get_option('newsletter_profile', array());
568
+ if (!is_array($options))
569
+ $options = array();
570
  $options = array_merge($options, array_filter($i18n_options));
571
  } else {
572
  $options = get_option('newsletter_profile', array());
580
  }
581
  return parent::get_options($sub, $language);
582
  }
583
+
584
+ /**
585
+ * Prepares the options used to build a subscription form for the current language
586
+ * in internal variable $form_options (for optimization). Can be called many times.
587
+ */
588
+ function setup_form_options() {
589
+ if (empty($this->form_options)) {
590
+ $this->form_options = $this->get_options('profile', $this->get_current_language());
591
+ }
592
+ }
593
+
594
+ function setup_antibot_options() {
595
+ if (empty($this->antibot_options)) {
596
+ $this->antibot_options = $this->get_options('antibot');
597
+ }
598
+ }
599
+
600
+ function get_form_options($language = '') {
601
+ return $this->get_options('profile', $language);
602
+ }
603
 
604
  function set_updated($user, $time = 0, $ip = '') {
605
  global $wpdb;
634
 
635
  $options_profile = $this->get_options('profile');
636
  /*
637
+ if ($options_profile['name_status'] == 0 && isset($_REQUEST['nn'])) {
638
+ // Name injection
639
+ die();
640
+ }
641
+ if ($options_profile['surname_status'] == 0 && isset($_REQUEST['ns'])) {
642
+ // Last name injection
643
+ die();
644
+ }
645
+ */
646
 
647
  // Validation
648
  $ip = $this->get_remote_ip();
1040
 
1041
  var $privacy_url = false;
1042
 
1043
+ /**
1044
+ * Generates the privacy URL and cache it.
1045
+ *
1046
+ * @return string
1047
+ */
1048
  function get_privacy_url() {
1049
+ if ($this->privacy_url === false) {
1050
+ $this->setup_form_options();
1051
+ if (!empty($this->form_options['privacy_use_wp_url']) && function_exists('get_privacy_policy_url')) {
1052
  $this->privacy_url = get_privacy_policy_url();
1053
  } else {
1054
+ $this->privacy_url = $this->form_options['privacy_url'];
1055
  }
1056
  }
1057
  return $this->privacy_url;
1058
  }
1059
 
1060
  function get_form_javascript() {
1061
+ $this->setup_form_options();
1062
 
1063
  $buffer = "\n\n";
1064
  $buffer .= '<script type="text/javascript">' . "\n";
1067
  $buffer .= 'window.newsletter_check = function (f) {' . "\n";
1068
  $buffer .= ' var re = /^([a-zA-Z0-9_\.\-\+])+\@(([a-zA-Z0-9\-]{1,})+\.)+([a-zA-Z0-9]{2,})+$/;' . "\n";
1069
  $buffer .= ' if (!re.test(f.elements["ne"].value)) {' . "\n";
1070
+ $buffer .= ' alert("' . addslashes($this->form_options['email_error']) . '");' . "\n";
1071
  $buffer .= ' return false;' . "\n";
1072
  $buffer .= ' }' . "\n";
1073
+ if ($this->form_options['name_status'] == 2 && $this->form_options['name_rules'] == 1) {
1074
  $buffer .= ' if (f.elements["nn"] && (f.elements["nn"].value == "" || f.elements["nn"].value == f.elements["nn"].defaultValue)) {' . "\n";
1075
+ $buffer .= ' alert("' . addslashes($this->form_options['name_error']) . '");' . "\n";
1076
  $buffer .= ' return false;' . "\n";
1077
  $buffer .= ' }' . "\n";
1078
  }
1079
+ if ($this->form_options['surname_status'] == 2 && $this->form_options['surname_rules'] == 1) {
1080
  $buffer .= ' if (f.elements["ns"] && (f.elements["ns"].value == "" || f.elements["ns"].value == f.elements["ns"].defaultValue)) {' . "\n";
1081
+ $buffer .= ' alert("' . addslashes($this->form_options['surname_error']) . '");' . "\n";
1082
  $buffer .= ' return false;' . "\n";
1083
  $buffer .= ' }' . "\n";
1084
  }
1085
  $buffer .= ' for (var i=1; i<' . NEWSLETTER_PROFILE_MAX . '; i++) {' . "\n";
1086
  $buffer .= ' if (f.elements["np" + i] && f.elements["np" + i].required && f.elements["np" + i].value == "") {' . "\n";
1087
+ $buffer .= ' alert("' . addslashes($this->form_options['profile_error']) . '");' . "\n";
1088
  $buffer .= ' return false;' . "\n";
1089
  $buffer .= ' }' . "\n";
1090
  $buffer .= ' }' . "\n";
1091
 
1092
  $buffer .= ' if (f.elements["ny"] && !f.elements["ny"].checked) {' . "\n";
1093
+ $buffer .= ' alert("' . addslashes($this->form_options['privacy_error']) . '");' . "\n";
1094
  $buffer .= ' return false;' . "\n";
1095
  $buffer .= ' }' . "\n";
1096
  $buffer .= ' return true;' . "\n";
1100
  $buffer .= '</script>' . "\n\n";
1101
  return $buffer;
1102
  }
1103
+
1104
+ /**
1105
+ * Manages the custom forms made with [newsletter_form] and internal [newsletter_field] shorcodes.
1106
+ *
1107
+ * @param array $attrs
1108
+ * @param string $content
1109
+ * @return string
1110
+ */
1111
+ function get_subscription_form_custom($attrs = [], $content = '') {
1112
  if (!is_array($attrs)) {
1113
+ $attrs = [];
1114
  }
1115
+
1116
+ $this->setup_form_options();
1117
 
1118
+ $attrs = array_merge(['class' => 'newsletter', 'style' => ''], $attrs);
1119
 
1120
  $action = esc_attr($this->build_action_url('s'));
1121
  $class = esc_attr($attrs['class']);
1131
  }
1132
 
1133
  if (isset($attrs['optin'])) {
1134
+ $buffer .= $this->build_optin_field($attrs['optin']);
1135
  }
1136
 
1137
  if (isset($attrs['confirmation_url'])) {
1140
  }
1141
  $buffer .= "<input type='hidden' name='ncu' value='" . esc_attr($attrs['confirmation_url']) . "'>\n";
1142
  }
1143
+
1144
+ // Compatibility
1145
  if (isset($attrs['list'])) {
1146
+ $attrs['lists'] = $attrs['list'];
1147
+ }
1148
+
1149
+ if (isset($attrs['lists'])) {
1150
+ $arr = explode(',', $attrs['lists']);
1151
  foreach ($arr as $a) {
1152
  $buffer .= "<input type='hidden' name='nl[]' value='" . esc_attr(trim($a)) . "'>\n";
1153
  }
1154
  }
1155
 
 
1156
  $buffer .= do_shortcode($content);
1157
 
1158
  if (isset($attrs['button_label'])) {
1159
  $label = $attrs['button_label'];
1160
  } else {
1161
+ $label = $this->form_options['subscribe'];
1162
  }
1163
 
1164
  if (!empty($label)) {
1185
  * @return string
1186
  */
1187
  function _shortcode_label($name, $attrs, $suffix = null) {
1188
+ //$this->setup_form_options();
1189
 
1190
  if (!$suffix) {
1191
  $suffix = $name;
1198
  $buffer .= esc_html($attrs['label']);
1199
  }
1200
  } else {
1201
+ $buffer .= esc_html($this->form_options[$name]);
1202
  }
1203
  $buffer .= "</label>\n";
1204
  return $buffer;
1205
  }
1206
+
1207
+ function build_field_admin_notice($message) {
1208
+ if (!current_user_can('administrator')) {
1209
+ return '';
1210
+ }
1211
+ return '<p style="background-color: #eee; color: #000; padding: 10px; margin: 10px 0">' . $message . ' <strong>This notice is shown only to administrators.</strong></p>';
1212
+ }
1213
 
1214
  function shortcode_newsletter_field($attrs, $content) {
1215
+ $this->setup_form_options();
1216
+ $language = $this->get_current_language();
1217
+
1218
  $name = $attrs['name'];
1219
 
1220
  $buffer = '';
1226
  $buffer .= '<input class="tnp-email" type="email" name="ne" value=""';
1227
  if (isset($attrs['placeholder']))
1228
  $buffer .= ' placeholder="' . esc_attr($attrs['placeholder']) . '"';
1229
+ $buffer .= ' required>';
1230
  if (isset($attrs['button_label'])) {
1231
  $label = $attrs['button_label'];
1232
  if (strpos($label, 'http') === 0) {
1244
  $buffer .= $this->_shortcode_label('name', $attrs);
1245
 
1246
  $buffer .= '<input class="tnp-name" type="text" name="nn" value=""';
1247
+ if (isset($attrs['placeholder'])) {
1248
  $buffer .= ' placeholder="' . esc_attr($attrs['placeholder']) . '"';
1249
+ }
1250
+ if ($this->form_options['name_rules'] == 1) {
1251
  $buffer .= ' required';
1252
  }
1253
  $buffer .= '>';
1260
  $buffer .= $this->_shortcode_label('surname', $attrs);
1261
 
1262
  $buffer .= '<input class="tnp-surname" type="text" name="ns" value=""';
1263
+ if (isset($attrs['placeholder'])) {
1264
  $buffer .= ' placeholder="' . esc_attr($attrs['placeholder']) . '"';
1265
+ }
1266
+ if ($this->form_options['surname_rules'] == 1) {
1267
  $buffer .= ' required';
1268
  }
1269
  $buffer .= '>';
1271
  return $buffer;
1272
  }
1273
 
1274
+ // Single list
1275
  if ($name == 'preference' || $name == 'list') {
1276
+ if (!isset($attrs['number'])) {
1277
+ return $this->build_field_admin_notice('List number not specified.');
1278
+ }
1279
+ $number = (int)$attrs['number'];
1280
+ $list = $this->get_list($number, $language);
1281
+ if (!$list) {
1282
+ return $this->build_field_admin_notice('List ' . $number . ' is not configured, cannot be shown.');
1283
+ }
1284
+
1285
+ if ($list->status == 0 || $list->forced) {
1286
+ return $this->build_field_admin_notice('List ' . $number . ' is private or enforced cannot be shown.');
1287
  }
1288
+
1289
  if (isset($attrs['hidden'])) {
1290
  return '<input type="hidden" name="nl[]" value="' . esc_attr($list->id) . '">';
1291
  }
1292
+
1293
  $buffer .= '<div class="tnp-field tnp-field-checkbox tnp-field-list"><label for="nl' . esc_attr($list->id) . '">';
1294
  $buffer .= '<input type="checkbox" id="nl' . esc_attr($list->id) . '" name="nl[]" value="' . esc_attr($list->id) . '"';
1295
  if (isset($attrs['checked'])) {
1307
  return $buffer;
1308
  }
1309
 
1310
+ // All lists
1311
  if ($name == 'lists' || $name == 'preferences') {
1312
  $tmp = '';
1313
+ $lists = $this->get_lists_for_subscription($language);
1314
  foreach ($lists as $list) {
 
 
 
 
1315
  $tmp .= '<div class="tnp-field tnp-field-checkbox tnp-field-list"><label for="nl' . $list->id . '">';
1316
  $tmp .= '<input type="checkbox" id="nl' . $list->id . '" name="nl[]" value="' . $list->id . '"';
1317
+ if ($list->checked) {
1318
  $tmp .= ' checked';
1319
+ }
1320
  $tmp .= '>&nbsp;' . esc_html($list->name) . '</label>';
1321
  $tmp .= "</div>\n";
1322
  }
1326
  // TODO: add the "not specified"
1327
  if ($name == 'sex' || $name == 'gender') {
1328
  $buffer .= '<div class="tnp-field tnp-field-gender">';
1329
+ $buffer .= $this->_shortcode_label('sex', $attrs);
 
 
 
 
 
1330
 
1331
+ $buffer .= '<select name="nx" class="tnp-gender" id="tnp-gender">';
1332
+ $buffer .= '<option value="m">' . esc_html($this->form_options['sex_male']) . '</option>';
1333
+ $buffer .= '<option value="f">' . esc_html($this->form_options['sex_female']) . '</option>';
1334
  $buffer .= '</select>';
1335
  $buffer .= "</div>\n";
1336
  return $buffer;
1337
  }
1338
 
1339
+ if ($name == 'profile') {
1340
+ if (!isset($attrs['number'])) {
1341
+ return $this->build_field_admin_notice('Extra profile number not specified.');
1342
+ }
1343
+
1344
  $number = (int) $attrs['number'];
1345
 
1346
+ $profile = TNP_Profile_Service::get_profile_by_id($number, $language);
 
 
 
 
 
 
 
 
 
 
1347
 
1348
+ if (!$profile) {
1349
+ return $this->build_field_admin_notice('Extra profile ' . $number . ' is not configured, cannot be shown.');
1350
+ }
1351
 
1352
+ if ($profile->status == 0) {
1353
+ return $this->build_field_admin_notice('Extra profile ' . $number . ' is private, cannot be shown.');
1354
+ }
1355
 
1356
  $size = isset($attrs['size']) ? $attrs['size'] : '';
1357
  $buffer .= '<div class="tnp-field tnp-field-profile">';
1358
+ $buffer .= $this->_shortcode_label('profile_' . $profile->id, $attrs);
 
 
 
 
 
 
1359
 
1360
  $placeholder = isset($attrs['placeholder']) ? $attrs['placeholder'] : $profile->placeholder;
1361
 
1362
  // Text field
1363
  if ($profile->type == TNP_Profile::TYPE_TEXT) {
1364
+ $buffer .= '<input class="tnp-profile tnp-profile-' . $number . '" id="tnp-profile_' . $number . '" type="text" size="' . esc_attr($size) . '" name="np' . $number . '" placeholder="' . esc_attr($placeholder) . '"';
1365
  if ($profile->is_required()) {
1366
  $buffer .= ' required';
1367
  }
1370
 
1371
  // Select field
1372
  if ($profile->type == TNP_Profile::TYPE_SELECT) {
1373
+ $buffer .= '<select class="tnp-profile tnp-profile-' . $number . '" id="tnp-profile_' . $number . '" name="np' . $number . '"';
1374
  if ($profile->is_required()) {
1375
  $buffer .= ' required';
1376
  }
1378
  if (!empty($placeholder)) {
1379
  $buffer .= '<option value="" selected disabled>' . esc_html($placeholder) . '</option>';
1380
  }
1381
+ foreach ($profile->options as $option) {
1382
+ $buffer .= '<option>' . esc_html(trim($option)) . '</option>';
1383
+ }
1384
  $buffer .= "</select>\n";
1385
  }
1386
 
1390
  }
1391
 
1392
  if (strpos($name, 'privacy') === 0) {
 
1393
  if (!isset($attrs['url'])) {
1394
  $attrs['url'] = $this->get_privacy_url();
1395
  }
1396
 
1397
  if (!isset($attrs['label'])) {
1398
+ $attrs['label'] = $this->form_options['privacy'];
1399
  }
1400
 
1401
  $buffer .= '<div class="tnp-field tnp-field-checkbox tnp-field-privacy">';
1426
  return $this->get_subscription_form($referrer, $action, $attrs);
1427
  }
1428
 
1429
+ /**
1430
+ * Builds the privacy field only for completely generated forms.
1431
+ *
1432
+ * @return string Empty id the privacy filed is not configured
1433
+ */
1434
+ function get_privacy_field($pre_html = '', $post_html = '') {
1435
+ $this->setup_form_options();
1436
+ $privacy_status = (int) $this->form_options['privacy_status'];
1437
+ if (empty($privacy_status)) {
1438
+ return '';
1439
+ }
1440
 
1441
  $buffer = '<label>';
1442
  if ($privacy_status === 1) {
1445
  $url = $this->get_privacy_url();
1446
  if (!empty($url)) {
1447
  $buffer .= '<a target="_blank" href="' . esc_attr($url) . '">';
1448
+ $buffer .= esc_attr($this->form_options['privacy']) . '</a>';
1449
  } else {
1450
+ $buffer .= esc_html($this->form_options['privacy']);
1451
  }
1452
 
1453
  $buffer .= "</label>";
1454
 
1455
+ return $pre_html . $buffer . $post_html;
1456
+ }
1457
+
1458
+ /**
1459
+ * Creates the hidden alternative optin field on custom form showing notices if used in the wrong
1460
+ * way.
1461
+ *
1462
+ * @since 6.8.3
1463
+ * @param string $optin Could be single or double, lowercase
1464
+ * @return string The complete HTML field
1465
+ */
1466
+ function build_optin_field($optin) {
1467
+ if (empty($optin)) {
1468
+ return '';
1469
+ }
1470
+
1471
+ if ($optin !== 'double' && $optin !== 'single') {
1472
+ return $this->build_field_admin_notice('The optin is set to an invalid value.');
1473
+ }
1474
+
1475
+ if ($optin !== 'double' && $this->is_double_optin() && empty($this->options['optin_override'])) {
1476
+ return $this->build_field_admin_notice('The optin is specified but cannot be overridden (see the subscription configiraton page).');
1477
+ }
1478
+
1479
+ $b = '<input type="hidden" name="optin" value="' . esc_attr($optin) . '">' . "\n";
1480
+
1481
+ return $b;
1482
  }
1483
 
1484
  /**
1522
  $buffer .= '<input type="hidden" name="nlang" value="' . esc_attr($language) . '">' . "\n";
1523
 
1524
  if (!empty($attrs['optin'])) {
1525
+ $buffer .= $this->build_optin_field($attrs['optin']);
1526
  }
1527
 
1528
  if (!empty($referrer)) {
1662
  $buffer .= $x['field'] . "</div>\n";
1663
  }
1664
 
1665
+ $buffer .= $this->get_privacy_field('<div class="tnp-field tnp-privacy-field">', '</div>');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1666
 
1667
  $buffer .= '<div class="tnp-field tnp-field-button">';
1668
 
1726
  return $buffer;
1727
  }
1728
 
1729
+ function notify_admin_on_subscription($user) {
1730
 
1731
+ if (empty($this->options['notify'])) {
1732
+ return;
1733
+ }
 
 
 
 
1734
 
1735
+ $message = $this->generate_admin_notification_message($user);
1736
+ $email = trim($this->options['notify_email']);
1737
+ $subject = $this->generate_admin_notification_subject('Newsletter subscription');
1738
 
1739
+ Newsletter::instance()->mail($email, $subject, array('html' => $message));
1740
+ }
1741
 
1742
  function get_subscription_form_minimal($attrs) {
1743
 
1744
  $language = $this->get_current_language();
1745
+ $this->setup_form_options();
1746
+
1747
  if (!is_array($attrs)) {
1748
+ $attrs = [];
1749
  }
1750
+
1751
  $attrs = array_merge(array('class' => '', 'referrer' => 'minimal',
1752
+ 'button' => $this->form_options['subscribe'], 'button_color' => '',
1753
+ 'button_radius' => '', 'placeholder' => $this->form_options['email']), $attrs);
1754
 
1755
  $form = '';
1756
  $form .= '<div class="tnp tnp-subscription-minimal ' . $attrs['class'] . '">';
1767
  $form .= '<input class="tnp-submit" type="submit" value="' . esc_attr($attrs['button']) . '"'
1768
  . ' style="background-color:' . esc_attr($attrs['button_color']) . '">';
1769
 
1770
+ $form .= $this->get_privacy_field('<div class="tnp_field tnp-privacy-field">', '</div>');
 
 
 
1771
 
1772
  $form .= "</form></div>\n";
1773
 
1777
  function shortcode_newsletter_form($attrs, $content) {
1778
 
1779
  if (isset($attrs['type']) && $attrs['type'] == 'minimal') {
1780
+ return $this->get_subscription_form_minimal($attrs);
1781
  }
1782
 
1783
+ // Custom form using the [newsletter_field] shortcodes
1784
  if (!empty($content)) {
1785
+ return $this->get_subscription_form_custom($attrs, $content);
1786
  }
1787
+
1788
+ // Custom form hand coded and saved in the custom forms option
1789
  if (isset($attrs['form'])) {
1790
+ return $this->get_form((int) $attrs['form']);
1791
+ }
1792
+
1793
+ // Custom hand coded form (as above, new syntax)
1794
+ if (isset($attrs['number'])) {
1795
+ return $this->get_form((int) $attrs['number']);
1796
+ }
1797
+
1798
+ return $this->get_subscription_form(null, null, $attrs);
 
1799
  }
1800
 
1801
  /**
1845
  if (isset($attrs['form'])) {
1846
  $message = str_replace('{subscription_form}', $this->get_form($attrs['form']), $message);
1847
  } else {
1848
+ $message = str_replace('{subscription_form}', $this->get_subscription_form('page', null, $attrs), $message);
1849
  }
1850
  }
1851
  }
1858
 
1859
  if (isset($_REQUEST['alert'])) {
1860
  // slashes are already added by wordpress!
1861
+ $message .= '<script>alert("' . esc_js(strip_tags($_REQUEST['alert'])) . '");</script>';
1862
  }
1863
 
1864
  return $message;
1870
 
1871
  // Compatibility code
1872
 
1873
+ /**
1874
+ * @deprecated
1875
+ * @param int $number
1876
+ */
1877
  function newsletter_form($number = null) {
1878
  if ($number != null) {
1879
  echo NewsletterSubscription::instance()->get_form($number);
users/import.php CHANGED
@@ -234,7 +234,7 @@ if ($controls->is_action('import')) {
234
  </div>
235
  </th>
236
  <td>
237
- <textarea name="options[csv]" wrap="off" style="width: 100%; height: 200px; font-size: 11px; font-family: monospace"><?php echo $controls->data['csv']; ?></textarea>
238
  </td>
239
  </tr>
240
  <tr>
234
  </div>
235
  </th>
236
  <td>
237
+ <textarea name="options[csv]" wrap="off" style="width: 100%; height: 200px; font-size: 11px; font-family: monospace"><?php echo $controls->get_value('csv'); ?></textarea>
238
  </td>
239
  </tr>
240
  <tr>