Newsletter - Version 7.3.6

Version Description

  • Improved composer reusability in other contexts
  • Removed obsolete composer code
  • Fixed default tracking for old theme-based neewsletters
  • Forced enconding on export (attempt)
  • WP 5.9 check
Download this release

Release Info

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

Code changes from version 7.3.5 to 7.3.6

admin/admin.css CHANGED
@@ -35,35 +35,35 @@
35
  }
36
 
37
  .container {
38
- width: 100%;
39
- padding-right: 1rem;
40
- padding-left: 1rem;
41
- margin-right: auto;
42
- margin-left: auto;
43
  }
44
 
45
  @media (min-width: 576px) {
46
- .container {
47
- max-width: 540px;
48
- }
49
  }
50
 
51
  @media (min-width: 768px) {
52
- .container {
53
- max-width: 720px;
54
- }
55
  }
56
 
57
  @media (min-width: 992px) {
58
- .container {
59
- max-width: 960px;
60
- }
61
  }
62
 
63
  @media (min-width: 1200px) {
64
- .container {
65
- max-width: 1140px;
66
- }
67
  }
68
 
69
  .row:before,
@@ -205,6 +205,11 @@
205
  margin-bottom: 10px;
206
  }
207
 
 
 
 
 
 
208
 
209
  #tnp-body ul {
210
  list-style-type: circle;
@@ -233,7 +238,7 @@
233
  #tnp-body .button-primary,
234
  #tnp-body .button-primary:visited,
235
  #tnp-body .button-primary:hover {
236
- color: #fff;
237
  text-shadow: none;
238
  width: auto;
239
  vertical-align: bottom;
35
  }
36
 
37
  .container {
38
+ width: 100%;
39
+ padding-right: 1rem;
40
+ padding-left: 1rem;
41
+ margin-right: auto;
42
+ margin-left: auto;
43
  }
44
 
45
  @media (min-width: 576px) {
46
+ .container {
47
+ max-width: 540px;
48
+ }
49
  }
50
 
51
  @media (min-width: 768px) {
52
+ .container {
53
+ max-width: 720px;
54
+ }
55
  }
56
 
57
  @media (min-width: 992px) {
58
+ .container {
59
+ max-width: 960px;
60
+ }
61
  }
62
 
63
  @media (min-width: 1200px) {
64
+ .container {
65
+ max-width: 1140px;
66
+ }
67
  }
68
 
69
  .row:before,
205
  margin-bottom: 10px;
206
  }
207
 
208
+ #tnp-body hr {
209
+ height: 0px;
210
+ border: 0;
211
+ border-top: 1px solid #666;
212
+ }
213
 
214
  #tnp-body ul {
215
  list-style-type: circle;
238
  #tnp-body .button-primary,
239
  #tnp-body .button-primary:visited,
240
  #tnp-body .button-primary:hover {
241
+ color: #fff;
242
  text-shadow: none;
243
  width: auto;
244
  vertical-align: bottom;
emails/blocks/header/block.php CHANGED
@@ -1,50 +1,50 @@
1
- <?php
2
-
3
- /*
4
- * Name: Header
5
- * Section: header
6
- * Description: Default header with company info
7
- */
8
-
9
- $default_options = array(
10
- 'font_family' => '',
11
- 'font_size' => '',
12
- 'font_color' => '',
13
- 'font_weight' => '',
14
- 'logo_height' => 100,
15
- 'logo_width' => '',
16
- 'block_padding_top' => 15,
17
- 'block_padding_bottom' => 15,
18
- 'block_padding_left' => 15,
19
- 'block_padding_right' => 15,
20
- 'block_background' => '',
21
- 'layout' => ''
22
- );
23
- $options = array_merge($default_options, $options);
24
-
25
- if (empty($info['header_logo']['id'])) {
26
- $media = false;
27
- } else {
28
- $media = tnp_get_media($info['header_logo']['id'], 'large');
29
- if ($media) {
30
- $media->alt = $info['header_title'];
31
- $media->link = home_url();
32
- }
33
- }
34
-
35
- $empty = !$media && empty($info['header_sub']) && empty($info['header_title']);
36
-
37
- if ($empty) {
38
- echo '<p>Please, set your company info.</p>';
39
- } elseif ($options['layout'] === 'logo') {
40
- include __DIR__ . '/layout-logo.php';
41
- return;
42
- } elseif ($options['layout'] === 'titlemotto') {
43
- include __DIR__ . '/layout-titlemotto.php';
44
- return;
45
- } else {
46
- include __DIR__ . '/layout-default.php';
47
- return;
48
- }
49
- ?>
50
-
1
+ <?php
2
+
3
+ /*
4
+ * Name: Header
5
+ * Section: header
6
+ * Description: Default header with company info
7
+ */
8
+
9
+ $default_options = array(
10
+ 'font_family' => '',
11
+ 'font_size' => '',
12
+ 'font_color' => '',
13
+ 'font_weight' => '',
14
+ 'logo_height' => 100,
15
+ 'logo_width' => '',
16
+ 'block_padding_top' => 15,
17
+ 'block_padding_bottom' => 15,
18
+ 'block_padding_left' => 15,
19
+ 'block_padding_right' => 15,
20
+ 'block_background' => '',
21
+ 'layout' => ''
22
+ );
23
+ $options = array_merge($default_options, $options);
24
+
25
+ if (empty($info['header_logo']['id'])) {
26
+ $media = false;
27
+ } else {
28
+ $media = tnp_get_media($info['header_logo']['id'], 'large');
29
+ if ($media) {
30
+ $media->alt = $info['header_title'];
31
+ $media->link = home_url();
32
+ }
33
+ }
34
+
35
+ $empty = !$media && empty($info['header_sub']) && empty($info['header_title']);
36
+
37
+ if ($empty) {
38
+ echo '<p>Please, set your company info.</p>';
39
+ } elseif ($options['layout'] === 'logo') {
40
+ include __DIR__ . '/layout-logo.php';
41
+ return;
42
+ } elseif ($options['layout'] === 'titlemotto') {
43
+ include __DIR__ . '/layout-titlemotto.php';
44
+ return;
45
+ } else {
46
+ include __DIR__ . '/layout-default.php';
47
+ return;
48
+ }
49
+ ?>
50
+
emails/composer.php CHANGED
@@ -22,7 +22,7 @@ if ($controls->is_action()) {
22
  TNP_Composer::update_email($email, $controls);
23
  $email->type = NewsletterEmails::PRESET_EMAIL_TYPE;
24
  $email->editor = NewsletterEmails::EDITOR_COMPOSER;
25
- $email->subject = $module->sanitize_preset_name($controls->data['subject']);
26
  $email->message = $controls->data['message'];
27
 
28
  $email = Newsletter::instance()->save_email($email);
@@ -33,14 +33,12 @@ if ($controls->is_action()) {
33
  return;
34
  }
35
 
36
- if ($controls->is_action('update_preset') && !empty($_POST['preset_id'])) {
37
  $this->admin_logger->info('Updating preset ' . $_POST['preset_id']);
38
- $email = Newsletter::instance()->get_email((int) $_POST['preset_id']);
39
  TNP_Composer::update_email($email, $controls);
40
 
41
- if ($email->subject != sanitize_text_field($controls->data['subject'])) {
42
- $email->subject = $module->sanitize_preset_name($controls->data['subject']);
43
- }
44
 
45
  // We store only the blocks, after the TNP_Composer::update_email(...) call we have the full HTML
46
  $email->message = $controls->data['message'];
22
  TNP_Composer::update_email($email, $controls);
23
  $email->type = NewsletterEmails::PRESET_EMAIL_TYPE;
24
  $email->editor = NewsletterEmails::EDITOR_COMPOSER;
25
+ $email->subject = $controls->data['subject'];
26
  $email->message = $controls->data['message'];
27
 
28
  $email = Newsletter::instance()->save_email($email);
33
  return;
34
  }
35
 
36
+ if ($controls->is_action('update_preset')) {
37
  $this->admin_logger->info('Updating preset ' . $_POST['preset_id']);
38
+ $email = Newsletter::instance()->get_email($_POST['preset_id']);
39
  TNP_Composer::update_email($email, $controls);
40
 
41
+ $email->subject = $controls->data['subject'];
 
 
42
 
43
  // We store only the blocks, after the TNP_Composer::update_email(...) call we have the full HTML
44
  $email->message = $controls->data['message'];
emails/emails.php CHANGED
@@ -33,18 +33,9 @@ class NewsletterEmails extends NewsletterModule {
33
  $this->themes = new NewsletterThemes('emails');
34
  parent::__construct('emails', '1.1.5');
35
  add_action('newsletter_action', array($this, 'hook_newsletter_action'), 13, 3);
36
-
 
37
  if (is_admin()) {
38
- if (defined('DOING_AJAX') && DOING_AJAX) {
39
- add_action('wp_ajax_tnpc_render', array($this, 'tnpc_render_callback'));
40
- add_action('wp_ajax_tnpc_preview', array($this, 'tnpc_preview_callback'));
41
- add_action('wp_ajax_tnpc_css', array($this, 'tnpc_css_callback'));
42
- add_action('wp_ajax_tnpc_options', array($this, 'hook_wp_ajax_tnpc_options'));
43
- add_action('wp_ajax_tnpc_get_all_presets', array($this, 'ajax_get_all_presets'));
44
- add_action('wp_ajax_tnpc_get_preset', array($this, 'ajax_get_preset'));
45
- add_action('wp_ajax_tnpc_delete_preset', array($this, 'hook_wp_ajax_tnpc_delete_preset'));
46
- add_action('wp_ajax_tnpc_regenerate_email', array($this, 'hook_wp_ajax_tnpc_regenerate_email'));
47
- }
48
  // Thank you to plugins which add the WP editor on other admin plugin pages...
49
  if (isset($_GET['page']) && $_GET['page'] == 'newsletter_emails_edit') {
50
  global $wp_actions;
@@ -52,21 +43,37 @@ class NewsletterEmails extends NewsletterModule {
52
  }
53
  }
54
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
 
56
  function options_decode($options) {
57
-
58
- // Start compatibility
59
  if (is_string($options) && strpos($options, 'options[') !== false) {
60
- $opts = array();
61
  parse_str($options, $opts);
62
  $options = $opts['options'];
63
  }
64
- // End compatibility
65
 
66
  if (is_array($options)) {
67
  return $options;
68
  }
69
 
 
70
  $tmp = json_decode($options, true);
71
  if (is_null($tmp)) {
72
  return json_decode(base64_decode($options), true);
@@ -83,14 +90,14 @@ class NewsletterEmails extends NewsletterModule {
83
  return base64_encode(json_encode($options, JSON_HEX_TAG | JSON_HEX_AMP));
84
  }
85
 
 
 
 
 
 
86
  function hook_wp_ajax_tnpc_options() {
87
  global $wpdb;
88
 
89
- // TODO: Uniform to use id everywhere
90
- // if (!isset($_REQUEST['id'])) {
91
- // $_REQUEST['id'] = $_REQUEST['b'];
92
- // }
93
-
94
  $block = $this->get_block($_REQUEST['id']);
95
  if (!$block) {
96
  die('Block not found with id ' . esc_html($_REQUEST['id']));
@@ -120,12 +127,8 @@ class NewsletterEmails extends NewsletterModule {
120
  $inline_edits = $controls->data['inline_edits'];
121
  }
122
  echo '<input type="hidden" name="options[inline_edits]" value="', esc_attr($this->options_encode($inline_edits)), '">';
123
-
124
- ob_start();
125
- include $block['dir'] . '/options.php';
126
- $content = ob_get_clean();
127
  echo "<h2>", esc_html($block["name"]), "</h2>";
128
- echo $content;
129
  wp_die();
130
  }
131
 
@@ -310,26 +313,6 @@ class NewsletterEmails extends NewsletterModule {
310
  return $result;
311
  }
312
 
313
- /**
314
- * Check if the preset name exists and adds an incremental suffix if the name exists.
315
- *
316
- * @param string $name
317
- *
318
- * @return string
319
- */
320
- public function sanitize_preset_name($name) {
321
- global $wpdb;
322
-
323
- $name = empty($name) ? __('Empty name preset', 'newsletter') : $name;
324
- $name = sanitize_text_field($name);
325
- $type = self::PRESET_EMAIL_TYPE;
326
- $count = (int) $wpdb->get_var("SELECT COUNT(*) FROM " . NEWSLETTER_EMAILS_TABLE . " WHERE type='$type' and subject='$name'");
327
-
328
- $name = $count > 0 ? $name . " - " . ($count + 1) : $name;
329
-
330
- return $name;
331
- }
332
-
333
  function has_dynamic_blocks($theme) {
334
  preg_match_all('/data-json="(.*?)"/m', $theme, $matches, PREG_PATTERN_ORDER);
335
  foreach ($matches[1] as $match) {
@@ -356,29 +339,14 @@ class NewsletterEmails extends NewsletterModule {
356
  *
357
  * $email can actually be even a string containing the full newsletter HTML code.
358
  *
359
- * @param TNP_Email $email (Rinominare)
360
  * @return string
361
  */
362
  function regenerate($email, $context = []) {
363
 
364
- // Cannot be removed due to compatibility issues with old Automated versions
365
- if (is_object($email)) {
366
- $theme = $email->message;
367
- } else {
368
- $theme = $email;
369
- }
370
-
371
- //$this->logger->debug('Starting email regeneration');
372
- //$this->logger->debug($context);
373
-
374
- if (empty($theme)) {
375
- $this->logger->debug('The email was empty');
376
- return array('body' => '', 'subject' => '');
377
- }
378
-
379
  $context = array_merge(['last_run' => 0, 'type' => ''], $context);
380
 
381
- preg_match_all('/data-json="(.*?)"/m', $theme, $matches, PREG_PATTERN_ORDER);
382
 
383
  $result = '';
384
  $subject = '';
@@ -397,15 +365,9 @@ class NewsletterEmails extends NewsletterModule {
397
  $out = $this->render_block($options['block_id'], true, $options, $context);
398
  if (is_array($out)) {
399
  if ($out['return_empty_message'] || $out['stop']) {
400
- if (is_object($email)) {
401
- return false;
402
- }
403
- return array();
404
  }
405
  if ($out['skip']) {
406
- if (NEWSLETTER_DEBUG) {
407
- $result .= 'Block removed by request';
408
- }
409
  continue;
410
  }
411
  if (empty($subject) && !empty($out['subject'])) {
@@ -416,28 +378,10 @@ class NewsletterEmails extends NewsletterModule {
416
  $result .= $block_html;
417
  }
418
 
419
- // We need to keep the CSS/HEAD part, the regenearion is only about blocks
420
-
421
- if (is_object($email)) {
422
- $result = TNP_Composer::get_main_wrapper_open($email) . $result . TNP_Composer::get_main_wrapper_close($email);
423
- }
424
-
425
- $x = strpos($theme, '<body');
426
- if ($x !== false) {
427
- $x = strpos($theme, '>', $x);
428
- $result = substr($theme, 0, $x + 1) . $result . '</body></html>';
429
- } else {
430
-
431
- }
432
-
433
- if (is_object($email)) {
434
- $email->message = $result;
435
- $email->subject = $subject;
436
- return true;
437
- }
438
-
439
- // Kept for compatibility
440
- return array('body' => $result, 'subject' => $subject);
441
  }
442
 
443
  function remove_block_data($text) {
@@ -529,7 +473,7 @@ class NewsletterEmails extends NewsletterModule {
529
  }
530
  echo $this->get_outlook_wrapper_open($width);
531
 
532
- echo '<p>Ops, this block type is no more registered!</p>';
533
 
534
  echo $this->get_outlook_wrapper_close();
535
 
@@ -907,7 +851,6 @@ class NewsletterEmails extends NewsletterModule {
907
  die();
908
  break;
909
 
910
-
911
  case 'emails-create':
912
  // Newsletter from themes are created on frontend context because sometime WP themes change the way the content,
913
  // excerpt, thumbnail are extracted.
@@ -934,7 +877,7 @@ class NewsletterEmails extends NewsletterModule {
934
  $email = array();
935
  $email['status'] = 'new';
936
  $email['subject'] = ''; //__('Here the email subject', 'newsletter');
937
- $email['track'] = 1;
938
  $email['send_on'] = time();
939
  $email['editor'] = NewsletterEmails::EDITOR_TINYMCE;
940
  $email['type'] = 'message';
@@ -948,7 +891,6 @@ class NewsletterEmails extends NewsletterModule {
948
  include $theme['dir'] . '/theme.php';
949
  $email['message'] = ob_get_clean();
950
 
951
-
952
  if (!empty($theme_subject)) {
953
  $email['subject'] = $theme_subject;
954
  }
@@ -997,7 +939,7 @@ class NewsletterEmails extends NewsletterModule {
997
  if (!is_file($full_file)) {
998
  return new WP_Error('1', 'Missing block.php file in ' . $dir);
999
  }
1000
-
1001
  $relative_dir = substr($dir, strlen(WP_CONTENT_DIR));
1002
  $file = basename($dir);
1003
 
@@ -1264,86 +1206,85 @@ class NewsletterEmails extends NewsletterModule {
1264
  $email->subject = $original_subject;
1265
  }
1266
 
1267
- /**
1268
- * Send an email to the test subscribers.
1269
- *
1270
- * @param TNP_Email $email Could be any object with the TNP_Email attributes
1271
- * @param string $email_address
1272
- *
1273
- * @throws Exception
1274
- */
1275
- function send_test_newsletter_to_email_address( $email, $email_address ) {
1276
-
1277
- if ( ! $email ) {
1278
- throw new Exception( __( 'Newsletter should be saved before send a test', 'newsletter' ) );
1279
- }
1280
-
1281
- $this->set_test_subject_to( $email );
1282
-
1283
- $dummy_subscriber = $this->make_dummy_subscriber();
1284
- $dummy_subscriber->email = $email_address;
1285
-
1286
- $result = Newsletter::instance()->send( $email, [ $dummy_subscriber ], true );
1287
-
1288
- $email = '<a href="admin.php?page=newsletter_users_edit&id=' . $dummy_subscriber->id . '" target="_blank">' . $dummy_subscriber->email . '</a>';
1289
-
1290
- if ( is_wp_error( $result ) ) {
1291
- $error_message = 'Something went wrong. Check the error logs on status page.<br>';
1292
- $error_message .= __( 'Test subscribers:', 'newsletter' );
1293
- $error_message .= ' ' . $email;
1294
- $error_message .= '<br>';
1295
- $error_message .= '<strong>' . esc_html( $result->get_error_message() ) . '</strong><br>';
1296
- $error_message .= '<a href="https://www.thenewsletterplugin.com/documentation/email-sending-issues" target="_blank"><strong>' . __( 'Read more about delivery issues', 'newsletter' ) . '</strong></a>.';
1297
- throw new Exception( $error_message );
1298
- }
1299
-
1300
- $messages = __( 'Test subscribers:', 'newsletter' );
1301
-
1302
- $messages .= ' ' . $email;
1303
- $messages .= '.<br>';
1304
- $messages .= '<a href="https://www.thenewsletterplugin.com/documentation/subscribers#test" target="_blank"><strong>' .
1305
- __( 'Read more about test subscribers', 'newsletter' ) . '</strong></a>.<br>';
1306
- $messages .= '<a href="https://www.thenewsletterplugin.com/documentation/email-sending-issues" target="_blank"><strong>' . __( 'Read more about delivery issues', 'newsletter' ) . '</strong></a>.';
1307
-
1308
- return $messages;
1309
- }
1310
-
1311
- private function set_test_subject_to($email) {
1312
- if ( $email->subject == '' ) {
1313
- $email->subject = '[TEST] Dummy subject, it was empty (remember to set it)';
1314
- } else {
1315
- $email->subject = $email->subject . ' (TEST)';
1316
- }
1317
- }
1318
-
1319
- private function make_dummy_subscriber() {
1320
- $dummy_user = new TNP_User();
1321
- $dummy_user->id = 0;
1322
- $dummy_user->email = 'john.doe@example.org';
1323
- $dummy_user->name = 'John';
1324
- $dummy_user->surname = 'Doe';
1325
- $dummy_user->sex = 'n';
1326
- $dummy_user->language = '';
1327
- $dummy_user->ip = '';
1328
-
1329
- for ( $i = 1; $i <= NEWSLETTER_PROFILE_MAX; $i ++ ) {
1330
- $profile_key = "profile_$i";
1331
- $dummy_user->$profile_key = '';
1332
- }
1333
-
1334
- return $dummy_user;
1335
- }
1336
 
1337
  function restore_options_from_request() {
1338
-
1339
  require_once NEWSLETTER_INCLUDES_DIR . '/controls.php';
1340
  $controls = new NewsletterControls();
1341
  $options = $controls->data;
1342
-
1343
  if (isset($_POST['options']) && is_array($_POST['options'])) {
1344
  // Get all block options
1345
  //$options = stripslashes_deep($_POST['options']);
1346
-
1347
  // Deserialize inline edits when
1348
  // render is preformed on saving block options
1349
  if (isset($options['inline_edits']) && !is_array($options['inline_edits'])) {
33
  $this->themes = new NewsletterThemes('emails');
34
  parent::__construct('emails', '1.1.5');
35
  add_action('newsletter_action', array($this, 'hook_newsletter_action'), 13, 3);
36
+ add_action('newsletter_init', [$this, 'hook_newsletter_init']);
37
+
38
  if (is_admin()) {
 
 
 
 
 
 
 
 
 
 
39
  // Thank you to plugins which add the WP editor on other admin plugin pages...
40
  if (isset($_GET['page']) && $_GET['page'] == 'newsletter_emails_edit') {
41
  global $wp_actions;
43
  }
44
  }
45
  }
46
+
47
+ function hook_newsletter_init() {
48
+ if (is_admin()) {
49
+ if (defined('DOING_AJAX') && DOING_AJAX) {
50
+ if (Newsletter::instance()->is_allowed()) {
51
+ add_action('wp_ajax_tnpc_render', array($this, 'tnpc_render_callback'));
52
+ add_action('wp_ajax_tnpc_preview', array($this, 'tnpc_preview_callback'));
53
+ add_action('wp_ajax_tnpc_css', array($this, 'tnpc_css_callback'));
54
+ add_action('wp_ajax_tnpc_options', array($this, 'hook_wp_ajax_tnpc_options'));
55
+ add_action('wp_ajax_tnpc_get_all_presets', array($this, 'ajax_get_all_presets'));
56
+ add_action('wp_ajax_tnpc_get_preset', array($this, 'ajax_get_preset'));
57
+ add_action('wp_ajax_tnpc_delete_preset', array($this, 'hook_wp_ajax_tnpc_delete_preset'));
58
+ add_action('wp_ajax_tnpc_regenerate_email', array($this, 'hook_wp_ajax_tnpc_regenerate_email'));
59
+ }
60
+ }
61
+ }
62
+ }
63
 
64
  function options_decode($options) {
65
+ // Old "query string" format
 
66
  if (is_string($options) && strpos($options, 'options[') !== false) {
67
+ $opts = [];
68
  parse_str($options, $opts);
69
  $options = $opts['options'];
70
  }
 
71
 
72
  if (is_array($options)) {
73
  return $options;
74
  }
75
 
76
+ // Json data should be base64 encoded, but for short time it wasn't
77
  $tmp = json_decode($options, true);
78
  if (is_null($tmp)) {
79
  return json_decode(base64_decode($options), true);
90
  return base64_encode(json_encode($options, JSON_HEX_TAG | JSON_HEX_AMP));
91
  }
92
 
93
+ /**
94
+ * Builds and returns the HTML with the form fields of a specific block.
95
+ *
96
+ * @global wpdb $wpdb
97
+ */
98
  function hook_wp_ajax_tnpc_options() {
99
  global $wpdb;
100
 
 
 
 
 
 
101
  $block = $this->get_block($_REQUEST['id']);
102
  if (!$block) {
103
  die('Block not found with id ' . esc_html($_REQUEST['id']));
127
  $inline_edits = $controls->data['inline_edits'];
128
  }
129
  echo '<input type="hidden" name="options[inline_edits]" value="', esc_attr($this->options_encode($inline_edits)), '">';
 
 
 
 
130
  echo "<h2>", esc_html($block["name"]), "</h2>";
131
+ include $block['dir'] . '/options.php';
132
  wp_die();
133
  }
134
 
313
  return $result;
314
  }
315
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
316
  function has_dynamic_blocks($theme) {
317
  preg_match_all('/data-json="(.*?)"/m', $theme, $matches, PREG_PATTERN_ORDER);
318
  foreach ($matches[1] as $match) {
339
  *
340
  * $email can actually be even a string containing the full newsletter HTML code.
341
  *
342
+ * @param TNP_Email $email
343
  * @return string
344
  */
345
  function regenerate($email, $context = []) {
346
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
347
  $context = array_merge(['last_run' => 0, 'type' => ''], $context);
348
 
349
+ preg_match_all('/data-json="(.*?)"/m', $email->message, $matches, PREG_PATTERN_ORDER);
350
 
351
  $result = '';
352
  $subject = '';
365
  $out = $this->render_block($options['block_id'], true, $options, $context);
366
  if (is_array($out)) {
367
  if ($out['return_empty_message'] || $out['stop']) {
368
+ return false;
 
 
 
369
  }
370
  if ($out['skip']) {
 
 
 
371
  continue;
372
  }
373
  if (empty($subject) && !empty($out['subject'])) {
378
  $result .= $block_html;
379
  }
380
 
381
+ $email->message = TNP_Composer::get_html_open($email) . TNP_Composer::get_main_wrapper_open($email) .
382
+ $result . TNP_Composer::get_main_wrapper_close($email) . TNP_Composer::get_html_close($email);
383
+ $email->subject = $subject;
384
+ return true;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
385
  }
386
 
387
  function remove_block_data($text) {
473
  }
474
  echo $this->get_outlook_wrapper_open($width);
475
 
476
+ echo '<p>Ops, this block type is not avalable.</p>';
477
 
478
  echo $this->get_outlook_wrapper_close();
479
 
851
  die();
852
  break;
853
 
 
854
  case 'emails-create':
855
  // Newsletter from themes are created on frontend context because sometime WP themes change the way the content,
856
  // excerpt, thumbnail are extracted.
877
  $email = array();
878
  $email['status'] = 'new';
879
  $email['subject'] = ''; //__('Here the email subject', 'newsletter');
880
+ $email['track'] = Newsletter::instance()->options['track'];
881
  $email['send_on'] = time();
882
  $email['editor'] = NewsletterEmails::EDITOR_TINYMCE;
883
  $email['type'] = 'message';
891
  include $theme['dir'] . '/theme.php';
892
  $email['message'] = ob_get_clean();
893
 
 
894
  if (!empty($theme_subject)) {
895
  $email['subject'] = $theme_subject;
896
  }
939
  if (!is_file($full_file)) {
940
  return new WP_Error('1', 'Missing block.php file in ' . $dir);
941
  }
942
+
943
  $relative_dir = substr($dir, strlen(WP_CONTENT_DIR));
944
  $file = basename($dir);
945
 
1206
  $email->subject = $original_subject;
1207
  }
1208
 
1209
+ /**
1210
+ * Send an email to the test subscribers.
1211
+ *
1212
+ * @param TNP_Email $email Could be any object with the TNP_Email attributes
1213
+ * @param string $email_address
1214
+ *
1215
+ * @throws Exception
1216
+ */
1217
+ function send_test_newsletter_to_email_address($email, $email_address) {
1218
+
1219
+ if (!$email) {
1220
+ throw new Exception(__('Newsletter should be saved before send a test', 'newsletter'));
1221
+ }
1222
+
1223
+ $this->set_test_subject_to($email);
1224
+
1225
+ $dummy_subscriber = $this->make_dummy_subscriber();
1226
+ $dummy_subscriber->email = $email_address;
1227
+
1228
+ $result = Newsletter::instance()->send($email, [$dummy_subscriber], true);
1229
+
1230
+ $email = '<a href="admin.php?page=newsletter_users_edit&id=' . $dummy_subscriber->id . '" target="_blank">' . $dummy_subscriber->email . '</a>';
1231
+
1232
+ if (is_wp_error($result)) {
1233
+ $error_message = 'Something went wrong. Check the error logs on status page.<br>';
1234
+ $error_message .= __('Test subscribers:', 'newsletter');
1235
+ $error_message .= ' ' . $email;
1236
+ $error_message .= '<br>';
1237
+ $error_message .= '<strong>' . esc_html($result->get_error_message()) . '</strong><br>';
1238
+ $error_message .= '<a href="https://www.thenewsletterplugin.com/documentation/email-sending-issues" target="_blank"><strong>' . __('Read more about delivery issues', 'newsletter') . '</strong></a>.';
1239
+ throw new Exception($error_message);
1240
+ }
1241
+
1242
+ $messages = __('Test subscribers:', 'newsletter');
1243
+
1244
+ $messages .= ' ' . $email;
1245
+ $messages .= '.<br>';
1246
+ $messages .= '<a href="https://www.thenewsletterplugin.com/documentation/subscribers#test" target="_blank"><strong>' .
1247
+ __('Read more about test subscribers', 'newsletter') . '</strong></a>.<br>';
1248
+ $messages .= '<a href="https://www.thenewsletterplugin.com/documentation/email-sending-issues" target="_blank"><strong>' . __('Read more about delivery issues', 'newsletter') . '</strong></a>.';
1249
+
1250
+ return $messages;
1251
+ }
1252
+
1253
+ private function set_test_subject_to($email) {
1254
+ if ($email->subject == '') {
1255
+ $email->subject = '[TEST] Dummy subject, it was empty (remember to set it)';
1256
+ } else {
1257
+ $email->subject = $email->subject . ' (TEST)';
1258
+ }
1259
+ }
1260
+
1261
+ private function make_dummy_subscriber() {
1262
+ $dummy_user = new TNP_User();
1263
+ $dummy_user->id = 0;
1264
+ $dummy_user->email = 'john.doe@example.org';
1265
+ $dummy_user->name = 'John';
1266
+ $dummy_user->surname = 'Doe';
1267
+ $dummy_user->sex = 'n';
1268
+ $dummy_user->language = '';
1269
+ $dummy_user->ip = '';
1270
+
1271
+ for ($i = 1; $i <= NEWSLETTER_PROFILE_MAX; $i++) {
1272
+ $profile_key = "profile_$i";
1273
+ $dummy_user->$profile_key = '';
1274
+ }
1275
+
1276
+ return $dummy_user;
1277
+ }
1278
 
1279
  function restore_options_from_request() {
1280
+
1281
  require_once NEWSLETTER_INCLUDES_DIR . '/controls.php';
1282
  $controls = new NewsletterControls();
1283
  $options = $controls->data;
1284
+
1285
  if (isset($_POST['options']) && is_array($_POST['options'])) {
1286
  // Get all block options
1287
  //$options = stripslashes_deep($_POST['options']);
 
1288
  // Deserialize inline edits when
1289
  // render is preformed on saving block options
1290
  if (isset($options['inline_edits']) && !is_array($options['inline_edits'])) {
emails/tnp-composer/_scripts/newsletter-builder-v2.js CHANGED
@@ -367,7 +367,7 @@ function tnpc_mobile_preview() {
367
  function tnpc_save(form) {
368
 
369
  form.elements["options[message]"].value = tnpc_get_email_content_from_builder_area();
370
-
371
  // When the composer is not showing the subject field (for example in Automated)
372
  if (document.getElementById("options-preheader")) {
373
  form.elements["options[options_preheader]"].value = jQuery('#options-preheader').val();
@@ -621,21 +621,11 @@ function tnpc_edit_preset(presetId, name, event) {
621
  event.stopPropagation();
622
  tnpc_load_preset(presetId, name, true);
623
 
624
- //DISABLE BUTTON AND SHOW UPDATE BUTTON
625
  const composerForm = document.querySelector('#tnpc-form');
626
- const buttons = composerForm.querySelectorAll('input[type=button]');
627
- const updatePresetButton = composerForm.querySelector('#update-preset-button');
628
-
629
- for (btn of buttons) {
630
- if (btn.id && btn.id === 'save-preset-button') {
631
- btn.style.display = 'none';
632
- updatePresetButton.style.display = 'inline';
633
- updatePresetButton.disabled = false;
634
- } else {
635
- btn.disabled = true;
636
- }
637
- }
638
-
639
  //Add preset id hidden field
640
  const presetIdfield = document.createElement("input");
641
  presetIdfield.type = "hidden";
@@ -654,24 +644,8 @@ function tnpc_remove_double_quotes_from(str) {
654
  }
655
 
656
  function tnpc_update_preset(form) {
657
-
658
- const presetName = document.getElementById('options-subject-subject').value.replace('"', '');
659
-
660
- const presetNameModal = new TNPModal({
661
- title: 'Choose a preset name',
662
- content: '<input type="text" id="preset_name" style="width: 100%" placeholder="Preset name" value="' + presetName + '"/>',
663
- showConfirm: true,
664
- clickConfirmOnPressEnter: true,
665
- onConfirm: function () {
666
- const inputEl = document.querySelector('#preset_name');
667
- document.querySelector('#options-subject-subject').value = inputEl.value;
668
- tnpc_save(form);
669
- form.submit();
670
- }
671
- });
672
-
673
- presetNameModal.open();
674
-
675
  }
676
 
677
  // ========================================================= //
367
  function tnpc_save(form) {
368
 
369
  form.elements["options[message]"].value = tnpc_get_email_content_from_builder_area();
370
+
371
  // When the composer is not showing the subject field (for example in Automated)
372
  if (document.getElementById("options-preheader")) {
373
  form.elements["options[options_preheader]"].value = jQuery('#options-preheader').val();
621
  event.stopPropagation();
622
  tnpc_load_preset(presetId, name, true);
623
 
 
624
  const composerForm = document.querySelector('#tnpc-form');
625
+
626
+ jQuery('#save-preset-button').hide();
627
+ jQuery('#update-preset-button').show();
628
+
 
 
 
 
 
 
 
 
 
629
  //Add preset id hidden field
630
  const presetIdfield = document.createElement("input");
631
  presetIdfield.type = "hidden";
644
  }
645
 
646
  function tnpc_update_preset(form) {
647
+ tnpc_save(form);
648
+ form.submit();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
649
  }
650
 
651
  // ========================================================= //
emails/tnp-composer/edit.php DELETED
@@ -1,252 +0,0 @@
1
- <?php
2
- if (!defined('ABSPATH')) exit;
3
-
4
- function tnp_buttons_row() {
5
- ?>
6
- <div class="tnpc-edit-box-buttons">
7
- <div class="tnpc-edit-box-buttons-cancel"><?php _e("Cancel", "newsletter") ?></div>
8
- <div class="tnpc-edit-box-buttons-save"><?php _e("Save", "newsletter") ?></div>
9
- </div>
10
- <?php
11
- }
12
- ?>
13
- <div class="tnpc-edit" id="tnpc-edit-image">
14
- <div class="tnpc-edit-box">
15
- <div class="tnpc-edit-box-title"><?php _e("Edit Image", "newsletter") ?></div>
16
- <div class="tnpc-edit-box-content">
17
- <div class="tnpc-edit-box-content-text"><?php _e("SOURCE", "newsletter") ?> <span>(full image URL including http://)</span></div>
18
- <div class="tnpc-edit-box-content-field">
19
- <input type="text" class="tnpc-edit-box-content-field-input image"/>
20
- <input class="button select_image" value="Select or Upload Image" type="button">
21
- </div>
22
- <div class="tnpc-edit-box-content-text"><?php _e("WIDTH", "newsletter") ?><span>(optional but recommended)</span></div>
23
- <div class="tnpc-edit-box-content-field"><input type="text" class="tnpc-edit-box-content-field-input width"/></div>
24
- <div class="tnpc-edit-box-content-text"><?php _e("ALT TEXT", "newsletter") ?><span>(optional but recommended)</span></div>
25
- <div class="tnpc-edit-box-content-field"><input type="text" class="tnpc-edit-box-content-field-input alt"/></div>
26
- <div class="tnpc-edit-box-content-text"><?php _e("LINK", "newsletter") ?> <span>(optional link address including http://)</span></div>
27
- <div class="tnpc-edit-box-content-field"><input type="text" class="tnpc-edit-box-content-field-input url"/></div>
28
- </div>
29
- <?php tnp_buttons_row() ?>
30
- </div>
31
- </div>
32
- <script type="text/javascript">
33
- var file_frame;
34
- jQuery('#tnpc-edit-image .select_image').live('click', function (event) {
35
- event.preventDefault();
36
- // If the media frame already exists, reopen it.
37
- if (file_frame) {
38
- file_frame.open();
39
- return;
40
- }
41
- // Create the media frame.
42
- file_frame = wp.media.frames.file_frame = wp.media({
43
- title: jQuery('#tnpc-edit-image .image').val(),
44
- multiple: false // Set to true to allow multiple files to be selected
45
- });
46
- // When an image is selected, run a callback.
47
- file_frame.on('select', function () {
48
- // We set multiple to false so only get one image from the uploader
49
- attachment = file_frame.state().get('selection').first().toJSON();
50
- // Do something with attachment.id and/or attachment.url here
51
- // Patch for plugins which remove the protocol (not good for a newsletter...)
52
- if (attachment.url.substring(0, 0) == "/") {
53
- attachment.url = "<?php echo site_url('/') ?>" + attachment.url;
54
- }
55
- if (attachment.url.indexOf("http") !== 0) attachment.url = "http:" + attachment.url;
56
- jQuery('#tnpc-edit-image .image').val(attachment.url);
57
- });
58
- // Finally, open the modal
59
- file_frame.open();
60
- });
61
- </script>
62
-
63
- <div class="tnpc-edit" id="tnpc-edit-link">
64
- <div class="tnpc-edit-box">
65
- <div class="tnpc-edit-box-title"><?php _e("Edit Link", "newsletter") ?></div>
66
-
67
- <div class="tnpc-edit-box-content">
68
- <div class="tnpc-edit-box-content-text"><?php _e("TITLE", "newsletter") ?> </div>
69
- <div class="tnpc-edit-box-content-field"><input type="text" class="tnpc-edit-box-content-field-input title"/></div>
70
- <div class="tnpc-edit-box-content-text"><?php _e("URL", "newsletter") ?> <span>(full address including http://)</span></div>
71
- <div class="tnpc-edit-box-content-field"><input type="text" class="tnpc-edit-box-content-field-input url"/></div>
72
- </div>
73
- <?php tnp_buttons_row() ?>
74
- </div>
75
- </div>
76
-
77
- <div class="tnpc-edit" id="tnpc-edit-button">
78
- <div class="tnpc-edit-box">
79
- <div class="tnpc-edit-box-title"><?php _e("Edit Button", "newsletter") ?></div>
80
-
81
- <div class="tnpc-edit-box-content">
82
- <div class="tnpc-edit-box-content-text"><?php _e("TITLE", "newsletter") ?> </div>
83
- <div class="tnpc-edit-box-content-field"><input type="text" class="tnpc-edit-box-content-field-input title"/></div>
84
- <div class="tnpc-edit-box-content-text"><?php _e("URL", "newsletter") ?> <span>(full address including http://)</span></div>
85
- <div class="tnpc-edit-box-content-field"><input type="text" class="tnpc-edit-box-content-field-input url"/></div>
86
- <div class="tnpc-edit-box-content-text"><?php _e("Text Color", "newsletter") ?></div>
87
- <div class="tnpc-edit-box-content-field"><input type="text" class="tnpc-edit-box-content-field-input fgcolor"/></div>
88
- <div class="tnpc-edit-box-content-text"><?php _e("Background Color", "newsletter") ?></div>
89
- <div class="tnpc-edit-box-content-field"><input type="text" class="tnpc-edit-box-content-field-input bgcolor"/></div>
90
- </div>
91
- <?php tnp_buttons_row() ?>
92
- </div>
93
- </div>
94
-
95
- <div class="tnpc-edit" id="tnpc-edit-title">
96
- <div class="tnpc-edit-box">
97
- <div class="tnpc-edit-box-title"><?php _e("Edit Title", "newsletter") ?></div>
98
-
99
- <div class="tnpc-edit-box-content">
100
- <div class="tnpc-edit-box-content-text"><?php _e("Title", "newsletter") ?></div>
101
- <div class="tnpc-edit-box-content-field"><input type="text" class="tnpc-edit-box-content-field-input title"/></div>
102
- <div class="tnpc-edit-box-content-text"><?php _e("Text Color", "newsletter") ?></div>
103
- <div class="tnpc-edit-box-content-field"><input type="text" class="tnpc-edit-box-content-field-input color"/></div>
104
-
105
- <div class="tnpc-edit-box-content-text"><?php _e("Font family", "newsletter") ?></div>
106
- <div class="tnpc-edit-box-content-field">
107
- <select id="tnpc-edit-title-font-family">
108
- <optgroup label="Sans Serif Web Safe Fonts">
109
- <option value="Arial">Arial</option>
110
- <option value="Arial Black">Arial Black</option>
111
- <option value="Tahoma">Tahoma</option>
112
- <option value="Trebuchet MS">Trebuchet MS</option>
113
- <option value="Verdana">Verdana</option>
114
- </optgroup>
115
- <optgroup label="Serif Web Safe Fonts">
116
- <option value="Georgia">Georgia</option>
117
- <option value="Times">Times</option>
118
- <option value="Times New Roman">Times New Roman</option>
119
- </optgroup>
120
- <optgroup label="Monospace Fonts">
121
- <option value="Courier">Courier</option>
122
- <option value="Courier New">Courier New</option>
123
- </optgroup>
124
- </select>
125
- </div>
126
-
127
- <div class="tnpc-edit-box-content-text"><?php _e("Font size", "newsletter") ?></div>
128
- <div class="tnpc-edit-box-content-field">
129
- <select id="tnpc-edit-title-font-size">
130
- <?php for ($i=10; $i<50; $i++) echo '<option>', $i, '</option>'?>
131
- </select>
132
- </div>
133
-
134
- <div class="tnpc-edit-box-content-text"><?php _e("Text align", "newsletter") ?></div>
135
- <div class="tnpc-edit-box-content-field">
136
- <select id="tnpc-edit-title-text-align">
137
- <option value="center">Center</option>
138
- <option value="left">Left</option>
139
- <option value="right">Right</option>
140
- </select>
141
- </div>
142
- </div>
143
- <?php tnp_buttons_row() ?>
144
- </div>
145
- </div>
146
-
147
-
148
- <div class="tnpc-edit" id="tnpc-edit-text">
149
- <div class="tnpc-edit-box">
150
- <div class="tnpc-edit-box-title"><?php _e("Edit Text", "newsletter") ?></div>
151
-
152
- <div class="tnpc-edit-box-content">
153
- <div class="tnpc-edit-box-content-text"><?php _e("Text", "newsletter") ?></div>
154
- <div class="tnpc-edit-box-content-field"><textarea class="tnpc-edit-box-content-field-textarea text"></textarea></div>
155
- </div>
156
- <?php tnp_buttons_row() ?>
157
- </div>
158
- </div>
159
-
160
- <!-- On eline text block, like headings -->
161
- <div class="tnpc-edit" id="tnpc-edit-block">
162
- <div class="tnpc-edit-box">
163
- <div class="tnpc-edit-box-title"><?php _e("Edit Block", "newsletter") ?></div>
164
-
165
- <div class="tnpc-edit-box-content">
166
- <div class="tnpc-edit-box-content-text"><?php _e("Background Color", "newsletter") ?></div>
167
- <div class="tnpc-edit-box-content-field"><input type="text" class="tnpc-edit-box-content-field-input bgcolor"/></div>
168
- <div class="tnpc-edit-box-content-text"><?php _e("Font Family", "newsletter") ?></div>
169
- <div class="tnpc-edit-box-content-field">
170
- <select class="tnpc-edit-box-content-field-input font">
171
- <optgroup label="Sans Serif Web Safe Fonts">
172
- <option value="Arial">Arial</option>
173
- <option value="Arial Black">Arial Black</option>
174
- <option value="Tahoma">Tahoma</option>
175
- <option value="Trebuchet MS">Trebuchet MS</option>
176
- <option value="Verdana">Verdana</option>
177
- </optgroup>
178
- <optgroup label="Serif Web Safe Fonts">
179
- <option value="Georgia">Georgia</option>
180
- <option value="Times">Times</option>
181
- <option value="Times New Roman">Times New Roman</option>
182
- </optgroup>
183
- <optgroup label="Monospace Fonts">
184
- <option value="Courier">Courier</option>
185
- <option value="Courier New">Courier New</option>
186
- </optgroup>
187
- </select>
188
- </div>
189
- <div class="tnpc-edit-box-content-field">
190
- <select class="tnpc-edit-box-content-field-input font-size">
191
- <?php for ($i=9; $i<51; $i++) { ?>
192
- <option value="<?php echo $i?>"><?php echo $i?></option>
193
- <?php } ?>
194
- </select>
195
- </div>
196
- </div>
197
- <?php tnp_buttons_row() ?>
198
- </div>
199
- </div>
200
-
201
- <div class="tnpc-edit" id="tnpc-edit-posts">
202
- <div class="tnpc-edit-box">
203
- <div class="tnpc-edit-box-title"><?php _e("Edit Block", "newsletter") ?></div>
204
-
205
- <div class="tnpc-edit-box-content">
206
- <div class="tnpc-edit-box-content-text"><?php _e("Background Color", "newsletter") ?></div>
207
- <div class="tnpc-edit-box-content-field"><input type="text" class="tnpc-edit-box-content-field-input bgcolor"/></div>
208
- <div class="tnpc-edit-box-content-text"><?php _e("Font Family", "newsletter") ?></div>
209
- <div class="tnpc-edit-box-content-field">
210
- <select class="tnpc-edit-box-content-field-input font">
211
- <optgroup label="Sans Serif Web Safe Fonts">
212
- <option value="Arial">Arial</option>
213
- <option value="Arial Black">Arial Black</option>
214
- <option value="Tahoma">Tahoma</option>
215
- <option value="Trebuchet MS">Trebuchet MS</option>
216
- <option value="Verdana">Verdana</option>
217
- </optgroup>
218
- <optgroup label="Serif Web Safe Fonts">
219
- <option value="Courier">Courier</option>
220
- <option value="Courier New">Courier New</option>
221
- <option value="Georgia">Georgia</option>
222
- <option value="Times">Times</option>
223
- <option value="Times New Roman">Times New Roman</option>
224
- </optgroup>
225
- <optgroup label="Monospace Fonts">
226
- <option value="Courier">Courier</option>
227
- <option value="Courier New">Courier New</option>
228
- </optgroup>
229
- </select>
230
- </div>
231
- <div class="tnpc-edit-box-content-text"><?php _e("Number of posts", "newsletter") ?></div>
232
- <div class="tnpc-edit-box-content-field"><input type="number" class="tnpc-edit-box-content-field-input number" value="3"/></div>
233
- <div class="tnpc-edit-box-content-text"><?php _e("Categories", "newsletter") ?></div>
234
- <div class="tnpc-edit-box-content-field">
235
- <!-- <input type="text" class="tnpc-edit-box-content-field-input categories"/>-->
236
- <?php $controls->categories_group('theme_categories'); ?>
237
- </div>
238
- <div class="tnpc-edit-box-content-text"><?php _e("Tags (comma separated)", "newsletter") ?></div>
239
- <div class="tnpc-edit-box-content-field"><input type="text" class="tnpc-edit-box-content-field-input tags"/></div>
240
- <?php _e("Any prior changes to single posts will be lost when editing these settings.", "newsletter") ?>
241
- </div>
242
- <?php tnp_buttons_row() ?>
243
- </div>
244
- </div>
245
-
246
- <?php // General container for generic block options ?>
247
- <div class="tnpc-edit" id="tnpc-block-options">
248
- <div class="tnpc-edit-box">
249
- <form id="tnpc-block-options-form" onsubmit="return false;"></form>
250
- <?php tnp_buttons_row() ?>
251
- </div>
252
- </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
emails/tnp-composer/index-v2.php CHANGED
@@ -28,13 +28,12 @@ $fields = new NewsletterFields($controls);
28
 
29
  $dir = is_rtl() ? 'rtl' : 'ltr';
30
  $rev_dir = is_rtl() ? 'ltr' : 'rlt';
31
-
32
  ?>
33
  <script type="text/javascript">
34
  if (window.innerWidth < 1550) {
35
  document.body.classList.add('folded');
36
  }
37
-
38
  function tnp_view(type) {
39
  if (type === 'mobile') {
40
  jQuery('#newsletter-builder-area-center-frame-content').addClass('tnp-view-mobile');
@@ -52,24 +51,31 @@ $rev_dir = is_rtl() ? 'ltr' : 'rlt';
52
 
53
  <div id="newsletter-builder-area" class="tnp-builder-column">
54
 
55
- <?php if ( $tnpc_show_subject ) { ?>
56
  <div id="tnpc-subject-wrap" dir="<?php echo $dir ?>">
57
  <table role="presentation" style="width: 100%">
 
 
 
 
 
 
58
  <tr>
59
- <th dir="<?php echo $dir ?>"><?php _e('From', 'newsletter') ?></th>
60
- <td dir="<?php echo $dir ?>"><?php echo esc_html( $controls->data['sender_email'] ) ?></td>
61
- </tr>
62
- <tr>
63
- <th dir="<?php echo $dir ?>"><?php _e('Subject', 'newsletter') ?></th>
 
64
  <td dir="<?php echo $dir ?>">
65
  <div id="tnpc-subject">
66
- <?php $this->subject( 'subject' ); ?>
67
  </div>
68
  </td>
69
  </tr>
70
  <tr>
71
- <th dir="<?php echo $dir ?>"><span title="<?php esc_attr_e('Shown by some email clients as excerpt', 'newsletter')?>"><?php _e('Snippet', 'newsletter') ?></span>
72
- <?php $this->field_help('https://www.thenewsletterplugin.com/documentation/newsletters/composer/#subject') ?>
73
  </th>
74
  <td dir="<?php echo $dir ?>"><?php $this->text('preheader') ?></td>
75
  </tr>
@@ -83,117 +89,118 @@ $rev_dir = is_rtl() ? 'ltr' : 'rlt';
83
  </div>
84
 
85
  <div class="composer-actions">
86
-
87
  <div id="attachment-newsletter-button" class="button-primary" data-tnp-modal-target="#attachment-modal">
88
  <i class="fas fa-paperclip"></i>
89
  </div>
90
-
91
- <div id="test-newsletter-button" class="button-primary" data-tnp-modal-target="#test-newsletter-modal">
92
- <i class="fas fa-paper-plane"></i> <?php _e( 'Test', 'newsletter' ) ?>
93
- </div>
94
-
 
95
  <div class="composer-view-mode">
96
  <span class="composer-view-mode__item" data-view-mode="desktop">
97
  <i class="fas fa-desktop"></i>
98
  </span>
99
-
100
  <span class="composer-view-mode__item" data-view-mode="mobile">
101
  <i class="fas fa-mobile"></i>
102
- <span>
103
- </div>
104
-
105
- </div>
106
-
107
- <?php include NEWSLETTER_DIR . '/emails/tnp-composer/modal/test-newsletter.php' ?>
108
- <?php include NEWSLETTER_DIR . '/emails/tnp-composer/modal/attachment.php' ?>
109
-
110
- </div>
111
- <?php } ?>
112
-
113
-
114
- <div id="newsletter-builder-area-center-frame-content" dir="<?php echo $dir ?>">
115
-
116
- <!-- Composer content -->
117
 
118
- </div>
119
- </div>
120
 
121
- <div id="newsletter-builder-sidebar" dir="<?php echo is_rtl() ? 'rtl' : 'ltr' ?>">
 
122
 
123
- <div class="tnpc-tabs">
124
- <button class="tablinks" onclick="openTab(event, 'tnpc-blocks')" data-tab-id='tnpc-blocks' id="defaultOpen"><?php _e('Blocks', 'newsletter') ?></button>
125
- <button class="tablinks" onclick="openTab(event, 'tnpc-global-styles')" data-tab-id='tnpc-global-styles'><?php _e('Settings', 'newsletter') ?></button>
126
- </div>
127
 
128
- <div id="tnpc-blocks" class="tabcontent">
129
- <?php foreach ($blocks as $k => $section) { ?>
130
- <div class="newsletter-sidebar-add-buttons" id="sidebar-add-<?php echo $k ?>">
131
- <!--<h4><span><?php echo ucfirst($k) ?></span></h4>-->
132
- <?php foreach ($section AS $key => $block) { ?>
133
- <div class="newsletter-sidebar-buttons-content-tab" data-id="<?php echo $key ?>" data-name="<?php echo esc_attr($block['name']) ?>">
134
- <img src="<?php echo $block['icon'] ?>" title="<?php echo esc_attr($block['name']) ?>">
135
- </div>
136
- <?php } ?>
137
- </div>
138
- <?php } ?>
139
- </div>
140
-
141
- <div id="tnpc-global-styles" class="tabcontent">
142
-
143
- <form id="tnpc-global-styles-form">
144
-
145
- <div class="tnp-field-row">
146
- <div class="tnp-field-col-2">
147
- <?php $fields->color('options_composer_background', __('Main background', 'newsletter')) ?>
148
- </div>
149
- <div class="tnp-field-col-2">
150
- <?php $fields->color('options_composer_block_background', 'Blocks background') ?>
151
- </div>
152
- </div>
153
-
154
- <?php $fields->font('options_composer_title_font', __('Titles font', 'newsletter')) ?>
155
- <?php $fields->font('options_composer_text_font', __('Text font', 'newsletter')) ?>
156
- <?php $fields->button_style('options_composer_button', __('Button style', 'newsletter')); ?>
157
 
158
- <button class="button-secondary" name="apply"><?php _e("Apply", 'newsletter') ?></button>
159
 
160
- </form>
161
 
162
- </div>
 
163
 
164
- <!-- Block options container (dynamically loaded -->
165
- <div id="tnpc-block-options">
166
- <div id="tnpc-block-options-buttons">
167
- <span id="tnpc-block-options-cancel" class="button-secondary"><?php _e("Cancel", "newsletter") ?></span>
168
- <span id="tnpc-block-options-save" class="button-primary"><?php _e("Apply", "newsletter") ?></span>
169
- </div>
170
- <form id="tnpc-block-options-form" onsubmit="return false;"></form>
171
- </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
172
 
173
- </div>
174
 
175
- <div style="clear: both"></div>
176
 
177
- </div>
178
 
179
- <div style="display: none">
180
- <div id="newsletter-preloaded-export"></div>
181
- <!-- Block placeholder used by jQuery UI -->
182
- <div id="draggable-helper"></div>
183
- <div id="sortable-helper"></div>
184
- </div>
185
 
186
- <script type="text/javascript">
187
- TNP_PLUGIN_URL = "<?php echo esc_js(NEWSLETTER_URL) ?>";
188
- TNP_HOME_URL = "<?php echo esc_js(home_url('/', is_ssl() ? 'https' : 'http')) ?>";
189
- tnp_context_type = "<?php echo esc_js($context_type) ?>";
190
- tnp_nonce = '<?php echo esc_js(wp_create_nonce('save')) ?>';
191
- tnp_preset_nonce = '<?php echo esc_js(wp_create_nonce('preset')) ?>';
192
- </script>
193
- <?php
194
- wp_enqueue_script('tnp-composer', plugins_url('newsletter') . '/emails/tnp-composer/_scripts/newsletter-builder-v2.js', ['tnp-modal', 'tnp-toast'], NEWSLETTER_VERSION);
195
- ?>
196
 
197
- <?php include NEWSLETTER_DIR . '/emails/subjects.php'; ?>
198
 
199
- <?php if (function_exists('wp_enqueue_editor')) wp_enqueue_editor(); ?>
28
 
29
  $dir = is_rtl() ? 'rtl' : 'ltr';
30
  $rev_dir = is_rtl() ? 'ltr' : 'rlt';
 
31
  ?>
32
  <script type="text/javascript">
33
  if (window.innerWidth < 1550) {
34
  document.body.classList.add('folded');
35
  }
36
+
37
  function tnp_view(type) {
38
  if (type === 'mobile') {
39
  jQuery('#newsletter-builder-area-center-frame-content').addClass('tnp-view-mobile');
51
 
52
  <div id="newsletter-builder-area" class="tnp-builder-column">
53
 
54
+ <?php if ($tnpc_show_subject) { ?>
55
  <div id="tnpc-subject-wrap" dir="<?php echo $dir ?>">
56
  <table role="presentation" style="width: 100%">
57
+ <?php if (!empty($controls->data['sender_email'])) { ?>
58
+ <tr>
59
+ <th dir="<?php echo $dir ?>"><?php _e('From', 'newsletter') ?></th>
60
+ <td dir="<?php echo $dir ?>"><?php echo esc_html($controls->data['sender_email']) ?></td>
61
+ </tr>
62
+ <?php } ?>
63
  <tr>
64
+ <th dir="<?php echo $dir ?>">
65
+ <?php _e('Subject', 'newsletter') ?>
66
+ <?php if ($context_type === 'automated') { ?>
67
+ <?php $this->field_help('https://www.thenewsletterplugin.com/documentation/addons/extended-features/automated-extension/#subject') ?>
68
+ <?php } ?>
69
+ </th>
70
  <td dir="<?php echo $dir ?>">
71
  <div id="tnpc-subject">
72
+ <?php $this->subject('subject'); ?>
73
  </div>
74
  </td>
75
  </tr>
76
  <tr>
77
+ <th dir="<?php echo $dir ?>"><span title="<?php esc_attr_e('Shown by some email clients as excerpt', 'newsletter') ?>"><?php _e('Snippet', 'newsletter') ?></span>
78
+ <?php $this->field_help('https://www.thenewsletterplugin.com/documentation/newsletters/composer/#subject') ?>
79
  </th>
80
  <td dir="<?php echo $dir ?>"><?php $this->text('preheader') ?></td>
81
  </tr>
89
  </div>
90
 
91
  <div class="composer-actions">
92
+
93
  <div id="attachment-newsletter-button" class="button-primary" data-tnp-modal-target="#attachment-modal">
94
  <i class="fas fa-paperclip"></i>
95
  </div>
96
+ <?php if ($show_test) { ?>
97
+ <div id="test-newsletter-button" class="button-primary" data-tnp-modal-target="#test-newsletter-modal">
98
+ <i class="fas fa-paper-plane"></i> <?php _e('Test', 'newsletter') ?>
99
+ </div>
100
+ <?php } ?>
101
+
102
  <div class="composer-view-mode">
103
  <span class="composer-view-mode__item" data-view-mode="desktop">
104
  <i class="fas fa-desktop"></i>
105
  </span>
106
+
107
  <span class="composer-view-mode__item" data-view-mode="mobile">
108
  <i class="fas fa-mobile"></i>
109
+ <span>
110
+ </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
111
 
112
+ </div>
 
113
 
114
+ <?php include NEWSLETTER_DIR . '/emails/tnp-composer/modal/test-newsletter.php' ?>
115
+ <?php include NEWSLETTER_DIR . '/emails/tnp-composer/modal/attachment.php' ?>
116
 
117
+ </div>
118
+ <?php } ?>
 
 
119
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
120
 
121
+ <div id="newsletter-builder-area-center-frame-content" dir="<?php echo $dir ?>">
122
 
123
+ <!-- Composer content -->
124
 
125
+ </div>
126
+ </div>
127
 
128
+ <div id="newsletter-builder-sidebar" dir="<?php echo is_rtl() ? 'rtl' : 'ltr' ?>">
129
+
130
+ <div class="tnpc-tabs">
131
+ <button class="tablinks" onclick="openTab(event, 'tnpc-blocks')" data-tab-id='tnpc-blocks' id="defaultOpen"><?php _e('Blocks', 'newsletter') ?></button>
132
+ <button class="tablinks" onclick="openTab(event, 'tnpc-global-styles')" data-tab-id='tnpc-global-styles'><?php _e('Settings', 'newsletter') ?></button>
133
+ </div>
134
+
135
+ <div id="tnpc-blocks" class="tabcontent">
136
+ <?php foreach ($blocks as $k => $section) { ?>
137
+ <div class="newsletter-sidebar-add-buttons" id="sidebar-add-<?php echo $k ?>">
138
+ <!--<h4><span><?php echo ucfirst($k) ?></span></h4>-->
139
+ <?php foreach ($section AS $key => $block) { ?>
140
+ <div class="newsletter-sidebar-buttons-content-tab" data-id="<?php echo $key ?>" data-name="<?php echo esc_attr($block['name']) ?>">
141
+ <img src="<?php echo $block['icon'] ?>" title="<?php echo esc_attr($block['name']) ?>">
142
+ </div>
143
+ <?php } ?>
144
+ </div>
145
+ <?php } ?>
146
+ </div>
147
+
148
+ <div id="tnpc-global-styles" class="tabcontent">
149
+
150
+ <form id="tnpc-global-styles-form">
151
+
152
+ <div class="tnp-field-row">
153
+ <div class="tnp-field-col-2">
154
+ <?php $fields->color('options_composer_background', __('Main background', 'newsletter')) ?>
155
+ </div>
156
+ <div class="tnp-field-col-2">
157
+ <?php $fields->color('options_composer_block_background', 'Blocks background') ?>
158
+ </div>
159
+ </div>
160
+
161
+ <?php $fields->font('options_composer_title_font', __('Titles font', 'newsletter')) ?>
162
+ <?php $fields->font('options_composer_text_font', __('Text font', 'newsletter')) ?>
163
+ <?php $fields->button_style('options_composer_button', __('Button style', 'newsletter')); ?>
164
+
165
+ <button class="button-secondary" name="apply"><?php _e("Apply", 'newsletter') ?></button>
166
+
167
+ </form>
168
+
169
+ </div>
170
+
171
+ <!-- Block options container (dynamically loaded -->
172
+ <div id="tnpc-block-options">
173
+ <div id="tnpc-block-options-buttons">
174
+ <span id="tnpc-block-options-cancel" class="button-secondary"><?php _e("Cancel", "newsletter") ?></span>
175
+ <span id="tnpc-block-options-save" class="button-primary"><?php _e("Apply", "newsletter") ?></span>
176
+ </div>
177
+ <form id="tnpc-block-options-form" onsubmit="return false;"></form>
178
+ </div>
179
 
180
+ </div>
181
 
182
+ <div style="clear: both"></div>
183
 
184
+ </div>
185
 
186
+ <div style="display: none">
187
+ <div id="newsletter-preloaded-export"></div>
188
+ <!-- Block placeholder used by jQuery UI -->
189
+ <div id="draggable-helper"></div>
190
+ <div id="sortable-helper"></div>
191
+ </div>
192
 
193
+ <script type="text/javascript">
194
+ TNP_PLUGIN_URL = "<?php echo esc_js(NEWSLETTER_URL) ?>";
195
+ TNP_HOME_URL = "<?php echo esc_js(home_url('/', is_ssl() ? 'https' : 'http')) ?>";
196
+ tnp_context_type = "<?php echo esc_js($context_type) ?>";
197
+ tnp_nonce = '<?php echo esc_js(wp_create_nonce('save')) ?>';
198
+ tnp_preset_nonce = '<?php echo esc_js(wp_create_nonce('preset')) ?>';
199
+ </script>
200
+ <?php
201
+ wp_enqueue_script('tnp-composer', plugins_url('newsletter') . '/emails/tnp-composer/_scripts/newsletter-builder-v2.js', ['tnp-modal', 'tnp-toast'], NEWSLETTER_VERSION);
202
+ ?>
203
 
204
+ <?php include NEWSLETTER_DIR . '/emails/subjects.php'; ?>
205
 
206
+ <?php if (function_exists('wp_enqueue_editor')) wp_enqueue_editor(); ?>
includes/antispam.php CHANGED
@@ -1,202 +1,202 @@
1
- <?php
2
-
3
- class NewsletterAntispam {
4
-
5
- var $options;
6
- var $logger;
7
-
8
- public static function instance() {
9
- static $instance;
10
- if (!$instance) {
11
- $instance = new NewsletterAntispam();
12
- }
13
- return $instance;
14
- }
15
-
16
- public function __construct() {
17
- $this->options = NewsletterSubscription::instance()->get_options('antibot');
18
- $this->logger = new NewsletterLogger('antispam');
19
- }
20
-
21
- /**
22
- * $email must be cleaned using the is_email() function.
23
- *
24
- * @param TNP_Subscription $subscription
25
- */
26
- function is_spam($subscription) {
27
-
28
- $email = $subscription->data->email;
29
- $ip = $subscription->data->ip;
30
-
31
-
32
- $full_name = $subscription->data->name . ' ' . $subscription->data->surname;
33
- if ($this->is_spam_text($full_name)) {
34
- $this->logger->fatal($email . ' - ' . $ip . ' - Name with http: ' . $full_name);
35
- return true;
36
- }
37
-
38
- if ($this->is_ip_blacklisted($ip)) {
39
- $this->logger->fatal($email . ' - ' . $ip . ' - IP blacklisted');
40
- return true;
41
- }
42
-
43
- if ($this->is_address_blacklisted($email)) {
44
- $this->logger->fatal($email . ' - ' . $ip . ' - Address blacklisted');
45
- return true;
46
- }
47
-
48
- // Akismet check
49
- $user_agent = isset( $_SERVER['HTTP_USER_AGENT'] ) ? $_SERVER['HTTP_USER_AGENT'] : '';
50
- $referrer = isset( $_SERVER['HTTP_REFERER'] ) ? $_SERVER['HTTP_REFERER'] : '';
51
- if ( $this->is_spam_by_akismet( $email, $full_name, $ip, $user_agent, $referrer ) ) {
52
- $this->logger->fatal( $email . ' - ' . $ip . ' - Akismet blocked' );
53
-
54
- return true;
55
- }
56
-
57
- // Flood check
58
- if ($this->is_flood($email, $ip)) {
59
- $this->logger->fatal($email . ' - ' . $ip . ' - Antiflood triggered');
60
- return true;
61
- }
62
-
63
- // if ($this->is_missing_domain_mx($email)) {
64
- // $this->logger->fatal($email . ' - ' . $ip . ' - MX check failed');
65
- // header("HTTP/1.0 404 Not Found");
66
- // return true;
67
- // }
68
-
69
- return false;
70
- }
71
-
72
- function is_address_blacklisted($email) {
73
-
74
- if (empty($this->options['address_blacklist'])) {
75
- return false;
76
- }
77
-
78
- $this->logger->debug('Address blacklist check');
79
- $rev_email = strrev($email);
80
- foreach ($this->options['address_blacklist'] as $item) {
81
- if (strpos($rev_email, strrev($item)) === 0) {
82
- return true;
83
- }
84
- }
85
- return false;
86
- }
87
-
88
- function is_ip_blacklisted($ip) {
89
-
90
- if ($ip === '::1' || $ip === '127.0.0.1') {
91
- return false;
92
- }
93
-
94
- if (empty($this->options['ip_blacklist'])) {
95
- return false;
96
- }
97
- $this->logger->debug('IP blacklist check');
98
- foreach ($this->options['ip_blacklist'] as $item) {
99
- if (substr($item, 0, 1) === '#') {
100
- continue;
101
- }
102
- if ($this->ip_match($ip, $item)) {
103
- return true;
104
- }
105
- }
106
- return false;
107
- }
108
-
109
- function is_missing_domain_mx($email) {
110
- // Actually not fully implemented
111
- return false;
112
-
113
- if (empty($this->options['domain_check'])) {
114
- return false;
115
- }
116
-
117
- $this->logger->debug('Domain MX check');
118
- list($local, $domain) = explode('@', $email);
119
-
120
- $hosts = array();
121
- if (!getmxrr($domain, $hosts)) {
122
- return true;
123
- }
124
- return false;
125
- }
126
-
127
- function is_flood($email, $ip) {
128
- global $wpdb;
129
-
130
- if (empty($this->options['antiflood'])) {
131
- return false;
132
- }
133
-
134
- $this->logger->debug('Antiflood check');
135
-
136
- $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));
137
-
138
- if ($updated && time() - $updated < $this->options['antiflood']) {
139
- return true;
140
- }
141
-
142
- return false;
143
- }
144
-
145
- function is_spam_text($text) {
146
- if (stripos($text, 'http:') !== false || stripos($text, 'https:') !== false) {
147
- return true;
148
- }
149
- if (stripos($text, 'www.') !== false) {
150
- return true;
151
- }
152
- if (preg_match('|[^\s\.]+\.[^\s\.]+\.[^\s\.]{2,}|', $text)) {
153
- return true;
154
- }
155
-
156
- return false;
157
- }
158
-
159
- function is_spam_by_akismet($email, $name, $ip, $agent, $referrer) {
160
-
161
- if (!class_exists('Akismet')) {
162
- return false;
163
- }
164
-
165
- if (empty($this->options['akismet'])) {
166
- return false;
167
- }
168
-
169
- $this->logger->debug('Akismet check');
170
- $request = 'blog=' . urlencode(home_url()) . '&referrer=' . urlencode($referrer) .
171
- '&user_agent=' . urlencode($agent) .
172
- '&comment_type=signup' .
173
- '&comment_author_email=' . urlencode($email) .
174
- '&user_ip=' . urlencode($ip);
175
- if (!empty($name)) {
176
- $request .= '&comment_author=' . urlencode($name);
177
- }
178
-
179
- $response = Akismet::http_post($request, 'comment-check');
180
-
181
- if ($response && $response[1] == 'true') {
182
- return true;
183
- }
184
- return false;
185
- }
186
-
187
- function ip_match($ip, $range) {
188
- if (empty($ip))
189
- return false;
190
- if (strpos($range, '/')) {
191
- list ($subnet, $bits) = explode('/', $range);
192
- $ip = ip2long($ip);
193
- $subnet = ip2long($subnet);
194
- $mask = -1 << (32 - $bits);
195
- $subnet &= $mask; # nb: in case the supplied subnet wasn't correctly aligned
196
- return ($ip & $mask) == $subnet;
197
- } else {
198
- return strpos($range, $ip) === 0;
199
- }
200
- }
201
-
202
- }
1
+ <?php
2
+
3
+ class NewsletterAntispam {
4
+
5
+ var $options;
6
+ var $logger;
7
+
8
+ public static function instance() {
9
+ static $instance;
10
+ if (!$instance) {
11
+ $instance = new NewsletterAntispam();
12
+ }
13
+ return $instance;
14
+ }
15
+
16
+ public function __construct() {
17
+ $this->options = NewsletterSubscription::instance()->get_options('antibot');
18
+ $this->logger = new NewsletterLogger('antispam');
19
+ }
20
+
21
+ /**
22
+ * $email must be cleaned using the is_email() function.
23
+ *
24
+ * @param TNP_Subscription $subscription
25
+ */
26
+ function is_spam($subscription) {
27
+
28
+ $email = $subscription->data->email;
29
+ $ip = $subscription->data->ip;
30
+
31
+
32
+ $full_name = $subscription->data->name . ' ' . $subscription->data->surname;
33
+ if ($this->is_spam_text($full_name)) {
34
+ $this->logger->fatal($email . ' - ' . $ip . ' - Name with http: ' . $full_name);
35
+ return true;
36
+ }
37
+
38
+ if ($this->is_ip_blacklisted($ip)) {
39
+ $this->logger->fatal($email . ' - ' . $ip . ' - IP blacklisted');
40
+ return true;
41
+ }
42
+
43
+ if ($this->is_address_blacklisted($email)) {
44
+ $this->logger->fatal($email . ' - ' . $ip . ' - Address blacklisted');
45
+ return true;
46
+ }
47
+
48
+ // Akismet check
49
+ $user_agent = isset( $_SERVER['HTTP_USER_AGENT'] ) ? $_SERVER['HTTP_USER_AGENT'] : '';
50
+ $referrer = isset( $_SERVER['HTTP_REFERER'] ) ? $_SERVER['HTTP_REFERER'] : '';
51
+ if ( $this->is_spam_by_akismet( $email, $full_name, $ip, $user_agent, $referrer ) ) {
52
+ $this->logger->fatal( $email . ' - ' . $ip . ' - Akismet blocked' );
53
+
54
+ return true;
55
+ }
56
+
57
+ // Flood check
58
+ if ($this->is_flood($email, $ip)) {
59
+ $this->logger->fatal($email . ' - ' . $ip . ' - Antiflood triggered');
60
+ return true;
61
+ }
62
+
63
+ // if ($this->is_missing_domain_mx($email)) {
64
+ // $this->logger->fatal($email . ' - ' . $ip . ' - MX check failed');
65
+ // header("HTTP/1.0 404 Not Found");
66
+ // return true;
67
+ // }
68
+
69
+ return false;
70
+ }
71
+
72
+ function is_address_blacklisted($email) {
73
+
74
+ if (empty($this->options['address_blacklist'])) {
75
+ return false;
76
+ }
77
+
78
+ $this->logger->debug('Address blacklist check');
79
+ $rev_email = strrev($email);
80
+ foreach ($this->options['address_blacklist'] as $item) {
81
+ if (strpos($rev_email, strrev($item)) === 0) {
82
+ return true;
83
+ }
84
+ }
85
+ return false;
86
+ }
87
+
88
+ function is_ip_blacklisted($ip) {
89
+
90
+ if ($ip === '::1' || $ip === '127.0.0.1') {
91
+ return false;
92
+ }
93
+
94
+ if (empty($this->options['ip_blacklist'])) {
95
+ return false;
96
+ }
97
+ $this->logger->debug('IP blacklist check');
98
+ foreach ($this->options['ip_blacklist'] as $item) {
99
+ if (substr($item, 0, 1) === '#') {
100
+ continue;
101
+ }
102
+ if ($this->ip_match($ip, $item)) {
103
+ return true;
104
+ }
105
+ }
106
+ return false;
107
+ }
108
+
109
+ function is_missing_domain_mx($email) {
110
+ // Actually not fully implemented
111
+ return false;
112
+
113
+ if (empty($this->options['domain_check'])) {
114
+ return false;
115
+ }
116
+
117
+ $this->logger->debug('Domain MX check');
118
+ list($local, $domain) = explode('@', $email);
119
+
120
+ $hosts = array();
121
+ if (!getmxrr($domain, $hosts)) {
122
+ return true;
123
+ }
124
+ return false;
125
+ }
126
+
127
+ function is_flood($email, $ip) {
128
+ global $wpdb;
129
+
130
+ if (empty($this->options['antiflood'])) {
131
+ return false;
132
+ }
133
+
134
+ $this->logger->debug('Antiflood check');
135
+
136
+ $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));
137
+
138
+ if ($updated && time() - $updated < $this->options['antiflood']) {
139
+ return true;
140
+ }
141
+
142
+ return false;
143
+ }
144
+
145
+ function is_spam_text($text) {
146
+ if (stripos($text, 'http:') !== false || stripos($text, 'https:') !== false) {
147
+ return true;
148
+ }
149
+ if (stripos($text, 'www.') !== false) {
150
+ return true;
151
+ }
152
+ if (preg_match('|[^\s\.]+\.[^\s\.]+\.[^\s\.]{2,}|', $text)) {
153
+ return true;
154
+ }
155
+
156
+ return false;
157
+ }
158
+
159
+ function is_spam_by_akismet($email, $name, $ip, $agent, $referrer) {
160
+
161
+ if (!class_exists('Akismet')) {
162
+ return false;
163
+ }
164
+
165
+ if (empty($this->options['akismet'])) {
166
+ return false;
167
+ }
168
+
169
+ $this->logger->debug('Akismet check');
170
+ $request = 'blog=' . urlencode(home_url()) . '&referrer=' . urlencode($referrer) .
171
+ '&user_agent=' . urlencode($agent) .
172
+ '&comment_type=signup' .
173
+ '&comment_author_email=' . urlencode($email) .
174
+ '&user_ip=' . urlencode($ip);
175
+ if (!empty($name)) {
176
+ $request .= '&comment_author=' . urlencode($name);
177
+ }
178
+
179
+ $response = Akismet::http_post($request, 'comment-check');
180
+
181
+ if ($response && $response[1] == 'true') {
182
+ return true;
183
+ }
184
+ return false;
185
+ }
186
+
187
+ function ip_match($ip, $range) {
188
+ if (empty($ip))
189
+ return false;
190
+ if (strpos($range, '/')) {
191
+ list ($subnet, $bits) = explode('/', $range);
192
+ $ip = ip2long($ip);
193
+ $subnet = ip2long($subnet);
194
+ $mask = -1 << (32 - $bits);
195
+ $subnet &= $mask; # nb: in case the supplied subnet wasn't correctly aligned
196
+ return ($ip & $mask) == $subnet;
197
+ } else {
198
+ return strpos($range, $ip) === 0;
199
+ }
200
+ }
201
+
202
+ }
includes/composer.php CHANGED
@@ -564,6 +564,13 @@ class TNP_Composer {
564
 
565
  /**
566
  * Inspired by: https://webdesign.tutsplus.com/tutorials/creating-a-future-proof-responsive-email-without-media-queries--cms-23919
 
 
 
 
 
 
 
567
  * @param string[] $items
568
  * @param array $attrs
569
  * @return string
564
 
565
  /**
566
  * Inspired by: https://webdesign.tutsplus.com/tutorials/creating-a-future-proof-responsive-email-without-media-queries--cms-23919
567
+ *
568
+ * Attributes:
569
+ * - columns: number of columns [2]
570
+ * - padding: cells padding [10]
571
+ * - responsive: il on mobile the cell should stack up [true]
572
+ * - width: the whole row width, it should reduced by the external row padding [600]
573
+ *
574
  * @param string[] $items
575
  * @param array $attrs
576
  * @return string
includes/controls.php CHANGED
@@ -1,2112 +1,2112 @@
1
- <?php
2
-
3
- defined('ABSPATH') || exit;
4
-
5
- include_once __DIR__ . '/fields.php';
6
-
7
- class NewsletterControls {
8
-
9
- var $data = [];
10
- var $action = false;
11
- var $button_data = '';
12
- var $errors = '';
13
-
14
- /**
15
- * @var string
16
- */
17
- var $messages = '';
18
-
19
- /**
20
- * @var array
21
- */
22
- var $warnings = array();
23
- var $countries = array(
24
- 'AF' => 'Afghanistan',
25
- 'AX' => 'Aland Islands',
26
- 'AL' => 'Albania',
27
- 'DZ' => 'Algeria',
28
- 'AS' => 'American Samoa',
29
- 'AD' => 'Andorra',
30
- 'AO' => 'Angola',
31
- 'AI' => 'Anguilla',
32
- 'AQ' => 'Antarctica',
33
- 'AG' => 'Antigua And Barbuda',
34
- 'AR' => 'Argentina',
35
- 'AM' => 'Armenia',
36
- 'AW' => 'Aruba',
37
- 'AU' => 'Australia',
38
- 'AT' => 'Austria',
39
- 'AZ' => 'Azerbaijan',
40
- 'BS' => 'Bahamas',
41
- 'BH' => 'Bahrain',
42
- 'BD' => 'Bangladesh',
43
- 'BB' => 'Barbados',
44
- 'BY' => 'Belarus',
45
- 'BE' => 'Belgium',
46
- 'BZ' => 'Belize',
47
- 'BJ' => 'Benin',
48
- 'BM' => 'Bermuda',
49
- 'BT' => 'Bhutan',
50
- 'BO' => 'Bolivia',
51
- 'BA' => 'Bosnia And Herzegovina',
52
- 'BW' => 'Botswana',
53
- 'BV' => 'Bouvet Island',
54
- 'BR' => 'Brazil',
55
- 'IO' => 'British Indian Ocean Territory',
56
- 'BN' => 'Brunei Darussalam',
57
- 'BG' => 'Bulgaria',
58
- 'BF' => 'Burkina Faso',
59
- 'BI' => 'Burundi',
60
- 'KH' => 'Cambodia',
61
- 'CM' => 'Cameroon',
62
- 'CA' => 'Canada',
63
- 'CV' => 'Cape Verde',
64
- 'KY' => 'Cayman Islands',
65
- 'CF' => 'Central African Republic',
66
- 'TD' => 'Chad',
67
- 'CL' => 'Chile',
68
- 'CN' => 'China',
69
- 'CX' => 'Christmas Island',
70
- 'CC' => 'Cocos (Keeling) Islands',
71
- 'CO' => 'Colombia',
72
- 'KM' => 'Comoros',
73
- 'CG' => 'Congo',
74
- 'CD' => 'Congo, Democratic Republic',
75
- 'CK' => 'Cook Islands',
76
- 'CR' => 'Costa Rica',
77
- 'CI' => 'Cote D\'Ivoire',
78
- 'HR' => 'Croatia',
79
- 'CU' => 'Cuba',
80
- 'CY' => 'Cyprus',
81
- 'CZ' => 'Czech Republic',
82
- 'DK' => 'Denmark',
83
- 'DJ' => 'Djibouti',
84
- 'DM' => 'Dominica',
85
- 'DO' => 'Dominican Republic',
86
- 'EC' => 'Ecuador',
87
- 'EG' => 'Egypt',
88
- 'SV' => 'El Salvador',
89
- 'GQ' => 'Equatorial Guinea',
90
- 'ER' => 'Eritrea',
91
- 'EE' => 'Estonia',
92
- 'ET' => 'Ethiopia',
93
- 'FK' => 'Falkland Islands (Malvinas)',
94
- 'FO' => 'Faroe Islands',
95
- 'FJ' => 'Fiji',
96
- 'FI' => 'Finland',
97
- 'FR' => 'France',
98
- 'GF' => 'French Guiana',
99
- 'PF' => 'French Polynesia',
100
- 'TF' => 'French Southern Territories',
101
- 'GA' => 'Gabon',
102
- 'GM' => 'Gambia',
103
- 'GE' => 'Georgia',
104
- 'DE' => 'Germany',
105
- 'GH' => 'Ghana',
106
- 'GI' => 'Gibraltar',
107
- 'GR' => 'Greece',
108
- 'GL' => 'Greenland',
109
- 'GD' => 'Grenada',
110
- 'GP' => 'Guadeloupe',
111
- 'GU' => 'Guam',
112
- 'GT' => 'Guatemala',
113
- 'GG' => 'Guernsey',
114
- 'GN' => 'Guinea',
115
- 'GW' => 'Guinea-Bissau',
116
- 'GY' => 'Guyana',
117
- 'HT' => 'Haiti',
118
- 'HM' => 'Heard Island & Mcdonald Islands',
119
- 'VA' => 'Holy See (Vatican City State)',
120
- 'HN' => 'Honduras',
121
- 'HK' => 'Hong Kong',
122
- 'HU' => 'Hungary',
123
- 'IS' => 'Iceland',
124
- 'IN' => 'India',
125
- 'ID' => 'Indonesia',
126
- 'IR' => 'Iran, Islamic Republic Of',
127
- 'IQ' => 'Iraq',
128
- 'IE' => 'Ireland',
129
- 'IM' => 'Isle Of Man',
130
- 'IL' => 'Israel',
131
- 'IT' => 'Italy',
132
- 'JM' => 'Jamaica',
133
- 'JP' => 'Japan',
134
- 'JE' => 'Jersey',
135
- 'JO' => 'Jordan',
136
- 'KZ' => 'Kazakhstan',
137
- 'KE' => 'Kenya',
138
- 'KI' => 'Kiribati',
139
- 'KR' => 'Korea',
140
- 'KW' => 'Kuwait',
141
- 'KG' => 'Kyrgyzstan',
142
- 'LA' => 'Lao People\'s Democratic Republic',
143
- 'LV' => 'Latvia',
144
- 'LB' => 'Lebanon',
145
- 'LS' => 'Lesotho',
146
- 'LR' => 'Liberia',
147
- 'LY' => 'Libyan Arab Jamahiriya',
148
- 'LI' => 'Liechtenstein',
149
- 'LT' => 'Lithuania',
150
- 'LU' => 'Luxembourg',
151
- 'MO' => 'Macao',
152
- 'MK' => 'Macedonia',
153
- 'MG' => 'Madagascar',
154
- 'MW' => 'Malawi',
155
- 'MY' => 'Malaysia',
156
- 'MV' => 'Maldives',
157
- 'ML' => 'Mali',
158
- 'MT' => 'Malta',
159
- 'MH' => 'Marshall Islands',
160
- 'MQ' => 'Martinique',
161
- 'MR' => 'Mauritania',
162
- 'MU' => 'Mauritius',
163
- 'YT' => 'Mayotte',
164
- 'MX' => 'Mexico',
165
- 'FM' => 'Micronesia, Federated States Of',
166
- 'MD' => 'Moldova',
167
- 'MC' => 'Monaco',
168
- 'MN' => 'Mongolia',
169
- 'ME' => 'Montenegro',
170
- 'MS' => 'Montserrat',
171
- 'MA' => 'Morocco',
172
- 'MZ' => 'Mozambique',
173
- 'MM' => 'Myanmar',
174
- 'NA' => 'Namibia',
175
- 'NR' => 'Nauru',
176
- 'NP' => 'Nepal',
177
- 'NL' => 'Netherlands',
178
- 'AN' => 'Netherlands Antilles',
179
- 'NC' => 'New Caledonia',
180
- 'NZ' => 'New Zealand',
181
- 'NI' => 'Nicaragua',
182
- 'NE' => 'Niger',
183
- 'NG' => 'Nigeria',
184
- 'NU' => 'Niue',
185
- 'NF' => 'Norfolk Island',
186
- 'MP' => 'Northern Mariana Islands',
187
- 'NO' => 'Norway',
188
- 'OM' => 'Oman',
189
- 'PK' => 'Pakistan',
190
- 'PW' => 'Palau',
191
- 'PS' => 'Palestinian Territory, Occupied',
192
- 'PA' => 'Panama',
193
- 'PG' => 'Papua New Guinea',
194
- 'PY' => 'Paraguay',
195
- 'PE' => 'Peru',
196
- 'PH' => 'Philippines',
197
- 'PN' => 'Pitcairn',
198
- 'PL' => 'Poland',
199
- 'PT' => 'Portugal',
200
- 'PR' => 'Puerto Rico',
201
- 'QA' => 'Qatar',
202
- 'RE' => 'Reunion',
203
- 'RO' => 'Romania',
204
- 'RU' => 'Russian Federation',
205
- 'RW' => 'Rwanda',
206
- 'BL' => 'Saint Barthelemy',
207
- 'SH' => 'Saint Helena',
208
- 'KN' => 'Saint Kitts And Nevis',
209
- 'LC' => 'Saint Lucia',
210
- 'MF' => 'Saint Martin',
211
- 'PM' => 'Saint Pierre And Miquelon',
212
- 'VC' => 'Saint Vincent And Grenadines',
213
- 'WS' => 'Samoa',
214
- 'SM' => 'San Marino',
215
- 'ST' => 'Sao Tome And Principe',
216
- 'SA' => 'Saudi Arabia',
217
- 'SN' => 'Senegal',
218
- 'RS' => 'Serbia',
219
- 'SC' => 'Seychelles',
220
- 'SL' => 'Sierra Leone',
221
- 'SG' => 'Singapore',
222
- 'SK' => 'Slovakia',
223
- 'SI' => 'Slovenia',
224
- 'SB' => 'Solomon Islands',
225
- 'SO' => 'Somalia',
226
- 'ZA' => 'South Africa',
227
- 'GS' => 'South Georgia And Sandwich Isl.',
228
- 'ES' => 'Spain',
229
- 'LK' => 'Sri Lanka',
230
- 'SD' => 'Sudan',
231
- 'SR' => 'Suriname',
232
- 'SJ' => 'Svalbard And Jan Mayen',
233
- 'SZ' => 'Swaziland',
234
- 'SE' => 'Sweden',
235
- 'CH' => 'Switzerland',
236
- 'SY' => 'Syrian Arab Republic',
237
- 'TW' => 'Taiwan',
238
- 'TJ' => 'Tajikistan',
239
- 'TZ' => 'Tanzania',
240
- 'TH' => 'Thailand',
241
- 'TL' => 'Timor-Leste',
242
- 'TG' => 'Togo',
243
- 'TK' => 'Tokelau',
244
- 'TO' => 'Tonga',
245
- 'TT' => 'Trinidad And Tobago',
246
- 'TN' => 'Tunisia',
247
- 'TR' => 'Turkey',
248
- 'TM' => 'Turkmenistan',
249
- 'TC' => 'Turks And Caicos Islands',
250
- 'TV' => 'Tuvalu',
251
- 'UG' => 'Uganda',
252
- 'UA' => 'Ukraine',
253
- 'AE' => 'United Arab Emirates',
254
- 'GB' => 'United Kingdom',
255
- 'US' => 'United States',
256
- 'UM' => 'United States Outlying Islands',
257
- 'UY' => 'Uruguay',
258
- 'UZ' => 'Uzbekistan',
259
- 'VU' => 'Vanuatu',
260
- 'VE' => 'Venezuela',
261
- 'VN' => 'Viet Nam',
262
- 'VG' => 'Virgin Islands, British',
263
- 'VI' => 'Virgin Islands, U.S.',
264
- 'WF' => 'Wallis And Futuna',
265
- 'EH' => 'Western Sahara',
266
- 'YE' => 'Yemen',
267
- 'ZM' => 'Zambia',
268
- 'ZW' => 'Zimbabwe',
269
- 'XX' => 'Undefined',
270
- 'CW' => 'Curaçao',
271
- 'SS' => 'South Sudan',
272
- 'EU' => 'Europe (generic)',
273
- 'A1' => 'Anonymous IP',
274
- 'A2' => 'Satellite IP'
275
- );
276
-
277
- /**
278
- *
279
- * @param array $options
280
- */
281
- function __construct($options = null) {
282
- if ($options === null) {
283
- if (isset($_POST['options'])) {
284
- $this->data = stripslashes_deep($_POST['options']);
285
- }
286
- } else {
287
- $this->data = (array) $options;
288
- }
289
-
290
- if (isset($_REQUEST['act'])) {
291
- $this->action = $_REQUEST['act'];
292
- }
293
-
294
- if (isset($_REQUEST['btn'])) {
295
- $this->button_data = $_REQUEST['btn'];
296
- }
297
- // Fields analysis
298
- if (isset($_REQUEST['tnp_fields'])) {
299
- $fields = $_REQUEST['tnp_fields'];
300
- if (is_array($fields)) {
301
- foreach ($fields as $name => $type) {
302
- if ($type == 'datetime') {
303
- // Ex. The user insert 01/07/2012 14:30 and it set the time zone to +2. We cannot use the
304
- // mktime, since it uses the time zone of the machine. We create the time as if we are on
305
- // GMT 0 and then we subtract the GMT offset (the example date and time on GMT+2 happens
306
- // "before").
307
-
308
- $time = gmmktime((int) $_REQUEST[$name . '_hour'], 0, 0, (int) $_REQUEST[$name . '_month'], (int) $_REQUEST[$name . '_day'], (int) $_REQUEST[$name . '_year']);
309
- $time -= get_option('gmt_offset') * 3600;
310
- $this->data[$name] = $time;
311
- continue;
312
- }
313
- if ($type === 'array') {
314
- if (!isset($this->data[$name]))
315
- $this->data[$name] = [];
316
- }
317
- if ($type === 'checkbox') {
318
- if (!isset($this->data[$name])) {
319
- $this->data[$name] = 0;
320
- }
321
- }
322
- }
323
- }
324
- }
325
- }
326
-
327
- function set_data($data) {
328
- if (is_array($data)) {
329
- $this->data = $data;
330
- } else if (is_object($data)) {
331
- $this->data = (array) $data;
332
- } else {
333
- $this->data = [];
334
- }
335
- }
336
-
337
- function merge($options) {
338
- if (!is_array($options))
339
- return;
340
- if ($this->data == null)
341
- $this->data = array();
342
- $this->data = array_merge($this->data, $options);
343
- }
344
-
345
- function merge_defaults($defaults) {
346
- if ($this->data == null)
347
- $this->data = $defaults;
348
- else
349
- $this->data = array_merge($defaults, $this->data);
350
- }
351
-
352
- /**
353
- * Return true is there in an asked action is no action name is specified or
354
- * true is the requested action matches the passed action.
355
- * Dies if it is not a safe call.
356
- */
357
- function is_action($action = null) {
358
- if ($action == null)
359
- return $this->action != null;
360
- if ($this->action == null)
361
- return false;
362
- if ($this->action != $action)
363
- return false;
364
- if (check_admin_referer('save'))
365
- return true;
366
- die('Invalid call');
367
- }
368
-
369
- function get_value($name, $def = null) {
370
- if (!isset($this->data[$name])) {
371
- return $def;
372
- }
373
- return $this->data[$name];
374
- }
375
-
376
- function get_value_array($name) {
377
- if (!isset($this->data[$name]) || !is_array($this->data[$name]))
378
- return array();
379
- return $this->data[$name];
380
- }
381
-
382
- function show_error($text) {
383
- echo '<div class="tnp-error">', $text, '</div>';
384
- }
385
-
386
- function show_warning($text) {
387
- echo '<div class="tnp-warning">', $text, '</div>';
388
- }
389
-
390
- function show_message($text) {
391
- echo '<div class="tnpc-message">', $text, '</div>';
392
- }
393
-
394
- /**
395
- * Show the errors and messages.
396
- */
397
- function show() {
398
- static $shown = false;
399
-
400
- if ($shown) {
401
- return;
402
- }
403
- $shown = true;
404
-
405
- if (!empty($this->errors)) {
406
- echo '<div class="tnpc-error">';
407
- echo $this->errors;
408
- echo '</div>';
409
- }
410
- if (!empty($this->warnings)) {
411
- foreach ((array) $this->warnings as $warning) {
412
- echo '<div class="tnpc-warning">';
413
- echo $warning;
414
- echo '</div>';
415
- }
416
- }
417
- if (!empty($this->messages)) {
418
- echo '<div class="tnpc-message">';
419
- echo $this->messages;
420
- echo '</div>';
421
- }
422
- }
423
-
424
- function add_message($text) {
425
- if (!empty($this->messages)) {
426
- $this->messages .= '<br><br>';
427
- }
428
- $this->messages .= $text;
429
- }
430
-
431
- function add_message_saved() {
432
- if (!empty($this->messages)) {
433
- $this->messages .= '<br><br>';
434
- }
435
- $this->messages .= __('Saved.', 'newsletter');
436
- }
437
-
438
- function add_message_deleted() {
439
- if (!empty($this->messages)) {
440
- $this->messages .= '<br><br>';
441
- }
442
- $this->messages .= __('Deleted.', 'newsletter');
443
- }
444
-
445
- function add_message_reset() {
446
- if (!empty($this->messages)) {
447
- $this->messages .= '<br><br>';
448
- }
449
- $this->messages .= __('Options reset.', 'newsletter');
450
- }
451
-
452
- function add_message_done() {
453
- if (!empty($this->messages)) {
454
- $this->messages .= '<br><br>';
455
- }
456
- $this->messages .= __('Done.', 'newsletter');
457
- }
458
-
459
- function add_language_warning() {
460
- $newsletter = Newsletter::instance();
461
- $current_language = $newsletter->get_current_language();
462
-
463
- if (!$current_language) {
464
- return;
465
- }
466
- $this->warnings[] = 'You are configuring the language <strong>' . $newsletter->get_language_label($current_language) . '</strong>. Switch to "all languages" to see all options.';
467
- }
468
-
469
- function switch_to_all_languages_notice() {
470
- echo '<div class="tnpc-languages-notice">';
471
- _e('Switch the administration side to "all languages" to set these options', 'newsletter');
472
- echo '</div>';
473
- }
474
-
475
- function hint($text, $url = '') {
476
- echo '<div class="tnpc-hint">';
477
- // Do not escape that, it can be formatted
478
- echo $text;
479
- if (!empty($url)) {
480
- echo ' <a href="' . esc_attr($url) . '" target="_blank">Read more</a>.';
481
- }
482
- echo '</div>';
483
- }
484
-
485
- function yesno($name) {
486
- $value = isset($this->data[$name]) ? (int) $this->data[$name] : 0;
487
-
488
- echo '<select style="width: 60px" name="options[', esc_attr($name), ']">';
489
- echo '<option value="0"';
490
- if ($value == 0) {
491
- echo ' selected';
492
- }
493
- echo '>', __('No', 'newsletter'), '</option>';
494
- echo '<option value="1"';
495
- if ($value == 1) {
496
- echo ' selected';
497
- }
498
- echo '>', __('Yes', 'newsletter'), '</option>';
499
- echo '</select>&nbsp;&nbsp;&nbsp;';
500
- }
501
-
502
- function enabled($name = 'enabled', $attrs = []) {
503
- $value = isset($this->data[$name]) ? (int) $this->data[$name] : 0;
504
- $name = esc_attr($name);
505
-
506
- echo '<select style="width: 100px" name="options[', $name, ']" id="options-', $name, '"';
507
- if (isset($attrs['bind_to'])) {
508
- echo ' onchange="tnp_select_toggle(this, \'', $attrs['bind_to'], '\')"';
509
- }
510
- echo '>';
511
- echo '<option value="0"';
512
- if ($value == 0) {
513
- echo ' selected';
514
- }
515
- echo '>', __('Disabled', 'newsletter'), '</option>';
516
- echo '<option value="1"';
517
- if ($value == 1) {
518
- echo ' selected';
519
- }
520
- echo '>', __('Enabled', 'newsletter'), '</option>';
521
- echo '</select>';
522
- if (isset($attrs['bind_to'])) {
523
- if ($value) {
524
- echo '<script>jQuery(function ($) {$("#options-', $attrs['bind_to'], '").show()})</script>';
525
- } else {
526
- echo '<script>jQuery(function ($) {$("#options-', $attrs['bind_to'], '").hide()})</script>';
527
- }
528
- }
529
- }
530
-
531
- function disabled($name) {
532
- $value = isset($this->data[$name]) ? (int) $this->data[$name] : 0;
533
-
534
- echo '<select style="width: 100px" name="options[' . esc_attr($name) . ']">';
535
- echo '<option value="0"';
536
- if ($value == 0) {
537
- echo ' selected';
538
- }
539
- echo '>Enabled</option>';
540
- echo '<option value="1"';
541
- if ($value == 1) {
542
- echo ' selected';
543
- }
544
- echo '>Disabled</option>';
545
- echo '</select>';
546
- }
547
-
548
- /**
549
- * Creates a set of checkbox all named as $name with values and labels extracted from
550
- * $values_labels. A checkbox will be checked if internal data under key $name is an array
551
- * and contains the value of the current (echoing) checkbox.
552
- *
553
- * On submit it produces an array under the name $name IF at least one checkbox has
554
- * been checked. Otherwise the key won't be present.
555
- *
556
- * @param array $values
557
- * @param string $name
558
- * @param array $values_labels
559
- */
560
- function checkboxes_group($name, $values_labels) {
561
- $value_array = $this->get_value_array($name);
562
-
563
- echo '<div class="tnpc-checkboxes">';
564
- foreach ($values_labels as $value => $label) {
565
- echo '<label><input type="checkbox" id="' . esc_attr($name) . '" name="options[' . esc_attr($name) . '][]" value="' . esc_attr($value) . '"';
566
- if (array_search($value, $value_array) !== false) {
567
- echo ' checked';
568
- }
569
- echo '>';
570
- if ($label != '') {
571
- echo '&nbsp;' . esc_html($label);
572
- }
573
- echo "</label>";
574
- }
575
- echo "<div style='clear: both'></div>";
576
- }
577
-
578
- /** Creates a checkbox group with all public post types.
579
- */
580
- function post_types($name = 'post_types') {
581
- $list = array();
582
- $post_types = get_post_types(array('public' => true), 'objects', 'and');
583
- foreach ($post_types as $post_type) {
584
- $list[$post_type->name] = $post_type->labels->name;
585
- }
586
-
587
- $this->checkboxes_group($name, $list);
588
- }
589
-
590
- function posts_select($name, $max = 20, $args = array()) {
591
- $args = array_merge(array(
592
- 'posts_per_page' => 5,
593
- 'offset' => 0,
594
- 'category' => '',
595
- 'category_name' => '',
596
- 'orderby' => 'date',
597
- 'order' => 'DESC',
598
- 'include' => '',
599
- 'exclude' => '',
600
- 'meta_key' => '',
601
- 'meta_value' => '',
602
- 'post_type' => 'post',
603
- 'post_mime_type' => '',
604
- 'post_parent' => '',
605
- 'author' => '',
606
- 'author_name' => '',
607
- 'post_status' => 'publish',
608
- 'suppress_filters' => true
609
- ), $args);
610
- $args['posts_per_page'] = $max;
611
-
612
- $posts = get_posts($args);
613
- $options = array();
614
- foreach ($posts as $post) {
615
- $options['' . $post->ID] = $post->post_title;
616
- }
617
-
618
- $this->select($name, $options);
619
- }
620
-
621
- function select_number($name, $min, $max) {
622
- $options = array();
623
- for ($i = $min; $i <= $max; $i++) {
624
- $options['' . $i] = $i;
625
- }
626
- $this->select($name, $options);
627
- }
628
-
629
- function page($name = 'page', $first = null, $language = '', $show_id = false) {
630
- $args = array(
631
- 'post_type' => 'page',
632
- 'posts_per_page' => 1000,
633
- 'offset' => 0,
634
- 'orderby' => 'post_title',
635
- 'post_status' => 'any',
636
- 'suppress_filters' => true
637
- );
638
-
639
- $pages = get_posts($args);
640
- //$pages = get_pages();
641
- $options = array();
642
- foreach ($pages as $page) {
643
- /* @var $page WP_Post */
644
- $label = $page->post_title;
645
- if ($page->post_status != 'publish') {
646
- $label .= ' (' . $page->post_status . ')';
647
- }
648
- if ($show_id) {
649
- $label .= ' [' . $page->ID . ']';
650
- }
651
- $options[$page->ID] = $label;
652
- }
653
- $this->select($name, $options, $first);
654
- }
655
-
656
- /** Used to create a select which is part of a group of controls identified by $name that will
657
- * produce an array of values as $_REQUEST['name'].
658
- * @param string $name
659
- * @param array $options Associative array
660
- */
661
- function select_group($name, $options) {
662
- $value_array = $this->get_value_array($name);
663
-
664
- echo '<select name="options[' . esc_attr($name) . '][]">';
665
-
666
- foreach ($options as $key => $label) {
667
- echo '<option value="' . esc_attr($key) . '"';
668
- if (array_search($key, $value_array) !== false) {
669
- echo ' selected';
670
- }
671
- echo '>' . esc_html($label) . '</option>';
672
- }
673
-
674
- echo '</select>';
675
- }
676
-
677
- function select($name, $options, $first = null, $attrs = []) {
678
- echo '<select id="options-' . esc_attr($name) . '" name="options[' . esc_attr($name) . ']"';
679
- if ($attrs) {
680
- foreach ($attrs as $key => $value) {
681
- echo ' ', $key, '="' . esc_attr($value), '"';
682
- }
683
- }
684
- echo '>';
685
- if (!empty($first)) {
686
- echo '<option value="">' . esc_html($first) . '</option>';
687
- }
688
- $value = $this->get_value($name);
689
- foreach ($options as $key => $label) {
690
- echo '<option value="' . esc_attr($key) . '"';
691
- if ($value == $key) {
692
- echo ' selected';
693
- }
694
- echo '>' . esc_html($label) . '</option>';
695
- }
696
- echo '</select>';
697
- }
698
-
699
- function select_images($name, $options, $first = null) {
700
- $value = $this->get_value($name);
701
-
702
- echo '<select id="options-' . esc_attr($name) . '" name="options[' . esc_attr($name) . ']" style="min-width: 200px">';
703
- if (!empty($first)) {
704
- echo '<option value="">' . esc_html($first) . '</option>';
705
- } else {
706
- // if (empty($value)) {
707
- // $keys = array_keys($options);
708
- // $value = $keys[0];
709
- // }
710
- }
711
- foreach ($options as $key => $data) {
712
- echo '<option value="' . esc_attr($key) . '" image="' . esc_attr($data['image']) . '"';
713
- if ($value == $key)
714
- echo ' selected';
715
- echo '>' . esc_html($data['label']) . '</option>';
716
- }
717
- echo '</select>';
718
- echo '<script>jQuery("#options-' . esc_attr($name) . '").select2({templateResult: tnp_select_images, templateSelection: tnp_select_images_selection});</script>';
719
- }
720
-
721
- function select2($name, $options, $first = null, $multiple = false, $style = null, $placeholder = '') {
722
-
723
- if ($multiple) {
724
- $option_name = "options[" . esc_attr($name) . "][]";
725
- } else {
726
- $option_name = "options[" . esc_attr($name) . "]";
727
- }
728
-
729
- if (is_null($style)) {
730
- $style = 'width: 100%';
731
- }
732
-
733
- $value = $this->get_value($name);
734
-
735
- echo '<select id="options-', esc_attr($name), '" name="', $option_name, '" style="', $style, '"',
736
- ($multiple ? ' multiple' : ''), '>';
737
- if (!empty($first)) {
738
- echo '<option value="">' . esc_html($first) . '</option>';
739
- }
740
-
741
- foreach ($options as $key => $data) {
742
- echo '<option value="' . esc_attr($key) . '"';
743
- if (is_array($value) && in_array($key, $value) || (!is_null($value) && $value == $key )) {
744
- echo ' selected';
745
- }
746
- echo '>' . esc_html($data) . '</option>';
747
- }
748
-
749
- echo '</select>';
750
- echo '<script>jQuery("#options-' . esc_attr($name) . '").select2({placeholder: "', esc_js($placeholder), '"});</script>';
751
- }
752
-
753
- function select_grouped($name, $groups) {
754
- $value = $this->get_value($name);
755
- $name = esc_attr($name);
756
- echo '<select name="options[', $name, ']">';
757
-
758
- foreach ($groups as $group) {
759
- echo '<optgroup label="' . esc_attr($group['']) . '">';
760
- if (!empty($group)) {
761
- foreach ($group as $key => $label) {
762
- if ($key == '') {
763
- continue;
764
- }
765
- echo '<option value="' . esc_attr($key) . '"';
766
- if ($value == $key) {
767
- echo ' selected';
768
- }
769
- echo '>' . esc_html($label) . '</option>';
770
- }
771
- }
772
- echo '</optgroup>';
773
- }
774
- echo '</select>';
775
- }
776
-
777
- /**
778
- * Generated a select control with all available templates. From version 3 there are
779
- * only on kind of templates, they are no more separated by type.
780
- */
781
- function themes($name, $themes, $submit_on_click = true) {
782
- foreach ($themes as $key => $data) {
783
- echo '<label style="display: block; float: left; text-align: center; margin-right: 10px;">';
784
- echo esc_html($key) . '<br>';
785
- echo '<img src="' . esc_attr($data['screenshot']) . '" width="100" height="100" style="border: 1px solid #666; padding: 5px"><br>';
786
- echo '<input style="position: relative; top: -40px" type="radio" onchange="this.form.act.value=\'theme\';this.form.submit()" name="options[' . esc_attr($name) . ']" value="' . esc_attr($key) . '"';
787
- if ($this->data[$name] == $key) {
788
- echo ' checked';
789
- }
790
- echo '>';
791
- echo '</label>';
792
- }
793
- echo '<div style="clear: both"></div>';
794
- }
795
-
796
- function value($name) {
797
- echo esc_html($this->data[$name]);
798
- }
799
-
800
- function value_date($name, $show_remaining = true) {
801
- $time = $this->get_value($name);
802
-
803
- echo gmdate(get_option('date_format') . ' ' . get_option('time_format'), $time + get_option('gmt_offset') * 3600);
804
- $delta = $time - time();
805
- if ($show_remaining && $delta > 0) {
806
- echo 'Remaining: ';
807
- $delta = $time - time();
808
- $days = floor($delta / (24 * 3600));
809
- $delta = $delta - $days * 24 * 3600;
810
- $hours = floor($delta / 3600);
811
- $delta = $delta - $hours * 3600;
812
- $minutes = floor($delta / 60);
813
-
814
- if ($days > 0) {
815
- echo $days . ' days ';
816
- }
817
- echo $hours . ' hours ';
818
- echo $minutes . ' minutes ';
819
- }
820
- }
821
-
822
- function password($name, $size = 20, $placeholder = '') {
823
- $value = $this->get_value($name);
824
- $name = esc_attr($name);
825
- echo '<input id="options-', $name, '" placeholder="' . esc_attr($placeholder) . '" name="options[', $name, ']" type="password" autocomplete="off" ';
826
- if (!empty($size)) {
827
- echo 'size="', $size, '" ';
828
- }
829
- echo 'value="', esc_attr($value), '">';
830
- }
831
-
832
- function text($name, $size = 20, $placeholder = '') {
833
- $value = $this->get_value($name);
834
- $name = esc_attr($name);
835
- echo '<input id="options-', $name, '" placeholder="' . esc_attr($placeholder) . '" title="' . esc_attr($placeholder) . '" name="options[', $name, ']" type="text" ';
836
- if (!empty($size)) {
837
- echo 'size="', esc_attr($size), '" ';
838
- }
839
- echo 'value="', esc_attr($value), '">';
840
- }
841
-
842
- function text_email($name, $attrs = []) {
843
- if (is_numeric($attrs)) {
844
- $attrs = ['size' => $attrs];
845
- }
846
- $attrs = array_merge(['placeholder' => __('Valid email address', 'newsletter'), 'size' => 40, 'required' => false], $attrs);
847
-
848
- $value = $this->get_value($name);
849
- echo '<input name="options[' . esc_attr($name) . ']" type="email" placeholder="';
850
- echo esc_attr($attrs['placeholder']);
851
- echo '" size="', esc_attr($attrs['size']), '" value="', esc_attr($value) , '"';
852
- if ($attrs['required']) {
853
- echo ' required';
854
- }
855
- echo '>';
856
- }
857
-
858
- function text_url($name, $size = 40) {
859
- $value = $this->get_value($name);
860
- echo '<input name="options[' . esc_attr($name) . ']" type="url" placeholder="http://..." size="' . esc_attr($size) . '" value="';
861
- echo esc_attr($value);
862
- echo '"/>';
863
- }
864
-
865
- function hidden($name) {
866
- $value = $this->get_value($name);
867
- echo '<input name="options[', esc_attr($name), ']" id="options-', esc_attr($name), '" type="hidden" value="', esc_attr($value), '">';
868
- }
869
-
870
- /**
871
- * General button. Attributes:
872
- * - id: the element HTML id
873
- * - confirm: if string the text is shown in a confirmation message, if true shows a standard confirm message
874
- * - icon: the font awesome icon name (fa-xxx)
875
- * - style: the CSS style
876
- * - data: free data associated to the button click ($controls->button_data) for example to pass the element ID from a list of elements
877
- *
878
- * @param string $action
879
- * @param string $label
880
- * @param array $attrs
881
- */
882
- function btn($action, $label, $attrs = []) {
883
- echo '<button class="button-primary tnpc-button"';
884
- if (isset($attrs['id'])) {
885
- echo ' id="', esc_attrs($attrs['id']), '"';
886
- }
887
- $onclick = "this.form.act.value='" . esc_attr(esc_js(trim($action))) . "';";
888
- if (!empty($attrs['data'])) {
889
- $onclick .= "this.form.btn.value='" . esc_attr(esc_js($attrs['data'])) . "';";
890
- }
891
- if (isset($attrs['confirm'])) {
892
- if (is_string($attrs['confirm'])) {
893
- $onclick .= "if (!confirm('" . esc_attr(esc_js($attrs['confirm'])) . "')) return false;";
894
- } else if ($attrs['confirm'] === true) {
895
- $onclick .= "if (!confirm('" . esc_attr(esc_js(__('Proceed?', 'newsletter'))) . "')) return false;";
896
- }
897
- }
898
- echo 'onclick="', $onclick, '"';
899
- if (!empty($attrs['title'])) {
900
- echo ' title="', esc_attr($attrs['title']), '"';
901
- }
902
- if (!empty($attrs['style'])) {
903
- echo ' style="', esc_attr($attrs['style']), '"';
904
- }
905
- echo '>';
906
- if (!empty($attrs['icon'])) {
907
- echo '<i class="fas ', esc_attr($attrs['icon']), '"></i>';
908
- if (!empty($label)) {
909
- echo '&nbsp;', esc_html($label);
910
- }
911
- } else {
912
- echo esc_html($label);
913
- }
914
- echo '</button>';
915
- }
916
-
917
- /**
918
- * Creates a link looking lie a standard button. Attributes:
919
- * - title: the link "title" HTML attribute
920
- * - target: the link "target" HTML attribute
921
- * - icon: the font awesome icon name (fa-xxx)
922
- * - style: the CSS style
923
- *
924
- * @param string $url
925
- * @param string $label
926
- * @param array $attrs
927
- */
928
- function btn_link($url, $label, $attrs = []) {
929
- echo '<a href="', esc_attr($url), '" class="button-primary tnpc-button"';
930
- if (!empty($attrs['style'])) {
931
- echo ' style="', esc_attr($attrs['style']), '"';
932
- }
933
- if (!empty($attrs['title'])) {
934
- echo ' title="', esc_attr($attrs['title']), '"';
935
- }
936
- if (!empty($attrs['target'])) {
937
- echo ' target="', esc_attr($attrs['target']), '"';
938
- }
939
- echo '>';
940
- if (!empty($attrs['icon'])) {
941
- echo '<i class="fas ', esc_attr($attrs['icon']), '"></i>';
942
- if (!empty($label)) {
943
- echo '&nbsp;', esc_html($label);
944
- }
945
- } else {
946
- echo esc_html($label);
947
- }
948
- echo '</a>';
949
- }
950
-
951
- function button($action, $label, $function = '', $id = '') {
952
- $id = !empty($id) ? " id=\"$id\" " : '';
953
- if ($function != null) {
954
- echo '<input ' . $id . ' class="button-primary tnpc-button" type="button" value="' . esc_attr($label) . '" onclick="this.form.act.value=\'' . esc_attr($action) . '\';' . esc_html($function) . '"/>';
955
- } else {
956
- echo '<input ' . $id . ' class="button-primary tnpc-button" type="submit" value="' . esc_attr($label) . '" onclick="this.form.act.value=\'' . esc_attr($action) . '\';return true;"/>';
957
- }
958
- }
959
-
960
- function action_link($action, $label, $function = null) {
961
- if ($function != null) {
962
- echo '<input class="button-link tnpc-button" type="button" value="' . esc_attr($label) . '" onclick="this.form.act.value=\'' . esc_attr($action) . '\';' . esc_html($function) . '"/>';
963
- } else {
964
- echo '<input class="button-link tnpc-button" type="submit" value="' . esc_attr($label) . '" onclick="this.form.act.value=\'' . esc_attr($action) . '\';return true;"/>';
965
- }
966
- }
967
-
968
- function button_save() {
969
- $this->btn('save', __('Save', 'newsletter'), ['icon' => 'fa-save']);
970
- }
971
-
972
- function button_reset($action = 'reset') {
973
- $this->btn($action, __('Reset', 'newsletter'), ['icon' => 'fa-reply', 'confirm' => true]);
974
- }
975
-
976
- function button_copy($data = '') {
977
- $this->btn('copy', __('Duplicate', 'newsletter'), ['data' => $data, 'icon' => 'fa-copy', 'confirm' => true]);
978
- }
979
-
980
- function button_icon_copy($data = '') {
981
- $this->btn('copy', '', ['data' => $data, 'icon' => 'fa-copy', 'confirm' => true, 'title' => __('Duplicate', 'newsletter')]);
982
- }
983
-
984
- /**
985
- * Creates a button with "delete" action.
986
- * @param type $data
987
- */
988
- function button_delete($data = '') {
989
- $this->btn('delete', __('Delete', 'newsletter'), ['data' => $data, 'icon' => 'fa-times', 'confirm' => true, 'style' => 'background-color: darkred; color: #ffffff']);
990
- }
991
-
992
- function button_icon_delete($data = '') {
993
- $this->btn('delete', '', ['data' => $data, 'icon' => 'fa-times', 'confirm' => true, 'title' => __('Delete', 'newsletter'), 'style' => 'background-color: darkred; color: #ffffff']);
994
- }
995
-
996
- function button_icon_configure($url) {
997
- $this->btn_link($url, '', ['icon' => 'fa-cog', 'title' => __('Configure', 'newsletter')]);
998
- }
999
-
1000
- function button_icon_subscribers($url) {
1001
- $this->btn_link($url, '', ['icon' => 'fa-users', 'title' => __('Subscribers', 'newsletter')]);
1002
- }
1003
-
1004
- function button_statistics($url) {
1005
- $this->btn_link($url, __('Statistics', 'newsletter'), ['icon' => 'fa-chart-bar']);
1006
- }
1007
-
1008
- function button_icon_statistics($url) {
1009
- $this->btn_link($url, '', ['icon' => 'fa-chart-bar', 'title' => __('Statistics', 'newsletter')]);
1010
- }
1011
-
1012
- function button_icon_view($url) {
1013
- $this->btn_link($url, '', ['icon' => 'fa-eye', 'title' => __('View', 'newsletter'), 'target' => '_blank']);
1014
- }
1015
-
1016
- function button_icon_newsletters($url) {
1017
- $this->btn_link($url, '', ['icon' => 'fa-file-alt', 'title' => __('Newsletters', 'newsletter')]);
1018
- }
1019
-
1020
- function button_icon_design($url) {
1021
- $this->btn_link($url, '', ['icon' => 'fa-paint-brush', 'title' => __('Design', 'newsletter')]);
1022
- }
1023
-
1024
- function button_icon_edit($url) {
1025
- $this->btn_link($url, '', ['icon' => 'fa-edit', 'title' => __('Edit', 'newsletter')]);
1026
- }
1027
-
1028
- function button_icon_back($url) {
1029
- $this->btn_link($url, '', ['icon' => 'fa-chevron-left', 'title' => __('Back', 'newsletter')]);
1030
- }
1031
-
1032
- function button_icon($action, $icon, $title = '', $data = '', $confirm = false) {
1033
- $this->btn($action, '', ['data' => $data, 'icon' => $icon, 'title' => $title, 'confirm' => $confirm]);
1034
- }
1035
-
1036
- function button_back($url) {
1037
- $this->btn_link($url, __('Back', 'newsletter'), ['icon' => 'fa-chevron-left']);
1038
- }
1039
-
1040
- function button_test($action = 'test') {
1041
- $this->btn($action, __('Test', 'newsletter'), ['icon' => 'fa-vial']);
1042
- }
1043
-
1044
- /**
1045
- * @deprecated
1046
- */
1047
- function button_primary($action, $label, $function = null) {
1048
- if ($function != null) {
1049
- echo '<button class="button-primary" onclick="this.form.act.value=\'' . esc_attr($action) . '\';' . esc_attr($function) . '">', $label, '</button>';
1050
- } else {
1051
- echo '<button class="button-primary" onclick="this.form.act.value=\'' . esc_attr($action) . '\'; return true;"/>', $label, '</button>';
1052
- }
1053
- }
1054
-
1055
- function button_confirm($action, $label, $message = true, $data = '') {
1056
- $this->btn($action, $label, ['data' => $data, 'confirm' => $message]);
1057
- }
1058
-
1059
- /**
1060
- * @deprecated
1061
- * @param string $url
1062
- * @param string $label Not escaped.
1063
- */
1064
- function button_link($url, $label = '') {
1065
- echo '<a href="', esc_attr($url), '" class="button-primary">', $label, '</a>';
1066
- }
1067
-
1068
- function editor($name, $rows = 5, $cols = 75) {
1069
- echo '<textarea class="visual" name="options[' . esc_attr($name) . ']" style="width: 100%" wrap="off" rows="' . esc_attr($rows) . '">';
1070
- echo esc_html($this->get_value($name));
1071
- echo '</textarea>';
1072
- }
1073
-
1074
- function wp_editor($name, $settings = []) {
1075
-
1076
- add_filter('mce_buttons', function ($mce_buttons) {
1077
- $mce_buttons[] = 'wp_add_media';
1078
- //$mce_buttons[] = 'wp_code';
1079
- return $mce_buttons;
1080
- });
1081
-
1082
- $settings = array_merge(['media_buttons' => false], $settings);
1083
-
1084
- $value = $this->get_value($name);
1085
- wp_editor($value, $name, array_merge(array(
1086
- 'tinymce' => array('content_css' => plugins_url('newsletter') . '/admin/wp-editor.css?ver=' . NEWSLETTER_VERSION),
1087
- 'textarea_name' => 'options[' . esc_attr($name) . ']',
1088
- 'wpautop' => false
1089
- ), $settings));
1090
- }
1091
-
1092
- function textarea($name, $width = '100%', $height = '50') {
1093
- $value = $this->get_value($name);
1094
- if (is_array($value)) {
1095
- $value = implode("\n", $value);
1096
- }
1097
- echo '<textarea id="options-' . esc_attr($name) . '" class="dynamic" name="options[' . esc_attr($name) . ']" wrap="off" style="width:' . esc_attr($width) . ';height:' . esc_attr($height) . '">';
1098
- echo esc_html($value);
1099
- echo '</textarea>';
1100
- }
1101
-
1102
- function textarea_fixed($name, $width = '100%', $height = '200') {
1103
- $value = $this->get_value($name);
1104
- $name = esc_attr($name);
1105
- echo '<textarea id="options-', $name, '" name="options[', $name, ']" wrap="off" style="width:', esc_attr($width), ';height:', esc_attr($height), 'px">';
1106
- echo esc_html($value);
1107
- echo '</textarea>';
1108
- }
1109
-
1110
- function textarea_preview($name, $width = '100%', $height = '200', $header = '', $footer = '', $switch_button = true) {
1111
- $value = $this->get_value($name);
1112
- $name = esc_attr($name);
1113
- if ($switch_button) {
1114
- echo '<input class="button-primary" type="button" onclick="newsletter_textarea_preview(\'options-', $name, '\', \'\', \'\')" value="Switch editor/preview">';
1115
- echo '<br><br>';
1116
- }
1117
- echo '<div style="box-sizing: border-box; position: relative; margin: 0; padding: 0; width:' . esc_attr($width) . '; height:' . esc_attr($height) . '">';
1118
- echo '<textarea id="options-', $name, '" name="options[', $name, ']" wrap="off" style="width:' . esc_attr($width) . ';height:' . esc_attr($height) . 'px">';
1119
- echo esc_html($value);
1120
- echo '</textarea>';
1121
- echo '<div id="options-', $name, '-preview" style="box-sizing: border-box; background-color: #eee; border: 1px solid #bbb; padding: 15px; width: auto; position: absolute; top: 20px; left: 20px; box-shadow: 0 0 20px #777; z-index: 10000; display: none">';
1122
- echo '<iframe id="options-', $name, '-iframe" class="tnp-editor-preview-desktop"></iframe>';
1123
- echo '<iframe id="options-', $name, '-iframe-phone" class="tnp-editor-preview-mobile"></iframe>';
1124
- echo '</div>';
1125
- echo '</div>';
1126
- }
1127
-
1128
- function email($prefix, $editor = null, $disable_option = false, $settings = array()) {
1129
- if ($disable_option) {
1130
- $this->disabled($prefix . '_disabled');
1131
- echo '<br>';
1132
- }
1133
-
1134
- $this->text($prefix . '_subject', 90, 'Subject');
1135
- echo '<br><br>';
1136
-
1137
- if ($editor == 'wordpress') {
1138
- $this->wp_editor($prefix . '_message', $settings);
1139
- } else if ($editor == 'textarea') {
1140
- $this->textarea($prefix . '_message');
1141
- } else {
1142
- $this->editor($prefix . '_message');
1143
- }
1144
- }
1145
-
1146
- /**
1147
- * Standard checkbox, when not checked no value is transmitted (checkbox2).
1148
- *
1149
- * @param string $name
1150
- * @param string $label
1151
- */
1152
- function checkbox($name, $label = '') {
1153
- if ($label != '') {
1154
- echo '<label>';
1155
- }
1156
- echo '<input type="checkbox" id="options-' . esc_attr($name) . '" name="options[' . esc_attr($name) . ']" value="1"';
1157
- if (!empty($this->data[$name])) {
1158
- echo ' checked';
1159
- }
1160
- echo '>';
1161
- if ($label != '') {
1162
- echo '&nbsp;' . esc_html($label) . '</label>';
1163
- }
1164
- }
1165
-
1166
- /**
1167
- * Checkbox with a hidden field to transmit 1 or 0 even when the checkbox is not checked.
1168
- *
1169
- * @param string $name
1170
- * @param string $label
1171
- */
1172
- function checkbox2($name, $label = '') {
1173
- if ($label != '') {
1174
- echo '<label>';
1175
- }
1176
- echo '<input type="checkbox" id="' . esc_attr($name) . '" onchange="document.getElementById(\'' . esc_attr($name) . '_hidden\').value=this.checked?\'1\':\'0\'"';
1177
- if (!empty($this->data[$name])) {
1178
- echo ' checked';
1179
- }
1180
- echo '>';
1181
- if ($label != '') {
1182
- echo '&nbsp;' . esc_html($label) . '</label>';
1183
- }
1184
- echo '<input type="hidden" id="' . esc_attr($name) . '_hidden" name="options[' . esc_attr($name) . ']" value="';
1185
-
1186
- echo empty($this->data[$name]) ? '0' : '1';
1187
- echo '">';
1188
- }
1189
-
1190
- function radio($name, $value, $label = '') {
1191
- if ($label != '') {
1192
- echo '<label>';
1193
- }
1194
- echo '<input type="radio" id="' . esc_attr($name) . '" name="options[' . esc_attr($name) . ']" value="' . esc_attr($value) . '"';
1195
- $v = $this->get_value($name);
1196
- if ($v == $value) {
1197
- echo ' checked';
1198
- }
1199
- echo '>';
1200
- if ($label != '') {
1201
- echo '&nbsp;' . esc_html($label) . '</label>';
1202
- }
1203
- }
1204
-
1205
- /**
1206
- * Creates a checkbox named $name and checked if the internal data contains under
1207
- * the key $name an array containig the passed value.
1208
- */
1209
- function checkbox_group($name, $value, $label = '', $attrs = []) {
1210
- $attrs = wp_parse_args($attrs, ['label_escape' => true]);
1211
- echo '<label><input type="checkbox" id="' . esc_attr($name) . '" name="options[' . esc_attr($name) . '][]" value="' . esc_attr($value) . '"';
1212
- if (isset($this->data[$name]) && is_array($this->data[$name]) && array_search($value, $this->data[$name]) !== false) {
1213
- echo ' checked';
1214
- }
1215
- echo '>';
1216
- if ($label != '') {
1217
- if ($attrs['label_escape']) {
1218
- echo esc_html($label);
1219
- } else {
1220
- echo $label;
1221
- }
1222
- }
1223
- echo '</label>';
1224
- }
1225
-
1226
- function checkboxes($name, $options) {
1227
- echo '<div class="tnpc-checkboxes">';
1228
- foreach ($options as $value => $label) {
1229
- $this->checkbox_group($name, $value, $label);
1230
- }
1231
- echo '<div style="clear: both"></div>';
1232
- echo '</div>';
1233
- }
1234
-
1235
- function color($name, $default = '') {
1236
- $value = esc_attr($this->get_value($name, $default));
1237
- $name = esc_attr($name);
1238
- echo '<input class="tnpc-color" id="options-', $name, '" name="options[', $name, ']" type="text" value="', $value, '">';
1239
- }
1240
-
1241
- /** Creates a set of checkbox named $name_[category id] (so they are posted with distinct names).
1242
- */
1243
- function categories($name = 'category') {
1244
- $categories = get_categories();
1245
- echo '<div class="tnpc-checkboxes">';
1246
- foreach ($categories as $c) {
1247
- $this->checkbox($name . '_' . $c->cat_ID, esc_html($c->cat_name));
1248
- }
1249
- echo '<div style="clear: both"></div>';
1250
- }
1251
-
1252
- /**
1253
- * Creates a set of checkbox to activate the profile preferences. Every checkbox has a DIV around to
1254
- * be formatted.
1255
- */
1256
- function categories_group($name, $show_mode = false) {
1257
- $categories = get_categories();
1258
- if ($show_mode) {
1259
- $this->select($name . '_mode', array('include' => 'To be included', 'exclude' => 'To be excluded'));
1260
- }
1261
- echo '<div class="tnpc-checkboxes">';
1262
- foreach ($categories as &$c) {
1263
- $this->checkbox_group($name, $c->cat_ID, esc_html($c->cat_name));
1264
- }
1265
- echo '<div style="clear: both"></div>';
1266
- }
1267
-
1268
- /**
1269
- * Creates a set of checkboxes named $name_[preference number] (so they are
1270
- * distinct fields).
1271
- * Empty preferences are skipped.
1272
- */
1273
- function preferences($name = 'preferences') {
1274
- $lists = Newsletter::instance()->get_lists();
1275
-
1276
- echo '<div class="tnpc-checkboxes">';
1277
- foreach ($lists as $list) {
1278
- $this->checkbox2($name . '_' . $list->id, esc_html($list->name));
1279
- }
1280
- echo '<div style="clear: both"></div>';
1281
- }
1282
-
1283
- /** A list of all lists defined each one with a checkbox to select it. An array
1284
- * of ID of all checked lists is submitted.
1285
- *
1286
- * @param string $name
1287
- */
1288
- function lists($name = 'lists') {
1289
- echo '<input type="hidden" name="tnp_fields[' . esc_attr($name) . ']" value="array">';
1290
- $this->preferences_group($name);
1291
- }
1292
-
1293
- function lists_checkboxes($name = 'lists') {
1294
- $this->preferences_group($name);
1295
- }
1296
-
1297
- /**
1298
- * Creates a set of checkboxes all names $name[] and the preference number as value
1299
- * so the selected checkboxes are retrieved as an array of values ($REQUEST[$name]
1300
- * will be an array if at east one preference is checked).
1301
- */
1302
- function preferences_group($name = 'preferences') {
1303
-
1304
- $lists = Newsletter::instance()->get_lists();
1305
-
1306
- echo '<div class="tnpc-lists">';
1307
- foreach ($lists as $list) {
1308
- $this->checkbox_group($name, $list->id, '<span>' . $list->id . '</span> ' . esc_html($list->name), ['label_escape' => false]);
1309
- }
1310
- echo '<a href="https://www.thenewsletterplugin.com/documentation/newsletter-lists" target="_blank">'
1311
- . 'Click here to read more about lists.'
1312
- . '</a>';
1313
- echo '</div>';
1314
- }
1315
-
1316
- /** Creates as many selects as the active preferences with the three values
1317
- * 'any', 'yes', 'no' corresponding to the values 0, 1, 2.
1318
- */
1319
- function preferences_selects($name = 'preferences', $skip_empty = false) {
1320
- $lists = Newsletter::instance()->get_lists();
1321
-
1322
- echo '<div class="newsletter-preferences-group">';
1323
- foreach ($lists as $list) {
1324
-
1325
- echo '<div class="newsletter-preferences-item">';
1326
-
1327
- $this->select($name . '_' . $list->id, array(0 => 'Any', 1 => 'Yes', 2 => 'No'));
1328
- echo '(' . $list->id . ') ' . esc_html($list->name);
1329
-
1330
- echo '</div>';
1331
- }
1332
- echo '<div style="clear: both"></div>';
1333
- echo '<a href="https://www.thenewsletterplugin.com/plugins/newsletter/newsletter-preferences" target="_blank">Click here know more about preferences.</a> They can be configured on Subscription/Form field panel.';
1334
- echo '</div>';
1335
- }
1336
-
1337
- /**
1338
- * Creates a single select with the active preferences.
1339
- */
1340
- function preferences_select($name = 'preference', $empty_label = null) {
1341
- $lists = $this->get_list_options($empty_label);
1342
- $this->select($name, $lists);
1343
- echo ' <a href="admin.php?page=newsletter_subscription_lists" target="_blank"><i class="fas fa-edit"></i></a>';
1344
- }
1345
-
1346
- function lists_select($name = 'list', $empty_label = null) {
1347
- $lists = $this->get_list_options($empty_label);
1348
- $this->select($name, $lists);
1349
- }
1350
-
1351
- function lists_select_with_notes($name = 'list', $empty_label = null) {
1352
-
1353
- $value = $this->get_value($name);
1354
-
1355
- $lists = Newsletter::instance()->get_lists();
1356
- $options = [];
1357
- if ($empty_label) {
1358
- $options[''] = $empty_label;
1359
- }
1360
-
1361
- foreach ($lists as $list) {
1362
- $options['' . $list->id] = '(' . $list->id . ') ' . esc_html($list->name);
1363
- }
1364
-
1365
- $this->select($name, $options, null, ['onchange' => 'tnp_lists_toggle(this); return true;']);
1366
- echo '<div id="options-', esc_attr($name), '-notes" class="tnpc_lists_notes">';
1367
- foreach ($lists as $list) {
1368
- $id = $list->id;
1369
- $notes = apply_filters('newsletter_lists_notes', [], $id);
1370
-
1371
- echo '<div class="list_', $id, '" style="display: ', ($value == $id ? 'block' : 'none'), '">';
1372
- if ($list->forced) {
1373
- echo 'Enforced on subscription<br>';
1374
- }
1375
- echo implode('<br>', $notes);
1376
- echo '</div>';
1377
- }
1378
- echo '</div>';
1379
- }
1380
-
1381
- function public_lists_select($name = 'list', $empty_label = null) {
1382
- $lists = $this->get_public_list_options($empty_label);
1383
- $this->select($name, $lists);
1384
- }
1385
-
1386
- /**
1387
- * Generates an associative array with the active lists to be used in a select.
1388
- * @param string $empty_label
1389
- * @return array
1390
- */
1391
- function get_list_options($empty_label = null) {
1392
- $objs = Newsletter::instance()->get_lists();
1393
- $lists = array();
1394
- if ($empty_label) {
1395
- $lists[''] = $empty_label;
1396
- }
1397
- foreach ($objs as $list) {
1398
- $lists['' . $list->id] = '(' . $list->id . ') ' . esc_html($list->name);
1399
- }
1400
- return $lists;
1401
- }
1402
-
1403
- function get_public_list_options($empty_label = null) {
1404
- $objs = Newsletter::instance()->get_lists_public();
1405
- $lists = array();
1406
- if ($empty_label) {
1407
- $lists[''] = $empty_label;
1408
- }
1409
- foreach ($objs as $list) {
1410
- $lists['' . $list->id] = '(' . $list->id . ') ' . esc_html($list->name);
1411
- }
1412
- return $lists;
1413
- }
1414
-
1415
- function date($name) {
1416
- $this->hidden($name);
1417
- $year = date('Y', $this->data[$name]);
1418
- $day = date('j', $this->data[$name]);
1419
- $month = date('m', $this->data[$name]);
1420
- $onchange = "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";
1421
- echo '<select id="' . $name . '_month" onchange="' . esc_attr($onchange) . '">';
1422
- for ($i = 0; $i < 12; $i++) {
1423
- echo '<option value="' . $i . '"';
1424
- if ($month - 1 == $i) {
1425
- echo ' selected';
1426
- }
1427
- echo '>' . date('F', mktime(0, 0, 0, $i + 1, 1, 2000)) . '</option>';
1428
- }
1429
- echo '</select>';
1430
-
1431
- echo '<select id="' . esc_attr($name) . '_day" onchange="' . esc_attr($onchange) . '">';
1432
- for ($i = 1; $i <= 31; $i++) {
1433
- echo '<option value="' . $i . '"';
1434
- if ($day == $i) {
1435
- echo ' selected';
1436
- }
1437
- echo '>' . $i . '</option>';
1438
- }
1439
- echo '</select>';
1440
-
1441
- echo '<select id="' . esc_attr($name) . '_year" onchange="' . esc_attr($onchange) . '">';
1442
- for ($i = 2011; $i <= date('Y')+3; $i++) {
1443
- echo '<option value="' . $i . '"';
1444
- if ($year == $i) {
1445
- echo ' selected';
1446
- }
1447
- echo '>' . $i . '</option>';
1448
- }
1449
- echo '</select>';
1450
- }
1451
-
1452
- /**
1453
- * Creates a set of fields to collect a date and sends back the triplet year, month and day.
1454
- *
1455
- * @param string $name
1456
- */
1457
- function date2($name) {
1458
- $year = $this->get_value($name . '_year');
1459
- $day = $this->get_value($name . '_day');
1460
- $month = $this->get_value($name . '_month');
1461
-
1462
- echo '<select name="options[' . $name . '_month]">';
1463
- echo '<option value="">-</option>';
1464
- for ($i = 1; $i <= 12; $i++) {
1465
- echo '<option value="' . $i . '"';
1466
- if ($month == $i) {
1467
- echo ' selected';
1468
- }
1469
- echo '>' . date_i18n('F', mktime(0, 0, 0, $i, 1, 2000)) . '</option>';
1470
- }
1471
- echo '</select>';
1472
-
1473
- echo '<select name="options[' . esc_attr($name) . '_day]">';
1474
- echo '<option value="">-</option>';
1475
- for ($i = 1; $i <= 31; $i++) {
1476
- echo '<option value="' . $i . '"';
1477
- if ($day == $i) {
1478
- echo ' selected';
1479
- }
1480
- echo '>' . $i . '</option>';
1481
- }
1482
- echo '</select>';
1483
-
1484
- echo '<select name="options[' . esc_attr($name) . '_year]">';
1485
- echo '<option value="">-</option>';
1486
- for ($i = 2011; $i <= date('Y')+3; $i++) {
1487
- echo '<option value="' . $i . '"';
1488
- if ($year == $i) {
1489
- echo ' selected';
1490
- }
1491
- echo '>' . $i . '</option>';
1492
- }
1493
- echo '</select>';
1494
- }
1495
-
1496
- /**
1497
- * Date and time (hour) selector. Timestamp stored.
1498
- */
1499
- function datetime($name) {
1500
- echo '<input type="hidden" name="tnp_fields[' . esc_attr($name) . ']" value="datetime">';
1501
- $value = (int) $this->get_value($name);
1502
- if (empty($value)) {
1503
- $value = time();
1504
- }
1505
-
1506
- $time = $value + get_option('gmt_offset') * 3600;
1507
- $year = gmdate('Y', $time);
1508
- $day = gmdate('j', $time);
1509
- $month = gmdate('m', $time);
1510
- $hour = gmdate('H', $time);
1511
-
1512
- echo '<select name="' . esc_attr($name) . '_month">';
1513
- for ($i = 1; $i <= 12; $i++) {
1514
- echo '<option value="' . $i . '"';
1515
- if ($month == $i) {
1516
- echo ' selected';
1517
- }
1518
- echo '>' . date('F', mktime(0, 0, 0, $i, 1, 2000)) . '</option>';
1519
- }
1520
- echo '</select>';
1521
-
1522
- echo '<select name="' . esc_attr($name) . '_day">';
1523
- for ($i = 1; $i <= 31; $i++) {
1524
- echo '<option value="' . $i . '"';
1525
- if ($day == $i) {
1526
- echo ' selected';
1527
- }
1528
- echo '>' . $i . '</option>';
1529
- }
1530
- echo '</select>';
1531
-
1532
- $last_year = date('Y') + 2;
1533
- echo '<select name="' . esc_attr($name) . '_year">';
1534
- for ($i = 2011; $i <= $last_year; $i++) {
1535
- echo '<option value="' . $i . '"';
1536
- if ($year == $i) {
1537
- echo ' selected';
1538
- }
1539
- echo '>' . $i . '</option>';
1540
- }
1541
- echo '</select>';
1542
-
1543
- echo '<select name="' . esc_attr($name) . '_hour">';
1544
- for ($i = 0; $i <= 23; $i++) {
1545
- echo '<option value="' . $i . '"';
1546
- if ($hour == $i) {
1547
- echo ' selected';
1548
- }
1549
- echo '>' . $i . ':00</option>';
1550
- }
1551
- echo '</select>';
1552
- }
1553
-
1554
- function hours($name) {
1555
- $hours = array();
1556
- for ($i = 0; $i < 24; $i++) {
1557
- $hours['' . $i] = sprintf('%02d', $i) . ':00';
1558
- }
1559
- $this->select($name, $hours);
1560
- }
1561
-
1562
- function days($name) {
1563
- $days = array(0 => 'Every day', 1 => 'Monday', 2 => 'Tuesday', 3 => 'Wednesday', 4 => 'Thursday', 5 => 'Friday', 6 => 'Saturday', 7 => 'Sunday');
1564
- $this->select($name, $days);
1565
- }
1566
-
1567
- function init($options = array()) {
1568
- $cookie_name = 'newsletter_tab';
1569
- if (isset($options['cookie_name'])) {
1570
- $cookie_name = $options['cookie_name'];
1571
- }
1572
- echo '<script type="text/javascript">
1573
- jQuery(document).ready(function(){
1574
-
1575
- tnp_controls_init();
1576
-
1577
- jQuery("textarea.dynamic").focus(function() {
1578
- jQuery("textarea.dynamic").css("height", "50px");
1579
- jQuery(this).css("height", "400px");
1580
- });
1581
- tabs = jQuery("#tabs").tabs({
1582
- active : jQuery.cookie("' . $cookie_name . '"),
1583
- activate : function( event, ui ){
1584
- jQuery.cookie("' . $cookie_name . '", ui.newTab.index(),{expires: 1});
1585
- }
1586
- });
1587
- jQuery(".tnp-tabs").tabs({});
1588
-
1589
- });
1590
- function newsletter_media(name) {
1591
- var tnp_uploader = wp.media({
1592
- title: "Select an image",
1593
- button: {
1594
- text: "Select"
1595
- },
1596
- multiple: false
1597
- }).on("select", function() {
1598
- var media = tnp_uploader.state().get("selection").first();
1599
- document.getElementById(name + "_id").value = media.id;
1600
- jQuery("#" + name + "_id").trigger("change");
1601
- //alert(media.attributes.url);
1602
- if (media.attributes.url.substring(0, 0) == "/") {
1603
- media.attributes.url = "' . site_url('/') . '" + media.attributes.url;
1604
- }
1605
- document.getElementById(name + "_url").value = media.attributes.url;
1606
-
1607
- var img_url = media.attributes.url;
1608
- if (typeof media.attributes.sizes.medium !== "undefined") img_url = media.attributes.sizes.medium.url;
1609
- if (img_url.substring(0, 0) == "/") {
1610
- img_url = "' . site_url('/') . '" + img_url;
1611
- }
1612
- document.getElementById(name + "_img").src = img_url;
1613
- }).open();
1614
- }
1615
- function newsletter_media_remove(name) {
1616
- if (confirm("Are you sure?")) {
1617
- document.getElementById(name + "_id").value = "";
1618
- document.getElementById(name + "_url").value = "";
1619
- document.getElementById(name + "_img").src = "' . plugins_url('newsletter') . '/images/nomedia.png";
1620
- }
1621
- }
1622
- function newsletter_textarea_preview(id, header, footer) {
1623
- var d = document.getElementById(id + "-iframe").contentWindow.document;
1624
- d.open();
1625
- if (templateEditor) {
1626
- d.write(templateEditor.getValue());
1627
- } else {
1628
- d.write(header + document.getElementById(id).value + footer);
1629
- }
1630
- d.close();
1631
-
1632
- var d = document.getElementById(id + "-iframe-phone").contentWindow.document;
1633
- d.open();
1634
- if (templateEditor) {
1635
- d.write(templateEditor.getValue());
1636
- } else {
1637
- d.write(header + document.getElementById(id).value + footer);
1638
- }
1639
- d.close();
1640
- //jQuery("#" + id + "-iframe-phone").toggle();
1641
- jQuery("#" + id + "-preview").toggle();
1642
- }
1643
- function tnp_select_images(state) {
1644
- if (!state.id) { return state.text; }
1645
- var $state = jQuery("<span class=\"tnp-select2-option\"><img style=\"height: 20px!important; position: relative; top: 5px\" src=\"" + state.element.getAttribute("image") + "\"> " + state.text + "</span>");
1646
- return $state;
1647
- }
1648
- function tnp_select_images_selection(state) {
1649
- if (!state.id) { return state.text; }
1650
- var $state = jQuery("<span class=\"tnp-select2-option\"><img style=\"height: 20px!important; position: relative; top: 5px\" src=\"" + state.element.getAttribute("image") + "\"> " + state.text + "</span>");
1651
- return $state;
1652
- }
1653
-
1654
-
1655
- </script>
1656
- ';
1657
- echo '<input name="act" type="hidden" value=""/>';
1658
- echo '<input name="btn" type="hidden" value=""/>';
1659
- wp_nonce_field('save');
1660
- }
1661
-
1662
- function log_level($name = 'log_level') {
1663
- $this->select($name, array(0 => 'None', 2 => 'Error', 3 => 'Normal', 4 => 'Debug'));
1664
- }
1665
-
1666
- function update_option($name, $data = null) {
1667
- if ($data == null) {
1668
- $data = $this->data;
1669
- }
1670
- update_option($name, $data);
1671
- if (isset($data['log_level'])) {
1672
- update_option($name . '_log_level', $data['log_level']);
1673
- }
1674
- }
1675
-
1676
- function js_redirect($url) {
1677
- echo '<script>';
1678
- echo 'location.href="' . $url . '"';
1679
- echo '</script>';
1680
- die();
1681
- }
1682
-
1683
- /**
1684
- * @deprecated
1685
- */
1686
- function get_test_subscribers() {
1687
- return NewsletterUsers::instance()->get_test_users();
1688
- }
1689
-
1690
- /**
1691
- * Attributes:
1692
- * weight: [true|false]
1693
- * color: [true|false]
1694
- *
1695
- * @param string $name
1696
- * @param array $attrs
1697
- */
1698
- function css_font($name = 'font', $attrs = array()) {
1699
- $default = [
1700
- 'color' => true,
1701
- 'weight' => true,
1702
- 'hide_size' => false,
1703
- 'hide_weight' => false,
1704
- 'hide_color' => false,
1705
- ];
1706
- $attrs = array_merge($default, $attrs);
1707
- $this->css_font_family($name . '_family', !empty($attrs['family_default']));
1708
- if (!$attrs['hide_size']) {
1709
- $this->css_font_size($name . '_size', !empty($attrs['size_default']));
1710
- }
1711
- if ($attrs['weight'] && !$attrs['hide_weight']) {
1712
- $this->css_font_weight($name . '_weight', !empty($attrs['weight_default']));
1713
- }
1714
- if ($attrs['color'] && !$attrs['hide_color']) {
1715
- $this->color($name . '_color');
1716
- }
1717
- }
1718
-
1719
- function css_font_size($name = 'font_size', $show_empty_option = false) {
1720
- $value = $this->get_value($name);
1721
-
1722
- echo '<select class="tnpf-font-size" id="options-', esc_attr($name), '" name="options[', esc_attr($name), ']">';
1723
- if ($show_empty_option) {
1724
- echo "<option value=''>-</option>";
1725
- }
1726
- for ($i = 8; $i <= 50; $i++) {
1727
- echo '<option value="' . $i . '"';
1728
- if ($value == $i) {
1729
- echo ' selected';
1730
- }
1731
- echo '>' . $i . '</option>';
1732
- }
1733
- echo '</select>';
1734
- }
1735
-
1736
- function css_font_weight($name = 'font_weight', $show_empty_option = false) {
1737
- $value = $this->get_value($name);
1738
-
1739
- $fonts = array('normal' => 'Normal', 'bold' => 'Bold');
1740
-
1741
- echo '<select class="tnpf-font-weight" id="options-' . esc_attr($name) . '" name="options[' . esc_attr($name) . ']">';
1742
- if ($show_empty_option) {
1743
- echo "<option value=''>-</option>";
1744
- }
1745
- foreach ($fonts as $key => $font) {
1746
- echo '<option value="', esc_attr($key), '"';
1747
- if ($value == $key) {
1748
- echo ' selected';
1749
- }
1750
- echo '>', esc_html($font), '</option>';
1751
- }
1752
- echo '</select>';
1753
- }
1754
-
1755
- function css_font_family($name = 'font_family', $show_empty_option = false) {
1756
- $value = $this->get_value($name);
1757
-
1758
- $fonts = [];
1759
- if ($show_empty_option) {
1760
- $fonts[''] = 'Default';
1761
- }
1762
-
1763
- $fonts = array_merge($fonts, ['Helvetica, Arial, sans-serif' => 'Helvetica, Arial',
1764
- 'Arial Black, Gadget, sans-serif' => 'Arial Black, Gadget',
1765
- 'Garamond, serif' => 'Garamond',
1766
- 'Courier, monospace' => 'Courier',
1767
- 'Comic Sans MS, cursive' => 'Comic Sans MS',
1768
- 'Impact, Charcoal, sans-serif' => 'Impact, Charcoal',
1769
- 'Tahoma, Geneva, sans-serif' => 'Tahoma, Geneva',
1770
- 'Times New Roman, Times, serif' => 'Times New Roman',
1771
- 'Verdana, Geneva, sans-serif' => 'Verdana, Geneva']);
1772
-
1773
- echo '<select class="tnpf-font-family" id="options-', esc_attr($name), '" name="options[', esc_attr($name), ']">';
1774
- foreach ($fonts as $font => $label) {
1775
- echo '<option value="', esc_attr($font), '"';
1776
- if ($value == $font) {
1777
- echo ' selected';
1778
- }
1779
- echo '>', esc_html($label), '</option>';
1780
- }
1781
- echo '</select>';
1782
- }
1783
-
1784
- function css_text_align($name) {
1785
- $options = array('left' => __('Left', 'newsletter'), 'right' => __('Right', 'newsletter'),
1786
- 'center' => __('Center', 'newsletter'));
1787
- $this->select($name, $options);
1788
- }
1789
-
1790
- function css_border($name) {
1791
- $value = $this->get_value($name . '_width');
1792
-
1793
- echo 'width&nbsp;<select id="options-' . esc_attr($name) . '-width" name="options[' . esc_attr($name) . '_width]">';
1794
- for ($i = 0; $i < 10; $i++) {
1795
- echo '<option value="' . $i . '"';
1796
- if ($value == $i) {
1797
- echo ' selected';
1798
- }
1799
- echo '>' . $i . '</option>';
1800
- }
1801
- echo '</select>&nbsp;px&nbsp;&nbsp;';
1802
-
1803
- $this->select($name . '_type', array('solid' => 'Solid', 'dashed' => 'Dashed'));
1804
-
1805
- $this->color($name . '_color');
1806
-
1807
- $value = $this->get_value($name . '_radius');
1808
-
1809
- echo '&nbsp;&nbsp;radius&nbsp;<select id="options-' . esc_attr($name) . '-radius" name="options[' . esc_attr($name) . '_radius]">';
1810
- for ($i = 0; $i < 10; $i++) {
1811
- echo '<option value="' . $i . '"';
1812
- if ($value == $i) {
1813
- echo ' selected';
1814
- }
1815
- echo '>' . $i . '</option>';
1816
- }
1817
- echo '</select>&nbsp;px';
1818
- }
1819
-
1820
- /**
1821
- * Media selector using the media library of WP. Produces a field which values is an array containing 'id' and 'url'.
1822
- *
1823
- * @param string $name
1824
- */
1825
- function media($name) {
1826
- if (isset($this->data[$name]['id'])) {
1827
- $media_id = (int) $this->data[$name]['id'];
1828
- $media = wp_get_attachment_image_src($media_id, 'medium');
1829
- $media_full = wp_get_attachment_image_src($media_id, 'full');
1830
- } else {
1831
- $media = false;
1832
- }
1833
- echo '<div style="position: relative">';
1834
- echo '<a style="position: absolute; top: 5px; right: 5px; background-color: none; color: #000; padding: 0px 5px 6px 5px; font-size: 24px; display: block; text-decoration: none" href="#" onclick="newsletter_media_remove(\'' . esc_attr($name) . '\'); return false">&times;</a>';
1835
- if ($media === false) {
1836
- $media = array('', '', '');
1837
- $media_full = array('', '', '');
1838
- $media_id = 0;
1839
- echo '<img style="max-width: 200px; max-height: 150px; width: 100px;" id="' . esc_attr($name) . '_img" src="' . plugins_url('newsletter') . '/images/nomedia.png" onclick="newsletter_media(\'' . esc_attr($name) . '\')">';
1840
- } else {
1841
- echo '<img style="max-width: 200px; max-height: 150px;" id="' . esc_attr($name) . '_img" src="' . esc_attr($media[0]) . '" onclick="newsletter_media(\'' . esc_attr($name) . '\')">';
1842
- }
1843
-
1844
- echo '</div>';
1845
- echo '<input type="hidden" id="' . esc_attr($name) . '_id" name="options[' . esc_attr($name) . '][id]" value="' . esc_attr($media_id) . '" size="5">';
1846
- echo '<input type="hidden" id="' . esc_attr($name) . '_url" name="options[' . esc_attr($name) . '][url]" value="' . esc_attr($media_full[0]) . '" size="50">';
1847
- }
1848
-
1849
- function media_input($option, $name, $label) {
1850
-
1851
- if (!empty($label)) {
1852
- $output = '<label class="select" for="tnp_' . esc_attr($name) . '">' . esc_html($label) . ':</label>';
1853
- }
1854
- $output .= '<input id="tnp_' . esc_attr($name) . '" type="text" size="36" name="' . esc_attr($option) . '[' . esc_attr($name) . ']" value="' . esc_attr($val) . '" />';
1855
- $output .= '<input id="tnp_' . esc_attr($name) . '_button" class="button-primary" type="button" value="Select Image" />';
1856
- $output .= '<br class="clear"/>';
1857
-
1858
- echo $output;
1859
- }
1860
-
1861
- function language($name = 'language', $empty_label = 'All') {
1862
- if (!class_exists('SitePress') && !function_exists('pll_default_language') && !class_exists('TRP_Translate_Press')) {
1863
- echo __('Install a multilanguage plugin.', 'newsletter');
1864
- echo ' <a href="https://www.thenewsletterplugin.com/documentation/multilanguage" target="_blank">', __('Read more', 'newsletter'), '</a>';
1865
- return;
1866
- }
1867
-
1868
- $languages = Newsletter::instance()->get_languages();
1869
- if (!empty($empty_label)) {
1870
- $languages = array_merge(array('' => $empty_label), $languages);
1871
- }
1872
- $this->select($name, $languages);
1873
- }
1874
-
1875
- function is_multilanguage() {
1876
- return Newsletter::instance()->is_multilanguage();
1877
- }
1878
-
1879
- /**
1880
- * Creates a checkbox group with all active languages. Each checkbox is named
1881
- * $name[] and values with the relative language code.
1882
- *
1883
- * @param string $name
1884
- */
1885
- function languages($name = 'languages') {
1886
- if (!$this->is_multilanguage()) {
1887
- echo __('Install WPML or Polylang for multilanguage support', 'newsletter');
1888
- return;
1889
- }
1890
-
1891
- $language_options = Newsletter::instance()->get_languages();
1892
-
1893
- if (empty($language_options)) {
1894
- echo __('Your multilanguage plugin is not supported or there are no languages defined', 'newsletter');
1895
- return;
1896
- }
1897
-
1898
- $this->checkboxes_group($name, $language_options);
1899
- }
1900
-
1901
- /**
1902
- * Prints a formatted date using the formats and timezone of WP, including the current date and time and the
1903
- * time left to the passed time.
1904
- *
1905
- * @param int $time
1906
- * @param int $now
1907
- * @param bool $left
1908
- * @return string
1909
- */
1910
- static function print_date($time = null, $now = false, $left = false) {
1911
- if (is_null($time)) {
1912
- $time = time();
1913
- }
1914
- if ($time == false) {
1915
- $buffer = 'none';
1916
- } else {
1917
- $buffer = date_i18n(get_option('date_format') . ' ' . get_option('time_format'), $time + get_option('gmt_offset') * 3600);
1918
-
1919
- if ($now) {
1920
- $buffer .= ' (now: ' . gmdate(get_option('date_format') . ' ' .
1921
- get_option('time_format'), time() + get_option('gmt_offset') * 3600);
1922
- $buffer .= ')';
1923
- }
1924
- if ($left) {
1925
- if ($time - time() < 0) {
1926
- $buffer .= ', ' . (time() - $time) . ' seconds late';
1927
- } else {
1928
- $buffer .= ', ' . gmdate('H:i:s', $time - time()) . ' left';
1929
- }
1930
- }
1931
- }
1932
- return $buffer;
1933
- }
1934
-
1935
- static function delta_time($delta = 0) {
1936
- $seconds = $delta % 60;
1937
- $minutes = floor(($delta / 60) % 60);
1938
- $hours = floor(($delta / (60 * 60)) % 24);
1939
- $days = floor($delta / (24 * 60 * 60));
1940
-
1941
- return $days . ' day(s), ' . $hours . ' hour(s), ' . $minutes . ' minute(s)';
1942
- }
1943
-
1944
- /**
1945
- * Prints the help button near a form field. The label is used as icon title.
1946
- *
1947
- * @param string $url
1948
- * @param string $label
1949
- */
1950
- static function help($url, $label = '') {
1951
- echo '<a href="', $url, '" target="_blank" title="', esc_attr($label), '"><i class="fas fa-question-circle"></i></a>';
1952
- }
1953
-
1954
- static function idea($url, $label = '') {
1955
- echo '<a href="', $url, '" target="_blank" title="', esc_attr($label), '"><i class="fas fa-lightbulb-o"></i></a>';
1956
- }
1957
-
1958
- static function field_help($url, $text = '') {
1959
- if (strpos($url, 'http') !== 0) {
1960
- $url = 'https://www.thenewsletterplugin.com' . $url;
1961
- }
1962
- echo '<a href="', $url, '" target="_blank" style="text-decoration: none" title="' . esc_attr(__('Read more', 'newsletter')) . '"><i class="fas fa-question-circle"></i>';
1963
- if ($text)
1964
- echo '&nbsp;', $text;
1965
- echo '</a>';
1966
- }
1967
-
1968
- static function field_label($label, $help_url = false) {
1969
- echo $label;
1970
- if ($help_url) {
1971
- echo '&nbsp';
1972
- self::field_help($help_url);
1973
- }
1974
- }
1975
-
1976
- /**
1977
- * Prints a panel link to the documentation.
1978
- *
1979
- * @param type $url
1980
- * @param type $text
1981
- */
1982
- static function panel_help($url, $text = '') {
1983
- if (empty($text))
1984
- $text = __('Need help?', 'newsletter');
1985
- echo '<span class="tnp-panel-help"><a href="', $url, '" target="_blank">', $text, '</a></span>';
1986
- }
1987
-
1988
- /**
1989
- * Prints an administration page link to the documentation (just under the administration page title.
1990
- * @param type $url
1991
- * @param type $text
1992
- */
1993
- static function page_help($url, $text = '') {
1994
- if (empty($text))
1995
- $text = __('Need help?', 'newsletter');
1996
- echo '<div class="tnp-page-help"><a href="', $url, '" target="_blank">', $text, '</a></div>';
1997
- }
1998
-
1999
- static function print_truncated($text, $size = 50) {
2000
- if (mb_strlen($text) < $size)
2001
- return esc_html($text);
2002
- $sub = mb_substr($text, 0, $size);
2003
- echo '<span title="', esc_attr($text), '">', esc_html($sub), '...</span>';
2004
- }
2005
-
2006
- function block_background($name = 'block_background') {
2007
- $this->color($name);
2008
- }
2009
-
2010
- function block_padding($name = 'block_padding', $options = array()) {
2011
- echo '<div style="text-align: center; width: 250px;">';
2012
- $this->text($name . '_top', 5);
2013
- echo '<br>';
2014
- $this->text($name . '_left', 5);
2015
- echo "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
2016
- $this->text($name . '_right', 5);
2017
- echo '<br>';
2018
- $this->text($name . '_bottom', 5);
2019
- echo '</div>';
2020
- }
2021
-
2022
- /**
2023
- * Adds the fields used by the composer (version 1) in the page form.
2024
- *
2025
- * @param type $name
2026
- */
2027
- function composer_fields($name = 'body') {
2028
-
2029
- // body
2030
- $value = $this->get_value($name);
2031
-
2032
- // Extracts only the body part
2033
- $x = strpos($value, '<body');
2034
- if ($x) {
2035
- $x = strpos($value, '>', $x);
2036
- $y = strpos($value, '</body>');
2037
- $value = substr($value, $x + 1, $y - $x - 1);
2038
- }
2039
-
2040
- /* Cleans up uncorrectly stored newsletter bodies */
2041
- $value = preg_replace('/<style\s+.*?>.*?<\\/style>/is', '', $value);
2042
- $value = preg_replace('/<meta.*?>/', '', $value);
2043
- $value = preg_replace('/<title\s+.*?>.*?<\\/title>/i', '', $value);
2044
- $value = trim($value);
2045
-
2046
- // Required since esc_html DOES NOT escape the HTML entities (apparently)
2047
- $value = str_replace('&', '&amp;', $value);
2048
- $value = str_replace('"', '&quot;', $value);
2049
- $value = str_replace('<', '&lt;', $value);
2050
- $value = str_replace('>', '&gt;', $value);
2051
- echo '<input type="hidden" name="options[', esc_attr($name), ']" id="options-', esc_attr($name), '" value="', esc_attr($value), '">';
2052
-
2053
- // Used by composer to rebuild the full HTML
2054
- $css = NewsletterEmails::instance()->get_composer_css();
2055
- echo '<input type="hidden" name="options[css]" id="options-css" value="', esc_attr($css), '">';
2056
-
2057
- // subject
2058
- $value = $this->get_value('subject');
2059
- echo '<input type="hidden" name="options[subject]" id="options-subject" value="', esc_attr($value), '">';
2060
- }
2061
-
2062
- function composer_load($name = 'body', $show_subject = false, $show_test = true, $context_type = '') {
2063
-
2064
- global $controls;
2065
- global $tnpc_show_subject;
2066
- $tnpc_show_subject = $show_subject;
2067
-
2068
- wp_enqueue_style('tnpc-style', plugins_url('newsletter') . '/emails/tnp-composer/_css/newsletter-builder.css', array(), time());
2069
-
2070
- include NEWSLETTER_DIR . '/emails/tnp-composer/index.php';
2071
- }
2072
-
2073
- /**
2074
- * Adds the fields used by the composer (version 2) in the page form.
2075
- */
2076
- function composer_fields_v2($name = 'message') {
2077
-
2078
- // The composer, on saving, fills in those fields
2079
- $this->hidden('subject');
2080
- $this->hidden('message');
2081
- $this->hidden('options_preheader');
2082
- $this->hidden('updated');
2083
-
2084
- //$preheader_value = $this->get_value('options_preheader');
2085
- // echo '<input name="options[preheader]" id="options-preheader" type="hidden" value="', esc_attr($preheader_value), '">';
2086
- }
2087
-
2088
- function composer_load_v2($show_subject = false, $show_test = true, $context_type = '') {
2089
-
2090
- global $tnpc_show_subject;
2091
- $tnpc_show_subject = $show_subject;
2092
-
2093
- echo "<link href='", plugins_url('newsletter'), "/emails/tnp-composer/_css/newsletter-builder-v2.css?ver=" . NEWSLETTER_VERSION . "' rel='stylesheet' type='text/css'>";
2094
-
2095
- $controls = $this;
2096
- include NEWSLETTER_DIR . '/emails/tnp-composer/index-v2.php';
2097
- }
2098
-
2099
- function subject($name) {
2100
- $value = $this->get_value($name);
2101
- // Leave the ID with this prefix!
2102
- echo '<div style="position: relative"><input size="80" id="options-subject-', esc_attr($name), '" name="options[' . esc_attr($name) . ']" type="text" placeholder="" value="';
2103
- echo esc_attr($value);
2104
- echo '">';
2105
- echo '&nbsp;<i class="far fa-lightbulb tnp-suggest-subject" data-tnp-modal-target="#subject-ideas-modal"></i>';
2106
-
2107
- echo '<img src="', plugins_url('newsletter'), '/admin/images/subject/android.png" style="position: absolute; width: 16px; left: 330px; top: 25px; display: block; opacity: 0">';
2108
- echo '<img src="', plugins_url('newsletter'), '/admin/images/subject/iphone.png" style="position: absolute; width: 16px; left: 380px; top: 25px; display: block; opacity: 0">';
2109
- echo '</div>';
2110
- }
2111
-
2112
- }
1
+ <?php
2
+
3
+ defined('ABSPATH') || exit;
4
+
5
+ include_once __DIR__ . '/fields.php';
6
+
7
+ class NewsletterControls {
8
+
9
+ var $data = [];
10
+ var $action = false;
11
+ var $button_data = '';
12
+ var $errors = '';
13
+
14
+ /**
15
+ * @var string
16
+ */
17
+ var $messages = '';
18
+
19
+ /**
20
+ * @var array
21
+ */
22
+ var $warnings = array();
23
+ var $countries = array(
24
+ 'AF' => 'Afghanistan',
25
+ 'AX' => 'Aland Islands',
26
+ 'AL' => 'Albania',
27
+ 'DZ' => 'Algeria',
28
+ 'AS' => 'American Samoa',
29
+ 'AD' => 'Andorra',
30
+ 'AO' => 'Angola',
31
+ 'AI' => 'Anguilla',
32
+ 'AQ' => 'Antarctica',
33
+ 'AG' => 'Antigua And Barbuda',
34
+ 'AR' => 'Argentina',
35
+ 'AM' => 'Armenia',
36
+ 'AW' => 'Aruba',
37
+ 'AU' => 'Australia',
38
+ 'AT' => 'Austria',
39
+ 'AZ' => 'Azerbaijan',
40
+ 'BS' => 'Bahamas',
41
+ 'BH' => 'Bahrain',
42
+ 'BD' => 'Bangladesh',
43
+ 'BB' => 'Barbados',
44
+ 'BY' => 'Belarus',
45
+ 'BE' => 'Belgium',
46
+ 'BZ' => 'Belize',
47
+ 'BJ' => 'Benin',
48
+ 'BM' => 'Bermuda',
49
+ 'BT' => 'Bhutan',
50
+ 'BO' => 'Bolivia',
51
+ 'BA' => 'Bosnia And Herzegovina',
52
+ 'BW' => 'Botswana',
53
+ 'BV' => 'Bouvet Island',
54
+ 'BR' => 'Brazil',
55
+ 'IO' => 'British Indian Ocean Territory',
56
+ 'BN' => 'Brunei Darussalam',
57
+ 'BG' => 'Bulgaria',
58
+ 'BF' => 'Burkina Faso',
59
+ 'BI' => 'Burundi',
60
+ 'KH' => 'Cambodia',
61
+ 'CM' => 'Cameroon',
62
+ 'CA' => 'Canada',
63
+ 'CV' => 'Cape Verde',
64
+ 'KY' => 'Cayman Islands',
65
+ 'CF' => 'Central African Republic',
66
+ 'TD' => 'Chad',
67
+ 'CL' => 'Chile',
68
+ 'CN' => 'China',
69
+ 'CX' => 'Christmas Island',
70
+ 'CC' => 'Cocos (Keeling) Islands',
71
+ 'CO' => 'Colombia',
72
+ 'KM' => 'Comoros',
73
+ 'CG' => 'Congo',
74
+ 'CD' => 'Congo, Democratic Republic',
75
+ 'CK' => 'Cook Islands',
76
+ 'CR' => 'Costa Rica',
77
+ 'CI' => 'Cote D\'Ivoire',
78
+ 'HR' => 'Croatia',
79
+ 'CU' => 'Cuba',
80
+ 'CY' => 'Cyprus',
81
+ 'CZ' => 'Czech Republic',
82
+ 'DK' => 'Denmark',
83
+ 'DJ' => 'Djibouti',
84
+ 'DM' => 'Dominica',
85
+ 'DO' => 'Dominican Republic',
86
+ 'EC' => 'Ecuador',
87
+ 'EG' => 'Egypt',
88
+ 'SV' => 'El Salvador',
89
+ 'GQ' => 'Equatorial Guinea',
90
+ 'ER' => 'Eritrea',
91
+ 'EE' => 'Estonia',
92
+ 'ET' => 'Ethiopia',
93
+ 'FK' => 'Falkland Islands (Malvinas)',
94
+ 'FO' => 'Faroe Islands',
95
+ 'FJ' => 'Fiji',
96
+ 'FI' => 'Finland',
97
+ 'FR' => 'France',
98
+ 'GF' => 'French Guiana',
99
+ 'PF' => 'French Polynesia',
100
+ 'TF' => 'French Southern Territories',
101
+ 'GA' => 'Gabon',
102
+ 'GM' => 'Gambia',
103
+ 'GE' => 'Georgia',
104
+ 'DE' => 'Germany',
105
+ 'GH' => 'Ghana',
106
+ 'GI' => 'Gibraltar',
107
+ 'GR' => 'Greece',
108
+ 'GL' => 'Greenland',
109
+ 'GD' => 'Grenada',
110
+ 'GP' => 'Guadeloupe',
111
+ 'GU' => 'Guam',
112
+ 'GT' => 'Guatemala',
113
+ 'GG' => 'Guernsey',
114
+ 'GN' => 'Guinea',
115
+ 'GW' => 'Guinea-Bissau',
116
+ 'GY' => 'Guyana',
117
+ 'HT' => 'Haiti',
118
+ 'HM' => 'Heard Island & Mcdonald Islands',
119
+ 'VA' => 'Holy See (Vatican City State)',
120
+ 'HN' => 'Honduras',
121
+ 'HK' => 'Hong Kong',
122
+ 'HU' => 'Hungary',
123
+ 'IS' => 'Iceland',
124
+ 'IN' => 'India',
125
+ 'ID' => 'Indonesia',
126
+ 'IR' => 'Iran, Islamic Republic Of',
127
+ 'IQ' => 'Iraq',
128
+ 'IE' => 'Ireland',
129
+ 'IM' => 'Isle Of Man',
130
+ 'IL' => 'Israel',
131
+ 'IT' => 'Italy',
132
+ 'JM' => 'Jamaica',
133
+ 'JP' => 'Japan',
134
+ 'JE' => 'Jersey',
135
+ 'JO' => 'Jordan',
136
+ 'KZ' => 'Kazakhstan',
137
+ 'KE' => 'Kenya',
138
+ 'KI' => 'Kiribati',
139
+ 'KR' => 'Korea',
140
+ 'KW' => 'Kuwait',
141
+ 'KG' => 'Kyrgyzstan',
142
+ 'LA' => 'Lao People\'s Democratic Republic',
143
+ 'LV' => 'Latvia',
144
+ 'LB' => 'Lebanon',
145
+ 'LS' => 'Lesotho',
146
+ 'LR' => 'Liberia',
147
+ 'LY' => 'Libyan Arab Jamahiriya',
148
+ 'LI' => 'Liechtenstein',
149
+ 'LT' => 'Lithuania',
150
+ 'LU' => 'Luxembourg',
151
+ 'MO' => 'Macao',
152
+ 'MK' => 'Macedonia',
153
+ 'MG' => 'Madagascar',
154
+ 'MW' => 'Malawi',
155
+ 'MY' => 'Malaysia',
156
+ 'MV' => 'Maldives',
157
+ 'ML' => 'Mali',
158
+ 'MT' => 'Malta',
159
+ 'MH' => 'Marshall Islands',
160
+ 'MQ' => 'Martinique',
161
+ 'MR' => 'Mauritania',
162
+ 'MU' => 'Mauritius',
163
+ 'YT' => 'Mayotte',
164
+ 'MX' => 'Mexico',
165
+ 'FM' => 'Micronesia, Federated States Of',
166
+ 'MD' => 'Moldova',
167
+ 'MC' => 'Monaco',
168
+ 'MN' => 'Mongolia',
169
+ 'ME' => 'Montenegro',
170
+ 'MS' => 'Montserrat',
171
+ 'MA' => 'Morocco',
172
+ 'MZ' => 'Mozambique',
173
+ 'MM' => 'Myanmar',
174
+ 'NA' => 'Namibia',
175
+ 'NR' => 'Nauru',
176
+ 'NP' => 'Nepal',
177
+ 'NL' => 'Netherlands',
178
+ 'AN' => 'Netherlands Antilles',
179
+ 'NC' => 'New Caledonia',
180
+ 'NZ' => 'New Zealand',
181
+ 'NI' => 'Nicaragua',
182
+ 'NE' => 'Niger',
183
+ 'NG' => 'Nigeria',
184
+ 'NU' => 'Niue',
185
+ 'NF' => 'Norfolk Island',
186
+ 'MP' => 'Northern Mariana Islands',
187
+ 'NO' => 'Norway',
188
+ 'OM' => 'Oman',
189
+ 'PK' => 'Pakistan',
190
+ 'PW' => 'Palau',
191
+ 'PS' => 'Palestinian Territory, Occupied',
192
+ 'PA' => 'Panama',
193
+ 'PG' => 'Papua New Guinea',
194
+ 'PY' => 'Paraguay',
195
+ 'PE' => 'Peru',
196
+ 'PH' => 'Philippines',
197
+ 'PN' => 'Pitcairn',
198
+ 'PL' => 'Poland',
199
+ 'PT' => 'Portugal',
200
+ 'PR' => 'Puerto Rico',
201
+ 'QA' => 'Qatar',
202
+ 'RE' => 'Reunion',
203
+ 'RO' => 'Romania',
204
+ 'RU' => 'Russian Federation',
205
+ 'RW' => 'Rwanda',
206
+ 'BL' => 'Saint Barthelemy',
207
+ 'SH' => 'Saint Helena',
208
+ 'KN' => 'Saint Kitts And Nevis',
209
+ 'LC' => 'Saint Lucia',
210
+ 'MF' => 'Saint Martin',
211
+ 'PM' => 'Saint Pierre And Miquelon',
212
+ 'VC' => 'Saint Vincent And Grenadines',
213
+ 'WS' => 'Samoa',
214
+ 'SM' => 'San Marino',
215
+ 'ST' => 'Sao Tome And Principe',
216
+ 'SA' => 'Saudi Arabia',
217
+ 'SN' => 'Senegal',
218
+ 'RS' => 'Serbia',
219
+ 'SC' => 'Seychelles',
220
+ 'SL' => 'Sierra Leone',
221
+ 'SG' => 'Singapore',
222
+ 'SK' => 'Slovakia',
223
+ 'SI' => 'Slovenia',
224
+ 'SB' => 'Solomon Islands',
225
+ 'SO' => 'Somalia',
226
+ 'ZA' => 'South Africa',
227
+ 'GS' => 'South Georgia And Sandwich Isl.',
228
+ 'ES' => 'Spain',
229
+ 'LK' => 'Sri Lanka',
230
+ 'SD' => 'Sudan',
231
+ 'SR' => 'Suriname',
232
+ 'SJ' => 'Svalbard And Jan Mayen',
233
+ 'SZ' => 'Swaziland',
234
+ 'SE' => 'Sweden',
235
+ 'CH' => 'Switzerland',
236
+ 'SY' => 'Syrian Arab Republic',
237
+ 'TW' => 'Taiwan',
238
+ 'TJ' => 'Tajikistan',
239
+ 'TZ' => 'Tanzania',
240
+ 'TH' => 'Thailand',
241
+ 'TL' => 'Timor-Leste',
242
+ 'TG' => 'Togo',
243
+ 'TK' => 'Tokelau',
244
+ 'TO' => 'Tonga',
245
+ 'TT' => 'Trinidad And Tobago',
246
+ 'TN' => 'Tunisia',
247
+ 'TR' => 'Turkey',
248
+ 'TM' => 'Turkmenistan',
249
+ 'TC' => 'Turks And Caicos Islands',
250
+ 'TV' => 'Tuvalu',
251
+ 'UG' => 'Uganda',
252
+ 'UA' => 'Ukraine',
253
+ 'AE' => 'United Arab Emirates',
254
+ 'GB' => 'United Kingdom',
255
+ 'US' => 'United States',
256
+ 'UM' => 'United States Outlying Islands',
257
+ 'UY' => 'Uruguay',
258
+ 'UZ' => 'Uzbekistan',
259
+ 'VU' => 'Vanuatu',
260
+ 'VE' => 'Venezuela',
261
+ 'VN' => 'Viet Nam',
262
+ 'VG' => 'Virgin Islands, British',
263
+ 'VI' => 'Virgin Islands, U.S.',
264
+ 'WF' => 'Wallis And Futuna',
265
+ 'EH' => 'Western Sahara',
266
+ 'YE' => 'Yemen',
267
+ 'ZM' => 'Zambia',
268
+ 'ZW' => 'Zimbabwe',
269
+ 'XX' => 'Undefined',
270
+ 'CW' => 'Curaçao',
271
+ 'SS' => 'South Sudan',
272
+ 'EU' => 'Europe (generic)',
273
+ 'A1' => 'Anonymous IP',
274
+ 'A2' => 'Satellite IP'
275
+ );
276
+
277
+ /**
278
+ *
279
+ * @param array $options
280
+ */
281
+ function __construct($options = null) {
282
+ if ($options === null) {
283
+ if (isset($_POST['options'])) {
284
+ $this->data = stripslashes_deep($_POST['options']);
285
+ }
286
+ } else {
287
+ $this->data = (array) $options;
288
+ }
289
+
290
+ if (isset($_REQUEST['act'])) {
291
+ $this->action = $_REQUEST['act'];
292
+ }
293
+
294
+ if (isset($_REQUEST['btn'])) {
295
+ $this->button_data = $_REQUEST['btn'];
296
+ }
297
+ // Fields analysis
298
+ if (isset($_REQUEST['tnp_fields'])) {
299
+ $fields = $_REQUEST['tnp_fields'];
300
+ if (is_array($fields)) {
301
+ foreach ($fields as $name => $type) {
302
+ if ($type == 'datetime') {
303
+ // Ex. The user insert 01/07/2012 14:30 and it set the time zone to +2. We cannot use the
304
+ // mktime, since it uses the time zone of the machine. We create the time as if we are on
305
+ // GMT 0 and then we subtract the GMT offset (the example date and time on GMT+2 happens
306
+ // "before").
307
+
308
+ $time = gmmktime((int) $_REQUEST[$name . '_hour'], 0, 0, (int) $_REQUEST[$name . '_month'], (int) $_REQUEST[$name . '_day'], (int) $_REQUEST[$name . '_year']);
309
+ $time -= get_option('gmt_offset') * 3600;
310
+ $this->data[$name] = $time;
311
+ continue;
312
+ }
313
+ if ($type === 'array') {
314
+ if (!isset($this->data[$name]))
315
+ $this->data[$name] = [];
316
+ }
317
+ if ($type === 'checkbox') {
318
+ if (!isset($this->data[$name])) {
319
+ $this->data[$name] = 0;
320
+ }
321
+ }
322
+ }
323
+ }
324
+ }
325
+ }
326
+
327
+ function set_data($data) {
328
+ if (is_array($data)) {
329
+ $this->data = $data;
330
+ } else if (is_object($data)) {
331
+ $this->data = (array) $data;
332
+ } else {
333
+ $this->data = [];
334
+ }
335
+ }
336
+
337
+ function merge($options) {
338
+ if (!is_array($options))
339
+ return;
340
+ if ($this->data == null)
341
+ $this->data = array();
342
+ $this->data = array_merge($this->data, $options);
343
+ }
344
+
345
+ function merge_defaults($defaults) {
346
+ if ($this->data == null)
347
+ $this->data = $defaults;
348
+ else
349
+ $this->data = array_merge($defaults, $this->data);
350
+ }
351
+
352
+ /**
353
+ * Return true is there in an asked action is no action name is specified or
354
+ * true is the requested action matches the passed action.
355
+ * Dies if it is not a safe call.
356
+ */
357
+ function is_action($action = null) {
358
+ if ($action == null)
359
+ return $this->action != null;
360
+ if ($this->action == null)
361
+ return false;
362
+ if ($this->action != $action)
363
+ return false;
364
+ if (check_admin_referer('save'))
365
+ return true;
366
+ die('Invalid call');
367
+ }
368
+
369
+ function get_value($name, $def = null) {
370
+ if (!isset($this->data[$name])) {
371
+ return $def;
372
+ }
373
+ return $this->data[$name];
374
+ }
375
+
376
+ function get_value_array($name) {
377
+ if (!isset($this->data[$name]) || !is_array($this->data[$name]))
378
+ return array();
379
+ return $this->data[$name];
380
+ }
381
+
382
+ function show_error($text) {
383
+ echo '<div class="tnp-error">', $text, '</div>';
384
+ }
385
+
386
+ function show_warning($text) {
387
+ echo '<div class="tnp-warning">', $text, '</div>';
388
+ }
389
+
390
+ function show_message($text) {
391
+ echo '<div class="tnpc-message">', $text, '</div>';
392
+ }
393
+
394
+ /**
395
+ * Show the errors and messages.
396
+ */
397
+ function show() {
398
+ static $shown = false;
399
+
400
+ if ($shown) {
401
+ return;
402
+ }
403
+ $shown = true;
404
+
405
+ if (!empty($this->errors)) {
406
+ echo '<div class="tnpc-error">';
407
+ echo $this->errors;
408
+ echo '</div>';
409
+ }
410
+ if (!empty($this->warnings)) {
411
+ foreach ((array) $this->warnings as $warning) {
412
+ echo '<div class="tnpc-warning">';
413
+ echo $warning;
414
+ echo '</div>';
415
+ }
416
+ }
417
+ if (!empty($this->messages)) {
418
+ echo '<div class="tnpc-message">';
419
+ echo $this->messages;
420
+ echo '</div>';
421
+ }
422
+ }
423
+
424
+ function add_message($text) {
425
+ if (!empty($this->messages)) {
426
+ $this->messages .= '<br><br>';
427
+ }
428
+ $this->messages .= $text;
429
+ }
430
+
431
+ function add_message_saved() {
432
+ if (!empty($this->messages)) {
433
+ $this->messages .= '<br><br>';
434
+ }
435
+ $this->messages .= __('Saved.', 'newsletter');
436
+ }
437
+
438
+ function add_message_deleted() {
439
+ if (!empty($this->messages)) {
440
+ $this->messages .= '<br><br>';
441
+ }
442
+ $this->messages .= __('Deleted.', 'newsletter');
443
+ }
444
+
445
+ function add_message_reset() {
446
+ if (!empty($this->messages)) {
447
+ $this->messages .= '<br><br>';
448
+ }
449
+ $this->messages .= __('Options reset.', 'newsletter');
450
+ }
451
+
452
+ function add_message_done() {
453
+ if (!empty($this->messages)) {
454
+ $this->messages .= '<br><br>';
455
+ }
456
+ $this->messages .= __('Done.', 'newsletter');
457
+ }
458
+
459
+ function add_language_warning() {
460
+ $newsletter = Newsletter::instance();
461
+ $current_language = $newsletter->get_current_language();
462
+
463
+ if (!$current_language) {
464
+ return;
465
+ }
466
+ $this->warnings[] = 'You are configuring the language <strong>' . $newsletter->get_language_label($current_language) . '</strong>. Switch to "all languages" to see all options.';
467
+ }
468
+
469
+ function switch_to_all_languages_notice() {
470
+ echo '<div class="tnpc-languages-notice">';
471
+ _e('Switch the administration side to "all languages" to set these options', 'newsletter');
472
+ echo '</div>';
473
+ }
474
+
475
+ function hint($text, $url = '') {
476
+ echo '<div class="tnpc-hint">';
477
+ // Do not escape that, it can be formatted
478
+ echo $text;
479
+ if (!empty($url)) {
480
+ echo ' <a href="' . esc_attr($url) . '" target="_blank">Read more</a>.';
481
+ }
482
+ echo '</div>';
483
+ }
484
+
485
+ function yesno($name) {
486
+ $value = isset($this->data[$name]) ? (int) $this->data[$name] : 0;
487
+
488
+ echo '<select style="width: 60px" name="options[', esc_attr($name), ']">';
489
+ echo '<option value="0"';
490
+ if ($value == 0) {
491
+ echo ' selected';
492
+ }
493
+ echo '>', __('No', 'newsletter'), '</option>';
494
+ echo '<option value="1"';
495
+ if ($value == 1) {
496
+ echo ' selected';
497
+ }
498
+ echo '>', __('Yes', 'newsletter'), '</option>';
499
+ echo '</select>&nbsp;&nbsp;&nbsp;';
500
+ }
501
+
502
+ function enabled($name = 'enabled', $attrs = []) {
503
+ $value = isset($this->data[$name]) ? (int) $this->data[$name] : 0;
504
+ $name = esc_attr($name);
505
+
506
+ echo '<select style="width: 100px" name="options[', $name, ']" id="options-', $name, '"';
507
+ if (isset($attrs['bind_to'])) {
508
+ echo ' onchange="tnp_select_toggle(this, \'', $attrs['bind_to'], '\')"';
509
+ }
510
+ echo '>';
511
+ echo '<option value="0"';
512
+ if ($value == 0) {
513
+ echo ' selected';
514
+ }
515
+ echo '>', __('Disabled', 'newsletter'), '</option>';
516
+ echo '<option value="1"';
517
+ if ($value == 1) {
518
+ echo ' selected';
519
+ }
520
+ echo '>', __('Enabled', 'newsletter'), '</option>';
521
+ echo '</select>';
522
+ if (isset($attrs['bind_to'])) {
523
+ if ($value) {
524
+ echo '<script>jQuery(function ($) {$("#options-', $attrs['bind_to'], '").show()})</script>';
525
+ } else {
526
+ echo '<script>jQuery(function ($) {$("#options-', $attrs['bind_to'], '").hide()})</script>';
527
+ }
528
+ }
529
+ }
530
+
531
+ function disabled($name) {
532
+ $value = isset($this->data[$name]) ? (int) $this->data[$name] : 0;
533
+
534
+ echo '<select style="width: 100px" name="options[' . esc_attr($name) . ']">';
535
+ echo '<option value="0"';
536
+ if ($value == 0) {
537
+ echo ' selected';
538
+ }
539
+ echo '>Enabled</option>';
540
+ echo '<option value="1"';
541
+ if ($value == 1) {
542
+ echo ' selected';
543
+ }
544
+ echo '>Disabled</option>';
545
+ echo '</select>';
546
+ }
547
+
548
+ /**
549
+ * Creates a set of checkbox all named as $name with values and labels extracted from
550
+ * $values_labels. A checkbox will be checked if internal data under key $name is an array
551
+ * and contains the value of the current (echoing) checkbox.
552
+ *
553
+ * On submit it produces an array under the name $name IF at least one checkbox has
554
+ * been checked. Otherwise the key won't be present.
555
+ *
556
+ * @param array $values
557
+ * @param string $name
558
+ * @param array $values_labels
559
+ */
560
+ function checkboxes_group($name, $values_labels) {
561
+ $value_array = $this->get_value_array($name);
562
+
563
+ echo '<div class="tnpc-checkboxes">';
564
+ foreach ($values_labels as $value => $label) {
565
+ echo '<label><input type="checkbox" id="' . esc_attr($name) . '" name="options[' . esc_attr($name) . '][]" value="' . esc_attr($value) . '"';
566
+ if (array_search($value, $value_array) !== false) {
567
+ echo ' checked';
568
+ }
569
+ echo '>';
570
+ if ($label != '') {
571
+ echo '&nbsp;' . esc_html($label);
572
+ }
573
+ echo "</label>";
574
+ }
575
+ echo "<div style='clear: both'></div>";
576
+ }
577
+
578
+ /** Creates a checkbox group with all public post types.
579
+ */
580
+ function post_types($name = 'post_types') {
581
+ $list = array();
582
+ $post_types = get_post_types(array('public' => true), 'objects', 'and');
583
+ foreach ($post_types as $post_type) {
584
+ $list[$post_type->name] = $post_type->labels->name;
585
+ }
586
+
587
+ $this->checkboxes_group($name, $list);
588
+ }
589
+
590
+ function posts_select($name, $max = 20, $args = array()) {
591
+ $args = array_merge(array(
592
+ 'posts_per_page' => 5,
593
+ 'offset' => 0,
594
+ 'category' => '',
595
+ 'category_name' => '',
596
+ 'orderby' => 'date',
597
+ 'order' => 'DESC',
598
+ 'include' => '',
599
+ 'exclude' => '',
600
+ 'meta_key' => '',
601
+ 'meta_value' => '',
602
+ 'post_type' => 'post',
603
+ 'post_mime_type' => '',
604
+ 'post_parent' => '',
605
+ 'author' => '',
606
+ 'author_name' => '',
607
+ 'post_status' => 'publish',
608
+ 'suppress_filters' => true
609
+ ), $args);
610
+ $args['posts_per_page'] = $max;
611
+
612
+ $posts = get_posts($args);
613
+ $options = array();
614
+ foreach ($posts as $post) {
615
+ $options['' . $post->ID] = $post->post_title;
616
+ }
617
+
618
+ $this->select($name, $options);
619
+ }
620
+
621
+ function select_number($name, $min, $max) {
622
+ $options = array();
623
+ for ($i = $min; $i <= $max; $i++) {
624
+ $options['' . $i] = $i;
625
+ }
626
+ $this->select($name, $options);
627
+ }
628
+
629
+ function page($name = 'page', $first = null, $language = '', $show_id = false) {
630
+ $args = array(
631
+ 'post_type' => 'page',
632
+ 'posts_per_page' => 1000,
633
+ 'offset' => 0,
634
+ 'orderby' => 'post_title',
635
+ 'post_status' => 'any',
636
+ 'suppress_filters' => true
637
+ );
638
+
639
+ $pages = get_posts($args);
640
+ //$pages = get_pages();
641
+ $options = array();
642
+ foreach ($pages as $page) {
643
+ /* @var $page WP_Post */
644
+ $label = $page->post_title;
645
+ if ($page->post_status != 'publish') {
646
+ $label .= ' (' . $page->post_status . ')';
647
+ }
648
+ if ($show_id) {
649
+ $label .= ' [' . $page->ID . ']';
650
+ }
651
+ $options[$page->ID] = $label;
652
+ }
653
+ $this->select($name, $options, $first);
654
+ }
655
+
656
+ /** Used to create a select which is part of a group of controls identified by $name that will
657
+ * produce an array of values as $_REQUEST['name'].
658
+ * @param string $name
659
+ * @param array $options Associative array
660
+ */
661
+ function select_group($name, $options) {
662
+ $value_array = $this->get_value_array($name);
663
+
664
+ echo '<select name="options[' . esc_attr($name) . '][]">';
665
+
666
+ foreach ($options as $key => $label) {
667
+ echo '<option value="' . esc_attr($key) . '"';
668
+ if (array_search($key, $value_array) !== false) {
669
+ echo ' selected';
670
+ }
671
+ echo '>' . esc_html($label) . '</option>';
672
+ }
673
+
674
+ echo '</select>';
675
+ }
676
+
677
+ function select($name, $options, $first = null, $attrs = []) {
678
+ echo '<select id="options-' . esc_attr($name) . '" name="options[' . esc_attr($name) . ']"';
679
+ if ($attrs) {
680
+ foreach ($attrs as $key => $value) {
681
+ echo ' ', $key, '="' . esc_attr($value), '"';
682
+ }
683
+ }
684
+ echo '>';
685
+ if (!empty($first)) {
686
+ echo '<option value="">' . esc_html($first) . '</option>';
687
+ }
688
+ $value = $this->get_value($name);
689
+ foreach ($options as $key => $label) {
690
+ echo '<option value="' . esc_attr($key) . '"';
691
+ if ($value == $key) {
692
+ echo ' selected';
693
+ }
694
+ echo '>' . esc_html($label) . '</option>';
695
+ }
696
+ echo '</select>';
697
+ }
698
+
699
+ function select_images($name, $options, $first = null) {
700
+ $value = $this->get_value($name);
701
+
702
+ echo '<select id="options-' . esc_attr($name) . '" name="options[' . esc_attr($name) . ']" style="min-width: 200px">';
703
+ if (!empty($first)) {
704
+ echo '<option value="">' . esc_html($first) . '</option>';
705
+ } else {
706
+ // if (empty($value)) {
707
+ // $keys = array_keys($options);
708
+ // $value = $keys[0];
709
+ // }
710
+ }
711
+ foreach ($options as $key => $data) {
712
+ echo '<option value="' . esc_attr($key) . '" image="' . esc_attr($data['image']) . '"';
713
+ if ($value == $key)
714
+ echo ' selected';
715
+ echo '>' . esc_html($data['label']) . '</option>';
716
+ }
717
+ echo '</select>';
718
+ echo '<script>jQuery("#options-' . esc_attr($name) . '").select2({templateResult: tnp_select_images, templateSelection: tnp_select_images_selection});</script>';
719
+ }
720
+
721
+ function select2($name, $options, $first = null, $multiple = false, $style = null, $placeholder = '') {
722
+
723
+ if ($multiple) {
724
+ $option_name = "options[" . esc_attr($name) . "][]";
725
+ } else {
726
+ $option_name = "options[" . esc_attr($name) . "]";
727
+ }
728
+
729
+ if (is_null($style)) {
730
+ $style = 'width: 100%';
731
+ }
732
+
733
+ $value = $this->get_value($name);
734
+
735
+ echo '<select id="options-', esc_attr($name), '" name="', $option_name, '" style="', $style, '"',
736
+ ($multiple ? ' multiple' : ''), '>';
737
+ if (!empty($first)) {
738
+ echo '<option value="">' . esc_html($first) . '</option>';
739
+ }
740
+
741
+ foreach ($options as $key => $data) {
742
+ echo '<option value="' . esc_attr($key) . '"';
743
+ if (is_array($value) && in_array($key, $value) || (!is_null($value) && $value == $key )) {
744
+ echo ' selected';
745
+ }
746
+ echo '>' . esc_html($data) . '</option>';
747
+ }
748
+
749
+ echo '</select>';
750
+ echo '<script>jQuery("#options-' . esc_attr($name) . '").select2({placeholder: "', esc_js($placeholder), '"});</script>';
751
+ }
752
+
753
+ function select_grouped($name, $groups) {
754
+ $value = $this->get_value($name);
755
+ $name = esc_attr($name);
756
+ echo '<select name="options[', $name, ']">';
757
+
758
+ foreach ($groups as $group) {
759
+ echo '<optgroup label="' . esc_attr($group['']) . '">';
760
+ if (!empty($group)) {
761
+ foreach ($group as $key => $label) {
762
+ if ($key == '') {
763
+ continue;
764
+ }
765
+ echo '<option value="' . esc_attr($key) . '"';
766
+ if ($value == $key) {
767
+ echo ' selected';
768
+ }
769
+ echo '>' . esc_html($label) . '</option>';
770
+ }
771
+ }
772
+ echo '</optgroup>';
773
+ }
774
+ echo '</select>';
775
+ }
776
+
777
+ /**
778
+ * Generated a select control with all available templates. From version 3 there are
779
+ * only on kind of templates, they are no more separated by type.
780
+ */
781
+ function themes($name, $themes, $submit_on_click = true) {
782
+ foreach ($themes as $key => $data) {
783
+ echo '<label style="display: block; float: left; text-align: center; margin-right: 10px;">';
784
+ echo esc_html($key) . '<br>';
785
+ echo '<img src="' . esc_attr($data['screenshot']) . '" width="100" height="100" style="border: 1px solid #666; padding: 5px"><br>';
786
+ echo '<input style="position: relative; top: -40px" type="radio" onchange="this.form.act.value=\'theme\';this.form.submit()" name="options[' . esc_attr($name) . ']" value="' . esc_attr($key) . '"';
787
+ if ($this->data[$name] == $key) {
788
+ echo ' checked';
789
+ }
790
+ echo '>';
791
+ echo '</label>';
792
+ }
793
+ echo '<div style="clear: both"></div>';
794
+ }
795
+
796
+ function value($name) {
797
+ echo esc_html($this->data[$name]);
798
+ }
799
+
800
+ function value_date($name, $show_remaining = true) {
801
+ $time = $this->get_value($name);
802
+
803
+ echo gmdate(get_option('date_format') . ' ' . get_option('time_format'), $time + get_option('gmt_offset') * 3600);
804
+ $delta = $time - time();
805
+ if ($show_remaining && $delta > 0) {
806
+ echo 'Remaining: ';
807
+ $delta = $time - time();
808
+ $days = floor($delta / (24 * 3600));
809
+ $delta = $delta - $days * 24 * 3600;
810
+ $hours = floor($delta / 3600);
811
+ $delta = $delta - $hours * 3600;
812
+ $minutes = floor($delta / 60);
813
+
814
+ if ($days > 0) {
815
+ echo $days . ' days ';
816
+ }
817
+ echo $hours . ' hours ';
818
+ echo $minutes . ' minutes ';
819
+ }
820
+ }
821
+
822
+ function password($name, $size = 20, $placeholder = '') {
823
+ $value = $this->get_value($name);
824
+ $name = esc_attr($name);
825
+ echo '<input id="options-', $name, '" placeholder="' . esc_attr($placeholder) . '" name="options[', $name, ']" type="password" autocomplete="off" ';
826
+ if (!empty($size)) {
827
+ echo 'size="', $size, '" ';
828
+ }
829
+ echo 'value="', esc_attr($value), '">';
830
+ }
831
+
832
+ function text($name, $size = 20, $placeholder = '') {
833
+ $value = $this->get_value($name);
834
+ $name = esc_attr($name);
835
+ echo '<input id="options-', $name, '" placeholder="' . esc_attr($placeholder) . '" title="' . esc_attr($placeholder) . '" name="options[', $name, ']" type="text" ';
836
+ if (!empty($size)) {
837
+ echo 'size="', esc_attr($size), '" ';
838
+ }
839
+ echo 'value="', esc_attr($value), '">';
840
+ }
841
+
842
+ function text_email($name, $attrs = []) {
843
+ if (is_numeric($attrs)) {
844
+ $attrs = ['size' => $attrs];
845
+ }
846
+ $attrs = array_merge(['placeholder' => __('Valid email address', 'newsletter'), 'size' => 40, 'required' => false], $attrs);
847
+
848
+ $value = $this->get_value($name);
849
+ echo '<input name="options[' . esc_attr($name) . ']" type="email" placeholder="';
850
+ echo esc_attr($attrs['placeholder']);
851
+ echo '" size="', esc_attr($attrs['size']), '" value="', esc_attr($value) , '"';
852
+ if ($attrs['required']) {
853
+ echo ' required';
854
+ }
855
+ echo '>';
856
+ }
857
+
858
+ function text_url($name, $size = 40) {
859
+ $value = $this->get_value($name);
860
+ echo '<input name="options[' . esc_attr($name) . ']" type="url" placeholder="http://..." size="' . esc_attr($size) . '" value="';
861
+ echo esc_attr($value);
862
+ echo '"/>';
863
+ }
864
+
865
+ function hidden($name) {
866
+ $value = $this->get_value($name);
867
+ echo '<input name="options[', esc_attr($name), ']" id="options-', esc_attr($name), '" type="hidden" value="', esc_attr($value), '">';
868
+ }
869
+
870
+ /**
871
+ * General button. Attributes:
872
+ * - id: the element HTML id
873
+ * - confirm: if string the text is shown in a confirmation message, if true shows a standard confirm message
874
+ * - icon: the font awesome icon name (fa-xxx)
875
+ * - style: the CSS style
876
+ * - data: free data associated to the button click ($controls->button_data) for example to pass the element ID from a list of elements
877
+ *
878
+ * @param string $action
879
+ * @param string $label
880
+ * @param array $attrs
881
+ */
882
+ function btn($action, $label, $attrs = []) {
883
+ echo '<button class="button-primary tnpc-button"';
884
+ if (isset($attrs['id'])) {
885
+ echo ' id="', esc_attrs($attrs['id']), '"';
886
+ }
887
+ $onclick = "this.form.act.value='" . esc_attr(esc_js(trim($action))) . "';";
888
+ if (!empty($attrs['data'])) {
889
+ $onclick .= "this.form.btn.value='" . esc_attr(esc_js($attrs['data'])) . "';";
890
+ }
891
+ if (isset($attrs['confirm'])) {
892
+ if (is_string($attrs['confirm'])) {
893
+ $onclick .= "if (!confirm('" . esc_attr(esc_js($attrs['confirm'])) . "')) return false;";
894
+ } else if ($attrs['confirm'] === true) {
895
+ $onclick .= "if (!confirm('" . esc_attr(esc_js(__('Proceed?', 'newsletter'))) . "')) return false;";
896
+ }
897
+ }
898
+ echo 'onclick="', $onclick, '"';
899
+ if (!empty($attrs['title'])) {
900
+ echo ' title="', esc_attr($attrs['title']), '"';
901
+ }
902
+ if (!empty($attrs['style'])) {
903
+ echo ' style="', esc_attr($attrs['style']), '"';
904
+ }
905
+ echo '>';
906
+ if (!empty($attrs['icon'])) {
907
+ echo '<i class="fas ', esc_attr($attrs['icon']), '"></i>';
908
+ if (!empty($label)) {
909
+ echo '&nbsp;', esc_html($label);
910
+ }
911
+ } else {
912
+ echo esc_html($label);
913
+ }
914
+ echo '</button>';
915
+ }
916
+
917
+ /**
918
+ * Creates a link looking lie a standard button. Attributes:
919
+ * - title: the link "title" HTML attribute
920
+ * - target: the link "target" HTML attribute
921
+ * - icon: the font awesome icon name (fa-xxx)
922
+ * - style: the CSS style
923
+ *
924
+ * @param string $url
925
+ * @param string $label
926
+ * @param array $attrs
927
+ */
928
+ function btn_link($url, $label, $attrs = []) {
929
+ echo '<a href="', esc_attr($url), '" class="button-primary tnpc-button"';
930
+ if (!empty($attrs['style'])) {
931
+ echo ' style="', esc_attr($attrs['style']), '"';
932
+ }
933
+ if (!empty($attrs['title'])) {
934
+ echo ' title="', esc_attr($attrs['title']), '"';
935
+ }
936
+ if (!empty($attrs['target'])) {
937
+ echo ' target="', esc_attr($attrs['target']), '"';
938
+ }
939
+ echo '>';
940
+ if (!empty($attrs['icon'])) {
941
+ echo '<i class="fas ', esc_attr($attrs['icon']), '"></i>';
942
+ if (!empty($label)) {
943
+ echo '&nbsp;', esc_html($label);
944
+ }
945
+ } else {
946
+ echo esc_html($label);
947
+ }
948
+ echo '</a>';
949
+ }
950
+
951
+ function button($action, $label, $function = '', $id = '') {
952
+ $id = !empty($id) ? " id=\"$id\" " : '';
953
+ if ($function != null) {
954
+ echo '<input ' . $id . ' class="button-primary tnpc-button" type="button" value="' . esc_attr($label) . '" onclick="this.form.act.value=\'' . esc_attr($action) . '\';' . esc_html($function) . '"/>';
955
+ } else {
956
+ echo '<input ' . $id . ' class="button-primary tnpc-button" type="submit" value="' . esc_attr($label) . '" onclick="this.form.act.value=\'' . esc_attr($action) . '\';return true;"/>';
957
+ }
958
+ }
959
+
960
+ function action_link($action, $label, $function = null) {
961
+ if ($function != null) {
962
+ echo '<input class="button-link tnpc-button" type="button" value="' . esc_attr($label) . '" onclick="this.form.act.value=\'' . esc_attr($action) . '\';' . esc_html($function) . '"/>';
963
+ } else {
964
+ echo '<input class="button-link tnpc-button" type="submit" value="' . esc_attr($label) . '" onclick="this.form.act.value=\'' . esc_attr($action) . '\';return true;"/>';
965
+ }
966
+ }
967
+
968
+ function button_save() {
969
+ $this->btn('save', __('Save', 'newsletter'), ['icon' => 'fa-save']);
970
+ }
971
+
972
+ function button_reset($action = 'reset') {
973
+ $this->btn($action, __('Reset', 'newsletter'), ['icon' => 'fa-reply', 'confirm' => true]);
974
+ }
975
+
976
+ function button_copy($data = '') {
977
+ $this->btn('copy', __('Duplicate', 'newsletter'), ['data' => $data, 'icon' => 'fa-copy', 'confirm' => true]);
978
+ }
979
+
980
+ function button_icon_copy($data = '') {
981
+ $this->btn('copy', '', ['data' => $data, 'icon' => 'fa-copy', 'confirm' => true, 'title' => __('Duplicate', 'newsletter')]);
982
+ }
983
+
984
+ /**
985
+ * Creates a button with "delete" action.
986
+ * @param type $data
987
+ */
988
+ function button_delete($data = '') {
989
+ $this->btn('delete', __('Delete', 'newsletter'), ['data' => $data, 'icon' => 'fa-times', 'confirm' => true, 'style' => 'background-color: darkred; color: #ffffff']);
990
+ }
991
+
992
+ function button_icon_delete($data = '') {
993
+ $this->btn('delete', '', ['data' => $data, 'icon' => 'fa-times', 'confirm' => true, 'title' => __('Delete', 'newsletter'), 'style' => 'background-color: darkred; color: #ffffff']);
994
+ }
995
+
996
+ function button_icon_configure($url) {
997
+ $this->btn_link($url, '', ['icon' => 'fa-cog', 'title' => __('Configure', 'newsletter')]);
998
+ }
999
+
1000
+ function button_icon_subscribers($url) {
1001
+ $this->btn_link($url, '', ['icon' => 'fa-users', 'title' => __('Subscribers', 'newsletter')]);
1002
+ }
1003
+
1004
+ function button_statistics($url) {
1005
+ $this->btn_link($url, __('Statistics', 'newsletter'), ['icon' => 'fa-chart-bar']);
1006
+ }
1007
+
1008
+ function button_icon_statistics($url) {
1009
+ $this->btn_link($url, '', ['icon' => 'fa-chart-bar', 'title' => __('Statistics', 'newsletter')]);
1010
+ }
1011
+
1012
+ function button_icon_view($url) {
1013
+ $this->btn_link($url, '', ['icon' => 'fa-eye', 'title' => __('View', 'newsletter'), 'target' => '_blank']);
1014
+ }
1015
+
1016
+ function button_icon_newsletters($url) {
1017
+ $this->btn_link($url, '', ['icon' => 'fa-file-alt', 'title' => __('Newsletters', 'newsletter')]);
1018
+ }
1019
+
1020
+ function button_icon_design($url) {
1021
+ $this->btn_link($url, '', ['icon' => 'fa-paint-brush', 'title' => __('Design', 'newsletter')]);
1022
+ }
1023
+
1024
+ function button_icon_edit($url) {
1025
+ $this->btn_link($url, '', ['icon' => 'fa-edit', 'title' => __('Edit', 'newsletter')]);
1026
+ }
1027
+
1028
+ function button_icon_back($url) {
1029
+ $this->btn_link($url, '', ['icon' => 'fa-chevron-left', 'title' => __('Back', 'newsletter')]);
1030
+ }
1031
+
1032
+ function button_icon($action, $icon, $title = '', $data = '', $confirm = false) {
1033
+ $this->btn($action, '', ['data' => $data, 'icon' => $icon, 'title' => $title, 'confirm' => $confirm]);
1034
+ }
1035
+
1036
+ function button_back($url) {
1037
+ $this->btn_link($url, __('Back', 'newsletter'), ['icon' => 'fa-chevron-left']);
1038
+ }
1039
+
1040
+ function button_test($action = 'test') {
1041
+ $this->btn($action, __('Test', 'newsletter'), ['icon' => 'fa-vial']);
1042
+ }
1043
+
1044
+ /**
1045
+ * @deprecated
1046
+ */
1047
+ function button_primary($action, $label, $function = null) {
1048
+ if ($function != null) {
1049
+ echo '<button class="button-primary" onclick="this.form.act.value=\'' . esc_attr($action) . '\';' . esc_attr($function) . '">', $label, '</button>';
1050
+ } else {
1051
+ echo '<button class="button-primary" onclick="this.form.act.value=\'' . esc_attr($action) . '\'; return true;"/>', $label, '</button>';
1052
+ }
1053
+ }
1054
+
1055
+ function button_confirm($action, $label, $message = true, $data = '') {
1056
+ $this->btn($action, $label, ['data' => $data, 'confirm' => $message]);
1057
+ }
1058
+
1059
+ /**
1060
+ * @deprecated
1061
+ * @param string $url
1062
+ * @param string $label Not escaped.
1063
+ */
1064
+ function button_link($url, $label = '') {
1065
+ echo '<a href="', esc_attr($url), '" class="button-primary">', $label, '</a>';
1066
+ }
1067
+
1068
+ function editor($name, $rows = 5, $cols = 75) {
1069
+ echo '<textarea class="visual" name="options[' . esc_attr($name) . ']" style="width: 100%" wrap="off" rows="' . esc_attr($rows) . '">';
1070
+ echo esc_html($this->get_value($name));
1071
+ echo '</textarea>';
1072
+ }
1073
+
1074
+ function wp_editor($name, $settings = []) {
1075
+
1076
+ add_filter('mce_buttons', function ($mce_buttons) {
1077
+ $mce_buttons[] = 'wp_add_media';
1078
+ //$mce_buttons[] = 'wp_code';
1079
+ return $mce_buttons;
1080
+ });
1081
+
1082
+ $settings = array_merge(['media_buttons' => false], $settings);
1083
+
1084
+ $value = $this->get_value($name);
1085
+ wp_editor($value, $name, array_merge(array(
1086
+ 'tinymce' => array('content_css' => plugins_url('newsletter') . '/admin/wp-editor.css?ver=' . NEWSLETTER_VERSION),
1087
+ 'textarea_name' => 'options[' . esc_attr($name) . ']',
1088
+ 'wpautop' => false
1089
+ ), $settings));
1090
+ }
1091
+
1092
+ function textarea($name, $width = '100%', $height = '50') {
1093
+ $value = $this->get_value($name);
1094
+ if (is_array($value)) {
1095
+ $value = implode("\n", $value);
1096
+ }
1097
+ echo '<textarea id="options-' . esc_attr($name) . '" class="dynamic" name="options[' . esc_attr($name) . ']" wrap="off" style="width:' . esc_attr($width) . ';height:' . esc_attr($height) . '">';
1098
+ echo esc_html($value);
1099
+ echo '</textarea>';
1100
+ }
1101
+
1102
+ function textarea_fixed($name, $width = '100%', $height = '200') {
1103
+ $value = $this->get_value($name);
1104
+ $name = esc_attr($name);
1105
+ echo '<textarea id="options-', $name, '" name="options[', $name, ']" wrap="off" style="width:', esc_attr($width), ';height:', esc_attr($height), 'px">';
1106
+ echo esc_html($value);
1107
+ echo '</textarea>';
1108
+ }
1109
+
1110
+ function textarea_preview($name, $width = '100%', $height = '200', $header = '', $footer = '', $switch_button = true) {
1111
+ $value = $this->get_value($name);
1112
+ $name = esc_attr($name);
1113
+ if ($switch_button) {
1114
+ echo '<input class="button-primary" type="button" onclick="newsletter_textarea_preview(\'options-', $name, '\', \'\', \'\')" value="Switch editor/preview">';
1115
+ echo '<br><br>';
1116
+ }
1117
+ echo '<div style="box-sizing: border-box; position: relative; margin: 0; padding: 0; width:' . esc_attr($width) . '; height:' . esc_attr($height) . '">';
1118
+ echo '<textarea id="options-', $name, '" name="options[', $name, ']" wrap="off" style="width:' . esc_attr($width) . ';height:' . esc_attr($height) . 'px">';
1119
+ echo esc_html($value);
1120
+ echo '</textarea>';
1121
+ echo '<div id="options-', $name, '-preview" style="box-sizing: border-box; background-color: #eee; border: 1px solid #bbb; padding: 15px; width: auto; position: absolute; top: 20px; left: 20px; box-shadow: 0 0 20px #777; z-index: 10000; display: none">';
1122
+ echo '<iframe id="options-', $name, '-iframe" class="tnp-editor-preview-desktop"></iframe>';
1123
+ echo '<iframe id="options-', $name, '-iframe-phone" class="tnp-editor-preview-mobile"></iframe>';
1124
+ echo '</div>';
1125
+ echo '</div>';
1126
+ }
1127
+
1128
+ function email($prefix, $editor = null, $disable_option = false, $settings = array()) {
1129
+ if ($disable_option) {
1130
+ $this->disabled($prefix . '_disabled');
1131
+ echo '<br>';
1132
+ }
1133
+
1134
+ $this->text($prefix . '_subject', 90, 'Subject');
1135
+ echo '<br><br>';
1136
+
1137
+ if ($editor == 'wordpress') {
1138
+ $this->wp_editor($prefix . '_message', $settings);
1139
+ } else if ($editor == 'textarea') {
1140
+ $this->textarea($prefix . '_message');
1141
+ } else {
1142
+ $this->editor($prefix . '_message');
1143
+ }
1144
+ }
1145
+
1146
+ /**
1147
+ * Standard checkbox, when not checked no value is transmitted (checkbox2).
1148
+ *
1149
+ * @param string $name
1150
+ * @param string $label
1151
+ */
1152
+ function checkbox($name, $label = '') {
1153
+ if ($label != '') {
1154
+ echo '<label>';
1155
+ }
1156
+ echo '<input type="checkbox" id="options-' . esc_attr($name) . '" name="options[' . esc_attr($name) . ']" value="1"';
1157
+ if (!empty($this->data[$name])) {
1158
+ echo ' checked';
1159
+ }
1160
+ echo '>';
1161
+ if ($label != '') {
1162
+ echo '&nbsp;' . esc_html($label) . '</label>';
1163
+ }
1164
+ }
1165
+
1166
+ /**
1167
+ * Checkbox with a hidden field to transmit 1 or 0 even when the checkbox is not checked.
1168
+ *
1169
+ * @param string $name
1170
+ * @param string $label
1171
+ */
1172
+ function checkbox2($name, $label = '') {
1173
+ if ($label != '') {
1174
+ echo '<label>';
1175
+ }
1176
+ echo '<input type="checkbox" id="' . esc_attr($name) . '" onchange="document.getElementById(\'' . esc_attr($name) . '_hidden\').value=this.checked?\'1\':\'0\'"';
1177
+ if (!empty($this->data[$name])) {
1178
+ echo ' checked';
1179
+ }
1180
+ echo '>';
1181
+ if ($label != '') {
1182
+ echo '&nbsp;' . esc_html($label) . '</label>';
1183
+ }
1184
+ echo '<input type="hidden" id="' . esc_attr($name) . '_hidden" name="options[' . esc_attr($name) . ']" value="';
1185
+
1186
+ echo empty($this->data[$name]) ? '0' : '1';
1187
+ echo '">';
1188
+ }
1189
+
1190
+ function radio($name, $value, $label = '') {
1191
+ if ($label != '') {
1192
+ echo '<label>';
1193
+ }
1194
+ echo '<input type="radio" id="' . esc_attr($name) . '" name="options[' . esc_attr($name) . ']" value="' . esc_attr($value) . '"';
1195
+ $v = $this->get_value($name);
1196
+ if ($v == $value) {
1197
+ echo ' checked';
1198
+ }
1199
+ echo '>';
1200
+ if ($label != '') {
1201
+ echo '&nbsp;' . esc_html($label) . '</label>';
1202
+ }
1203
+ }
1204
+
1205
+ /**
1206
+ * Creates a checkbox named $name and checked if the internal data contains under
1207
+ * the key $name an array containig the passed value.
1208
+ */
1209
+ function checkbox_group($name, $value, $label = '', $attrs = []) {
1210
+ $attrs = wp_parse_args($attrs, ['label_escape' => true]);
1211
+ echo '<label><input type="checkbox" id="' . esc_attr($name) . '" name="options[' . esc_attr($name) . '][]" value="' . esc_attr($value) . '"';
1212
+ if (isset($this->data[$name]) && is_array($this->data[$name]) && array_search($value, $this->data[$name]) !== false) {
1213
+ echo ' checked';
1214
+ }
1215
+ echo '>';
1216
+ if ($label != '') {
1217
+ if ($attrs['label_escape']) {
1218
+ echo esc_html($label);
1219
+ } else {
1220
+ echo $label;
1221
+ }
1222
+ }
1223
+ echo '</label>';
1224
+ }
1225
+
1226
+ function checkboxes($name, $options) {
1227
+ echo '<div class="tnpc-checkboxes">';
1228
+ foreach ($options as $value => $label) {
1229
+ $this->checkbox_group($name, $value, $label);
1230
+ }
1231
+ echo '<div style="clear: both"></div>';
1232
+ echo '</div>';
1233
+ }
1234
+
1235
+ function color($name, $default = '') {
1236
+ $value = esc_attr($this->get_value($name, $default));
1237
+ $name = esc_attr($name);
1238
+ echo '<input class="tnpc-color" id="options-', $name, '" name="options[', $name, ']" type="text" value="', $value, '">';
1239
+ }
1240
+
1241
+ /** Creates a set of checkbox named $name_[category id] (so they are posted with distinct names).
1242
+ */
1243
+ function categories($name = 'category') {
1244
+ $categories = get_categories();
1245
+ echo '<div class="tnpc-checkboxes">';
1246
+ foreach ($categories as $c) {
1247
+ $this->checkbox($name . '_' . $c->cat_ID, esc_html($c->cat_name));
1248
+ }
1249
+ echo '<div style="clear: both"></div>';
1250
+ }
1251
+
1252
+ /**
1253
+ * Creates a set of checkbox to activate the profile preferences. Every checkbox has a DIV around to
1254
+ * be formatted.
1255
+ */
1256
+ function categories_group($name, $show_mode = false) {
1257
+ $categories = get_categories();
1258
+ if ($show_mode) {
1259
+ $this->select($name . '_mode', array('include' => 'To be included', 'exclude' => 'To be excluded'));
1260
+ }
1261
+ echo '<div class="tnpc-checkboxes">';
1262
+ foreach ($categories as &$c) {
1263
+ $this->checkbox_group($name, $c->cat_ID, esc_html($c->cat_name));
1264
+ }
1265
+ echo '<div style="clear: both"></div>';
1266
+ }
1267
+
1268
+ /**
1269
+ * Creates a set of checkboxes named $name_[preference number] (so they are
1270
+ * distinct fields).
1271
+ * Empty preferences are skipped.
1272
+ */
1273
+ function preferences($name = 'preferences') {
1274
+ $lists = Newsletter::instance()->get_lists();
1275
+
1276
+ echo '<div class="tnpc-checkboxes">';
1277
+ foreach ($lists as $list) {
1278
+ $this->checkbox2($name . '_' . $list->id, esc_html($list->name));
1279
+ }
1280
+ echo '<div style="clear: both"></div>';
1281
+ }
1282
+
1283
+ /** A list of all lists defined each one with a checkbox to select it. An array
1284
+ * of ID of all checked lists is submitted.
1285
+ *
1286
+ * @param string $name
1287
+ */
1288
+ function lists($name = 'lists') {
1289
+ echo '<input type="hidden" name="tnp_fields[' . esc_attr($name) . ']" value="array">';
1290
+ $this->preferences_group($name);
1291
+ }
1292
+
1293
+ function lists_checkboxes($name = 'lists') {
1294
+ $this->preferences_group($name);
1295
+ }
1296
+
1297
+ /**
1298
+ * Creates a set of checkboxes all names $name[] and the preference number as value
1299
+ * so the selected checkboxes are retrieved as an array of values ($REQUEST[$name]
1300
+ * will be an array if at east one preference is checked).
1301
+ */
1302
+ function preferences_group($name = 'preferences') {
1303
+
1304
+ $lists = Newsletter::instance()->get_lists();
1305
+
1306
+ echo '<div class="tnpc-lists">';
1307
+ foreach ($lists as $list) {
1308
+ $this->checkbox_group($name, $list->id, '<span>' . $list->id . '</span> ' . esc_html($list->name), ['label_escape' => false]);
1309
+ }
1310
+ echo '<a href="https://www.thenewsletterplugin.com/documentation/newsletter-lists" target="_blank">'
1311
+ . 'Click here to read more about lists.'
1312
+ . '</a>';
1313
+ echo '</div>';
1314
+ }
1315
+
1316
+ /** Creates as many selects as the active preferences with the three values
1317
+ * 'any', 'yes', 'no' corresponding to the values 0, 1, 2.
1318
+ */
1319
+ function preferences_selects($name = 'preferences', $skip_empty = false) {
1320
+ $lists = Newsletter::instance()->get_lists();
1321
+
1322
+ echo '<div class="newsletter-preferences-group">';
1323
+ foreach ($lists as $list) {
1324
+
1325
+ echo '<div class="newsletter-preferences-item">';
1326
+
1327
+ $this->select($name . '_' . $list->id, array(0 => 'Any', 1 => 'Yes', 2 => 'No'));
1328
+ echo '(' . $list->id . ') ' . esc_html($list->name);
1329
+
1330
+ echo '</div>';
1331
+ }
1332
+ echo '<div style="clear: both"></div>';
1333
+ echo '<a href="https://www.thenewsletterplugin.com/plugins/newsletter/newsletter-preferences" target="_blank">Click here know more about preferences.</a> They can be configured on Subscription/Form field panel.';
1334
+ echo '</div>';
1335
+ }
1336
+
1337
+ /**
1338
+ * Creates a single select with the active preferences.
1339
+ */
1340
+ function preferences_select($name = 'preference', $empty_label = null) {
1341
+ $lists = $this->get_list_options($empty_label);
1342
+ $this->select($name, $lists);
1343
+ echo ' <a href="admin.php?page=newsletter_subscription_lists" target="_blank"><i class="fas fa-edit"></i></a>';
1344
+ }
1345
+
1346
+ function lists_select($name = 'list', $empty_label = null) {
1347
+ $lists = $this->get_list_options($empty_label);
1348
+ $this->select($name, $lists);
1349
+ }
1350
+
1351
+ function lists_select_with_notes($name = 'list', $empty_label = null) {
1352
+
1353
+ $value = $this->get_value($name);
1354
+
1355
+ $lists = Newsletter::instance()->get_lists();
1356
+ $options = [];
1357
+ if ($empty_label) {
1358
+ $options[''] = $empty_label;
1359
+ }
1360
+
1361
+ foreach ($lists as $list) {
1362
+ $options['' . $list->id] = '(' . $list->id . ') ' . esc_html($list->name);
1363
+ }
1364
+
1365
+ $this->select($name, $options, null, ['onchange' => 'tnp_lists_toggle(this); return true;']);
1366
+ echo '<div id="options-', esc_attr($name), '-notes" class="tnpc_lists_notes">';
1367
+ foreach ($lists as $list) {
1368
+ $id = $list->id;
1369
+ $notes = apply_filters('newsletter_lists_notes', [], $id);
1370
+
1371
+ echo '<div class="list_', $id, '" style="display: ', ($value == $id ? 'block' : 'none'), '">';
1372
+ if ($list->forced) {
1373
+ echo 'Enforced on subscription<br>';
1374
+ }
1375
+ echo implode('<br>', $notes);
1376
+ echo '</div>';
1377
+ }
1378
+ echo '</div>';
1379
+ }
1380
+
1381
+ function public_lists_select($name = 'list', $empty_label = null) {
1382
+ $lists = $this->get_public_list_options($empty_label);
1383
+ $this->select($name, $lists);
1384
+ }
1385
+
1386
+ /**
1387
+ * Generates an associative array with the active lists to be used in a select.
1388
+ * @param string $empty_label
1389
+ * @return array
1390
+ */
1391
+ function get_list_options($empty_label = null) {
1392
+ $objs = Newsletter::instance()->get_lists();
1393
+ $lists = array();
1394
+ if ($empty_label) {
1395
+ $lists[''] = $empty_label;
1396
+ }
1397
+ foreach ($objs as $list) {
1398
+ $lists['' . $list->id] = '(' . $list->id . ') ' . esc_html($list->name);
1399
+ }
1400
+ return $lists;
1401
+ }
1402
+
1403
+ function get_public_list_options($empty_label = null) {
1404
+ $objs = Newsletter::instance()->get_lists_public();
1405
+ $lists = array();
1406
+ if ($empty_label) {
1407
+ $lists[''] = $empty_label;
1408
+ }
1409
+ foreach ($objs as $list) {
1410
+ $lists['' . $list->id] = '(' . $list->id . ') ' . esc_html($list->name);
1411
+ }
1412
+ return $lists;
1413
+ }
1414
+
1415
+ function date($name) {
1416
+ $this->hidden($name);
1417
+ $year = date('Y', $this->data[$name]);
1418
+ $day = date('j', $this->data[$name]);
1419
+ $month = date('m', $this->data[$name]);
1420
+ $onchange = "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";
1421
+ echo '<select id="' . $name . '_month" onchange="' . esc_attr($onchange) . '">';
1422
+ for ($i = 0; $i < 12; $i++) {
1423
+ echo '<option value="' . $i . '"';
1424
+ if ($month - 1 == $i) {
1425
+ echo ' selected';
1426
+ }
1427
+ echo '>' . date('F', mktime(0, 0, 0, $i + 1, 1, 2000)) . '</option>';
1428
+ }
1429
+ echo '</select>';
1430
+
1431
+ echo '<select id="' . esc_attr($name) . '_day" onchange="' . esc_attr($onchange) . '">';
1432
+ for ($i = 1; $i <= 31; $i++) {
1433
+ echo '<option value="' . $i . '"';
1434
+ if ($day == $i) {
1435
+ echo ' selected';
1436
+ }
1437
+ echo '>' . $i . '</option>';
1438
+ }
1439
+ echo '</select>';
1440
+
1441
+ echo '<select id="' . esc_attr($name) . '_year" onchange="' . esc_attr($onchange) . '">';
1442
+ for ($i = 2011; $i <= date('Y')+3; $i++) {
1443
+ echo '<option value="' . $i . '"';
1444
+ if ($year == $i) {
1445
+ echo ' selected';
1446
+ }
1447
+ echo '>' . $i . '</option>';
1448
+ }
1449
+ echo '</select>';
1450
+ }
1451
+
1452
+ /**
1453
+ * Creates a set of fields to collect a date and sends back the triplet year, month and day.
1454
+ *
1455
+ * @param string $name
1456
+ */
1457
+ function date2($name) {
1458
+ $year = $this->get_value($name . '_year');
1459
+ $day = $this->get_value($name . '_day');
1460
+ $month = $this->get_value($name . '_month');
1461
+
1462
+ echo '<select name="options[' . $name . '_month]">';
1463
+ echo '<option value="">-</option>';
1464
+ for ($i = 1; $i <= 12; $i++) {
1465
+ echo '<option value="' . $i . '"';
1466
+ if ($month == $i) {
1467
+ echo ' selected';
1468
+ }
1469
+ echo '>' . date_i18n('F', mktime(0, 0, 0, $i, 1, 2000)) . '</option>';
1470
+ }
1471
+ echo '</select>';
1472
+
1473
+ echo '<select name="options[' . esc_attr($name) . '_day]">';
1474
+ echo '<option value="">-</option>';
1475
+ for ($i = 1; $i <= 31; $i++) {
1476
+ echo '<option value="' . $i . '"';
1477
+ if ($day == $i) {
1478
+ echo ' selected';
1479
+ }
1480
+ echo '>' . $i . '</option>';
1481
+ }
1482
+ echo '</select>';
1483
+
1484
+ echo '<select name="options[' . esc_attr($name) . '_year]">';
1485
+ echo '<option value="">-</option>';
1486
+ for ($i = 2011; $i <= date('Y')+3; $i++) {
1487
+ echo '<option value="' . $i . '"';
1488
+ if ($year == $i) {
1489
+ echo ' selected';
1490
+ }
1491
+ echo '>' . $i . '</option>';
1492
+ }
1493
+ echo '</select>';
1494
+ }
1495
+
1496
+ /**
1497
+ * Date and time (hour) selector. Timestamp stored.
1498
+ */
1499
+ function datetime($name) {
1500
+ echo '<input type="hidden" name="tnp_fields[' . esc_attr($name) . ']" value="datetime">';
1501
+ $value = (int) $this->get_value($name);
1502
+ if (empty($value)) {
1503
+ $value = time();
1504
+ }
1505
+
1506
+ $time = $value + get_option('gmt_offset') * 3600;
1507
+ $year = gmdate('Y', $time);
1508
+ $day = gmdate('j', $time);
1509
+ $month = gmdate('m', $time);
1510
+ $hour = gmdate('H', $time);
1511
+
1512
+ echo '<select name="' . esc_attr($name) . '_month">';
1513
+ for ($i = 1; $i <= 12; $i++) {
1514
+ echo '<option value="' . $i . '"';
1515
+ if ($month == $i) {
1516
+ echo ' selected';
1517
+ }
1518
+ echo '>' . date('F', mktime(0, 0, 0, $i, 1, 2000)) . '</option>';
1519
+ }
1520
+ echo '</select>';
1521
+
1522
+ echo '<select name="' . esc_attr($name) . '_day">';
1523
+ for ($i = 1; $i <= 31; $i++) {
1524
+ echo '<option value="' . $i . '"';
1525
+ if ($day == $i) {
1526
+ echo ' selected';
1527
+ }
1528
+ echo '>' . $i . '</option>';
1529
+ }
1530
+ echo '</select>';
1531
+
1532
+ $last_year = date('Y') + 2;
1533
+ echo '<select name="' . esc_attr($name) . '_year">';
1534
+ for ($i = 2011; $i <= $last_year; $i++) {
1535
+ echo '<option value="' . $i . '"';
1536
+ if ($year == $i) {
1537
+ echo ' selected';
1538
+ }
1539
+ echo '>' . $i . '</option>';
1540
+ }
1541
+ echo '</select>';
1542
+
1543
+ echo '<select name="' . esc_attr($name) . '_hour">';
1544
+ for ($i = 0; $i <= 23; $i++) {
1545
+ echo '<option value="' . $i . '"';
1546
+ if ($hour == $i) {
1547
+ echo ' selected';
1548
+ }
1549
+ echo '>' . $i . ':00</option>';
1550
+ }
1551
+ echo '</select>';
1552
+ }
1553
+
1554
+ function hours($name) {
1555
+ $hours = array();
1556
+ for ($i = 0; $i < 24; $i++) {
1557
+ $hours['' . $i] = sprintf('%02d', $i) . ':00';
1558
+ }
1559
+ $this->select($name, $hours);
1560
+ }
1561
+
1562
+ function days($name) {
1563
+ $days = array(0 => 'Every day', 1 => 'Monday', 2 => 'Tuesday', 3 => 'Wednesday', 4 => 'Thursday', 5 => 'Friday', 6 => 'Saturday', 7 => 'Sunday');
1564
+ $this->select($name, $days);
1565
+ }
1566
+
1567
+ function init($options = array()) {
1568
+ $cookie_name = 'newsletter_tab';
1569
+ if (isset($options['cookie_name'])) {
1570
+ $cookie_name = $options['cookie_name'];
1571
+ }
1572
+ echo '<script type="text/javascript">
1573
+ jQuery(document).ready(function(){
1574
+
1575
+ tnp_controls_init();
1576
+
1577
+ jQuery("textarea.dynamic").focus(function() {
1578
+ jQuery("textarea.dynamic").css("height", "50px");
1579
+ jQuery(this).css("height", "400px");
1580
+ });
1581
+ tabs = jQuery("#tabs").tabs({
1582
+ active : jQuery.cookie("' . $cookie_name . '"),
1583
+ activate : function( event, ui ){
1584
+ jQuery.cookie("' . $cookie_name . '", ui.newTab.index(),{expires: 1});
1585
+ }
1586
+ });
1587
+ jQuery(".tnp-tabs").tabs({});
1588
+
1589
+ });
1590
+ function newsletter_media(name) {
1591
+ var tnp_uploader = wp.media({
1592
+ title: "Select an image",
1593
+ button: {
1594
+ text: "Select"
1595
+ },
1596
+ multiple: false
1597
+ }).on("select", function() {
1598
+ var media = tnp_uploader.state().get("selection").first();
1599
+ document.getElementById(name + "_id").value = media.id;
1600
+ jQuery("#" + name + "_id").trigger("change");
1601
+ //alert(media.attributes.url);
1602
+ if (media.attributes.url.substring(0, 0) == "/") {
1603
+ media.attributes.url = "' . site_url('/') . '" + media.attributes.url;
1604
+ }
1605
+ document.getElementById(name + "_url").value = media.attributes.url;
1606
+
1607
+ var img_url = media.attributes.url;
1608
+ if (typeof media.attributes.sizes.medium !== "undefined") img_url = media.attributes.sizes.medium.url;
1609
+ if (img_url.substring(0, 0) == "/") {
1610
+ img_url = "' . site_url('/') . '" + img_url;
1611
+ }
1612
+ document.getElementById(name + "_img").src = img_url;
1613
+ }).open();
1614
+ }
1615
+ function newsletter_media_remove(name) {
1616
+ if (confirm("Are you sure?")) {
1617
+ document.getElementById(name + "_id").value = "";
1618
+ document.getElementById(name + "_url").value = "";
1619
+ document.getElementById(name + "_img").src = "' . plugins_url('newsletter') . '/images/nomedia.png";
1620
+ }
1621
+ }
1622
+ function newsletter_textarea_preview(id, header, footer) {
1623
+ var d = document.getElementById(id + "-iframe").contentWindow.document;
1624
+ d.open();
1625
+ if (templateEditor) {
1626
+ d.write(templateEditor.getValue());
1627
+ } else {
1628
+ d.write(header + document.getElementById(id).value + footer);
1629
+ }
1630
+ d.close();
1631
+
1632
+ var d = document.getElementById(id + "-iframe-phone").contentWindow.document;
1633
+ d.open();
1634
+ if (templateEditor) {
1635
+ d.write(templateEditor.getValue());
1636
+ } else {
1637
+ d.write(header + document.getElementById(id).value + footer);
1638
+ }
1639
+ d.close();
1640
+ //jQuery("#" + id + "-iframe-phone").toggle();
1641
+ jQuery("#" + id + "-preview").toggle();
1642
+ }
1643
+ function tnp_select_images(state) {
1644
+ if (!state.id) { return state.text; }
1645
+ var $state = jQuery("<span class=\"tnp-select2-option\"><img style=\"height: 20px!important; position: relative; top: 5px\" src=\"" + state.element.getAttribute("image") + "\"> " + state.text + "</span>");
1646
+ return $state;
1647
+ }
1648
+ function tnp_select_images_selection(state) {
1649
+ if (!state.id) { return state.text; }
1650
+ var $state = jQuery("<span class=\"tnp-select2-option\"><img style=\"height: 20px!important; position: relative; top: 5px\" src=\"" + state.element.getAttribute("image") + "\"> " + state.text + "</span>");
1651
+ return $state;
1652
+ }
1653
+
1654
+
1655
+ </script>
1656
+ ';
1657
+ echo '<input name="act" type="hidden" value=""/>';
1658
+ echo '<input name="btn" type="hidden" value=""/>';
1659
+ wp_nonce_field('save');
1660
+ }
1661
+
1662
+ function log_level($name = 'log_level') {
1663
+ $this->select($name, array(0 => 'None', 2 => 'Error', 3 => 'Normal', 4 => 'Debug'));
1664
+ }
1665
+
1666
+ function update_option($name, $data = null) {
1667
+ if ($data == null) {
1668
+ $data = $this->data;
1669
+ }
1670
+ update_option($name, $data);
1671
+ if (isset($data['log_level'])) {
1672
+ update_option($name . '_log_level', $data['log_level']);
1673
+ }
1674
+ }
1675
+
1676
+ function js_redirect($url) {
1677
+ echo '<script>';
1678
+ echo 'location.href="' . $url . '"';
1679
+ echo '</script>';
1680
+ die();
1681
+ }
1682
+
1683
+ /**
1684
+ * @deprecated
1685
+ */
1686
+ function get_test_subscribers() {
1687
+ return NewsletterUsers::instance()->get_test_users();
1688
+ }
1689
+
1690
+ /**
1691
+ * Attributes:
1692
+ * weight: [true|false]
1693
+ * color: [true|false]
1694
+ *
1695
+ * @param string $name
1696
+ * @param array $attrs
1697
+ */
1698
+ function css_font($name = 'font', $attrs = array()) {
1699
+ $default = [
1700
+ 'color' => true,
1701
+ 'weight' => true,
1702
+ 'hide_size' => false,
1703
+ 'hide_weight' => false,
1704
+ 'hide_color' => false,
1705
+ ];
1706
+ $attrs = array_merge($default, $attrs);
1707
+ $this->css_font_family($name . '_family', !empty($attrs['family_default']));
1708
+ if (!$attrs['hide_size']) {
1709
+ $this->css_font_size($name . '_size', !empty($attrs['size_default']));
1710
+ }
1711
+ if ($attrs['weight'] && !$attrs['hide_weight']) {
1712
+ $this->css_font_weight($name . '_weight', !empty($attrs['weight_default']));
1713
+ }
1714
+ if ($attrs['color'] && !$attrs['hide_color']) {
1715
+ $this->color($name . '_color');
1716
+ }
1717
+ }
1718
+
1719
+ function css_font_size($name = 'font_size', $show_empty_option = false) {
1720
+ $value = $this->get_value($name);
1721
+
1722
+ echo '<select class="tnpf-font-size" id="options-', esc_attr($name), '" name="options[', esc_attr($name), ']">';
1723
+ if ($show_empty_option) {
1724
+ echo "<option value=''>-</option>";
1725
+ }
1726
+ for ($i = 8; $i <= 50; $i++) {
1727
+ echo '<option value="' . $i . '"';
1728
+ if ($value == $i) {
1729
+ echo ' selected';
1730
+ }
1731
+ echo '>' . $i . '</option>';
1732
+ }
1733
+ echo '</select>';
1734
+ }
1735
+
1736
+ function css_font_weight($name = 'font_weight', $show_empty_option = false) {
1737
+ $value = $this->get_value($name);
1738
+
1739
+ $fonts = array('normal' => 'Normal', 'bold' => 'Bold');
1740
+
1741
+ echo '<select class="tnpf-font-weight" id="options-' . esc_attr($name) . '" name="options[' . esc_attr($name) . ']">';
1742
+ if ($show_empty_option) {
1743
+ echo "<option value=''>-</option>";
1744
+ }
1745
+ foreach ($fonts as $key => $font) {
1746
+ echo '<option value="', esc_attr($key), '"';
1747
+ if ($value == $key) {
1748
+ echo ' selected';
1749
+ }
1750
+ echo '>', esc_html($font), '</option>';
1751
+ }
1752
+ echo '</select>';
1753
+ }
1754
+
1755
+ function css_font_family($name = 'font_family', $show_empty_option = false) {
1756
+ $value = $this->get_value($name);
1757
+
1758
+ $fonts = [];
1759
+ if ($show_empty_option) {
1760
+ $fonts[''] = 'Default';
1761
+ }
1762
+
1763
+ $fonts = array_merge($fonts, ['Helvetica, Arial, sans-serif' => 'Helvetica, Arial',
1764
+ 'Arial Black, Gadget, sans-serif' => 'Arial Black, Gadget',
1765
+ 'Garamond, serif' => 'Garamond',
1766
+ 'Courier, monospace' => 'Courier',
1767
+ 'Comic Sans MS, cursive' => 'Comic Sans MS',
1768
+ 'Impact, Charcoal, sans-serif' => 'Impact, Charcoal',
1769
+ 'Tahoma, Geneva, sans-serif' => 'Tahoma, Geneva',
1770
+ 'Times New Roman, Times, serif' => 'Times New Roman',
1771
+ 'Verdana, Geneva, sans-serif' => 'Verdana, Geneva']);
1772
+
1773
+ echo '<select class="tnpf-font-family" id="options-', esc_attr($name), '" name="options[', esc_attr($name), ']">';
1774
+ foreach ($fonts as $font => $label) {
1775
+ echo '<option value="', esc_attr($font), '"';
1776
+ if ($value == $font) {
1777
+ echo ' selected';
1778
+ }
1779
+ echo '>', esc_html($label), '</option>';
1780
+ }
1781
+ echo '</select>';
1782
+ }
1783
+
1784
+ function css_text_align($name) {
1785
+ $options = array('left' => __('Left', 'newsletter'), 'right' => __('Right', 'newsletter'),
1786
+ 'center' => __('Center', 'newsletter'));
1787
+ $this->select($name, $options);
1788
+ }
1789
+
1790
+ function css_border($name) {
1791
+ $value = $this->get_value($name . '_width');
1792
+
1793
+ echo 'width&nbsp;<select id="options-' . esc_attr($name) . '-width" name="options[' . esc_attr($name) . '_width]">';
1794
+ for ($i = 0; $i < 10; $i++) {
1795
+ echo '<option value="' . $i . '"';
1796
+ if ($value == $i) {
1797
+ echo ' selected';
1798
+ }
1799
+ echo '>' . $i . '</option>';
1800
+ }
1801
+ echo '</select>&nbsp;px&nbsp;&nbsp;';
1802
+
1803
+ $this->select($name . '_type', array('solid' => 'Solid', 'dashed' => 'Dashed'));
1804
+
1805
+ $this->color($name . '_color');
1806
+
1807
+ $value = $this->get_value($name . '_radius');
1808
+
1809
+ echo '&nbsp;&nbsp;radius&nbsp;<select id="options-' . esc_attr($name) . '-radius" name="options[' . esc_attr($name) . '_radius]">';
1810
+ for ($i = 0; $i < 10; $i++) {
1811
+ echo '<option value="' . $i . '"';
1812
+ if ($value == $i) {
1813
+ echo ' selected';
1814
+ }
1815
+ echo '>' . $i . '</option>';
1816
+ }
1817
+ echo '</select>&nbsp;px';
1818
+ }
1819
+
1820
+ /**
1821
+ * Media selector using the media library of WP. Produces a field which values is an array containing 'id' and 'url'.
1822
+ *
1823
+ * @param string $name
1824
+ */
1825
+ function media($name) {
1826
+ if (isset($this->data[$name]['id'])) {
1827
+ $media_id = (int) $this->data[$name]['id'];
1828
+ $media = wp_get_attachment_image_src($media_id, 'medium');
1829
+ $media_full = wp_get_attachment_image_src($media_id, 'full');
1830
+ } else {
1831
+ $media = false;
1832
+ }
1833
+ echo '<div style="position: relative">';
1834
+ echo '<a style="position: absolute; top: 5px; right: 5px; background-color: none; color: #000; padding: 0px 5px 6px 5px; font-size: 24px; display: block; text-decoration: none" href="#" onclick="newsletter_media_remove(\'' . esc_attr($name) . '\'); return false">&times;</a>';
1835
+ if ($media === false) {
1836
+ $media = array('', '', '');
1837
+ $media_full = array('', '', '');
1838
+ $media_id = 0;
1839
+ echo '<img style="max-width: 200px; max-height: 150px; width: 100px;" id="' . esc_attr($name) . '_img" src="' . plugins_url('newsletter') . '/images/nomedia.png" onclick="newsletter_media(\'' . esc_attr($name) . '\')">';
1840
+ } else {
1841
+ echo '<img style="max-width: 200px; max-height: 150px;" id="' . esc_attr($name) . '_img" src="' . esc_attr($media[0]) . '" onclick="newsletter_media(\'' . esc_attr($name) . '\')">';
1842
+ }
1843
+
1844
+ echo '</div>';
1845
+ echo '<input type="hidden" id="' . esc_attr($name) . '_id" name="options[' . esc_attr($name) . '][id]" value="' . esc_attr($media_id) . '" size="5">';
1846
+ echo '<input type="hidden" id="' . esc_attr($name) . '_url" name="options[' . esc_attr($name) . '][url]" value="' . esc_attr($media_full[0]) . '" size="50">';
1847
+ }
1848
+
1849
+ function media_input($option, $name, $label) {
1850
+
1851
+ if (!empty($label)) {
1852
+ $output = '<label class="select" for="tnp_' . esc_attr($name) . '">' . esc_html($label) . ':</label>';
1853
+ }
1854
+ $output .= '<input id="tnp_' . esc_attr($name) . '" type="text" size="36" name="' . esc_attr($option) . '[' . esc_attr($name) . ']" value="' . esc_attr($val) . '" />';
1855
+ $output .= '<input id="tnp_' . esc_attr($name) . '_button" class="button-primary" type="button" value="Select Image" />';
1856
+ $output .= '<br class="clear"/>';
1857
+
1858
+ echo $output;
1859
+ }
1860
+
1861
+ function language($name = 'language', $empty_label = 'All') {
1862
+ if (!class_exists('SitePress') && !function_exists('pll_default_language') && !class_exists('TRP_Translate_Press')) {
1863
+ echo __('Install a multilanguage plugin.', 'newsletter');
1864
+ echo ' <a href="https://www.thenewsletterplugin.com/documentation/multilanguage" target="_blank">', __('Read more', 'newsletter'), '</a>';
1865
+ return;
1866
+ }
1867
+
1868
+ $languages = Newsletter::instance()->get_languages();
1869
+ if (!empty($empty_label)) {
1870
+ $languages = array_merge(array('' => $empty_label), $languages);
1871
+ }
1872
+ $this->select($name, $languages);
1873
+ }
1874
+
1875
+ function is_multilanguage() {
1876
+ return Newsletter::instance()->is_multilanguage();
1877
+ }
1878
+
1879
+ /**
1880
+ * Creates a checkbox group with all active languages. Each checkbox is named
1881
+ * $name[] and values with the relative language code.
1882
+ *
1883
+ * @param string $name
1884
+ */
1885
+ function languages($name = 'languages') {
1886
+ if (!$this->is_multilanguage()) {
1887
+ echo __('Install WPML or Polylang for multilanguage support', 'newsletter');
1888
+ return;
1889
+ }
1890
+
1891
+ $language_options = Newsletter::instance()->get_languages();
1892
+
1893
+ if (empty($language_options)) {
1894
+ echo __('Your multilanguage plugin is not supported or there are no languages defined', 'newsletter');
1895
+ return;
1896
+ }
1897
+
1898
+ $this->checkboxes_group($name, $language_options);
1899
+ }
1900
+
1901
+ /**
1902
+ * Prints a formatted date using the formats and timezone of WP, including the current date and time and the
1903
+ * time left to the passed time.
1904
+ *
1905
+ * @param int $time
1906
+ * @param int $now
1907
+ * @param bool $left
1908
+ * @return string
1909
+ */
1910
+ static function print_date($time = null, $now = false, $left = false) {
1911
+ if (is_null($time)) {
1912
+ $time = time();
1913
+ }
1914
+ if ($time == false) {
1915
+ $buffer = 'none';
1916
+ } else {
1917
+ $buffer = date_i18n(get_option('date_format') . ' ' . get_option('time_format'), $time + get_option('gmt_offset') * 3600);
1918
+
1919
+ if ($now) {
1920
+ $buffer .= ' (now: ' . gmdate(get_option('date_format') . ' ' .
1921
+ get_option('time_format'), time() + get_option('gmt_offset') * 3600);
1922
+ $buffer .= ')';
1923
+ }
1924
+ if ($left) {
1925
+ if ($time - time() < 0) {
1926
+ $buffer .= ', ' . (time() - $time) . ' seconds late';
1927
+ } else {
1928
+ $buffer .= ', ' . gmdate('H:i:s', $time - time()) . ' left';
1929
+ }
1930
+ }
1931
+ }
1932
+ return $buffer;
1933
+ }
1934
+
1935
+ static function delta_time($delta = 0) {
1936
+ $seconds = $delta % 60;
1937
+ $minutes = floor(($delta / 60) % 60);
1938
+ $hours = floor(($delta / (60 * 60)) % 24);
1939
+ $days = floor($delta / (24 * 60 * 60));
1940
+
1941
+ return $days . ' day(s), ' . $hours . ' hour(s), ' . $minutes . ' minute(s)';
1942
+ }
1943
+
1944
+ /**
1945
+ * Prints the help button near a form field. The label is used as icon title.
1946
+ *
1947
+ * @param string $url
1948
+ * @param string $label
1949
+ */
1950
+ static function help($url, $label = '') {
1951
+ echo '<a href="', $url, '" target="_blank" title="', esc_attr($label), '"><i class="fas fa-question-circle"></i></a>';
1952
+ }
1953
+
1954
+ static function idea($url, $label = '') {
1955
+ echo '<a href="', $url, '" target="_blank" title="', esc_attr($label), '"><i class="fas fa-lightbulb-o"></i></a>';
1956
+ }
1957
+
1958
+ static function field_help($url, $text = '') {
1959
+ if (strpos($url, 'http') !== 0) {
1960
+ $url = 'https://www.thenewsletterplugin.com' . $url;
1961
+ }
1962
+ echo '<a href="', $url, '" target="_blank" style="text-decoration: none" title="' . esc_attr(__('Read more', 'newsletter')) . '"><i class="fas fa-question-circle"></i>';
1963
+ if ($text)
1964
+ echo '&nbsp;', $text;
1965
+ echo '</a>';
1966
+ }
1967
+
1968
+ static function field_label($label, $help_url = false) {
1969
+ echo $label;
1970
+ if ($help_url) {
1971
+ echo '&nbsp';
1972
+ self::field_help($help_url);
1973
+ }
1974
+ }
1975
+
1976
+ /**
1977
+ * Prints a panel link to the documentation.
1978
+ *
1979
+ * @param type $url
1980
+ * @param type $text
1981
+ */
1982
+ static function panel_help($url, $text = '') {
1983
+ if (empty($text))
1984
+ $text = __('Need help?', 'newsletter');
1985
+ echo '<span class="tnp-panel-help"><a href="', $url, '" target="_blank">', $text, '</a></span>';
1986
+ }
1987
+
1988
+ /**
1989
+ * Prints an administration page link to the documentation (just under the administration page title.
1990
+ * @param type $url
1991
+ * @param type $text
1992
+ */
1993
+ static function page_help($url, $text = '') {
1994
+ if (empty($text))
1995
+ $text = __('Need help?', 'newsletter');
1996
+ echo '<div class="tnp-page-help"><a href="', $url, '" target="_blank">', $text, '</a></div>';
1997
+ }
1998
+
1999
+ static function print_truncated($text, $size = 50) {
2000
+ if (mb_strlen($text) < $size)
2001
+ return esc_html($text);
2002
+ $sub = mb_substr($text, 0, $size);
2003
+ echo '<span title="', esc_attr($text), '">', esc_html($sub), '...</span>';
2004
+ }
2005
+
2006
+ function block_background($name = 'block_background') {
2007
+ $this->color($name);
2008
+ }
2009
+
2010
+ function block_padding($name = 'block_padding', $options = array()) {
2011
+ echo '<div style="text-align: center; width: 250px;">';
2012
+ $this->text($name . '_top', 5);
2013
+ echo '<br>';
2014
+ $this->text($name . '_left', 5);
2015
+ echo "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
2016
+ $this->text($name . '_right', 5);
2017
+ echo '<br>';
2018
+ $this->text($name . '_bottom', 5);
2019
+ echo '</div>';
2020
+ }
2021
+
2022
+ /**
2023
+ * Adds the fields used by the composer (version 1) in the page form.
2024
+ *
2025
+ * @param type $name
2026
+ */
2027
+ function composer_fields($name = 'body') {
2028
+
2029
+ // body
2030
+ $value = $this->get_value($name);
2031
+
2032
+ // Extracts only the body part
2033
+ $x = strpos($value, '<body');
2034
+ if ($x) {
2035
+ $x = strpos($value, '>', $x);
2036
+ $y = strpos($value, '</body>');
2037
+ $value = substr($value, $x + 1, $y - $x - 1);
2038
+ }
2039
+
2040
+ /* Cleans up uncorrectly stored newsletter bodies */
2041
+ $value = preg_replace('/<style\s+.*?>.*?<\\/style>/is', '', $value);
2042
+ $value = preg_replace('/<meta.*?>/', '', $value);
2043
+ $value = preg_replace('/<title\s+.*?>.*?<\\/title>/i', '', $value);
2044
+ $value = trim($value);
2045
+
2046
+ // Required since esc_html DOES NOT escape the HTML entities (apparently)
2047
+ $value = str_replace('&', '&amp;', $value);
2048
+ $value = str_replace('"', '&quot;', $value);
2049
+ $value = str_replace('<', '&lt;', $value);
2050
+ $value = str_replace('>', '&gt;', $value);
2051
+ echo '<input type="hidden" name="options[', esc_attr($name), ']" id="options-', esc_attr($name), '" value="', esc_attr($value), '">';
2052
+
2053
+ // Used by composer to rebuild the full HTML
2054
+ $css = NewsletterEmails::instance()->get_composer_css();
2055
+ echo '<input type="hidden" name="options[css]" id="options-css" value="', esc_attr($css), '">';
2056
+
2057
+ // subject
2058
+ $value = $this->get_value('subject');
2059
+ echo '<input type="hidden" name="options[subject]" id="options-subject" value="', esc_attr($value), '">';
2060
+ }
2061
+
2062
+ function composer_load($name = 'body', $show_subject = false, $show_test = true, $context_type = '') {
2063
+
2064
+ global $controls;
2065
+ global $tnpc_show_subject;
2066
+ $tnpc_show_subject = $show_subject;
2067
+
2068
+ wp_enqueue_style('tnpc-style', plugins_url('newsletter') . '/emails/tnp-composer/_css/newsletter-builder.css', array(), time());
2069
+
2070
+ include NEWSLETTER_DIR . '/emails/tnp-composer/index.php';
2071
+ }
2072
+
2073
+ /**
2074
+ * Adds the fields used by the composer (version 2) in the page form.
2075
+ */
2076
+ function composer_fields_v2($name = 'message') {
2077
+
2078
+ // The composer, on saving, fills in those fields
2079
+ $this->hidden('subject');
2080
+ $this->hidden('message');
2081
+ $this->hidden('options_preheader');
2082
+ $this->hidden('updated');
2083
+
2084
+ //$preheader_value = $this->get_value('options_preheader');
2085
+ // echo '<input name="options[preheader]" id="options-preheader" type="hidden" value="', esc_attr($preheader_value), '">';
2086
+ }
2087
+
2088
+ function composer_load_v2($show_subject = false, $show_test = true, $context_type = '') {
2089
+
2090
+ global $tnpc_show_subject;
2091
+ $tnpc_show_subject = $show_subject;
2092
+
2093
+ echo "<link href='", plugins_url('newsletter'), "/emails/tnp-composer/_css/newsletter-builder-v2.css?ver=" . NEWSLETTER_VERSION . "' rel='stylesheet' type='text/css'>";
2094
+
2095
+ $controls = $this;
2096
+ include NEWSLETTER_DIR . '/emails/tnp-composer/index-v2.php';
2097
+ }
2098
+
2099
+ function subject($name) {
2100
+ $value = $this->get_value($name);
2101
+ // Leave the ID with this prefix!
2102
+ echo '<div style="position: relative"><input size="80" id="options-subject-', esc_attr($name), '" name="options[' . esc_attr($name) . ']" type="text" placeholder="" value="';
2103
+ echo esc_attr($value);
2104
+ echo '">';
2105
+ echo '&nbsp;<i class="far fa-lightbulb tnp-suggest-subject" data-tnp-modal-target="#subject-ideas-modal"></i>';
2106
+
2107
+ echo '<img src="', plugins_url('newsletter'), '/admin/images/subject/android.png" style="position: absolute; width: 16px; left: 330px; top: 25px; display: block; opacity: 0">';
2108
+ echo '<img src="', plugins_url('newsletter'), '/admin/images/subject/iphone.png" style="position: absolute; width: 16px; left: 380px; top: 25px; display: block; opacity: 0">';
2109
+ echo '</div>';
2110
+ }
2111
+
2112
+ }
includes/helper.php CHANGED
@@ -1,417 +1,417 @@
1
- <?php
2
-
3
- defined('ABSPATH') || exit;
4
-
5
- function tnp_post_thumbnail_src($post, $size = 'thumbnail', $alternative = '') {
6
- if (is_object($post)) {
7
- $post = $post->ID;
8
- }
9
-
10
- // Find a media id to be used as featured image
11
- $media_id = get_post_thumbnail_id($post);
12
- if (empty($media_id)) {
13
- $attachments = get_children(array('numberpost' => 1, 'post_parent' => $post, 'post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => 'image', 'order' => 'ASC', 'orderby' => 'menu_order'));
14
- if (!empty($attachments)) {
15
- foreach ($attachments as $id => &$attachment) {
16
- $media_id = $id;
17
- break;
18
- }
19
- }
20
- }
21
-
22
- if (!$media_id) {
23
- return $alternative;
24
- }
25
-
26
- if (!defined('NEWSLETTER_MEDIA_RESIZE') || NEWSLETTER_MEDIA_RESIZE) {
27
- if (is_array($size)) {
28
- $src = tnp_media_resize($media_id, $size);
29
- if (is_wp_error($src)) {
30
- Newsletter::instance()->logger->error($src);
31
- return $alternative;
32
- } else {
33
- return $src;
34
- }
35
- }
36
- }
37
-
38
- $media = wp_get_attachment_image_src($media_id, $size);
39
- if (strpos($media[0], 'http') !== 0) {
40
- $media[0] = 'http:' . $media[0];
41
- }
42
- return $media[0];
43
- }
44
-
45
- /**
46
- * @param WP_Post $post
47
- * @param int $length
48
- *
49
- * @return string
50
- */
51
- function tnp_post_excerpt($post, $length = 30, $characters = false) {
52
- if (!$length) return '';
53
-
54
- $excerpt = get_the_excerpt($post->ID);
55
- $excerpt = tnp_delete_all_shordcodes_tags($excerpt);
56
- $excerpt = trim($excerpt);
57
- $excerpt = str_replace('&nbsp;', '', $excerpt);
58
-
59
- if ($characters) {
60
- if (strlen($excerpt) > $length) {
61
- $excerpt = substr($excerpt, 0, $length);
62
- $i = strrpos($excerpt, ' ');
63
- if ($i) {
64
- $excerpt = substr($excerpt, 0, $i);
65
- $excerpt .= '&hellip;';
66
- }
67
- }
68
- } else {
69
- $excerpt = wp_trim_words($excerpt, $length);
70
- }
71
-
72
- return $excerpt;
73
- }
74
-
75
- function tnp_delete_all_shordcodes_tags($post_content = '') {
76
- //Delete open tags
77
- $post_content = preg_replace("/\[[a-zA-Z0-9_-]*?(\s.*?)?\]/", '', $post_content);
78
- //Delete close tags
79
- $post_content = preg_replace("/\[\/[a-zA-Z0-9_-]*?\]/", '', $post_content);
80
-
81
- return $post_content;
82
- }
83
-
84
- function tnp_post_permalink($post) {
85
- return get_permalink($post->ID);
86
- }
87
-
88
- function tnp_post_content($post) {
89
- return $post->post_content;
90
- }
91
-
92
- function tnp_post_title($post) {
93
- return $post->post_title;
94
- }
95
-
96
- function tnp_post_date($post, $format = null) {
97
- if (empty($format)) {
98
- $format = get_option('date_format');
99
- }
100
- return mysql2date($format, $post->post_date);
101
- }
102
-
103
- /**
104
- * Tries to create a resized version of a media uploaded to the media library.
105
- * Returns an empty string if the media does not exists or generally if the attached file
106
- * cannot be found. If the resize fails for whatever reason, fall backs to the
107
- * standard image source returned by WP which is usually not exactly the
108
- * requested size.
109
- *
110
- * @param int $media_id
111
- * @param array $size
112
- * @return string
113
- */
114
- function tnp_media_resize($media_id, $size) {
115
- if (empty($media_id)) {
116
- return '';
117
- }
118
-
119
- $relative_file = get_post_meta($media_id, '_wp_attached_file', true);
120
- if (empty($relative_file)) {
121
- return '';
122
- }
123
-
124
- $width = $size[0];
125
- $height = $size[1];
126
- $crop = false;
127
- if (isset($size[2])) {
128
- $crop = (boolean) $size[2];
129
- }
130
-
131
- $uploads = wp_upload_dir();
132
-
133
- // Based on _wp_relative_upload_path() function for blog which store the
134
- // full patch of media files
135
- if (0 === strpos($relative_file, $uploads['basedir'])) {
136
- $relative_file = str_replace($uploads['basedir'], '', $relative_file);
137
- $relative_file = ltrim($relative_file, '/');
138
- }
139
-
140
- $absolute_file = $uploads['basedir'] . '/' . $relative_file;
141
- // Relative and absolute name of the thumbnail.
142
- $pathinfo = pathinfo($relative_file);
143
-
144
- // We don't know why, but on some systems files with non-ascii characters loose the file name (grrr...)
145
- if (empty($pathinfo['filename'])) {
146
- $src = wp_get_attachment_image_src($media_id, 'full');
147
- return $src[0];
148
- }
149
-
150
- $relative_thumb = $pathinfo['dirname'] . '/' . $pathinfo['filename'] . '-' . $width . 'x' .
151
- $height . ($crop ? '-c' : '') . '.' . $pathinfo['extension'];
152
- $absolute_thumb = $uploads['basedir'] . '/newsletter/thumbnails/' . $relative_thumb;
153
-
154
- // Thumbnail generation if needed.
155
- if (!file_exists($absolute_thumb) || filemtime($absolute_thumb) < filemtime($absolute_file)) {
156
- $r = wp_mkdir_p($uploads['basedir'] . '/newsletter/thumbnails/' . $pathinfo['dirname']);
157
-
158
- if (!$r) {
159
- $src = wp_get_attachment_image_src($media_id, 'full');
160
- return $src[0];
161
- }
162
-
163
- $editor = wp_get_image_editor($absolute_file);
164
- if (is_wp_error($editor)) {
165
- $src = wp_get_attachment_image_src($media_id, 'full');
166
- return $src[0];
167
- //return $editor;
168
- //return $uploads['baseurl'] . '/' . $relative_file;
169
- }
170
-
171
- $original_size = $editor->get_size();
172
- if ($width > $original_size['width'] || $height > $original_size['height']) {
173
- $src = wp_get_attachment_image_src($media_id, 'full');
174
- return $src[0];
175
- }
176
-
177
- $editor->set_quality(80);
178
- $resized = $editor->resize($width, $height, $crop);
179
-
180
- if (is_wp_error($resized)) {
181
- $src = wp_get_attachment_image_src($media_id, 'full');
182
- return $src[0];
183
- }
184
-
185
- $saved = $editor->save($absolute_thumb);
186
- if (is_wp_error($saved)) {
187
- $src = wp_get_attachment_image_src($media_id, 'full');
188
- return $src[0];
189
- //return $saved;
190
- //return $uploads['baseurl'] . '/' . $relative_file;
191
- }
192
- }
193
-
194
- return $uploads['baseurl'] . '/newsletter/thumbnails/' . $relative_thumb;
195
- }
196
-
197
- function _tnp_get_default_media($media_id, $size) {
198
-
199
- $src = wp_get_attachment_image_src($media_id, $size);
200
- if (!$src) {
201
- return null;
202
- }
203
- $media = new TNP_Media();
204
- $media->id = $media_id;
205
- $media->url = $src[0];
206
- $media->width = $src[1];
207
- $media->height = $src[2];
208
- return $media;
209
- }
210
-
211
- function tnp_get_media($media_id, $size) {
212
- $src = wp_get_attachment_image_src($media_id, $size);
213
- if (!$src) {
214
- return null;
215
- }
216
- $media = new TNP_Media();
217
- $media->id = $media_id;
218
- $media->url = $src[0];
219
- $media->width = $src[1];
220
- $media->height = $src[2];
221
- return $media;
222
- }
223
-
224
- /**
225
- * Create a resized version of the media stored in the WP media library.
226
- *
227
- * @param int $media_id
228
- * @param array $size
229
- * @return TNP_Media
230
- */
231
- function tnp_resize($media_id, $size) {
232
- if (empty($media_id)) {
233
- return null;
234
- }
235
-
236
- $relative_file = get_post_meta($media_id, '_wp_attached_file', true);
237
-
238
- if (empty($relative_file)) {
239
- return null;
240
- }
241
-
242
- $uploads = wp_upload_dir();
243
-
244
- // Based on _wp_relative_upload_path() function for blog which store the
245
- // full path of media files
246
- if (0 === strpos($relative_file, $uploads['basedir'])) {
247
- $relative_file = str_replace($uploads['basedir'], '', $relative_file);
248
- $relative_file = ltrim($relative_file, '/');
249
- }
250
-
251
- $width = $size[0];
252
- $height = $size[1];
253
- $crop = false;
254
- if (isset($size[2])) {
255
- $crop = (boolean) $size[2];
256
- }
257
-
258
- $absolute_file = $uploads['basedir'] . '/' . $relative_file;
259
-
260
- if (substr($relative_file, -4) === '.gif') {
261
- $editor = wp_get_image_editor($absolute_file);
262
- if (is_wp_error($editor)) {
263
- return _tnp_get_default_media($media_id, $size);
264
- }
265
- $new_size = $editor->get_size();
266
- $media = new TNP_Media();
267
- $media->id = $media_id;
268
- $media->width = $new_size['width'];
269
- $media->height = $new_size['height'];
270
- if ($media->width > $width) {
271
- $media->set_width($width);
272
- }
273
- $media->url = $uploads['baseurl'] . '/' . $relative_file;
274
- return $media;
275
- }
276
-
277
- // Relative and absolute name of the thumbnail.
278
- $pathinfo = pathinfo($relative_file);
279
-
280
- // We don't know why, but on some systems files with non-ascii characters loose the file name (grrr...)
281
- if (empty($pathinfo['filename'])) {
282
- return _tnp_get_default_media($media_id, $size);
283
- }
284
-
285
- $relative_thumb = $pathinfo['dirname'] . '/' . $pathinfo['filename'] . '-' . $width . 'x' . $height . ($crop ? '-c' : '') . '.' . $pathinfo['extension'];
286
- $absolute_thumb = $uploads['basedir'] . '/newsletter/thumbnails/' . $relative_thumb;
287
-
288
- // Thumbnail generation if needed.
289
- if (!file_exists($absolute_thumb) || filemtime($absolute_thumb) < filemtime($absolute_file)) {
290
- $r = wp_mkdir_p($uploads['basedir'] . '/newsletter/thumbnails/' . $pathinfo['dirname']);
291
-
292
- if (!$r) {
293
- Newsletter::instance()->logger->error('Unable to create dir ' . $uploads['basedir'] . '/newsletter/thumbnails/' . $pathinfo['dirname']);
294
- return _tnp_get_default_media($media_id, $size);
295
- }
296
-
297
- $editor = wp_get_image_editor($absolute_file);
298
- if (is_wp_error($editor)) {
299
- Newsletter::instance()->logger->error($editor);
300
- Newsletter::instance()->logger->error('File: ' . $absolute_file);
301
- return _tnp_get_default_media($media_id, $size);
302
- }
303
-
304
- $original_size = $editor->get_size();
305
- if ($width > $original_size['width'] && ($height > $original_size['height'] || $height == 0)) {
306
- Newsletter::instance()->logger->error('Requested size larger than the original one');
307
- return _tnp_get_default_media($media_id, $size);
308
- }
309
-
310
- if ($height > $original_size['height'] && ($width > $original_size['width'] || $width == 0)) {
311
- Newsletter::instance()->logger->error('Requested size larger than the original one');
312
- return _tnp_get_default_media($media_id, $size);
313
- }
314
-
315
- $editor->set_quality(85);
316
- $resized = $editor->resize($width, $height, $crop);
317
-
318
- if (is_wp_error($resized)) {
319
- Newsletter::instance()->logger->error($resized);
320
- Newsletter::instance()->logger->error('File: ' . $absolute_file);
321
- return _tnp_get_default_media($media_id, $size);
322
- }
323
-
324
- $saved = $editor->save($absolute_thumb);
325
- if (is_wp_error($saved)) {
326
- Newsletter::instance()->logger->error($saved);
327
- return _tnp_get_default_media($media_id, $size);
328
- }
329
- $new_size = $editor->get_size();
330
-
331
- $media = new TNP_Media();
332
- $media->width = $new_size['width'];
333
- $media->height = $new_size['height'];
334
- $media->url = $uploads['baseurl'] . '/newsletter/thumbnails/' . $relative_thumb;
335
- } else {
336
- $media = new TNP_Media();
337
- $new_size = getimagesize($absolute_thumb);
338
- $media->width = $new_size[0];
339
- $media->height = $new_size[1];
340
- $media->url = $uploads['baseurl'] . '/newsletter/thumbnails/' . $relative_thumb;
341
- }
342
-
343
- return $media;
344
- }
345
-
346
- function tnp_resize_2x($media_id, $size) {
347
- $size[0] = $size[0] * 2;
348
- $size[1] = $size[1] * 2;
349
- $media = tnp_resize($media_id, $size);
350
- if (!$media)
351
- return $media;
352
- $media->set_width($size[0] / 2);
353
- return $media;
354
- }
355
-
356
- /**
357
- * @param TNP_Media[] $images
358
- *
359
- * @return int
360
- */
361
- function tnp_get_max_height_of($images) {
362
- $max_height = 0;
363
- foreach ($images as $image) {
364
- $max_height = $image->height > $max_height ? $image->height : $max_height;
365
- }
366
-
367
- return $max_height;
368
- }
369
-
370
- /**
371
- * @param WP_Post[] $product_list
372
- * @param array $size
373
- *
374
- * @return TNP_Media[]
375
- */
376
- function tnp_resize_product_list_featured_image($product_list, $size) {
377
- $images = [];
378
- foreach ($product_list as $p) {
379
- $images[$p->ID] = tnp_resize_2x(TNP_Composer::get_post_thumbnail_id($p->ID), $size);
380
- }
381
-
382
- return $images;
383
- }
384
-
385
- /**
386
- * Get media for "posts" composer block
387
- *
388
- * @param WP_Post post
389
- * @param array $size
390
- * @param string $default_image_url
391
- *
392
- * @return TNP_Media
393
- */
394
- function tnp_composer_block_posts_get_media($post, $size, $default_image_url = null) {
395
- $post_thumbnail_id = TNP_Composer::get_post_thumbnail_id($post);
396
-
397
- $media = null;
398
-
399
- if (!empty($post_thumbnail_id)) {
400
- $media = tnp_resize($post_thumbnail_id, array_values($size));
401
- } else if ($default_image_url) {
402
- Newsletter::instance()->logger->error('Thumbnail id not found');
403
- $media = new TNP_Media();
404
- $media->url = $default_image_url;
405
- $media->width = $size['width'];
406
- $media->height = $size['height'];
407
- }
408
- return $media;
409
- }
410
-
411
- function tnp_outlook_wrapper_open($width = 600) {
412
- return NewsletterEmails::get_outlook_wrapper_open($width);
413
- }
414
-
415
- function tnp_outlook_wrapper_close() {
416
- return NewsletterEmails::get_outlook_wrapper_close();
417
- }
1
+ <?php
2
+
3
+ defined('ABSPATH') || exit;
4
+
5
+ function tnp_post_thumbnail_src($post, $size = 'thumbnail', $alternative = '') {
6
+ if (is_object($post)) {
7
+ $post = $post->ID;
8
+ }
9
+
10
+ // Find a media id to be used as featured image
11
+ $media_id = get_post_thumbnail_id($post);
12
+ if (empty($media_id)) {
13
+ $attachments = get_children(array('numberpost' => 1, 'post_parent' => $post, 'post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => 'image', 'order' => 'ASC', 'orderby' => 'menu_order'));
14
+ if (!empty($attachments)) {
15
+ foreach ($attachments as $id => &$attachment) {
16
+ $media_id = $id;
17
+ break;
18
+ }
19
+ }
20
+ }
21
+
22
+ if (!$media_id) {
23
+ return $alternative;
24
+ }
25
+
26
+ if (!defined('NEWSLETTER_MEDIA_RESIZE') || NEWSLETTER_MEDIA_RESIZE) {
27
+ if (is_array($size)) {
28
+ $src = tnp_media_resize($media_id, $size);
29
+ if (is_wp_error($src)) {
30
+ Newsletter::instance()->logger->error($src);
31
+ return $alternative;
32
+ } else {
33
+ return $src;
34
+ }
35
+ }
36
+ }
37
+
38
+ $media = wp_get_attachment_image_src($media_id, $size);
39
+ if (strpos($media[0], 'http') !== 0) {
40
+ $media[0] = 'http:' . $media[0];
41
+ }
42
+ return $media[0];
43
+ }
44
+
45
+ /**
46
+ * @param WP_Post $post
47
+ * @param int $length
48
+ *
49
+ * @return string
50
+ */
51
+ function tnp_post_excerpt($post, $length = 30, $characters = false) {
52
+ if (!$length) return '';
53
+
54
+ $excerpt = get_the_excerpt($post->ID);
55
+ $excerpt = tnp_delete_all_shordcodes_tags($excerpt);
56
+ $excerpt = trim($excerpt);
57
+ $excerpt = str_replace('&nbsp;', '', $excerpt);
58
+
59
+ if ($characters) {
60
+ if (strlen($excerpt) > $length) {
61
+ $excerpt = substr($excerpt, 0, $length);
62
+ $i = strrpos($excerpt, ' ');
63
+ if ($i) {
64
+ $excerpt = substr($excerpt, 0, $i);
65
+ $excerpt .= '&hellip;';
66
+ }
67
+ }
68
+ } else {
69
+ $excerpt = wp_trim_words($excerpt, $length);
70
+ }
71
+
72
+ return $excerpt;
73
+ }
74
+
75
+ function tnp_delete_all_shordcodes_tags($post_content = '') {
76
+ //Delete open tags
77
+ $post_content = preg_replace("/\[[a-zA-Z0-9_-]*?(\s.*?)?\]/", '', $post_content);
78
+ //Delete close tags
79
+ $post_content = preg_replace("/\[\/[a-zA-Z0-9_-]*?\]/", '', $post_content);
80
+
81
+ return $post_content;
82
+ }
83
+
84
+ function tnp_post_permalink($post) {
85
+ return get_permalink($post->ID);
86
+ }
87
+
88
+ function tnp_post_content($post) {
89
+ return $post->post_content;
90
+ }
91
+
92
+ function tnp_post_title($post) {
93
+ return $post->post_title;
94
+ }
95
+
96
+ function tnp_post_date($post, $format = null) {
97
+ if (empty($format)) {
98
+ $format = get_option('date_format');
99
+ }
100
+ return mysql2date($format, $post->post_date);
101
+ }
102
+
103
+ /**
104
+ * Tries to create a resized version of a media uploaded to the media library.
105
+ * Returns an empty string if the media does not exists or generally if the attached file
106
+ * cannot be found. If the resize fails for whatever reason, fall backs to the
107
+ * standard image source returned by WP which is usually not exactly the
108
+ * requested size.
109
+ *
110
+ * @param int $media_id
111
+ * @param array $size
112
+ * @return string
113
+ */
114
+ function tnp_media_resize($media_id, $size) {
115
+ if (empty($media_id)) {
116
+ return '';
117
+ }
118
+
119
+ $relative_file = get_post_meta($media_id, '_wp_attached_file', true);
120
+ if (empty($relative_file)) {
121
+ return '';
122
+ }
123
+
124
+ $width = $size[0];
125
+ $height = $size[1];
126
+ $crop = false;
127
+ if (isset($size[2])) {
128
+ $crop = (boolean) $size[2];
129
+ }
130
+
131
+ $uploads = wp_upload_dir();
132
+
133
+ // Based on _wp_relative_upload_path() function for blog which store the
134
+ // full patch of media files
135
+ if (0 === strpos($relative_file, $uploads['basedir'])) {
136
+ $relative_file = str_replace($uploads['basedir'], '', $relative_file);
137
+ $relative_file = ltrim($relative_file, '/');
138
+ }
139
+
140
+ $absolute_file = $uploads['basedir'] . '/' . $relative_file;
141
+ // Relative and absolute name of the thumbnail.
142
+ $pathinfo = pathinfo($relative_file);
143
+
144
+ // We don't know why, but on some systems files with non-ascii characters loose the file name (grrr...)
145
+ if (empty($pathinfo['filename'])) {
146
+ $src = wp_get_attachment_image_src($media_id, 'full');
147
+ return $src[0];
148
+ }
149
+
150
+ $relative_thumb = $pathinfo['dirname'] . '/' . $pathinfo['filename'] . '-' . $width . 'x' .
151
+ $height . ($crop ? '-c' : '') . '.' . $pathinfo['extension'];
152
+ $absolute_thumb = $uploads['basedir'] . '/newsletter/thumbnails/' . $relative_thumb;
153
+
154
+ // Thumbnail generation if needed.
155
+ if (!file_exists($absolute_thumb) || filemtime($absolute_thumb) < filemtime($absolute_file)) {
156
+ $r = wp_mkdir_p($uploads['basedir'] . '/newsletter/thumbnails/' . $pathinfo['dirname']);
157
+
158
+ if (!$r) {
159
+ $src = wp_get_attachment_image_src($media_id, 'full');
160
+ return $src[0];
161
+ }
162
+
163
+ $editor = wp_get_image_editor($absolute_file);
164
+ if (is_wp_error($editor)) {
165
+ $src = wp_get_attachment_image_src($media_id, 'full');
166
+ return $src[0];
167
+ //return $editor;
168
+ //return $uploads['baseurl'] . '/' . $relative_file;
169
+ }
170
+
171
+ $original_size = $editor->get_size();
172
+ if ($width > $original_size['width'] || $height > $original_size['height']) {
173
+ $src = wp_get_attachment_image_src($media_id, 'full');
174
+ return $src[0];
175
+ }
176
+
177
+ $editor->set_quality(80);
178
+ $resized = $editor->resize($width, $height, $crop);
179
+
180
+ if (is_wp_error($resized)) {
181
+ $src = wp_get_attachment_image_src($media_id, 'full');
182
+ return $src[0];
183
+ }
184
+
185
+ $saved = $editor->save($absolute_thumb);
186
+ if (is_wp_error($saved)) {
187
+ $src = wp_get_attachment_image_src($media_id, 'full');
188
+ return $src[0];
189
+ //return $saved;
190
+ //return $uploads['baseurl'] . '/' . $relative_file;
191
+ }
192
+ }
193
+
194
+ return $uploads['baseurl'] . '/newsletter/thumbnails/' . $relative_thumb;
195
+ }
196
+
197
+ function _tnp_get_default_media($media_id, $size) {
198
+
199
+ $src = wp_get_attachment_image_src($media_id, $size);
200
+ if (!$src) {
201
+ return null;
202
+ }
203
+ $media = new TNP_Media();
204
+ $media->id = $media_id;
205
+ $media->url = $src[0];
206
+ $media->width = $src[1];
207
+ $media->height = $src[2];
208
+ return $media;
209
+ }
210
+
211
+ function tnp_get_media($media_id, $size) {
212
+ $src = wp_get_attachment_image_src($media_id, $size);
213
+ if (!$src) {
214
+ return null;
215
+ }
216
+ $media = new TNP_Media();
217
+ $media->id = $media_id;
218
+ $media->url = $src[0];
219
+ $media->width = $src[1];
220
+ $media->height = $src[2];
221
+ return $media;
222
+ }
223
+
224
+ /**
225
+ * Create a resized version of the media stored in the WP media library.
226
+ *
227
+ * @param int $media_id
228
+ * @param array $size
229
+ * @return TNP_Media
230
+ */
231
+ function tnp_resize($media_id, $size) {
232
+ if (empty($media_id)) {
233
+ return null;
234
+ }
235
+
236
+ $relative_file = get_post_meta($media_id, '_wp_attached_file', true);
237
+
238
+ if (empty($relative_file)) {
239
+ return null;
240
+ }
241
+
242
+ $uploads = wp_upload_dir();
243
+
244
+ // Based on _wp_relative_upload_path() function for blog which store the
245
+ // full path of media files
246
+ if (0 === strpos($relative_file, $uploads['basedir'])) {
247
+ $relative_file = str_replace($uploads['basedir'], '', $relative_file);
248
+ $relative_file = ltrim($relative_file, '/');
249
+ }
250
+
251
+ $width = $size[0];
252
+ $height = $size[1];
253
+ $crop = false;
254
+ if (isset($size[2])) {
255
+ $crop = (boolean) $size[2];
256
+ }
257
+
258
+ $absolute_file = $uploads['basedir'] . '/' . $relative_file;
259
+
260
+ if (substr($relative_file, -4) === '.gif') {
261
+ $editor = wp_get_image_editor($absolute_file);
262
+ if (is_wp_error($editor)) {
263
+ return _tnp_get_default_media($media_id, $size);
264
+ }
265
+ $new_size = $editor->get_size();
266
+ $media = new TNP_Media();
267
+ $media->id = $media_id;
268
+ $media->width = $new_size['width'];
269
+ $media->height = $new_size['height'];
270
+ if ($media->width > $width) {
271
+ $media->set_width($width);
272
+ }
273
+ $media->url = $uploads['baseurl'] . '/' . $relative_file;
274
+ return $media;
275
+ }
276
+
277
+ // Relative and absolute name of the thumbnail.
278
+ $pathinfo = pathinfo($relative_file);
279
+
280
+ // We don't know why, but on some systems files with non-ascii characters loose the file name (grrr...)
281
+ if (empty($pathinfo['filename'])) {
282
+ return _tnp_get_default_media($media_id, $size);
283
+ }
284
+
285
+ $relative_thumb = $pathinfo['dirname'] . '/' . $pathinfo['filename'] . '-' . $width . 'x' . $height . ($crop ? '-c' : '') . '.' . $pathinfo['extension'];
286
+ $absolute_thumb = $uploads['basedir'] . '/newsletter/thumbnails/' . $relative_thumb;
287
+
288
+ // Thumbnail generation if needed.
289
+ if (!file_exists($absolute_thumb) || filemtime($absolute_thumb) < filemtime($absolute_file)) {
290
+ $r = wp_mkdir_p($uploads['basedir'] . '/newsletter/thumbnails/' . $pathinfo['dirname']);
291
+
292
+ if (!$r) {
293
+ Newsletter::instance()->logger->error('Unable to create dir ' . $uploads['basedir'] . '/newsletter/thumbnails/' . $pathinfo['dirname']);
294
+ return _tnp_get_default_media($media_id, $size);
295
+ }
296
+
297
+ $editor = wp_get_image_editor($absolute_file);
298
+ if (is_wp_error($editor)) {
299
+ Newsletter::instance()->logger->error($editor);
300
+ Newsletter::instance()->logger->error('File: ' . $absolute_file);
301
+ return _tnp_get_default_media($media_id, $size);
302
+ }
303
+
304
+ $original_size = $editor->get_size();
305
+ if ($width > $original_size['width'] && ($height > $original_size['height'] || $height == 0)) {
306
+ Newsletter::instance()->logger->error('Requested size larger than the original one');
307
+ return _tnp_get_default_media($media_id, $size);
308
+ }
309
+
310
+ if ($height > $original_size['height'] && ($width > $original_size['width'] || $width == 0)) {
311
+ Newsletter::instance()->logger->error('Requested size larger than the original one');
312
+ return _tnp_get_default_media($media_id, $size);
313
+ }
314
+
315
+ $editor->set_quality(85);
316
+ $resized = $editor->resize($width, $height, $crop);
317
+
318
+ if (is_wp_error($resized)) {
319
+ Newsletter::instance()->logger->error($resized);
320
+ Newsletter::instance()->logger->error('File: ' . $absolute_file);
321
+ return _tnp_get_default_media($media_id, $size);
322
+ }
323
+
324
+ $saved = $editor->save($absolute_thumb);
325
+ if (is_wp_error($saved)) {
326
+ Newsletter::instance()->logger->error($saved);
327
+ return _tnp_get_default_media($media_id, $size);
328
+ }
329
+ $new_size = $editor->get_size();
330
+
331
+ $media = new TNP_Media();
332
+ $media->width = $new_size['width'];
333
+ $media->height = $new_size['height'];
334
+ $media->url = $uploads['baseurl'] . '/newsletter/thumbnails/' . $relative_thumb;
335
+ } else {
336
+ $media = new TNP_Media();
337
+ $new_size = getimagesize($absolute_thumb);
338
+ $media->width = $new_size[0];
339
+ $media->height = $new_size[1];
340
+ $media->url = $uploads['baseurl'] . '/newsletter/thumbnails/' . $relative_thumb;
341
+ }
342
+
343
+ return $media;
344
+ }
345
+
346
+ function tnp_resize_2x($media_id, $size) {
347
+ $size[0] = $size[0] * 2;
348
+ $size[1] = $size[1] * 2;
349
+ $media = tnp_resize($media_id, $size);
350
+ if (!$media)
351
+ return $media;
352
+ $media->set_width($size[0] / 2);
353
+ return $media;
354
+ }
355
+
356
+ /**
357
+ * @param TNP_Media[] $images
358
+ *
359
+ * @return int
360
+ */
361
+ function tnp_get_max_height_of($images) {
362
+ $max_height = 0;
363
+ foreach ($images as $image) {
364
+ $max_height = $image->height > $max_height ? $image->height : $max_height;
365
+ }
366
+
367
+ return $max_height;
368
+ }
369
+
370
+ /**
371
+ * @param WP_Post[] $product_list
372
+ * @param array $size
373
+ *
374
+ * @return TNP_Media[]
375
+ */
376
+ function tnp_resize_product_list_featured_image($product_list, $size) {
377
+ $images = [];
378
+ foreach ($product_list as $p) {
379
+ $images[$p->ID] = tnp_resize_2x(TNP_Composer::get_post_thumbnail_id($p->ID), $size);
380
+ }
381
+
382
+ return $images;
383
+ }
384
+
385
+ /**
386
+ * Get media for "posts" composer block
387
+ *
388
+ * @param WP_Post post
389
+ * @param array $size
390
+ * @param string $default_image_url
391
+ *
392
+ * @return TNP_Media
393
+ */
394
+ function tnp_composer_block_posts_get_media($post, $size, $default_image_url = null) {
395
+ $post_thumbnail_id = TNP_Composer::get_post_thumbnail_id($post);
396
+
397
+ $media = null;
398
+
399
+ if (!empty($post_thumbnail_id)) {
400
+ $media = tnp_resize($post_thumbnail_id, array_values($size));
401
+ } else if ($default_image_url) {
402
+ Newsletter::instance()->logger->error('Thumbnail id not found');
403
+ $media = new TNP_Media();
404
+ $media->url = $default_image_url;
405
+ $media->width = $size['width'];
406
+ $media->height = $size['height'];
407
+ }
408
+ return $media;
409
+ }
410
+
411
+ function tnp_outlook_wrapper_open($width = 600) {
412
+ return NewsletterEmails::get_outlook_wrapper_open($width);
413
+ }
414
+
415
+ function tnp_outlook_wrapper_close() {
416
+ return NewsletterEmails::get_outlook_wrapper_close();
417
+ }
includes/mailer.php CHANGED
@@ -1,470 +1,470 @@
1
- <?php
2
-
3
- use TNP\Mailer\PHPMailerLoader;
4
-
5
- /**
6
- * @property string $to
7
- * @property string $to_name
8
- * @property string $subject
9
- * @property string $body
10
- * @property array $headers
11
- * @property string $from
12
- * @property string $from_name
13
- */
14
- class TNP_Mailer_Message {
15
-
16
- var $to_name = '';
17
- var $headers = array();
18
- var $user_id = 0;
19
- var $email_id = 0;
20
- var $error = '';
21
- var $subject = '';
22
- var $body = '';
23
- var $body_text = '';
24
- var $from = '';
25
- var $from_name = '';
26
-
27
- }
28
-
29
- /**
30
- * A basic class able to send one or more TNP_Mailer_Message objects using a
31
- * delivery method (wp-mail(), SMTP, API, ...).
32
- */
33
- class NewsletterMailer {
34
-
35
- const ERROR_GENERIC = '1';
36
- const ERROR_FATAL = '2';
37
-
38
- /* @var NewsletterLogger */
39
-
40
- var $logger;
41
- var $name;
42
- var $options;
43
- private $delta;
44
- protected $batch_size = 1;
45
- protected $speed = 0;
46
-
47
- public function __construct($name, $options = []) {
48
- $this->name = $name;
49
- $this->options = $options;
50
- if (!empty($this->options['speed'])) {
51
- $this->speed = max(0, (int)$this->options['speed']);
52
- }
53
- if (!empty($this->options['turbo'])) {
54
- $this->batch_size = max(1, (int)$this->options['turbo']);
55
- }
56
- $this->get_logger()->debug($options);
57
- }
58
-
59
- public function get_name() {
60
- return $this->name;
61
- }
62
-
63
- public function get_description() {
64
- return ucfirst($this->name) . ' Addon';
65
- }
66
-
67
- public function get_batch_size() {
68
- return $this->batch_size;
69
- }
70
-
71
- public function get_speed() {
72
- return $this->speed;
73
- }
74
-
75
- function send_with_stats($message) {
76
- $this->delta = microtime(true);
77
- $r = $this->send($message);
78
- $this->delta = microtime(true) - $this->delta;
79
- return $r;
80
- }
81
-
82
- /**
83
- *
84
- * @param TNP_Mailer_Message $message
85
- * @return bool|WP_Error
86
- */
87
- public function send($message) {
88
- $message->error = 'No mailing system available';
89
- return new WP_Error(self::ERROR_FATAL, 'No mailing system available');
90
- }
91
-
92
- public function send_batch_with_stats($messages) {
93
- $this->delta = microtime(true);
94
- $r = $this->send_batch($messages);
95
- $this->delta = microtime(true) - $this->delta;
96
- return $r;
97
- }
98
-
99
- function get_capability() {
100
- return (int) (3600 * $this->batch_size / $this->delta);
101
- }
102
-
103
- /**
104
- *
105
- * @param TNP_Mailer_Message[] $messages
106
- * @return bool|WP_Error
107
- */
108
- public function send_batch($messages) {
109
-
110
- // We should not get there is the batch size is one, the caller should use "send()". We can get
111
- // there if the array of messages counts to one, since could be the last of a series of chunks.
112
- if ($this->batch_size == 1 || count($messages) == 1) {
113
- $last_result = true;
114
- foreach ($messages as $message) {
115
- $r = $this->send($message);
116
- if (is_wp_error($r)) {
117
- $last_result = $r;
118
- }
119
- }
120
- return $last_result;
121
- }
122
-
123
- // We should always get there
124
- if (count($messages) <= $this->batch_size) {
125
- return $this->send_chunk($messages);
126
- }
127
-
128
- // We should not get here, since it is not optimized
129
- $chunks = array_chunk($message, $this->batch_size);
130
- $last_result = true;
131
- foreach ($chunks as $chunk) {
132
- $r = $this->send_chunk($chunk);
133
- if (is_wp_error($r)) {
134
- $last_result = $r;
135
- }
136
- }
137
- return $last_result;
138
- }
139
-
140
- /**
141
- * This one should be implemented by specilized classes.
142
- *
143
- * @param TNP_Mailer_Message[] $messages
144
- * @return bool|WP_Error
145
- */
146
- protected function send_chunk($messages) {
147
- $last_result = true;
148
- foreach ($messages as $message) {
149
- $r = $this->send($message);
150
- if (is_wp_error($r)) {
151
- $last_result = $r;
152
- }
153
- }
154
- return $last_result;
155
- }
156
-
157
- /**
158
- * @return NewsletterLogger
159
- */
160
- function get_logger() {
161
- if ($this->logger) {
162
- return $this->logger;
163
- }
164
- $this->logger = new NewsletterLogger($this->name . '-mailer');
165
- return $this->logger;
166
- }
167
-
168
- /**
169
- * Original mail function simulation for compatibility.
170
- * @deprecated
171
- *
172
- * @param string $to
173
- * @param string $subject
174
- * @param array $message
175
- * @param array $headers
176
- * @param bool $enqueue
177
- * @param type $from Actually ignored
178
- * @return type
179
- */
180
- public function mail($to, $subject, $message, $headers = null, $enqueue = false, $from = false) {
181
- $mailer_message = new TNP_Mailer_Message();
182
- $mailer_message->to = $to;
183
- $mailer_message->subject = $subject;
184
- $mailer_message->headers = $headers;
185
- $mailer_message->body = $message['html'];
186
- $mailer_message->body_text = $message['text'];
187
-
188
- return !is_wp_error($this->send($mailer_message));
189
- }
190
-
191
- /**
192
- * Used by bounce detection.
193
- *
194
- * @param int $time
195
- */
196
- function save_last_run($time) {
197
- update_option($this->prefix . '_last_run', $time);
198
- }
199
-
200
- /**
201
- * Used by bounce detection.
202
- *
203
- * @param int $time
204
- */
205
- function get_last_run() {
206
- return (int) get_option($this->prefix . '_last_run', 0);
207
- }
208
-
209
- }
210
-
211
- /**
212
- * Standard Mailer which uses the wp_mail() function of WP.
213
- */
214
- class NewsletterDefaultMailer extends NewsletterMailer {
215
-
216
- var $filter_active = false;
217
-
218
- /**
219
- * Static to be accessed in the hook: on some installation the object $this is not working, we're still trying to understand why
220
- * @var TNP_Mailer_Message
221
- */
222
- var $current_message = null;
223
-
224
- function __construct() {
225
- parent::__construct('default', Newsletter::instance()->get_options('smtp'));
226
- }
227
-
228
- function get_description() {
229
- // TODO: check if overloaded
230
- return 'wp_mail() WordPress function (could be extended by a SMTP plugin)';
231
- }
232
-
233
- function get_speed() {
234
- return (int)Newsletter::instance()->options['scheduler_max'];
235
- }
236
-
237
- function fix_mailer($mailer) {
238
- // If there is not a current message, wp_mail() was not called by us
239
- if (is_null($this->current_message)) {
240
- return;
241
- }
242
-
243
- $newsletter = Newsletter::instance();
244
- if (isset($this->current_message->encoding)) {
245
- $mailer->Encoding = $this->current_message->encoding;
246
- } else {
247
- if (!empty($newsletter->options['content_transfer_encoding'])) {
248
- $mailer->Encoding = $newsletter->options['content_transfer_encoding'];
249
- } else {
250
- // Setting and encoding sometimes conflict with SMTP plugins
251
- //$mailer->Encoding = 'base64';
252
- }
253
- }
254
-
255
- /* @var $mailer PHPMailer */
256
- $mailer->Sender = $newsletter->options['return_path'];
257
-
258
- // If there is an HTML body AND a text body, add the text part.
259
- if (!empty($this->current_message->body) && !empty($this->current_message->body_text)) {
260
- $mailer->AltBody = $this->current_message->body_text;
261
- }
262
- }
263
-
264
- /**
265
- *
266
- * @param TNP_Mailer_Message $message
267
- * @return \WP_Error|boolean
268
- */
269
- function send($message) {
270
-
271
- if (!$this->filter_active) {
272
- add_action('phpmailer_init', array($this, 'fix_mailer'), 100);
273
- $this->filter_active = true;
274
- }
275
-
276
- $newsletter = Newsletter::instance();
277
- $wp_mail_headers = [];
278
- if (empty($message->from)) {
279
- $message->from = $newsletter->options['sender_email'];
280
- }
281
-
282
- if (empty($message->from_name)) {
283
- $message->from_name = $newsletter->options['sender_name'];
284
- }
285
-
286
- $wp_mail_headers[] = 'From: "' . $message->from_name . '" <' . $message->from . '>';
287
-
288
- if (!empty($newsletter->options['reply_to'])) {
289
- $wp_mail_headers[] = 'Reply-To: ' . $newsletter->options['reply_to'];
290
- }
291
-
292
- // Manage from and from name
293
-
294
- if (!empty($message->headers)) {
295
- foreach ($message->headers as $key => $value) {
296
- $wp_mail_headers[] = $key . ': ' . $value;
297
- }
298
- }
299
-
300
- if (!empty($message->body)) {
301
- $wp_mail_headers[] = 'Content-Type: text/html;charset=UTF-8';
302
- $body = $message->body;
303
- } else if (!empty($message->body_text)) {
304
- $wp_mail_headers[] = 'Content-Type: text/plain;charset=UTF-8';
305
- $body = $message->body_text;
306
- } else {
307
- $message->error = 'Empty body';
308
- return new WP_Error(self::ERROR_GENERIC, 'Message format');
309
- }
310
-
311
- $this->current_message = $message;
312
-
313
- $r = wp_mail($message->to, $message->subject, $body, $wp_mail_headers);
314
- $this->current_message = null;
315
-
316
- if (!$r) {
317
- $last_error = error_get_last();
318
- if (is_array($last_error)) {
319
- $message->error = $last_error['message'];
320
- if (stripos($message->error, 'Could not instantiate mail function') || stripos($message->error, 'Failed to connect to mailserver')) {
321
- return new WP_Error(self::ERROR_FATAL, $last_error['message']);
322
- } else {
323
- return new WP_Error(self::ERROR_GENERIC, $last_error['message']);
324
- }
325
- } else {
326
- $message->error = 'No error explanation reported';
327
- return new WP_Error(self::ERROR_GENERIC, 'No error message reported');
328
- }
329
- }
330
- return true;
331
- }
332
-
333
- }
334
-
335
- /**
336
- * @deprecated since version 6.2.0
337
- * Internal SMTP mailer implementation (move to an SMTP plugin or use the
338
- * SMTP Addon).
339
- */
340
- class NewsletterDefaultSMTPMailer extends NewsletterMailer {
341
-
342
- var $mailer = null;
343
-
344
- function __construct($options) {
345
- parent::__construct('internal-smtp', $options);
346
- }
347
-
348
- function get_description() {
349
- return 'Internal SMTP (deprecated)';
350
- }
351
-
352
- /**
353
- *
354
- * @param TNP_Mailer_Message $message
355
- * @return \WP_Error|boolean
356
- */
357
- public function send($message) {
358
- $logger = $this->get_logger();
359
- $logger->debug('Start sending to ' . $message->to);
360
- $mailer = $this->get_mailer();
361
-
362
- if (!empty($message->body)) {
363
- $mailer->IsHTML(true);
364
- $mailer->Body = $message->body;
365
- $mailer->AltBody = $message->body_text;
366
- } else {
367
- $mailer->IsHTML(false);
368
- $mailer->Body = $message->body_text;
369
- $mailer->AltBody = '';
370
- }
371
-
372
- $mailer->Subject = $message->subject;
373
-
374
- $mailer->ClearCustomHeaders();
375
- if (!empty($message->headers)) {
376
- foreach ($message->headers as $key => $value) {
377
- $mailer->AddCustomHeader($key . ': ' . $value);
378
- }
379
- }
380
-
381
- if ($message->from) {
382
- $logger->debug('Alternative from available');
383
- $mailer->setFrom($message->from, $message->from_name);
384
- } else {
385
- $newsletter = Newsletter::instance();
386
- $mailer->setFrom($newsletter->options['sender_email'], $newsletter->options['sender_name']);
387
- }
388
-
389
- $mailer->ClearAddresses();
390
- $mailer->AddAddress($message->to);
391
- $mailer->Send();
392
-
393
- if ($mailer->IsError()) {
394
-
395
- $logger->error($mailer->ErrorInfo);
396
- // If the error is due to SMTP connection, the mailer cannot be reused since it does not clean up the connection
397
- // on error.
398
- //$this->mailer = null;
399
- $message->error = $mailer->ErrorInfo;
400
- return new WP_Error(self::ERROR_GENERIC, $mailer->ErrorInfo);
401
- }
402
-
403
- $logger->debug('Sent ' . $message->to);
404
- //$logger->error('Time: ' . (microtime(true) - $start) . ' seconds');
405
- return true;
406
- }
407
-
408
- /**
409
- *
410
- * @return PHPMailer
411
- */
412
- function get_mailer() {
413
- global $wp_version;
414
-
415
- if ($this->mailer) {
416
- return $this->mailer;
417
- }
418
-
419
- $logger = $this->get_logger();
420
- $logger->debug('Setting up PHP mailer');
421
-
422
- require_once 'PHPMailerLoader.php';
423
- $this->mailer = PHPMailerLoader::make_instance();
424
-
425
- $this->mailer->XMailer = ' '; // A space!
426
-
427
- $this->mailer->IsSMTP();
428
- $this->mailer->Host = $this->options['host'];
429
- if (!empty($this->options['port'])) {
430
- $this->mailer->Port = (int) $this->options['port'];
431
- }
432
-
433
- if (!empty($this->options['user'])) {
434
- $this->mailer->SMTPAuth = true;
435
- $this->mailer->Username = $this->options['user'];
436
- $this->mailer->Password = $this->options['pass'];
437
- }
438
-
439
- $this->mailer->SMTPSecure = $this->options['secure'];
440
- $this->mailer->SMTPAutoTLS = false;
441
-
442
- if ($this->options['ssl_insecure'] == 1) {
443
- $this->mailer->SMTPOptions = array(
444
- 'ssl' => array(
445
- 'verify_peer' => false,
446
- 'verify_peer_name' => false,
447
- 'allow_self_signed' => true
448
- )
449
- );
450
- }
451
-
452
- $newsletter = Newsletter::instance();
453
-
454
- $this->mailer->CharSet = 'UTF-8';
455
- $this->mailer->From = $newsletter->options['sender_email'];
456
-
457
- if (!empty($newsletter->options['return_path'])) {
458
- $this->mailer->Sender = $newsletter->options['return_path'];
459
- }
460
- if (!empty($newsletter->options['reply_to'])) {
461
- $this->mailer->AddReplyTo($newsletter->options['reply_to']);
462
- }
463
-
464
- $this->mailer->FromName = $newsletter->options['sender_name'];
465
-
466
-
467
- return $this->mailer;
468
- }
469
-
470
- }
1
+ <?php
2
+
3
+ use TNP\Mailer\PHPMailerLoader;
4
+
5
+ /**
6
+ * @property string $to
7
+ * @property string $to_name
8
+ * @property string $subject
9
+ * @property string $body
10
+ * @property array $headers
11
+ * @property string $from
12
+ * @property string $from_name
13
+ */
14
+ class TNP_Mailer_Message {
15
+
16
+ var $to_name = '';
17
+ var $headers = array();
18
+ var $user_id = 0;
19
+ var $email_id = 0;
20
+ var $error = '';
21
+ var $subject = '';
22
+ var $body = '';
23
+ var $body_text = '';
24
+ var $from = '';
25
+ var $from_name = '';
26
+
27
+ }
28
+
29
+ /**
30
+ * A basic class able to send one or more TNP_Mailer_Message objects using a
31
+ * delivery method (wp-mail(), SMTP, API, ...).
32
+ */
33
+ class NewsletterMailer {
34
+
35
+ const ERROR_GENERIC = '1';
36
+ const ERROR_FATAL = '2';
37
+
38
+ /* @var NewsletterLogger */
39
+
40
+ var $logger;
41
+ var $name;
42
+ var $options;
43
+ private $delta;
44
+ protected $batch_size = 1;
45
+ protected $speed = 0;
46
+
47
+ public function __construct($name, $options = []) {
48
+ $this->name = $name;
49
+ $this->options = $options;
50
+ if (!empty($this->options['speed'])) {
51
+ $this->speed = max(0, (int)$this->options['speed']);
52
+ }
53
+ if (!empty($this->options['turbo'])) {
54
+ $this->batch_size = max(1, (int)$this->options['turbo']);
55
+ }
56
+ $this->get_logger()->debug($options);
57
+ }
58
+
59
+ public function get_name() {
60
+ return $this->name;
61
+ }
62
+
63
+ public function get_description() {
64
+ return ucfirst($this->name) . ' Addon';
65
+ }
66
+
67
+ public function get_batch_size() {
68
+ return $this->batch_size;
69
+ }
70
+
71
+ public function get_speed() {
72
+ return $this->speed;
73
+ }
74
+
75
+ function send_with_stats($message) {
76
+ $this->delta = microtime(true);
77
+ $r = $this->send($message);
78
+ $this->delta = microtime(true) - $this->delta;
79
+ return $r;
80
+ }
81
+
82
+ /**
83
+ *
84
+ * @param TNP_Mailer_Message $message
85
+ * @return bool|WP_Error
86
+ */
87
+ public function send($message) {
88
+ $message->error = 'No mailing system available';
89
+ return new WP_Error(self::ERROR_FATAL, 'No mailing system available');
90
+ }
91
+
92
+ public function send_batch_with_stats($messages) {
93
+ $this->delta = microtime(true);
94
+ $r = $this->send_batch($messages);
95
+ $this->delta = microtime(true) - $this->delta;
96
+ return $r;
97
+ }
98
+
99
+ function get_capability() {
100
+ return (int) (3600 * $this->batch_size / $this->delta);
101
+ }
102
+
103
+ /**
104
+ *
105
+ * @param TNP_Mailer_Message[] $messages
106
+ * @return bool|WP_Error
107
+ */
108
+ public function send_batch($messages) {
109
+
110
+ // We should not get there is the batch size is one, the caller should use "send()". We can get
111
+ // there if the array of messages counts to one, since could be the last of a series of chunks.
112
+ if ($this->batch_size == 1 || count($messages) == 1) {
113
+ $last_result = true;
114
+ foreach ($messages as $message) {
115
+ $r = $this->send($message);
116
+ if (is_wp_error($r)) {
117
+ $last_result = $r;
118
+ }
119
+ }
120
+ return $last_result;
121
+ }
122
+
123
+ // We should always get there
124
+ if (count($messages) <= $this->batch_size) {
125
+ return $this->send_chunk($messages);
126
+ }
127
+
128
+ // We should not get here, since it is not optimized
129
+ $chunks = array_chunk($message, $this->batch_size);
130
+ $last_result = true;
131
+ foreach ($chunks as $chunk) {
132
+ $r = $this->send_chunk($chunk);
133
+ if (is_wp_error($r)) {
134
+ $last_result = $r;
135
+ }
136
+ }
137
+ return $last_result;
138
+ }
139
+
140
+ /**
141
+ * This one should be implemented by specilized classes.
142
+ *
143
+ * @param TNP_Mailer_Message[] $messages
144
+ * @return bool|WP_Error
145
+ */
146
+ protected function send_chunk($messages) {
147
+ $last_result = true;
148
+ foreach ($messages as $message) {
149
+ $r = $this->send($message);
150
+ if (is_wp_error($r)) {
151
+ $last_result = $r;
152
+ }
153
+ }
154
+ return $last_result;
155
+ }
156
+
157
+ /**
158
+ * @return NewsletterLogger
159
+ */
160
+ function get_logger() {
161
+ if ($this->logger) {
162
+ return $this->logger;
163
+ }
164
+ $this->logger = new NewsletterLogger($this->name . '-mailer');
165
+ return $this->logger;
166
+ }
167
+
168
+ /**
169
+ * Original mail function simulation for compatibility.
170
+ * @deprecated
171
+ *
172
+ * @param string $to
173
+ * @param string $subject
174
+ * @param array $message
175
+ * @param array $headers
176
+ * @param bool $enqueue
177
+ * @param type $from Actually ignored
178
+ * @return type
179
+ */
180
+ public function mail($to, $subject, $message, $headers = null, $enqueue = false, $from = false) {
181
+ $mailer_message = new TNP_Mailer_Message();
182
+ $mailer_message->to = $to;
183
+ $mailer_message->subject = $subject;
184
+ $mailer_message->headers = $headers;
185
+ $mailer_message->body = $message['html'];
186
+ $mailer_message->body_text = $message['text'];
187
+
188
+ return !is_wp_error($this->send($mailer_message));
189
+ }
190
+
191
+ /**
192
+ * Used by bounce detection.
193
+ *
194
+ * @param int $time
195
+ */
196
+ function save_last_run($time) {
197
+ update_option($this->prefix . '_last_run', $time);
198
+ }
199
+
200
+ /**
201
+ * Used by bounce detection.
202
+ *
203
+ * @param int $time
204
+ */
205
+ function get_last_run() {
206
+ return (int) get_option($this->prefix . '_last_run', 0);
207
+ }
208
+
209
+ }
210
+
211
+ /**
212
+ * Standard Mailer which uses the wp_mail() function of WP.
213
+ */
214
+ class NewsletterDefaultMailer extends NewsletterMailer {
215
+
216
+ var $filter_active = false;
217
+
218
+ /**
219
+ * Static to be accessed in the hook: on some installation the object $this is not working, we're still trying to understand why
220
+ * @var TNP_Mailer_Message
221
+ */
222
+ var $current_message = null;
223
+
224
+ function __construct() {
225
+ parent::__construct('default', Newsletter::instance()->get_options('smtp'));
226
+ }
227
+
228
+ function get_description() {
229
+ // TODO: check if overloaded
230
+ return 'wp_mail() WordPress function (could be extended by a SMTP plugin)';
231
+ }
232
+
233
+ function get_speed() {
234
+ return (int)Newsletter::instance()->options['scheduler_max'];
235
+ }
236
+
237
+ function fix_mailer($mailer) {
238
+ // If there is not a current message, wp_mail() was not called by us
239
+ if (is_null($this->current_message)) {
240
+ return;
241
+ }
242
+
243
+ $newsletter = Newsletter::instance();
244
+ if (isset($this->current_message->encoding)) {
245
+ $mailer->Encoding = $this->current_message->encoding;
246
+ } else {
247
+ if (!empty($newsletter->options['content_transfer_encoding'])) {
248
+ $mailer->Encoding = $newsletter->options['content_transfer_encoding'];
249
+ } else {
250
+ // Setting and encoding sometimes conflict with SMTP plugins
251
+ //$mailer->Encoding = 'base64';
252
+ }
253
+ }
254
+
255
+ /* @var $mailer PHPMailer */
256
+ $mailer->Sender = $newsletter->options['return_path'];
257
+
258
+ // If there is an HTML body AND a text body, add the text part.
259
+ if (!empty($this->current_message->body) && !empty($this->current_message->body_text)) {
260
+ $mailer->AltBody = $this->current_message->body_text;
261
+ }
262
+ }
263
+
264
+ /**
265
+ *
266
+ * @param TNP_Mailer_Message $message
267
+ * @return \WP_Error|boolean
268
+ */
269
+ function send($message) {
270
+
271
+ if (!$this->filter_active) {
272
+ add_action('phpmailer_init', array($this, 'fix_mailer'), 100);
273
+ $this->filter_active = true;
274
+ }
275
+
276
+ $newsletter = Newsletter::instance();
277
+ $wp_mail_headers = [];
278
+ if (empty($message->from)) {
279
+ $message->from = $newsletter->options['sender_email'];
280
+ }
281
+
282
+ if (empty($message->from_name)) {
283
+ $message->from_name = $newsletter->options['sender_name'];
284
+ }
285
+
286
+ $wp_mail_headers[] = 'From: "' . $message->from_name . '" <' . $message->from . '>';
287
+
288
+ if (!empty($newsletter->options['reply_to'])) {
289
+ $wp_mail_headers[] = 'Reply-To: ' . $newsletter->options['reply_to'];
290
+ }
291
+
292
+ // Manage from and from name
293
+
294
+ if (!empty($message->headers)) {
295
+ foreach ($message->headers as $key => $value) {
296
+ $wp_mail_headers[] = $key . ': ' . $value;
297
+ }
298
+ }
299
+
300
+ if (!empty($message->body)) {
301
+ $wp_mail_headers[] = 'Content-Type: text/html;charset=UTF-8';
302
+ $body = $message->body;
303
+ } else if (!empty($message->body_text)) {
304
+ $wp_mail_headers[] = 'Content-Type: text/plain;charset=UTF-8';
305
+ $body = $message->body_text;
306
+ } else {
307
+ $message->error = 'Empty body';
308
+ return new WP_Error(self::ERROR_GENERIC, 'Message format');
309
+ }
310
+
311
+ $this->current_message = $message;
312
+
313
+ $r = wp_mail($message->to, $message->subject, $body, $wp_mail_headers);
314
+ $this->current_message = null;
315
+
316
+ if (!$r) {
317
+ $last_error = error_get_last();
318
+ if (is_array($last_error)) {
319
+ $message->error = $last_error['message'];
320
+ if (stripos($message->error, 'Could not instantiate mail function') || stripos($message->error, 'Failed to connect to mailserver')) {
321
+ return new WP_Error(self::ERROR_FATAL, $last_error['message']);
322
+ } else {
323
+ return new WP_Error(self::ERROR_GENERIC, $last_error['message']);
324
+ }
325
+ } else {
326
+ $message->error = 'No error explanation reported';
327
+ return new WP_Error(self::ERROR_GENERIC, 'No error message reported');
328
+ }
329
+ }
330
+ return true;
331
+ }
332
+
333
+ }
334
+
335
+ /**
336
+ * @deprecated since version 6.2.0
337
+ * Internal SMTP mailer implementation (move to an SMTP plugin or use the
338
+ * SMTP Addon).
339
+ */
340
+ class NewsletterDefaultSMTPMailer extends NewsletterMailer {
341
+
342
+ var $mailer = null;
343
+
344
+ function __construct($options) {
345
+ parent::__construct('internal-smtp', $options);
346
+ }
347
+
348
+ function get_description() {
349
+ return 'Internal SMTP (deprecated)';
350
+ }
351
+
352
+ /**
353
+ *
354
+ * @param TNP_Mailer_Message $message
355
+ * @return \WP_Error|boolean
356
+ */
357
+ public function send($message) {
358
+ $logger = $this->get_logger();
359
+ $logger->debug('Start sending to ' . $message->to);
360
+ $mailer = $this->get_mailer();
361
+
362
+ if (!empty($message->body)) {
363
+ $mailer->IsHTML(true);
364
+ $mailer->Body = $message->body;
365
+ $mailer->AltBody = $message->body_text;
366
+ } else {
367
+ $mailer->IsHTML(false);
368
+ $mailer->Body = $message->body_text;
369
+ $mailer->AltBody = '';
370
+ }
371
+
372
+ $mailer->Subject = $message->subject;
373
+
374
+ $mailer->ClearCustomHeaders();
375
+ if (!empty($message->headers)) {
376
+ foreach ($message->headers as $key => $value) {
377
+ $mailer->AddCustomHeader($key . ': ' . $value);
378
+ }
379
+ }
380
+
381
+ if ($message->from) {
382
+ $logger->debug('Alternative from available');
383
+ $mailer->setFrom($message->from, $message->from_name);
384
+ } else {
385
+ $newsletter = Newsletter::instance();
386
+ $mailer->setFrom($newsletter->options['sender_email'], $newsletter->options['sender_name']);
387
+ }
388
+
389
+ $mailer->ClearAddresses();
390
+ $mailer->AddAddress($message->to);
391
+ $mailer->Send();
392
+
393
+ if ($mailer->IsError()) {
394
+
395
+ $logger->error($mailer->ErrorInfo);
396
+ // If the error is due to SMTP connection, the mailer cannot be reused since it does not clean up the connection
397
+ // on error.
398
+ //$this->mailer = null;
399
+ $message->error = $mailer->ErrorInfo;
400
+ return new WP_Error(self::ERROR_GENERIC, $mailer->ErrorInfo);
401
+ }
402
+
403
+ $logger->debug('Sent ' . $message->to);
404
+ //$logger->error('Time: ' . (microtime(true) - $start) . ' seconds');
405
+ return true;
406
+ }
407
+
408
+ /**
409
+ *
410
+ * @return PHPMailer
411
+ */
412
+ function get_mailer() {
413
+ global $wp_version;
414
+
415
+ if ($this->mailer) {
416
+ return $this->mailer;
417
+ }
418
+
419
+ $logger = $this->get_logger();
420
+ $logger->debug('Setting up PHP mailer');
421
+
422
+ require_once 'PHPMailerLoader.php';
423
+ $this->mailer = PHPMailerLoader::make_instance();
424
+
425
+ $this->mailer->XMailer = ' '; // A space!
426
+
427
+ $this->mailer->IsSMTP();
428
+ $this->mailer->Host = $this->options['host'];
429
+ if (!empty($this->options['port'])) {
430
+ $this->mailer->Port = (int) $this->options['port'];
431
+ }
432
+
433
+ if (!empty($this->options['user'])) {
434
+ $this->mailer->SMTPAuth = true;
435
+ $this->mailer->Username = $this->options['user'];
436
+ $this->mailer->Password = $this->options['pass'];
437
+ }
438
+
439
+ $this->mailer->SMTPSecure = $this->options['secure'];
440
+ $this->mailer->SMTPAutoTLS = false;
441
+
442
+ if ($this->options['ssl_insecure'] == 1) {
443
+ $this->mailer->SMTPOptions = array(
444
+ 'ssl' => array(
445
+ 'verify_peer' => false,
446
+ 'verify_peer_name' => false,
447
+ 'allow_self_signed' => true
448
+ )
449
+ );
450
+ }
451
+
452
+ $newsletter = Newsletter::instance();
453
+
454
+ $this->mailer->CharSet = 'UTF-8';
455
+ $this->mailer->From = $newsletter->options['sender_email'];
456
+
457
+ if (!empty($newsletter->options['return_path'])) {
458
+ $this->mailer->Sender = $newsletter->options['return_path'];
459
+ }
460
+ if (!empty($newsletter->options['reply_to'])) {
461
+ $this->mailer->AddReplyTo($newsletter->options['reply_to']);
462
+ }
463
+
464
+ $this->mailer->FromName = $newsletter->options['sender_name'];
465
+
466
+
467
+ return $this->mailer;
468
+ }
469
+
470
+ }
includes/module.php CHANGED
@@ -338,6 +338,8 @@ class TNP_User {
338
  const STATUS_UNSUBSCRIBED = 'U';
339
  const STATUS_BOUNCED = 'B';
340
  const STATUS_COMPLAINED = 'P';
 
 
341
 
342
  public static function get_status_label($status) {
343
  switch ($status) {
338
  const STATUS_UNSUBSCRIBED = 'U';
339
  const STATUS_BOUNCED = 'B';
340
  const STATUS_COMPLAINED = 'P';
341
+
342
+ var $ip = '';
343
 
344
  public static function get_status_label($status) {
345
  switch ($status) {
main/main.php CHANGED
@@ -171,7 +171,9 @@ if (!empty($return_path)) {
171
  </th>
172
  <td>
173
  <?php $controls->text_email('sender_email', 40); ?>
174
-
 
 
175
  </td>
176
  </tr>
177
  <tr>
171
  </th>
172
  <td>
173
  <?php $controls->text_email('sender_email', 40); ?>
174
+ <div class="description">
175
+ <a href="https://www.thenewsletterplugin.com/documentation/installation/newsletter-configuration/#sender" target="_blank">Emails delivered with a different address?</a>
176
+ </div>
177
  </td>
178
  </tr>
179
  <tr>
plugin.php CHANGED
@@ -1,1418 +1,1418 @@
1
- <?php
2
-
3
- /*
4
- Plugin Name: Newsletter
5
- Plugin URI: https://www.thenewsletterplugin.com/plugins/newsletter
6
- Description: Newsletter is a cool plugin to create your own subscriber list, to send newsletters, to build your business. <strong>Before update give a look to <a href="https://www.thenewsletterplugin.com/category/release">this page</a> to know what's changed.</strong>
7
- Version: 7.3.5
8
- Author: Stefano Lissa & The Newsletter Team
9
- Author URI: https://www.thenewsletterplugin.com
10
- Disclaimer: Use at your own risk. No warranty expressed or implied is provided.
11
- Text Domain: newsletter
12
- License: GPLv2 or later
13
- Requires at least: 4.6
14
- Requires PHP: 5.6
15
-
16
- Copyright 2009-2022 The Newsletter Team (email: info@thenewsletterplugin.com, web: https://www.thenewsletterplugin.com)
17
-
18
- Newsletter is free software: you can redistribute it and/or modify
19
- it under the terms of the GNU General Public License as published by
20
- the Free Software Foundation, either version 2 of the License, or
21
- any later version.
22
-
23
- Newsletter is distributed in the hope that it will be useful,
24
- but WITHOUT ANY WARRANTY; without even the implied warranty of
25
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26
- GNU General Public License for more details.
27
-
28
- You should have received a copy of the GNU General Public License
29
- along with Newsletter. If not, see https://www.gnu.org/licenses/gpl-2.0.html.
30
-
31
- */
32
-
33
- if (version_compare(phpversion(), '5.6', '<')) {
34
- add_action('admin_notices', function () {
35
- echo '<div class="notice notice-error"><p>PHP version 5.6 or greater is required for Newsletter. Ask your provider to upgrade. <a href="https://www.php.net/supported-versions.php" target="_blank">Read more on PHP versions</a></p></div>';
36
- });
37
- return;
38
- }
39
-
40
- define('NEWSLETTER_VERSION', '7.3.5');
41
-
42
- global $newsletter, $wpdb;
43
-
44
- // For acceptance tests, DO NOT CHANGE
45
- if (!defined('NEWSLETTER_DEBUG'))
46
- define('NEWSLETTER_DEBUG', false);
47
-
48
- if (!defined('NEWSLETTER_EXTENSION_UPDATE'))
49
- define('NEWSLETTER_EXTENSION_UPDATE', true);
50
-
51
- if (!defined('NEWSLETTER_EMAILS_TABLE'))
52
- define('NEWSLETTER_EMAILS_TABLE', $wpdb->prefix . 'newsletter_emails');
53
-
54
- if (!defined('NEWSLETTER_USERS_TABLE'))
55
- define('NEWSLETTER_USERS_TABLE', $wpdb->prefix . 'newsletter');
56
-
57
- if (!defined('NEWSLETTER_STATS_TABLE'))
58
- define('NEWSLETTER_STATS_TABLE', $wpdb->prefix . 'newsletter_stats');
59
-
60
- if (!defined('NEWSLETTER_SENT_TABLE'))
61
- define('NEWSLETTER_SENT_TABLE', $wpdb->prefix . 'newsletter_sent');
62
-
63
- define('NEWSLETTER_SLUG', 'newsletter');
64
-
65
- define('NEWSLETTER_DIR', __DIR__);
66
- define('NEWSLETTER_INCLUDES_DIR', __DIR__ . '/includes');
67
-
68
- // Deperacted since plugin can change the plugins_url()
69
- define('NEWSLETTER_URL', WP_PLUGIN_URL . '/newsletter');
70
-
71
- if (!defined('NEWSLETTER_LIST_MAX'))
72
- define('NEWSLETTER_LIST_MAX', 40);
73
-
74
- if (!defined('NEWSLETTER_PROFILE_MAX'))
75
- define('NEWSLETTER_PROFILE_MAX', 20);
76
-
77
- if (!defined('NEWSLETTER_FORMS_MAX'))
78
- define('NEWSLETTER_FORMS_MAX', 10);
79
-
80
- if (!defined('NEWSLETTER_HEADER'))
81
- define('NEWSLETTER_HEADER', true);
82
-
83
- require_once NEWSLETTER_INCLUDES_DIR . '/module.php';
84
- require_once NEWSLETTER_INCLUDES_DIR . '/TNP.php';
85
- require_once NEWSLETTER_INCLUDES_DIR . '/cron.php';
86
-
87
- class Newsletter extends NewsletterModule {
88
-
89
- // Limits to respect to avoid memory, time or provider limits
90
- var $time_start;
91
- var $time_limit = 0;
92
- var $max_emails = null;
93
-
94
- var $mailer = null;
95
-
96
- var $action = '';
97
-
98
- /** @var Newsletter */
99
- static $instance;
100
-
101
- const STATUS_NOT_CONFIRMED = 'S';
102
- const STATUS_CONFIRMED = 'C';
103
-
104
- /**
105
- * @return Newsletter
106
- */
107
- static function instance() {
108
- if (self::$instance == null) {
109
- self::$instance = new Newsletter();
110
- }
111
- return self::$instance;
112
- }
113
-
114
- function __construct() {
115
-
116
- // Grab it before a plugin decides to remove it.
117
- if (isset($_GET['na'])) {
118
- $this->action = $_GET['na'];
119
- }
120
- if (isset($_POST['na'])) {
121
- $this->action = $_POST['na'];
122
- }
123
-
124
- $this->time_start = time();
125
-
126
- parent::__construct('main', '1.6.7', null, ['info', 'smtp']);
127
-
128
- add_action('plugins_loaded', [$this, 'hook_plugins_loaded']);
129
- add_action('init', [$this, 'hook_init'], 1);
130
- add_action('wp_loaded', [$this, 'hook_wp_loaded'], 1);
131
-
132
- add_action('newsletter', [$this, 'hook_newsletter'], 1);
133
-
134
- register_activation_hook(__FILE__, [$this, 'hook_activate']);
135
- register_deactivation_hook(__FILE__, [$this, 'hook_deactivate']);
136
-
137
- add_action('admin_init', [$this, 'hook_admin_init']);
138
-
139
- if (is_admin()) {
140
- add_action('admin_head', [$this, 'hook_admin_head']);
141
-
142
- // Protection against strange schedule removal on some installations
143
- if (!wp_next_scheduled('newsletter') && (!defined('WP_INSTALLING') || !WP_INSTALLING)) {
144
- wp_schedule_event(time() + 30, 'newsletter', 'newsletter');
145
- }
146
-
147
- add_action('admin_menu', [$this, 'add_extensions_menu'], 90);
148
-
149
- add_filter('display_post_states', [$this, 'add_notice_to_chosen_profile_page_hook'], 10, 2);
150
-
151
- if ($this->is_admin_page()) {
152
- // Remove the emoji replacer to save to database the original emoji characters (see even woocommerce for the same problem)
153
- remove_action('admin_print_scripts', 'print_emoji_detection_script');
154
- add_action('admin_enqueue_scripts', [$this, 'hook_admin_enqueue_scripts']);
155
- }
156
-
157
- }
158
- }
159
-
160
- function hook_plugins_loaded() {
161
- // Used to load dependant modules
162
- do_action('newsletter_loaded', NEWSLETTER_VERSION);
163
-
164
- if (function_exists('load_plugin_textdomain')) {
165
- load_plugin_textdomain('newsletter', false, plugin_basename(__DIR__) . '/languages');
166
- }
167
- }
168
-
169
- function hook_init() {
170
- global $wpdb;
171
-
172
- if (isset($this->options['debug']) && $this->options['debug'] == 1) {
173
- ini_set('log_errors', 1);
174
- ini_set('error_log', WP_CONTENT_DIR . '/logs/newsletter/php-' . date('Y-m') . '-' . get_option('newsletter_logger_secret') . '.txt');
175
- }
176
-
177
- add_shortcode('newsletter_replace', [$this, 'shortcode_newsletter_replace']);
178
-
179
- add_filter('site_transient_update_plugins', [$this, 'hook_site_transient_update_plugins']);
180
-
181
- if (is_admin()) {
182
- if (!class_exists('NewsletterExtensions')) {
183
-
184
- add_filter('plugin_row_meta', function ($plugin_meta, $plugin_file) {
185
-
186
- static $slugs = array();
187
- if (empty($slugs)) {
188
- $addons = $this->getTnpExtensions();
189
- if ($addons) {
190
- foreach ($addons as $addon) {
191
- $slugs[] = $addon->wp_slug;
192
- }
193
- }
194
- }
195
- if (array_search($plugin_file, $slugs) !== false) {
196
-
197
- $plugin_meta[] = '<a href="admin.php?page=newsletter_main_extensions" style="font-weight: bold">Newsletter Addons Manager required</a>';
198
- }
199
- return $plugin_meta;
200
- }, 10, 2);
201
- }
202
-
203
- add_action('in_admin_header', array($this, 'hook_in_admin_header'), 1000);
204
-
205
- if ($this->is_admin_page()) {
206
-
207
- $dismissed = get_option('newsletter_dismissed', []);
208
-
209
- if (isset($_GET['dismiss'])) {
210
- $dismissed[$_GET['dismiss']] = 1;
211
- update_option('newsletter_dismissed', $dismissed);
212
- wp_redirect($_SERVER['HTTP_REFERER']);
213
- exit();
214
- }
215
- }
216
- } else {
217
- add_action('wp_enqueue_scripts', [$this, 'hook_wp_enqueue_scripts']);
218
- }
219
-
220
- do_action('newsletter_init');
221
- }
222
-
223
- function hook_wp_loaded() {
224
- if (empty($this->action)) {
225
- return;
226
- }
227
-
228
- if ($this->action == 'test') {
229
- // This response is tested, do not change it!
230
- echo 'ok';
231
- die();
232
- }
233
-
234
- if ($this->action === 'nul') {
235
- $this->dienow('This link is not active on newsletter preview', 'You can send a test message to test subscriber to have the real working link.');
236
- }
237
-
238
- $user = $this->get_user_from_request();
239
- $email = $this->get_email_from_request();
240
- do_action('newsletter_action', $this->action, $user, $email);
241
- }
242
-
243
- function hook_activate() {
244
- // Ok, why? When the plugin is not active WordPress may remove the scheduled "newsletter" action because
245
- // the every-five-minutes schedule named "newsletter" is not present.
246
- // Since the activation does not forces an upgrade, that schedule must be reactivated here. It is activated on
247
- // the upgrade method as well for the user which upgrade the plugin without deactivte it (many).
248
- if (!wp_next_scheduled('newsletter')) {
249
- wp_schedule_event(time() + 30, 'newsletter', 'newsletter');
250
- }
251
-
252
- $install_time = get_option('newsletter_install_time');
253
- if (!$install_time) {
254
- update_option('newsletter_install_time', time(), false);
255
- }
256
-
257
- touch(NEWSLETTER_LOG_DIR . '/index.html');
258
-
259
- Newsletter::instance()->upgrade();
260
- NewsletterUsers::instance()->upgrade();
261
- NewsletterEmails::instance()->upgrade();
262
- NewsletterSubscription::instance()->upgrade();
263
- NewsletterStatistics::instance()->upgrade();
264
- NewsletterProfile::instance()->upgrade();
265
- }
266
-
267
- function first_install() {
268
- parent::first_install();
269
- update_option('newsletter_show_welcome', '1', false);
270
- }
271
-
272
- function upgrade() {
273
- global $wpdb, $charset_collate;
274
-
275
- require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
276
-
277
- parent::upgrade();
278
-
279
- $sql = "CREATE TABLE `" . $wpdb->prefix . "newsletter_emails` (
280
- `id` int(11) NOT NULL AUTO_INCREMENT,
281
- `language` varchar(10) NOT NULL DEFAULT '',
282
- `subject` varchar(255) NOT NULL DEFAULT '',
283
- `message` longtext,
284
- `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
285
- `status` enum('new','sending','sent','paused','error') NOT NULL DEFAULT 'new',
286
- `total` int(11) NOT NULL DEFAULT '0',
287
- `last_id` int(11) NOT NULL DEFAULT '0',
288
- `sent` int(11) NOT NULL DEFAULT '0',
289
- `track` int(11) NOT NULL DEFAULT '1',
290
- `list` int(11) NOT NULL DEFAULT '0',
291
- `type` varchar(50) NOT NULL DEFAULT '',
292
- `query` longtext,
293
- `editor` tinyint(4) NOT NULL DEFAULT '0',
294
- `sex` varchar(20) NOT NULL DEFAULT '',
295
- `theme` varchar(50) NOT NULL DEFAULT '',
296
- `message_text` longtext,
297
- `preferences` longtext,
298
- `send_on` int(11) NOT NULL DEFAULT '0',
299
- `token` varchar(10) NOT NULL DEFAULT '',
300
- `options` longtext,
301
- `private` tinyint(1) NOT NULL DEFAULT '0',
302
- `click_count` int(10) unsigned NOT NULL DEFAULT '0',
303
- `version` varchar(10) NOT NULL DEFAULT '',
304
- `open_count` int(10) unsigned NOT NULL DEFAULT '0',
305
- `unsub_count` int(10) unsigned NOT NULL DEFAULT '0',
306
- `error_count` int(10) unsigned NOT NULL DEFAULT '0',
307
- `stats_time` int(10) unsigned NOT NULL DEFAULT '0',
308
- `updated` int(10) unsigned NOT NULL DEFAULT '0',
309
- PRIMARY KEY (`id`)
310
- ) $charset_collate;";
311
-
312
- dbDelta($sql);
313
-
314
- // WP does not manage composite primary key when it tries to upgrade a table...
315
- $suppress_errors = $wpdb->suppress_errors(true);
316
-
317
- dbDelta("CREATE TABLE " . $wpdb->prefix . "newsletter_sent (
318
- email_id int(10) unsigned NOT NULL DEFAULT '0',
319
- user_id int(10) unsigned NOT NULL DEFAULT '0',
320
- status tinyint(1) unsigned NOT NULL DEFAULT '0',
321
- open tinyint(1) unsigned NOT NULL DEFAULT '0',
322
- time int(10) unsigned NOT NULL DEFAULT '0',
323
- error varchar(255) NOT NULL DEFAULT '',
324
- ip varchar(100) NOT NULL DEFAULT '',
325
- PRIMARY KEY (email_id,user_id),
326
- KEY user_id (user_id),
327
- KEY email_id (email_id)
328
- ) $charset_collate;");
329
- $wpdb->suppress_errors($suppress_errors);
330
-
331
- // Some setting check to avoid the common support request for mis-configurations
332
- $options = $this->get_options();
333
-
334
- if (empty($options['scheduler_max']) || !is_numeric($options['scheduler_max'])) {
335
- $options['scheduler_max'] = 100;
336
- $this->save_options($options);
337
- }
338
-
339
- wp_clear_scheduled_hook('newsletter');
340
- wp_schedule_event(time() + 30, 'newsletter', 'newsletter');
341
-
342
- if (!empty($this->options['editor'])) {
343
- if (empty($this->options['roles'])) {
344
- $this->options['roles'] = array('editor');
345
- unset($this->options['editor']);
346
- }
347
- $this->save_options($this->options);
348
- }
349
-
350
- // Clear the addons and license caches
351
- delete_transient('newsletter_license_data');
352
- $this->clear_extensions_cache();
353
-
354
- touch(NEWSLETTER_LOG_DIR . '/index.html');
355
-
356
- return true;
357
- }
358
-
359
- function is_allowed() {
360
- if (current_user_can('administrator')) {
361
- return true;
362
- }
363
- if (!empty($this->options['roles'])) {
364
- foreach ($this->options['roles'] as $role) {
365
- if (current_user_can($role)) {
366
- return true;
367
- }
368
- }
369
- }
370
- return false;
371
- }
372
-
373
- function admin_menu() {
374
- if (!$this->is_allowed()) {
375
- return;
376
- }
377
-
378
- add_menu_page('Newsletter', 'Newsletter', 'exist', 'newsletter_main_index', '', plugins_url('newsletter') . '/admin/images/menu-icon.png', '30.333');
379
-
380
- $this->add_menu_page('index', __('Dashboard', 'newsletter'));
381
- $this->add_admin_page('info', __('Company info', 'newsletter'));
382
-
383
- if (current_user_can('administrator')) {
384
- $this->add_menu_page('welcome', __('Welcome', 'newsletter'));
385
- $this->add_menu_page('main', __('Settings', 'newsletter'));
386
-
387
- // Pages not on menu
388
- $this->add_admin_page('smtp', 'SMTP');
389
- $this->add_admin_page('diagnostic', __('Diagnostic', 'newsletter'));
390
- $this->add_admin_page('test', __('Test', 'newsletter'));
391
- }
392
- }
393
-
394
- function add_extensions_menu() {
395
- if (!class_exists('NewsletterExtensions')) {
396
- $this->add_menu_page('extensions', '<span style="color:#27AE60; font-weight: bold;">' . __('Addons', 'newsletter') . '</span>');
397
- }
398
- }
399
-
400
- function hook_in_admin_header() {
401
- if (!$this->is_admin_page()) {
402
- add_action('admin_notices', array($this, 'hook_admin_notices'));
403
- return;
404
- }
405
- remove_all_actions('admin_notices');
406
- remove_all_actions('all_admin_notices');
407
- add_action('admin_notices', array($this, 'hook_admin_notices'));
408
- }
409
-
410
- function hook_admin_notices() {
411
- // Check of Newsletter dedicated page
412
- if (!empty($this->options['page'])) {
413
- if (get_post_status($this->options['page']) !== 'publish') {
414
- echo '<div class="notice notice-error"><p>The Newsletter dedicated page is not published. <a href="', site_url('/wp-admin/post.php') . '?post=', $this->options['page'], '&action=edit"><strong>Edit the page</strong></a> or <a href="admin.php?page=newsletter_main_main"><strong>review the main settings</strong></a>.</p></div>';
415
- }
416
- }
417
-
418
- if (isset($this->options['debug']) && $this->options['debug'] == 1) {
419
- echo '<div class="notice notice-warning"><p>The Newsletter plugin is in <strong>debug mode</strong>. When done change it on Newsletter <a href="admin.php?page=newsletter_main_main"><strong>main settings</strong></a>. Do not keep the debug mode active on production sites.</p></div>';
420
- }
421
- }
422
-
423
- function hook_wp_enqueue_scripts() {
424
- if (empty($this->options['css_disabled']) && apply_filters('newsletter_enqueue_style', true)) {
425
- wp_enqueue_style('newsletter', plugins_url('newsletter') . '/style.css', [], NEWSLETTER_VERSION);
426
- if (!empty($this->options['css'])) {
427
- wp_add_inline_style('newsletter', $this->options['css']);
428
- }
429
- } else {
430
- if (!empty($this->options['css'])) {
431
- add_action('wp_head', function () {
432
- echo '<style>', $this->options['css'], '</style>';
433
- });
434
- }
435
- }
436
- }
437
-
438
- function hook_admin_enqueue_scripts() {
439
-
440
- $newsletter_url = plugins_url('newsletter');
441
- wp_enqueue_script('jquery-ui-tabs');
442
- wp_enqueue_script('jquery-ui-tooltip');
443
- wp_enqueue_script('jquery-ui-draggable');
444
- wp_enqueue_media();
445
-
446
- wp_enqueue_script('tnp-admin', $newsletter_url . '/admin/admin.js', ['jquery'], NEWSLETTER_VERSION);
447
-
448
- wp_enqueue_style('tnp-select2', $newsletter_url . '/vendor/select2/css/select2.min.css', [], NEWSLETTER_VERSION);
449
- wp_enqueue_script('tnp-select2', $newsletter_url . '/vendor/select2/js/select2.min.js', ['jquery'], NEWSLETTER_VERSION);
450
-
451
- wp_enqueue_style('tnp-modal', $newsletter_url . '/admin/modal.css', [], NEWSLETTER_VERSION);
452
- wp_enqueue_script('tnp-modal', $newsletter_url . '/admin/modal.js', ['jquery'], NEWSLETTER_VERSION, true);
453
-
454
- wp_enqueue_style('tnp-toast', $newsletter_url . '/admin/toast.css', [], NEWSLETTER_VERSION);
455
- wp_enqueue_script('tnp-toast', $newsletter_url . '/admin/toast.js', ['jquery'], NEWSLETTER_VERSION);
456
-
457
- wp_enqueue_style('tnp-admin-font', 'https://use.typekit.net/jlj2wjy.css');
458
- wp_enqueue_style('tnp-admin-fontawesome', $newsletter_url . '/vendor/fa/css/all.min.css', [], NEWSLETTER_VERSION);
459
- wp_enqueue_style('tnp-admin-jquery-ui', $newsletter_url . '/vendor/jquery-ui/jquery-ui.min.css', [], NEWSLETTER_VERSION);
460
- wp_enqueue_style('tnp-admin', $newsletter_url . '/admin/admin.css', [], NEWSLETTER_VERSION);
461
- wp_enqueue_style('tnp-admin-dropdown', $newsletter_url . '/admin/dropdown.css', [], NEWSLETTER_VERSION);
462
- wp_enqueue_style('tnp-admin-tabs', $newsletter_url . '/admin/tabs.css', [], NEWSLETTER_VERSION);
463
- wp_enqueue_style('tnp-admin-controls', $newsletter_url . '/admin/controls.css', [], NEWSLETTER_VERSION);
464
- wp_enqueue_style('tnp-admin-fields', $newsletter_url . '/admin/fields.css', [], NEWSLETTER_VERSION);
465
- wp_enqueue_style('tnp-admin-widgets', $newsletter_url . '/admin/widgets.css', [], NEWSLETTER_VERSION);
466
- wp_enqueue_style('tnp-admin-extensions', $newsletter_url . '/admin/extensions.css', [], NEWSLETTER_VERSION);
467
-
468
- $translations_array = array(
469
- 'save_to_update_counter' => __('Save the newsletter to update the counter!', 'newsletter')
470
- );
471
- wp_localize_script('tnp-admin', 'tnp_translations', $translations_array);
472
-
473
- wp_enqueue_script('tnp-jquery-vmap', $newsletter_url . '/vendor/jqvmap/jquery.vmap.min.js', ['jquery'], NEWSLETTER_VERSION);
474
- wp_enqueue_script('tnp-jquery-vmap-world', $newsletter_url . '/vendor/jqvmap/jquery.vmap.world.js', ['tnp-jquery-vmap'], NEWSLETTER_VERSION);
475
- wp_enqueue_style('tnp-jquery-vmap', $newsletter_url . '/vendor/jqvmap/jqvmap.min.css', [], NEWSLETTER_VERSION);
476
-
477
- wp_register_script('tnp-chart', $newsletter_url . '/vendor/chartjs/Chart.min.js', ['jquery'], NEWSLETTER_VERSION);
478
-
479
- wp_enqueue_script('tnp-color-picker', $newsletter_url . '/vendor/spectrum/spectrum.min.js', ['jquery']);
480
- wp_enqueue_style('tnp-color-picker', $newsletter_url . '/vendor/spectrum/spectrum.min.css', [], NEWSLETTER_VERSION);
481
- }
482
-
483
- function shortcode_newsletter_replace($attrs, $content) {
484
- $content = do_shortcode($content);
485
- $content = $this->replace($content, $this->get_user_from_request(), $this->get_email_from_request());
486
- return $content;
487
- }
488
-
489
- function is_admin_page() {
490
- if (!isset($_GET['page'])) {
491
- return false;
492
- }
493
- $page = $_GET['page'];
494
- return strpos($page, 'newsletter_') === 0;
495
- }
496
-
497
- function hook_admin_init() {
498
- // Verificare il contesto
499
- if (isset($_GET['page']) && $_GET['page'] === 'newsletter_main_welcome')
500
- return;
501
- if (get_option('newsletter_show_welcome')) {
502
- delete_option('newsletter_show_welcome');
503
- wp_redirect(admin_url('admin.php?page=newsletter_main_welcome'));
504
- }
505
- }
506
-
507
- function hook_admin_head() {
508
- // Small global rule for sidebar menu entries
509
- echo '<style>';
510
- echo '.tnp-side-menu { color: #E67E22!important; }';
511
- echo '</style>';
512
- }
513
-
514
- function relink($text, $email_id, $user_id, $email_token = '') {
515
- return NewsletterStatistics::instance()->relink($text, $email_id, $user_id, $email_token);
516
- }
517
-
518
- /**
519
- * Runs every 5 minutes and look for emails that need to be processed.
520
- */
521
- function hook_newsletter() {
522
-
523
- $this->logger->debug(__METHOD__ . '> Start');
524
-
525
- if (!$this->check_transient('engine', NEWSLETTER_CRON_INTERVAL)) {
526
- $this->logger->debug(__METHOD__ . '> Engine already active, exit');
527
- return;
528
- }
529
-
530
- $emails = $this->get_results("select * from " . NEWSLETTER_EMAILS_TABLE . " where status='sending' and send_on<" . time() . " order by id asc");
531
- $this->logger->debug(__METHOD__ . '> Emails found in sending status: ' . count($emails));
532
-
533
- foreach ($emails as $email) {
534
- $this->logger->debug(__METHOD__ . '> Start newsletter ' . $email->id);
535
- $email->options = maybe_unserialize($email->options);
536
- $r = $this->send($email);
537
- $this->logger->debug(__METHOD__ . '> End newsletter ' . $email->id);
538
- if (!$r) {
539
- $this->logger->debug(__METHOD__ . '> Engine returned false, there is no more capacity');
540
- break;
541
- }
542
- }
543
- // Remove the semaphore so the delivery engine can be activated again
544
- $this->delete_transient('engine');
545
-
546
- $this->logger->debug(__METHOD__ . '> End');
547
- }
548
-
549
- function get_send_speed($email = null) {
550
- $this->logger->debug(__METHOD__ . '> Computing delivery speed');
551
- $mailer = $this->get_mailer();
552
- $speed = (int) $mailer->get_speed();
553
- if (!$speed) {
554
- $this->logger->debug(__METHOD__ . '> Speed not set by mailer, use the default');
555
- $speed = (int) $this->options['scheduler_max'];
556
- } else {
557
- $this->logger->debug(__METHOD__ . '> Speed set by mailer');
558
- }
559
-
560
- //$speed = (int) apply_filters('newsletter_send_speed', $speed, $email);
561
- // Fail safe setting
562
- $runs_per_hour = $this->get_runs_per_hour();
563
- if (!$speed || $speed < $runs_per_hour) {
564
- return $runs_per_hour;
565
- }
566
-
567
- $this->logger->debug(__METHOD__ . '> Speed: ' . $speed);
568
- return $speed;
569
- }
570
-
571
- function get_send_delay() {
572
- return 0;
573
- }
574
-
575
- function skip_this_run($email = null) {
576
- return (boolean) apply_filters('newsletter_send_skip', false, $email);
577
- }
578
-
579
- function get_runs_per_hour() {
580
- return (int) (3600 / NEWSLETTER_CRON_INTERVAL);
581
- }
582
-
583
- function get_emails_per_run() {
584
- $speed = $this->get_send_speed();
585
- $max = (int)($speed / $this->get_runs_per_hour());
586
-
587
- return $max;
588
- }
589
-
590
- function get_max_emails($email) {
591
- // Obsolete, here from Speed Control Addon
592
- $max = (int) apply_filters('newsletter_send_max_emails', $this->max_emails, $email);
593
-
594
- return min($max, $this->max_emails);
595
- }
596
-
597
- function fix_email($email) {
598
- if (empty($email->query)) {
599
- $email->query = "select * from " . NEWSLETTER_USERS_TABLE . " where status='C'";
600
- }
601
- if (empty($email->id)) {
602
- $email->id = 0;
603
- }
604
- }
605
-
606
- function send_setup() {
607
- $this->logger->debug(__METHOD__ . '> Setup delivery engine');
608
- if (is_null($this->max_emails)) {
609
- $this->max_emails = $this->get_emails_per_run();
610
- $this->logger->debug(__METHOD__ . '> Max emails: ' . $this->max_emails);
611
- ignore_user_abort(true);
612
-
613
- @set_time_limit(NEWSLETTER_CRON_INTERVAL + 30);
614
-
615
- $max_time = (int) (@ini_get('max_execution_time') * 0.95);
616
- if ($max_time == 0 || $max_time > NEWSLETTER_CRON_INTERVAL) {
617
- $max_time = (int) (NEWSLETTER_CRON_INTERVAL * 0.95);
618
- }
619
-
620
- $this->time_limit = $this->time_start + $max_time;
621
-
622
- $this->logger->debug(__METHOD__ . '> Max time set to ' . $max_time);
623
- } else {
624
- $this->logger->debug(__METHOD__ . '> Already setup');
625
- }
626
- }
627
-
628
- function time_exceeded() {
629
- if ($this->time_limit && time() > $this->time_limit) {
630
- $this->logger->info(__METHOD__ . '> Max execution time limit reached');
631
- return true;
632
- }
633
- }
634
-
635
- /**
636
- * Sends an email to targeted users or to given users. If a list of users is given (usually a list of test users)
637
- * the query inside the email to retrieve users is not used.
638
- *
639
- * @global wpdb $wpdb
640
- * @global type $newsletter_feed
641
- * @param TNP_Email $email
642
- * @param array $users
643
- * @return boolean|WP_Error True if the process completed, false if limits was reached. On false the caller should no continue to call it with other emails.
644
- */
645
- function send($email, $users = null, $test = false) {
646
- global $wpdb;
647
-
648
- $this->logger->info(__METHOD__ . '> Send email ' . $email->id);
649
-
650
- $this->send_setup();
651
-
652
- if ($this->max_emails <= 0) {
653
- $this->logger->info(__METHOD__ . '> No more capacity');
654
- return false;
655
- }
656
-
657
- if (is_array($email)) {
658
- $email = (object) $email;
659
- }
660
-
661
- $this->fix_email($email);
662
-
663
- // This stops the update of last_id and sent fields since
664
- // it's not a scheduled delivery but a test or something else (like an autoresponder)
665
- $supplied_users = $users != null;
666
-
667
- if (!$supplied_users) {
668
-
669
- if ($this->skip_this_run($email)) {
670
- return true;
671
- }
672
-
673
- // Speed change for specific email by Speed Control Addon
674
- $max_emails = $this->get_max_emails($email);
675
- if ($max_emails <= 0) {
676
- return true;
677
- }
678
-
679
- $query = $email->query;
680
- $query .= " and id>" . $email->last_id . " order by id limit " . $max_emails;
681
-
682
- $this->logger->debug(__METHOD__ . '> Query: ' . $query);
683
-
684
- //Retrieve subscribers
685
- $users = $this->get_results($query);
686
-
687
- $this->logger->debug(__METHOD__ . '> Loaded subscribers: ' . count($users));
688
-
689
- // If there was a database error, return error
690
- if ($users === false) {
691
- return new WP_Error('1', 'Unable to query subscribers, check the logs');
692
- }
693
-
694
- if (empty($users)) {
695
- $this->logger->info(__METHOD__ . '> No more users, set as sent');
696
- $wpdb->query("update " . NEWSLETTER_EMAILS_TABLE . " set status='sent', total=sent where id=" . $email->id . " limit 1");
697
- do_action('newsletter_ended_sending_newsletter', $email);
698
- return true;
699
- }
700
- } else {
701
- $this->logger->info(__METHOD__ . '> Subscribers supplied');
702
- }
703
-
704
- $start_time = microtime(true);
705
- $count = 0;
706
- $result = true;
707
-
708
- $mailer = $this->get_mailer();
709
-
710
- $batch_size = $mailer->get_batch_size();
711
-
712
- $this->logger->debug(__METHOD__ . '> Batch size: ' . $batch_size);
713
-
714
- // For batch size == 1 (normal condition) we optimize
715
- if ($batch_size == 1) {
716
-
717
- foreach ($users as $user) {
718
-
719
- $this->logger->debug(__METHOD__ . '> Processing user ID: ' . $user->id);
720
- $user = apply_filters('newsletter_send_user', $user);
721
- $message = $this->build_message($email, $user);
722
-
723
- // Save even test emails since people wants to see some stats even for test emails. Stats are reset upon the real "send" of a newsletter
724
- $this->save_sent_message($message);
725
-
726
- //Se non è un test incremento il contatore delle email spedite. Perchè incremento prima di spedire??
727
- if (!$test) {
728
- $wpdb->query("update " . NEWSLETTER_EMAILS_TABLE . " set sent=sent+1, last_id=" . $user->id . " where id=" . $email->id . " limit 1");
729
- }
730
-
731
- $r = $mailer->send($message);
732
-
733
- if (!empty($message->error)) {
734
- $this->logger->error($message);
735
- $this->save_sent_message($message);
736
- }
737
-
738
- if (is_wp_error($r)) {
739
- $this->logger->error($r);
740
-
741
- // For fatal error, the newsletter status i changed to error (and the delivery stopped)
742
- if (!$test && $r->get_error_code() == NewsletterMailer::ERROR_FATAL) {
743
- $this->set_error_state_of_email($email, $r->get_error_message());
744
- return $r;
745
- }
746
- }
747
-
748
- if (!$supplied_users && !$test && $this->time_exceeded()) {
749
- $result = false;
750
- break;
751
- }
752
- }
753
-
754
- $this->max_emails--;
755
- $count++;
756
- } else {
757
-
758
- $chunks = array_chunk($users, $batch_size);
759
-
760
- foreach ($chunks as $chunk) {
761
-
762
- $messages = [];
763
-
764
- // Peeparing a batch of messages
765
- foreach ($chunk as $user) {
766
- $this->logger->debug(__METHOD__ . '> Processing user ID: ' . $user->id);
767
- $user = apply_filters('newsletter_send_user', $user);
768
- $message = $this->build_message($email, $user);
769
- $this->save_sent_message($message);
770
- $messages[] = $message;
771
-
772
- if (!$test) {
773
- $wpdb->query("update " . NEWSLETTER_EMAILS_TABLE . " set sent=sent+1, last_id=" . $user->id . " where id=" . $email->id . " limit 1");
774
- }
775
- $this->max_emails--;
776
- $count++;
777
- }
778
-
779
- $r = $mailer->send_batch($messages);
780
-
781
- // Updating the status of the sent messages
782
- foreach ($messages as $message) {
783
- if (!empty($message->error)) {
784
- $this->save_sent_message($message);
785
- }
786
- }
787
-
788
- // The batch went in error
789
- if (is_wp_error($r)) {
790
- $this->logger->error($r);
791
-
792
- if (!$test && $r->get_error_code() == NewsletterMailer::ERROR_FATAL) {
793
- $this->set_error_state_of_email($email, $r->get_error_message());
794
- return $r;
795
- }
796
- }
797
-
798
- if (!$supplied_users && !$test && $this->time_exceeded()) {
799
- $result = false;
800
- break;
801
- }
802
- }
803
- }
804
-
805
- $end_time = microtime(true);
806
-
807
- // Stats only for newsletter with enough emails in a batch (we exclude the Autoresponder since it send one email per call)
808
- if (!$test && !$supplied_users && $count > 5) {
809
- $this->update_send_stats($start_time, $end_time, $count, $result);
810
- }
811
-
812
- // Cached general statistics are reset
813
- if (!$test) {
814
- NewsletterStatistics::instance()->reset_stats_time($email->id);
815
- }
816
-
817
- $this->logger->info(__METHOD__ . '> End run for email ' . $email->id);
818
-
819
- return $result;
820
- }
821
-
822
- function update_send_stats($start_time, $end_time, $count, $result) {
823
- $send_calls = get_option('newsletter_diagnostic_send_calls', []);
824
- $send_calls[] = [$start_time, $end_time, $count, $result];
825
-
826
- if (count($send_calls) > 100) {
827
- array_shift($send_calls);
828
- }
829
-
830
- update_option('newsletter_diagnostic_send_calls', $send_calls, false);
831
- }
832
-
833
- /**
834
- * @param TNP_Email $email
835
- */
836
- private function set_error_state_of_email($email, $message = '') {
837
- // Handle only message type at the moment
838
- if ($email->type !== 'message') {
839
- return;
840
- }
841
-
842
- do_action('newsletter_error_on_sending', $email, $message);
843
-
844
- $edited_email = new TNP_Email();
845
- $edited_email->id = $email->id;
846
- $edited_email->status = TNP_Email::STATUS_ERROR;
847
- $edited_email->options = $email->options;
848
- $edited_email->options['error_message'] = $message;
849
-
850
- $this->save_email($edited_email);
851
- }
852
-
853
- /**
854
- *
855
- * @param TNP_Email $email
856
- * @param TNP_User $user
857
- * @return \TNP_Mailer_Message
858
- */
859
- function build_message($email, $user) {
860
-
861
- $message = new TNP_Mailer_Message();
862
-
863
- $message->to = $user->email;
864
-
865
- $message->headers = [];
866
- $message->headers['Precedence'] = 'bulk';
867
- $message->headers['X-Newsletter-Email-Id'] = $email->id;
868
- $message->headers['X-Auto-Response-Suppress'] = 'OOF, AutoReply';
869
- $message->headers = apply_filters('newsletter_message_headers', $message->headers, $email, $user);
870
-
871
- $message->body = preg_replace('/data-json=".*?"/is', '', $email->message);
872
- $message->body = preg_replace('/ +/s', ' ', $message->body);
873
- $message->body = $this->replace($message->body, $user, $email);
874
- if ($this->options['do_shortcodes']) {
875
- $message->body = do_shortcode($message->body);
876
- }
877
- $message->body = apply_filters('newsletter_message_html', $message->body, $email, $user);
878
-
879
- $message->body_text = $this->replace($email->message_text, $user, $email);
880
- $message->body_text = apply_filters('newsletter_message_text', $message->body_text, $email, $user);
881
-
882
- if ($email->track == 1) {
883
- $message->body = $this->relink($message->body, $email->id, $user->id, $email->token);
884
- }
885
-
886
- $message->subject = $this->replace($email->subject, $user);
887
- $message->subject = apply_filters('newsletter_message_subject', $message->subject, $email, $user);
888
-
889
- if (!empty($email->options['sender_email'])) {
890
- $message->from = $email->options['sender_email'];
891
- } else {
892
- $message->from = $this->options['sender_email'];
893
- }
894
-
895
- if (!empty($email->options['sender_name'])) {
896
- $message->from_name = $email->options['sender_name'];
897
- } else {
898
- $message->from_name = $this->options['sender_name'];
899
- }
900
-
901
- $message->email_id = $email->id;
902
- $message->user_id = $user->id;
903
-
904
- return apply_filters('newsletter_message', $message, $email, $user);
905
- }
906
-
907
- /**
908
- *
909
- * @param TNP_Mailer_Message $message
910
- * @param int $status
911
- * @param string $error
912
- */
913
- function save_sent_message($message) {
914
- global $wpdb;
915
-
916
- if (!$message->user_id || !$message->email_id) {
917
- return;
918
- }
919
- $status = empty($message->error) ? 0 : 1;
920
-
921
- $error = mb_substr($message->error, 0, 250);
922
-
923
- $this->query($wpdb->prepare("insert into " . $wpdb->prefix . 'newsletter_sent (user_id, email_id, time, status, error) values (%d, %d, %d, %d, %s) on duplicate key update time=%d, status=%d, error=%s', $message->user_id, $message->email_id, time(), $status, $error, time(), $status, $error));
924
- }
925
-
926
- /**
927
- * @deprecated since version 7.3.0
928
- */
929
- function limits_exceeded() {
930
- return false;
931
- }
932
-
933
- /**
934
- * @deprecated since version 6.0.0
935
- */
936
- function register_mail_method($callable) {
937
- }
938
-
939
- function register_mailer($mailer) {
940
- if ($mailer instanceof NewsletterMailer) {
941
- $this->mailer = $mailer;
942
- }
943
- }
944
-
945
- /**
946
- * Returns the current registered mailer which must be used to send emails.
947
- *
948
- * @return NewsletterMailer
949
- */
950
- function get_mailer() {
951
- if ($this->mailer) {
952
- return $this->mailer;
953
- }
954
-
955
- do_action('newsletter_register_mailer');
956
-
957
- if (!$this->mailer) {
958
- // Compatibility
959
- $smtp = $this->get_options('smtp');
960
- if (!empty($smtp['enabled'])) {
961
- $this->mailer = new NewsletterDefaultSMTPMailer($smtp);
962
- } else {
963
- $this->mailer = new NewsletterDefaultMailer();
964
- }
965
- }
966
- return $this->mailer;
967
- }
968
-
969
- /**
970
- *
971
- * @param TNP_Mailer_Message $message
972
- * @return type
973
- */
974
- function deliver($message) {
975
- $mailer = $this->get_mailer();
976
- if (empty($message->from))
977
- $message->from = $this->options['sender_email'];
978
- if (empty($message->from_name))
979
- $mailer->from_name = $this->options['sender_name'];
980
- return $mailer->send($message);
981
- }
982
-
983
- /**
984
- *
985
- * @param type $to
986
- * @param type $subject
987
- * @param string|array $message If string is considered HTML, is array should contains the keys "html" and "text"
988
- * @param type $headers
989
- * @param type $enqueue
990
- * @param type $from
991
- * @return boolean
992
- */
993
- function mail($to, $subject, $message, $headers = array(), $enqueue = false, $from = false) {
994
-
995
- if (empty($subject)) {
996
- $this->logger->error('mail> Subject empty, skipped');
997
- return true;
998
- }
999
-
1000
- $mailer_message = new TNP_Mailer_Message();
1001
- $mailer_message->to = $to;
1002
- $mailer_message->subject = $subject;
1003
- $mailer_message->from = $this->options['sender_email'];
1004
- $mailer_message->from_name = $this->options['sender_name'];
1005
-
1006
- if (!empty($headers)) {
1007
- $mailer_message->headers = $headers;
1008
- }
1009
- $mailer_message->headers['X-Auto-Response-Suppress'] = 'OOF, AutoReply';
1010
-
1011
- // Message carrige returns and line feeds clean up
1012
- if (!is_array($message)) {
1013
- $mailer_message->body = $this->clean_eol($message);
1014
- } else {
1015
- if (!empty($message['text'])) {
1016
- $mailer_message->body_text = $this->clean_eol($message['text']);
1017
- }
1018
-
1019
- if (!empty($message['html'])) {
1020
- $mailer_message->body = $this->clean_eol($message['html']);
1021
- }
1022
- }
1023
-
1024
- $this->logger->debug($mailer_message);
1025
-
1026
- $mailer = $this->get_mailer();
1027
-
1028
- $r = $mailer->send($mailer_message);
1029
-
1030
- return !is_wp_error($r);
1031
- }
1032
-
1033
- function hook_deactivate() {
1034
- wp_clear_scheduled_hook('newsletter');
1035
- }
1036
-
1037
- function find_file($file1, $file2) {
1038
- if (is_file($file1))
1039
- return $file1;
1040
- return $file2;
1041
- }
1042
-
1043
- function hook_site_transient_update_plugins($value) {
1044
- static $extra_response = array();
1045
-
1046
- //$this->logger->debug('Update plugins transient called');
1047
-
1048
- if (!$value || !is_object($value)) {
1049
- //$this->logger->info('Empty object');
1050
- return $value;
1051
- }
1052
-
1053
- if (!isset($value->response) || !is_array($value->response)) {
1054
- $value->response = array();
1055
- }
1056
-
1057
- // Already computed? Use it! (this filter is called many times in a single request)
1058
- if ($extra_response) {
1059
- //$this->logger->debug('Already updated');
1060
- $value->response = array_merge($value->response, $extra_response);
1061
- return $value;
1062
- }
1063
-
1064
- $extensions = $this->getTnpExtensions();
1065
-
1066
- // Ops...
1067
- if (!$extensions) {
1068
- return $value;
1069
- }
1070
-
1071
- foreach ($extensions as $extension) {
1072
- unset($value->response[$extension->wp_slug]);
1073
- unset($value->no_update[$extension->wp_slug]);
1074
- }
1075
-
1076
- // Someone doesn't want our addons updated, let respect it (this constant should be defined in wp-config.php)
1077
- if (!NEWSLETTER_EXTENSION_UPDATE) {
1078
- //$this->logger->info('Updates disabled');
1079
- return $value;
1080
- }
1081
-
1082
- include_once(ABSPATH . 'wp-admin/includes/plugin.php');
1083
-
1084
- // Ok, that is really bad (should we remove it? is there a minimum WP version?)
1085
- if (!function_exists('get_plugin_data')) {
1086
- //$this->logger->error('No get_plugin_data function available!');
1087
- return $value;
1088
- }
1089
-
1090
- $license_key = $this->get_license_key();
1091
-
1092
- // Here we prepare the update information BUT do not add the link to the package which is privided
1093
- // by our Addons Manager (due to WP policies)
1094
- foreach ($extensions as $extension) {
1095
-
1096
- // Patch for names convention
1097
- $extension->plugin = $extension->wp_slug;
1098
-
1099
- //$this->logger->debug('Processing ' . $extension->plugin);
1100
- //$this->logger->debug($extension);
1101
-
1102
- $plugin_data = false;
1103
- if (file_exists(WP_PLUGIN_DIR . '/' . $extension->plugin)) {
1104
- $plugin_data = get_plugin_data(WP_PLUGIN_DIR . '/' . $extension->plugin, false, false);
1105
- } else if (file_exists(WPMU_PLUGIN_DIR . '/' . $extension->plugin)) {
1106
- $plugin_data = get_plugin_data(WPMU_PLUGIN_DIR . '/' . $extension->plugin, false, false);
1107
- }
1108
-
1109
- if (!$plugin_data) {
1110
- //$this->logger->debug('Seems not installed');
1111
- continue;
1112
- }
1113
-
1114
- $plugin = new stdClass();
1115
- $plugin->id = $extension->id;
1116
- $plugin->slug = $extension->slug;
1117
- $plugin->plugin = $extension->plugin;
1118
- $plugin->new_version = $extension->version;
1119
- $plugin->url = $extension->url;
1120
- if (class_exists('NewsletterExtensions')) {
1121
- // NO filters here!
1122
- $plugin->package = NewsletterExtensions::$instance->get_package($extension->id, $license_key);
1123
- } else {
1124
- $plugin->package = '';
1125
- }
1126
- // [banners] => Array
1127
- // (
1128
- // [2x] => https://ps.w.org/wp-rss-aggregator/assets/banner-1544x500.png?rev=2040548
1129
- // [1x] => https://ps.w.org/wp-rss-aggregator/assets/banner-772x250.png?rev=2040548
1130
- // )
1131
- // [icons] => Array
1132
- // (
1133
- // [2x] => https://ps.w.org/advanced-custom-fields/assets/icon-256x256.png?rev=1082746
1134
- // [1x] => https://ps.w.org/advanced-custom-fields/assets/icon-128x128.png?rev=1082746
1135
- // )
1136
- if (version_compare($extension->version, $plugin_data['Version']) > 0) {
1137
- //$this->logger->debug('There is a new version');
1138
- $extra_response[$extension->plugin] = $plugin;
1139
- } else {
1140
- // Maybe useless...
1141
- //$this->logger->debug('There is NOT a new version');
1142
- $value->no_update[$extension->plugin] = $plugin;
1143
- }
1144
- //$this->logger->debug('Added');
1145
- }
1146
-
1147
- $value->response = array_merge($value->response, $extra_response);
1148
-
1149
- return $value;
1150
- }
1151
-
1152
- /**
1153
- * @deprecated since version 6.1.9
1154
- */
1155
- function get_extension_version($extension_id) {
1156
- return null;
1157
- }
1158
-
1159
- /**
1160
- * @deprecated since version 6.1.9
1161
- */
1162
- function set_extension_update_data($value, $extension) {
1163
- return $value;
1164
- }
1165
-
1166
- /**
1167
- * Retrieve the extensions form the tnp site
1168
- * @return array
1169
- */
1170
- function getTnpExtensions() {
1171
-
1172
- $extensions_json = get_transient('tnp_extensions_json');
1173
-
1174
- if (empty($extensions_json)) {
1175
- $url = "http://www.thenewsletterplugin.com/wp-content/extensions.json?ver=" . NEWSLETTER_VERSION;
1176
- $extensions_response = wp_remote_get($url);
1177
-
1178
- if (is_wp_error($extensions_response)) {
1179
- // Cache anyway for blogs which cannot connect outside
1180
- $extensions_json = '[]';
1181
- set_transient('tnp_extensions_json', $extensions_json, 72 * 60 * 60);
1182
- $this->logger->error($extensions_response);
1183
- } else {
1184
-
1185
- $extensions_json = wp_remote_retrieve_body($extensions_response);
1186
-
1187
- // Not clear cases
1188
- if (empty($extensions_json) || !json_decode($extensions_json)) {
1189
- $this->logger->error('Invalid json from thenewsletterplugin.com: retrying in 72 hours');
1190
- $this->logger->error('JSON: ' . $extensions_json);
1191
- $extensions_json = '[]';
1192
- }
1193
- set_transient('tnp_extensions_json', $extensions_json, 72 * 60 * 60);
1194
- }
1195
- }
1196
-
1197
- $extensions = json_decode($extensions_json);
1198
-
1199
- return $extensions;
1200
- }
1201
-
1202
- function clear_extensions_cache() {
1203
- delete_transient('tnp_extensions_json');
1204
- }
1205
-
1206
- var $panels = array();
1207
-
1208
- function add_panel($key, $panel) {
1209
- if (!isset($this->panels[$key]))
1210
- $this->panels[$key] = array();
1211
- if (!isset($panel['id']))
1212
- $panel['id'] = sanitize_key($panel['label']);
1213
- $this->panels[$key][] = $panel;
1214
- }
1215
-
1216
- function has_license() {
1217
- return !empty($this->options['contract_key']);
1218
- }
1219
-
1220
- function get_sender_name() {
1221
- return $this->options['sender_name'];
1222
- }
1223
-
1224
- function get_sender_email() {
1225
- return $this->options['sender_email'];
1226
- }
1227
-
1228
- /**
1229
- *
1230
- * @return int
1231
- */
1232
- function get_newsletter_page_id() {
1233
- return (int) $this->options['page'];
1234
- }
1235
-
1236
- /**
1237
- *
1238
- * @return WP_Post
1239
- */
1240
- function get_newsletter_page() {
1241
- return get_post($this->get_newsletter_page_id());
1242
- }
1243
-
1244
- /**
1245
- * Returns the Newsletter dedicated page URL or an alternative URL if that page if not
1246
- * configured or not available.
1247
- *
1248
- * @staticvar string $url
1249
- * @return string
1250
- */
1251
- function get_newsletter_page_url($language = '') {
1252
-
1253
- $page = $this->get_newsletter_page();
1254
-
1255
- if (!$page || $page->post_status !== 'publish') {
1256
- return $this->build_action_url('m');
1257
- }
1258
-
1259
- $newsletter_page_url = get_permalink($page->ID);
1260
- if ($language && $newsletter_page_url) {
1261
- if (class_exists('SitePress')) {
1262
- $newsletter_page_url = apply_filters('wpml_permalink', $newsletter_page_url, $language, true);
1263
- }
1264
- if (function_exists('pll_get_post')) {
1265
- $translated_page = get_permalink(pll_get_post($page->ID, $language));
1266
- if ($translated_page) {
1267
- $newsletter_page_url = $translated_page;
1268
- }
1269
- }
1270
- }
1271
-
1272
- return $newsletter_page_url;
1273
- }
1274
-
1275
- function get_license_key() {
1276
- if (defined('NEWSLETTER_LICENSE_KEY')) {
1277
- return NEWSLETTER_LICENSE_KEY;
1278
- } else {
1279
- if (!empty($this->options['contract_key'])) {
1280
- return trim($this->options['contract_key']);
1281
- }
1282
- }
1283
- return false;
1284
- }
1285
-
1286
- /**
1287
- * Get the data connected to the specified license code on man settings.
1288
- *
1289
- * - false if no license is present
1290
- * - WP_Error if something went wrong if getting the license data
1291
- * - object with expiration and addons list
1292
- *
1293
- * @param boolean $refresh
1294
- * @return \WP_Error|boolean|object
1295
- */
1296
- function get_license_data($refresh = false) {
1297
-
1298
- $this->logger->debug('Getting license data');
1299
-
1300
- $license_key = $this->get_license_key();
1301
- if (empty($license_key)) {
1302
- $this->logger->debug('License was empty');
1303
- delete_transient('newsletter_license_data');
1304
- return false;
1305
- }
1306
-
1307
- if (!$refresh) {
1308
- $license_data = get_transient('newsletter_license_data');
1309
- if ($license_data !== false && is_object($license_data)) {
1310
- $this->logger->debug('License data found on cache');
1311
- return $license_data;
1312
- }
1313
- }
1314
-
1315
- $this->logger->debug('Refreshing the license data');
1316
-
1317
- $license_data_url = 'https://www.thenewsletterplugin.com/wp-content/plugins/file-commerce-pro/get-license-data.php';
1318
-
1319
- $response = wp_remote_post($license_data_url, array(
1320
- 'body' => array('k' => $license_key)
1321
- ));
1322
-
1323
- // Fall back to http...
1324
- if (is_wp_error($response)) {
1325
- $this->logger->error($response);
1326
- $this->logger->error('Falling back to http');
1327
- $license_data_url = str_replace('https', 'http', $license_data_url);
1328
- $response = wp_remote_post($license_data_url, array(
1329
- 'body' => array('k' => $license_key)
1330
- ));
1331
- if (is_wp_error($response)) {
1332
- $this->logger->error($response);
1333
- set_transient('newsletter_license_data', $response, DAY_IN_SECONDS);
1334
- return $response;
1335
- }
1336
- }
1337
-
1338
- $download_message = 'You can download all addons from www.thenewsletterplugin.com if your license is valid.';
1339
-
1340
- if (wp_remote_retrieve_response_code($response) != '200') {
1341
- $this->logger->error('license data error: ' . wp_remote_retrieve_response_code($response));
1342
- $data = new WP_Error(wp_remote_retrieve_response_code($response), 'License validation service error. <br>' . $download_message);
1343
- set_transient('newsletter_license_data', $data, DAY_IN_SECONDS);
1344
- return $data;
1345
- }
1346
-
1347
- $json = wp_remote_retrieve_body($response);
1348
- $data = json_decode($json);
1349
-
1350
- if (!is_object($data)) {
1351
- $this->logger->error($json);
1352
- $data = new WP_Error(1, 'License validation service error. <br>' . $download_message);
1353
- set_transient('newsletter_license_data', $data, DAY_IN_SECONDS);
1354
- return $data;
1355
- }
1356
-
1357
- if (isset($data->message)) {
1358
- $data = new WP_Error(1, $data->message . ' (check the license on Newsletter main settings)');
1359
- set_transient('newsletter_license_data', $data, DAY_IN_SECONDS);
1360
- return $data;
1361
- }
1362
-
1363
- $expiration = WEEK_IN_SECONDS;
1364
- // If the license expires in few days, make the transient live only few days, so it will be refreshed
1365
- if ($data->expire > time() && $data->expire - time() < WEEK_IN_SECONDS) {
1366
- $expiration = $data->expire - time();
1367
- }
1368
- set_transient('newsletter_license_data', $data, $expiration);
1369
-
1370
- return $data;
1371
- }
1372
-
1373
- /**
1374
- * @deprecated
1375
- * @param type $license_key
1376
- * @return \WP_Error
1377
- */
1378
- public static function check_license($license_key) {
1379
- $response = wp_remote_get('http://www.thenewsletterplugin.com/wp-content/plugins/file-commerce-pro/check.php?k=' . urlencode($license_key), array('sslverify' => false));
1380
- if (is_wp_error($response)) {
1381
- /* @var $response WP_Error */
1382
- return new WP_Error(-1, 'It seems that your blog cannot contact the license validator. Ask your provider to unlock the HTTP/HTTPS connections to www.thenewsletterplugin.com<br>'
1383
- . esc_html($response->get_error_code()) . ' - ' . esc_html($response->get_error_message()));
1384
- } else if ($response['response']['code'] != 200) {
1385
- return new WP_Error(-1, '[' . $response['response']['code'] . '] The license seems expired or not valid, please check your <a href="https://www.thenewsletterplugin.com/account">license code and status</a>, thank you.'
1386
- . '<br>You can anyway download the professional extension from https://www.thenewsletterplugin.com.');
1387
- } elseif ($expires = json_decode(wp_remote_retrieve_body($response))) {
1388
- return array('expires' => $expires->expire, 'message' => 'Your license is valid and expires on ' . esc_html(date('Y-m-d', $expires->expire)));
1389
- } else {
1390
- return new WP_Error(-1, 'Unable to detect the license expiration. Debug data to report to the support: <code>' . esc_html(wp_remote_retrieve_body($response)) . '</code>');
1391
- }
1392
- }
1393
-
1394
- function add_notice_to_chosen_profile_page_hook($post_states, $post) {
1395
-
1396
- if ($post->ID == $this->options['page']) {
1397
- $post_states[] = __('Newsletter plugin page, do not delete', 'newsletter');
1398
- }
1399
-
1400
- return $post_states;
1401
- }
1402
-
1403
- }
1404
-
1405
- $newsletter = Newsletter::instance();
1406
-
1407
- if (is_admin()) {
1408
- require_once NEWSLETTER_DIR . '/system/system.php';
1409
- }
1410
-
1411
- require_once NEWSLETTER_DIR . '/subscription/subscription.php';
1412
- require_once NEWSLETTER_DIR . '/unsubscription/unsubscription.php';
1413
- require_once NEWSLETTER_DIR . '/profile/profile.php';
1414
- require_once NEWSLETTER_DIR . '/emails/emails.php';
1415
- require_once NEWSLETTER_DIR . '/users/users.php';
1416
- require_once NEWSLETTER_DIR . '/statistics/statistics.php';
1417
- require_once NEWSLETTER_DIR . '/widget/standard.php';
1418
- require_once NEWSLETTER_DIR . '/widget/minimal.php';
1
+ <?php
2
+
3
+ /*
4
+ Plugin Name: Newsletter
5
+ Plugin URI: https://www.thenewsletterplugin.com/plugins/newsletter
6
+ Description: Newsletter is a cool plugin to create your own subscriber list, to send newsletters, to build your business. <strong>Before update give a look to <a href="https://www.thenewsletterplugin.com/category/release">this page</a> to know what's changed.</strong>
7
+ Version: 7.3.6
8
+ Author: Stefano Lissa & The Newsletter Team
9
+ Author URI: https://www.thenewsletterplugin.com
10
+ Disclaimer: Use at your own risk. No warranty expressed or implied is provided.
11
+ Text Domain: newsletter
12
+ License: GPLv2 or later
13
+ Requires at least: 4.6
14
+ Requires PHP: 5.6
15
+
16
+ Copyright 2009-2022 The Newsletter Team (email: info@thenewsletterplugin.com, web: https://www.thenewsletterplugin.com)
17
+
18
+ Newsletter is free software: you can redistribute it and/or modify
19
+ it under the terms of the GNU General Public License as published by
20
+ the Free Software Foundation, either version 2 of the License, or
21
+ any later version.
22
+
23
+ Newsletter is distributed in the hope that it will be useful,
24
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
25
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26
+ GNU General Public License for more details.
27
+
28
+ You should have received a copy of the GNU General Public License
29
+ along with Newsletter. If not, see https://www.gnu.org/licenses/gpl-2.0.html.
30
+
31
+ */
32
+
33
+ if (version_compare(phpversion(), '5.6', '<')) {
34
+ add_action('admin_notices', function () {
35
+ echo '<div class="notice notice-error"><p>PHP version 5.6 or greater is required for Newsletter. Ask your provider to upgrade. <a href="https://www.php.net/supported-versions.php" target="_blank">Read more on PHP versions</a></p></div>';
36
+ });
37
+ return;
38
+ }
39
+
40
+ define('NEWSLETTER_VERSION', '7.3.6');
41
+
42
+ global $newsletter, $wpdb;
43
+
44
+ // For acceptance tests, DO NOT CHANGE
45
+ if (!defined('NEWSLETTER_DEBUG'))
46
+ define('NEWSLETTER_DEBUG', false);
47
+
48
+ if (!defined('NEWSLETTER_EXTENSION_UPDATE'))
49
+ define('NEWSLETTER_EXTENSION_UPDATE', true);
50
+
51
+ if (!defined('NEWSLETTER_EMAILS_TABLE'))
52
+ define('NEWSLETTER_EMAILS_TABLE', $wpdb->prefix . 'newsletter_emails');
53
+
54
+ if (!defined('NEWSLETTER_USERS_TABLE'))
55
+ define('NEWSLETTER_USERS_TABLE', $wpdb->prefix . 'newsletter');
56
+
57
+ if (!defined('NEWSLETTER_STATS_TABLE'))
58
+ define('NEWSLETTER_STATS_TABLE', $wpdb->prefix . 'newsletter_stats');
59
+
60
+ if (!defined('NEWSLETTER_SENT_TABLE'))
61
+ define('NEWSLETTER_SENT_TABLE', $wpdb->prefix . 'newsletter_sent');
62
+
63
+ define('NEWSLETTER_SLUG', 'newsletter');
64
+
65
+ define('NEWSLETTER_DIR', __DIR__);
66
+ define('NEWSLETTER_INCLUDES_DIR', __DIR__ . '/includes');
67
+
68
+ // Deperacted since plugin can change the plugins_url()
69
+ define('NEWSLETTER_URL', WP_PLUGIN_URL . '/newsletter');
70
+
71
+ if (!defined('NEWSLETTER_LIST_MAX'))
72
+ define('NEWSLETTER_LIST_MAX', 40);
73
+
74
+ if (!defined('NEWSLETTER_PROFILE_MAX'))
75
+ define('NEWSLETTER_PROFILE_MAX', 20);
76
+
77
+ if (!defined('NEWSLETTER_FORMS_MAX'))
78
+ define('NEWSLETTER_FORMS_MAX', 10);
79
+
80
+ if (!defined('NEWSLETTER_HEADER'))
81
+ define('NEWSLETTER_HEADER', true);
82
+
83
+ require_once NEWSLETTER_INCLUDES_DIR . '/module.php';
84
+ require_once NEWSLETTER_INCLUDES_DIR . '/TNP.php';
85
+ require_once NEWSLETTER_INCLUDES_DIR . '/cron.php';
86
+
87
+ class Newsletter extends NewsletterModule {
88
+
89
+ // Limits to respect to avoid memory, time or provider limits
90
+ var $time_start;
91
+ var $time_limit = 0;
92
+ var $max_emails = null;
93
+
94
+ var $mailer = null;
95
+
96
+ var $action = '';
97
+
98
+ /** @var Newsletter */
99
+ static $instance;
100
+
101
+ const STATUS_NOT_CONFIRMED = 'S';
102
+ const STATUS_CONFIRMED = 'C';
103
+
104
+ /**
105
+ * @return Newsletter
106
+ */
107
+ static function instance() {
108
+ if (self::$instance == null) {
109
+ self::$instance = new Newsletter();
110
+ }
111
+ return self::$instance;
112
+ }
113
+
114
+ function __construct() {
115
+
116
+ // Grab it before a plugin decides to remove it.
117
+ if (isset($_GET['na'])) {
118
+ $this->action = $_GET['na'];
119
+ }
120
+ if (isset($_POST['na'])) {
121
+ $this->action = $_POST['na'];
122
+ }
123
+
124
+ $this->time_start = time();
125
+
126
+ parent::__construct('main', '1.6.7', null, ['info', 'smtp']);
127
+
128
+ add_action('plugins_loaded', [$this, 'hook_plugins_loaded']);
129
+ add_action('init', [$this, 'hook_init'], 1);
130
+ add_action('wp_loaded', [$this, 'hook_wp_loaded'], 1);
131
+
132
+ add_action('newsletter', [$this, 'hook_newsletter'], 1);
133
+
134
+ register_activation_hook(__FILE__, [$this, 'hook_activate']);
135
+ register_deactivation_hook(__FILE__, [$this, 'hook_deactivate']);
136
+
137
+ add_action('admin_init', [$this, 'hook_admin_init']);
138
+
139
+ if (is_admin()) {
140
+ add_action('admin_head', [$this, 'hook_admin_head']);
141
+
142
+ // Protection against strange schedule removal on some installations
143
+ if (!wp_next_scheduled('newsletter') && (!defined('WP_INSTALLING') || !WP_INSTALLING)) {
144
+ wp_schedule_event(time() + 30, 'newsletter', 'newsletter');
145
+ }
146
+
147
+ add_action('admin_menu', [$this, 'add_extensions_menu'], 90);
148
+
149
+ add_filter('display_post_states', [$this, 'add_notice_to_chosen_profile_page_hook'], 10, 2);
150
+
151
+ if ($this->is_admin_page()) {
152
+ // Remove the emoji replacer to save to database the original emoji characters (see even woocommerce for the same problem)
153
+ remove_action('admin_print_scripts', 'print_emoji_detection_script');
154
+ add_action('admin_enqueue_scripts', [$this, 'hook_admin_enqueue_scripts']);
155
+ }
156
+
157
+ }
158
+ }
159
+
160
+ function hook_plugins_loaded() {
161
+ // Used to load dependant modules
162
+ do_action('newsletter_loaded', NEWSLETTER_VERSION);
163
+
164
+ if (function_exists('load_plugin_textdomain')) {
165
+ load_plugin_textdomain('newsletter', false, plugin_basename(__DIR__) . '/languages');
166
+ }
167
+ }
168
+
169
+ function hook_init() {
170
+ global $wpdb;
171
+
172
+ if (isset($this->options['debug']) && $this->options['debug'] == 1) {
173
+ ini_set('log_errors', 1);
174
+ ini_set('error_log', WP_CONTENT_DIR . '/logs/newsletter/php-' . date('Y-m') . '-' . get_option('newsletter_logger_secret') . '.txt');
175
+ }
176
+
177
+ add_shortcode('newsletter_replace', [$this, 'shortcode_newsletter_replace']);
178
+
179
+ add_filter('site_transient_update_plugins', [$this, 'hook_site_transient_update_plugins']);
180
+
181
+ if (is_admin()) {
182
+ if (!class_exists('NewsletterExtensions')) {
183
+
184
+ add_filter('plugin_row_meta', function ($plugin_meta, $plugin_file) {
185
+
186
+ static $slugs = array();
187
+ if (empty($slugs)) {
188
+ $addons = $this->getTnpExtensions();
189
+ if ($addons) {
190
+ foreach ($addons as $addon) {
191
+ $slugs[] = $addon->wp_slug;
192
+ }
193
+ }
194
+ }
195
+ if (array_search($plugin_file, $slugs) !== false) {
196
+
197
+ $plugin_meta[] = '<a href="admin.php?page=newsletter_main_extensions" style="font-weight: bold">Newsletter Addons Manager required</a>';
198
+ }
199
+ return $plugin_meta;
200
+ }, 10, 2);
201
+ }
202
+
203
+ add_action('in_admin_header', array($this, 'hook_in_admin_header'), 1000);
204
+
205
+ if ($this->is_admin_page()) {
206
+
207
+ $dismissed = get_option('newsletter_dismissed', []);
208
+
209
+ if (isset($_GET['dismiss'])) {
210
+ $dismissed[$_GET['dismiss']] = 1;
211
+ update_option('newsletter_dismissed', $dismissed);
212
+ wp_redirect($_SERVER['HTTP_REFERER']);
213
+ exit();
214
+ }
215
+ }
216
+ } else {
217
+ add_action('wp_enqueue_scripts', [$this, 'hook_wp_enqueue_scripts']);
218
+ }
219
+
220
+ do_action('newsletter_init');
221
+ }
222
+
223
+ function hook_wp_loaded() {
224
+ if (empty($this->action)) {
225
+ return;
226
+ }
227
+
228
+ if ($this->action == 'test') {
229
+ // This response is tested, do not change it!
230
+ echo 'ok';
231
+ die();
232
+ }
233
+
234
+ if ($this->action === 'nul') {
235
+ $this->dienow('This link is not active on newsletter preview', 'You can send a test message to test subscriber to have the real working link.');
236
+ }
237
+
238
+ $user = $this->get_user_from_request();
239
+ $email = $this->get_email_from_request();
240
+ do_action('newsletter_action', $this->action, $user, $email);
241
+ }
242
+
243
+ function hook_activate() {
244
+ // Ok, why? When the plugin is not active WordPress may remove the scheduled "newsletter" action because
245
+ // the every-five-minutes schedule named "newsletter" is not present.
246
+ // Since the activation does not forces an upgrade, that schedule must be reactivated here. It is activated on
247
+ // the upgrade method as well for the user which upgrade the plugin without deactivte it (many).
248
+ if (!wp_next_scheduled('newsletter')) {
249
+ wp_schedule_event(time() + 30, 'newsletter', 'newsletter');
250
+ }
251
+
252
+ $install_time = get_option('newsletter_install_time');
253
+ if (!$install_time) {
254
+ update_option('newsletter_install_time', time(), false);
255
+ }
256
+
257
+ touch(NEWSLETTER_LOG_DIR . '/index.html');
258
+
259
+ Newsletter::instance()->upgrade();
260
+ NewsletterUsers::instance()->upgrade();
261
+ NewsletterEmails::instance()->upgrade();
262
+ NewsletterSubscription::instance()->upgrade();
263
+ NewsletterStatistics::instance()->upgrade();
264
+ NewsletterProfile::instance()->upgrade();
265
+ }
266
+
267
+ function first_install() {
268
+ parent::first_install();
269
+ update_option('newsletter_show_welcome', '1', false);
270
+ }
271
+
272
+ function upgrade() {
273
+ global $wpdb, $charset_collate;
274
+
275
+ require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
276
+
277
+ parent::upgrade();
278
+
279
+ $sql = "CREATE TABLE `" . $wpdb->prefix . "newsletter_emails` (
280
+ `id` int(11) NOT NULL AUTO_INCREMENT,
281
+ `language` varchar(10) NOT NULL DEFAULT '',
282
+ `subject` varchar(255) NOT NULL DEFAULT '',
283
+ `message` longtext,
284
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
285
+ `status` enum('new','sending','sent','paused','error') NOT NULL DEFAULT 'new',
286
+ `total` int(11) NOT NULL DEFAULT '0',
287
+ `last_id` int(11) NOT NULL DEFAULT '0',
288
+ `sent` int(11) NOT NULL DEFAULT '0',
289
+ `track` int(11) NOT NULL DEFAULT '1',
290
+ `list` int(11) NOT NULL DEFAULT '0',
291
+ `type` varchar(50) NOT NULL DEFAULT '',
292
+ `query` longtext,
293
+ `editor` tinyint(4) NOT NULL DEFAULT '0',
294
+ `sex` varchar(20) NOT NULL DEFAULT '',
295
+ `theme` varchar(50) NOT NULL DEFAULT '',
296
+ `message_text` longtext,
297
+ `preferences` longtext,
298
+ `send_on` int(11) NOT NULL DEFAULT '0',
299
+ `token` varchar(10) NOT NULL DEFAULT '',
300
+ `options` longtext,
301
+ `private` tinyint(1) NOT NULL DEFAULT '0',
302
+ `click_count` int(10) unsigned NOT NULL DEFAULT '0',
303
+ `version` varchar(10) NOT NULL DEFAULT '',
304
+ `open_count` int(10) unsigned NOT NULL DEFAULT '0',
305
+ `unsub_count` int(10) unsigned NOT NULL DEFAULT '0',
306
+ `error_count` int(10) unsigned NOT NULL DEFAULT '0',
307
+ `stats_time` int(10) unsigned NOT NULL DEFAULT '0',
308
+ `updated` int(10) unsigned NOT NULL DEFAULT '0',
309
+ PRIMARY KEY (`id`)
310
+ ) $charset_collate;";
311
+
312
+ dbDelta($sql);
313
+
314
+ // WP does not manage composite primary key when it tries to upgrade a table...
315
+ $suppress_errors = $wpdb->suppress_errors(true);
316
+
317
+ dbDelta("CREATE TABLE " . $wpdb->prefix . "newsletter_sent (
318
+ email_id int(10) unsigned NOT NULL DEFAULT '0',
319
+ user_id int(10) unsigned NOT NULL DEFAULT '0',
320
+ status tinyint(1) unsigned NOT NULL DEFAULT '0',
321
+ open tinyint(1) unsigned NOT NULL DEFAULT '0',
322
+ time int(10) unsigned NOT NULL DEFAULT '0',
323
+ error varchar(255) NOT NULL DEFAULT '',
324
+ ip varchar(100) NOT NULL DEFAULT '',
325
+ PRIMARY KEY (email_id,user_id),
326
+ KEY user_id (user_id),
327
+ KEY email_id (email_id)
328
+ ) $charset_collate;");
329
+ $wpdb->suppress_errors($suppress_errors);
330
+
331
+ // Some setting check to avoid the common support request for mis-configurations
332
+ $options = $this->get_options();
333
+
334
+ if (empty($options['scheduler_max']) || !is_numeric($options['scheduler_max'])) {
335
+ $options['scheduler_max'] = 100;
336
+ $this->save_options($options);
337
+ }
338
+
339
+ wp_clear_scheduled_hook('newsletter');
340
+ wp_schedule_event(time() + 30, 'newsletter', 'newsletter');
341
+
342
+ if (!empty($this->options['editor'])) {
343
+ if (empty($this->options['roles'])) {
344
+ $this->options['roles'] = array('editor');
345
+ unset($this->options['editor']);
346
+ }
347
+ $this->save_options($this->options);
348
+ }
349
+
350
+ // Clear the addons and license caches
351
+ delete_transient('newsletter_license_data');
352
+ $this->clear_extensions_cache();
353
+
354
+ touch(NEWSLETTER_LOG_DIR . '/index.html');
355
+
356
+ return true;
357
+ }
358
+
359
+ function is_allowed() {
360
+ if (current_user_can('administrator')) {
361
+ return true;
362
+ }
363
+ if (!empty($this->options['roles'])) {
364
+ foreach ($this->options['roles'] as $role) {
365
+ if (current_user_can($role)) {
366
+ return true;
367
+ }
368
+ }
369
+ }
370
+ return false;
371
+ }
372
+
373
+ function admin_menu() {
374
+ if (!$this->is_allowed()) {
375
+ return;
376
+ }
377
+
378
+ add_menu_page('Newsletter', 'Newsletter', 'exist', 'newsletter_main_index', '', plugins_url('newsletter') . '/admin/images/menu-icon.png', '30.333');
379
+
380
+ $this->add_menu_page('index', __('Dashboard', 'newsletter'));
381
+ $this->add_admin_page('info', __('Company info', 'newsletter'));
382
+
383
+ if (current_user_can('administrator')) {
384
+ $this->add_menu_page('welcome', __('Welcome', 'newsletter'));
385
+ $this->add_menu_page('main', __('Settings', 'newsletter'));
386
+
387
+ // Pages not on menu
388
+ $this->add_admin_page('smtp', 'SMTP');
389
+ $this->add_admin_page('diagnostic', __('Diagnostic', 'newsletter'));
390
+ $this->add_admin_page('test', __('Test', 'newsletter'));
391
+ }
392
+ }
393
+
394
+ function add_extensions_menu() {
395
+ if (!class_exists('NewsletterExtensions')) {
396
+ $this->add_menu_page('extensions', '<span style="color:#27AE60; font-weight: bold;">' . __('Addons', 'newsletter') . '</span>');
397
+ }
398
+ }
399
+
400
+ function hook_in_admin_header() {
401
+ if (!$this->is_admin_page()) {
402
+ add_action('admin_notices', array($this, 'hook_admin_notices'));
403
+ return;
404
+ }
405
+ remove_all_actions('admin_notices');
406
+ remove_all_actions('all_admin_notices');
407
+ add_action('admin_notices', array($this, 'hook_admin_notices'));
408
+ }
409
+
410
+ function hook_admin_notices() {
411
+ // Check of Newsletter dedicated page
412
+ if (!empty($this->options['page'])) {
413
+ if (get_post_status($this->options['page']) !== 'publish') {
414
+ echo '<div class="notice notice-error"><p>The Newsletter dedicated page is not published. <a href="', site_url('/wp-admin/post.php') . '?post=', $this->options['page'], '&action=edit"><strong>Edit the page</strong></a> or <a href="admin.php?page=newsletter_main_main"><strong>review the main settings</strong></a>.</p></div>';
415
+ }
416
+ }
417
+
418
+ if (isset($this->options['debug']) && $this->options['debug'] == 1) {
419
+ echo '<div class="notice notice-warning"><p>The Newsletter plugin is in <strong>debug mode</strong>. When done change it on Newsletter <a href="admin.php?page=newsletter_main_main"><strong>main settings</strong></a>. Do not keep the debug mode active on production sites.</p></div>';
420
+ }
421
+ }
422
+
423
+ function hook_wp_enqueue_scripts() {
424
+ if (empty($this->options['css_disabled']) && apply_filters('newsletter_enqueue_style', true)) {
425
+ wp_enqueue_style('newsletter', plugins_url('newsletter') . '/style.css', [], NEWSLETTER_VERSION);
426
+ if (!empty($this->options['css'])) {
427
+ wp_add_inline_style('newsletter', $this->options['css']);
428
+ }
429
+ } else {
430
+ if (!empty($this->options['css'])) {
431
+ add_action('wp_head', function () {
432
+ echo '<style>', $this->options['css'], '</style>';
433
+ });
434
+ }
435
+ }
436
+ }
437
+
438
+ function hook_admin_enqueue_scripts() {
439
+
440
+ $newsletter_url = plugins_url('newsletter');
441
+ wp_enqueue_script('jquery-ui-tabs');
442
+ wp_enqueue_script('jquery-ui-tooltip');
443
+ wp_enqueue_script('jquery-ui-draggable');
444
+ wp_enqueue_media();
445
+
446
+ wp_enqueue_script('tnp-admin', $newsletter_url . '/admin/admin.js', ['jquery'], NEWSLETTER_VERSION);
447
+
448
+ wp_enqueue_style('tnp-select2', $newsletter_url . '/vendor/select2/css/select2.min.css', [], NEWSLETTER_VERSION);
449
+ wp_enqueue_script('tnp-select2', $newsletter_url . '/vendor/select2/js/select2.min.js', ['jquery'], NEWSLETTER_VERSION);
450
+
451
+ wp_enqueue_style('tnp-modal', $newsletter_url . '/admin/modal.css', [], NEWSLETTER_VERSION);
452
+ wp_enqueue_script('tnp-modal', $newsletter_url . '/admin/modal.js', ['jquery'], NEWSLETTER_VERSION, true);
453
+
454
+ wp_enqueue_style('tnp-toast', $newsletter_url . '/admin/toast.css', [], NEWSLETTER_VERSION);
455
+ wp_enqueue_script('tnp-toast', $newsletter_url . '/admin/toast.js', ['jquery'], NEWSLETTER_VERSION);
456
+
457
+ wp_enqueue_style('tnp-admin-font', 'https://use.typekit.net/jlj2wjy.css');
458
+ wp_enqueue_style('tnp-admin-fontawesome', $newsletter_url . '/vendor/fa/css/all.min.css', [], NEWSLETTER_VERSION);
459
+ wp_enqueue_style('tnp-admin-jquery-ui', $newsletter_url . '/vendor/jquery-ui/jquery-ui.min.css', [], NEWSLETTER_VERSION);
460
+ wp_enqueue_style('tnp-admin', $newsletter_url . '/admin/admin.css', [], NEWSLETTER_VERSION);
461
+ wp_enqueue_style('tnp-admin-dropdown', $newsletter_url . '/admin/dropdown.css', [], NEWSLETTER_VERSION);
462
+ wp_enqueue_style('tnp-admin-tabs', $newsletter_url . '/admin/tabs.css', [], NEWSLETTER_VERSION);
463
+ wp_enqueue_style('tnp-admin-controls', $newsletter_url . '/admin/controls.css', [], NEWSLETTER_VERSION);
464
+ wp_enqueue_style('tnp-admin-fields', $newsletter_url . '/admin/fields.css', [], NEWSLETTER_VERSION);
465
+ wp_enqueue_style('tnp-admin-widgets', $newsletter_url . '/admin/widgets.css', [], NEWSLETTER_VERSION);
466
+ wp_enqueue_style('tnp-admin-extensions', $newsletter_url . '/admin/extensions.css', [], NEWSLETTER_VERSION);
467
+
468
+ $translations_array = array(
469
+ 'save_to_update_counter' => __('Save the newsletter to update the counter!', 'newsletter')
470
+ );
471
+ wp_localize_script('tnp-admin', 'tnp_translations', $translations_array);
472
+
473
+ wp_enqueue_script('tnp-jquery-vmap', $newsletter_url . '/vendor/jqvmap/jquery.vmap.min.js', ['jquery'], NEWSLETTER_VERSION);
474
+ wp_enqueue_script('tnp-jquery-vmap-world', $newsletter_url . '/vendor/jqvmap/jquery.vmap.world.js', ['tnp-jquery-vmap'], NEWSLETTER_VERSION);
475
+ wp_enqueue_style('tnp-jquery-vmap', $newsletter_url . '/vendor/jqvmap/jqvmap.min.css', [], NEWSLETTER_VERSION);
476
+
477
+ wp_register_script('tnp-chart', $newsletter_url . '/vendor/chartjs/Chart.min.js', ['jquery'], NEWSLETTER_VERSION);
478
+
479
+ wp_enqueue_script('tnp-color-picker', $newsletter_url . '/vendor/spectrum/spectrum.min.js', ['jquery']);
480
+ wp_enqueue_style('tnp-color-picker', $newsletter_url . '/vendor/spectrum/spectrum.min.css', [], NEWSLETTER_VERSION);
481
+ }
482
+
483
+ function shortcode_newsletter_replace($attrs, $content) {
484
+ $content = do_shortcode($content);
485
+ $content = $this->replace($content, $this->get_user_from_request(), $this->get_email_from_request());
486
+ return $content;
487
+ }
488
+
489
+ function is_admin_page() {
490
+ if (!isset($_GET['page'])) {
491
+ return false;
492
+ }
493
+ $page = $_GET['page'];
494
+ return strpos($page, 'newsletter_') === 0;
495
+ }
496
+
497
+ function hook_admin_init() {
498
+ // Verificare il contesto
499
+ if (isset($_GET['page']) && $_GET['page'] === 'newsletter_main_welcome')
500
+ return;
501
+ if (get_option('newsletter_show_welcome')) {
502
+ delete_option('newsletter_show_welcome');
503
+ wp_redirect(admin_url('admin.php?page=newsletter_main_welcome'));
504
+ }
505
+ }
506
+
507
+ function hook_admin_head() {
508
+ // Small global rule for sidebar menu entries
509
+ echo '<style>';
510
+ echo '.tnp-side-menu { color: #E67E22!important; }';
511
+ echo '</style>';
512
+ }
513
+
514
+ function relink($text, $email_id, $user_id, $email_token = '') {
515
+ return NewsletterStatistics::instance()->relink($text, $email_id, $user_id, $email_token);
516
+ }
517
+
518
+ /**
519
+ * Runs every 5 minutes and look for emails that need to be processed.
520
+ */
521
+ function hook_newsletter() {
522
+
523
+ $this->logger->debug(__METHOD__ . '> Start');
524
+
525
+ if (!$this->check_transient('engine', NEWSLETTER_CRON_INTERVAL)) {
526
+ $this->logger->debug(__METHOD__ . '> Engine already active, exit');
527
+ return;
528
+ }
529
+
530
+ $emails = $this->get_results("select * from " . NEWSLETTER_EMAILS_TABLE . " where status='sending' and send_on<" . time() . " order by id asc");
531
+ $this->logger->debug(__METHOD__ . '> Emails found in sending status: ' . count($emails));
532
+
533
+ foreach ($emails as $email) {
534
+ $this->logger->debug(__METHOD__ . '> Start newsletter ' . $email->id);
535
+ $email->options = maybe_unserialize($email->options);
536
+ $r = $this->send($email);
537
+ $this->logger->debug(__METHOD__ . '> End newsletter ' . $email->id);
538
+ if (!$r) {
539
+ $this->logger->debug(__METHOD__ . '> Engine returned false, there is no more capacity');
540
+ break;
541
+ }
542
+ }
543
+ // Remove the semaphore so the delivery engine can be activated again
544
+ $this->delete_transient('engine');
545
+
546
+ $this->logger->debug(__METHOD__ . '> End');
547
+ }
548
+
549
+ function get_send_speed($email = null) {
550
+ $this->logger->debug(__METHOD__ . '> Computing delivery speed');
551
+ $mailer = $this->get_mailer();
552
+ $speed = (int) $mailer->get_speed();
553
+ if (!$speed) {
554
+ $this->logger->debug(__METHOD__ . '> Speed not set by mailer, use the default');
555
+ $speed = (int) $this->options['scheduler_max'];
556
+ } else {
557
+ $this->logger->debug(__METHOD__ . '> Speed set by mailer');
558
+ }
559
+
560
+ //$speed = (int) apply_filters('newsletter_send_speed', $speed, $email);
561
+ // Fail safe setting
562
+ $runs_per_hour = $this->get_runs_per_hour();
563
+ if (!$speed || $speed < $runs_per_hour) {
564
+ return $runs_per_hour;
565
+ }
566
+
567
+ $this->logger->debug(__METHOD__ . '> Speed: ' . $speed);
568
+ return $speed;
569
+ }
570
+
571
+ function get_send_delay() {
572
+ return 0;
573
+ }
574
+
575
+ function skip_this_run($email = null) {
576
+ return (boolean) apply_filters('newsletter_send_skip', false, $email);
577
+ }
578
+
579
+ function get_runs_per_hour() {
580
+ return (int) (3600 / NEWSLETTER_CRON_INTERVAL);
581
+ }
582
+
583
+ function get_emails_per_run() {
584
+ $speed = $this->get_send_speed();
585
+ $max = (int)($speed / $this->get_runs_per_hour());
586
+
587
+ return $max;
588
+ }
589
+
590
+ function get_max_emails($email) {
591
+ // Obsolete, here from Speed Control Addon
592
+ $max = (int) apply_filters('newsletter_send_max_emails', $this->max_emails, $email);
593
+
594
+ return min($max, $this->max_emails);
595
+ }
596
+
597
+ function fix_email($email) {
598
+ if (empty($email->query)) {
599
+ $email->query = "select * from " . NEWSLETTER_USERS_TABLE . " where status='C'";
600
+ }
601
+ if (empty($email->id)) {
602
+ $email->id = 0;
603
+ }
604
+ }
605
+
606
+ function send_setup() {
607
+ $this->logger->debug(__METHOD__ . '> Setup delivery engine');
608
+ if (is_null($this->max_emails)) {
609
+ $this->max_emails = $this->get_emails_per_run();
610
+ $this->logger->debug(__METHOD__ . '> Max emails: ' . $this->max_emails);
611
+ ignore_user_abort(true);
612
+
613
+ @set_time_limit(NEWSLETTER_CRON_INTERVAL + 30);
614
+
615
+ $max_time = (int) (@ini_get('max_execution_time') * 0.95);
616
+ if ($max_time == 0 || $max_time > NEWSLETTER_CRON_INTERVAL) {
617
+ $max_time = (int) (NEWSLETTER_CRON_INTERVAL * 0.95);
618
+ }
619
+
620
+ $this->time_limit = $this->time_start + $max_time;
621
+
622
+ $this->logger->debug(__METHOD__ . '> Max time set to ' . $max_time);
623
+ } else {
624
+ $this->logger->debug(__METHOD__ . '> Already setup');
625
+ }
626
+ }
627
+
628
+ function time_exceeded() {
629
+ if ($this->time_limit && time() > $this->time_limit) {
630
+ $this->logger->info(__METHOD__ . '> Max execution time limit reached');
631
+ return true;
632
+ }
633
+ }
634
+
635
+ /**
636
+ * Sends an email to targeted users or to given users. If a list of users is given (usually a list of test users)
637
+ * the query inside the email to retrieve users is not used.
638
+ *
639
+ * @global wpdb $wpdb
640
+ * @global type $newsletter_feed
641
+ * @param TNP_Email $email
642
+ * @param array $users
643
+ * @return boolean|WP_Error True if the process completed, false if limits was reached. On false the caller should no continue to call it with other emails.
644
+ */
645
+ function send($email, $users = null, $test = false) {
646
+ global $wpdb;
647
+
648
+ if (is_array($email)) {
649
+ $email = (object) $email;
650
+ }
651
+
652
+ $this->logger->info(__METHOD__ . '> Send email ' . $email->id);
653
+
654
+ $this->send_setup();
655
+
656
+ if ($this->max_emails <= 0) {
657
+ $this->logger->info(__METHOD__ . '> No more capacity');
658
+ return false;
659
+ }
660
+
661
+ $this->fix_email($email);
662
+
663
+ // This stops the update of last_id and sent fields since
664
+ // it's not a scheduled delivery but a test or something else (like an autoresponder)
665
+ $supplied_users = $users != null;
666
+
667
+ if (!$supplied_users) {
668
+
669
+ if ($this->skip_this_run($email)) {
670
+ return true;
671
+ }
672
+
673
+ // Speed change for specific email by Speed Control Addon
674
+ $max_emails = $this->get_max_emails($email);
675
+ if ($max_emails <= 0) {
676
+ return true;
677
+ }
678
+
679
+ $query = $email->query;
680
+ $query .= " and id>" . $email->last_id . " order by id limit " . $max_emails;
681
+
682
+ $this->logger->debug(__METHOD__ . '> Query: ' . $query);
683
+
684
+ //Retrieve subscribers
685
+ $users = $this->get_results($query);
686
+
687
+ $this->logger->debug(__METHOD__ . '> Loaded subscribers: ' . count($users));
688
+
689
+ // If there was a database error, return error
690
+ if ($users === false) {
691
+ return new WP_Error('1', 'Unable to query subscribers, check the logs');
692
+ }
693
+
694
+ if (empty($users)) {
695
+ $this->logger->info(__METHOD__ . '> No more users, set as sent');
696
+ $wpdb->query("update " . NEWSLETTER_EMAILS_TABLE . " set status='sent', total=sent where id=" . $email->id . " limit 1");
697
+ do_action('newsletter_ended_sending_newsletter', $email);
698
+ return true;
699
+ }
700
+ } else {
701
+ $this->logger->info(__METHOD__ . '> Subscribers supplied');
702
+ }
703
+
704
+ $start_time = microtime(true);
705
+ $count = 0;
706
+ $result = true;
707
+
708
+ $mailer = $this->get_mailer();
709
+
710
+ $batch_size = $mailer->get_batch_size();
711
+
712
+ $this->logger->debug(__METHOD__ . '> Batch size: ' . $batch_size);
713
+
714
+ // For batch size == 1 (normal condition) we optimize
715
+ if ($batch_size == 1) {
716
+
717
+ foreach ($users as $user) {
718
+
719
+ $this->logger->debug(__METHOD__ . '> Processing user ID: ' . $user->id);
720
+ $user = apply_filters('newsletter_send_user', $user);
721
+ $message = $this->build_message($email, $user);
722
+
723
+ // Save even test emails since people wants to see some stats even for test emails. Stats are reset upon the real "send" of a newsletter
724
+ $this->save_sent_message($message);
725
+
726
+ //Se non è un test incremento il contatore delle email spedite. Perchè incremento prima di spedire??
727
+ if (!$test) {
728
+ $wpdb->query("update " . NEWSLETTER_EMAILS_TABLE . " set sent=sent+1, last_id=" . $user->id . " where id=" . $email->id . " limit 1");
729
+ }
730
+
731
+ $r = $mailer->send($message);
732
+
733
+ if (!empty($message->error)) {
734
+ $this->logger->error($message);
735
+ $this->save_sent_message($message);
736
+ }
737
+
738
+ if (is_wp_error($r)) {
739
+ $this->logger->error($r);
740
+
741
+ // For fatal error, the newsletter status i changed to error (and the delivery stopped)
742
+ if (!$test && $r->get_error_code() == NewsletterMailer::ERROR_FATAL) {
743
+ $this->set_error_state_of_email($email, $r->get_error_message());
744
+ return $r;
745
+ }
746
+ }
747
+
748
+ if (!$supplied_users && !$test && $this->time_exceeded()) {
749
+ $result = false;
750
+ break;
751
+ }
752
+ }
753
+
754
+ $this->max_emails--;
755
+ $count++;
756
+ } else {
757
+
758
+ $chunks = array_chunk($users, $batch_size);
759
+
760
+ foreach ($chunks as $chunk) {
761
+
762
+ $messages = [];
763
+
764
+ // Peeparing a batch of messages
765
+ foreach ($chunk as $user) {
766
+ $this->logger->debug(__METHOD__ . '> Processing user ID: ' . $user->id);
767
+ $user = apply_filters('newsletter_send_user', $user);
768
+ $message = $this->build_message($email, $user);
769
+ $this->save_sent_message($message);
770
+ $messages[] = $message;
771
+
772
+ if (!$test) {
773
+ $wpdb->query("update " . NEWSLETTER_EMAILS_TABLE . " set sent=sent+1, last_id=" . $user->id . " where id=" . $email->id . " limit 1");
774
+ }
775
+ $this->max_emails--;
776
+ $count++;
777
+ }
778
+
779
+ $r = $mailer->send_batch($messages);
780
+
781
+ // Updating the status of the sent messages
782
+ foreach ($messages as $message) {
783
+ if (!empty($message->error)) {
784
+ $this->save_sent_message($message);
785
+ }
786
+ }
787
+
788
+ // The batch went in error
789
+ if (is_wp_error($r)) {
790
+ $this->logger->error($r);
791
+
792
+ if (!$test && $r->get_error_code() == NewsletterMailer::ERROR_FATAL) {
793
+ $this->set_error_state_of_email($email, $r->get_error_message());
794
+ return $r;
795
+ }
796
+ }
797
+
798
+ if (!$supplied_users && !$test && $this->time_exceeded()) {
799
+ $result = false;
800
+ break;
801
+ }
802
+ }
803
+ }
804
+
805
+ $end_time = microtime(true);
806
+
807
+ // Stats only for newsletter with enough emails in a batch (we exclude the Autoresponder since it send one email per call)
808
+ if (!$test && !$supplied_users && $count > 5) {
809
+ $this->update_send_stats($start_time, $end_time, $count, $result);
810
+ }
811
+
812
+ // Cached general statistics are reset
813
+ if (!$test) {
814
+ NewsletterStatistics::instance()->reset_stats_time($email->id);
815
+ }
816
+
817
+ $this->logger->info(__METHOD__ . '> End run for email ' . $email->id);
818
+
819
+ return $result;
820
+ }
821
+
822
+ function update_send_stats($start_time, $end_time, $count, $result) {
823
+ $send_calls = get_option('newsletter_diagnostic_send_calls', []);
824
+ $send_calls[] = [$start_time, $end_time, $count, $result];
825
+
826
+ if (count($send_calls) > 100) {
827
+ array_shift($send_calls);
828
+ }
829
+
830
+ update_option('newsletter_diagnostic_send_calls', $send_calls, false);
831
+ }
832
+
833
+ /**
834
+ * @param TNP_Email $email
835
+ */
836
+ private function set_error_state_of_email($email, $message = '') {
837
+ // Handle only message type at the moment
838
+ if ($email->type !== 'message') {
839
+ return;
840
+ }
841
+
842
+ do_action('newsletter_error_on_sending', $email, $message);
843
+
844
+ $edited_email = new TNP_Email();
845
+ $edited_email->id = $email->id;
846
+ $edited_email->status = TNP_Email::STATUS_ERROR;
847
+ $edited_email->options = $email->options;
848
+ $edited_email->options['error_message'] = $message;
849
+
850
+ $this->save_email($edited_email);
851
+ }
852
+
853
+ /**
854
+ *
855
+ * @param TNP_Email $email
856
+ * @param TNP_User $user
857
+ * @return \TNP_Mailer_Message
858
+ */
859
+ function build_message($email, $user) {
860
+
861
+ $message = new TNP_Mailer_Message();
862
+
863
+ $message->to = $user->email;
864
+
865
+ $message->headers = [];
866
+ $message->headers['Precedence'] = 'bulk';
867
+ $message->headers['X-Newsletter-Email-Id'] = $email->id;
868
+ $message->headers['X-Auto-Response-Suppress'] = 'OOF, AutoReply';
869
+ $message->headers = apply_filters('newsletter_message_headers', $message->headers, $email, $user);
870
+
871
+ $message->body = preg_replace('/data-json=".*?"/is', '', $email->message);
872
+ $message->body = preg_replace('/ +/s', ' ', $message->body);
873
+ $message->body = $this->replace($message->body, $user, $email);
874
+ if ($this->options['do_shortcodes']) {
875
+ $message->body = do_shortcode($message->body);
876
+ }
877
+ $message->body = apply_filters('newsletter_message_html', $message->body, $email, $user);
878
+
879
+ $message->body_text = $this->replace($email->message_text, $user, $email);
880
+ $message->body_text = apply_filters('newsletter_message_text', $message->body_text, $email, $user);
881
+
882
+ if ($email->track == 1) {
883
+ $message->body = $this->relink($message->body, $email->id, $user->id, $email->token);
884
+ }
885
+
886
+ $message->subject = $this->replace($email->subject, $user);
887
+ $message->subject = apply_filters('newsletter_message_subject', $message->subject, $email, $user);
888
+
889
+ if (!empty($email->options['sender_email'])) {
890
+ $message->from = $email->options['sender_email'];
891
+ } else {
892
+ $message->from = $this->options['sender_email'];
893
+ }
894
+
895
+ if (!empty($email->options['sender_name'])) {
896
+ $message->from_name = $email->options['sender_name'];
897
+ } else {
898
+ $message->from_name = $this->options['sender_name'];
899
+ }
900
+
901
+ $message->email_id = $email->id;
902
+ $message->user_id = $user->id;
903
+
904
+ return apply_filters('newsletter_message', $message, $email, $user);
905
+ }
906
+
907
+ /**
908
+ *
909
+ * @param TNP_Mailer_Message $message
910
+ * @param int $status
911
+ * @param string $error
912
+ */
913
+ function save_sent_message($message) {
914
+ global $wpdb;
915
+
916
+ if (!$message->user_id || !$message->email_id) {
917
+ return;
918
+ }
919
+ $status = empty($message->error) ? 0 : 1;
920
+
921
+ $error = mb_substr($message->error, 0, 250);
922
+
923
+ $this->query($wpdb->prepare("insert into " . $wpdb->prefix . 'newsletter_sent (user_id, email_id, time, status, error) values (%d, %d, %d, %d, %s) on duplicate key update time=%d, status=%d, error=%s', $message->user_id, $message->email_id, time(), $status, $error, time(), $status, $error));
924
+ }
925
+
926
+ /**
927
+ * @deprecated since version 7.3.0
928
+ */
929
+ function limits_exceeded() {
930
+ return false;
931
+ }
932
+
933
+ /**
934
+ * @deprecated since version 6.0.0
935
+ */
936
+ function register_mail_method($callable) {
937
+ }
938
+
939
+ function register_mailer($mailer) {
940
+ if ($mailer instanceof NewsletterMailer) {
941
+ $this->mailer = $mailer;
942
+ }
943
+ }
944
+
945
+ /**
946
+ * Returns the current registered mailer which must be used to send emails.
947
+ *
948
+ * @return NewsletterMailer
949
+ */
950
+ function get_mailer() {
951
+ if ($this->mailer) {
952
+ return $this->mailer;
953
+ }
954
+
955
+ do_action('newsletter_register_mailer');
956
+
957
+ if (!$this->mailer) {
958
+ // Compatibility
959
+ $smtp = $this->get_options('smtp');
960
+ if (!empty($smtp['enabled'])) {
961
+ $this->mailer = new NewsletterDefaultSMTPMailer($smtp);
962
+ } else {
963
+ $this->mailer = new NewsletterDefaultMailer();
964
+ }
965
+ }
966
+ return $this->mailer;
967
+ }
968
+
969
+ /**
970
+ *
971
+ * @param TNP_Mailer_Message $message
972
+ * @return type
973
+ */
974
+ function deliver($message) {
975
+ $mailer = $this->get_mailer();
976
+ if (empty($message->from))
977
+ $message->from = $this->options['sender_email'];
978
+ if (empty($message->from_name))
979
+ $mailer->from_name = $this->options['sender_name'];
980
+ return $mailer->send($message);
981
+ }
982
+
983
+ /**
984
+ *
985
+ * @param type $to
986
+ * @param type $subject
987
+ * @param string|array $message If string is considered HTML, is array should contains the keys "html" and "text"
988
+ * @param type $headers
989
+ * @param type $enqueue
990
+ * @param type $from
991
+ * @return boolean
992
+ */
993
+ function mail($to, $subject, $message, $headers = array(), $enqueue = false, $from = false) {
994
+
995
+ if (empty($subject)) {
996
+ $this->logger->error('mail> Subject empty, skipped');
997
+ return true;
998
+ }
999
+
1000
+ $mailer_message = new TNP_Mailer_Message();
1001
+ $mailer_message->to = $to;
1002
+ $mailer_message->subject = $subject;
1003
+ $mailer_message->from = $this->options['sender_email'];
1004
+ $mailer_message->from_name = $this->options['sender_name'];
1005
+
1006
+ if (!empty($headers)) {
1007
+ $mailer_message->headers = $headers;
1008
+ }
1009
+ $mailer_message->headers['X-Auto-Response-Suppress'] = 'OOF, AutoReply';
1010
+
1011
+ // Message carrige returns and line feeds clean up
1012
+ if (!is_array($message)) {
1013
+ $mailer_message->body = $this->clean_eol($message);
1014
+ } else {
1015
+ if (!empty($message['text'])) {
1016
+ $mailer_message->body_text = $this->clean_eol($message['text']);
1017
+ }
1018
+
1019
+ if (!empty($message['html'])) {
1020
+ $mailer_message->body = $this->clean_eol($message['html']);
1021
+ }
1022
+ }
1023
+
1024
+ $this->logger->debug($mailer_message);
1025
+
1026
+ $mailer = $this->get_mailer();
1027
+
1028
+ $r = $mailer->send($mailer_message);
1029
+
1030
+ return !is_wp_error($r);
1031
+ }
1032
+
1033
+ function hook_deactivate() {
1034
+ wp_clear_scheduled_hook('newsletter');
1035
+ }
1036
+
1037
+ function find_file($file1, $file2) {
1038
+ if (is_file($file1))
1039
+ return $file1;
1040
+ return $file2;
1041
+ }
1042
+
1043
+ function hook_site_transient_update_plugins($value) {
1044
+ static $extra_response = array();
1045
+
1046
+ //$this->logger->debug('Update plugins transient called');
1047
+
1048
+ if (!$value || !is_object($value)) {
1049
+ //$this->logger->info('Empty object');
1050
+ return $value;
1051
+ }
1052
+
1053
+ if (!isset($value->response) || !is_array($value->response)) {
1054
+ $value->response = array();
1055
+ }
1056
+
1057
+ // Already computed? Use it! (this filter is called many times in a single request)
1058
+ if ($extra_response) {
1059
+ //$this->logger->debug('Already updated');
1060
+ $value->response = array_merge($value->response, $extra_response);
1061
+ return $value;
1062
+ }
1063
+
1064
+ $extensions = $this->getTnpExtensions();
1065
+
1066
+ // Ops...
1067
+ if (!$extensions) {
1068
+ return $value;
1069
+ }
1070
+
1071
+ foreach ($extensions as $extension) {
1072
+ unset($value->response[$extension->wp_slug]);
1073
+ unset($value->no_update[$extension->wp_slug]);
1074
+ }
1075
+
1076
+ // Someone doesn't want our addons updated, let respect it (this constant should be defined in wp-config.php)
1077
+ if (!NEWSLETTER_EXTENSION_UPDATE) {
1078
+ //$this->logger->info('Updates disabled');
1079
+ return $value;
1080
+ }
1081
+
1082
+ include_once(ABSPATH . 'wp-admin/includes/plugin.php');
1083
+
1084
+ // Ok, that is really bad (should we remove it? is there a minimum WP version?)
1085
+ if (!function_exists('get_plugin_data')) {
1086
+ //$this->logger->error('No get_plugin_data function available!');
1087
+ return $value;
1088
+ }
1089
+
1090
+ $license_key = $this->get_license_key();
1091
+
1092
+ // Here we prepare the update information BUT do not add the link to the package which is privided
1093
+ // by our Addons Manager (due to WP policies)
1094
+ foreach ($extensions as $extension) {
1095
+
1096
+ // Patch for names convention
1097
+ $extension->plugin = $extension->wp_slug;
1098
+
1099
+ //$this->logger->debug('Processing ' . $extension->plugin);
1100
+ //$this->logger->debug($extension);
1101
+
1102
+ $plugin_data = false;
1103
+ if (file_exists(WP_PLUGIN_DIR . '/' . $extension->plugin)) {
1104
+ $plugin_data = get_plugin_data(WP_PLUGIN_DIR . '/' . $extension->plugin, false, false);
1105
+ } else if (file_exists(WPMU_PLUGIN_DIR . '/' . $extension->plugin)) {
1106
+ $plugin_data = get_plugin_data(WPMU_PLUGIN_DIR . '/' . $extension->plugin, false, false);
1107
+ }
1108
+
1109
+ if (!$plugin_data) {
1110
+ //$this->logger->debug('Seems not installed');
1111
+ continue;
1112
+ }
1113
+
1114
+ $plugin = new stdClass();
1115
+ $plugin->id = $extension->id;
1116
+ $plugin->slug = $extension->slug;
1117
+ $plugin->plugin = $extension->plugin;
1118
+ $plugin->new_version = $extension->version;
1119
+ $plugin->url = $extension->url;
1120
+ if (class_exists('NewsletterExtensions')) {
1121
+ // NO filters here!
1122
+ $plugin->package = NewsletterExtensions::$instance->get_package($extension->id, $license_key);
1123
+ } else {
1124
+ $plugin->package = '';
1125
+ }
1126
+ // [banners] => Array
1127
+ // (
1128
+ // [2x] => https://ps.w.org/wp-rss-aggregator/assets/banner-1544x500.png?rev=2040548
1129
+ // [1x] => https://ps.w.org/wp-rss-aggregator/assets/banner-772x250.png?rev=2040548
1130
+ // )
1131
+ // [icons] => Array
1132
+ // (
1133
+ // [2x] => https://ps.w.org/advanced-custom-fields/assets/icon-256x256.png?rev=1082746
1134
+ // [1x] => https://ps.w.org/advanced-custom-fields/assets/icon-128x128.png?rev=1082746
1135
+ // )
1136
+ if (version_compare($extension->version, $plugin_data['Version']) > 0) {
1137
+ //$this->logger->debug('There is a new version');
1138
+ $extra_response[$extension->plugin] = $plugin;
1139
+ } else {
1140
+ // Maybe useless...
1141
+ //$this->logger->debug('There is NOT a new version');
1142
+ $value->no_update[$extension->plugin] = $plugin;
1143
+ }
1144
+ //$this->logger->debug('Added');
1145
+ }
1146
+
1147
+ $value->response = array_merge($value->response, $extra_response);
1148
+
1149
+ return $value;
1150
+ }
1151
+
1152
+ /**
1153
+ * @deprecated since version 6.1.9
1154
+ */
1155
+ function get_extension_version($extension_id) {
1156
+ return null;
1157
+ }
1158
+
1159
+ /**
1160
+ * @deprecated since version 6.1.9
1161
+ */
1162
+ function set_extension_update_data($value, $extension) {
1163
+ return $value;
1164
+ }
1165
+
1166
+ /**
1167
+ * Retrieve the extensions form the tnp site
1168
+ * @return array
1169
+ */
1170
+ function getTnpExtensions() {
1171
+
1172
+ $extensions_json = get_transient('tnp_extensions_json');
1173
+
1174
+ if (empty($extensions_json)) {
1175
+ $url = "http://www.thenewsletterplugin.com/wp-content/extensions.json?ver=" . NEWSLETTER_VERSION;
1176
+ $extensions_response = wp_remote_get($url);
1177
+
1178
+ if (is_wp_error($extensions_response)) {
1179
+ // Cache anyway for blogs which cannot connect outside
1180
+ $extensions_json = '[]';
1181
+ set_transient('tnp_extensions_json', $extensions_json, 72 * 60 * 60);
1182
+ $this->logger->error($extensions_response);
1183
+ } else {
1184
+
1185
+ $extensions_json = wp_remote_retrieve_body($extensions_response);
1186
+
1187
+ // Not clear cases
1188
+ if (empty($extensions_json) || !json_decode($extensions_json)) {
1189
+ $this->logger->error('Invalid json from thenewsletterplugin.com: retrying in 72 hours');
1190
+ $this->logger->error('JSON: ' . $extensions_json);
1191
+ $extensions_json = '[]';
1192
+ }
1193
+ set_transient('tnp_extensions_json', $extensions_json, 72 * 60 * 60);
1194
+ }
1195
+ }
1196
+
1197
+ $extensions = json_decode($extensions_json);
1198
+
1199
+ return $extensions;
1200
+ }
1201
+
1202
+ function clear_extensions_cache() {
1203
+ delete_transient('tnp_extensions_json');
1204
+ }
1205
+
1206
+ var $panels = array();
1207
+
1208
+ function add_panel($key, $panel) {
1209
+ if (!isset($this->panels[$key]))
1210
+ $this->panels[$key] = array();
1211
+ if (!isset($panel['id']))
1212
+ $panel['id'] = sanitize_key($panel['label']);
1213
+ $this->panels[$key][] = $panel;
1214
+ }
1215
+
1216
+ function has_license() {
1217
+ return !empty($this->options['contract_key']);
1218
+ }
1219
+
1220
+ function get_sender_name() {
1221
+ return $this->options['sender_name'];
1222
+ }
1223
+
1224
+ function get_sender_email() {
1225
+ return $this->options['sender_email'];
1226
+ }
1227
+
1228
+ /**
1229
+ *
1230
+ * @return int
1231
+ */
1232
+ function get_newsletter_page_id() {
1233
+ return (int) $this->options['page'];
1234
+ }
1235
+
1236
+ /**
1237
+ *
1238
+ * @return WP_Post
1239
+ */
1240
+ function get_newsletter_page() {
1241
+ return get_post($this->get_newsletter_page_id());
1242
+ }
1243
+
1244
+ /**
1245
+ * Returns the Newsletter dedicated page URL or an alternative URL if that page if not
1246
+ * configured or not available.
1247
+ *
1248
+ * @staticvar string $url
1249
+ * @return string
1250
+ */
1251
+ function get_newsletter_page_url($language = '') {
1252
+
1253
+ $page = $this->get_newsletter_page();
1254
+
1255
+ if (!$page || $page->post_status !== 'publish') {
1256
+ return $this->build_action_url('m');
1257
+ }
1258
+
1259
+ $newsletter_page_url = get_permalink($page->ID);
1260
+ if ($language && $newsletter_page_url) {
1261
+ if (class_exists('SitePress')) {
1262
+ $newsletter_page_url = apply_filters('wpml_permalink', $newsletter_page_url, $language, true);
1263
+ }
1264
+ if (function_exists('pll_get_post')) {
1265
+ $translated_page = get_permalink(pll_get_post($page->ID, $language));
1266
+ if ($translated_page) {
1267
+ $newsletter_page_url = $translated_page;
1268
+ }
1269
+ }
1270
+ }
1271
+
1272
+ return $newsletter_page_url;
1273
+ }
1274
+
1275
+ function get_license_key() {
1276
+ if (defined('NEWSLETTER_LICENSE_KEY')) {
1277
+ return NEWSLETTER_LICENSE_KEY;
1278
+ } else {
1279
+ if (!empty($this->options['contract_key'])) {
1280
+ return trim($this->options['contract_key']);
1281
+ }
1282
+ }
1283
+ return false;
1284
+ }
1285
+
1286
+ /**
1287
+ * Get the data connected to the specified license code on man settings.
1288
+ *
1289
+ * - false if no license is present
1290
+ * - WP_Error if something went wrong if getting the license data
1291
+ * - object with expiration and addons list
1292
+ *
1293
+ * @param boolean $refresh
1294
+ * @return \WP_Error|boolean|object
1295
+ */
1296
+ function get_license_data($refresh = false) {
1297
+
1298
+ $this->logger->debug('Getting license data');
1299
+
1300
+ $license_key = $this->get_license_key();
1301
+ if (empty($license_key)) {
1302
+ $this->logger->debug('License was empty');
1303
+ delete_transient('newsletter_license_data');
1304
+ return false;
1305
+ }
1306
+
1307
+ if (!$refresh) {
1308
+ $license_data = get_transient('newsletter_license_data');
1309
+ if ($license_data !== false && is_object($license_data)) {
1310
+ $this->logger->debug('License data found on cache');
1311
+ return $license_data;
1312
+ }
1313
+ }
1314
+
1315
+ $this->logger->debug('Refreshing the license data');
1316
+
1317
+ $license_data_url = 'https://www.thenewsletterplugin.com/wp-content/plugins/file-commerce-pro/get-license-data.php';
1318
+
1319
+ $response = wp_remote_post($license_data_url, array(
1320
+ 'body' => array('k' => $license_key)
1321
+ ));
1322
+
1323
+ // Fall back to http...
1324
+ if (is_wp_error($response)) {
1325
+ $this->logger->error($response);
1326
+ $this->logger->error('Falling back to http');
1327
+ $license_data_url = str_replace('https', 'http', $license_data_url);
1328
+ $response = wp_remote_post($license_data_url, array(
1329
+ 'body' => array('k' => $license_key)
1330
+ ));
1331
+ if (is_wp_error($response)) {
1332
+ $this->logger->error($response);
1333
+ set_transient('newsletter_license_data', $response, DAY_IN_SECONDS);
1334
+ return $response;
1335
+ }
1336
+ }
1337
+
1338
+ $download_message = 'You can download all addons from www.thenewsletterplugin.com if your license is valid.';
1339
+
1340
+ if (wp_remote_retrieve_response_code($response) != '200') {
1341
+ $this->logger->error('license data error: ' . wp_remote_retrieve_response_code($response));
1342
+ $data = new WP_Error(wp_remote_retrieve_response_code($response), 'License validation service error. <br>' . $download_message);
1343
+ set_transient('newsletter_license_data', $data, DAY_IN_SECONDS);
1344
+ return $data;
1345
+ }
1346
+
1347
+ $json = wp_remote_retrieve_body($response);
1348
+ $data = json_decode($json);
1349
+
1350
+ if (!is_object($data)) {
1351
+ $this->logger->error($json);
1352
+ $data = new WP_Error(1, 'License validation service error. <br>' . $download_message);
1353
+ set_transient('newsletter_license_data', $data, DAY_IN_SECONDS);
1354
+ return $data;
1355
+ }
1356
+
1357
+ if (isset($data->message)) {
1358
+ $data = new WP_Error(1, $data->message . ' (check the license on Newsletter main settings)');
1359
+ set_transient('newsletter_license_data', $data, DAY_IN_SECONDS);
1360
+ return $data;
1361
+ }
1362
+
1363
+ $expiration = WEEK_IN_SECONDS;
1364
+ // If the license expires in few days, make the transient live only few days, so it will be refreshed
1365
+ if ($data->expire > time() && $data->expire - time() < WEEK_IN_SECONDS) {
1366
+ $expiration = $data->expire - time();
1367
+ }
1368
+ set_transient('newsletter_license_data', $data, $expiration);
1369
+
1370
+ return $data;
1371
+ }
1372
+
1373
+ /**
1374
+ * @deprecated
1375
+ * @param type $license_key
1376
+ * @return \WP_Error
1377
+ */
1378
+ public static function check_license($license_key) {
1379
+ $response = wp_remote_get('http://www.thenewsletterplugin.com/wp-content/plugins/file-commerce-pro/check.php?k=' . urlencode($license_key), array('sslverify' => false));
1380
+ if (is_wp_error($response)) {
1381
+ /* @var $response WP_Error */
1382
+ return new WP_Error(-1, 'It seems that your blog cannot contact the license validator. Ask your provider to unlock the HTTP/HTTPS connections to www.thenewsletterplugin.com<br>'
1383
+ . esc_html($response->get_error_code()) . ' - ' . esc_html($response->get_error_message()));
1384
+ } else if ($response['response']['code'] != 200) {
1385
+ return new WP_Error(-1, '[' . $response['response']['code'] . '] The license seems expired or not valid, please check your <a href="https://www.thenewsletterplugin.com/account">license code and status</a>, thank you.'
1386
+ . '<br>You can anyway download the professional extension from https://www.thenewsletterplugin.com.');
1387
+ } elseif ($expires = json_decode(wp_remote_retrieve_body($response))) {
1388
+ return array('expires' => $expires->expire, 'message' => 'Your license is valid and expires on ' . esc_html(date('Y-m-d', $expires->expire)));
1389
+ } else {
1390
+ return new WP_Error(-1, 'Unable to detect the license expiration. Debug data to report to the support: <code>' . esc_html(wp_remote_retrieve_body($response)) . '</code>');
1391
+ }
1392
+ }
1393
+
1394
+ function add_notice_to_chosen_profile_page_hook($post_states, $post) {
1395
+
1396
+ if ($post->ID == $this->options['page']) {
1397
+ $post_states[] = __('Newsletter plugin page, do not delete', 'newsletter');
1398
+ }
1399
+
1400
+ return $post_states;
1401
+ }
1402
+
1403
+ }
1404
+
1405
+ $newsletter = Newsletter::instance();
1406
+
1407
+ if (is_admin()) {
1408
+ require_once NEWSLETTER_DIR . '/system/system.php';
1409
+ }
1410
+
1411
+ require_once NEWSLETTER_DIR . '/subscription/subscription.php';
1412
+ require_once NEWSLETTER_DIR . '/unsubscription/unsubscription.php';
1413
+ require_once NEWSLETTER_DIR . '/profile/profile.php';
1414
+ require_once NEWSLETTER_DIR . '/emails/emails.php';
1415
+ require_once NEWSLETTER_DIR . '/users/users.php';
1416
+ require_once NEWSLETTER_DIR . '/statistics/statistics.php';
1417
+ require_once NEWSLETTER_DIR . '/widget/standard.php';
1418
+ require_once NEWSLETTER_DIR . '/widget/minimal.php';
readme.txt CHANGED
@@ -1,377 +1,385 @@
1
- === Newsletter ===
2
- Tags: newsletter, email marketing, welcome email, signup forms, contact, lead generation, marketing automation
3
- Tested up to: 5.8.3
4
- Stable tag: 7.3.5
5
- Contributors: satollo,webagile,michael-travan
6
- License: GPLv2 or later
7
- License URI: https://www.gnu.org/licenses/gpl-2.0.html
8
-
9
- Add a real newsletter system to your blog. For free. With unlimited newsletters and subscribers.
10
-
11
- == Description ==
12
-
13
- Newsletter is a **real newsletter and email marketing system** for your WordPress blog: perfect for list building, you can easily create, send and track e-mails, headache-free. It just works out of box!
14
-
15
- = Discover a completely rewritten composer =
16
-
17
- We redesigned our drag and drop composer to make your campaign creation even easier. Try it!
18
-
19
- = Main Features =
20
-
21
- * **Easy-to-use Drag and drop composer** to build responsive newsletters
22
- * **Unlimited subscribers** with statistics
23
- * **Unlimited newsletters** with tracking
24
- * **Subscription spam check** with domain/ip black lists, Akismet, captcha
25
- * **Delivery speed** fine control (from 12 emails per hour to as much as your blog can manage)
26
- * [WPML ready](https://www.thenewsletterplugin.com/documentation/multilanguage), [Polylang ready](https://www.thenewsletterplugin.com/documentation/multilanguage), [Translatepress ready](https://www.thenewsletterplugin.com/documentation/multilanguage)
27
- * All messages are **fully translatable** from administration panels (no .po/.mo file to edit)
28
- * [GDPR ready](https://www.thenewsletterplugin.com/documentation/gdpr-compliancy)
29
- * **Advanced targeting** with lists combinations like all in, at least one, not in and so on
30
- * Customizable **subscription widget**, **page** or **custom form**
31
- * Wordpress Users registration **seamless integration**
32
- * **Single** And **Double Opt-In** plus privacy checkbox for EU laws compliance
33
- * **Subscribers lists** to fine-target your campaigns
34
- * PHP API and REST API for coders and integrations
35
- * SMTP-Ready (with free addon)
36
- * Customizable Themes
37
- * **Status panel** to check your blog mailing capability and configuration
38
- * **Compatible with every SMTP plugin**: Post SMTP (aka Postman), WP Mail SMTP, Easy WP SMTP, Easy SMTP Mail, WP Mail Bank, ...
39
- * **Subscribers import** from file
40
- * Newsletter with Html and Text message versions
41
-
42
- = Find Us =
43
-
44
- Newsletter is a continuously evolving plugin. Stay tuned following us on [Facebook](https://www.facebook.com/thenewsletterplugin/) or [our site](https://www.thenewsletterplugin.com/).
45
-
46
- = Free Addons =
47
-
48
- Improve The Newsletter Plugin with these free addons:
49
-
50
- * [WP Registration Addon](https://www.thenewsletterplugin.com/documentation/wpusers-extension) - connects the WordPress standard and custom registration with Newsletter subscription. Optionally imports all registered users as subscribers.
51
- * [Archive Addon](https://www.thenewsletterplugin.com/documentation/archive-extension) - creates a simple blog page which lists all your sent newsletters
52
- * [Locked Content Addon](https://www.thenewsletterplugin.com/documentation/locked-content-extension) - open up your premium content only after subscription
53
- * [Newsletter REST API Addon](https://www.thenewsletterplugin.com/documentation/developers/newsletter-api-2/) - adds a tier of REST api to integrate with the Newsletter core services
54
- * [Sendinblue Addon](https://www.thenewsletterplugin.com/documentation/addons/delivery-addons/sendinblue-extension/) - deliver your newsletters with Sendinblue
55
- * [SMTP Addon](https://www.thenewsletterplugin.com/documentation/addons/delivery-addons/smtp-extension/) - deliver your newsletters with external SMTP
56
- * [Advanced Import Addon](https://www.thenewsletterplugin.com/documentation/addons/extended-features/advanced-import/) - import contact from file or copy and paste data with full mapping
57
-
58
- (*easily add them from our [Addons panel](https://www.thenewsletterplugin.com/documentation/install-extensions)*)
59
-
60
- = Addons on WordPress.org =
61
-
62
- * [RSS Composer Block](https://wordpress.org/plugins/newsletter-rss-block/) - (3rd party) a composer block which builds its content from a RSS feed
63
- * [Popup Maker Integration](https://wordpress.org/plugins/newsletter-popupmaker/) - (3rd party) integration of Newsletter forms with Popup Maker plugin
64
- * [BuddyPress integration](https://wordpress.org/plugins/newsletter-buddypress/) - subscription opt-in inside BuddyPress signup form
65
- * [WP User Manager addon for Newsletter](https://wordpress.org/plugins/wpum-newsletter/) - adds the subscription option on registration forms
66
-
67
- = Professional Addons =
68
-
69
- Need *more power*? Feel *something's missing*? The Newsletter Plugin features can be easily extended through our **premium, professional Addons**! Let us introduce just two of them : )
70
-
71
- * [Automated](https://www.thenewsletterplugin.com/automated) - generates and sends your newsletters using your blog last posts, even custom ones like events or products. Just sit and watch!
72
- * [Autoresponder](https://www.thenewsletterplugin.com/autoresponder) - creates email series to follow up your subscribers
73
- * [Extended Composer Blocks](https://www.thenewsletterplugin.com/composer) - adds new blocks to the drag & drop composer
74
- * [WooCommerce Integration](https://www.thenewsletterplugin.com/woocommerce) - subscribe customers to a mailing list and generate product newletters.
75
- * [Reports](https://www.thenewsletterplugin.com/reports) - improves the internal statistics collection system and provides better reports of data collected for each sent email. And retargeting. Neat.
76
- * [Leads](https://www.thenewsletterplugin.com/leads) adds a fancy subscription popup box or a fixed bar to your website that will boost your conversion rate
77
- * [Amazon SES and other mail providers integration](https://www.thenewsletterplugin.com/integrations) - seamlessly integrate Amazon SES and other email service providers with The Newsletter Plugin. Hassle-free.
78
- * [Contact Form 7 Integration](https://www.thenewsletterplugin.com/documentation/contact-form-7-extension) - integrate the subscription on Contact Form 7 forms
79
- * [Ninja Forms Integration](https://www.thenewsletterplugin.com/documentation/ninjaforms-extension) - integrate the subscription on Ninja Forms
80
- * [WP Forms Integration](https://www.thenewsletterplugin.com/documentation/wpforms-extension) - integrate the subscription on WP Forms
81
- * Events Manager and The Events Calendar (By Modern Tribe) integrations - easily add events to your newsletters
82
- * [Google Analytics](https://www.thenewsletterplugin.com/google-analytics) - track newsletter links with Google UTM tracking paramaters
83
- * [Subscribe on Comment](https://www.thenewsletterplugin.com/documentation/comments-extension) - adds the subscription option to your blog comment form
84
- * [Geolocation](https://www.thenewsletterplugin.com/documentation/geolocation-extension) - adds geolocation capability to target subscribers by location
85
-
86
- = GDPR =
87
-
88
- The Newsletter Plugin provides all the technical tools needed to achieve GDPR compliancy and we're continuously working to improve them and to give support even for specific use cases.
89
- The plugin does not collect users' own subscribers data, nor it has any access to those data: hence, we are not a data processor, so a data processing agreement is not needed.
90
- Anyway if you configure the plugin to use external services (usually an external mail delivery service) you should check with that service if some sort of agreement is required.
91
-
92
- = Support =
93
-
94
- We provide support for our plugin on [Wordpress.org forums](https://wordpress.org/support/plugin/newsletter) and through our [official forum](https://www.thenewsletterplugin.com/forums).
95
-
96
- Premium Users with an active license have access to one-to-one support via our [ticketing system](https://www.thenewsletterplugin.com/support-ticket).
97
-
98
- = Follow Us =
99
-
100
- * **Our Official Website** - [https://www.thenewsletterplugin.com/](https://www.thenewsletterplugin.com/)
101
- * **LinkedIn** - [https://www.linkedin.com/company/the-newsletter-plugin](https://www.linkedin.com/company/the-newsletter-plugin)
102
- * **Our Facebook Page** - [https://www.facebook.com/thenewsletterplugin](https://www.facebook.com/thenewsletterplugin)
103
- * **Our Twitter Account** - [https://twitter.com/newsletterwp](https://twitter.com/newsletterwp)
104
-
105
- == Frequently Asked Questions ==
106
-
107
- See the [Newsletter Forum](https://www.thenewsletterplugin.com/forums) to ask for help.
108
-
109
- For documentation start from [Newsletter documentation](https://www.thenewsletterplugin.com/documentation).
110
-
111
- Thank you, The Newsletter Team
112
-
113
- == Screenshots ==
114
-
115
- 1. The responsive email Drag & Drop composer
116
- 2. The plugin dashboard
117
- 3. The Reports extension
118
-
119
- == Changelog ==
120
-
121
- = 7.3.5 =
122
-
123
- * WP 5.8.3 compatibility check
124
- * Fixed 2021 max year in date picker
125
- * Typos
126
-
127
- = 7.3.4 =
128
-
129
- * Fixed delivery fatal error management
130
- * Fixed link to the schduler dianostica panel
131
-
132
- = 7.3.3 =
133
-
134
- * Added "complained" status to subscriber filters
135
- * Fixed some links bringing to a "not allowed" page
136
- * Fixed a notice on delivery diagnostic page
137
- * Fixed logo width notice on header block
138
-
139
- = 7.3.2 =
140
-
141
- * Fixed the remote ip retrieval and clean up
142
- * Fixed header link to status page
143
- * Fixed database error with too long IPs
144
- * Fixed the subscription of cancelled addresses
145
- * Fixed sender and name customization
146
-
147
- = 7.3.1 =
148
-
149
- * Dropped old mailers support
150
- * Improved sending process and limits checking
151
- * Removed obsolete notifications
152
- * Fixed the wrong report on single email delivery speed
153
- * Added support for custom sending speed by addons
154
- * Improved excerpt generation (but it still depends on plugins and themes...)
155
- * Support for building button option on composer blocks
156
-
157
- = 7.3.0 =
158
-
159
- * Fixed header block layout with (logo only layout)
160
- * Check for conflicts on newsletter saving
161
- * Added the subscriber complained status (actually not managed automatically)
162
-
163
- = 7.2.9 =
164
-
165
- * Fixed generic action button confirmation popup not showing the message
166
- * [SECURITY] Pre parsing of IP addresses on security panel
167
- * [SECURITY] Comments allowed on IP address list on security panel
168
- * [COMPOSER] Fixed preset name/subject
169
- * Improved generated forms for accessibility
170
-
171
- = 7.2.8 =
172
-
173
- * Fixed the print_date() when no time is provided
174
- * Fixed date alignment on posts block
175
- * Folders reorganization
176
- * Social block with a new set of icons
177
- * Boosted performances of the lists management page (for big databases)
178
- * Added a new scheduler diagnostic panel
179
- * Seriously improved the cron statistics and diagnostic panel (check it out!)
180
- * Removed obsolete migration code from ancient versions
181
-
182
- = 7.2.7 =
183
-
184
- * Fixed JS error on composer sometimes preventing the correct initialization
185
-
186
- = 7.2.6 =
187
-
188
- * Fixed links on test emails sent to a free email address
189
- * Added attachment explanation
190
- * Added link to explain the use of the snippet
191
-
192
- = 7.2.5 =
193
-
194
- * Fixed subject not saved under specific circumstance
195
-
196
- = 7.2.4 =
197
-
198
- * Fixed the composer not starting for blog with SSL plugin but still HTTP configured on main WP settings
199
- * Changed labels on subscriber maintenance panel
200
- * Updated requirements for WP version
201
-
202
- = 7.2.3 =
203
-
204
- * [COMPOSER] Added approx. indicators of the subsject visibile part in Apple and Android clients (experimental)
205
- * [COMPOSER] New mobile version view directly while composing (experimental)
206
- * [COMPOSER] New test email to test subscribers or to specific email address
207
- * [COMPOSER] Fixed missing background when creating a new message from a preset
208
- * [COMPOSER] Added media selector to the CTA block
209
- * [COMPOSER] Added reference to the tags on HTML and Text blocks
210
- * [COMPOSER] Move the snippet (preheader) field near the subject
211
- * [COMPOSER] Footer block with three link options: unsubscribe, manage and view online
212
- * [COMPOSER] Improve font coherence between blocks (by default)
213
- * [ANTISPAM] Improved the antispam checks on subscription
214
- * [GENERAL] Removed obsolete folders and code
215
- * [NEWSLETTERS] Refactored subject ideas selector
216
- * [SUBSCRIPTION] Inverted extra profile fields and lists on standard subscription form
217
- * [GENERAL] IP address extracted checking proxy variables
218
- * [GENERAL] Improved sending stats collection and display for the delivery engine (not related to click/open stats)
219
-
220
- = 7.2.2 =
221
-
222
- * [COMPOSER] Posts block excerpt removed when set to 0-length
223
- * [GENERAL]Added special characters on test message
224
- * [GENERAL]Added more specific error for action calls on System/Status panel
225
- * [SUBSCRIPTION] Check for the _wp_amp_action_xhr_converted parameter by AMP plugin
226
-
227
- = 7.2.1 =
228
-
229
- * [GENERAL] Added more detailed admin logging
230
- * [NEWSLETTERS] Fixed scheduled date sometimes reset to 1/1/1970
231
-
232
- = 7.2.0 =
233
-
234
- * [PROFILE] Fixed activation email on profile change
235
- * [NEWSLETTERS] Extended year selection on newsletter scheduling
236
- * [DELIVERY] Breaking change: old enqueue() and flush() methods have been removed
237
- * [GENERAL] Fixed alert message on some buttons
238
- * [SUBSCRIPTION] Fixed error message on multiple subscriptions (when not allowed)
239
- * [GENERAL] Fixed erratic error log line on main log
240
-
241
- = 7.1.9 =
242
-
243
- * [GENERAL] Removed the encodign defatlt to Base 64 when not specified since it seems incompatible with some SMTP plugins
244
- * [GENERAL] Review some controls layout and behavior
245
- * [DEBUG] Improved logging on database errors
246
- * [GENERAL] Added TikTok, Discord and Twitch socials
247
- * [GENERAL] Fixed odd error reported related to the cron call statistics collection
248
- * [DEBUG] Added a delivery diagnostic panel
249
-
250
- = 7.1.8 =
251
-
252
- * [COMPOSER] Fixed alignment of single big image on Outlook Android
253
-
254
- = 7.1.7 =
255
-
256
- * [GENERAL] Fix of permalink onm email with multilanguage plugins
257
-
258
- = 7.1.6 =
259
-
260
- * [COMPOSER] Fixed one column big image Read more links
261
-
262
- = 7.1.5 =
263
-
264
- * [COMPOSER] Improve buttons on posts layout
265
- * [COMPOSER] Improved header layout and logo
266
- * [COMPOSER] Generally improved block spacing
267
-
268
- = 7.1.4 =
269
-
270
- * [COMPOSER] Fixed image block for Outlook
271
- * [GENERAL] Fix undefined values in Gutenberg block
272
-
273
- = 7.1.3 =
274
-
275
- * [COMPOSER] Improvements on blocks layout compatibility
276
- * [COMPOSER] Fixed preset global options saving
277
- * [COMPOSER] Content regeneration on preset selection
278
- * [GENERAL] Added to System menu the Site Health link, a not well known native page of WordPress with system information
279
- * [GENERAL] Added sending statistics reset button on status panel
280
-
281
- = 7.1.2 =
282
-
283
- * [ADDONS] Fixed the addons list
284
-
285
- = 7.1.1 =
286
-
287
- * [GENERAL] Improved profile related functions
288
- * [GENERAL] Fixed check on field names (thanks Peter P.)
289
- * [COMPOSER] Fix on preset selection
290
- * [GENERAL] Fix ajax subscription on error
291
-
292
- = 7.1.0 =
293
-
294
- * [COMPOSER] Added link to tags documentation to inject subscriber's data
295
- * [COMPOSER] Fixed layout of posts block for Outlook 365
296
- * [GENERAL] Improved caching of addons json (even on error)
297
- * [GENERAL] Status menu changed to System/Status and System/Logs
298
- * [API] Fixed the subscriber status management (the API Addon should be updated as well)
299
- * [GENERAL] Fixed management of fatal errors on sending
300
- * [GENERAL] Limited error string per message to 250 chars
301
- * [GENERAL] Fixed check on field names (thanks Peter P.)
302
-
303
-
304
- = 7.0.9 =
305
-
306
- * [CAPTCHA] Fixed button label translation
307
- * [DELIVERY] Mailing fatal error management with newsletter stop and reporting
308
- * [SMTP] Marked obsolete the internal SMTP and made available the free SMTP addon
309
- * [DELIVERY] Better management of delivery fatal errors with a new "error" status for newsletters
310
- * [GENERAL] New log files panel
311
- * [IMPORT] Old import panel replaced by the new (really better) import addon (file, copy and paste, bounced addresses import)
312
- * [NEWSLETTERS] It's now possible to specify the sender name and email per newsletter (thanks to Matthew S.)
313
-
314
- = 7.0.8 =
315
-
316
- * [SUBSCRIBERS] Changed action buttons
317
- * [GENERAL] Reorganization of CSS and removal of unused files
318
- * [DASHBOARD] New window open for links and fix of invalid URLs
319
- * [NEWSLETTERS] New action buttons
320
- * [GENERAL] Minor fixes and optimizations
321
- * [COMPOSER] Fixed rare size error on gif images
322
-
323
- = 7.0.7 =
324
-
325
- * [COMPOSER] Fixed a warning in some inline editable blocks
326
- * [NEWSLETTERS] Fixed style which made list labels badly readable
327
- * [NEWSLETTERS] Fixed style which made bullet lists white (not on delivered newsletters)
328
- * [GENERAL] Added some references to the not working scheduler warning
329
-
330
- = 7.0.6 =
331
-
332
- * [COMPOSER] CTA block not grabbing settings from the "old" blocks
333
- * [COMPOSER] CSS issue on button settings group
334
- * [COMPOSER] Posts block two columns layout (author and padding)
335
- * [IMPORT] Removed the old low-featured import (the free import addon has everything needed!)
336
- * [GENERAL] Compatibility check with WP 5.7
337
-
338
- = 7.0.5 =
339
-
340
- * [COMPOSER] Hero CTA button not working
341
- * Fixed to the hero block button
342
- * Added support for the SMTP addon
343
-
344
- = 7.0.4 =
345
-
346
- * [COMPOSER] Redesigned drag and drop composer
347
- * [COMPOSER] NEW! Save drag and drop composed newsletters as templates to reuse easily
348
- * Redesigned dashboard
349
- * Several bug and fixes
350
-
351
- = 7.0.3 =
352
-
353
- * Option to choose between unsubscription and profile link in the footer block
354
- * Direct image src URL for image block
355
- * New media selector for blocks
356
- * Minor fixes
357
- * Updated codemirror
358
- * Updated default theme
359
- * Fixed tag filter on posts block (when tags have parathesis or like)
360
- * Fixed composer visualization of bullet points
361
-
362
- = 7.0.2 =
363
-
364
- * Fixed media 2x resize
365
-
366
- = 7.0.1 =
367
-
368
- * Fixed enforced lists by language with Polylang
369
-
370
- = 7.0.0 =
371
-
372
- * Added multiple newsletter selection for deletion
373
- * Added text part on welcome and activation email
374
- * Added the attribute "show_form" to "newsletter" shortcode
375
- * Added filter on subscriber saving (from external systems) with wrong list field values
376
- * Added index.html on log folder
377
-
 
 
 
 
 
 
 
 
1
+ === Newsletter ===
2
+ Tags: newsletter, email marketing, welcome email, signup forms, contact, lead generation, marketing automation
3
+ Tested up to: 5.9
4
+ Stable tag: 7.3.6
5
+ Contributors: satollo,webagile,michael-travan
6
+ License: GPLv2 or later
7
+ License URI: https://www.gnu.org/licenses/gpl-2.0.html
8
+
9
+ Add a real newsletter system to your blog. For free. With unlimited newsletters and subscribers.
10
+
11
+ == Description ==
12
+
13
+ Newsletter is a **real newsletter and email marketing system** for your WordPress blog: perfect for list building, you can easily create, send and track e-mails, headache-free. It just works out of box!
14
+
15
+ = Discover a completely rewritten composer =
16
+
17
+ We redesigned our drag and drop composer to make your campaign creation even easier. Try it!
18
+
19
+ = Main Features =
20
+
21
+ * **Easy-to-use Drag and drop composer** to build responsive newsletters
22
+ * **Unlimited subscribers** with statistics
23
+ * **Unlimited newsletters** with tracking
24
+ * **Subscription spam check** with domain/ip black lists, Akismet, captcha
25
+ * **Delivery speed** fine control (from 12 emails per hour to as much as your blog can manage)
26
+ * [WPML ready](https://www.thenewsletterplugin.com/documentation/multilanguage), [Polylang ready](https://www.thenewsletterplugin.com/documentation/multilanguage), [Translatepress ready](https://www.thenewsletterplugin.com/documentation/multilanguage)
27
+ * All messages are **fully translatable** from administration panels (no .po/.mo file to edit)
28
+ * [GDPR ready](https://www.thenewsletterplugin.com/documentation/gdpr-compliancy)
29
+ * **Advanced targeting** with lists combinations like all in, at least one, not in and so on
30
+ * Customizable **subscription widget**, **page** or **custom form**
31
+ * Wordpress Users registration **seamless integration**
32
+ * **Single** And **Double Opt-In** plus privacy checkbox for EU laws compliance
33
+ * **Subscribers lists** to fine-target your campaigns
34
+ * PHP API and REST API for coders and integrations
35
+ * SMTP-Ready (with free addon)
36
+ * Customizable Themes
37
+ * **Status panel** to check your blog mailing capability and configuration
38
+ * **Compatible with every SMTP plugin**: Post SMTP (aka Postman), WP Mail SMTP, Easy WP SMTP, Easy SMTP Mail, WP Mail Bank, ...
39
+ * **Subscribers import** from file
40
+ * Newsletter with Html and Text message versions
41
+
42
+ = Find Us =
43
+
44
+ Newsletter is a continuously evolving plugin. Stay tuned following us on [Facebook](https://www.facebook.com/thenewsletterplugin/) or [our site](https://www.thenewsletterplugin.com/).
45
+
46
+ = Free Addons =
47
+
48
+ Improve The Newsletter Plugin with these free addons:
49
+
50
+ * [WP Registration Addon](https://www.thenewsletterplugin.com/documentation/wpusers-extension) - connects the WordPress standard and custom registration with Newsletter subscription. Optionally imports all registered users as subscribers.
51
+ * [Archive Addon](https://www.thenewsletterplugin.com/documentation/archive-extension) - creates a simple blog page which lists all your sent newsletters
52
+ * [Locked Content Addon](https://www.thenewsletterplugin.com/documentation/locked-content-extension) - open up your premium content only after subscription
53
+ * [Newsletter REST API Addon](https://www.thenewsletterplugin.com/documentation/developers/newsletter-api-2/) - adds a tier of REST api to integrate with the Newsletter core services
54
+ * [Sendinblue Addon](https://www.thenewsletterplugin.com/documentation/addons/delivery-addons/sendinblue-extension/) - deliver your newsletters with Sendinblue
55
+ * [SMTP Addon](https://www.thenewsletterplugin.com/documentation/addons/delivery-addons/smtp-extension/) - deliver your newsletters with external SMTP
56
+ * [Advanced Import Addon](https://www.thenewsletterplugin.com/documentation/addons/extended-features/advanced-import/) - import contact from file or copy and paste data with full mapping
57
+
58
+ (*easily add them from our [Addons panel](https://www.thenewsletterplugin.com/documentation/install-extensions)*)
59
+
60
+ = Addons on WordPress.org =
61
+
62
+ * [RSS Composer Block](https://wordpress.org/plugins/newsletter-rss-block/) - (3rd party) a composer block which builds its content from a RSS feed
63
+ * [Popup Maker Integration](https://wordpress.org/plugins/newsletter-popupmaker/) - (3rd party) integration of Newsletter forms with Popup Maker plugin
64
+ * [BuddyPress integration](https://wordpress.org/plugins/newsletter-buddypress/) - subscription opt-in inside BuddyPress signup form
65
+ * [WP User Manager addon for Newsletter](https://wordpress.org/plugins/wpum-newsletter/) - adds the subscription option on registration forms
66
+
67
+ = Professional Addons =
68
+
69
+ Need *more power*? Feel *something's missing*? The Newsletter Plugin features can be easily extended through our **premium, professional Addons**! Let us introduce just two of them : )
70
+
71
+ * [Automated](https://www.thenewsletterplugin.com/automated) - generates and sends your newsletters using your blog last posts, even custom ones like events or products. Just sit and watch!
72
+ * [Autoresponder](https://www.thenewsletterplugin.com/autoresponder) - creates email series to follow up your subscribers
73
+ * [Extended Composer Blocks](https://www.thenewsletterplugin.com/composer) - adds new blocks to the drag & drop composer
74
+ * [WooCommerce Integration](https://www.thenewsletterplugin.com/woocommerce) - subscribe customers to a mailing list and generate product newletters.
75
+ * [Reports](https://www.thenewsletterplugin.com/reports) - improves the internal statistics collection system and provides better reports of data collected for each sent email. And retargeting. Neat.
76
+ * [Leads](https://www.thenewsletterplugin.com/leads) adds a fancy subscription popup box or a fixed bar to your website that will boost your conversion rate
77
+ * [Amazon SES and other mail providers integration](https://www.thenewsletterplugin.com/integrations) - seamlessly integrate Amazon SES and other email service providers with The Newsletter Plugin. Hassle-free.
78
+ * [Contact Form 7 Integration](https://www.thenewsletterplugin.com/documentation/contact-form-7-extension) - integrate the subscription on Contact Form 7 forms
79
+ * [Ninja Forms Integration](https://www.thenewsletterplugin.com/documentation/ninjaforms-extension) - integrate the subscription on Ninja Forms
80
+ * [WP Forms Integration](https://www.thenewsletterplugin.com/documentation/wpforms-extension) - integrate the subscription on WP Forms
81
+ * Events Manager and The Events Calendar (By Modern Tribe) integrations - easily add events to your newsletters
82
+ * [Google Analytics](https://www.thenewsletterplugin.com/google-analytics) - track newsletter links with Google UTM tracking paramaters
83
+ * [Subscribe on Comment](https://www.thenewsletterplugin.com/documentation/comments-extension) - adds the subscription option to your blog comment form
84
+ * [Geolocation](https://www.thenewsletterplugin.com/documentation/geolocation-extension) - adds geolocation capability to target subscribers by location
85
+
86
+ = GDPR =
87
+
88
+ The Newsletter Plugin provides all the technical tools needed to achieve GDPR compliancy and we're continuously working to improve them and to give support even for specific use cases.
89
+ The plugin does not collect users' own subscribers data, nor it has any access to those data: hence, we are not a data processor, so a data processing agreement is not needed.
90
+ Anyway if you configure the plugin to use external services (usually an external mail delivery service) you should check with that service if some sort of agreement is required.
91
+
92
+ = Support =
93
+
94
+ We provide support for our plugin on [Wordpress.org forums](https://wordpress.org/support/plugin/newsletter) and through our [official forum](https://www.thenewsletterplugin.com/forums).
95
+
96
+ Premium Users with an active license have access to one-to-one support via our [ticketing system](https://www.thenewsletterplugin.com/support-ticket).
97
+
98
+ = Follow Us =
99
+
100
+ * **Our Official Website** - [https://www.thenewsletterplugin.com/](https://www.thenewsletterplugin.com/)
101
+ * **LinkedIn** - [https://www.linkedin.com/company/the-newsletter-plugin](https://www.linkedin.com/company/the-newsletter-plugin)
102
+ * **Our Facebook Page** - [https://www.facebook.com/thenewsletterplugin](https://www.facebook.com/thenewsletterplugin)
103
+ * **Our Twitter Account** - [https://twitter.com/newsletterwp](https://twitter.com/newsletterwp)
104
+
105
+ == Frequently Asked Questions ==
106
+
107
+ See the [Newsletter Forum](https://www.thenewsletterplugin.com/forums) to ask for help.
108
+
109
+ For documentation start from [Newsletter documentation](https://www.thenewsletterplugin.com/documentation).
110
+
111
+ Thank you, The Newsletter Team
112
+
113
+ == Screenshots ==
114
+
115
+ 1. The responsive email Drag & Drop composer
116
+ 2. The plugin dashboard
117
+ 3. The Reports extension
118
+
119
+ == Changelog ==
120
+
121
+ = 7.3.6 =
122
+
123
+ * Improved composer reusability in other contexts
124
+ * Removed obsolete composer code
125
+ * Fixed default tracking for old theme-based neewsletters
126
+ * Forced enconding on export (attempt)
127
+ * WP 5.9 check
128
+
129
+ = 7.3.5 =
130
+
131
+ * WP 5.8.3 compatibility check
132
+ * Fixed 2021 max year in date picker
133
+ * Typos
134
+
135
+ = 7.3.4 =
136
+
137
+ * Fixed delivery fatal error management
138
+ * Fixed link to the schduler dianostica panel
139
+
140
+ = 7.3.3 =
141
+
142
+ * Added "complained" status to subscriber filters
143
+ * Fixed some links bringing to a "not allowed" page
144
+ * Fixed a notice on delivery diagnostic page
145
+ * Fixed logo width notice on header block
146
+
147
+ = 7.3.2 =
148
+
149
+ * Fixed the remote ip retrieval and clean up
150
+ * Fixed header link to status page
151
+ * Fixed database error with too long IPs
152
+ * Fixed the subscription of cancelled addresses
153
+ * Fixed sender and name customization
154
+
155
+ = 7.3.1 =
156
+
157
+ * Dropped old mailers support
158
+ * Improved sending process and limits checking
159
+ * Removed obsolete notifications
160
+ * Fixed the wrong report on single email delivery speed
161
+ * Added support for custom sending speed by addons
162
+ * Improved excerpt generation (but it still depends on plugins and themes...)
163
+ * Support for building button option on composer blocks
164
+
165
+ = 7.3.0 =
166
+
167
+ * Fixed header block layout with (logo only layout)
168
+ * Check for conflicts on newsletter saving
169
+ * Added the subscriber complained status (actually not managed automatically)
170
+
171
+ = 7.2.9 =
172
+
173
+ * Fixed generic action button confirmation popup not showing the message
174
+ * [SECURITY] Pre parsing of IP addresses on security panel
175
+ * [SECURITY] Comments allowed on IP address list on security panel
176
+ * [COMPOSER] Fixed preset name/subject
177
+ * Improved generated forms for accessibility
178
+
179
+ = 7.2.8 =
180
+
181
+ * Fixed the print_date() when no time is provided
182
+ * Fixed date alignment on posts block
183
+ * Folders reorganization
184
+ * Social block with a new set of icons
185
+ * Boosted performances of the lists management page (for big databases)
186
+ * Added a new scheduler diagnostic panel
187
+ * Seriously improved the cron statistics and diagnostic panel (check it out!)
188
+ * Removed obsolete migration code from ancient versions
189
+
190
+ = 7.2.7 =
191
+
192
+ * Fixed JS error on composer sometimes preventing the correct initialization
193
+
194
+ = 7.2.6 =
195
+
196
+ * Fixed links on test emails sent to a free email address
197
+ * Added attachment explanation
198
+ * Added link to explain the use of the snippet
199
+
200
+ = 7.2.5 =
201
+
202
+ * Fixed subject not saved under specific circumstance
203
+
204
+ = 7.2.4 =
205
+
206
+ * Fixed the composer not starting for blog with SSL plugin but still HTTP configured on main WP settings
207
+ * Changed labels on subscriber maintenance panel
208
+ * Updated requirements for WP version
209
+
210
+ = 7.2.3 =
211
+
212
+ * [COMPOSER] Added approx. indicators of the subsject visibile part in Apple and Android clients (experimental)
213
+ * [COMPOSER] New mobile version view directly while composing (experimental)
214
+ * [COMPOSER] New test email to test subscribers or to specific email address
215
+ * [COMPOSER] Fixed missing background when creating a new message from a preset
216
+ * [COMPOSER] Added media selector to the CTA block
217
+ * [COMPOSER] Added reference to the tags on HTML and Text blocks
218
+ * [COMPOSER] Move the snippet (preheader) field near the subject
219
+ * [COMPOSER] Footer block with three link options: unsubscribe, manage and view online
220
+ * [COMPOSER] Improve font coherence between blocks (by default)
221
+ * [ANTISPAM] Improved the antispam checks on subscription
222
+ * [GENERAL] Removed obsolete folders and code
223
+ * [NEWSLETTERS] Refactored subject ideas selector
224
+ * [SUBSCRIPTION] Inverted extra profile fields and lists on standard subscription form
225
+ * [GENERAL] IP address extracted checking proxy variables
226
+ * [GENERAL] Improved sending stats collection and display for the delivery engine (not related to click/open stats)
227
+
228
+ = 7.2.2 =
229
+
230
+ * [COMPOSER] Posts block excerpt removed when set to 0-length
231
+ * [GENERAL]Added special characters on test message
232
+ * [GENERAL]Added more specific error for action calls on System/Status panel
233
+ * [SUBSCRIPTION] Check for the _wp_amp_action_xhr_converted parameter by AMP plugin
234
+
235
+ = 7.2.1 =
236
+
237
+ * [GENERAL] Added more detailed admin logging
238
+ * [NEWSLETTERS] Fixed scheduled date sometimes reset to 1/1/1970
239
+
240
+ = 7.2.0 =
241
+
242
+ * [PROFILE] Fixed activation email on profile change
243
+ * [NEWSLETTERS] Extended year selection on newsletter scheduling
244
+ * [DELIVERY] Breaking change: old enqueue() and flush() methods have been removed
245
+ * [GENERAL] Fixed alert message on some buttons
246
+ * [SUBSCRIPTION] Fixed error message on multiple subscriptions (when not allowed)
247
+ * [GENERAL] Fixed erratic error log line on main log
248
+
249
+ = 7.1.9 =
250
+
251
+ * [GENERAL] Removed the encodign defatlt to Base 64 when not specified since it seems incompatible with some SMTP plugins
252
+ * [GENERAL] Review some controls layout and behavior
253
+ * [DEBUG] Improved logging on database errors
254
+ * [GENERAL] Added TikTok, Discord and Twitch socials
255
+ * [GENERAL] Fixed odd error reported related to the cron call statistics collection
256
+ * [DEBUG] Added a delivery diagnostic panel
257
+
258
+ = 7.1.8 =
259
+
260
+ * [COMPOSER] Fixed alignment of single big image on Outlook Android
261
+
262
+ = 7.1.7 =
263
+
264
+ * [GENERAL] Fix of permalink onm email with multilanguage plugins
265
+
266
+ = 7.1.6 =
267
+
268
+ * [COMPOSER] Fixed one column big image Read more links
269
+
270
+ = 7.1.5 =
271
+
272
+ * [COMPOSER] Improve buttons on posts layout
273
+ * [COMPOSER] Improved header layout and logo
274
+ * [COMPOSER] Generally improved block spacing
275
+
276
+ = 7.1.4 =
277
+
278
+ * [COMPOSER] Fixed image block for Outlook
279
+ * [GENERAL] Fix undefined values in Gutenberg block
280
+
281
+ = 7.1.3 =
282
+
283
+ * [COMPOSER] Improvements on blocks layout compatibility
284
+ * [COMPOSER] Fixed preset global options saving
285
+ * [COMPOSER] Content regeneration on preset selection
286
+ * [GENERAL] Added to System menu the Site Health link, a not well known native page of WordPress with system information
287
+ * [GENERAL] Added sending statistics reset button on status panel
288
+
289
+ = 7.1.2 =
290
+
291
+ * [ADDONS] Fixed the addons list
292
+
293
+ = 7.1.1 =
294
+
295
+ * [GENERAL] Improved profile related functions
296
+ * [GENERAL] Fixed check on field names (thanks Peter P.)
297
+ * [COMPOSER] Fix on preset selection
298
+ * [GENERAL] Fix ajax subscription on error
299
+
300
+ = 7.1.0 =
301
+
302
+ * [COMPOSER] Added link to tags documentation to inject subscriber's data
303
+ * [COMPOSER] Fixed layout of posts block for Outlook 365
304
+ * [GENERAL] Improved caching of addons json (even on error)
305
+ * [GENERAL] Status menu changed to System/Status and System/Logs
306
+ * [API] Fixed the subscriber status management (the API Addon should be updated as well)
307
+ * [GENERAL] Fixed management of fatal errors on sending
308
+ * [GENERAL] Limited error string per message to 250 chars
309
+ * [GENERAL] Fixed check on field names (thanks Peter P.)
310
+
311
+
312
+ = 7.0.9 =
313
+
314
+ * [CAPTCHA] Fixed button label translation
315
+ * [DELIVERY] Mailing fatal error management with newsletter stop and reporting
316
+ * [SMTP] Marked obsolete the internal SMTP and made available the free SMTP addon
317
+ * [DELIVERY] Better management of delivery fatal errors with a new "error" status for newsletters
318
+ * [GENERAL] New log files panel
319
+ * [IMPORT] Old import panel replaced by the new (really better) import addon (file, copy and paste, bounced addresses import)
320
+ * [NEWSLETTERS] It's now possible to specify the sender name and email per newsletter (thanks to Matthew S.)
321
+
322
+ = 7.0.8 =
323
+
324
+ * [SUBSCRIBERS] Changed action buttons
325
+ * [GENERAL] Reorganization of CSS and removal of unused files
326
+ * [DASHBOARD] New window open for links and fix of invalid URLs
327
+ * [NEWSLETTERS] New action buttons
328
+ * [GENERAL] Minor fixes and optimizations
329
+ * [COMPOSER] Fixed rare size error on gif images
330
+
331
+ = 7.0.7 =
332
+
333
+ * [COMPOSER] Fixed a warning in some inline editable blocks
334
+ * [NEWSLETTERS] Fixed style which made list labels badly readable
335
+ * [NEWSLETTERS] Fixed style which made bullet lists white (not on delivered newsletters)
336
+ * [GENERAL] Added some references to the not working scheduler warning
337
+
338
+ = 7.0.6 =
339
+
340
+ * [COMPOSER] CTA block not grabbing settings from the "old" blocks
341
+ * [COMPOSER] CSS issue on button settings group
342
+ * [COMPOSER] Posts block two columns layout (author and padding)
343
+ * [IMPORT] Removed the old low-featured import (the free import addon has everything needed!)
344
+ * [GENERAL] Compatibility check with WP 5.7
345
+
346
+ = 7.0.5 =
347
+
348
+ * [COMPOSER] Hero CTA button not working
349
+ * Fixed to the hero block button
350
+ * Added support for the SMTP addon
351
+
352
+ = 7.0.4 =
353
+
354
+ * [COMPOSER] Redesigned drag and drop composer
355
+ * [COMPOSER] NEW! Save drag and drop composed newsletters as templates to reuse easily
356
+ * Redesigned dashboard
357
+ * Several bug and fixes
358
+
359
+ = 7.0.3 =
360
+
361
+ * Option to choose between unsubscription and profile link in the footer block
362
+ * Direct image src URL for image block
363
+ * New media selector for blocks
364
+ * Minor fixes
365
+ * Updated codemirror
366
+ * Updated default theme
367
+ * Fixed tag filter on posts block (when tags have parathesis or like)
368
+ * Fixed composer visualization of bullet points
369
+
370
+ = 7.0.2 =
371
+
372
+ * Fixed media 2x resize
373
+
374
+ = 7.0.1 =
375
+
376
+ * Fixed enforced lists by language with Polylang
377
+
378
+ = 7.0.0 =
379
+
380
+ * Added multiple newsletter selection for deletion
381
+ * Added text part on welcome and activation email
382
+ * Added the attribute "show_form" to "newsletter" shortcode
383
+ * Added filter on subscriber saving (from external systems) with wrong list field values
384
+ * Added index.html on log folder
385
+
subscription/subscription.php CHANGED
@@ -111,8 +111,11 @@ class NewsletterSubscription extends NewsletterModule {
111
  $this->dienow('List change not allowed.', 'Please check if the list is marked as "private".', 400);
112
  }
113
 
114
- $url = esc_url_raw($_REQUEST['redirect']);
115
-
 
 
 
116
  $this->set_user_list($user, $list_id, $_REQUEST['value']);
117
 
118
  $user = $this->get_user($user->id);
111
  $this->dienow('List change not allowed.', 'Please check if the list is marked as "private".', 400);
112
  }
113
 
114
+ if (empty($_REQUEST['redirect'])) {
115
+ $url = home_url();
116
+ } else {
117
+ $url = esc_url_raw($_REQUEST['redirect']);
118
+ }
119
  $this->set_user_list($user, $list_id, $_REQUEST['value']);
120
 
121
  $user = $this->get_user($user->id);
system/delivery.php CHANGED
@@ -1,199 +1,199 @@
1
- <?php
2
- /* @var $this NewsletterSystem */
3
- /* @var $wpdb wpdb */
4
-
5
- defined('ABSPATH') || exit;
6
-
7
- include_once NEWSLETTER_INCLUDES_DIR . '/controls.php';
8
- $controls = new NewsletterControls();
9
- $newsletter = Newsletter::instance();
10
-
11
- if ($controls->is_action('test')) {
12
-
13
- if (!NewsletterModule::is_email($controls->data['test_email'])) {
14
- $controls->errors = 'The test email address is not set or is not correct.';
15
- }
16
-
17
- if (empty($controls->errors)) {
18
-
19
- $options = $controls->data;
20
-
21
- if ($controls->data['test_email'] == $newsletter->get_sender_email()) {
22
- $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>';
23
- }
24
-
25
- $message = NewsletterMailerAddon::get_test_message($controls->data['test_email'], 'Newsletter test email at ' . date(DATE_ISO8601));
26
-
27
- $r = $newsletter->deliver($message);
28
-
29
- if (!is_wp_error($r)) {
30
- $options['mail'] = 1;
31
- $controls->messages .= '<strong>SUCCESS</strong><br>';
32
- $controls->messages .= 'Anyway if the message does not appear the mailbox (check even the spam folder) you can ';
33
- $controls->messages .= '<a href="https://www.thenewsletterplugin.com/documentation/?p=15170" target="_blank"><strong>read more here</strong></a>.';
34
- } else {
35
- $options['mail'] = 0;
36
- $options['mail_error'] = $r->get_error_message();
37
-
38
- $controls->errors .= '<strong>FAILED</strong> (' . esc_html($r->get_error_message()) . ')<br>';
39
-
40
- if (!empty($newsletter->options['return_path'])) {
41
- $controls->errors .= '- Try to remove the return path on main settings.<br>';
42
- }
43
-
44
- $controls->errors .= '<a href="https://www.thenewsletterplugin.com/documentation/?p=15170" target="_blank"><strong>' . __('Read more', 'newsletter') . '</strong></a>.';
45
-
46
- $parts = explode('@', $newsletter->get_sender_email());
47
- $sitename = strtolower($_SERVER['SERVER_NAME']);
48
- if (substr($sitename, 0, 4) == 'www.') {
49
- $sitename = substr($sitename, 4);
50
- }
51
- if (strtolower($sitename) != strtolower($parts[1])) {
52
- $controls->errors .= '- Try to set on main setting a sender address with the same domain of your blog: ' . esc_html($sitename) . ' (you are using ' . esc_html($newsletter->get_sender_email()) . ')<br>';
53
- }
54
- }
55
- $this->save_options($options, 'status');
56
- }
57
- }
58
-
59
- $options = $this->get_options('status');
60
-
61
- $mailer = Newsletter::instance()->get_mailer();
62
- $functions = tnp_get_hook_functions('phpmailer_init');
63
- $icon = 'fas fa-plug';
64
- if ($mailer instanceof NewsletterDefaultMailer) {
65
- $mailer_name = 'Wordpress';
66
- $service_name = 'Hosting Provider';
67
- if (!empty($functions)) {
68
- $mailer_name .= '<br>(see below)';
69
- $service_name .= '<br>(see below)';
70
- }
71
- $icon = 'fab fa-wordpress';
72
- } else if ($mailer instanceof NewsletterDefaultSMTPMailer) {
73
- $mailer_name = 'Internal SMTP';
74
- $service_name = 'SMTP Provider';
75
- } else {
76
- $mailer_name = 'Unknown';
77
- $service_name = 'Unknown';
78
- if (is_object($mailer)) {
79
- if (method_exists($mailer, 'get_description')) {
80
- $mailer_name = esc_html($mailer->get_description());
81
- $service_name = esc_html(ucfirst($mailer->get_name()) . ' Service');
82
- } else {
83
- $mailer_name = esc_html(get_class($mailer));
84
- $service_name = $mailer_name;
85
- }
86
- }
87
- }
88
-
89
- function tnp_get_hook_functions($tag) {
90
- global $wp_filter;
91
- $b = '';
92
- if (isset($wp_filter[$tag])) {
93
- foreach ($wp_filter[$tag]->callbacks as $priority => $functions) {
94
-
95
- foreach ($functions as $function) {
96
- $b .= '[' . $priority . '] ';
97
- if (is_array($function['function'])) {
98
- if (is_object($function['function'][0])) {
99
- $b .= get_class($function['function'][0]) . '::' . $function['function'][1];
100
- } else {
101
- $b .= $function['function'][0] . '::' . $function['function'][1];
102
- }
103
- } else {
104
- if (is_object($function['function'])) {
105
- $fn = new ReflectionFunction($function['function']);
106
- $b .= get_class($fn->getClosureThis()) . '(closure)';
107
- } else {
108
- $b .= $function['function'];
109
- }
110
- }
111
- $b .= "<br>";
112
- }
113
- }
114
- }
115
- return $b;
116
- }
117
-
118
- $speed = Newsletter::$instance->get_send_speed();
119
- ?>
120
-
121
- <style>
122
- <?php include __DIR__ . '/css/system.css' ?>
123
- </style>
124
-
125
- <div class="wrap tnp-system tnp-system-delivery" id="tnp-wrap">
126
-
127
- <?php include NEWSLETTER_DIR . '/tnp-header.php'; ?>
128
-
129
- <div id="tnp-heading">
130
-
131
- <h2><?php _e('Email Delivery', 'newsletter') ?></h2>
132
-
133
- </div>
134
-
135
- <div id="tnp-body">
136
- <form method="post" action="">
137
- <?php $controls->init(); ?>
138
- <h3>Mailing test</h3>
139
-
140
- <p>
141
- <?php $controls->text_email('test_email', ['required' => true]) ?>
142
- <?php $controls->button('test', __('Send a test message')) ?>
143
- <?php if (empty($options['mail'])) { ?>
144
- <span class="tnp-ko">KO</span>
145
- <?php } else { ?>
146
- <span class="tnp-ok">OK</span>
147
- <?php } ?>
148
- </p>
149
-
150
- <p>
151
- <?php if (empty($options['mail'])) { ?>
152
- <?php if (empty($options['mail_error'])) { ?>
153
- <p>A test has never run.</p>
154
- <?php } else { ?>
155
- <p>Last test failed with error "<?php echo esc_html($options['mail_error']) ?>".</p>
156
-
157
- <?php } ?>
158
- <?php } else { ?>
159
- <p>Last test was successful. If you didn't receive the test email:</p>
160
- <ol style="color: #fff">
161
- <li>If you're using an third party SMTP plugin, do a test from that plugin configuration panel</li>
162
- <li>If you're using a Newsletter Delivery Addon, do a test from that addon configuration panel</li>
163
- <li>If previous points do not apply to you, ask for support to your provider reporting the emails from your blog are not delivered</li>
164
- </ol>
165
- <?php } ?>
166
- <p><a href="https://www.thenewsletterplugin.com/documentation/email-sending-issues" target="_blank">Read more to solve your issues, if any</a></p>
167
-
168
-
169
- </form>
170
-
171
- <h3>
172
- How are messages delivered by Newsletter to your subscribers?
173
- </h3>
174
-
175
- <div class="tnp-flow">
176
- <div class="tnp-mail"><i class="fas fa-envelope"></i><br><br>Messages<br>
177
- (max: <?php echo esc_html($speed) ?> emails per hour)
178
- </div>
179
- <div class="tnp-arrow">&rightarrow;</div>
180
- <div class="tnp-addon"><i class="<?php echo $icon ?>"></i><br><br><?php echo $mailer_name ?></div>
181
- <div class="tnp-arrow">&rightarrow;</div>
182
- <div class="tnp-service"><i class="fas fa-cog"></i><br><br>
183
- <?php echo $service_name ?>
184
- </div>
185
- <div class="tnp-arrow">&rightarrow;</div>
186
- <div class="tnp-user"><i class="fas fa-user"></i></div>
187
- </div>
188
-
189
-
190
- <?php if (!empty($functions)) { ?>
191
- <br><br>
192
- <h3>Functions that are changing the default WordPress delivery system</h3>
193
- <p><?php echo $functions ?></p>
194
- <?php } ?>
195
-
196
-
197
- </div>
198
- <?php include NEWSLETTER_DIR . '/tnp-footer.php'; ?>
199
- </div>
1
+ <?php
2
+ /* @var $this NewsletterSystem */
3
+ /* @var $wpdb wpdb */
4
+
5
+ defined('ABSPATH') || exit;
6
+
7
+ include_once NEWSLETTER_INCLUDES_DIR . '/controls.php';
8
+ $controls = new NewsletterControls();
9
+ $newsletter = Newsletter::instance();
10
+
11
+ if ($controls->is_action('test')) {
12
+
13
+ if (!NewsletterModule::is_email($controls->data['test_email'])) {
14
+ $controls->errors = 'The test email address is not set or is not correct.';
15
+ }
16
+
17
+ if (empty($controls->errors)) {
18
+
19
+ $options = $controls->data;
20
+
21
+ if ($controls->data['test_email'] == $newsletter->get_sender_email()) {
22
+ $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>';
23
+ }
24
+
25
+ $message = NewsletterMailerAddon::get_test_message($controls->data['test_email'], 'Newsletter test email at ' . date(DATE_ISO8601));
26
+
27
+ $r = $newsletter->deliver($message);
28
+
29
+ if (!is_wp_error($r)) {
30
+ $options['mail'] = 1;
31
+ $controls->messages .= '<strong>SUCCESS</strong><br>';
32
+ $controls->messages .= 'Anyway if the message does not appear the mailbox (check even the spam folder) you can ';
33
+ $controls->messages .= '<a href="https://www.thenewsletterplugin.com/documentation/?p=15170" target="_blank"><strong>read more here</strong></a>.';
34
+ } else {
35
+ $options['mail'] = 0;
36
+ $options['mail_error'] = $r->get_error_message();
37
+
38
+ $controls->errors .= '<strong>FAILED</strong> (' . esc_html($r->get_error_message()) . ')<br>';
39
+
40
+ if (!empty($newsletter->options['return_path'])) {
41
+ $controls->errors .= '- Try to remove the return path on main settings.<br>';
42
+ }
43
+
44
+ $controls->errors .= '<a href="https://www.thenewsletterplugin.com/documentation/?p=15170" target="_blank"><strong>' . __('Read more', 'newsletter') . '</strong></a>.';
45
+
46
+ $parts = explode('@', $newsletter->get_sender_email());
47
+ $sitename = strtolower($_SERVER['SERVER_NAME']);
48
+ if (substr($sitename, 0, 4) == 'www.') {
49
+ $sitename = substr($sitename, 4);
50
+ }
51
+ if (strtolower($sitename) != strtolower($parts[1])) {
52
+ $controls->errors .= '- Try to set on main setting a sender address with the same domain of your blog: ' . esc_html($sitename) . ' (you are using ' . esc_html($newsletter->get_sender_email()) . ')<br>';
53
+ }
54
+ }
55
+ $this->save_options($options, 'status');
56
+ }
57
+ }
58
+
59
+ $options = $this->get_options('status');
60
+
61
+ $mailer = Newsletter::instance()->get_mailer();
62
+ $functions = tnp_get_hook_functions('phpmailer_init');
63
+ $icon = 'fas fa-plug';
64
+ if ($mailer instanceof NewsletterDefaultMailer) {
65
+ $mailer_name = 'Wordpress';
66
+ $service_name = 'Hosting Provider';
67
+ if (!empty($functions)) {
68
+ $mailer_name .= '<br>(see below)';
69
+ $service_name .= '<br>(see below)';
70
+ }
71
+ $icon = 'fab fa-wordpress';
72
+ } else if ($mailer instanceof NewsletterDefaultSMTPMailer) {
73
+ $mailer_name = 'Internal SMTP';
74
+ $service_name = 'SMTP Provider';
75
+ } else {
76
+ $mailer_name = 'Unknown';
77
+ $service_name = 'Unknown';
78
+ if (is_object($mailer)) {
79
+ if (method_exists($mailer, 'get_description')) {
80
+ $mailer_name = esc_html($mailer->get_description());
81
+ $service_name = esc_html(ucfirst($mailer->get_name()) . ' Service');
82
+ } else {
83
+ $mailer_name = esc_html(get_class($mailer));
84
+ $service_name = $mailer_name;
85
+ }
86
+ }
87
+ }
88
+
89
+ function tnp_get_hook_functions($tag) {
90
+ global $wp_filter;
91
+ $b = '';
92
+ if (isset($wp_filter[$tag])) {
93
+ foreach ($wp_filter[$tag]->callbacks as $priority => $functions) {
94
+
95
+ foreach ($functions as $function) {
96
+ $b .= '[' . $priority . '] ';
97
+ if (is_array($function['function'])) {
98
+ if (is_object($function['function'][0])) {
99
+ $b .= get_class($function['function'][0]) . '::' . $function['function'][1];
100
+ } else {
101
+ $b .= $function['function'][0] . '::' . $function['function'][1];
102
+ }
103
+ } else {
104
+ if (is_object($function['function'])) {
105
+ $fn = new ReflectionFunction($function['function']);
106
+ $b .= get_class($fn->getClosureThis()) . '(closure)';
107
+ } else {
108
+ $b .= $function['function'];
109
+ }
110
+ }
111
+ $b .= "<br>";
112
+ }
113
+ }
114
+ }
115
+ return $b;
116
+ }
117
+
118
+ $speed = Newsletter::$instance->get_send_speed();
119
+ ?>
120
+
121
+ <style>
122
+ <?php include __DIR__ . '/css/system.css' ?>
123
+ </style>
124
+
125
+ <div class="wrap tnp-system tnp-system-delivery" id="tnp-wrap">
126
+
127
+ <?php include NEWSLETTER_DIR . '/tnp-header.php'; ?>
128
+
129
+ <div id="tnp-heading">
130
+
131
+ <h2><?php _e('Email Delivery', 'newsletter') ?></h2>
132
+
133
+ </div>
134
+
135
+ <div id="tnp-body">
136
+ <form method="post" action="">
137
+ <?php $controls->init(); ?>
138
+ <h3>Mailing test</h3>
139
+
140
+ <p>
141
+ <?php $controls->text_email('test_email', ['required' => true]) ?>
142
+ <?php $controls->button('test', __('Send a test message')) ?>
143
+ <?php if (empty($options['mail'])) { ?>
144
+ <span class="tnp-ko">KO</span>
145
+ <?php } else { ?>
146
+ <span class="tnp-ok">OK</span>
147
+ <?php } ?>
148
+ </p>
149
+
150
+ <p>
151
+ <?php if (empty($options['mail'])) { ?>
152
+ <?php if (empty($options['mail_error'])) { ?>
153
+ <p>A test has never run.</p>
154
+ <?php } else { ?>
155
+ <p>Last test failed with error "<?php echo esc_html($options['mail_error']) ?>".</p>
156
+
157
+ <?php } ?>
158
+ <?php } else { ?>
159
+ <p>Last test was successful. If you didn't receive the test email:</p>
160
+ <ol style="color: #fff">
161
+ <li>If you're using an third party SMTP plugin, do a test from that plugin configuration panel</li>
162
+ <li>If you're using a Newsletter Delivery Addon, do a test from that addon configuration panel</li>
163
+ <li>If previous points do not apply to you, ask for support to your provider reporting the emails from your blog are not delivered</li>
164
+ </ol>
165
+ <?php } ?>
166
+ <p><a href="https://www.thenewsletterplugin.com/documentation/email-sending-issues" target="_blank">Read more to solve your issues, if any</a></p>
167
+
168
+
169
+ </form>
170
+
171
+ <h3>
172
+ How are messages delivered by Newsletter to your subscribers?
173
+ </h3>
174
+
175
+ <div class="tnp-flow">
176
+ <div class="tnp-mail"><i class="fas fa-envelope"></i><br><br>Messages<br>
177
+ (max: <?php echo esc_html($speed) ?> emails per hour)
178
+ </div>
179
+ <div class="tnp-arrow">&rightarrow;</div>
180
+ <div class="tnp-addon"><i class="<?php echo $icon ?>"></i><br><br><?php echo $mailer_name ?></div>
181
+ <div class="tnp-arrow">&rightarrow;</div>
182
+ <div class="tnp-service"><i class="fas fa-cog"></i><br><br>
183
+ <?php echo $service_name ?>
184
+ </div>
185
+ <div class="tnp-arrow">&rightarrow;</div>
186
+ <div class="tnp-user"><i class="fas fa-user"></i></div>
187
+ </div>
188
+
189
+
190
+ <?php if (!empty($functions)) { ?>
191
+ <br><br>
192
+ <h3>Functions that are changing the default WordPress delivery system</h3>
193
+ <p><?php echo $functions ?></p>
194
+ <?php } ?>
195
+
196
+
197
+ </div>
198
+ <?php include NEWSLETTER_DIR . '/tnp-footer.php'; ?>
199
+ </div>
tnp-header.php CHANGED
@@ -1,335 +1,335 @@
1
- <?php
2
- global $current_user, $wpdb;
3
-
4
- defined('ABSPATH') || exit;
5
-
6
- $dismissed = get_option('newsletter_dismissed', []);
7
-
8
- $user_count = Newsletter::instance()->get_user_count();
9
-
10
- $is_administrator = current_user_can('administrator');
11
-
12
- function newsletter_print_entries($group) {
13
- $entries = apply_filters('newsletter_menu_' . $group, array());
14
- if (!$entries) {
15
- return;
16
- }
17
-
18
- foreach ($entries as &$entry) {
19
- echo '<li><a href="', $entry['url'], '">', $entry['label'];
20
- if (!empty($entry['description'])) {
21
- echo '<small>', $entry['description'], '</small>';
22
- }
23
- echo '</a></li>';
24
- }
25
- }
26
-
27
- // Check the status to show a warning if needed
28
- $status_options = Newsletter::instance()->get_options('status');
29
- $warning = false;
30
-
31
- $warning |= empty($status_options['mail']);
32
- ?>
33
-
34
- <div class="tnp-drowpdown" id="tnp-header">
35
- <a href="?page=newsletter_main_index"><img src="<?php echo plugins_url('newsletter'); ?>/admin/images/logo-red.png" class="tnp-header-logo" style="vertical-align: bottom;"></a>
36
- <ul>
37
- <li><a href="#"><i class="fas fa-users"></i> <?php _e('Subscribers', 'newsletter') ?> <i class="fas fa-chevron-down"></i></a>
38
- <ul>
39
- <li>
40
- <a href="?page=newsletter_users_index"><i class="fas fa-search"></i> <?php _e('Search And Edit', 'newsletter') ?>
41
- <small><?php _e('Add, edit, search', 'newsletter') ?></small></a>
42
- </li>
43
-
44
- <li>
45
- <a href="?page=newsletter_profile_index"><i class="fas fa-user-circle"></i> <?php _e('Profile page', 'newsletter') ?>
46
- <small><?php _e('The subscriber personal profile editing panel', 'newsletter') ?></small></a>
47
- </li>
48
-
49
- <?php if (!class_exists('NewsletterImport')) { ?>
50
- <li>
51
- <a href="?page=newsletter_users_import"><i class="fas fa-upload"></i> <?php _e('Import', 'newsletter') ?>
52
- <small><?php _e('Import from external sources', 'newsletter') ?></small></a>
53
- </li>
54
- <?php } ?>
55
-
56
- <li>
57
- <a href="?page=newsletter_users_export"><i class="fas fa-download"></i> <?php _e('Export', 'newsletter') ?>
58
- <small><?php _e('Export your subscribers list', 'newsletter') ?></small></a>
59
- </li>
60
-
61
- <li>
62
- <a href="?page=newsletter_users_massive"><i class="fas fa-wrench"></i> <?php _e('Maintenance', 'newsletter') ?>
63
- <small><?php _e('Massive actions: change list, clean up, ...', 'newsletter') ?></small></a>
64
- </li>
65
-
66
- <li>
67
- <a href="?page=newsletter_users_statistics"><i class="fas fa-chart-bar"></i> <?php _e('Statistics', 'newsletter') ?>
68
- <small><?php _e('All about your subscribers', 'newsletter') ?></small></a>
69
- </li>
70
-
71
- <?php newsletter_print_entries('subscribers') ?>
72
- </ul>
73
- </li>
74
- <li><a href="#"><i class="fas fa-list"></i> <?php _e('List Building', 'newsletter') ?> <i class="fas fa-chevron-down"></i></a>
75
- <ul>
76
- <li>
77
- <a href="?page=newsletter_subscription_profile"><i class="fas fa-check-square"></i> <?php _e('Subscription Form Fields, Buttons, Labels', 'newsletter') ?>
78
- <small><?php _e('When and what data to collect', 'newsletter') ?></small></a>
79
- </li>
80
-
81
- <li>
82
- <a href="?page=newsletter_subscription_options"><i class="fas fa-sign-in-alt"></i> <?php _e('Subscription', 'newsletter') ?>
83
- <small><?php _e('The subscription process in detail', 'newsletter') ?></small></a>
84
- </li>
85
-
86
- <li>
87
- <a href="?page=newsletter_subscription_lists"><i class="fas fa-th-list"></i> <?php _e('Lists', 'newsletter') ?>
88
- <small><?php _e('Profile the subscribers for a better targeting', 'newsletter') ?></small></a>
89
- </li>
90
-
91
- <li>
92
- <a href="?page=newsletter_subscription_antibot"><i class="fas fa-lock"></i> <?php _e('Security', 'newsletter') ?>
93
- <small><?php _e('Spam subscriptions control', 'newsletter') ?></small></a>
94
- </li>
95
-
96
- <li>
97
- <a href="?page=newsletter_unsubscription_index"><i class="fas fa-sign-out-alt"></i> <?php _e('Unsubscription', 'newsletter') ?>
98
- <small><?php _e('How to give the last goodbye (or avoid it!)', 'newsletter') ?></small></a>
99
- </li>
100
-
101
- <?php
102
- newsletter_print_entries('subscription');
103
- ?>
104
- </ul>
105
- </li>
106
-
107
- <li>
108
- <a href="#"><i class="fas fa-newspaper"></i> <?php _e('Newsletters', 'newsletter') ?> <i class="fas fa-chevron-down"></i></a>
109
- <ul>
110
- <li>
111
- <a href="?page=newsletter_emails_composer"><i class="fas fa-plus"></i> <?php _e('Create newsletter', 'newsletter') ?>
112
- <small><?php _e('Start your new campaign', 'newsletter') ?></small></a>
113
- </li>
114
-
115
- <li>
116
- <a href="?page=newsletter_emails_index"><i class="fas fa-newspaper"></i> <?php _e('Newsletters', 'newsletter') ?>
117
- <small><?php _e('The classic "write & send" newsletters', 'newsletter') ?></small></a>
118
- </li>
119
-
120
- <li>
121
- <a href="<?php echo NewsletterStatistics::instance()->get_index_url() ?>"><i class="fas fa-chart-bar"></i> <?php _e('Statistics', 'newsletter') ?>
122
- <small><?php _e('Tracking configuration and basic data', 'newsletter') ?></small></a>
123
- </li>
124
- <?php
125
- newsletter_print_entries('newsletters');
126
- ?>
127
- </ul>
128
- </li>
129
-
130
- <li>
131
- <a href="#"><i class="fas fa-cog"></i> <?php _e('Settings', 'newsletter') ?> <i class="fas fa-chevron-down"></i></a>
132
- <ul>
133
- <?php if ($is_administrator) { ?>
134
- <li>
135
- <a href="?page=newsletter_main_main"><i class="fas fa-cogs"></i> <?php _e('General Settings', 'newsletter') ?>
136
- <small><?php _e('Delivery speed, sender details, ...', 'newsletter') ?></small></a>
137
- </li>
138
- <?php if (!class_exists('NewsletterSmtp')) { ?>
139
- <li>
140
- <a href="?page=newsletter_main_smtp"><i class="fas fa-server"></i> <?php _e('SMTP', 'newsletter') ?>
141
- <small><?php _e('External mail server', 'newsletter') ?></small>
142
- </a>
143
- </li>
144
- <?php } ?>
145
- <?php } ?>
146
-
147
- <li>
148
- <a href="?page=newsletter_main_info"><i class="fas fa-info"></i> <?php _e('Company Info', 'newsletter') ?>
149
- <small><?php _e('Social, address, logo and general info', 'newsletter') ?></small></a>
150
- </li>
151
-
152
- <li>
153
- <a href="?page=newsletter_subscription_template"><i class="fas fa-file-alt"></i> <?php _e('Messages Template', 'newsletter') ?>
154
- <small><?php _e('Change the look of your service emails', 'newsletter') ?></small></a>
155
- </li>
156
-
157
- <?php
158
- newsletter_print_entries('settings');
159
- ?>
160
- </ul>
161
- </li>
162
-
163
- <?php if ($is_administrator) { ?>
164
- <li>
165
- <a href="#"><i class="fas fa-thermometer"></i> <?php _e('System', 'newsletter') ?>
166
- <?php if ($warning) { ?>
167
- <i class="fas fa-exclamation-triangle" style="color: red;"></i>
168
- <?php } ?>
169
- </a>
170
- <ul>
171
- <li>
172
- <a href="<?php echo admin_url('site-health.php') ?> "><i class="fas fa-file"></i> <?php _e('Site health') ?>
173
- <small><?php _e('WP native site health checks', 'newsletter') ?></small></a>
174
- </li>
175
- <li>
176
- <a href="?page=newsletter_system_delivery"><i class="fas fa-file"></i> <?php _e('Delivery Diagnostic', 'newsletter') ?>
177
- <small><?php _e('Delivery analysis and test', 'newsletter') ?></small></a>
178
- </li>
179
- <li>
180
- <a href="?page=newsletter_system_scheduler"><i class="fas fa-robot"></i> <?php _e('Scheduler', 'newsletter') ?>
181
- <small><?php _e('WordPress background jobs', 'newsletter') ?></small></a>
182
- </li>
183
- <li>
184
- <a href="?page=newsletter_system_status"><i class="fas fa-file"></i> <?php _e('Status', 'newsletter') ?>
185
- <small><?php _e('Checks and parameters', 'newsletter') ?></small></a>
186
- </li>
187
-
188
- <li>
189
- <a href="?page=newsletter_system_logs"><i class="fas fa-file"></i> <?php _e('Logs', 'newsletter') ?>
190
- <small><?php _e('Plugin and addons logs', 'newsletter') ?></small></a>
191
- </li>
192
- </ul>
193
- </li>
194
- <?php } ?>
195
-
196
- <?php
197
- $license_data = Newsletter::instance()->get_license_data();
198
- $premium_url = 'https://www.thenewsletterplugin.com/premium?utm_source=header&utm_medium=link&utm_campaign=plugin&utm_content=' . urlencode($_GET['page']);
199
- ?>
200
-
201
- <?php if (empty($license_data)) { ?>
202
- <?php if (time() < 1638226799) { ?>
203
- <li class="tnp-professional-extensions-button" style="background-color: #000; color: #fff; border-color: #FF5F65!important; border-width: 2px !important;"><a href="https://www.thenewsletterplugin.com/premium?utm_source=header&utm_medium=link&utm_campaign=black-friday" target="_blank">
204
- <i class="fas fa-trophy"></i> BLACK FRIDAY IS LIVE</a>
205
- </li>
206
- <?php } else { ?>
207
- <li class="tnp-professional-extensions-button"><a href="<?php echo $premium_url ?>" target="_blank">
208
- <i class="fas fa-trophy"></i> <?php _e('Get Professional Addons', 'newsletter') ?></a>
209
- </li>
210
- <?php } ?>
211
- <?php } elseif (is_wp_error($license_data)) { ?>
212
- <li class="tnp-professional-extensions-button-red">
213
- <a href="?page=newsletter_main_main"><i class="fas fa-hand-paper" style="color: white"></i> <?php _e('Unable to check', 'newsletter') ?></a>
214
- </li>
215
-
216
- <?php } elseif ($license_data->expire == 0) { ?>
217
- <?php if (time() < 1638226799) { ?>
218
- <li class="tnp-professional-extensions-button" style="background-color: #000; color: #fff; border-color: #FF5F65!important; border-width: 2px !important;"><a href="https://www.thenewsletterplugin.com/premium?utm_source=header&utm_medium=link&utm_campaign=black-friday" target="_blank">
219
- <i class="fas fa-trophy"></i> BLACK FRIDAY IS LIVE</a>
220
- </li>
221
- <?php } else { ?>
222
- <li class="tnp-professional-extensions-button"><a href="<?php echo $premium_url ?>" target="_blank">
223
- <i class="fas fa-trophy"></i> <?php _e('Get Professional Addons', 'newsletter') ?></a>
224
- </li>
225
- <?php } ?>
226
-
227
- <?php } elseif ($license_data->expire < time()) { ?>
228
-
229
- <li class="tnp-professional-extensions-button-red">
230
- <a href="?page=newsletter_main_main"><i class="fas fa-hand-paper" style="color: white"></i> <?php _e('License expired', 'newsletter') ?></a>
231
- </li>
232
-
233
- <?php } elseif ($license_data->expire >= time()) { ?>
234
-
235
- <?php $p = class_exists('NewsletterExtensions') ? 'newsletter_extensions_index' : 'newsletter_main_extensions'; ?>
236
- <li class="tnp-professional-extensions-button">
237
- <a href="?page=<?php echo $p ?>"><i class="fas fa-check-square"></i> <?php _e('License active', 'newsletter') ?></a>
238
- </li>
239
-
240
- <?php } ?>
241
- </ul>
242
- </div>
243
-
244
- <?php if (isset($_GET['debug']) || !isset($dismissed['newsletter-shortcode'])) { ?>
245
- <?php
246
- // Check of Newsletter dedicated page
247
- if (!empty(Newsletter::instance()->options['page'])) {
248
- if (get_post_status(Newsletter::instance()->options['page']) === 'publish') {
249
- $content = get_post_field('post_content', Newsletter::instance()->options['page']);
250
- // With and without attributes
251
- if (strpos($content, '[newsletter]') === false && strpos($content, '[newsletter ') === false) {
252
- ?>
253
- <div class="tnp-notice">
254
- <a href="<?php echo $_SERVER['REQUEST_URI'] . '&noheader=1&dismiss=newsletter-shortcode' ?>" class="tnp-dismiss">&times;</a>
255
- The Newsletter dedicated page does not contain the [newsletter] shortcode. If you're using a visual composer it could be ok.
256
- <a href="<?php echo site_url('/wp-admin/post.php') ?>?post=<?php echo esc_attr(Newsletter::instance()->options['page']) ?>&action=edit"><strong>Edit the page</strong></a>.
257
-
258
- </div>
259
- <?php
260
- }
261
- }
262
- }
263
- ?>
264
- <?php } ?>
265
-
266
- <?php if (isset($_GET['debug']) || !isset($dismissed['rate']) && $user_count > 300) { ?>
267
- <div class="tnp-notice">
268
- <a href="<?php echo $_SERVER['REQUEST_URI'] . '&noheader=1&dismiss=rate' ?>" class="tnp-dismiss">&times;</a>
269
-
270
- We never asked before and we're curious: <a href="http://wordpress.org/plugins/newsletter/" target="_blank">would you rate this plugin</a>?
271
- (few seconds required - account on WordPress.org required, every blog owner should have one...). <strong>Really appreciated, The Newsletter Team</strong>.
272
-
273
- </div>
274
- <?php } ?>
275
-
276
- <?php if (isset($_GET['debug']) || !isset($dismissed['newsletter-page']) && empty(Newsletter::instance()->options['page'])) { ?>
277
- <div class="tnp-notice">
278
- <a href="<?php echo $_SERVER['REQUEST_URI'] . '&noheader=1&dismiss=newsletter-page' ?>" class="tnp-dismiss">&times;</a>
279
-
280
- You should create a blog page to show the subscription form and the subscription messages. Go to the
281
- <a href="?page=newsletter_main_main">general settings panel</a> to configure it.
282
-
283
- </div>
284
- <?php } ?>
285
-
286
- <?php if (isset($_GET['debug']) || !isset($dismissed['newsletter-subscribe']) && get_option('newsletter_install_time') && get_option('newsletter_install_time') < time() - 86400 * 15) { ?>
287
- <div class="tnp-notice">
288
- <a href="<?php echo $_SERVER['REQUEST_URI'] . '&noheader=1&dismiss=newsletter-subscribe' ?>" class="tnp-dismiss">&times;</a>
289
- If you want to be informed of important updates of Newsletter, you may want to subscribe to our newsletter<br>
290
- <form action="https://www.thenewsletterplugin.com/?na=s" target="_blank" method="post">
291
- <input type="hidden" value="plugin-header" name="nr">
292
- <input type="hidden" value="3" name="nl[]">
293
- <input type="hidden" value="single" name="optin">
294
- <input type="email" name="ne" value="<?php echo esc_attr($current_user->user_email) ?>">
295
- <input type="submit" value="<?php esc_attr_e('Subscribe', 'newsletter') ?>">
296
- </form>
297
- </div>
298
- <?php } ?>
299
-
300
- <?php
301
- if (!defined('NEWSLETTER_CRON_WARNINGS') || NEWSLETTER_CRON_WARNINGS) {
302
- $x = NewsletterSystem::instance()->get_job_status();
303
- if ($x !== NewsletterSystem::JOB_OK) {
304
- echo '<div class="tnpc-warning">The are issues with the delivery engine. Please <a href="?page=newsletter_system_scheduler">check them here</a>.</div>';
305
- }
306
- }
307
- ?>
308
-
309
- <?php
310
- if ($_GET['page'] !== 'newsletter_emails_edit') {
311
-
312
- $last_failed_newsletters = Newsletter::instance()->get_emails_by_field('status', TNP_Email::STATUS_ERROR);
313
-
314
- foreach ($last_failed_newsletters as $newsletter) {
315
- echo '<div class="tnpc-error">';
316
- printf(__('Newsletter "%s" stopped by fatal error.', 'newsletter'), esc_html($newsletter->subject));
317
- echo '&nbsp;';
318
- $c = new NewsletterControls();
319
- $c->btn_link('?page=newsletter_emails_edit&id=' . $newsletter->id, __('Check', 'newsletter'));
320
- echo '</div>';
321
- }
322
- }
323
- ?>
324
-
325
- <div id="tnp-notification">
326
- <?php
327
- if (isset($controls)) {
328
- $controls->show();
329
- $controls->messages = '';
330
- $controls->errors = '';
331
- }
332
- ?>
333
- </div>
334
-
335
-
1
+ <?php
2
+ global $current_user, $wpdb;
3
+
4
+ defined('ABSPATH') || exit;
5
+
6
+ $dismissed = get_option('newsletter_dismissed', []);
7
+
8
+ $user_count = Newsletter::instance()->get_user_count();
9
+
10
+ $is_administrator = current_user_can('administrator');
11
+
12
+ function newsletter_print_entries($group) {
13
+ $entries = apply_filters('newsletter_menu_' . $group, array());
14
+ if (!$entries) {
15
+ return;
16
+ }
17
+
18
+ foreach ($entries as &$entry) {
19
+ echo '<li><a href="', $entry['url'], '">', $entry['label'];
20
+ if (!empty($entry['description'])) {
21
+ echo '<small>', $entry['description'], '</small>';
22
+ }
23
+ echo '</a></li>';
24
+ }
25
+ }
26
+
27
+ // Check the status to show a warning if needed
28
+ $status_options = Newsletter::instance()->get_options('status');
29
+ $warning = false;
30
+
31
+ $warning |= empty($status_options['mail']);
32
+ ?>
33
+
34
+ <div class="tnp-drowpdown" id="tnp-header">
35
+ <a href="?page=newsletter_main_index"><img src="<?php echo plugins_url('newsletter'); ?>/admin/images/logo-red.png" class="tnp-header-logo" style="vertical-align: bottom;"></a>
36
+ <ul>
37
+ <li><a href="#"><i class="fas fa-users"></i> <?php _e('Subscribers', 'newsletter') ?> <i class="fas fa-chevron-down"></i></a>
38
+ <ul>
39
+ <li>
40
+ <a href="?page=newsletter_users_index"><i class="fas fa-search"></i> <?php _e('Search And Edit', 'newsletter') ?>
41
+ <small><?php _e('Add, edit, search', 'newsletter') ?></small></a>
42
+ </li>
43
+
44
+ <li>
45
+ <a href="?page=newsletter_profile_index"><i class="fas fa-user-circle"></i> <?php _e('Profile page', 'newsletter') ?>
46
+ <small><?php _e('The subscriber personal profile editing panel', 'newsletter') ?></small></a>
47
+ </li>
48
+
49
+ <?php if (!class_exists('NewsletterImport')) { ?>
50
+ <li>
51
+ <a href="?page=newsletter_users_import"><i class="fas fa-upload"></i> <?php _e('Import', 'newsletter') ?>
52
+ <small><?php _e('Import from external sources', 'newsletter') ?></small></a>
53
+ </li>
54
+ <?php } ?>
55
+
56
+ <li>
57
+ <a href="?page=newsletter_users_export"><i class="fas fa-download"></i> <?php _e('Export', 'newsletter') ?>
58
+ <small><?php _e('Export your subscribers list', 'newsletter') ?></small></a>
59
+ </li>
60
+
61
+ <li>
62
+ <a href="?page=newsletter_users_massive"><i class="fas fa-wrench"></i> <?php _e('Maintenance', 'newsletter') ?>
63
+ <small><?php _e('Massive actions: change list, clean up, ...', 'newsletter') ?></small></a>
64
+ </li>
65
+
66
+ <li>
67
+ <a href="?page=newsletter_users_statistics"><i class="fas fa-chart-bar"></i> <?php _e('Statistics', 'newsletter') ?>
68
+ <small><?php _e('All about your subscribers', 'newsletter') ?></small></a>
69
+ </li>
70
+
71
+ <?php newsletter_print_entries('subscribers') ?>
72
+ </ul>
73
+ </li>
74
+ <li><a href="#"><i class="fas fa-list"></i> <?php _e('List Building', 'newsletter') ?> <i class="fas fa-chevron-down"></i></a>
75
+ <ul>
76
+ <li>
77
+ <a href="?page=newsletter_subscription_profile"><i class="fas fa-check-square"></i> <?php _e('Subscription Form Fields, Buttons, Labels', 'newsletter') ?>
78
+ <small><?php _e('When and what data to collect', 'newsletter') ?></small></a>
79
+ </li>
80
+
81
+ <li>
82
+ <a href="?page=newsletter_subscription_options"><i class="fas fa-sign-in-alt"></i> <?php _e('Subscription', 'newsletter') ?>
83
+ <small><?php _e('The subscription process in detail', 'newsletter') ?></small></a>
84
+ </li>
85
+
86
+ <li>
87
+ <a href="?page=newsletter_subscription_lists"><i class="fas fa-th-list"></i> <?php _e('Lists', 'newsletter') ?>
88
+ <small><?php _e('Profile the subscribers for a better targeting', 'newsletter') ?></small></a>
89
+ </li>
90
+
91
+ <li>
92
+ <a href="?page=newsletter_subscription_antibot"><i class="fas fa-lock"></i> <?php _e('Security', 'newsletter') ?>
93
+ <small><?php _e('Spam subscriptions control', 'newsletter') ?></small></a>
94
+ </li>
95
+
96
+ <li>
97
+ <a href="?page=newsletter_unsubscription_index"><i class="fas fa-sign-out-alt"></i> <?php _e('Unsubscription', 'newsletter') ?>
98
+ <small><?php _e('How to give the last goodbye (or avoid it!)', 'newsletter') ?></small></a>
99
+ </li>
100
+
101
+ <?php
102
+ newsletter_print_entries('subscription');
103
+ ?>
104
+ </ul>
105
+ </li>
106
+
107
+ <li>
108
+ <a href="#"><i class="fas fa-newspaper"></i> <?php _e('Newsletters', 'newsletter') ?> <i class="fas fa-chevron-down"></i></a>
109
+ <ul>
110
+ <li>
111
+ <a href="?page=newsletter_emails_composer"><i class="fas fa-plus"></i> <?php _e('Create newsletter', 'newsletter') ?>
112
+ <small><?php _e('Start your new campaign', 'newsletter') ?></small></a>
113
+ </li>
114
+
115
+ <li>
116
+ <a href="?page=newsletter_emails_index"><i class="fas fa-newspaper"></i> <?php _e('Newsletters', 'newsletter') ?>
117
+ <small><?php _e('The classic "write & send" newsletters', 'newsletter') ?></small></a>
118
+ </li>
119
+
120
+ <li>
121
+ <a href="<?php echo NewsletterStatistics::instance()->get_index_url() ?>"><i class="fas fa-chart-bar"></i> <?php _e('Statistics', 'newsletter') ?>
122
+ <small><?php _e('Tracking configuration and basic data', 'newsletter') ?></small></a>
123
+ </li>
124
+ <?php
125
+ newsletter_print_entries('newsletters');
126
+ ?>
127
+ </ul>
128
+ </li>
129
+
130
+ <li>
131
+ <a href="#"><i class="fas fa-cog"></i> <?php _e('Settings', 'newsletter') ?> <i class="fas fa-chevron-down"></i></a>
132
+ <ul>
133
+ <?php if ($is_administrator) { ?>
134
+ <li>
135
+ <a href="?page=newsletter_main_main"><i class="fas fa-cogs"></i> <?php _e('General Settings', 'newsletter') ?>
136
+ <small><?php _e('Delivery speed, sender details, ...', 'newsletter') ?></small></a>
137
+ </li>
138
+ <?php if (!class_exists('NewsletterSmtp')) { ?>
139
+ <li>
140
+ <a href="?page=newsletter_main_smtp"><i class="fas fa-server"></i> <?php _e('SMTP', 'newsletter') ?>
141
+ <small><?php _e('External mail server', 'newsletter') ?></small>
142
+ </a>
143
+ </li>
144
+ <?php } ?>
145
+ <?php } ?>
146
+
147
+ <li>
148
+ <a href="?page=newsletter_main_info"><i class="fas fa-info"></i> <?php _e('Company Info', 'newsletter') ?>
149
+ <small><?php _e('Social, address, logo and general info', 'newsletter') ?></small></a>
150
+ </li>
151
+
152
+ <li>
153
+ <a href="?page=newsletter_subscription_template"><i class="fas fa-file-alt"></i> <?php _e('Messages Template', 'newsletter') ?>
154
+ <small><?php _e('Change the look of your service emails', 'newsletter') ?></small></a>
155
+ </li>
156
+
157
+ <?php
158
+ newsletter_print_entries('settings');
159
+ ?>
160
+ </ul>
161
+ </li>
162
+
163
+ <?php if ($is_administrator) { ?>
164
+ <li>
165
+ <a href="#"><i class="fas fa-thermometer"></i> <?php _e('System', 'newsletter') ?>
166
+ <?php if ($warning) { ?>
167
+ <i class="fas fa-exclamation-triangle" style="color: red;"></i>
168
+ <?php } ?>
169
+ </a>
170
+ <ul>
171
+ <li>
172
+ <a href="<?php echo admin_url('site-health.php') ?> "><i class="fas fa-file"></i> <?php _e('Site health') ?>
173
+ <small><?php _e('WP native site health checks', 'newsletter') ?></small></a>
174
+ </li>
175
+ <li>
176
+ <a href="?page=newsletter_system_delivery"><i class="fas fa-file"></i> <?php _e('Delivery Diagnostic', 'newsletter') ?>
177
+ <small><?php _e('Delivery analysis and test', 'newsletter') ?></small></a>
178
+ </li>
179
+ <li>
180
+ <a href="?page=newsletter_system_scheduler"><i class="fas fa-robot"></i> <?php _e('Scheduler', 'newsletter') ?>
181
+ <small><?php _e('WordPress background jobs', 'newsletter') ?></small></a>
182
+ </li>
183
+ <li>
184
+ <a href="?page=newsletter_system_status"><i class="fas fa-file"></i> <?php _e('Status', 'newsletter') ?>
185
+ <small><?php _e('Checks and parameters', 'newsletter') ?></small></a>
186
+ </li>
187
+
188
+ <li>
189
+ <a href="?page=newsletter_system_logs"><i class="fas fa-file"></i> <?php _e('Logs', 'newsletter') ?>
190
+ <small><?php _e('Plugin and addons logs', 'newsletter') ?></small></a>
191
+ </li>
192
+ </ul>
193
+ </li>
194
+ <?php } ?>
195
+
196
+ <?php
197
+ $license_data = Newsletter::instance()->get_license_data();
198
+ $premium_url = 'https://www.thenewsletterplugin.com/premium?utm_source=header&utm_medium=link&utm_campaign=plugin&utm_content=' . urlencode($_GET['page']);
199
+ ?>
200
+
201
+ <?php if (empty($license_data)) { ?>
202
+ <?php if (time() < 1638226799) { ?>
203
+ <li class="tnp-professional-extensions-button" style="background-color: #000; color: #fff; border-color: #FF5F65!important; border-width: 2px !important;"><a href="https://www.thenewsletterplugin.com/premium?utm_source=header&utm_medium=link&utm_campaign=black-friday" target="_blank">
204
+ <i class="fas fa-trophy"></i> BLACK FRIDAY IS LIVE</a>
205
+ </li>
206
+ <?php } else { ?>
207
+ <li class="tnp-professional-extensions-button"><a href="<?php echo $premium_url ?>" target="_blank">
208
+ <i class="fas fa-trophy"></i> <?php _e('Get Professional Addons', 'newsletter') ?></a>
209
+ </li>
210
+ <?php } ?>
211
+ <?php } elseif (is_wp_error($license_data)) { ?>
212
+ <li class="tnp-professional-extensions-button-red">
213
+ <a href="?page=newsletter_main_main"><i class="fas fa-hand-paper" style="color: white"></i> <?php _e('Unable to check', 'newsletter') ?></a>
214
+ </li>
215
+
216
+ <?php } elseif ($license_data->expire == 0) { ?>
217
+ <?php if (time() < 1638226799) { ?>
218
+ <li class="tnp-professional-extensions-button" style="background-color: #000; color: #fff; border-color: #FF5F65!important; border-width: 2px !important;"><a href="https://www.thenewsletterplugin.com/premium?utm_source=header&utm_medium=link&utm_campaign=black-friday" target="_blank">
219
+ <i class="fas fa-trophy"></i> BLACK FRIDAY IS LIVE</a>
220
+ </li>
221
+ <?php } else { ?>
222
+ <li class="tnp-professional-extensions-button"><a href="<?php echo $premium_url ?>" target="_blank">
223
+ <i class="fas fa-trophy"></i> <?php _e('Get Professional Addons', 'newsletter') ?></a>
224
+ </li>
225
+ <?php } ?>
226
+
227
+ <?php } elseif ($license_data->expire < time()) { ?>
228
+
229
+ <li class="tnp-professional-extensions-button-red">
230
+ <a href="?page=newsletter_main_main"><i class="fas fa-hand-paper" style="color: white"></i> <?php _e('License expired', 'newsletter') ?></a>
231
+ </li>
232
+
233
+ <?php } elseif ($license_data->expire >= time()) { ?>
234
+
235
+ <?php $p = class_exists('NewsletterExtensions') ? 'newsletter_extensions_index' : 'newsletter_main_extensions'; ?>
236
+ <li class="tnp-professional-extensions-button">
237
+ <a href="?page=<?php echo $p ?>"><i class="fas fa-check-square"></i> <?php _e('License active', 'newsletter') ?></a>
238
+ </li>
239
+
240
+ <?php } ?>
241
+ </ul>
242
+ </div>
243
+
244
+ <?php if (isset($_GET['debug']) || !isset($dismissed['newsletter-shortcode'])) { ?>
245
+ <?php
246
+ // Check of Newsletter dedicated page
247
+ if (!empty(Newsletter::instance()->options['page'])) {
248
+ if (get_post_status(Newsletter::instance()->options['page']) === 'publish') {
249
+ $content = get_post_field('post_content', Newsletter::instance()->options['page']);
250
+ // With and without attributes
251
+ if (strpos($content, '[newsletter]') === false && strpos($content, '[newsletter ') === false) {
252
+ ?>
253
+ <div class="tnp-notice">
254
+ <a href="<?php echo $_SERVER['REQUEST_URI'] . '&noheader=1&dismiss=newsletter-shortcode' ?>" class="tnp-dismiss">&times;</a>
255
+ The Newsletter dedicated page does not contain the [newsletter] shortcode. If you're using a visual composer it could be ok.
256
+ <a href="<?php echo site_url('/wp-admin/post.php') ?>?post=<?php echo esc_attr(Newsletter::instance()->options['page']) ?>&action=edit"><strong>Edit the page</strong></a>.
257
+
258
+ </div>
259
+ <?php
260
+ }
261
+ }
262
+ }
263
+ ?>
264
+ <?php } ?>
265
+
266
+ <?php if (isset($_GET['debug']) || !isset($dismissed['rate']) && $user_count > 300) { ?>
267
+ <div class="tnp-notice">
268
+ <a href="<?php echo $_SERVER['REQUEST_URI'] . '&noheader=1&dismiss=rate' ?>" class="tnp-dismiss">&times;</a>
269
+
270
+ We never asked before and we're curious: <a href="http://wordpress.org/plugins/newsletter/" target="_blank">would you rate this plugin</a>?
271
+ (few seconds required - account on WordPress.org required, every blog owner should have one...). <strong>Really appreciated, The Newsletter Team</strong>.
272
+
273
+ </div>
274
+ <?php } ?>
275
+
276
+ <?php if (isset($_GET['debug']) || !isset($dismissed['newsletter-page']) && empty(Newsletter::instance()->options['page'])) { ?>
277
+ <div class="tnp-notice">
278
+ <a href="<?php echo $_SERVER['REQUEST_URI'] . '&noheader=1&dismiss=newsletter-page' ?>" class="tnp-dismiss">&times;</a>
279
+
280
+ You should create a blog page to show the subscription form and the subscription messages. Go to the
281
+ <a href="?page=newsletter_main_main">general settings panel</a> to configure it.
282
+
283
+ </div>
284
+ <?php } ?>
285
+
286
+ <?php if (isset($_GET['debug']) || !isset($dismissed['newsletter-subscribe']) && get_option('newsletter_install_time') && get_option('newsletter_install_time') < time() - 86400 * 15) { ?>
287
+ <div class="tnp-notice">
288
+ <a href="<?php echo $_SERVER['REQUEST_URI'] . '&noheader=1&dismiss=newsletter-subscribe' ?>" class="tnp-dismiss">&times;</a>
289
+ If you want to be informed of important updates of Newsletter, you may want to subscribe to our newsletter<br>
290
+ <form action="https://www.thenewsletterplugin.com/?na=s" target="_blank" method="post">
291
+ <input type="hidden" value="plugin-header" name="nr">
292
+ <input type="hidden" value="3" name="nl[]">
293
+ <input type="hidden" value="single" name="optin">
294
+ <input type="email" name="ne" value="<?php echo esc_attr($current_user->user_email) ?>">
295
+ <input type="submit" value="<?php esc_attr_e('Subscribe', 'newsletter') ?>">
296
+ </form>
297
+ </div>
298
+ <?php } ?>
299
+
300
+ <?php
301
+ if (!defined('NEWSLETTER_CRON_WARNINGS') || NEWSLETTER_CRON_WARNINGS) {
302
+ $x = NewsletterSystem::instance()->get_job_status();
303
+ if ($x !== NewsletterSystem::JOB_OK) {
304
+ echo '<div class="tnpc-warning">The are issues with the delivery engine. Please <a href="?page=newsletter_system_scheduler">check them here</a>.</div>';
305
+ }
306
+ }
307
+ ?>
308
+
309
+ <?php
310
+ if ($_GET['page'] !== 'newsletter_emails_edit') {
311
+
312
+ $last_failed_newsletters = Newsletter::instance()->get_emails_by_field('status', TNP_Email::STATUS_ERROR);
313
+
314
+ foreach ($last_failed_newsletters as $newsletter) {
315
+ echo '<div class="tnpc-error">';
316
+ printf(__('Newsletter "%s" stopped by fatal error.', 'newsletter'), esc_html($newsletter->subject));
317
+ echo '&nbsp;';
318
+ $c = new NewsletterControls();
319
+ $c->btn_link('?page=newsletter_emails_edit&id=' . $newsletter->id, __('Check', 'newsletter'));
320
+ echo '</div>';
321
+ }
322
+ }
323
+ ?>
324
+
325
+ <div id="tnp-notification">
326
+ <?php
327
+ if (isset($controls)) {
328
+ $controls->show();
329
+ $controls->messages = '';
330
+ $controls->errors = '';
331
+ }
332
+ ?>
333
+ </div>
334
+
335
+
users/index.php CHANGED
@@ -1,251 +1,251 @@
1
- <?php
2
- /* @var $this NewsletterUsers */
3
- defined('ABSPATH') || exit;
4
-
5
- require_once NEWSLETTER_INCLUDES_DIR . '/controls.php';
6
-
7
- $controls = new NewsletterControls();
8
-
9
- $options = $controls->data;
10
- $options_profile = get_option('newsletter_profile');
11
- $options_main = get_option('newsletter_main');
12
-
13
- // Move to base zero
14
- if ($controls->is_action()) {
15
- if ($controls->is_action('reset')) {
16
- $controls->data = array();
17
- } else {
18
- $controls->data['search_page'] = (int) $controls->data['search_page'] - 1;
19
- }
20
- $this->save_options($controls->data, 'search');
21
- } else {
22
- $controls->data = $this->get_options('search');
23
- if (empty($controls->data['search_page']))
24
- $controls->data['search_page'] = 0;
25
- }
26
-
27
- if ($controls->is_action('resend')) {
28
- $user = $this->get_user($controls->button_data);
29
- NewsletterSubscription::instance()->send_message('confirmation', $user, true);
30
- $controls->messages = __('Activation email sent.', 'newsletter');
31
- }
32
-
33
- if ($controls->is_action('resend_welcome')) {
34
- $user = $this->get_user($controls->button_data);
35
- NewsletterSubscription::instance()->send_message('confirmed', $user, true);
36
- $controls->messages = __('Welcome email sent.', 'newsletter');
37
- }
38
-
39
- if ($controls->is_action('delete')) {
40
- $this->delete_user($controls->button_data);
41
- unset($controls->data['subscriber_id']);
42
- }
43
-
44
- if ($controls->is_action('delete_selected')) {
45
- $r = Newsletter::instance()->delete_user($_POST['ids']);
46
- $controls->messages .= $r . ' user(s) deleted';
47
- }
48
-
49
- // We build the query condition
50
- $where = 'where 1=1';
51
- $query_args = array();
52
- $text = trim($controls->get_value('search_text'));
53
- if ($text) {
54
- $query_args[] = '%' . $text . '%';
55
- $query_args[] = '%' . $text . '%';
56
- $query_args[] = '%' . $text . '%';
57
- $where .= " and (email like %s or name like %s or surname like %s)";
58
- }
59
-
60
- if (!empty($controls->data['search_status'])) {
61
- if ($controls->data['search_status'] == 'T') {
62
- $where .= " and test=1";
63
- } else {
64
- $query_args[] = $controls->data['search_status'];
65
- $where .= " and status=%s";
66
- }
67
- }
68
-
69
- if (!empty($controls->data['search_list'])) {
70
- $where .= " and list_" . ((int) $controls->data['search_list']) . "=1";
71
- }
72
-
73
- $filtered = $where != 'where 1=1';
74
-
75
- // Total items, total pages
76
- $items_per_page = 20;
77
- if (!empty($query_args)) {
78
- $where = $wpdb->prepare($where, $query_args);
79
- }
80
- $count = Newsletter::instance()->store->get_count(NEWSLETTER_USERS_TABLE, $where);
81
- $last_page = floor($count / $items_per_page) - ($count % $items_per_page == 0 ? 1 : 0);
82
- if ($last_page < 0)
83
- $last_page = 0;
84
-
85
- if ($controls->is_action('last')) {
86
- $controls->data['search_page'] = $last_page;
87
- }
88
- if ($controls->is_action('first')) {
89
- $controls->data['search_page'] = 0;
90
- }
91
- if ($controls->is_action('next')) {
92
- $controls->data['search_page'] = (int) $controls->data['search_page'] + 1;
93
- }
94
- if ($controls->is_action('prev')) {
95
- $controls->data['search_page'] = (int) $controls->data['search_page'] - 1;
96
- }
97
- if ($controls->is_action('search')) {
98
- $controls->data['search_page'] = 0;
99
- }
100
-
101
- // Eventually fix the page
102
- if (!isset($controls->data['search_page']) || $controls->data['search_page'] < 0)
103
- $controls->data['search_page'] = 0;
104
- if ($controls->data['search_page'] > $last_page)
105
- $controls->data['search_page'] = $last_page;
106
-
107
- $query = "select * from " . NEWSLETTER_USERS_TABLE . ' ' . $where . " order by id desc";
108
- $query .= " limit " . ($controls->data['search_page'] * $items_per_page) . "," . $items_per_page;
109
- $list = $wpdb->get_results($query);
110
-
111
- // Move to base 1
112
- $controls->data['search_page'] ++;
113
- ?>
114
-
115
- <div class="wrap tnp-users tnp-users-index" id="tnp-wrap">
116
-
117
- <?php include NEWSLETTER_DIR . '/tnp-header.php'; ?>
118
-
119
- <div id="tnp-heading">
120
-
121
- <h2><?php _e('Subscribers', 'newsletter') ?>
122
- <a class="tnp-btn-h1" href="?page=newsletter_users_new"><?php _e('Add a subscriber', 'newsletter') ?></a>
123
- </h2>
124
-
125
- <p>
126
- See the <a href="admin.php?page=newsletter_users_massive">maintenance panel</a> to move subscribers between list, massively delete and so on.
127
- </p>
128
-
129
- </div>
130
-
131
- <div id="tnp-body">
132
-
133
- <form id="channel" method="post" action="">
134
- <?php $controls->init(); ?>
135
-
136
- <div class="tnp-subscribers-search">
137
- <?php $controls->text('search_text', 45, __('Search text', 'newsletter')); ?>
138
-
139
- <?php _e('filter by', 'newsletter') ?>:
140
- <?php $controls->select('search_status', ['' => 'Any status', 'T' => 'Test subscribers', 'C' => 'Confirmed', 'S' => 'Not confirmed', 'U' => 'Unsubscribed', 'B' => 'Bounced', 'P'=> TNP_User::get_status_label('P')]); ?>
141
- <?php $controls->lists_select('search_list', '-'); ?>
142
-
143
- <?php $controls->button('search', __('Search', 'newsletter')); ?>
144
- <?php if ($where != "where 1=1") { ?>
145
- <?php $controls->button('reset', __('Reset Filters', 'newsletter')); ?>
146
- <?php } ?>
147
- <br>
148
- <?php $controls->checkbox('show_preferences', __('Show lists', 'newsletter')); ?>
149
- </div>
150
-
151
- <?php if ($filtered) { ?>
152
- <p><?php _e('The list below is filtered.', 'newsletter') ?></p>
153
- <?php } ?>
154
-
155
- <div class="tnp-paginator">
156
-
157
- <?php $controls->button('first', '«'); ?>
158
- <?php $controls->button('prev', '‹'); ?>
159
- <?php $controls->text('search_page', 3); ?> of <?php echo $last_page + 1 ?> <?php $controls->button('go', __('Go', 'newsletter')); ?>
160
- <?php $controls->button('next', '›'); ?>
161
- <?php $controls->button('last', '»'); ?>
162
-
163
- <?php echo $count ?> <?php _e('subscriber(s) found', 'newsletter') ?>
164
-
165
- <?php $controls->button_confirm('delete_selected', __('Delete selected', 'newsletter')); ?>
166
-
167
- </div>
168
-
169
- <table class="widefat">
170
- <thead>
171
- <tr>
172
- <td class="check-column"><input type="checkbox" onchange="jQuery('input.tnp-selector').prop('checked', this.checked)"></th>
173
- <th>Id</th>
174
- <th>Email</th>
175
- <th><?php _e('Name', 'newsletter') ?></th>
176
- <th><?php _e('Status', 'newsletter') ?></th>
177
- <?php if (isset($options['show_preferences']) && $options['show_preferences'] == 1) { ?>
178
- <th><?php _e('Lists', 'newsletter') ?></th>
179
- <?php } ?>
180
- <th>&nbsp;</th>
181
-
182
- <th>&nbsp;</th>
183
- </tr>
184
- </thead>
185
- <?php $i = 0; ?>
186
- <?php foreach ($list as $s) { ?>
187
- <tr>
188
- <th scope="row" class="check-column" style="vertical-align: middle"><input class="tnp-selector" type="checkbox" name="ids[]" value="<?php echo $s->id; ?>"/></td>
189
- <td>
190
- <?php echo $s->id; ?>
191
- </td>
192
-
193
- <td>
194
- <?php echo esc_html($s->email); ?>
195
- </td>
196
-
197
- <td>
198
- <?php echo esc_html($s->name); ?> <?php echo esc_html($s->surname); ?>
199
- </td>
200
-
201
- <td>
202
- <small>
203
- <?php echo $this->get_user_status_label($s, true) ?>
204
- </small>
205
- </td>
206
-
207
- <?php if (isset($options['show_preferences']) && $options['show_preferences'] == 1) { ?>
208
- <td>
209
- <small>
210
- <?php
211
- $lists = $this->get_lists();
212
- foreach ($lists as $item) {
213
- $l = 'list_' . $item->id;
214
- if ($s->$l == 1)
215
- echo esc_html($item->name) . '<br>';
216
- }
217
- ?>
218
- </small>
219
- </td>
220
- <?php } ?>
221
-
222
- <td>
223
- <?php $controls->button_icon_edit($this->get_admin_page_url('edit') . '&amp;id=' . $s->id)?>
224
- </td>
225
-
226
- <td style="white-space: nowrap">
227
- <?php $controls->button_icon_delete($s->id); ?>
228
-
229
- <?php if ($s->status == "C") { ?>
230
- <?php $controls->button_icon('resend_welcome', 'fa-redo', __('Resend welcome', 'newsletter'), $s->id, true); ?>
231
- <?php } else { ?>
232
- <?php $controls->button_icon('resend', 'fa-redo', __('Resend activation', 'newsletter'), $s->id, true); ?>
233
- <?php } ?>
234
- </td>
235
-
236
- </tr>
237
- <?php } ?>
238
- </table>
239
- <div class="tnp-paginator">
240
-
241
- <?php $controls->button('first', '«'); ?>
242
- <?php $controls->button('prev', '‹'); ?>
243
- <?php $controls->button('next', '›'); ?>
244
- <?php $controls->button('last', '»'); ?>
245
- </div>
246
- </form>
247
- </div>
248
-
249
- <?php include NEWSLETTER_DIR . '/tnp-footer.php'; ?>
250
-
251
- </div>
1
+ <?php
2
+ /* @var $this NewsletterUsers */
3
+ defined('ABSPATH') || exit;
4
+
5
+ require_once NEWSLETTER_INCLUDES_DIR . '/controls.php';
6
+
7
+ $controls = new NewsletterControls();
8
+
9
+ $options = $controls->data;
10
+ $options_profile = get_option('newsletter_profile');
11
+ $options_main = get_option('newsletter_main');
12
+
13
+ // Move to base zero
14
+ if ($controls->is_action()) {
15
+ if ($controls->is_action('reset')) {
16
+ $controls->data = array();
17
+ } else {
18
+ $controls->data['search_page'] = (int) $controls->data['search_page'] - 1;
19
+ }
20
+ $this->save_options($controls->data, 'search');
21
+ } else {
22
+ $controls->data = $this->get_options('search');
23
+ if (empty($controls->data['search_page']))
24
+ $controls->data['search_page'] = 0;
25
+ }
26
+
27
+ if ($controls->is_action('resend')) {
28
+ $user = $this->get_user($controls->button_data);
29
+ NewsletterSubscription::instance()->send_message('confirmation', $user, true);
30
+ $controls->messages = __('Activation email sent.', 'newsletter');
31
+ }
32
+
33
+ if ($controls->is_action('resend_welcome')) {
34
+ $user = $this->get_user($controls->button_data);
35
+ NewsletterSubscription::instance()->send_message('confirmed', $user, true);
36
+ $controls->messages = __('Welcome email sent.', 'newsletter');
37
+ }
38
+
39
+ if ($controls->is_action('delete')) {
40
+ $this->delete_user($controls->button_data);
41
+ unset($controls->data['subscriber_id']);
42
+ }
43
+
44
+ if ($controls->is_action('delete_selected')) {
45
+ $r = Newsletter::instance()->delete_user($_POST['ids']);
46
+ $controls->messages .= $r . ' user(s) deleted';
47
+ }
48
+
49
+ // We build the query condition
50
+ $where = 'where 1=1';
51
+ $query_args = array();
52
+ $text = trim($controls->get_value('search_text'));
53
+ if ($text) {
54
+ $query_args[] = '%' . $text . '%';
55
+ $query_args[] = '%' . $text . '%';
56
+ $query_args[] = '%' . $text . '%';
57
+ $where .= " and (email like %s or name like %s or surname like %s)";
58
+ }
59
+
60
+ if (!empty($controls->data['search_status'])) {
61
+ if ($controls->data['search_status'] == 'T') {
62
+ $where .= " and test=1";
63
+ } else {
64
+ $query_args[] = $controls->data['search_status'];
65
+ $where .= " and status=%s";
66
+ }
67
+ }
68
+
69
+ if (!empty($controls->data['search_list'])) {
70
+ $where .= " and list_" . ((int) $controls->data['search_list']) . "=1";
71
+ }
72
+
73
+ $filtered = $where != 'where 1=1';
74
+
75
+ // Total items, total pages
76
+ $items_per_page = 20;
77
+ if (!empty($query_args)) {
78
+ $where = $wpdb->prepare($where, $query_args);
79
+ }
80
+ $count = Newsletter::instance()->store->get_count(NEWSLETTER_USERS_TABLE, $where);
81
+ $last_page = floor($count / $items_per_page) - ($count % $items_per_page == 0 ? 1 : 0);
82
+ if ($last_page < 0)
83
+ $last_page = 0;
84
+
85
+ if ($controls->is_action('last')) {
86
+ $controls->data['search_page'] = $last_page;
87
+ }
88
+ if ($controls->is_action('first')) {
89
+ $controls->data['search_page'] = 0;
90
+ }
91
+ if ($controls->is_action('next')) {
92
+ $controls->data['search_page'] = (int) $controls->data['search_page'] + 1;
93
+ }
94
+ if ($controls->is_action('prev')) {
95
+ $controls->data['search_page'] = (int) $controls->data['search_page'] - 1;
96
+ }
97
+ if ($controls->is_action('search')) {
98
+ $controls->data['search_page'] = 0;
99
+ }
100
+
101
+ // Eventually fix the page
102
+ if (!isset($controls->data['search_page']) || $controls->data['search_page'] < 0)
103
+ $controls->data['search_page'] = 0;
104
+ if ($controls->data['search_page'] > $last_page)
105
+ $controls->data['search_page'] = $last_page;
106
+
107
+ $query = "select * from " . NEWSLETTER_USERS_TABLE . ' ' . $where . " order by id desc";
108
+ $query .= " limit " . ($controls->data['search_page'] * $items_per_page) . "," . $items_per_page;
109
+ $list = $wpdb->get_results($query);
110
+
111
+ // Move to base 1
112
+ $controls->data['search_page'] ++;
113
+ ?>
114
+
115
+ <div class="wrap tnp-users tnp-users-index" id="tnp-wrap">
116
+
117
+ <?php include NEWSLETTER_DIR . '/tnp-header.php'; ?>
118
+
119
+ <div id="tnp-heading">
120
+
121
+ <h2><?php _e('Subscribers', 'newsletter') ?>
122
+ <a class="tnp-btn-h1" href="?page=newsletter_users_new"><?php _e('Add a subscriber', 'newsletter') ?></a>
123
+ </h2>
124
+
125
+ <p>
126
+ See the <a href="admin.php?page=newsletter_users_massive">maintenance panel</a> to move subscribers between list, massively delete and so on.
127
+ </p>
128
+
129
+ </div>
130
+
131
+ <div id="tnp-body">
132
+
133
+ <form id="channel" method="post" action="">
134
+ <?php $controls->init(); ?>
135
+
136
+ <div class="tnp-subscribers-search">
137
+ <?php $controls->text('search_text', 45, __('Search text', 'newsletter')); ?>
138
+
139
+ <?php _e('filter by', 'newsletter') ?>:
140
+ <?php $controls->select('search_status', ['' => 'Any status', 'T' => 'Test subscribers', 'C' => 'Confirmed', 'S' => 'Not confirmed', 'U' => 'Unsubscribed', 'B' => 'Bounced', 'P'=> TNP_User::get_status_label('P')]); ?>
141
+ <?php $controls->lists_select('search_list', '-'); ?>
142
+
143
+ <?php $controls->button('search', __('Search', 'newsletter')); ?>
144
+ <?php if ($where != "where 1=1") { ?>
145
+ <?php $controls->button('reset', __('Reset Filters', 'newsletter')); ?>
146
+ <?php } ?>
147
+ <br>
148
+ <?php $controls->checkbox('show_preferences', __('Show lists', 'newsletter')); ?>
149
+ </div>
150
+
151
+ <?php if ($filtered) { ?>
152
+ <p><?php _e('The list below is filtered.', 'newsletter') ?></p>
153
+ <?php } ?>
154
+
155
+ <div class="tnp-paginator">
156
+
157
+ <?php $controls->button('first', '«'); ?>
158
+ <?php $controls->button('prev', '‹'); ?>
159
+ <?php $controls->text('search_page', 3); ?> of <?php echo $last_page + 1 ?> <?php $controls->button('go', __('Go', 'newsletter')); ?>
160
+ <?php $controls->button('next', '›'); ?>
161
+ <?php $controls->button('last', '»'); ?>
162
+
163
+ <?php echo $count ?> <?php _e('subscriber(s) found', 'newsletter') ?>
164
+
165
+ <?php $controls->button_confirm('delete_selected', __('Delete selected', 'newsletter')); ?>
166
+
167
+ </div>
168
+
169
+ <table class="widefat">
170
+ <thead>
171
+ <tr>
172
+ <td class="check-column"><input type="checkbox" onchange="jQuery('input.tnp-selector').prop('checked', this.checked)"></th>
173
+ <th>Id</th>
174
+ <th>Email</th>
175
+ <th><?php _e('Name', 'newsletter') ?></th>
176
+ <th><?php _e('Status', 'newsletter') ?></th>
177
+ <?php if (isset($options['show_preferences']) && $options['show_preferences'] == 1) { ?>
178
+ <th><?php _e('Lists', 'newsletter') ?></th>
179
+ <?php } ?>
180
+ <th>&nbsp;</th>
181
+
182
+ <th>&nbsp;</th>
183
+ </tr>
184
+ </thead>
185
+ <?php $i = 0; ?>
186
+ <?php foreach ($list as $s) { ?>
187
+ <tr>
188
+ <th scope="row" class="check-column" style="vertical-align: middle"><input class="tnp-selector" type="checkbox" name="ids[]" value="<?php echo $s->id; ?>"/></td>
189
+ <td>
190
+ <?php echo $s->id; ?>
191
+ </td>
192
+
193
+ <td>
194
+ <?php echo esc_html($s->email); ?>
195
+ </td>
196
+
197
+ <td>
198
+ <?php echo esc_html($s->name); ?> <?php echo esc_html($s->surname); ?>
199
+ </td>
200
+
201
+ <td>
202
+ <small>
203
+ <?php echo $this->get_user_status_label($s, true) ?>
204
+ </small>
205
+ </td>
206
+
207
+ <?php if (isset($options['show_preferences']) && $options['show_preferences'] == 1) { ?>
208
+ <td>
209
+ <small>
210
+ <?php
211
+ $lists = $this->get_lists();
212
+ foreach ($lists as $item) {
213
+ $l = 'list_' . $item->id;
214
+ if ($s->$l == 1)
215
+ echo esc_html($item->name) . '<br>';
216
+ }
217
+ ?>
218
+ </small>
219
+ </td>
220
+ <?php } ?>
221
+
222
+ <td>
223
+ <?php $controls->button_icon_edit($this->get_admin_page_url('edit') . '&amp;id=' . $s->id)?>
224
+ </td>
225
+
226
+ <td style="white-space: nowrap">
227
+ <?php $controls->button_icon_delete($s->id); ?>
228
+
229
+ <?php if ($s->status == "C") { ?>
230
+ <?php $controls->button_icon('resend_welcome', 'fa-redo', __('Resend welcome', 'newsletter'), $s->id, true); ?>
231
+ <?php } else { ?>
232
+ <?php $controls->button_icon('resend', 'fa-redo', __('Resend activation', 'newsletter'), $s->id, true); ?>
233
+ <?php } ?>
234
+ </td>
235
+
236
+ </tr>
237
+ <?php } ?>
238
+ </table>
239
+ <div class="tnp-paginator">
240
+
241
+ <?php $controls->button('first', '«'); ?>
242
+ <?php $controls->button('prev', '‹'); ?>
243
+ <?php $controls->button('next', '›'); ?>
244
+ <?php $controls->button('last', '»'); ?>
245
+ </div>
246
+ </form>
247
+ </div>
248
+
249
+ <?php include NEWSLETTER_DIR . '/tnp-footer.php'; ?>
250
+
251
+ </div>
users/users.php CHANGED
@@ -1,256 +1,252 @@
1
- <?php
2
-
3
- defined( 'ABSPATH' ) || exit;
4
-
5
- class NewsletterUsers extends NewsletterModule {
6
-
7
- static $instance;
8
-
9
- /**
10
- * @return NewsletterUsers
11
- */
12
- static function instance() {
13
- if ( self::$instance == null ) {
14
- self::$instance = new NewsletterUsers();
15
- }
16
-
17
- return self::$instance;
18
- }
19
-
20
- function __construct() {
21
- parent::__construct( 'users', '1.3.0' );
22
- if ( is_admin() ) {
23
- add_action( 'wp_ajax_newsletter_users_export', array( $this, 'hook_wp_ajax_newsletter_users_export' ) );
24
- }
25
- }
26
-
27
- function hook_wp_ajax_newsletter_users_export() {
28
-
29
- $newsletter = Newsletter::instance();
30
- if ( $newsletter->is_allowed() ) {
31
- require_once NEWSLETTER_INCLUDES_DIR . '/controls.php';
32
- $controls = new NewsletterControls();
33
-
34
- if ( $controls->is_action( 'export' ) ) {
35
- $this->export( $controls->data );
36
- }
37
- } else {
38
- die( 'Not allowed.' );
39
- }
40
- }
41
-
42
- function upgrade() {
43
- global $wpdb, $charset_collate;
44
-
45
- parent::upgrade();
46
-
47
- $sql = "CREATE TABLE `" . $wpdb->prefix . "newsletter` (
48
- `name` varchar(100) NOT NULL DEFAULT '',
49
- `email` varchar(100) NOT NULL DEFAULT '',
50
- `token` varchar(50) NOT NULL DEFAULT '',
51
- `language` varchar(10) NOT NULL DEFAULT '',
52
- `status` varchar(1) NOT NULL DEFAULT 'S',
53
- `id` int(11) NOT NULL AUTO_INCREMENT,
54
- `profile` mediumtext,
55
- `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
56
- `updated` int(11) NOT NULL DEFAULT '0',
57
- `last_activity` int(11) NOT NULL DEFAULT '0',
58
- `followup_step` tinyint(4) NOT NULL DEFAULT '0',
59
- `followup_time` bigint(20) NOT NULL DEFAULT '0',
60
- `followup` tinyint(4) NOT NULL DEFAULT '0',
61
- `surname` varchar(100) NOT NULL DEFAULT '',
62
- `sex` char(1) NOT NULL DEFAULT 'n',
63
- `feed_time` bigint(20) NOT NULL DEFAULT '0',
64
- `feed` tinyint(4) NOT NULL DEFAULT '0',
65
- `referrer` varchar(50) NOT NULL DEFAULT '',
66
- `ip` varchar(50) NOT NULL DEFAULT '',
67
- `wp_user_id` int(11) NOT NULL DEFAULT '0',
68
- `http_referer` varchar(255) NOT NULL DEFAULT '',
69
- `geo` tinyint(4) NOT NULL DEFAULT '0',
70
- `country` varchar(4) NOT NULL DEFAULT '',
71
- `region` varchar(100) NOT NULL DEFAULT '',
72
- `city` varchar(100) NOT NULL DEFAULT '',
73
- `bounce_type` varchar(50) NOT NULL DEFAULT '',
74
- `bounce_time` int(11) NOT NULL DEFAULT '0',
75
- `unsub_email_id` int(11) NOT NULL DEFAULT '0',
76
- `unsub_time` int(11) NOT NULL DEFAULT '0',\n";
77
-
78
- for ( $i = 1; $i <= NEWSLETTER_LIST_MAX; $i ++ ) {
79
- $sql .= "`list_$i` tinyint(4) NOT NULL DEFAULT '0',\n";
80
- }
81
-
82
- for ( $i = 1; $i <= NEWSLETTER_PROFILE_MAX; $i ++ ) {
83
- $sql .= "`profile_$i` varchar(255) NOT NULL DEFAULT '',\n";
84
- }
85
- // Leave as last
86
- $sql .= "`test` tinyint(4) NOT NULL DEFAULT '0',\n";
87
- $sql .= "PRIMARY KEY (`id`),\nUNIQUE KEY `email` (`email`),\nKEY `wp_user_id` (`wp_user_id`)\n) $charset_collate;";
88
-
89
- dbDelta( $sql );
90
-
91
- if ( $this->old_version < '1.2.7' ) {
92
- $this->query( "update " . NEWSLETTER_USERS_TABLE . " set geo=1 where country<>''" );
93
-
94
- }
95
- if ( $this->old_version > '1.2.5' && $this->old_version < '1.2.9' ) {
96
- $this->upgrade_query( "ALTER TABLE " . NEWSLETTER_USERS_TABLE . " DROP COLUMN last_ip;" );
97
- }
98
-
99
- }
100
-
101
- function admin_menu() {
102
- $this->add_menu_page( 'index', 'Subscribers' );
103
- $this->add_admin_page( 'new', 'New subscriber' );
104
- $this->add_admin_page( 'edit', 'Subscribers Edit' );
105
- $this->add_admin_page( 'massive', 'Massive Management' );
106
- $this->add_admin_page( 'export', 'Export' );
107
- $this->add_admin_page( 'import', 'Import' );
108
- $this->add_admin_page( 'statistics', 'Statistics' );
109
- }
110
-
111
- function export( $options = null ) {
112
- global $wpdb;
113
-
114
- header( 'Content-Type: application/octet-stream' );
115
- header( 'Content-Disposition: attachment; filename="newsletter-subscribers.csv"' );
116
-
117
- // BOM
118
- echo "\xEF\xBB\xBF";
119
-
120
- $sep = ';';
121
- if ( $options ) {
122
- $sep = $options['separator'];
123
- }
124
- if ( $sep == 'tab' ) {
125
- $sep = "\t";
126
- }
127
-
128
- // CSV header
129
- echo '"Email"' . $sep . '"Name"' . $sep . '"Surname"' . $sep . '"Gender"' . $sep . '"Status"' . $sep . '"Date"' . $sep . '"Token"' . $sep;
130
-
131
- // In table profiles
132
- for ( $i = 1; $i <= NEWSLETTER_PROFILE_MAX; $i ++ ) {
133
- echo '"Profile ' . $i . '"' . $sep; // To adjust with field name
134
- }
135
-
136
- // Lists
137
- for ( $i = 1; $i <= NEWSLETTER_LIST_MAX; $i ++ ) {
138
- echo '"List ' . $i . '"' . $sep;
139
- }
140
-
141
- echo '"Feed by mail"' . $sep . '"Follow up"' . $sep;
142
- echo '"IP"' . $sep . '"Referrer"' . $sep . '"Country"';
143
-
144
- echo "\n";
145
-
146
- $page = 0;
147
- while ( true ) {
148
- $query = "select * from " . NEWSLETTER_USERS_TABLE . "";
149
- $list = (int) $_POST['options']['list'];
150
- if ( ! empty( $list ) ) {
151
- $query .= " where list_" . $list . "=1";
152
- }
153
- $recipients = $wpdb->get_results( $query . " order by email limit " . $page * 500 . ",500" );
154
- for ( $i = 0; $i < count( $recipients ); $i ++ ) {
155
- echo '"' . $recipients[ $i ]->email . '"' . $sep . '"' . $this->sanitize_csv( $recipients[ $i ]->name ) .
156
- '"' . $sep . '"' . $this->sanitize_csv( $recipients[ $i ]->surname ) .
157
- '"' . $sep . '"' . $recipients[ $i ]->sex .
158
- '"' . $sep . '"' . $recipients[ $i ]->status . '"' . $sep . '"' . $recipients[ $i ]->created . '"' . $sep . '"' . $recipients[ $i ]->token . '"' . $sep;
159
-
160
- for ( $j = 1; $j <= NEWSLETTER_PROFILE_MAX; $j ++ ) {
161
- $column = 'profile_' . $j;
162
- echo '"' . $this->sanitize_csv( $recipients[ $i ]->$column ) . '"' . $sep;
163
- }
164
-
165
- for ( $j = 1; $j <= NEWSLETTER_LIST_MAX; $j ++ ) {
166
- $list = 'list_' . $j;
167
- echo '"' . $recipients[ $i ]->$list . '"' . $sep;
168
- }
169
-
170
- echo '"' . $recipients[ $i ]->feed . '"' . $sep;
171
- echo '"' . $recipients[ $i ]->followup . '"' . $sep;
172
- echo '"' . $recipients[ $i ]->ip . '"' . $sep;
173
- echo '"' . $recipients[ $i ]->referrer . '"' . $sep;
174
- echo '"' . $recipients[ $i ]->country . '"' . $sep;
175
-
176
- echo "\n";
177
- flush();
178
- }
179
- if ( count( $recipients ) < 500 ) {
180
- break;
181
- }
182
- $page ++;
183
- }
184
- die();
185
- }
186
-
187
- function sanitize_csv( $text ) {
188
- $text = str_replace( '"', "'", $text );
189
- $text = str_replace( "\n", ' ', $text );
190
- $text = str_replace( "\r", ' ', $text );
191
- $text = str_replace( ";", ' ', $text );
192
-
193
- // Do you wonder? Excel...
194
- $first = substr( $text, 0, 1 );
195
- if ( $first == '=' || $first == '+' || $first == '-' || $first == '@' ) {
196
- $text = "'" . $text;
197
- }
198
-
199
- return $text;
200
- }
201
-
202
- /**
203
- * @param array $args
204
- * @param string $format
205
- *
206
- * @return array|object|null
207
- */
208
- function get_users( $args, $format = OBJECT ) {
209
- global $wpdb;
210
-
211
- $default_args = array(
212
- 'page' => 1,
213
- 'per_page' => 10
214
- );
215
-
216
- $args = array_merge( $default_args, $args );
217
-
218
- $query = 'SELECT * FROM ' . NEWSLETTER_USERS_TABLE . ' ';
219
- $query_args = [];
220
-
221
- $query .= ' LIMIT %d OFFSET %d';
222
- $query_args[] = (int) $args['per_page'];
223
- $query_args[] = ( (int) $args['page'] - 1 ) * (int) $args['per_page'];
224
-
225
-
226
- $records = $wpdb->get_results( $wpdb->prepare( $query, $query_args ), $format );
227
-
228
- if ( $wpdb->last_error ) {
229
- $this->logger->error( $wpdb->last_error );
230
-
231
- return null;
232
- }
233
-
234
- return $records;
235
-
236
- }
237
-
238
- /**
239
- * Check if email exists
240
- *
241
- * @param string $email
242
- *
243
- * @return bool
244
- */
245
- function email_exists( $email ) {
246
-
247
- $email = parent::normalize_email( $email );
248
- $user = parent::get_user( $email );
249
-
250
- return $user ? true : false;
251
-
252
- }
253
-
254
- }
255
-
256
- NewsletterUsers::instance();
1
+ <?php
2
+
3
+ defined('ABSPATH') || exit;
4
+
5
+ class NewsletterUsers extends NewsletterModule {
6
+
7
+ static $instance;
8
+
9
+ /**
10
+ * @return NewsletterUsers
11
+ */
12
+ static function instance() {
13
+ if (self::$instance == null) {
14
+ self::$instance = new NewsletterUsers();
15
+ }
16
+
17
+ return self::$instance;
18
+ }
19
+
20
+ function __construct() {
21
+ parent::__construct('users', '1.3.0');
22
+ if (is_admin()) {
23
+ add_action('wp_ajax_newsletter_users_export', array($this, 'hook_wp_ajax_newsletter_users_export'));
24
+ }
25
+ }
26
+
27
+ function hook_wp_ajax_newsletter_users_export() {
28
+
29
+ $newsletter = Newsletter::instance();
30
+ if ($newsletter->is_allowed()) {
31
+ require_once NEWSLETTER_INCLUDES_DIR . '/controls.php';
32
+ $controls = new NewsletterControls();
33
+
34
+ if ($controls->is_action('export')) {
35
+ $this->export($controls->data);
36
+ }
37
+ } else {
38
+ die('Not allowed.');
39
+ }
40
+ }
41
+
42
+ function upgrade() {
43
+ global $wpdb, $charset_collate;
44
+
45
+ parent::upgrade();
46
+
47
+ $sql = "CREATE TABLE `" . $wpdb->prefix . "newsletter` (
48
+ `name` varchar(100) NOT NULL DEFAULT '',
49
+ `email` varchar(100) NOT NULL DEFAULT '',
50
+ `token` varchar(50) NOT NULL DEFAULT '',
51
+ `language` varchar(10) NOT NULL DEFAULT '',
52
+ `status` varchar(1) NOT NULL DEFAULT 'S',
53
+ `id` int(11) NOT NULL AUTO_INCREMENT,
54
+ `profile` mediumtext,
55
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
56
+ `updated` int(11) NOT NULL DEFAULT '0',
57
+ `last_activity` int(11) NOT NULL DEFAULT '0',
58
+ `followup_step` tinyint(4) NOT NULL DEFAULT '0',
59
+ `followup_time` bigint(20) NOT NULL DEFAULT '0',
60
+ `followup` tinyint(4) NOT NULL DEFAULT '0',
61
+ `surname` varchar(100) NOT NULL DEFAULT '',
62
+ `sex` char(1) NOT NULL DEFAULT 'n',
63
+ `feed_time` bigint(20) NOT NULL DEFAULT '0',
64
+ `feed` tinyint(4) NOT NULL DEFAULT '0',
65
+ `referrer` varchar(50) NOT NULL DEFAULT '',
66
+ `ip` varchar(50) NOT NULL DEFAULT '',
67
+ `wp_user_id` int(11) NOT NULL DEFAULT '0',
68
+ `http_referer` varchar(255) NOT NULL DEFAULT '',
69
+ `geo` tinyint(4) NOT NULL DEFAULT '0',
70
+ `country` varchar(4) NOT NULL DEFAULT '',
71
+ `region` varchar(100) NOT NULL DEFAULT '',
72
+ `city` varchar(100) NOT NULL DEFAULT '',
73
+ `bounce_type` varchar(50) NOT NULL DEFAULT '',
74
+ `bounce_time` int(11) NOT NULL DEFAULT '0',
75
+ `unsub_email_id` int(11) NOT NULL DEFAULT '0',
76
+ `unsub_time` int(11) NOT NULL DEFAULT '0',\n";
77
+
78
+ for ($i = 1; $i <= NEWSLETTER_LIST_MAX; $i++) {
79
+ $sql .= "`list_$i` tinyint(4) NOT NULL DEFAULT '0',\n";
80
+ }
81
+
82
+ for ($i = 1; $i <= NEWSLETTER_PROFILE_MAX; $i++) {
83
+ $sql .= "`profile_$i` varchar(255) NOT NULL DEFAULT '',\n";
84
+ }
85
+ // Leave as last
86
+ $sql .= "`test` tinyint(4) NOT NULL DEFAULT '0',\n";
87
+ $sql .= "PRIMARY KEY (`id`),\nUNIQUE KEY `email` (`email`),\nKEY `wp_user_id` (`wp_user_id`)\n) $charset_collate;";
88
+
89
+ dbDelta($sql);
90
+
91
+ if ($this->old_version < '1.2.7') {
92
+ $this->query("update " . NEWSLETTER_USERS_TABLE . " set geo=1 where country<>''");
93
+ }
94
+ if ($this->old_version > '1.2.5' && $this->old_version < '1.2.9') {
95
+ $this->upgrade_query("ALTER TABLE " . NEWSLETTER_USERS_TABLE . " DROP COLUMN last_ip;");
96
+ }
97
+ }
98
+
99
+ function admin_menu() {
100
+ $this->add_menu_page('index', 'Subscribers');
101
+ $this->add_admin_page('new', 'New subscriber');
102
+ $this->add_admin_page('edit', 'Subscribers Edit');
103
+ $this->add_admin_page('massive', 'Massive Management');
104
+ $this->add_admin_page('export', 'Export');
105
+ $this->add_admin_page('import', 'Import');
106
+ $this->add_admin_page('statistics', 'Statistics');
107
+ }
108
+
109
+ function export($options = null) {
110
+ global $wpdb;
111
+
112
+ @setlocale(LC_CTYPE, 'en_US.UTF-8');
113
+ header('Content-Type: application/octet-stream;charset=UTF-8');
114
+ header('Content-Disposition: attachment; filename="newsletter-subscribers.csv"');
115
+
116
+ // BOM
117
+ echo "\xEF\xBB\xBF";
118
+
119
+ $sep = ';';
120
+ if ($options) {
121
+ $sep = $options['separator'];
122
+ }
123
+ if ($sep == 'tab') {
124
+ $sep = "\t";
125
+ }
126
+
127
+ // CSV header
128
+ echo '"Email"' . $sep . '"Name"' . $sep . '"Surname"' . $sep . '"Gender"' . $sep . '"Status"' . $sep . '"Date"' . $sep . '"Token"' . $sep;
129
+
130
+ // In table profiles
131
+ for ($i = 1; $i <= NEWSLETTER_PROFILE_MAX; $i++) {
132
+ echo '"Profile ' . $i . '"' . $sep; // To adjust with field name
133
+ }
134
+
135
+ // Lists
136
+ for ($i = 1; $i <= NEWSLETTER_LIST_MAX; $i++) {
137
+ echo '"List ' . $i . '"' . $sep;
138
+ }
139
+
140
+ echo '"Feed by mail"' . $sep . '"Follow up"' . $sep;
141
+ echo '"IP"' . $sep . '"Referrer"' . $sep . '"Country"';
142
+
143
+ echo "\n";
144
+
145
+ $page = 0;
146
+ while (true) {
147
+ $query = "select * from " . NEWSLETTER_USERS_TABLE . "";
148
+ $list = (int) $_POST['options']['list'];
149
+ if (!empty($list)) {
150
+ $query .= " where list_" . $list . "=1";
151
+ }
152
+ $recipients = $wpdb->get_results($query . " order by email limit " . $page * 500 . ",500");
153
+ for ($i = 0; $i < count($recipients); $i++) {
154
+ echo '"' . $recipients[$i]->email . '"' . $sep . '"' . $this->sanitize_csv($recipients[$i]->name) .
155
+ '"' . $sep . '"' . $this->sanitize_csv($recipients[$i]->surname) .
156
+ '"' . $sep . '"' . $recipients[$i]->sex .
157
+ '"' . $sep . '"' . $recipients[$i]->status . '"' . $sep . '"' . $recipients[$i]->created . '"' . $sep . '"' . $recipients[$i]->token . '"' . $sep;
158
+
159
+ for ($j = 1; $j <= NEWSLETTER_PROFILE_MAX; $j++) {
160
+ $column = 'profile_' . $j;
161
+ echo '"' . $this->sanitize_csv($recipients[$i]->$column) . '"' . $sep;
162
+ }
163
+
164
+ for ($j = 1; $j <= NEWSLETTER_LIST_MAX; $j++) {
165
+ $list = 'list_' . $j;
166
+ echo '"' . $recipients[$i]->$list . '"' . $sep;
167
+ }
168
+
169
+ echo '"' . $recipients[$i]->feed . '"' . $sep;
170
+ echo '"' . $recipients[$i]->followup . '"' . $sep;
171
+ echo '"' . $recipients[$i]->ip . '"' . $sep;
172
+ echo '"' . $recipients[$i]->referrer . '"' . $sep;
173
+ echo '"' . $recipients[$i]->country . '"' . $sep;
174
+
175
+ echo "\n";
176
+ flush();
177
+ }
178
+ if (count($recipients) < 500) {
179
+ break;
180
+ }
181
+ $page++;
182
+ }
183
+ die();
184
+ }
185
+
186
+ function sanitize_csv($text) {
187
+ $text = str_replace('"', "'", $text);
188
+ $text = str_replace("\n", ' ', $text);
189
+ $text = str_replace("\r", ' ', $text);
190
+ $text = str_replace(";", ' ', $text);
191
+
192
+ // Do you wonder? Excel...
193
+ $first = substr($text, 0, 1);
194
+ if ($first == '=' || $first == '+' || $first == '-' || $first == '@') {
195
+ $text = "'" . $text;
196
+ }
197
+
198
+ return $text;
199
+ }
200
+
201
+ /**
202
+ * @param array $args
203
+ * @param string $format
204
+ *
205
+ * @return array|object|null
206
+ */
207
+ function get_users($args, $format = OBJECT) {
208
+ global $wpdb;
209
+
210
+ $default_args = array(
211
+ 'page' => 1,
212
+ 'per_page' => 10
213
+ );
214
+
215
+ $args = array_merge($default_args, $args);
216
+
217
+ $query = 'SELECT * FROM ' . NEWSLETTER_USERS_TABLE . ' ';
218
+ $query_args = [];
219
+
220
+ $query .= ' LIMIT %d OFFSET %d';
221
+ $query_args[] = (int) $args['per_page'];
222
+ $query_args[] = ( (int) $args['page'] - 1 ) * (int) $args['per_page'];
223
+
224
+ $records = $wpdb->get_results($wpdb->prepare($query, $query_args), $format);
225
+
226
+ if ($wpdb->last_error) {
227
+ $this->logger->error($wpdb->last_error);
228
+
229
+ return null;
230
+ }
231
+
232
+ return $records;
233
+ }
234
+
235
+ /**
236
+ * Check if email exists
237
+ *
238
+ * @param string $email
239
+ *
240
+ * @return bool
241
+ */
242
+ function email_exists($email) {
243
+
244
+ $email = parent::normalize_email($email);
245
+ $user = parent::get_user($email);
246
+
247
+ return $user ? true : false;
248
+ }
249
+
250
+ }
251
+
252
+ NewsletterUsers::instance();