Newsletter - Version 7.2.9

Version Description

  • Fixed generic action button confirmation popup not showing the message
  • [SECURITY] Pre parsing of IP addresses on security panel
  • [SECURITY] Comments allowed on IP address list on security panel
  • [COMPOSER] Fixed preset name/subject
  • Improved generated forms for accessibility
Download this release

Release Info

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

Code changes from version 7.2.8 to 7.2.9

emails/blocks/posts/layout-two.php CHANGED
@@ -5,6 +5,8 @@ $column_width = $total_width / 2 - 20;
5
 
6
  $title_style = TNP_Composer::get_style($options, 'title', $composer, 'title', ['scale' => .8]);
7
  $text_style = TNP_Composer::get_style($options, '', $composer, 'text');
 
 
8
  ?>
9
  <style>
10
  .title {
@@ -48,34 +50,33 @@ $text_style = TNP_Composer::get_style($options, '', $composer, 'text');
48
 
49
  </style>
50
 
51
- <table cellspacing="0" cellpadding="0" border="0" width="100%">
52
 
53
- <?php foreach (array_chunk($posts, 2) AS $row) { ?>
54
  <?php
55
  $media = null;
56
  if ($show_image) {
57
- $media = tnp_composer_block_posts_get_media($row[0], $size, $image_placeholder_url);
58
- $media->link = tnp_post_permalink($row[0]);
59
  $media->set_width($column_width);
60
  }
61
 
62
  $meta = [];
63
 
64
  if ($show_date) {
65
- $meta[] = tnp_post_date($row[0]);
66
  }
67
 
68
  if ($show_author) {
69
- $author_object = get_user_by('id', $row[0]->post_author);
70
  if ($author_object) {
71
  $meta[] = $author_object->display_name;
72
  }
73
  }
74
 
75
- $button_options['button_url'] = tnp_post_permalink($row[0]);
 
76
  ?>
77
- <tr>
78
- <td inline-class="column-left" width="50%" valign="top" class="responsive">
79
 
80
 
81
  <table cellpadding="0" cellspacing="0" border="0" width="100%">
@@ -87,11 +88,11 @@ $text_style = TNP_Composer::get_style($options, '', $composer, 'text');
87
  </tr>
88
  <?php } ?>
89
  <tr>
90
- <td align="center" inline-class="title" class="title tnpc-row-edit tnpc-inline-editable" data-type="title" data-id="<?php echo $row[0]->ID ?>">
91
  <?php
92
- echo TNP_Composer::is_post_field_edited_inline($options['inline_edits'], 'title', $row[0]->ID) ?
93
- TNP_Composer::get_edited_inline_post_field($options['inline_edits'], 'title', $row[0]->ID) :
94
- tnp_post_title($row[0])
95
  ?>
96
  </td>
97
  </tr>
@@ -106,11 +107,11 @@ $text_style = TNP_Composer::get_style($options, '', $composer, 'text');
106
 
107
  <?php if ($excerpt_length) { ?>
108
  <tr>
109
- <td align="center" inline-class="excerpt" class="title tnpc-row-edit tnpc-inline-editable" data-type="text" data-id="<?php echo $row[0]->ID ?>">
110
  <?php
111
- echo TNP_Composer::is_post_field_edited_inline($options['inline_edits'], 'text', $row[0]->ID) ?
112
- TNP_Composer::get_edited_inline_post_field($options['inline_edits'], 'text', $row[0]->ID) :
113
- tnp_post_excerpt($row[0], $excerpt_length)
114
  ?>
115
  </td>
116
  </tr>
@@ -124,85 +125,13 @@ $text_style = TNP_Composer::get_style($options, '', $composer, 'text');
124
  </tr>
125
  <?php } ?>
126
  </table>
127
- </td>
128
-
129
- <td inline-class="column-right" width="50%" valign="top" class="responsive">
130
  <?php
131
- if (isset($row[1])) {
132
-
133
- $media = null;
134
- if ($show_image) {
135
- $media = tnp_composer_block_posts_get_media($row[1], $size, $image_placeholder_url);
136
- $media->link = tnp_post_permalink($row[1]);
137
- $media->set_width($column_width);
138
- }
139
-
140
- $meta = [];
141
-
142
- if ($show_date) {
143
- $meta[] = tnp_post_date($row[1]);
144
- }
145
-
146
- if ($show_author) {
147
- $author_object = get_user_by('id', $row[1]->post_author);
148
- if ($author_object) {
149
- $meta[] = $author_object->display_name;
150
- }
151
- }
152
-
153
- $button_options['button_url'] = tnp_post_permalink($row[1]);
154
- ?>
155
-
156
-
157
- <table cellpadding="0" cellspacing="0" border="0" width="100%">
158
- <?php if ($media) { ?>
159
- <tr>
160
- <td align="center" valign="middle">
161
- <?php echo TNP_Composer::image($media, ['class' => 'fluid']) ?>
162
- </td>
163
- </tr>
164
- <?php } ?>
165
- <tr>
166
- <td align="center" inline-class="title" class="tnpc-row-edit tnpc-inline-editable" data-type="title" data-id="<?php echo $row[1]->ID ?>">
167
- <?php
168
- echo TNP_Composer::is_post_field_edited_inline($options['inline_edits'], 'title', $row[1]->ID) ?
169
- TNP_Composer::get_edited_inline_post_field($options['inline_edits'], 'title', $row[1]->ID) :
170
- tnp_post_title($row[1])
171
- ?>
172
- </td>
173
- </tr>
174
- <?php if ($meta) { ?>
175
- <tr>
176
- <td inline-class="meta">
177
- <?php echo esc_html(implode(' - ', $meta)) ?>
178
- </td>
179
- </tr>
180
- <?php } ?>
181
-
182
- <tr>
183
- <td align="center" inline-class="excerpt" class="tnpc-row-edit tnpc-inline-editable" data-type="text" data-id="<?php echo $row[1]->ID ?>">
184
- <?php
185
- echo TNP_Composer::is_post_field_edited_inline($options['inline_edits'], 'text', $row[1]->ID) ?
186
- TNP_Composer::get_edited_inline_post_field($options['inline_edits'], 'text', $row[1]->ID) :
187
- tnp_post_excerpt($row[1], $excerpt_length)
188
- ?>
189
- </td>
190
- </tr>
191
- <?php if ($show_read_more_button) { ?>
192
- <tr>
193
- <td align="center" inline-class="button">
194
- <?php echo TNP_Composer::button($button_options) ?>
195
- </td>
196
- </tr>
197
- <?php } ?>
198
- </table>
199
- <?php } ?>
200
 
201
 
202
- </td>
203
- </tr>
204
 
205
- <?php } ?>
206
 
207
- </table>
208
 
5
 
6
  $title_style = TNP_Composer::get_style($options, 'title', $composer, 'title', ['scale' => .8]);
7
  $text_style = TNP_Composer::get_style($options, '', $composer, 'text');
8
+
9
+ $items = [];
10
  ?>
11
  <style>
12
  .title {
50
 
51
  </style>
52
 
 
53
 
54
+ <?php foreach ($posts AS $p) { ?>
55
  <?php
56
  $media = null;
57
  if ($show_image) {
58
+ $media = tnp_composer_block_posts_get_media($p, $size, $image_placeholder_url);
59
+ $media->link = tnp_post_permalink($p);
60
  $media->set_width($column_width);
61
  }
62
 
63
  $meta = [];
64
 
65
  if ($show_date) {
66
+ $meta[] = tnp_post_date($p);
67
  }
68
 
69
  if ($show_author) {
70
+ $author_object = get_user_by('id', $p->post_author);
71
  if ($author_object) {
72
  $meta[] = $author_object->display_name;
73
  }
74
  }
75
 
76
+ $button_options['button_url'] = tnp_post_permalink($p);
77
+ ob_start();
78
  ?>
79
+
 
80
 
81
 
82
  <table cellpadding="0" cellspacing="0" border="0" width="100%">
88
  </tr>
89
  <?php } ?>
90
  <tr>
91
+ <td align="center" inline-class="title" class="title tnpc-row-edit tnpc-inline-editable" data-type="title" data-id="<?php echo $p->ID ?>">
92
  <?php
93
+ echo TNP_Composer::is_post_field_edited_inline($options['inline_edits'], 'title', $p->ID) ?
94
+ TNP_Composer::get_edited_inline_post_field($options['inline_edits'], 'title', $p->ID) :
95
+ tnp_post_title($p)
96
  ?>
97
  </td>
98
  </tr>
107
 
108
  <?php if ($excerpt_length) { ?>
109
  <tr>
110
+ <td align="center" inline-class="excerpt" class="title tnpc-row-edit tnpc-inline-editable" data-type="text" data-id="<?php echo $p->ID ?>">
111
  <?php
112
+ echo TNP_Composer::is_post_field_edited_inline($options['inline_edits'], 'text', $p->ID) ?
113
+ TNP_Composer::get_edited_inline_post_field($options['inline_edits'], 'text', $p->ID) :
114
+ tnp_post_excerpt($p, $excerpt_length)
115
  ?>
116
  </td>
117
  </tr>
125
  </tr>
126
  <?php } ?>
127
  </table>
 
 
 
128
  <?php
129
+ $items[] = ob_get_clean();
130
+
131
+ } ?>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
132
 
133
 
134
+ <?php echo TNP_Composer::grid($items, ['width'=>$total_width, 'responsive'=>true, 'padding'=>0])?>
 
135
 
 
136
 
 
137
 
emails/tnp-composer/_css/newsletter-builder-v2.css CHANGED
@@ -16,7 +16,7 @@
16
  overflow: hidden;
17
  box-sizing: border-box;
18
  max-width: 1380px;
19
- margin: 0 auto;
20
  }
21
 
22
  /* Contains the newsletter editing area */
16
  overflow: hidden;
17
  box-sizing: border-box;
18
  max-width: 1380px;
19
+ /*margin: 0 auto;*/
20
  }
21
 
22
  /* Contains the newsletter editing area */
emails/tnp-composer/_scripts/newsletter-builder-v2.js CHANGED
@@ -542,7 +542,7 @@ function tnpc_load_preset(id, subject, isEditMode) {
542
  }
543
 
544
  if (subject && subject.length > 0) {
545
- jQuery('#options-subject').val(tnpc_remove_double_quotes_escape_from(subject));
546
  }
547
  },
548
  });
@@ -562,8 +562,7 @@ function tnpc_load_preset(id, subject, isEditMode) {
562
  }
563
 
564
  function tnpc_save_preset(form) {
565
-
566
- const presetName = tnpc_remove_double_quotes_from(document.querySelector('#options-subject').value);
567
 
568
  const presetNameModal = new TNPModal({
569
  title: 'Choose a preset name',
@@ -572,7 +571,7 @@ function tnpc_save_preset(form) {
572
  clickConfirmOnPressEnter: true,
573
  onConfirm: function () {
574
  const inputEl = document.querySelector('#preset_name');
575
- document.querySelector('#options-subject').value = inputEl.value;
576
  tnpc_save(form);
577
  form.submit();
578
  }
@@ -656,7 +655,7 @@ function tnpc_remove_double_quotes_from(str) {
656
 
657
  function tnpc_update_preset(form) {
658
 
659
- const presetName = tnpc_remove_double_quotes_from(document.querySelector('#options-subject').value);
660
 
661
  const presetNameModal = new TNPModal({
662
  title: 'Choose a preset name',
@@ -665,7 +664,7 @@ function tnpc_update_preset(form) {
665
  clickConfirmOnPressEnter: true,
666
  onConfirm: function () {
667
  const inputEl = document.querySelector('#preset_name');
668
- document.querySelector('#options-subject').value = inputEl.value;
669
  tnpc_save(form);
670
  form.submit();
671
  }
542
  }
543
 
544
  if (subject && subject.length > 0) {
545
+ jQuery('#options-subject-subject').val(tnpc_remove_double_quotes_escape_from(subject));
546
  }
547
  },
548
  });
562
  }
563
 
564
  function tnpc_save_preset(form) {
565
+ const presetName = document.getElementById('options-subject-subject').value.replace('"', '');
 
566
 
567
  const presetNameModal = new TNPModal({
568
  title: 'Choose a preset name',
571
  clickConfirmOnPressEnter: true,
572
  onConfirm: function () {
573
  const inputEl = document.querySelector('#preset_name');
574
+ document.querySelector('#options-subject-subject').value = inputEl.value;
575
  tnpc_save(form);
576
  form.submit();
577
  }
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',
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
  }
emails/tnp-composer/css/backend.css CHANGED
@@ -1,7 +1,7 @@
1
  /* Contains some rules to simulate device layout on backend preview */
2
 
3
  #newsletter-builder-area-center-frame-content.tnp-view-mobile table[class="responsive"],
4
- #newsletter-builder-area-center-frame-content.tnp-view-mobile table.responsive
5
  {
6
  width:100%!important;
7
  float: none;
@@ -23,7 +23,7 @@
23
  #newsletter-builder-area-center-frame-content.tnp-view-mobile img.responsive
24
  {
25
  max-width: 100%!important;
26
- }
27
 
28
  #newsletter-builder-area-center-frame-content.tnp-view-mobile img[class="fluid"]{
29
  width: 100%;
@@ -41,3 +41,7 @@
41
  #newsletter-builder-area-center-frame-content.tnp-view-mobile .tnp-grid-column {
42
  max-width: 100%!important;
43
  }
 
 
 
 
1
  /* Contains some rules to simulate device layout on backend preview */
2
 
3
  #newsletter-builder-area-center-frame-content.tnp-view-mobile table[class="responsive"],
4
+ #newsletter-builder-area-center-frame-content.tnp-view-mobile table.responsive
5
  {
6
  width:100%!important;
7
  float: none;
23
  #newsletter-builder-area-center-frame-content.tnp-view-mobile img.responsive
24
  {
25
  max-width: 100%!important;
26
+ }
27
 
28
  #newsletter-builder-area-center-frame-content.tnp-view-mobile img[class="fluid"]{
29
  width: 100%;
41
  #newsletter-builder-area-center-frame-content.tnp-view-mobile .tnp-grid-column {
42
  max-width: 100%!important;
43
  }
44
+
45
+ #newsletter-builder-area-center-frame-content.tnp-view-mobile td[data-responsive-image="true"] {
46
+ height: auto !important;
47
+ }
emails/tnp-composer/css/newsletter.css CHANGED
@@ -8,31 +8,31 @@ img { border: 0; height: auto; line-height: 100%; outline: none; text-decoration
8
  img.aligncenter { display: block; margin: 0 auto;}
9
 
10
  @media screen and (max-width: 525px) {
11
- .pt-1 { padding-top: 15px!important; }
12
- .pb-1 { padding-bottom: 15px!important; }
13
  .responsive { width:100%!important; }
14
  table.responsive { width:100%!important; float: none; display: table; padding-left: 0; padding-right: 0; }
15
  table[class="responsive"] { width:100%!important; float: none; display: table; padding-left: 0; padding-right: 0; }
16
- img[class="responsive"] { max-width: 100%!important; }
 
17
  .fluid { max-width: 100%!important; width: auto; }
18
  img[class="fluid"] { max-width: 100%!important; width: auto; }
19
 
20
  .block { display: block; }
21
 
22
- td[class="responsive"]{
23
- width:100%!important;
24
- max-width: 100%!important;
25
- display: block;
26
- padding-left: 0 !important;
27
- padding-right: 0 !important;
28
- float: none;
29
- }
30
 
31
  td[class="section-padding-bottom-image"]{
32
  padding: 50px 15px 0 15px !important;
33
  }
34
 
 
 
 
35
  .tnp-grid-column {
36
  max-width: 100%!important;
37
  }
 
38
  }
 
8
  img.aligncenter { display: block; margin: 0 auto;}
9
 
10
  @media screen and (max-width: 525px) {
11
+ .pt-1, .padding-top-15 { padding-top: 15px!important; }
12
+ .pb-1, .padding-bottom-15 { padding-bottom: 15px!important; }
13
  .responsive { width:100%!important; }
14
  table.responsive { width:100%!important; float: none; display: table; padding-left: 0; padding-right: 0; }
15
  table[class="responsive"] { width:100%!important; float: none; display: table; padding-left: 0; padding-right: 0; }
16
+ img[class="responsive"] { max-width: 100%!important; }
17
+ /* "width: auto" restores the natural dimensions forced with attributes for Outlook which blocks the expansion on mobile */
18
  .fluid { max-width: 100%!important; width: auto; }
19
  img[class="fluid"] { max-width: 100%!important; width: auto; }
20
 
21
  .block { display: block; }
22
 
23
+ td[class="responsive"]{width:100%!important; max-width: 100%!important; display: block; padding-left: 0 !important; padding-right: 0!important; float: none; }
24
+ td.responsive { width:100%!important; max-width: 100%!important; display: block; padding-left: 0 !important; padding-right: 0!important; float: none; }
 
 
 
 
 
 
25
 
26
  td[class="section-padding-bottom-image"]{
27
  padding: 50px 15px 0 15px !important;
28
  }
29
 
30
+ .max-width-100 { max-width: 100%!important; }
31
+
32
+ /* Obsolete */
33
  .tnp-grid-column {
34
  max-width: 100%!important;
35
  }
36
+
37
  }
38
+
emails/tnp-composer/index-v2.php CHANGED
@@ -31,9 +31,10 @@ $rev_dir = is_rtl() ? 'ltr' : 'rlt';
31
 
32
  ?>
33
  <script type="text/javascript">
34
- // collapse wp menu
35
- document.body.classList.add('folded');
36
-
 
37
  function tnp_view(type) {
38
  if (type === 'mobile') {
39
  jQuery('#newsletter-builder-area-center-frame-content').addClass('tnp-view-mobile');
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');
includes/antispam.php CHANGED
@@ -1,195 +1,198 @@
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 (empty($this->options['ip_blacklist'])) {
91
- return false;
92
- }
93
- $this->logger->debug('IP blacklist check');
94
- foreach ($this->options['ip_blacklist'] as $item) {
95
- if ($this->ip_match($ip, $item)) {
96
- return true;
97
- }
98
- }
99
- return false;
100
- }
101
-
102
- function is_missing_domain_mx($email) {
103
- // Actually not fully implemented
104
- return false;
105
-
106
- if (empty($this->options['domain_check'])) {
107
- return false;
108
- }
109
-
110
- $this->logger->debug('Domain MX check');
111
- list($local, $domain) = explode('@', $email);
112
-
113
- $hosts = array();
114
- if (!getmxrr($domain, $hosts)) {
115
- return true;
116
- }
117
- return false;
118
- }
119
-
120
- function is_flood($email, $ip) {
121
- global $wpdb;
122
-
123
- if (empty($this->options['antiflood'])) {
124
- return false;
125
- }
126
-
127
- $this->logger->debug('Antiflood check');
128
-
129
- $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));
130
-
131
- if ($updated && time() - $updated < $this->options['antiflood']) {
132
- return true;
133
- }
134
-
135
- return false;
136
- }
137
-
138
- function is_spam_text($text) {
139
- if (stripos($text, 'http:') !== false || stripos($text, 'https:') !== false) {
140
- return true;
141
- }
142
- if (stripos($text, 'www.') !== false) {
143
- return true;
144
- }
145
- if (preg_match('|[^\s\.]+\.[^\s\.]+\.[^\s\.]{2,}|', $text)) {
146
- return true;
147
- }
148
-
149
- return false;
150
- }
151
-
152
- function is_spam_by_akismet($email, $name, $ip, $agent, $referrer) {
153
-
154
- if (!class_exists('Akismet')) {
155
- return false;
156
- }
157
-
158
- if (empty($this->options['akismet'])) {
159
- return false;
160
- }
161
-
162
- $this->logger->debug('Akismet check');
163
- $request = 'blog=' . urlencode(home_url()) . '&referrer=' . urlencode($referrer) .
164
- '&user_agent=' . urlencode($agent) .
165
- '&comment_type=signup' .
166
- '&comment_author_email=' . urlencode($email) .
167
- '&user_ip=' . urlencode($ip);
168
- if (!empty($name)) {
169
- $request .= '&comment_author=' . urlencode($name);
170
- }
171
-
172
- $response = Akismet::http_post($request, 'comment-check');
173
-
174
- if ($response && $response[1] == 'true') {
175
- return true;
176
- }
177
- return false;
178
- }
179
-
180
- function ip_match($ip, $range) {
181
- if (empty($ip))
182
- return false;
183
- if (strpos($range, '/')) {
184
- list ($subnet, $bits) = explode('/', $range);
185
- $ip = ip2long($ip);
186
- $subnet = ip2long($subnet);
187
- $mask = -1 << (32 - $bits);
188
- $subnet &= $mask; # nb: in case the supplied subnet wasn't correctly aligned
189
- return ($ip & $mask) == $subnet;
190
- } else {
191
- return strpos($range, $ip) === 0;
192
- }
193
- }
194
-
195
- }
 
 
 
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 (empty($this->options['ip_blacklist'])) {
91
+ return false;
92
+ }
93
+ $this->logger->debug('IP blacklist check');
94
+ foreach ($this->options['ip_blacklist'] as $item) {
95
+ if (substr($item, 0, 1) === '#') {
96
+ continue;
97
+ }
98
+ if ($this->ip_match($ip, $item)) {
99
+ return true;
100
+ }
101
+ }
102
+ return false;
103
+ }
104
+
105
+ function is_missing_domain_mx($email) {
106
+ // Actually not fully implemented
107
+ return false;
108
+
109
+ if (empty($this->options['domain_check'])) {
110
+ return false;
111
+ }
112
+
113
+ $this->logger->debug('Domain MX check');
114
+ list($local, $domain) = explode('@', $email);
115
+
116
+ $hosts = array();
117
+ if (!getmxrr($domain, $hosts)) {
118
+ return true;
119
+ }
120
+ return false;
121
+ }
122
+
123
+ function is_flood($email, $ip) {
124
+ global $wpdb;
125
+
126
+ if (empty($this->options['antiflood'])) {
127
+ return false;
128
+ }
129
+
130
+ $this->logger->debug('Antiflood check');
131
+
132
+ $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));
133
+
134
+ if ($updated && time() - $updated < $this->options['antiflood']) {
135
+ return true;
136
+ }
137
+
138
+ return false;
139
+ }
140
+
141
+ function is_spam_text($text) {
142
+ if (stripos($text, 'http:') !== false || stripos($text, 'https:') !== false) {
143
+ return true;
144
+ }
145
+ if (stripos($text, 'www.') !== false) {
146
+ return true;
147
+ }
148
+ if (preg_match('|[^\s\.]+\.[^\s\.]+\.[^\s\.]{2,}|', $text)) {
149
+ return true;
150
+ }
151
+
152
+ return false;
153
+ }
154
+
155
+ function is_spam_by_akismet($email, $name, $ip, $agent, $referrer) {
156
+
157
+ if (!class_exists('Akismet')) {
158
+ return false;
159
+ }
160
+
161
+ if (empty($this->options['akismet'])) {
162
+ return false;
163
+ }
164
+
165
+ $this->logger->debug('Akismet check');
166
+ $request = 'blog=' . urlencode(home_url()) . '&referrer=' . urlencode($referrer) .
167
+ '&user_agent=' . urlencode($agent) .
168
+ '&comment_type=signup' .
169
+ '&comment_author_email=' . urlencode($email) .
170
+ '&user_ip=' . urlencode($ip);
171
+ if (!empty($name)) {
172
+ $request .= '&comment_author=' . urlencode($name);
173
+ }
174
+
175
+ $response = Akismet::http_post($request, 'comment-check');
176
+
177
+ if ($response && $response[1] == 'true') {
178
+ return true;
179
+ }
180
+ return false;
181
+ }
182
+
183
+ function ip_match($ip, $range) {
184
+ if (empty($ip))
185
+ return false;
186
+ if (strpos($range, '/')) {
187
+ list ($subnet, $bits) = explode('/', $range);
188
+ $ip = ip2long($ip);
189
+ $subnet = ip2long($subnet);
190
+ $mask = -1 << (32 - $bits);
191
+ $subnet &= $mask; # nb: in case the supplied subnet wasn't correctly aligned
192
+ return ($ip & $mask) == $subnet;
193
+ } else {
194
+ return strpos($range, $ip) === 0;
195
+ }
196
+ }
197
+
198
+ }
includes/composer.php CHANGED
@@ -1,863 +1,879 @@
1
- <?php
2
-
3
- /** For old style coders */
4
- function tnp_register_block($dir) {
5
- return TNP_Composer::register_block($dir);
6
- }
7
-
8
- /**
9
- * Generates and HTML button for email using the values found on $options and
10
- * prefixed by $prefix, with the standard syntax of NewsletterFields::button().
11
- *
12
- * @param array $options
13
- * @param string $prefix
14
- * @return string
15
- */
16
- function tnpc_button($options, $prefix = 'button') {
17
- return TNP_Composer::button($options, $prefix);
18
- }
19
-
20
- class TNP_Composer {
21
-
22
- static $block_dirs = array();
23
-
24
- static function register_block($dir) {
25
- // Checks
26
- $dir = realpath($dir);
27
- if (!$dir) {
28
- $error = new WP_Error('1', 'Seems not a valid path: ' . $dir);
29
- NewsletterEmails::instance()->logger->error($error);
30
- return $error;
31
- }
32
-
33
- $dir = wp_normalize_path($dir);
34
-
35
- if (!file_exists($dir . '/block.php')) {
36
- $error = new WP_Error('1', 'block.php missing on folder ' . $dir);
37
- NewsletterEmails::instance()->logger->error($error);
38
- return $error;
39
- }
40
-
41
- self::$block_dirs[] = $dir;
42
- return true;
43
- }
44
-
45
- /**
46
- * @param string $open
47
- * @param string $inner
48
- * @param string $close
49
- * @param string[] $markers
50
- *
51
- * @return string
52
- */
53
- static function wrap_html_element($open, $inner, $close, $markers = array('<!-- tnp -->', '<!-- /tnp -->')) {
54
-
55
- return $open . $markers[0] . $inner . $markers[1] . $close;
56
- }
57
-
58
- /**
59
- * @param string $block
60
- * @param string[] $markers
61
- *
62
- * @return string
63
- */
64
- static function unwrap_html_element($block, $markers = array('<!-- tnp -->', '<!-- /tnp -->')) {
65
- if (self::_has_markers($block, $markers)) {
66
- self::_escape_markers($markers);
67
- $pattern = sprintf('/%s(.*?)%s/s', $markers[0], $markers[1]);
68
-
69
- $matches = array();
70
- preg_match($pattern, $block, $matches);
71
-
72
- return $matches[1];
73
- }
74
-
75
- return $block;
76
- }
77
-
78
- /**
79
- * @param string $block
80
- * @param string[] $markers
81
- *
82
- * @return bool
83
- */
84
- private static function _has_markers($block, $markers = array('<!-- tnp -->', '<!-- /tnp -->')) {
85
-
86
- self::_escape_markers($markers);
87
-
88
- $pattern = sprintf('/%s(.*?)%s/s', $markers[0], $markers[1]);
89
-
90
- return preg_match($pattern, $block);
91
- }
92
-
93
- /**
94
- * Sources:
95
- * - https://webdesign.tutsplus.com/tutorials/creating-a-future-proof-responsive-email-without-media-queries--cms-23919
96
- *
97
- * @param type $email
98
- * @return type
99
- */
100
- static function get_html_open($email) {
101
- $open = "<!DOCTYPE html>\n";
102
- $open .= "<html xmlns=\"https://www.w3.org/1999/xhtml\" xmlns:o=\"urn:schemas-microsoft-com:office:office\">\n<head>\n<title>{email_subject}</title>\n";
103
- $open .= "<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n";
104
-
105
- $open .= '<!--[if !mso]><!-->' . "\n";
106
- $open .= '<meta http-equiv="X-UA-Compatible" content="IE=edge" />' . "\n";
107
- $open .= '<!--<![endif]-->' . "\n";
108
-
109
- $open .= '<!--[if mso]>' . "\n";
110
- ;
111
- $open .= '<style type="text/css">';
112
- $open .= 'table {border-collapse:collapse;border-spacing:0;margin:0;}';
113
- $open .= 'div, td {padding:0;}';
114
- $open .= 'div {margin:0 !important;}';
115
- $open .= '</style>';
116
- $open .= "\n";
117
- $open .= '<noscript>';
118
- $open .= '<xml>';
119
- $open .= '<o:OfficeDocumentSettings>';
120
- $open .= '<o:PixelsPerInch>96</o:PixelsPerInch>';
121
- $open .= '</o:OfficeDocumentSettings>';
122
- $open .= '</xml>';
123
- $open .= '</noscript>';
124
- $open .= "\n";
125
- $open .= '<![endif]-->';
126
- $open .= "\n";
127
- $open .= "<style type=\"text/css\">\n";
128
- $open .= NewsletterEmails::instance()->get_composer_css();
129
- $open .= "\n</style>\n";
130
- $open .= "</head>\n";
131
- $open .= '<body style="margin: 0; padding: 0; line-height: normal; word-spacing: normal;" dir="' . (is_rtl() ? 'rtl' : 'ltr') . '">';
132
- $open .= "\n";
133
- $open .= self::get_html_preheader($email);
134
-
135
- return $open;
136
- }
137
-
138
- static private function get_html_preheader($email) {
139
-
140
- if (empty($email->options['preheader'])) {
141
- return "";
142
- }
143
-
144
- $preheader_text = $email->options['preheader'];
145
- $html = "<div style=\"display:none;font-size:1px;color:#ffffff;line-height:1px;max-height:0px;max-width:0px;opacity:0;overflow:hidden;\">$preheader_text</div>";
146
- $html .= "\n";
147
-
148
- return $html;
149
- }
150
-
151
- static function get_html_close($email) {
152
- return "</body>\n</html>";
153
- }
154
-
155
- /**
156
- *
157
- * @param TNP_Email $email
158
- * @return string
159
- */
160
- static function get_main_wrapper_open($email) {
161
- if (!isset($email->options['composer_background']) || $email->options['composer_background'] == 'inherit') {
162
- $bgcolor = '';
163
- } else {
164
- $bgcolor = $email->options['composer_background'];
165
- }
166
-
167
- return "\n<table cellpadding='0' cellspacing='0' border='0' width='100%'>\n" .
168
- "<tr>\n" .
169
- "<td bgcolor='$bgcolor' valign='top'><!-- tnp -->";
170
- }
171
-
172
- /**
173
- *
174
- * @param TNP_Email $email
175
- * @return string
176
- */
177
- static function get_main_wrapper_close($email) {
178
- return "\n<!-- /tnp -->\n" .
179
- "</td>\n" .
180
- "</tr>\n" .
181
- "</table>\n\n";
182
- }
183
-
184
- /**
185
- * Remove <doctype>, <body> and unnecessary envelopes for editing with composer
186
- *
187
- * @param string $html_email
188
- *
189
- * @return string
190
- */
191
- static function unwrap_email($html_email) {
192
-
193
- if (self::_has_markers($html_email)) {
194
- $html_email = self::unwrap_html_element($html_email);
195
- } else {
196
- //KEEP FOR OLD EMAIL COMPATIBILITY
197
- // Extracts only the body part
198
- $x = strpos($html_email, '<body');
199
- if ($x) {
200
- $x = strpos($html_email, '>', $x);
201
- $y = strpos($html_email, '</body>');
202
- $html_email = substr($html_email, $x + 1, $y - $x - 1);
203
- }
204
-
205
- /* Cleans up uncorrectly stored newsletter bodies */
206
- $html_email = preg_replace('/<style\s+.*?>.*?<\\/style>/is', '', $html_email);
207
- $html_email = preg_replace('/<meta.*?>/', '', $html_email);
208
- $html_email = preg_replace('/<title\s+.*?>.*?<\\/title>/i', '', $html_email);
209
- $html_email = trim($html_email);
210
- }
211
-
212
- // Required since esc_html DOES NOT escape the HTML entities (apparently)
213
- $html_email = str_replace('&', '&amp;', $html_email);
214
- $html_email = str_replace('"', '&quot;', $html_email);
215
- $html_email = str_replace('<', '&lt;', $html_email);
216
- $html_email = str_replace('>', '&gt;', $html_email);
217
-
218
- return $html_email;
219
- }
220
-
221
- private static function _escape_markers(&$markers) {
222
- $markers[0] = str_replace('/', '\/', $markers[0]);
223
- $markers[1] = str_replace('/', '\/', $markers[1]);
224
- }
225
-
226
- /**
227
- * Using the data collected inside $controls (and submitted by a form containing the
228
- * composer fields), updates the email. The message body is completed with doctype,
229
- * head, style and the main wrapper.
230
- *
231
- * @param TNP_Email $email
232
- * @param NewsletterControls $controls
233
- */
234
- static function update_email($email, $controls) {
235
- if (isset($controls->data['subject'])) {
236
- $email->subject = $controls->data['subject'];
237
- }
238
-
239
- // They should be only composer options
240
- foreach ($controls->data as $name => $value) {
241
- if (strpos($name, 'options_') === 0) {
242
- $email->options[substr($name, 8)] = $value;
243
- }
244
- }
245
-
246
- //if (isset($controls->data['preheader'])) {
247
- // $email->options['preheader'] = $controls->data['preheader'];
248
- //}
249
-
250
- $email->editor = NewsletterEmails::EDITOR_COMPOSER;
251
-
252
- $email->message = self::get_html_open($email) . self::get_main_wrapper_open($email) .
253
- $controls->data['message'] . self::get_main_wrapper_close($email) . self::get_html_close($email);
254
- }
255
-
256
- /**
257
- * Prepares a controls object injecting the relevant fields from an email
258
- * which cannot be directly used by controls. If $email is null or missing,
259
- * $controls is prepared with default values.
260
- *
261
- * @param NewsletterControls $controls
262
- * @param TNP_Email $email
263
- */
264
- static function prepare_controls($controls, $email = null) {
265
-
266
- // Controls for a new email (which actually does not exist yet
267
- if (!empty($email)) {
268
-
269
- foreach ($email->options as $name => $value) {
270
- $controls->data['options_' . $name] = $value;
271
- }
272
-
273
- $controls->data['message'] = TNP_Composer::unwrap_email($email->message);
274
- $controls->data['subject'] = $email->subject;
275
- }
276
-
277
- if (!empty($email->options['sender_email'])) {
278
- $controls->data['sender_email'] = $email->options['sender_email'];
279
- } else {
280
- $controls->data['sender_email'] = Newsletter::instance()->options['sender_email'];
281
- }
282
-
283
- if (!empty($email->options['sender_name'])) {
284
- $controls->data['sender_name'] = $email->options['sender_name'];
285
- } else {
286
- $controls->data['sender_name'] = Newsletter::instance()->options['sender_name'];
287
- }
288
-
289
- $controls->data = array_merge(TNP_Composer::get_global_style_defaults(), $controls->data);
290
- }
291
-
292
- /**
293
- * Extract inline edited post field from inline_edit_list[]
294
- *
295
- * @param array $inline_edit_list
296
- * @param string $field_type
297
- * @param int $post_id
298
- *
299
- * @return string
300
- */
301
- static function get_edited_inline_post_field($inline_edit_list, $field_type, $post_id) {
302
-
303
- foreach ($inline_edit_list as $edit) {
304
- if ($edit['type'] == $field_type && $edit['post_id'] == $post_id) {
305
- return $edit['content'];
306
- }
307
- }
308
-
309
- return '';
310
- }
311
-
312
- /**
313
- * Check if inline_edit_list[] have inline edit field for specific post
314
- *
315
- * @param array $inline_edit_list
316
- * @param string $field_type
317
- * @param int $post_id
318
- *
319
- * @return bool
320
- */
321
- static function is_post_field_edited_inline($inline_edit_list, $field_type, $post_id) {
322
- if (empty($inline_edit_list) || !is_array($inline_edit_list)) {
323
- return false;
324
- }
325
- foreach ($inline_edit_list as $edit) {
326
- if ($edit['type'] == $field_type && $edit['post_id'] == $post_id) {
327
- return true;
328
- }
329
- }
330
-
331
- return false;
332
- }
333
-
334
- /**
335
- * Creates the HTML for a button extrating from the options, with the provided prefix, the button attributes:
336
- *
337
- * - [prefix]_url The button URL
338
- * - [prefix]_font_family
339
- * - [prefix]_font_size
340
- * - [prefix]_font_weight
341
- * - [prefix]_label
342
- * - [prefix]_font_color The label color
343
- * - [prefix]_background The button color
344
- *
345
- * TODO: Add radius and possiblt the alignment
346
- *
347
- * @param array $options
348
- * @param string $prefix
349
- * @return string
350
- */
351
- static function button($options, $prefix = 'button') {
352
-
353
- if (empty($options[$prefix . '_label'])) {
354
- return;
355
- }
356
- $defaults = [
357
- $prefix . '_url' => '#',
358
- $prefix . '_font_family' => 'Helvetica, Arial, sans-serif',
359
- $prefix . '_font_color' => '#ffffff',
360
- $prefix . '_font_weight' => 'bold',
361
- $prefix . '_font_size' => 20,
362
- $prefix . '_background' => '#256F9C',
363
- $prefix . '_align' => 'center'
364
- ];
365
-
366
- $options = array_merge($defaults, array_filter($options));
367
-
368
- $b = '<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="margin: 0 auto"';
369
- if (!empty($options[$prefix . '_align'])) {
370
- $b .= ' align="' . esc_attr($options[$prefix . '_align']) . '"';
371
- }
372
- if (!empty($options[$prefix . '_width'])) {
373
- $b .= ' width="' . esc_attr($options[$prefix . '_width']) . '"';
374
- }
375
- $b .= '>';
376
- $b .= '<tr>';
377
- $b .= '<td align="center" bgcolor="' . $options[$prefix . '_background'] . '" role="presentation" style="border:none;border-radius:3px;cursor:auto;mso-padding-alt:10px 25px;background:' . $options[$prefix . '_background'] . '" valign="middle">';
378
- $b .= '<a href="' . $options[$prefix . '_url'] . '"';
379
- $b .= ' style="display:inline-block;background:' . $options[$prefix . '_background'] . ';color:' . $options[$prefix . '_font_color'] . ';font-family:' . $options[$prefix . '_font_family'] . ';font-size:' . $options[$prefix . '_font_size'] . 'px;font-weight:' . $options[$prefix . '_font_weight'] . ';line-height:120%;margin:0;text-decoration:none;text-transform:none;padding:10px 25px;mso-padding-alt:0px;border-radius:3px;"';
380
- $b .= ' target="_blank">';
381
- $b .= $options[$prefix . '_label'];
382
- $b .= '</a>';
383
- $b .= '</td></tr></table>';
384
- return $b;
385
- }
386
-
387
- /**
388
- * Generates an IMG tag, linked if the media has an URL.
389
- *
390
- * @param TNP_Media $media
391
- * @param string $style
392
- * @return string
393
- */
394
- static function image($media, $attr = []) {
395
-
396
- $default_attrs = [
397
- 'style' => 'max-width: 100%; height: auto; display: inline-block',
398
- 'class' => '',
399
- 'link-style' => 'text-decoration: none; display: inline-block',
400
- 'link-class' => null,
401
- ];
402
-
403
- $attr = array_merge($default_attrs, $attr);
404
-
405
- //Class and style attribute are mutually exclusive.
406
- //Class take priority to style because classes will transform to inline style inside block rendering operation
407
- if (!empty($attr['inline-class'])) {
408
- $styling = ' inline-class="' . esc_attr($attr['inline-class']) . '" ';
409
- } else {
410
- $styling = ' style="' . esc_attr($attr['style']) . '" ';
411
- }
412
-
413
- if (!empty($attr['class'])) {
414
- $styling .= ' class="' . esc_attr($attr['class']) . '" ';
415
- }
416
-
417
- //Class and style attribute are mutually exclusive.
418
- //Class take priority to style because classes will transform to inline style inside block rendering operation
419
- if (!empty($attr['link-class'])) {
420
- $link_styling = ' inline-class="' . esc_attr($attr['link-class']) . '" ';
421
- } else {
422
- $link_styling = ' style="' . esc_attr($attr['link-style']) . '" ';
423
- }
424
-
425
- $b = '';
426
- if ($media->link) {
427
- $b .= '<a href="' . esc_attr($media->link) . '" target="_blank" rel="noopener nofollow" style="display: inline-block; font-size: 0; text-decoration: none; line-height: normal!important">';
428
- } else {
429
- // The span grants images are not upscaled when fluid (two columns posts block)
430
- $b .= '<span style="display: inline-block; font-size: 0; text-decoration: none; line-height: normal!important">';
431
- }
432
- if ($media) {
433
- $b .= '<img src="' . esc_attr($media->url) . '" width="' . esc_attr($media->width) . '"';
434
- if ($media->height) {
435
- $b .= ' height="' . esc_attr($media->height) . '"';
436
- }
437
- $b .= ' alt="' . esc_attr($media->alt) . '"'
438
- . ' border="0"'
439
- . ' style="display: inline-block; max-width: 100%!important; padding: 0; border: 0;"'
440
- . ' class="' . esc_attr($attr['class']) . '" '
441
- . '>';
442
- }
443
-
444
- if ($media->link) {
445
- $b .= '</a>';
446
- } else {
447
- $b .= '</span>';
448
- }
449
-
450
- return $b;
451
- }
452
-
453
- /**
454
- * Returns a WP media ID for the specified post (or false if nothing can be found)
455
- * looking for the featured image or, if missing, taking the first media in the gallery and
456
- * if again missing, searching the first reference to a media in the post content.
457
- *
458
- * The media ID is not checked for real existance of the associated attachment.
459
- *
460
- * @param int $post_id
461
- * @return int
462
- */
463
- static function get_post_thumbnail_id($post_id) {
464
- if (is_object($post_id)) {
465
- $post_id = $post_id->ID;
466
- }
467
-
468
- // Find a media id to be used as featured image
469
- $media_id = get_post_thumbnail_id($post_id);
470
- if (!empty($media_id)) {
471
- return $media_id;
472
- }
473
-
474
- $attachments = get_children(array('numberpost' => 1, 'post_parent' => $post_id, 'post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => 'image', 'order' => 'ASC', 'orderby' => 'menu_order'));
475
- if (!empty($attachments)) {
476
- foreach ($attachments as $id => &$attachment) {
477
- return $id;
478
- }
479
- }
480
-
481
- $post = get_post($post_id);
482
-
483
- $r = preg_match('/wp-image-(\d+)/', $post->post_content, $matches);
484
- if ($matches) {
485
- return (int) $matches[1];
486
- }
487
-
488
- return false;
489
- }
490
-
491
- /**
492
- * Builds a TNP_Media object to be used in newsletters from a WP media/attachement ID. The returned
493
- * media has a size which best match the one requested (this is the standard WP behavior, plugins
494
- * could change it).
495
- *
496
- * @param int $media_id
497
- * @param array $size
498
- * @return \TNP_Media
499
- */
500
- function get_media($media_id, $size) {
501
- $src = wp_get_attachment_image_src($media_id, $size);
502
- if (!$src) {
503
- return null;
504
- }
505
- $media = new TNP_Media();
506
- $media->id = $media_id;
507
- $media->url = $src[0];
508
- $media->width = $src[1];
509
- $media->height = $src[2];
510
- return $media;
511
- }
512
-
513
- static function post_content($post) {
514
- $content = $post->post_content;
515
- $content = wpautop($content);
516
- if (true || $options['enable shortcodes']) {
517
- remove_shortcode('gallery');
518
- add_shortcode('gallery', 'tnp_gallery_shortcode');
519
- $content = do_shortcode($content);
520
- }
521
- $content = str_replace('<p>', '<p class="paragraph">', $content);
522
-
523
- $selected_images = array();
524
- if (preg_match_all('/<img [^>]+>/', $content, $matches)) {
525
- foreach ($matches[0] as $image) {
526
- if (preg_match('/wp-image-([0-9]+)/i', $image, $class_id) && ( $attachment_id = absint($class_id[1]) )) {
527
- $selected_images[$image] = $attachment_id;
528
- }
529
- }
530
- }
531
-
532
- foreach ($selected_images as $image => $attachment_id) {
533
- $src = tnp_media_resize($attachment_id, array(600, 0));
534
- if (is_wp_error($src)) {
535
- continue;
536
- }
537
- $content = str_replace($image, '<img src="' . $src . '" width="600" style="max-width: 100%">', $content);
538
- }
539
-
540
- return $content;
541
- }
542
-
543
- static function get_global_style_defaults() {
544
- return [
545
- 'options_composer_title_font_family' => 'Verdana, Geneva, sans-serif',
546
- 'options_composer_title_font_size' => 32,
547
- 'options_composer_title_font_weight' => 'normal',
548
- 'options_composer_title_font_color' => '#222222',
549
- 'options_composer_text_font_family' => 'Verdana, Geneva, sans-serif',
550
- 'options_composer_text_font_size' => 16,
551
- 'options_composer_text_font_weight' => 'normal',
552
- 'options_composer_text_font_color' => '#222222',
553
- 'options_composer_button_font_family' => 'Verdana, Geneva, sans-serif',
554
- 'options_composer_button_font_size' => 16,
555
- 'options_composer_button_font_weight' => 'normal',
556
- 'options_composer_button_font_color' => '#FFFFFF',
557
- 'options_composer_button_background_color' => '#256F9C',
558
- 'options_composer_background' => '#FFFFFF',
559
- 'options_composer_block_background' => '#FFFFFF',
560
- ];
561
- }
562
-
563
- /**
564
- * Inspired by: https://webdesign.tutsplus.com/tutorials/creating-a-future-proof-responsive-email-without-media-queries--cms-23919
565
- * @param string[] $items
566
- * @param array $attrs
567
- * @return string
568
- */
569
- static function grid($items = [], $attrs = []) {
570
- $attrs = wp_parse_args($attrs, ['width' => 600, 'columns' => 2, 'padding' => 10]);
571
- $width = (int) $attrs['width'];
572
- $columns = (int) $attrs['columns'];
573
- $padding = (int) $attrs['padding'];
574
- $column_width = $width / $columns;
575
- $td_width = 100 / $columns;
576
- $chunks = array_chunk($items, $columns);
577
-
578
- $e = '<div style="text-align:center;font-size:0;">';
579
- foreach ($chunks as &$chunk) {
580
-
581
- $e .= '<!--[if mso]><table role="presentation" width="100%"><tr><![endif]-->';
582
- foreach ($chunk as &$item) {
583
- $e .= '<!--[if mso]><td style="width:' . $td_width . '%;padding:' . $padding . 'px" valign="top"><![endif]-->';
584
-
585
- $e .= '<div class="tnp-grid-column" style="width:100%;max-width:' . $column_width . 'px;display:inline-block;vertical-align: top;box-sizing: border-box;">';
586
-
587
- // This element to add padding without deal with border-box not well supported
588
- $e .= '<div style="padding:' . $padding . 'px;">';
589
- $e .= $item;
590
- $e .= '</div>';
591
- $e .= '</div>';
592
-
593
- $e .= '<!--[if mso]></td><![endif]-->';
594
- }
595
- $e .= '<!--[if mso]></tr></table><![endif]-->';
596
- $e .= '</div>';
597
- }
598
- return $e;
599
- }
600
-
601
- static function get_text_style($options, $prefix, $composer, $attrs = []) {
602
- return self::get_style($options, $prefix, $composer, 'text', $attrs);
603
- }
604
-
605
- static function get_title_style($options, $prefix, $composer, $attrs = []) {
606
- return self::get_style($options, $prefix, $composer, 'title', $attrs);
607
- }
608
-
609
- static function get_style($options, $prefix, $composer, $type = 'text', $attrs = []) {
610
- $style = new TNP_Style();
611
- $scale = 1.0;
612
- if (!empty($attrs['scale'])) {
613
- $scale = (float) $attrs['scale'];
614
- }
615
- if (!empty($prefix))
616
- $prefix .= '_';
617
-
618
- $style->font_family = empty($options[$prefix . 'font_family']) ? $composer[$type . '_font_family'] : $options[$prefix . 'font_family'];
619
- $style->font_size = empty($options[$prefix . 'font_size']) ? round($composer[$type . '_font_size'] * $scale) : $options[$prefix . 'font_size'];
620
- $style->font_color = empty($options[$prefix . 'font_color']) ? $composer[$type . '_font_color'] : $options[$prefix . 'font_color'];
621
- $style->font_weight = empty($options[$prefix . 'font_weight']) ? $composer[$type . '_font_weight'] : $options[$prefix . 'font_weight'];
622
- if ($type === 'button') {
623
- $style->background = empty($options[$prefix . 'background']) ? $composer[$type . '_background_color'] : $options[$prefix . 'background'];
624
- }
625
- return $style;
626
- }
627
-
628
- }
629
-
630
- class TNP_Style {
631
-
632
- var $font_family;
633
- var $font_size;
634
- var $font_weight;
635
- var $font_color;
636
- var $background;
637
-
638
- function echo_css($scale = 1.0) {
639
- echo 'font-size: ', round($this->font_size * $scale), 'px;';
640
- echo 'font-family: ', $this->font_family, ';';
641
- echo 'font-weight: ', $this->font_weight, ';';
642
- echo 'color: ', $this->font_color, ';';
643
- }
644
-
645
- }
646
-
647
- /**
648
- * Generate multicolumn and responsive html template for email.
649
- * Initialize class with max columns per row and start to add cells.
650
- */
651
- class TNP_Composer_Grid_System {
652
-
653
- /**
654
- * @var TNP_Composer_Grid_Row[]
655
- */
656
- private $rows;
657
-
658
- /**
659
- * @var int
660
- */
661
- private $cells_per_row;
662
-
663
- /**
664
- * @var int
665
- */
666
- private $cells_counter;
667
-
668
- /**
669
- * TNP_Composer_Grid_System constructor.
670
- *
671
- * @param int $columns_per_row Max columns per row
672
- */
673
- public function __construct($columns_per_row) {
674
- $this->cells_per_row = $columns_per_row;
675
- $this->cells_counter = 0;
676
- $this->rows = [];
677
- }
678
-
679
- public function __toString() {
680
- return $this->render();
681
- }
682
-
683
- /**
684
- * Add cell to grid
685
- *
686
- * @param TNP_Composer_Grid_Cell $cell
687
- */
688
- public function add_cell($cell) {
689
-
690
- if ($this->cells_counter % $this->cells_per_row === 0) {
691
- $this->add_row(new TNP_Composer_Grid_Row());
692
- }
693
-
694
- $row_idx = (int) floor($this->cells_counter / $this->cells_per_row);
695
- $this->rows[$row_idx]->add_cell($cell);
696
- $this->cells_counter++;
697
- }
698
-
699
- private function add_row($row) {
700
- $this->rows[] = $row;
701
- }
702
-
703
- public function render() {
704
-
705
- $str = '';
706
- foreach ($this->rows as $row) {
707
- $str .= $row->render();
708
- }
709
-
710
- return $str;
711
- }
712
-
713
- }
714
-
715
- /**
716
- * Class TNP_Composer_Grid_Row
717
- */
718
- class TNP_Composer_Grid_Row {
719
-
720
- /**
721
- * @var TNP_Composer_Grid_Cell[]
722
- */
723
- private $cells;
724
-
725
- public function __construct(...$cells) {
726
- if (!empty($cells)) {
727
- foreach ($cells as $cell) {
728
- $this->add_cell($cell);
729
- }
730
- }
731
- }
732
-
733
- /**
734
- * @param TNP_Composer_Grid_Cell $cell
735
- */
736
- public function add_cell($cell) {
737
- $this->cells[] = $cell;
738
- }
739
-
740
- public function render() {
741
- $rendered_cells = '';
742
- $column_percentage_width = round(100 / $this->cells_count(), 0, PHP_ROUND_HALF_DOWN) . '%';
743
- foreach ($this->cells as $cell) {
744
- $rendered_cells .= $cell->render(['width' => $column_percentage_width]);
745
- }
746
-
747
- $row_template = $this->get_template();
748
-
749
- return str_replace('TNP_ROW_CONTENT_PH', $rendered_cells, $row_template);
750
- }
751
-
752
- private function cells_count() {
753
- return count($this->cells);
754
- }
755
-
756
- private function get_template() {
757
- return "<table border='0' cellpadding='0' cellspacing='0' width='100%'><tbody><tr><td>TNP_ROW_CONTENT_PH</td></tr></tbody></table>";
758
- }
759
-
760
- }
761
-
762
- /**
763
- * Class TNP_Composer_Grid_Cell
764
- */
765
- class TNP_Composer_Grid_Cell {
766
-
767
- /**
768
- * @var string
769
- */
770
- private $content;
771
-
772
- /**
773
- * @var array
774
- */
775
- public $args;
776
-
777
- public function __construct($content = null, $args = []) {
778
- $default_args = [
779
- 'width' => '100%',
780
- 'class' => '',
781
- 'align' => 'left',
782
- 'valign' => 'top'
783
- ];
784
-
785
- $this->args = array_merge($default_args, $args);
786
-
787
- $this->content = $content ? $content : '';
788
- }
789
-
790
- public function add_content($content) {
791
- $this->content .= $content;
792
- }
793
-
794
- public function render($args) {
795
- $this->args = array_merge($this->args, $args);
796
-
797
- $column_template = $this->get_template();
798
- $column = str_replace(
799
- [
800
- 'TNP_ALIGN_PH',
801
- 'TNP_VALIGN_PH',
802
- 'TNP_WIDTH_PH',
803
- 'TNP_CLASS_PH',
804
- 'TNP_COLUMN_CONTENT_PH'
805
- ], [
806
- $this->args['align'],
807
- $this->args['valign'],
808
- $this->args['width'],
809
- $this->args['class'],
810
- $this->content
811
- ], $column_template);
812
-
813
- return $column;
814
- }
815
-
816
- private function get_template() {
817
- return "<table border='0' cellpadding='0' cellspacing='0' width='TNP_WIDTH_PH' align='left' style='table-layout: fixed;' class='responsive'>
818
- <tbody>
819
- <tr>
820
- <td border='0' style='padding: 20px 10px 40px;' align='TNP_ALIGN_PH' valign='TNP_VALIGN_PH' class='TNP_CLASS_PH'>
821
- TNP_COLUMN_CONTENT_PH
822
- </td>
823
- </tr>
824
- </tbody>
825
- </table>";
826
- }
827
-
828
- }
829
-
830
- class TNP_Composer_Component_Factory {
831
-
832
- private $options;
833
-
834
- /**
835
- * TNP_Composer_Component_Factory constructor.
836
- *
837
- * @param Controller$controller
838
- */
839
- public function __construct($controller) {
840
-
841
- }
842
-
843
- function heading() {
844
-
845
- }
846
-
847
- function paragraph() {
848
-
849
- }
850
-
851
- function link() {
852
-
853
- }
854
-
855
- function button() {
856
-
857
- }
858
-
859
- function image() {
860
-
861
- }
862
-
863
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /** For old style coders */
4
+ function tnp_register_block($dir) {
5
+ return TNP_Composer::register_block($dir);
6
+ }
7
+
8
+ /**
9
+ * Generates and HTML button for email using the values found on $options and
10
+ * prefixed by $prefix, with the standard syntax of NewsletterFields::button().
11
+ *
12
+ * @param array $options
13
+ * @param string $prefix
14
+ * @return string
15
+ */
16
+ function tnpc_button($options, $prefix = 'button') {
17
+ return TNP_Composer::button($options, $prefix);
18
+ }
19
+
20
+ class TNP_Composer {
21
+
22
+ static $block_dirs = array();
23
+
24
+ static function register_block($dir) {
25
+ // Checks
26
+ $dir = realpath($dir);
27
+ if (!$dir) {
28
+ $error = new WP_Error('1', 'Seems not a valid path: ' . $dir);
29
+ NewsletterEmails::instance()->logger->error($error);
30
+ return $error;
31
+ }
32
+
33
+ $dir = wp_normalize_path($dir);
34
+
35
+ if (!file_exists($dir . '/block.php')) {
36
+ $error = new WP_Error('1', 'block.php missing on folder ' . $dir);
37
+ NewsletterEmails::instance()->logger->error($error);
38
+ return $error;
39
+ }
40
+
41
+ self::$block_dirs[] = $dir;
42
+ return true;
43
+ }
44
+
45
+ /**
46
+ * @param string $open
47
+ * @param string $inner
48
+ * @param string $close
49
+ * @param string[] $markers
50
+ *
51
+ * @return string
52
+ */
53
+ static function wrap_html_element($open, $inner, $close, $markers = array('<!-- tnp -->', '<!-- /tnp -->')) {
54
+
55
+ return $open . $markers[0] . $inner . $markers[1] . $close;
56
+ }
57
+
58
+ /**
59
+ * @param string $block
60
+ * @param string[] $markers
61
+ *
62
+ * @return string
63
+ */
64
+ static function unwrap_html_element($block, $markers = array('<!-- tnp -->', '<!-- /tnp -->')) {
65
+ if (self::_has_markers($block, $markers)) {
66
+ self::_escape_markers($markers);
67
+ $pattern = sprintf('/%s(.*?)%s/s', $markers[0], $markers[1]);
68
+
69
+ $matches = array();
70
+ preg_match($pattern, $block, $matches);
71
+
72
+ return $matches[1];
73
+ }
74
+
75
+ return $block;
76
+ }
77
+
78
+ /**
79
+ * @param string $block
80
+ * @param string[] $markers
81
+ *
82
+ * @return bool
83
+ */
84
+ private static function _has_markers($block, $markers = array('<!-- tnp -->', '<!-- /tnp -->')) {
85
+
86
+ self::_escape_markers($markers);
87
+
88
+ $pattern = sprintf('/%s(.*?)%s/s', $markers[0], $markers[1]);
89
+
90
+ return preg_match($pattern, $block);
91
+ }
92
+
93
+ /**
94
+ * Sources:
95
+ * - https://webdesign.tutsplus.com/tutorials/creating-a-future-proof-responsive-email-without-media-queries--cms-23919
96
+ *
97
+ * @param type $email
98
+ * @return type
99
+ */
100
+ static function get_html_open($email) {
101
+ $open = "<!DOCTYPE html>\n";
102
+ $open .= "<html xmlns=\"https://www.w3.org/1999/xhtml\" xmlns:o=\"urn:schemas-microsoft-com:office:office\">\n<head>\n<title>{email_subject}</title>\n";
103
+ $open .= "<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n";
104
+
105
+ $open .= '<!--[if !mso]><!-->' . "\n";
106
+ $open .= '<meta http-equiv="X-UA-Compatible" content="IE=edge" />' . "\n";
107
+ $open .= '<!--<![endif]-->' . "\n";
108
+
109
+ $open .= '<!--[if mso]>' . "\n";
110
+ ;
111
+ $open .= '<style type="text/css">';
112
+ $open .= 'table {border-collapse:collapse;border-spacing:0;margin:0;}';
113
+ $open .= 'div, td {padding:0;}';
114
+ $open .= 'div {margin:0 !important;}';
115
+ $open .= '</style>';
116
+ $open .= "\n";
117
+ $open .= '<noscript>';
118
+ $open .= '<xml>';
119
+ $open .= '<o:OfficeDocumentSettings>';
120
+ $open .= '<o:PixelsPerInch>96</o:PixelsPerInch>';
121
+ $open .= '</o:OfficeDocumentSettings>';
122
+ $open .= '</xml>';
123
+ $open .= '</noscript>';
124
+ $open .= "\n";
125
+ $open .= '<![endif]-->';
126
+ $open .= "\n";
127
+ $open .= "<style type=\"text/css\">\n";
128
+ $open .= NewsletterEmails::instance()->get_composer_css();
129
+ $open .= "\n</style>\n";
130
+ $open .= "</head>\n";
131
+ $open .= '<body style="margin: 0; padding: 0; line-height: normal; word-spacing: normal;" dir="' . (is_rtl() ? 'rtl' : 'ltr') . '">';
132
+ $open .= "\n";
133
+ $open .= self::get_html_preheader($email);
134
+
135
+ return $open;
136
+ }
137
+
138
+ static private function get_html_preheader($email) {
139
+
140
+ if (empty($email->options['preheader'])) {
141
+ return "";
142
+ }
143
+
144
+ $preheader_text = $email->options['preheader'];
145
+ $html = "<div style=\"display:none;font-size:1px;color:#ffffff;line-height:1px;max-height:0px;max-width:0px;opacity:0;overflow:hidden;\">$preheader_text</div>";
146
+ $html .= "\n";
147
+
148
+ return $html;
149
+ }
150
+
151
+ static function get_html_close($email) {
152
+ return "</body>\n</html>";
153
+ }
154
+
155
+ /**
156
+ *
157
+ * @param TNP_Email $email
158
+ * @return string
159
+ */
160
+ static function get_main_wrapper_open($email) {
161
+ if (!isset($email->options['composer_background']) || $email->options['composer_background'] == 'inherit') {
162
+ $bgcolor = '';
163
+ } else {
164
+ $bgcolor = $email->options['composer_background'];
165
+ }
166
+
167
+ return "\n<table cellpadding='0' cellspacing='0' border='0' width='100%'>\n" .
168
+ "<tr>\n" .
169
+ "<td bgcolor='$bgcolor' valign='top'><!-- tnp -->";
170
+ }
171
+
172
+ /**
173
+ *
174
+ * @param TNP_Email $email
175
+ * @return string
176
+ */
177
+ static function get_main_wrapper_close($email) {
178
+ return "\n<!-- /tnp -->\n" .
179
+ "</td>\n" .
180
+ "</tr>\n" .
181
+ "</table>\n\n";
182
+ }
183
+
184
+ /**
185
+ * Remove <doctype>, <body> and unnecessary envelopes for editing with composer
186
+ *
187
+ * @param string $html_email
188
+ *
189
+ * @return string
190
+ */
191
+ static function unwrap_email($html_email) {
192
+
193
+ if (self::_has_markers($html_email)) {
194
+ $html_email = self::unwrap_html_element($html_email);
195
+ } else {
196
+ //KEEP FOR OLD EMAIL COMPATIBILITY
197
+ // Extracts only the body part
198
+ $x = strpos($html_email, '<body');
199
+ if ($x) {
200
+ $x = strpos($html_email, '>', $x);
201
+ $y = strpos($html_email, '</body>');
202
+ $html_email = substr($html_email, $x + 1, $y - $x - 1);
203
+ }
204
+
205
+ /* Cleans up uncorrectly stored newsletter bodies */
206
+ $html_email = preg_replace('/<style\s+.*?>.*?<\\/style>/is', '', $html_email);
207
+ $html_email = preg_replace('/<meta.*?>/', '', $html_email);
208
+ $html_email = preg_replace('/<title\s+.*?>.*?<\\/title>/i', '', $html_email);
209
+ $html_email = trim($html_email);
210
+ }
211
+
212
+ // Required since esc_html DOES NOT escape the HTML entities (apparently)
213
+ $html_email = str_replace('&', '&amp;', $html_email);
214
+ $html_email = str_replace('"', '&quot;', $html_email);
215
+ $html_email = str_replace('<', '&lt;', $html_email);
216
+ $html_email = str_replace('>', '&gt;', $html_email);
217
+
218
+ return $html_email;
219
+ }
220
+
221
+ private static function _escape_markers(&$markers) {
222
+ $markers[0] = str_replace('/', '\/', $markers[0]);
223
+ $markers[1] = str_replace('/', '\/', $markers[1]);
224
+ }
225
+
226
+ /**
227
+ * Using the data collected inside $controls (and submitted by a form containing the
228
+ * composer fields), updates the email. The message body is completed with doctype,
229
+ * head, style and the main wrapper.
230
+ *
231
+ * @param TNP_Email $email
232
+ * @param NewsletterControls $controls
233
+ */
234
+ static function update_email($email, $controls) {
235
+ if (isset($controls->data['subject'])) {
236
+ $email->subject = $controls->data['subject'];
237
+ }
238
+
239
+ // They should be only composer options
240
+ foreach ($controls->data as $name => $value) {
241
+ if (strpos($name, 'options_') === 0) {
242
+ $email->options[substr($name, 8)] = $value;
243
+ }
244
+ }
245
+
246
+ //if (isset($controls->data['preheader'])) {
247
+ // $email->options['preheader'] = $controls->data['preheader'];
248
+ //}
249
+
250
+ $email->editor = NewsletterEmails::EDITOR_COMPOSER;
251
+
252
+ $email->message = self::get_html_open($email) . self::get_main_wrapper_open($email) .
253
+ $controls->data['message'] . self::get_main_wrapper_close($email) . self::get_html_close($email);
254
+ }
255
+
256
+ /**
257
+ * Prepares a controls object injecting the relevant fields from an email
258
+ * which cannot be directly used by controls. If $email is null or missing,
259
+ * $controls is prepared with default values.
260
+ *
261
+ * @param NewsletterControls $controls
262
+ * @param TNP_Email $email
263
+ */
264
+ static function prepare_controls($controls, $email = null) {
265
+
266
+ // Controls for a new email (which actually does not exist yet
267
+ if (!empty($email)) {
268
+
269
+ foreach ($email->options as $name => $value) {
270
+ $controls->data['options_' . $name] = $value;
271
+ }
272
+
273
+ $controls->data['message'] = TNP_Composer::unwrap_email($email->message);
274
+ $controls->data['subject'] = $email->subject;
275
+ }
276
+
277
+ if (!empty($email->options['sender_email'])) {
278
+ $controls->data['sender_email'] = $email->options['sender_email'];
279
+ } else {
280
+ $controls->data['sender_email'] = Newsletter::instance()->options['sender_email'];
281
+ }
282
+
283
+ if (!empty($email->options['sender_name'])) {
284
+ $controls->data['sender_name'] = $email->options['sender_name'];
285
+ } else {
286
+ $controls->data['sender_name'] = Newsletter::instance()->options['sender_name'];
287
+ }
288
+
289
+ $controls->data = array_merge(TNP_Composer::get_global_style_defaults(), $controls->data);
290
+ }
291
+
292
+ /**
293
+ * Extract inline edited post field from inline_edit_list[]
294
+ *
295
+ * @param array $inline_edit_list
296
+ * @param string $field_type
297
+ * @param int $post_id
298
+ *
299
+ * @return string
300
+ */
301
+ static function get_edited_inline_post_field($inline_edit_list, $field_type, $post_id) {
302
+
303
+ foreach ($inline_edit_list as $edit) {
304
+ if ($edit['type'] == $field_type && $edit['post_id'] == $post_id) {
305
+ return $edit['content'];
306
+ }
307
+ }
308
+
309
+ return '';
310
+ }
311
+
312
+ /**
313
+ * Check if inline_edit_list[] have inline edit field for specific post
314
+ *
315
+ * @param array $inline_edit_list
316
+ * @param string $field_type
317
+ * @param int $post_id
318
+ *
319
+ * @return bool
320
+ */
321
+ static function is_post_field_edited_inline($inline_edit_list, $field_type, $post_id) {
322
+ if (empty($inline_edit_list) || !is_array($inline_edit_list)) {
323
+ return false;
324
+ }
325
+ foreach ($inline_edit_list as $edit) {
326
+ if ($edit['type'] == $field_type && $edit['post_id'] == $post_id) {
327
+ return true;
328
+ }
329
+ }
330
+
331
+ return false;
332
+ }
333
+
334
+ /**
335
+ * Creates the HTML for a button extrating from the options, with the provided prefix, the button attributes:
336
+ *
337
+ * - [prefix]_url The button URL
338
+ * - [prefix]_font_family
339
+ * - [prefix]_font_size
340
+ * - [prefix]_font_weight
341
+ * - [prefix]_label
342
+ * - [prefix]_font_color The label color
343
+ * - [prefix]_background The button color
344
+ *
345
+ * TODO: Add radius and possiblt the alignment
346
+ *
347
+ * @param array $options
348
+ * @param string $prefix
349
+ * @return string
350
+ */
351
+ static function button($options, $prefix = 'button') {
352
+
353
+ if (empty($options[$prefix . '_label'])) {
354
+ return;
355
+ }
356
+ $defaults = [
357
+ $prefix . '_url' => '#',
358
+ $prefix . '_font_family' => 'Helvetica, Arial, sans-serif',
359
+ $prefix . '_font_color' => '#ffffff',
360
+ $prefix . '_font_weight' => 'bold',
361
+ $prefix . '_font_size' => 20,
362
+ $prefix . '_background' => '#256F9C',
363
+ $prefix . '_align' => 'center'
364
+ ];
365
+
366
+ $options = array_merge($defaults, array_filter($options));
367
+
368
+ $b = '<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="margin: 0 auto"';
369
+ if (!empty($options[$prefix . '_align'])) {
370
+ $b .= ' align="' . esc_attr($options[$prefix . '_align']) . '"';
371
+ }
372
+ if (!empty($options[$prefix . '_width'])) {
373
+ $b .= ' width="' . esc_attr($options[$prefix . '_width']) . '"';
374
+ }
375
+ $b .= '>';
376
+ $b .= '<tr>';
377
+ $b .= '<td align="center" bgcolor="' . $options[$prefix . '_background'] . '" role="presentation" style="border:none;border-radius:3px;cursor:auto;mso-padding-alt:10px 25px;background:' . $options[$prefix . '_background'] . '" valign="middle">';
378
+ $b .= '<a href="' . $options[$prefix . '_url'] . '"';
379
+ $b .= ' style="display:inline-block;background:' . $options[$prefix . '_background'] . ';color:' . $options[$prefix . '_font_color'] . ';font-family:' . $options[$prefix . '_font_family'] . ';font-size:' . $options[$prefix . '_font_size'] . 'px;font-weight:' . $options[$prefix . '_font_weight'] . ';line-height:120%;margin:0;text-decoration:none;text-transform:none;padding:10px 25px;mso-padding-alt:0px;border-radius:3px;"';
380
+ $b .= ' target="_blank">';
381
+ $b .= $options[$prefix . '_label'];
382
+ $b .= '</a>';
383
+ $b .= '</td></tr></table>';
384
+ return $b;
385
+ }
386
+
387
+ /**
388
+ * Generates an IMG tag, linked if the media has an URL.
389
+ *
390
+ * @param TNP_Media $media
391
+ * @param string $style
392
+ * @return string
393
+ */
394
+ static function image($media, $attr = []) {
395
+
396
+ $default_attrs = [
397
+ 'style' => 'max-width: 100%; height: auto; display: inline-block',
398
+ 'class' => '',
399
+ 'link-style' => 'text-decoration: none; display: inline-block',
400
+ 'link-class' => null,
401
+ ];
402
+
403
+ $attr = array_merge($default_attrs, $attr);
404
+
405
+ //Class and style attribute are mutually exclusive.
406
+ //Class take priority to style because classes will transform to inline style inside block rendering operation
407
+ if (!empty($attr['inline-class'])) {
408
+ $styling = ' inline-class="' . esc_attr($attr['inline-class']) . '" ';
409
+ } else {
410
+ $styling = ' style="' . esc_attr($attr['style']) . '" ';
411
+ }
412
+
413
+ if (!empty($attr['class'])) {
414
+ $styling .= ' class="' . esc_attr($attr['class']) . '" ';
415
+ }
416
+
417
+ //Class and style attribute are mutually exclusive.
418
+ //Class take priority to style because classes will transform to inline style inside block rendering operation
419
+ if (!empty($attr['link-class'])) {
420
+ $link_styling = ' inline-class="' . esc_attr($attr['link-class']) . '" ';
421
+ } else {
422
+ $link_styling = ' style="' . esc_attr($attr['link-style']) . '" ';
423
+ }
424
+
425
+ $b = '';
426
+ if ($media->link) {
427
+ $b .= '<a href="' . esc_attr($media->link) . '" target="_blank" rel="noopener nofollow" style="display: inline-block; font-size: 0; text-decoration: none; line-height: normal!important">';
428
+ } else {
429
+ // The span grants images are not upscaled when fluid (two columns posts block)
430
+ $b .= '<span style="display: inline-block; font-size: 0; text-decoration: none; line-height: normal!important">';
431
+ }
432
+ if ($media) {
433
+ $b .= '<img src="' . esc_attr($media->url) . '" width="' . esc_attr($media->width) . '"';
434
+ if ($media->height) {
435
+ $b .= ' height="' . esc_attr($media->height) . '"';
436
+ }
437
+ $b .= ' alt="' . esc_attr($media->alt) . '"'
438
+ . ' border="0"'
439
+ . ' style="display: inline-block; max-width: 100%!important; padding: 0; border: 0;"'
440
+ . ' class="' . esc_attr($attr['class']) . '" '
441
+ . '>';
442
+ }
443
+
444
+ if ($media->link) {
445
+ $b .= '</a>';
446
+ } else {
447
+ $b .= '</span>';
448
+ }
449
+
450
+ return $b;
451
+ }
452
+
453
+ /**
454
+ * Returns a WP media ID for the specified post (or false if nothing can be found)
455
+ * looking for the featured image or, if missing, taking the first media in the gallery and
456
+ * if again missing, searching the first reference to a media in the post content.
457
+ *
458
+ * The media ID is not checked for real existance of the associated attachment.
459
+ *
460
+ * @param int $post_id
461
+ * @return int
462
+ */
463
+ static function get_post_thumbnail_id($post_id) {
464
+ if (is_object($post_id)) {
465
+ $post_id = $post_id->ID;
466
+ }
467
+
468
+ // Find a media id to be used as featured image
469
+ $media_id = get_post_thumbnail_id($post_id);
470
+ if (!empty($media_id)) {
471
+ return $media_id;
472
+ }
473
+
474
+ $attachments = get_children(array('numberpost' => 1, 'post_parent' => $post_id, 'post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => 'image', 'order' => 'ASC', 'orderby' => 'menu_order'));
475
+ if (!empty($attachments)) {
476
+ foreach ($attachments as $id => &$attachment) {
477
+ return $id;
478
+ }
479
+ }
480
+
481
+ $post = get_post($post_id);
482
+
483
+ $r = preg_match('/wp-image-(\d+)/', $post->post_content, $matches);
484
+ if ($matches) {
485
+ return (int) $matches[1];
486
+ }
487
+
488
+ return false;
489
+ }
490
+
491
+ /**
492
+ * Builds a TNP_Media object to be used in newsletters from a WP media/attachement ID. The returned
493
+ * media has a size which best match the one requested (this is the standard WP behavior, plugins
494
+ * could change it).
495
+ *
496
+ * @param int $media_id
497
+ * @param array $size
498
+ * @return \TNP_Media
499
+ */
500
+ function get_media($media_id, $size) {
501
+ $src = wp_get_attachment_image_src($media_id, $size);
502
+ if (!$src) {
503
+ return null;
504
+ }
505
+ $media = new TNP_Media();
506
+ $media->id = $media_id;
507
+ $media->url = $src[0];
508
+ $media->width = $src[1];
509
+ $media->height = $src[2];
510
+ return $media;
511
+ }
512
+
513
+ static function post_content($post) {
514
+ $content = $post->post_content;
515
+ $content = wpautop($content);
516
+ if (true || $options['enable shortcodes']) {
517
+ remove_shortcode('gallery');
518
+ add_shortcode('gallery', 'tnp_gallery_shortcode');
519
+ $content = do_shortcode($content);
520
+ }
521
+ $content = str_replace('<p>', '<p class="paragraph">', $content);
522
+
523
+ $selected_images = array();
524
+ if (preg_match_all('/<img [^>]+>/', $content, $matches)) {
525
+ foreach ($matches[0] as $image) {
526
+ if (preg_match('/wp-image-([0-9]+)/i', $image, $class_id) && ( $attachment_id = absint($class_id[1]) )) {
527
+ $selected_images[$image] = $attachment_id;
528
+ }
529
+ }
530
+ }
531
+
532
+ foreach ($selected_images as $image => $attachment_id) {
533
+ $src = tnp_media_resize($attachment_id, array(600, 0));
534
+ if (is_wp_error($src)) {
535
+ continue;
536
+ }
537
+ $content = str_replace($image, '<img src="' . $src . '" width="600" style="max-width: 100%">', $content);
538
+ }
539
+
540
+ return $content;
541
+ }
542
+
543
+ static function get_global_style_defaults() {
544
+ return [
545
+ 'options_composer_title_font_family' => 'Verdana, Geneva, sans-serif',
546
+ 'options_composer_title_font_size' => 32,
547
+ 'options_composer_title_font_weight' => 'normal',
548
+ 'options_composer_title_font_color' => '#222222',
549
+ 'options_composer_text_font_family' => 'Verdana, Geneva, sans-serif',
550
+ 'options_composer_text_font_size' => 16,
551
+ 'options_composer_text_font_weight' => 'normal',
552
+ 'options_composer_text_font_color' => '#222222',
553
+ 'options_composer_button_font_family' => 'Verdana, Geneva, sans-serif',
554
+ 'options_composer_button_font_size' => 16,
555
+ 'options_composer_button_font_weight' => 'normal',
556
+ 'options_composer_button_font_color' => '#FFFFFF',
557
+ 'options_composer_button_background_color' => '#256F9C',
558
+ 'options_composer_background' => '#FFFFFF',
559
+ 'options_composer_block_background' => '#FFFFFF',
560
+ ];
561
+ }
562
+
563
+ /**
564
+ * Inspired by: https://webdesign.tutsplus.com/tutorials/creating-a-future-proof-responsive-email-without-media-queries--cms-23919
565
+ * @param string[] $items
566
+ * @param array $attrs
567
+ * @return string
568
+ */
569
+ static function grid($items = [], $attrs = []) {
570
+ $attrs = wp_parse_args($attrs, ['width' => 600, 'columns' => 2, 'padding' => 10, 'responsive' => true]);
571
+ $width = (int) $attrs['width'];
572
+ $columns = (int) $attrs['columns'];
573
+ $padding = (int) $attrs['padding'];
574
+ $column_width = $width / $columns;
575
+ $td_width = 100 / $columns;
576
+ $chunks = array_chunk($items, $columns);
577
+
578
+ if ($attrs['responsive']) {
579
+
580
+ $e = '<div style="text-align:center;font-size:0;">';
581
+ foreach ($chunks as &$chunk) {
582
+
583
+ $e .= '<!--[if mso]><table role="presentation" width="100%"><tr><![endif]-->';
584
+ foreach ($chunk as &$item) {
585
+ $e .= '<!--[if mso]><td width="' . $td_width . '%" style="width:' . $td_width . '%;padding:' . $padding . 'px" valign="top"><![endif]-->';
586
+
587
+ $e .= '<div class="max-width-100" style="width:100%;max-width:' . $column_width . 'px;display:inline-block;vertical-align: top;box-sizing: border-box;">';
588
+
589
+ // This element to add padding without deal with border-box not well supported
590
+ $e .= '<div style="padding:' . $padding . 'px;">';
591
+ $e .= $item;
592
+ $e .= '</div>';
593
+ $e .= '</div>';
594
+
595
+ $e .= '<!--[if mso]></td><![endif]-->';
596
+ }
597
+ $e .= '<!--[if mso]></tr></table><![endif]-->';
598
+ $e .= '</div>';
599
+ }
600
+ return $e;
601
+ } else {
602
+ $e = '<table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0" style="width: 100%; max-width: 100%!important">';
603
+ foreach ($chunks as &$chunk) {
604
+ $e .= '<tr>';
605
+ foreach ($chunk as &$item) {
606
+ $e .= '<td width="' . $td_width . '%" style="width:' . $td_width . '%; padding:' . $padding . 'px" valign="top">';
607
+ $e .= $item;
608
+ $e .= '</td>';
609
+ }
610
+ $e .= '</tr>';
611
+ }
612
+ $e .= '</table>';
613
+ return $e;
614
+ }
615
+ }
616
+
617
+ static function get_text_style($options, $prefix, $composer, $attrs = []) {
618
+ return self::get_style($options, $prefix, $composer, 'text', $attrs);
619
+ }
620
+
621
+ static function get_title_style($options, $prefix, $composer, $attrs = []) {
622
+ return self::get_style($options, $prefix, $composer, 'title', $attrs);
623
+ }
624
+
625
+ static function get_style($options, $prefix, $composer, $type = 'text', $attrs = []) {
626
+ $style = new TNP_Style();
627
+ $scale = 1.0;
628
+ if (!empty($attrs['scale'])) {
629
+ $scale = (float) $attrs['scale'];
630
+ }
631
+ if (!empty($prefix))
632
+ $prefix .= '_';
633
+
634
+ $style->font_family = empty($options[$prefix . 'font_family']) ? $composer[$type . '_font_family'] : $options[$prefix . 'font_family'];
635
+ $style->font_size = empty($options[$prefix . 'font_size']) ? round($composer[$type . '_font_size'] * $scale) : $options[$prefix . 'font_size'];
636
+ $style->font_color = empty($options[$prefix . 'font_color']) ? $composer[$type . '_font_color'] : $options[$prefix . 'font_color'];
637
+ $style->font_weight = empty($options[$prefix . 'font_weight']) ? $composer[$type . '_font_weight'] : $options[$prefix . 'font_weight'];
638
+ if ($type === 'button') {
639
+ $style->background = empty($options[$prefix . 'background']) ? $composer[$type . '_background_color'] : $options[$prefix . 'background'];
640
+ }
641
+ return $style;
642
+ }
643
+
644
+ }
645
+
646
+ class TNP_Style {
647
+
648
+ var $font_family;
649
+ var $font_size;
650
+ var $font_weight;
651
+ var $font_color;
652
+ var $background;
653
+
654
+ function echo_css($scale = 1.0) {
655
+ echo 'font-size: ', round($this->font_size * $scale), 'px;';
656
+ echo 'font-family: ', $this->font_family, ';';
657
+ echo 'font-weight: ', $this->font_weight, ';';
658
+ echo 'color: ', $this->font_color, ';';
659
+ }
660
+
661
+ }
662
+
663
+ /**
664
+ * Generate multicolumn and responsive html template for email.
665
+ * Initialize class with max columns per row and start to add cells.
666
+ */
667
+ class TNP_Composer_Grid_System {
668
+
669
+ /**
670
+ * @var TNP_Composer_Grid_Row[]
671
+ */
672
+ private $rows;
673
+
674
+ /**
675
+ * @var int
676
+ */
677
+ private $cells_per_row;
678
+
679
+ /**
680
+ * @var int
681
+ */
682
+ private $cells_counter;
683
+
684
+ /**
685
+ * TNP_Composer_Grid_System constructor.
686
+ *
687
+ * @param int $columns_per_row Max columns per row
688
+ */
689
+ public function __construct($columns_per_row) {
690
+ $this->cells_per_row = $columns_per_row;
691
+ $this->cells_counter = 0;
692
+ $this->rows = [];
693
+ }
694
+
695
+ public function __toString() {
696
+ return $this->render();
697
+ }
698
+
699
+ /**
700
+ * Add cell to grid
701
+ *
702
+ * @param TNP_Composer_Grid_Cell $cell
703
+ */
704
+ public function add_cell($cell) {
705
+
706
+ if ($this->cells_counter % $this->cells_per_row === 0) {
707
+ $this->add_row(new TNP_Composer_Grid_Row());
708
+ }
709
+
710
+ $row_idx = (int) floor($this->cells_counter / $this->cells_per_row);
711
+ $this->rows[$row_idx]->add_cell($cell);
712
+ $this->cells_counter++;
713
+ }
714
+
715
+ private function add_row($row) {
716
+ $this->rows[] = $row;
717
+ }
718
+
719
+ public function render() {
720
+
721
+ $str = '';
722
+ foreach ($this->rows as $row) {
723
+ $str .= $row->render();
724
+ }
725
+
726
+ return $str;
727
+ }
728
+
729
+ }
730
+
731
+ /**
732
+ * Class TNP_Composer_Grid_Row
733
+ */
734
+ class TNP_Composer_Grid_Row {
735
+
736
+ /**
737
+ * @var TNP_Composer_Grid_Cell[]
738
+ */
739
+ private $cells;
740
+
741
+ public function __construct(...$cells) {
742
+ if (!empty($cells)) {
743
+ foreach ($cells as $cell) {
744
+ $this->add_cell($cell);
745
+ }
746
+ }
747
+ }
748
+
749
+ /**
750
+ * @param TNP_Composer_Grid_Cell $cell
751
+ */
752
+ public function add_cell($cell) {
753
+ $this->cells[] = $cell;
754
+ }
755
+
756
+ public function render() {
757
+ $rendered_cells = '';
758
+ $column_percentage_width = round(100 / $this->cells_count(), 0, PHP_ROUND_HALF_DOWN) . '%';
759
+ foreach ($this->cells as $cell) {
760
+ $rendered_cells .= $cell->render(['width' => $column_percentage_width]);
761
+ }
762
+
763
+ $row_template = $this->get_template();
764
+
765
+ return str_replace('TNP_ROW_CONTENT_PH', $rendered_cells, $row_template);
766
+ }
767
+
768
+ private function cells_count() {
769
+ return count($this->cells);
770
+ }
771
+
772
+ private function get_template() {
773
+ return "<table border='0' cellpadding='0' cellspacing='0' width='100%'><tbody><tr><td>TNP_ROW_CONTENT_PH</td></tr></tbody></table>";
774
+ }
775
+
776
+ }
777
+
778
+ /**
779
+ * Class TNP_Composer_Grid_Cell
780
+ */
781
+ class TNP_Composer_Grid_Cell {
782
+
783
+ /**
784
+ * @var string
785
+ */
786
+ private $content;
787
+
788
+ /**
789
+ * @var array
790
+ */
791
+ public $args;
792
+
793
+ public function __construct($content = null, $args = []) {
794
+ $default_args = [
795
+ 'width' => '100%',
796
+ 'class' => '',
797
+ 'align' => 'left',
798
+ 'valign' => 'top'
799
+ ];
800
+
801
+ $this->args = array_merge($default_args, $args);
802
+
803
+ $this->content = $content ? $content : '';
804
+ }
805
+
806
+ public function add_content($content) {
807
+ $this->content .= $content;
808
+ }
809
+
810
+ public function render($args) {
811
+ $this->args = array_merge($this->args, $args);
812
+
813
+ $column_template = $this->get_template();
814
+ $column = str_replace(
815
+ [
816
+ 'TNP_ALIGN_PH',
817
+ 'TNP_VALIGN_PH',
818
+ 'TNP_WIDTH_PH',
819
+ 'TNP_CLASS_PH',
820
+ 'TNP_COLUMN_CONTENT_PH'
821
+ ], [
822
+ $this->args['align'],
823
+ $this->args['valign'],
824
+ $this->args['width'],
825
+ $this->args['class'],
826
+ $this->content
827
+ ], $column_template);
828
+
829
+ return $column;
830
+ }
831
+
832
+ private function get_template() {
833
+ return "<table border='0' cellpadding='0' cellspacing='0' width='TNP_WIDTH_PH' align='left' style='table-layout: fixed;' class='responsive'>
834
+ <tbody>
835
+ <tr>
836
+ <td border='0' style='padding: 20px 10px 40px;' align='TNP_ALIGN_PH' valign='TNP_VALIGN_PH' class='TNP_CLASS_PH'>
837
+ TNP_COLUMN_CONTENT_PH
838
+ </td>
839
+ </tr>
840
+ </tbody>
841
+ </table>";
842
+ }
843
+
844
+ }
845
+
846
+ class TNP_Composer_Component_Factory {
847
+
848
+ private $options;
849
+
850
+ /**
851
+ * TNP_Composer_Component_Factory constructor.
852
+ *
853
+ * @param Controller$controller
854
+ */
855
+ public function __construct($controller) {
856
+
857
+ }
858
+
859
+ function heading() {
860
+
861
+ }
862
+
863
+ function paragraph() {
864
+
865
+ }
866
+
867
+ function link() {
868
+
869
+ }
870
+
871
+ function button() {
872
+
873
+ }
874
+
875
+ function image() {
876
+
877
+ }
878
+
879
+ }
includes/controls.php CHANGED
@@ -1044,11 +1044,8 @@ class NewsletterControls {
1044
  echo '<button class="button-primary" onclick="this.form.act.value=\'' . esc_attr($action) . '\'; return true;"/>', $label, '</button>';
1045
  }
1046
  }
1047
-
1048
- /**
1049
- * @deprecated
1050
- */
1051
- function button_confirm($action, $label, $message = '', $data = '') {
1052
  $this->btn($action, $label, ['data' => $data, 'confirm' => $message]);
1053
  }
1054
 
1044
  echo '<button class="button-primary" onclick="this.form.act.value=\'' . esc_attr($action) . '\'; return true;"/>', $label, '</button>';
1045
  }
1046
  }
1047
+
1048
+ function button_confirm($action, $label, $message = true, $data = '') {
 
 
 
1049
  $this->btn($action, $label, ['data' => $data, 'confirm' => $message]);
1050
  }
1051
 
includes/helper.php CHANGED
@@ -1,352 +1,395 @@
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) {
52
- $excerpt = tnp_delete_all_shordcodes_tags(get_the_excerpt($post->ID));
53
- $excerpt = wp_trim_words($excerpt, $length);
54
-
55
- return $excerpt;
56
- }
57
-
58
- function tnp_delete_all_shordcodes_tags($post_content = '') {
59
- //Delete open tags
60
- $post_content = preg_replace("/\[[a-zA-Z0-9_-]*?(\s.*?)?\]/", '', $post_content);
61
- //Delete close tags
62
- $post_content = preg_replace("/\[\/[a-zA-Z0-9_-]*?\]/", '', $post_content);
63
-
64
- return $post_content;
65
- }
66
-
67
- function tnp_post_permalink($post) {
68
- return get_permalink($post->ID);
69
- }
70
-
71
- function tnp_post_content($post) {
72
- return $post->post_content;
73
- }
74
-
75
- function tnp_post_title($post) {
76
- return $post->post_title;
77
- }
78
-
79
- function tnp_post_date($post, $format = null) {
80
- if (empty($format)) {
81
- $format = get_option('date_format');
82
- }
83
- return mysql2date($format, $post->post_date);
84
- }
85
-
86
- /**
87
- * Tries to create a resized version of a media uploaded to the media library.
88
- * Returns an empty string if the media does not exists or generally if the attached file
89
- * cannot be found. If the resize fails for whatever reason, fall backs to the
90
- * standard image source returned by WP which is usually not exactly the
91
- * requested size.
92
- *
93
- * @param int $media_id
94
- * @param array $size
95
- * @return string
96
- */
97
- function tnp_media_resize($media_id, $size) {
98
- if (empty($media_id)) {
99
- return '';
100
- }
101
-
102
- $relative_file = get_post_meta($media_id, '_wp_attached_file', true);
103
- if (empty($relative_file)) {
104
- return '';
105
- }
106
-
107
- $width = $size[0];
108
- $height = $size[1];
109
- $crop = false;
110
- if (isset($size[2])) {
111
- $crop = (boolean) $size[2];
112
- }
113
-
114
- $uploads = wp_upload_dir();
115
-
116
- // Based on _wp_relative_upload_path() function for blog which store the
117
- // full patch of media files
118
- if (0 === strpos($relative_file, $uploads['basedir'])) {
119
- $relative_file = str_replace($uploads['basedir'], '', $relative_file);
120
- $relative_file = ltrim($relative_file, '/');
121
- }
122
-
123
- $absolute_file = $uploads['basedir'] . '/' . $relative_file;
124
- // Relative and absolute name of the thumbnail.
125
- $pathinfo = pathinfo($relative_file);
126
- $relative_thumb = $pathinfo['dirname'] . '/' . $pathinfo['filename'] . '-' . $width . 'x' .
127
- $height . ($crop ? '-c' : '') . '.' . $pathinfo['extension'];
128
- $absolute_thumb = $uploads['basedir'] . '/newsletter/thumbnails/' . $relative_thumb;
129
-
130
- // Thumbnail generation if needed.
131
- if (!file_exists($absolute_thumb) || filemtime($absolute_thumb) < filemtime($absolute_file)) {
132
- $r = wp_mkdir_p($uploads['basedir'] . '/newsletter/thumbnails/' . $pathinfo['dirname']);
133
-
134
- if (!$r) {
135
- $src = wp_get_attachment_image_src($media_id, 'full');
136
- return $src[0];
137
- }
138
-
139
- $editor = wp_get_image_editor($absolute_file);
140
- if (is_wp_error($editor)) {
141
- $src = wp_get_attachment_image_src($media_id, 'full');
142
- return $src[0];
143
- //return $editor;
144
- //return $uploads['baseurl'] . '/' . $relative_file;
145
- }
146
-
147
- $original_size = $editor->get_size();
148
- if ($width > $original_size['width'] || $height > $original_size['height']) {
149
- $src = wp_get_attachment_image_src($media_id, 'full');
150
- return $src[0];
151
- }
152
-
153
- $editor->set_quality(80);
154
- $resized = $editor->resize($width, $height, $crop);
155
-
156
- if (is_wp_error($resized)) {
157
- $src = wp_get_attachment_image_src($media_id, 'full');
158
- return $src[0];
159
- }
160
-
161
- $saved = $editor->save($absolute_thumb);
162
- if (is_wp_error($saved)) {
163
- $src = wp_get_attachment_image_src($media_id, 'full');
164
- return $src[0];
165
- //return $saved;
166
- //return $uploads['baseurl'] . '/' . $relative_file;
167
- }
168
- }
169
-
170
- return $uploads['baseurl'] . '/newsletter/thumbnails/' . $relative_thumb;
171
- }
172
-
173
- function _tnp_get_default_media($media_id, $size) {
174
-
175
- $src = wp_get_attachment_image_src($media_id, $size);
176
- if (!$src) {
177
- return null;
178
- }
179
- $media = new TNP_Media();
180
- $media->id = $media_id;
181
- $media->url = $src[0];
182
- $media->width = $src[1];
183
- $media->height = $src[2];
184
- return $media;
185
- }
186
-
187
- function tnp_get_media($media_id, $size) {
188
- $src = wp_get_attachment_image_src($media_id, $size);
189
- if (!$src) {
190
- return null;
191
- }
192
- $media = new TNP_Media();
193
- $media->id = $media_id;
194
- $media->url = $src[0];
195
- $media->width = $src[1];
196
- $media->height = $src[2];
197
- return $media;
198
- }
199
-
200
- /**
201
- * Create a resized version of the media stored in the WP media library.
202
- *
203
- * @param int $media_id
204
- * @param array $size
205
- * @return TNP_Media
206
- */
207
- function tnp_resize($media_id, $size) {
208
- if (empty($media_id)) {
209
- return null;
210
- }
211
-
212
- $relative_file = get_post_meta($media_id, '_wp_attached_file', true);
213
-
214
- if (empty($relative_file)) {
215
- return null;
216
- }
217
-
218
- $uploads = wp_upload_dir();
219
-
220
- // Based on _wp_relative_upload_path() function for blog which store the
221
- // full patch of media files
222
- if (0 === strpos($relative_file, $uploads['basedir'])) {
223
- $relative_file = str_replace($uploads['basedir'], '', $relative_file);
224
- $relative_file = ltrim($relative_file, '/');
225
- }
226
-
227
- $width = $size[0];
228
- $height = $size[1];
229
- $crop = false;
230
- if (isset($size[2])) {
231
- $crop = (boolean) $size[2];
232
- }
233
-
234
- $absolute_file = $uploads['basedir'] . '/' . $relative_file;
235
-
236
- if (substr($relative_file, -4) === '.gif') {
237
- $editor = wp_get_image_editor($absolute_file);
238
- if (is_wp_error($editor)) {
239
- return _tnp_get_default_media($media_id, $size);
240
- }
241
- $new_size = $editor->get_size();
242
- $media = new TNP_Media();
243
- $media->id = $media_id;
244
- $media->width = $new_size['width'];
245
- $media->height = $new_size['height'];
246
- if ($media->width > $width) {
247
- $media->set_width($width);
248
- }
249
- $media->url = $uploads['baseurl'] . '/' . $relative_file;
250
- return $media;
251
- }
252
-
253
- // Relative and absolute name of the thumbnail.
254
- $pathinfo = pathinfo($relative_file);
255
- $relative_thumb = $pathinfo['dirname'] . '/' . $pathinfo['filename'] . '-' . $width . 'x' . $height . ($crop ? '-c' : '') . '.' . $pathinfo['extension'];
256
- $absolute_thumb = $uploads['basedir'] . '/newsletter/thumbnails/' . $relative_thumb;
257
-
258
- // Thumbnail generation if needed.
259
- if (!file_exists($absolute_thumb) || filemtime($absolute_thumb) < filemtime($absolute_file)) {
260
- $r = wp_mkdir_p($uploads['basedir'] . '/newsletter/thumbnails/' . $pathinfo['dirname']);
261
-
262
- if (!$r) {
263
- Newsletter::instance()->logger->error('Unable to create dir ' . $uploads['basedir'] . '/newsletter/thumbnails/' . $pathinfo['dirname']);
264
- return _tnp_get_default_media($media_id, $size);
265
- }
266
-
267
- $editor = wp_get_image_editor($absolute_file);
268
- if (is_wp_error($editor)) {
269
- Newsletter::instance()->logger->error($editor);
270
- Newsletter::instance()->logger->error('File: ' . $absolute_file);
271
- return _tnp_get_default_media($media_id, $size);
272
- }
273
-
274
- $original_size = $editor->get_size();
275
- if ($width > $original_size['width'] && $height > $original_size['height']) {
276
- Newsletter::instance()->logger->error('Requested size larger than the original one');
277
- return _tnp_get_default_media($media_id, $size);
278
- }
279
-
280
- $editor->set_quality(85);
281
- $resized = $editor->resize($width, $height, $crop);
282
-
283
- if (is_wp_error($resized)) {
284
- Newsletter::instance()->logger->error($resized);
285
- Newsletter::instance()->logger->error('File: ' . $absolute_file);
286
- return _tnp_get_default_media($media_id, $size);
287
- }
288
-
289
- $saved = $editor->save($absolute_thumb);
290
- if (is_wp_error($saved)) {
291
- Newsletter::instance()->logger->error($saved);
292
- return _tnp_get_default_media($media_id, $size);
293
- }
294
- $new_size = $editor->get_size();
295
-
296
- $media = new TNP_Media();
297
- $media->width = $new_size['width'];
298
- $media->height = $new_size['height'];
299
- $media->url = $uploads['baseurl'] . '/newsletter/thumbnails/' . $relative_thumb;
300
- } else {
301
- $media = new TNP_Media();
302
- $new_size = getimagesize($absolute_thumb);
303
- $media->width = $new_size[0];
304
- $media->height = $new_size[1];
305
- $media->url = $uploads['baseurl'] . '/newsletter/thumbnails/' . $relative_thumb;
306
- }
307
-
308
- return $media;
309
- }
310
-
311
- function tnp_resize_2x($media_id, $size) {
312
- $size[0] = $size[0] * 2;
313
- $size[1] = $size[1] * 2;
314
- $media = tnp_resize($media_id, $size);
315
- if (!$media) return $media;
316
- $media->set_width( $size[0] / 2 );
317
- return $media;
318
- }
319
-
320
- /**
321
- * Get media for "posts" composer block
322
- *
323
- * @param WP_Post post
324
- * @param array $size
325
- * @param string $default_image_url
326
- *
327
- * @return TNP_Media
328
- */
329
- function tnp_composer_block_posts_get_media($post, $size, $default_image_url = null) {
330
- $post_thumbnail_id = TNP_Composer::get_post_thumbnail_id($post);
331
-
332
- $media = null;
333
-
334
- if (!empty($post_thumbnail_id)) {
335
- $media = tnp_resize($post_thumbnail_id, array_values($size));
336
- } else if ($default_image_url) {
337
- Newsletter::instance()->logger->error('Thumbnail id not found');
338
- $media = new TNP_Media();
339
- $media->url = $default_image_url;
340
- $media->width = $size['width'];
341
- $media->height = $size['height'];
342
- }
343
- return $media;
344
- }
345
-
346
- function tnp_outlook_wrapper_open($width = 600) {
347
- return NewsletterEmails::get_outlook_wrapper_open($width);
348
- }
349
-
350
- function tnp_outlook_wrapper_close() {
351
- return NewsletterEmails::get_outlook_wrapper_close();
352
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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) {
52
+ $excerpt = tnp_delete_all_shordcodes_tags(get_the_excerpt($post->ID));
53
+ $excerpt = wp_trim_words($excerpt, $length);
54
+ $excerpt = str_replace('&nbsp;', '', $excerpt);
55
+
56
+ return $excerpt;
57
+ }
58
+
59
+ function tnp_delete_all_shordcodes_tags($post_content = '') {
60
+ //Delete open tags
61
+ $post_content = preg_replace("/\[[a-zA-Z0-9_-]*?(\s.*?)?\]/", '', $post_content);
62
+ //Delete close tags
63
+ $post_content = preg_replace("/\[\/[a-zA-Z0-9_-]*?\]/", '', $post_content);
64
+
65
+ return $post_content;
66
+ }
67
+
68
+ function tnp_post_permalink($post) {
69
+ return get_permalink($post->ID);
70
+ }
71
+
72
+ function tnp_post_content($post) {
73
+ return $post->post_content;
74
+ }
75
+
76
+ function tnp_post_title($post) {
77
+ return $post->post_title;
78
+ }
79
+
80
+ function tnp_post_date($post, $format = null) {
81
+ if (empty($format)) {
82
+ $format = get_option('date_format');
83
+ }
84
+ return mysql2date($format, $post->post_date);
85
+ }
86
+
87
+ /**
88
+ * Tries to create a resized version of a media uploaded to the media library.
89
+ * Returns an empty string if the media does not exists or generally if the attached file
90
+ * cannot be found. If the resize fails for whatever reason, fall backs to the
91
+ * standard image source returned by WP which is usually not exactly the
92
+ * requested size.
93
+ *
94
+ * @param int $media_id
95
+ * @param array $size
96
+ * @return string
97
+ */
98
+ function tnp_media_resize($media_id, $size) {
99
+ if (empty($media_id)) {
100
+ return '';
101
+ }
102
+
103
+ $relative_file = get_post_meta($media_id, '_wp_attached_file', true);
104
+ if (empty($relative_file)) {
105
+ return '';
106
+ }
107
+
108
+ $width = $size[0];
109
+ $height = $size[1];
110
+ $crop = false;
111
+ if (isset($size[2])) {
112
+ $crop = (boolean) $size[2];
113
+ }
114
+
115
+ $uploads = wp_upload_dir();
116
+
117
+ // Based on _wp_relative_upload_path() function for blog which store the
118
+ // full patch of media files
119
+ if (0 === strpos($relative_file, $uploads['basedir'])) {
120
+ $relative_file = str_replace($uploads['basedir'], '', $relative_file);
121
+ $relative_file = ltrim($relative_file, '/');
122
+ }
123
+
124
+ $absolute_file = $uploads['basedir'] . '/' . $relative_file;
125
+ // Relative and absolute name of the thumbnail.
126
+ $pathinfo = pathinfo($relative_file);
127
+
128
+ // We don't know why, but on some systems files with non-ascii characters loose the file name (grrr...)
129
+ if (empty($pathinfo['filename'])) {
130
+ $src = wp_get_attachment_image_src($media_id, 'full');
131
+ return $src[0];
132
+ }
133
+
134
+ $relative_thumb = $pathinfo['dirname'] . '/' . $pathinfo['filename'] . '-' . $width . 'x' .
135
+ $height . ($crop ? '-c' : '') . '.' . $pathinfo['extension'];
136
+ $absolute_thumb = $uploads['basedir'] . '/newsletter/thumbnails/' . $relative_thumb;
137
+
138
+ // Thumbnail generation if needed.
139
+ if (!file_exists($absolute_thumb) || filemtime($absolute_thumb) < filemtime($absolute_file)) {
140
+ $r = wp_mkdir_p($uploads['basedir'] . '/newsletter/thumbnails/' . $pathinfo['dirname']);
141
+
142
+ if (!$r) {
143
+ $src = wp_get_attachment_image_src($media_id, 'full');
144
+ return $src[0];
145
+ }
146
+
147
+ $editor = wp_get_image_editor($absolute_file);
148
+ if (is_wp_error($editor)) {
149
+ $src = wp_get_attachment_image_src($media_id, 'full');
150
+ return $src[0];
151
+ //return $editor;
152
+ //return $uploads['baseurl'] . '/' . $relative_file;
153
+ }
154
+
155
+ $original_size = $editor->get_size();
156
+ if ($width > $original_size['width'] || $height > $original_size['height']) {
157
+ $src = wp_get_attachment_image_src($media_id, 'full');
158
+ return $src[0];
159
+ }
160
+
161
+ $editor->set_quality(80);
162
+ $resized = $editor->resize($width, $height, $crop);
163
+
164
+ if (is_wp_error($resized)) {
165
+ $src = wp_get_attachment_image_src($media_id, 'full');
166
+ return $src[0];
167
+ }
168
+
169
+ $saved = $editor->save($absolute_thumb);
170
+ if (is_wp_error($saved)) {
171
+ $src = wp_get_attachment_image_src($media_id, 'full');
172
+ return $src[0];
173
+ //return $saved;
174
+ //return $uploads['baseurl'] . '/' . $relative_file;
175
+ }
176
+ }
177
+
178
+ return $uploads['baseurl'] . '/newsletter/thumbnails/' . $relative_thumb;
179
+ }
180
+
181
+ function _tnp_get_default_media($media_id, $size) {
182
+
183
+ $src = wp_get_attachment_image_src($media_id, $size);
184
+ if (!$src) {
185
+ return null;
186
+ }
187
+ $media = new TNP_Media();
188
+ $media->id = $media_id;
189
+ $media->url = $src[0];
190
+ $media->width = $src[1];
191
+ $media->height = $src[2];
192
+ return $media;
193
+ }
194
+
195
+ function tnp_get_media($media_id, $size) {
196
+ $src = wp_get_attachment_image_src($media_id, $size);
197
+ if (!$src) {
198
+ return null;
199
+ }
200
+ $media = new TNP_Media();
201
+ $media->id = $media_id;
202
+ $media->url = $src[0];
203
+ $media->width = $src[1];
204
+ $media->height = $src[2];
205
+ return $media;
206
+ }
207
+
208
+ /**
209
+ * Create a resized version of the media stored in the WP media library.
210
+ *
211
+ * @param int $media_id
212
+ * @param array $size
213
+ * @return TNP_Media
214
+ */
215
+ function tnp_resize($media_id, $size) {
216
+ if (empty($media_id)) {
217
+ return null;
218
+ }
219
+
220
+ $relative_file = get_post_meta($media_id, '_wp_attached_file', true);
221
+
222
+ if (empty($relative_file)) {
223
+ return null;
224
+ }
225
+
226
+ $uploads = wp_upload_dir();
227
+
228
+ // Based on _wp_relative_upload_path() function for blog which store the
229
+ // full patch of media files
230
+ if (0 === strpos($relative_file, $uploads['basedir'])) {
231
+ $relative_file = str_replace($uploads['basedir'], '', $relative_file);
232
+ $relative_file = ltrim($relative_file, '/');
233
+ }
234
+
235
+ $width = $size[0];
236
+ $height = $size[1];
237
+ $crop = false;
238
+ if (isset($size[2])) {
239
+ $crop = (boolean) $size[2];
240
+ }
241
+
242
+ $absolute_file = $uploads['basedir'] . '/' . $relative_file;
243
+
244
+ if (substr($relative_file, -4) === '.gif') {
245
+ $editor = wp_get_image_editor($absolute_file);
246
+ if (is_wp_error($editor)) {
247
+ return _tnp_get_default_media($media_id, $size);
248
+ }
249
+ $new_size = $editor->get_size();
250
+ $media = new TNP_Media();
251
+ $media->id = $media_id;
252
+ $media->width = $new_size['width'];
253
+ $media->height = $new_size['height'];
254
+ if ($media->width > $width) {
255
+ $media->set_width($width);
256
+ }
257
+ $media->url = $uploads['baseurl'] . '/' . $relative_file;
258
+ return $media;
259
+ }
260
+
261
+ // Relative and absolute name of the thumbnail.
262
+ $pathinfo = pathinfo($relative_file);
263
+
264
+ // We don't know why, but on some systems files with non-ascii characters loose the file name (grrr...)
265
+ if (empty($pathinfo['filename'])) {
266
+ return _tnp_get_default_media($media_id, $size);
267
+ }
268
+
269
+ $relative_thumb = $pathinfo['dirname'] . '/' . $pathinfo['filename'] . '-' . $width . 'x' . $height . ($crop ? '-c' : '') . '.' . $pathinfo['extension'];
270
+ $absolute_thumb = $uploads['basedir'] . '/newsletter/thumbnails/' . $relative_thumb;
271
+
272
+ // Thumbnail generation if needed.
273
+ if (!file_exists($absolute_thumb) || filemtime($absolute_thumb) < filemtime($absolute_file)) {
274
+ $r = wp_mkdir_p($uploads['basedir'] . '/newsletter/thumbnails/' . $pathinfo['dirname']);
275
+
276
+ if (!$r) {
277
+ Newsletter::instance()->logger->error('Unable to create dir ' . $uploads['basedir'] . '/newsletter/thumbnails/' . $pathinfo['dirname']);
278
+ return _tnp_get_default_media($media_id, $size);
279
+ }
280
+
281
+ $editor = wp_get_image_editor($absolute_file);
282
+ if (is_wp_error($editor)) {
283
+ Newsletter::instance()->logger->error($editor);
284
+ Newsletter::instance()->logger->error('File: ' . $absolute_file);
285
+ return _tnp_get_default_media($media_id, $size);
286
+ }
287
+
288
+ $original_size = $editor->get_size();
289
+ if ($width > $original_size['width'] && $height > $original_size['height']) {
290
+ Newsletter::instance()->logger->error('Requested size larger than the original one');
291
+ return _tnp_get_default_media($media_id, $size);
292
+ }
293
+
294
+ $editor->set_quality(85);
295
+ $resized = $editor->resize($width, $height, $crop);
296
+
297
+ if (is_wp_error($resized)) {
298
+ Newsletter::instance()->logger->error($resized);
299
+ Newsletter::instance()->logger->error('File: ' . $absolute_file);
300
+ return _tnp_get_default_media($media_id, $size);
301
+ }
302
+
303
+ $saved = $editor->save($absolute_thumb);
304
+ if (is_wp_error($saved)) {
305
+ Newsletter::instance()->logger->error($saved);
306
+ return _tnp_get_default_media($media_id, $size);
307
+ }
308
+ $new_size = $editor->get_size();
309
+
310
+ $media = new TNP_Media();
311
+ $media->width = $new_size['width'];
312
+ $media->height = $new_size['height'];
313
+ $media->url = $uploads['baseurl'] . '/newsletter/thumbnails/' . $relative_thumb;
314
+ } else {
315
+ $media = new TNP_Media();
316
+ $new_size = getimagesize($absolute_thumb);
317
+ $media->width = $new_size[0];
318
+ $media->height = $new_size[1];
319
+ $media->url = $uploads['baseurl'] . '/newsletter/thumbnails/' . $relative_thumb;
320
+ }
321
+
322
+ return $media;
323
+ }
324
+
325
+ function tnp_resize_2x($media_id, $size) {
326
+ $size[0] = $size[0] * 2;
327
+ $size[1] = $size[1] * 2;
328
+ $media = tnp_resize($media_id, $size);
329
+ if (!$media) return $media;
330
+ $media->set_width( $size[0] / 2 );
331
+ return $media;
332
+ }
333
+
334
+ /**
335
+ * @param TNP_Media[] $images
336
+ *
337
+ * @return int
338
+ */
339
+ function tnp_get_max_height_of( $images ) {
340
+ $max_height = 0;
341
+ foreach ( $images as $image ) {
342
+ $max_height = $image->height > $max_height ? $image->height : $max_height;
343
+ }
344
+
345
+ return $max_height;
346
+ }
347
+
348
+ /**
349
+ * @param WP_Post[] $product_list
350
+ * @param array $size
351
+ *
352
+ * @return TNP_Media[]
353
+ */
354
+ function tnp_resize_product_list_featured_image( $product_list, $size ) {
355
+ $images = [];
356
+ foreach ( $product_list as $p ) {
357
+ $images[ $p->ID ] = tnp_resize_2x( TNP_Composer::get_post_thumbnail_id( $p->ID ), $size );
358
+ }
359
+
360
+ return $images;
361
+ }
362
+
363
+ /**
364
+ * Get media for "posts" composer block
365
+ *
366
+ * @param WP_Post post
367
+ * @param array $size
368
+ * @param string $default_image_url
369
+ *
370
+ * @return TNP_Media
371
+ */
372
+ function tnp_composer_block_posts_get_media($post, $size, $default_image_url = null) {
373
+ $post_thumbnail_id = TNP_Composer::get_post_thumbnail_id($post);
374
+
375
+ $media = null;
376
+
377
+ if (!empty($post_thumbnail_id)) {
378
+ $media = tnp_resize($post_thumbnail_id, array_values($size));
379
+ } else if ($default_image_url) {
380
+ Newsletter::instance()->logger->error('Thumbnail id not found');
381
+ $media = new TNP_Media();
382
+ $media->url = $default_image_url;
383
+ $media->width = $size['width'];
384
+ $media->height = $size['height'];
385
+ }
386
+ return $media;
387
+ }
388
+
389
+ function tnp_outlook_wrapper_open($width = 600) {
390
+ return NewsletterEmails::get_outlook_wrapper_open($width);
391
+ }
392
+
393
+ function tnp_outlook_wrapper_close() {
394
+ return NewsletterEmails::get_outlook_wrapper_close();
395
+ }
includes/system.php CHANGED
@@ -1,263 +1,263 @@
1
- <?php
2
-
3
- defined('ABSPATH') || exit;
4
-
5
- class NewsletterSystem {
6
-
7
- static $instance;
8
-
9
- const JOB_OK = 0;
10
- const JOB_MISSING = 2;
11
- const JOB_LATE = 1;
12
- const JOB_SKIPPED = 3;
13
-
14
- /**
15
- * @return NewsletterSystem
16
- */
17
- static function instance() {
18
- if (self::$instance == null) {
19
- self::$instance = new NewsletterSystem();
20
- }
21
- return self::$instance;
22
- }
23
-
24
- function __construct() {
25
-
26
- }
27
-
28
- function reset_cron_stats() {
29
- update_option('newsletter_diagnostic_cron_calls', [], false);
30
- }
31
-
32
- /**
33
- *
34
- * @param type $calls
35
- * @return \TNP_Cron_Stats
36
- */
37
- function get_cron_stats() {
38
- $calls = get_option('newsletter_diagnostic_cron_calls', []);
39
-
40
- // Not enough data
41
- if (count($calls) < 12) {
42
- return null;
43
- }
44
-
45
- $stats = new TNP_Cron_Stats();
46
-
47
- for ($i = 1; $i < count($calls); $i++) {
48
- $delta = $calls[$i] - $calls[$i - 1];
49
- $stats->deltas[] = $delta;
50
- if ($stats->min > $delta) {
51
- $stats->min = $delta;
52
- }
53
- if ($stats->max < $delta) {
54
- $stats->max = $delta;
55
- }
56
- }
57
- $stats->avg = round(array_sum($stats->deltas) / count($stats->deltas));
58
- $stats->good = $stats->avg < NEWSLETTER_CRON_INTERVAL * 1.1; // 10% error
59
- return $stats;
60
- }
61
-
62
- function get_last_cron_call() {
63
- $calls = get_option('newsletter_diagnostic_cron_calls', []);
64
- if (empty($calls))
65
- return 0;
66
- return end($calls);
67
- }
68
-
69
- function has_newsletter_schedule() {
70
- $schedules = wp_get_schedules();
71
- if (!empty($schedules)) {
72
- foreach ($schedules as $key => $data) {
73
- if ($key == 'newsletter') {
74
- return true;
75
- }
76
- }
77
- }
78
- return false;
79
- }
80
-
81
- /**
82
- *
83
- * @return boolean|int
84
- */
85
- function get_job_delay() {
86
- $x = wp_next_scheduled('newsletter');
87
- return $x === false ? false : time() - $x;
88
- }
89
-
90
- function get_job_schedule() {
91
- return wp_next_scheduled('newsletter');
92
- }
93
-
94
- function get_cache_job_status($refresh = false) {
95
- $status = get_transient('newsletter_job_status');
96
- if ($status === false || $refresh) {
97
- $status = $this->get_job_status();
98
- set_transient('newsletter_job_status', $status, DAY_IN_SECONDS);
99
- }
100
- return $status;
101
- }
102
-
103
- function get_job_status() {
104
-
105
- $x = wp_next_scheduled('newsletter');
106
- if ($x === false) {
107
- return self::JOB_MISSING;
108
- }
109
-
110
- // Special case: the scheduler has been triggered but the job not executed
111
- $calls = get_option('newsletter_diagnostic_cron_calls', []);
112
- if (!empty($calls)) {
113
- $last = end($calls);
114
- if ($last > $x) {
115
- return self::JOB_SKIPPED;
116
- }
117
- }
118
-
119
- if (time() - $x > 900) {
120
- return self::JOB_LATE;
121
- }
122
- return self::JOB_OK;
123
- }
124
-
125
- function reset_send_stats() {
126
- update_option('newsletter_diagnostic_send_calls', [], false);
127
- }
128
-
129
- function get_send_stats() {
130
- // Send calls stats
131
- $send_calls = get_option('newsletter_diagnostic_send_calls', []);
132
- if (!$send_calls) {
133
- return null;
134
- }
135
- $send_max = 0;
136
- $send_min = PHP_INT_MAX;
137
- $send_total_time = 0;
138
- $send_total_emails = 0;
139
- $send_completed = 0;
140
- $stats = new TNP_Send_stats();
141
-
142
- // Batches
143
- for ($i = 0; $i < count($send_calls); $i++) {
144
- // 0 - batch start time
145
- // 1 - batch end time
146
- // 2 - number of sent email in this batch
147
- // 3 - 0: prematurely stopped, 1: completed
148
- if (empty($send_calls[$i][2])) {
149
- continue;
150
- }
151
- if ($send_calls[$i][2] <= 1) {
152
- continue;
153
- }
154
-
155
- $delta = $send_calls[$i][1] - $send_calls[$i][0];
156
- $send_total_time += $delta;
157
- $send_total_emails += $send_calls[$i][2];
158
-
159
- $send_mean = $delta / $send_calls[$i][2];
160
- $stats->means[] = $send_mean;
161
- $stats->sizes[] = $send_calls[$i][2];
162
- if ($send_min > $send_mean) {
163
- $send_min = $send_mean;
164
- }
165
- if ($send_max < $send_mean) {
166
- $send_max = $send_mean;
167
- }
168
- if (isset($send_calls[$i][3]) && $send_calls[$i][3]) {
169
- $send_completed++;
170
- }
171
- }
172
-
173
- if (empty($stats->means)) {
174
- return null;
175
- }
176
- $send_mean = $send_total_time / $send_total_emails;
177
-
178
- $stats->min = round($send_min, 2);
179
- $stats->max = round($send_max, 2);
180
- $stats->mean = round($send_mean, 2);
181
- $stats->total_time = round($send_total_time);
182
- $stats->total_emails = $send_total_emails;
183
- $stats->total_runs = count($stats->means);
184
-
185
- $stats->completed = $send_completed;
186
- $stats->interrupted = $stats->total_runs - $stats->completed;
187
- return $stats;
188
- }
189
-
190
- /**
191
- * Returns a list of functions attached to the prvoded filter or action name.
192
- *
193
- * @global array $wp_filter
194
- * @param string $tag
195
- * @return string
196
- */
197
- function get_hook_functions($tag) {
198
- global $wp_filter;
199
- if (isset($wp_filter[$tag])) {
200
- $b = '';
201
- foreach ($wp_filter[$tag]->callbacks as $priority => $functions) {
202
-
203
- foreach ($functions as $function) {
204
- $b .= '[' . $priority . '] ';
205
- if (is_array($function['function'])) {
206
- if (is_object($function['function'][0])) {
207
- $b .= get_class($function['function'][0]) . '::' . $function['function'][1];
208
- } else {
209
- $b .= $function['function'][0] . '::' . $function['function'][1];
210
- }
211
- } else {
212
- if (is_object($function['function'])) {
213
- $fn = new ReflectionFunction($function['function']);
214
- $b .= get_class($fn->getClosureThis()) . '(closure)';
215
- } else {
216
- $b .= $function['function'];
217
- }
218
- }
219
- $b .= "<br>";
220
- }
221
- }
222
- }
223
- return $b;
224
- }
225
-
226
- }
227
-
228
- class TNP_Cron_Stats {
229
-
230
- var $min = PHP_INT_MAX;
231
- var $max = 0;
232
- var $avg = 0;
233
- /* List of intervals between cron calls */
234
- var $deltas = [];
235
- /* If the cron is triggered enough often */
236
- var $good = true;
237
-
238
- }
239
-
240
- class TNP_Send_Stats {
241
-
242
- /* Emails sent */
243
- var $total_emails;
244
- /* Total batches collected (1 sized-batch by Autoresponder are ignored) */
245
- var $total_runs;
246
- /* Total sending time of the collected batches */
247
- var $total_time;
248
- /* Batch completed without timeout or max emails limit */
249
- var $completed;
250
- /* Batches interrupted due to reached limits */
251
- var $interrupted;
252
- /* Min time to send an email */
253
- var $min = PHP_INT_MAX;
254
- /* Max time to send an email */
255
- var $max = 0;
256
- /* Average time to send all collected emails */
257
- var $mean = 0;
258
- /* List of single batches average per email sending time */
259
- var $means = [];
260
- /* Number of emails in the single batches */
261
- var $sizes = [];
262
-
263
- }
1
+ <?php
2
+
3
+ defined('ABSPATH') || exit;
4
+
5
+ class NewsletterSystem {
6
+
7
+ static $instance;
8
+
9
+ const JOB_OK = 0;
10
+ const JOB_MISSING = 2;
11
+ const JOB_LATE = 1;
12
+ const JOB_SKIPPED = 3;
13
+
14
+ /**
15
+ * @return NewsletterSystem
16
+ */
17
+ static function instance() {
18
+ if (self::$instance == null) {
19
+ self::$instance = new NewsletterSystem();
20
+ }
21
+ return self::$instance;
22
+ }
23
+
24
+ function __construct() {
25
+
26
+ }
27
+
28
+ function reset_cron_stats() {
29
+ update_option('newsletter_diagnostic_cron_calls', [], false);
30
+ }
31
+
32
+ /**
33
+ *
34
+ * @param type $calls
35
+ * @return \TNP_Cron_Stats
36
+ */
37
+ function get_cron_stats() {
38
+ $calls = get_option('newsletter_diagnostic_cron_calls', []);
39
+
40
+ // Not enough data
41
+ if (count($calls) < 12) {
42
+ return null;
43
+ }
44
+
45
+ $stats = new TNP_Cron_Stats();
46
+
47
+ for ($i = 1; $i < count($calls); $i++) {
48
+ $delta = $calls[$i] - $calls[$i - 1];
49
+ $stats->deltas[] = $delta;
50
+ if ($stats->min > $delta) {
51
+ $stats->min = $delta;
52
+ }
53
+ if ($stats->max < $delta) {
54
+ $stats->max = $delta;
55
+ }
56
+ }
57
+ $stats->avg = round(array_sum($stats->deltas) / count($stats->deltas));
58
+ $stats->good = $stats->avg < NEWSLETTER_CRON_INTERVAL * 1.1; // 10% error
59
+ return $stats;
60
+ }
61
+
62
+ function get_last_cron_call() {
63
+ $calls = get_option('newsletter_diagnostic_cron_calls', []);
64
+ if (empty($calls))
65
+ return 0;
66
+ return end($calls);
67
+ }
68
+
69
+ function has_newsletter_schedule() {
70
+ $schedules = wp_get_schedules();
71
+ if (!empty($schedules)) {
72
+ foreach ($schedules as $key => $data) {
73
+ if ($key == 'newsletter') {
74
+ return true;
75
+ }
76
+ }
77
+ }
78
+ return false;
79
+ }
80
+
81
+ /**
82
+ *
83
+ * @return boolean|int
84
+ */
85
+ function get_job_delay() {
86
+ $x = wp_next_scheduled('newsletter');
87
+ return $x === false ? false : time() - $x;
88
+ }
89
+
90
+ function get_job_schedule() {
91
+ return wp_next_scheduled('newsletter');
92
+ }
93
+
94
+ function get_cache_job_status($refresh = false) {
95
+ $status = get_transient('newsletter_job_status');
96
+ if ($status === false || $refresh) {
97
+ $status = $this->get_job_status();
98
+ set_transient('newsletter_job_status', $status, DAY_IN_SECONDS);
99
+ }
100
+ return $status;
101
+ }
102
+
103
+ function get_job_status() {
104
+
105
+ $x = wp_next_scheduled('newsletter');
106
+ if ($x === false) {
107
+ return self::JOB_MISSING;
108
+ }
109
+
110
+ // Special case: the scheduler has been triggered but the job not executed
111
+ $calls = get_option('newsletter_diagnostic_cron_calls', []);
112
+ if (!empty($calls)) {
113
+ $last = end($calls);
114
+ if ($last > $x) {
115
+ return self::JOB_SKIPPED;
116
+ }
117
+ }
118
+
119
+ if (time() - $x > 900) {
120
+ return self::JOB_LATE;
121
+ }
122
+ return self::JOB_OK;
123
+ }
124
+
125
+ function reset_send_stats() {
126
+ update_option('newsletter_diagnostic_send_calls', [], false);
127
+ }
128
+
129
+ function get_send_stats() {
130
+ // Send calls stats
131
+ $send_calls = get_option('newsletter_diagnostic_send_calls', []);
132
+ if (!$send_calls) {
133
+ return null;
134
+ }
135
+ $send_max = 0;
136
+ $send_min = PHP_INT_MAX;
137
+ $send_total_time = 0;
138
+ $send_total_emails = 0;
139
+ $send_completed = 0;
140
+ $stats = new TNP_Send_stats();
141
+
142
+ // Batches
143
+ for ($i = 0; $i < count($send_calls); $i++) {
144
+ // 0 - batch start time
145
+ // 1 - batch end time
146
+ // 2 - number of sent email in this batch
147
+ // 3 - 0: prematurely stopped, 1: completed
148
+ if (empty($send_calls[$i][2])) {
149
+ continue;
150
+ }
151
+ if ($send_calls[$i][2] <= 1) {
152
+ continue;
153
+ }
154
+
155
+ $delta = $send_calls[$i][1] - $send_calls[$i][0];
156
+ $send_total_time += $delta;
157
+ $send_total_emails += $send_calls[$i][2];
158
+
159
+ $send_mean = $delta / $send_calls[$i][2];
160
+ $stats->means[] = $send_mean;
161
+ $stats->sizes[] = $send_calls[$i][2];
162
+ if ($send_min > $send_mean) {
163
+ $send_min = $send_mean;
164
+ }
165
+ if ($send_max < $send_mean) {
166
+ $send_max = $send_mean;
167
+ }
168
+ if (isset($send_calls[$i][3]) && $send_calls[$i][3]) {
169
+ $send_completed++;
170
+ }
171
+ }
172
+
173
+ if (empty($stats->means)) {
174
+ return null;
175
+ }
176
+ $send_mean = $send_total_time / $send_total_emails;
177
+
178
+ $stats->min = round($send_min, 2);
179
+ $stats->max = round($send_max, 2);
180
+ $stats->mean = round($send_mean, 2);
181
+ $stats->total_time = round($send_total_time);
182
+ $stats->total_emails = $send_total_emails;
183
+ $stats->total_runs = count($stats->means);
184
+
185
+ $stats->completed = $send_completed;
186
+ $stats->interrupted = $stats->total_runs - $stats->completed;
187
+ return $stats;
188
+ }
189
+
190
+ /**
191
+ * Returns a list of functions attached to the prvoded filter or action name.
192
+ *
193
+ * @global array $wp_filter
194
+ * @param string $tag
195
+ * @return string
196
+ */
197
+ function get_hook_functions($tag) {
198
+ global $wp_filter;
199
+ $b = '';
200
+ if (isset($wp_filter[$tag])) {
201
+ foreach ($wp_filter[$tag]->callbacks as $priority => $functions) {
202
+
203
+ foreach ($functions as $function) {
204
+ $b .= '[' . $priority . '] ';
205
+ if (is_array($function['function'])) {
206
+ if (is_object($function['function'][0])) {
207
+ $b .= get_class($function['function'][0]) . '::' . $function['function'][1];
208
+ } else {
209
+ $b .= $function['function'][0] . '::' . $function['function'][1];
210
+ }
211
+ } else {
212
+ if (is_object($function['function'])) {
213
+ $fn = new ReflectionFunction($function['function']);
214
+ $b .= get_class($fn->getClosureThis()) . '(closure)';
215
+ } else {
216
+ $b .= $function['function'];
217
+ }
218
+ }
219
+ $b .= "<br>";
220
+ }
221
+ }
222
+ }
223
+ return $b;
224
+ }
225
+
226
+ }
227
+
228
+ class TNP_Cron_Stats {
229
+
230
+ var $min = PHP_INT_MAX;
231
+ var $max = 0;
232
+ var $avg = 0;
233
+ /* List of intervals between cron calls */
234
+ var $deltas = [];
235
+ /* If the cron is triggered enough often */
236
+ var $good = true;
237
+
238
+ }
239
+
240
+ class TNP_Send_Stats {
241
+
242
+ /* Emails sent */
243
+ var $total_emails;
244
+ /* Total batches collected (1 sized-batch by Autoresponder are ignored) */
245
+ var $total_runs;
246
+ /* Total sending time of the collected batches */
247
+ var $total_time;
248
+ /* Batch completed without timeout or max emails limit */
249
+ var $completed;
250
+ /* Batches interrupted due to reached limits */
251
+ var $interrupted;
252
+ /* Min time to send an email */
253
+ var $min = PHP_INT_MAX;
254
+ /* Max time to send an email */
255
+ var $max = 0;
256
+ /* Average time to send all collected emails */
257
+ var $mean = 0;
258
+ /* List of single batches average per email sending time */
259
+ var $means = [];
260
+ /* Number of emails in the single batches */
261
+ var $sizes = [];
262
+
263
+ }
main/delivery.php CHANGED
@@ -1,220 +1,220 @@
1
- <?php
2
- /* @var $this Newsletter */
3
- /* @var $wpdb wpdb */
4
-
5
- defined('ABSPATH') || exit;
6
-
7
- include_once NEWSLETTER_INCLUDES_DIR . '/controls.php';
8
- $controls = new NewsletterControls();
9
-
10
- if ($controls->is_action('test')) {
11
-
12
- if (!NewsletterModule::is_email($controls->data['test_email'])) {
13
- $controls->errors = 'The test email address is not set or is not correct.';
14
- }
15
-
16
- if (empty($controls->errors)) {
17
-
18
- $options = $controls->data;
19
-
20
- if ($controls->data['test_email'] == $this->options['sender_email']) {
21
- $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>';
22
- }
23
-
24
- $message = NewsletterMailerAddon::get_test_message($controls->data['test_email'], 'Newsletter test email at ' . date(DATE_ISO8601));
25
-
26
- $r = $this->deliver($message);
27
-
28
- if (!is_wp_error($r)) {
29
- $options['mail'] = 1;
30
- $controls->messages .= '<strong>SUCCESS</strong><br>';
31
- $controls->messages .= 'Anyway if the message does not appear the mailbox (check even the spam folder) you can ';
32
- $controls->messages .= '<a href="https://www.thenewsletterplugin.com/documentation/?p=15170" target="_blank"><strong>read more here</strong></a>.';
33
- } else {
34
- $options['mail'] = 0;
35
- $options['mail_error'] = $r->get_error_message();
36
-
37
- $controls->errors .= '<strong>FAILED</strong> (' . $r->get_error_message() . ')<br>';
38
-
39
- if (!empty($this->options['return_path'])) {
40
- $controls->errors .= '- Try to remove the return path on main settings.<br>';
41
- }
42
-
43
- $controls->errors .= '<a href="https://www.thenewsletterplugin.com/documentation/?p=15170" target="_blank"><strong>' . __('Read more', 'newsletter') . '</strong></a>.';
44
-
45
- $parts = explode('@', $this->options['sender_email']);
46
- $sitename = strtolower($_SERVER['SERVER_NAME']);
47
- if (substr($sitename, 0, 4) == 'www.') {
48
- $sitename = substr($sitename, 4);
49
- }
50
- if (strtolower($sitename) != strtolower($parts[1])) {
51
- $controls->errors .= '- Try to set on main setting a sender address with the same domain of your blog: ' . $sitename . ' (you are using ' . $this->options['sender_email'] . ')<br>';
52
- }
53
- }
54
- $this->save_options($options, 'status');
55
- }
56
- }
57
-
58
- $options = $this->get_options('status');
59
-
60
- $mailer = Newsletter::instance()->get_mailer();
61
- $functions = tnp_get_hook_functions('phpmailer_init');
62
- $icon = 'fas fa-plug';
63
- if ($mailer instanceof NewsletterDefaultMailer) {
64
- $mailer_name = 'Wordpress';
65
- $service_name = 'Hosting Provider';
66
- if (!empty($functions)) {
67
- $mailer_name .= '<br>(see below)';
68
- $service_name .= '<br>(see below)';
69
- }
70
- $icon = 'fab fa-wordpress';
71
- } else if ($mailer instanceof NewsletterDefaultSMTPMailer) {
72
- $mailer_name = 'Internal SMTP';
73
- $service_name = 'SMTP Provider';
74
- } else {
75
- $mailer_name = 'Unknown';
76
- $service_name = 'Unknown';
77
- if (is_object($mailer)) {
78
- if (method_exists($mailer, 'get_description')) {
79
- $mailer_name = esc_html($mailer->get_description());
80
- $service_name = esc_html(ucfirst($mailer->get_name()));
81
- } else {
82
- $mailer_name = esc_html(get_class($mailer));
83
- $service_name = $mailer_name;
84
- }
85
- }
86
- }
87
-
88
- function tnp_get_hook_functions($tag) {
89
- global $wp_filter;
90
- if (isset($wp_filter)) {
91
- $b = '';
92
- foreach ($wp_filter[$tag]->callbacks as $priority => $functions) {
93
-
94
- foreach ($functions as $function) {
95
- $b .= '[' . $priority . '] ';
96
- if (is_array($function['function'])) {
97
- if (is_object($function['function'][0])) {
98
- $b .= get_class($function['function'][0]) . '::' . $function['function'][1];
99
- } else {
100
- $b .= $function['function'][0] . '::' . $function['function'][1];
101
- }
102
- } else {
103
- if (is_object($function['function'])) {
104
- $fn = new ReflectionFunction($function['function']);
105
- $b .= get_class($fn->getClosureThis()) . '(closure)';
106
- } else {
107
- $b .= $function['function'];
108
- }
109
- }
110
- $b .= "<br>";
111
- }
112
- }
113
- }
114
- return $b;
115
- }
116
-
117
- $speed = Newsletter::$instance->options['scheduler_max'];
118
- ?>
119
- <style>
120
- .tnp-flow {
121
- margin-top: 30px;
122
- }
123
- .tnp-flow div {
124
- display: inline-block;
125
- vertical-align: middle;
126
- color: #fff;
127
- text-align: center;
128
- margin: 0 20px;
129
- padding: 1em;
130
- }
131
- .tnp-flow div i {
132
- font-size: 50px;
133
- }
134
- .tnp-flow div.tnp-arrow {
135
- font-size: 40px;
136
- color: #bbb;
137
- padding: 0;
138
- margin: 0;
139
- }
140
- .tnp-mail, .tnp-addon, .tnp-service {
141
- height: 150px;
142
- width: 150px;
143
- border: 1px solid #fff;
144
- }
145
- </style>
146
-
147
- <div class="wrap tnp-main-status" id="tnp-wrap">
148
-
149
- <?php include NEWSLETTER_DIR . '/tnp-header.php'; ?>
150
-
151
- <div id="tnp-heading">
152
-
153
- <h2><?php _e('Email Delivery', 'newsletter') ?></h2>
154
-
155
- </div>
156
-
157
- <div id="tnp-body">
158
- <form method="post" action="">
159
- <?php $controls->init(); ?>
160
- <h3>Mailing test</h3>
161
-
162
- <p>
163
- <?php $controls->text_email('test_email') ?> <?php $controls->button('test', __('Send a test message')) ?>
164
- <?php if (empty($options['mail'])) { ?>
165
- <span class="tnp-ko">KO</span>
166
- <?php } else { ?>
167
- <span class="tnp-ok">OK</span>
168
- <?php } ?>
169
- </p>
170
-
171
- <p>
172
- <?php if (empty($options['mail'])) { ?>
173
- <?php if (empty($options['mail_error'])) { ?>
174
- <p>A test has never run.</p>
175
- <?php } else { ?>
176
- <p>Last test failed with error "<?php echo esc_html($options['mail_error']) ?>".</p>
177
-
178
- <?php } ?>
179
- <?php } else { ?>
180
- <p>Last test was successful. If you didn't receive the test email:</p>
181
- <ol style="color: #fff">
182
- <li>If you're using an third party SMTP plugin, do a test from that plugin configuration panel</li>
183
- <li>If you're using a Newsletter Delivery Addon, do a test from that addon configuration panel</li>
184
- <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>
185
- </ol>
186
- <?php } ?>
187
- <p><a href="https://www.thenewsletterplugin.com/documentation/email-sending-issues" target="_blank">Read more to solve your issues, if any</a></p>
188
-
189
-
190
- </form>
191
-
192
- <h3>
193
- How are messages delivered by Newsletter to your subscribers?
194
- </h3>
195
-
196
- <div class="tnp-flow">
197
- <div class="tnp-mail"><i class="fas fa-envelope"></i><br><br>Messages<br>
198
- (max: <?php echo esc_html($speed) ?> emails per hour)
199
- </div>
200
- <div class="tnp-arrow">&rightarrow;</div>
201
- <div class="tnp-addon"><i class="<?php echo $icon ?>"></i><br><br><?php echo $mailer_name ?></div>
202
- <div class="tnp-arrow">&rightarrow;</div>
203
- <div class="tnp-service"><i class="fas fa-cog"></i><br><br>
204
- <?php echo $service_name ?>
205
- </div>
206
- <div class="tnp-arrow">&rightarrow;</div>
207
- <div class="tnp-user"><i class="fas fa-user"></i></div>
208
- </div>
209
-
210
-
211
- <?php if (!empty($functions)) { ?>
212
- <br><br>
213
- <h3>Functions that are changing the default WordPress delivery system</h3>
214
- <p><?php echo $functions ?></p>
215
- <?php } ?>
216
-
217
-
218
- </div>
219
- <?php include NEWSLETTER_DIR . '/tnp-footer.php'; ?>
220
- </div>
1
+ <?php
2
+ /* @var $this Newsletter */
3
+ /* @var $wpdb wpdb */
4
+
5
+ defined('ABSPATH') || exit;
6
+
7
+ include_once NEWSLETTER_INCLUDES_DIR . '/controls.php';
8
+ $controls = new NewsletterControls();
9
+
10
+ if ($controls->is_action('test')) {
11
+
12
+ if (!NewsletterModule::is_email($controls->data['test_email'])) {
13
+ $controls->errors = 'The test email address is not set or is not correct.';
14
+ }
15
+
16
+ if (empty($controls->errors)) {
17
+
18
+ $options = $controls->data;
19
+
20
+ if ($controls->data['test_email'] == $this->options['sender_email']) {
21
+ $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>';
22
+ }
23
+
24
+ $message = NewsletterMailerAddon::get_test_message($controls->data['test_email'], 'Newsletter test email at ' . date(DATE_ISO8601));
25
+
26
+ $r = $this->deliver($message);
27
+
28
+ if (!is_wp_error($r)) {
29
+ $options['mail'] = 1;
30
+ $controls->messages .= '<strong>SUCCESS</strong><br>';
31
+ $controls->messages .= 'Anyway if the message does not appear the mailbox (check even the spam folder) you can ';
32
+ $controls->messages .= '<a href="https://www.thenewsletterplugin.com/documentation/?p=15170" target="_blank"><strong>read more here</strong></a>.';
33
+ } else {
34
+ $options['mail'] = 0;
35
+ $options['mail_error'] = $r->get_error_message();
36
+
37
+ $controls->errors .= '<strong>FAILED</strong> (' . $r->get_error_message() . ')<br>';
38
+
39
+ if (!empty($this->options['return_path'])) {
40
+ $controls->errors .= '- Try to remove the return path on main settings.<br>';
41
+ }
42
+
43
+ $controls->errors .= '<a href="https://www.thenewsletterplugin.com/documentation/?p=15170" target="_blank"><strong>' . __('Read more', 'newsletter') . '</strong></a>.';
44
+
45
+ $parts = explode('@', $this->options['sender_email']);
46
+ $sitename = strtolower($_SERVER['SERVER_NAME']);
47
+ if (substr($sitename, 0, 4) == 'www.') {
48
+ $sitename = substr($sitename, 4);
49
+ }
50
+ if (strtolower($sitename) != strtolower($parts[1])) {
51
+ $controls->errors .= '- Try to set on main setting a sender address with the same domain of your blog: ' . $sitename . ' (you are using ' . $this->options['sender_email'] . ')<br>';
52
+ }
53
+ }
54
+ $this->save_options($options, 'status');
55
+ }
56
+ }
57
+
58
+ $options = $this->get_options('status');
59
+
60
+ $mailer = Newsletter::instance()->get_mailer();
61
+ $functions = tnp_get_hook_functions('phpmailer_init');
62
+ $icon = 'fas fa-plug';
63
+ if ($mailer instanceof NewsletterDefaultMailer) {
64
+ $mailer_name = 'Wordpress';
65
+ $service_name = 'Hosting Provider';
66
+ if (!empty($functions)) {
67
+ $mailer_name .= '<br>(see below)';
68
+ $service_name .= '<br>(see below)';
69
+ }
70
+ $icon = 'fab fa-wordpress';
71
+ } else if ($mailer instanceof NewsletterDefaultSMTPMailer) {
72
+ $mailer_name = 'Internal SMTP';
73
+ $service_name = 'SMTP Provider';
74
+ } else {
75
+ $mailer_name = 'Unknown';
76
+ $service_name = 'Unknown';
77
+ if (is_object($mailer)) {
78
+ if (method_exists($mailer, 'get_description')) {
79
+ $mailer_name = esc_html($mailer->get_description());
80
+ $service_name = esc_html(ucfirst($mailer->get_name()));
81
+ } else {
82
+ $mailer_name = esc_html(get_class($mailer));
83
+ $service_name = $mailer_name;
84
+ }
85
+ }
86
+ }
87
+
88
+ function tnp_get_hook_functions($tag) {
89
+ global $wp_filter;
90
+ if (isset($wp_filter)) {
91
+ $b = '';
92
+ foreach ($wp_filter[$tag]->callbacks as $priority => $functions) {
93
+
94
+ foreach ($functions as $function) {
95
+ $b .= '[' . $priority . '] ';
96
+ if (is_array($function['function'])) {
97
+ if (is_object($function['function'][0])) {
98
+ $b .= get_class($function['function'][0]) . '::' . $function['function'][1];
99
+ } else {
100
+ $b .= $function['function'][0] . '::' . $function['function'][1];
101
+ }
102
+ } else {
103
+ if (is_object($function['function'])) {
104
+ $fn = new ReflectionFunction($function['function']);
105
+ $b .= get_class($fn->getClosureThis()) . '(closure)';
106
+ } else {
107
+ $b .= $function['function'];
108
+ }
109
+ }
110
+ $b .= "<br>";
111
+ }
112
+ }
113
+ }
114
+ return $b;
115
+ }
116
+
117
+ $speed = Newsletter::$instance->options['scheduler_max'];
118
+ ?>
119
+ <style>
120
+ .tnp-flow {
121
+ margin-top: 30px;
122
+ }
123
+ .tnp-flow div {
124
+ display: inline-block;
125
+ vertical-align: middle;
126
+ color: #fff;
127
+ text-align: center;
128
+ margin: 0 20px;
129
+ padding: 1em;
130
+ }
131
+ .tnp-flow div i {
132
+ font-size: 50px;
133
+ }
134
+ .tnp-flow div.tnp-arrow {
135
+ font-size: 40px;
136
+ color: #bbb;
137
+ padding: 0;
138
+ margin: 0;
139
+ }
140
+ .tnp-mail, .tnp-addon, .tnp-service {
141
+ height: 150px;
142
+ width: 150px;
143
+ border: 1px solid #fff;
144
+ }
145
+ </style>
146
+
147
+ <div class="wrap tnp-main-status" id="tnp-wrap">
148
+
149
+ <?php include NEWSLETTER_DIR . '/tnp-header.php'; ?>
150
+
151
+ <div id="tnp-heading">
152
+
153
+ <h2><?php _e('Email Delivery', 'newsletter') ?></h2>
154
+
155
+ </div>
156
+
157
+ <div id="tnp-body">
158
+ <form method="post" action="">
159
+ <?php $controls->init(); ?>
160
+ <h3>Mailing test</h3>
161
+
162
+ <p>
163
+ <?php $controls->text_email('test_email') ?> <?php $controls->button('test', __('Send a test message')) ?>
164
+ <?php if (empty($options['mail'])) { ?>
165
+ <span class="tnp-ko">KO</span>
166
+ <?php } else { ?>
167
+ <span class="tnp-ok">OK</span>
168
+ <?php } ?>
169
+ </p>
170
+
171
+ <p>
172
+ <?php if (empty($options['mail'])) { ?>
173
+ <?php if (empty($options['mail_error'])) { ?>
174
+ <p>A test has never run.</p>
175
+ <?php } else { ?>
176
+ <p>Last test failed with error "<?php echo esc_html($options['mail_error']) ?>".</p>
177
+
178
+ <?php } ?>
179
+ <?php } else { ?>
180
+ <p>Last test was successful. If you didn't receive the test email:</p>
181
+ <ol style="color: #fff">
182
+ <li>If you're using an third party SMTP plugin, do a test from that plugin configuration panel</li>
183
+ <li>If you're using a Newsletter Delivery Addon, do a test from that addon configuration panel</li>
184
+ <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>
185
+ </ol>
186
+ <?php } ?>
187
+ <p><a href="https://www.thenewsletterplugin.com/documentation/email-sending-issues" target="_blank">Read more to solve your issues, if any</a></p>
188
+
189
+
190
+ </form>
191
+
192
+ <h3>
193
+ How are messages delivered by Newsletter to your subscribers?
194
+ </h3>
195
+
196
+ <div class="tnp-flow">
197
+ <div class="tnp-mail"><i class="fas fa-envelope"></i><br><br>Messages<br>
198
+ (max: <?php echo esc_html($speed) ?> emails per hour)
199
+ </div>
200
+ <div class="tnp-arrow">&rightarrow;</div>
201
+ <div class="tnp-addon"><i class="<?php echo $icon ?>"></i><br><br><?php echo $mailer_name ?></div>
202
+ <div class="tnp-arrow">&rightarrow;</div>
203
+ <div class="tnp-service"><i class="fas fa-cog"></i><br><br>
204
+ <?php echo $service_name ?>
205
+ </div>
206
+ <div class="tnp-arrow">&rightarrow;</div>
207
+ <div class="tnp-user"><i class="fas fa-user"></i></div>
208
+ </div>
209
+
210
+
211
+ <?php if (!empty($functions)) { ?>
212
+ <br><br>
213
+ <h3>Functions that are changing the default WordPress delivery system</h3>
214
+ <p><?php echo $functions ?></p>
215
+ <?php } ?>
216
+
217
+
218
+ </div>
219
+ <?php include NEWSLETTER_DIR . '/tnp-footer.php'; ?>
220
+ </div>
main/scheduler.php CHANGED
@@ -1,411 +1,412 @@
1
- <?php
2
- /* @var $this Newsletter */
3
- /* @var $wpdb wpdb */
4
-
5
- defined('ABSPATH') || exit;
6
-
7
- wp_enqueue_script('tnp-chart');
8
-
9
- include_once NEWSLETTER_INCLUDES_DIR . '/controls.php';
10
- $controls = new NewsletterControls();
11
- $system = NewsletterSystem::instance();
12
-
13
- if ($controls->is_action('reset')) {
14
- $system->reset_cron_stats();
15
- $controls->add_message_done();
16
- }
17
-
18
- if ($controls->is_action('reschedule')) {
19
- wp_clear_scheduled_hook('newsletter');
20
- wp_schedule_event(time() + 30, 'newsletter', 'newsletter');
21
- $controls->add_message_done();
22
- }
23
-
24
- if ($controls->is_action('trigger')) {
25
- wp_clear_scheduled_hook('newsletter');
26
- wp_schedule_event(time() + NEWSLETTER_CRON_INTERVAL, 'newsletter', 'newsletter');
27
- Newsletter::instance()->hook_newsletter();
28
- $controls->add_message_done();
29
- }
30
-
31
- if ($controls->is_action('test')) {
32
- $response = wp_remote_get(site_url('/wp-cron.php') . '?' . time());
33
- if (is_wp_error($response)) {
34
- $controls->errors = 'Test failed: ' . esc_html($response->get_error_message());
35
- } else if (wp_remote_retrieve_response_code($response) != 200) {
36
- $controls->errors = 'Test failed: ' . esc_html(wp_remote_retrieve_response_message($response));
37
- } else {
38
- $controls->add_message('Test ok');
39
- }
40
-
41
- if ($controls->errors) {
42
- $controls->errors .= '<br>Report this error to your provider saying the site cannot make an HTTP call to its wp-cron.php file and copying the error message above.';
43
- }
44
- }
45
-
46
- function tnp_status_print_flag($condition, $url = '') {
47
- switch ($condition) {
48
- case 0: echo ' <span class="tnp-ko">KO</span>';
49
- break;
50
- case 1: echo '<span class="tnp-ok">OK</span>';
51
- break;
52
- case 2: echo '<span class="tnp-maybe">MAYBE</span>';
53
- break;
54
- }
55
- if ($url) {
56
- echo '<a href="', $url, '" target="_blank">Read more</a>';
57
- }
58
- }
59
-
60
- $system = NewsletterSystem::instance();
61
- ?>
62
-
63
- <style>
64
- #tnp-body table.widefat td {
65
- vertical-align: top;
66
- }
67
- #tnp-body table.widefat td.status {
68
- text-align: center;
69
- }
70
- </style>
71
- <div class="wrap tnp-main-status" id="tnp-wrap">
72
-
73
- <?php include NEWSLETTER_DIR . '/tnp-header.php'; ?>
74
-
75
- <div id="tnp-heading">
76
-
77
- <h2><?php _e('WordPress Scheduler and Newsletter Delivery Engine', 'newsletter') ?></h2>
78
- <p>
79
- The scheduler is a WordPress component that executes <strong>background tasks</strong>
80
- (publish future post, run backups, send newsletters, ...).
81
- <br>
82
- Here some steps you can consider if the scheduler has issues.
83
- </p>
84
- <ul>
85
- <li>Check the <a href="<?php echo admin_url('/site-health.php') ?>">site health panel</a> and try to solve the issues identified by WordPress in your site</li>
86
- <li>Install the <a href="https://wordpress.org/plugins/wp-crontrol/" target="_blank">WP Crontol</a> plugin which shows all the scheduled jobs and delays</li>
87
- <li>Configure an <a href="https://www.thenewsletterplugin.com/documentation/delivery-and-spam/newsletter-delivery-engine/" target="_blank">external cron service</a>
88
- (if you have a license you can use our <a href="https://www.thenewsletterplugin.com/account/cron/" target="_blank">cron service</a>)</li>
89
- </ul>
90
- </div>
91
-
92
- <div id="tnp-body">
93
-
94
-
95
- <form method="post" action="">
96
- <?php $controls->init(); ?>
97
-
98
- <table class="widefat">
99
- <?php
100
- $status = $system->get_job_status();
101
- $condition = $status == NewsletterSystem::JOB_OK ? 1 : 0;
102
- ?>
103
- <tr>
104
- <td>Delivery background job</td>
105
- <td class="status">
106
- <?php tnp_status_print_flag($condition, 'https://www.thenewsletterplugin.com/documentation/delivery-and-spam/newsletter-delivery-engine/') ?>
107
- </td>
108
- <td>
109
- <?php
110
- switch ($status) {
111
- case NewsletterSystem::JOB_MISSING:
112
- echo 'The engine schdule is missing. Try to deactivate and reactivate the Newsletter plugin.';
113
- break;
114
- case NewsletterSystem::JOB_LATE:
115
- echo 'The engine schdule is late. You probably need and external scheduler trigger.';
116
- break;
117
- case NewsletterSystem::JOB_SKIPPED:
118
- echo 'The engine schdule has been skipped. The schduler is overloaded or a job has fatal error and blocks the scheduler.';
119
- break;
120
- case NewsletterSystem::JOB_OK:
121
- echo 'Everything seems fine!';
122
- break;
123
- }
124
- ?>
125
- <br><br>
126
- Next run: <?php echo $controls->print_date($system->get_job_schedule(), false, true) ?>
127
- <br><br>
128
- <?php
129
- if ($status == NewsletterSystem::JOB_LATE) {
130
- $controls->button('trigger', 'Run manually');
131
- }
132
- ?>
133
- </td>
134
- </tr>
135
-
136
-
137
- <tr>
138
- <td>Last cron call</td>
139
- <td class="status">&nbsp;</td>
140
- <td>
141
- <?php echo $controls->print_date($system->get_last_cron_call()) ?>
142
- </td>
143
- </tr>
144
-
145
-
146
- <tr>
147
- <?php
148
- $stats = NewsletterSystem::instance()->get_cron_stats();
149
- ?>
150
- <td>
151
- Cron call stats
152
- </td>
153
- <?php if ($stats == null) { ?>
154
- <td class="status">
155
- &nbsp;
156
- </td>
157
- <td>
158
- Not enough data, some hours are still required.
159
- </td>
160
-
161
- <?php } else { ?>
162
-
163
- <?php
164
- $condition = $stats->good ? 1 : 0;
165
- ?>
166
-
167
- <td class="status">
168
- <?php tnp_status_print_flag($condition, 'https://www.thenewsletterplugin.com/documentation/delivery-and-spam/newsletter-delivery-engine/') ?>
169
- </td>
170
- <td>
171
- <?php if ($condition == 0) { ?>
172
- The blog cron system is NOT triggered enough often.<br>
173
- <?php } ?>
174
-
175
- Samples <?php echo count($stats->deltas) ?>, average <?php echo $stats->avg ?>&nbsp;s, max <?php echo $stats->max ?>&nbsp;s, min <?php echo $stats->min ?>&nbsp;s
176
-
177
- <canvas id="tnp-cron-chart" style="width: 550px; height: 180px"></canvas>
178
- <script>
179
- jQuery(function () {
180
- var cronChartData = {
181
- labels: <?php echo json_encode(range(1, count($stats->deltas))) ?>,
182
- datasets: [
183
- {
184
- label: "Batch Average Time",
185
- data: <?php echo json_encode($stats->deltas) ?>,
186
- borderColor: '#2980b9',
187
- fill: false
188
- }]
189
- };
190
- var cronChartConfig = {
191
- type: "line",
192
- data: cronChartData,
193
- options: {
194
- responsive: false,
195
- maintainAspectRatio: false,
196
- scales: {
197
- x: {
198
- type: 'linear'
199
- }
200
- }
201
- }
202
- };
203
- new Chart('tnp-cron-chart', cronChartConfig);
204
- });
205
- </script>
206
-
207
- <?php $controls->button_reset() ?>
208
-
209
- </td>
210
- <?php } ?>
211
- </tr>
212
-
213
- <?php
214
- $condition = $system->has_newsletter_schedule() ? 1 : 0;
215
- $schedules = wp_get_schedules();
216
- ?>
217
- <tr>
218
- <td>
219
- Newsletter engine schedule
220
- </td>
221
- <td class="status"><?php tnp_status_print_flag($condition) ?></td>
222
- <td>
223
- <?php if (!$condition) { ?>
224
- The Newsletter schedule is not present probably another plugin is interfering with the starndard WordPress scheuling system.<br>
225
- You can reactivate it, but is the problem persist
226
- <?php $controls->button('reschedule', 'Reactivate') ?>
227
- <?php } ?>
228
-
229
- Registered recurring schedules:<br>
230
- <ul style="margin-left: 0em;">
231
- <?php
232
- if (!empty($schedules)) {
233
- foreach ($schedules as $key => $data) {
234
- if ($key == 'newsletter') {
235
- echo '<li style="padding: 0; margin: 0; font-weight: bold">', esc_html($key . ' - ' . $data['interval']), ' seconds</li>';
236
- } else {
237
- echo '<li style="padding: 0; margin: 0;">', esc_html($key . ' - ' . $data['interval']), ' seconds</li>';
238
- }
239
- }
240
- }
241
- ?>
242
- </ul>
243
- </td>
244
- </tr>
245
-
246
-
247
- <tr>
248
- <td>Cron URL</td>
249
- <td class="status">&nbsp;</td>
250
- <td>
251
- <strong><?php echo esc_html(site_url('/wp-cron.php')) ?></strong>
252
- <br><br>
253
- Can be used to trigger the WordPress scheduler from an external cron service.
254
- </td>
255
- </tr>
256
-
257
-
258
-
259
-
260
- <tr>
261
- <td>
262
- WordPress scheduler auto trigger
263
- </td>
264
- <td class="status">
265
- <?php //tnp_status_print_flag($condition) ?>
266
- </td>
267
- <td>
268
- <?php $controls->button_test() ?>
269
- </td>
270
- </tr>
271
-
272
- <?php
273
- $condition = (defined('DISABLE_WP_CRON') && DISABLE_WP_CRON) ? 2 : 1;
274
- ?>
275
- <tr>
276
- <td>
277
- <code>DISABLE_WP_CRON</code>
278
- </td>
279
- <td class="status">
280
- <?php tnp_status_print_flag($condition) ?>
281
- </td>
282
- <td>
283
- <?php if ($condition == 2) { ?>
284
- The constant <code>DISABLE_WP_CRON</code> is set to <code>true</code> (probably in <code>wp-config.php</code>). That disables the scheduler auto triggering and it's
285
- good ONLY if you setup an external trigger.
286
- <?php } ?>
287
- </td>
288
- </tr>
289
-
290
- <tr>
291
- <td>
292
- <code>ALTERNATE_WP_CRON</code>
293
- </td>
294
- <td class="status">
295
- &nbsp;
296
- </td>
297
- <td>
298
- <?php if (defined('ALTERNATE_WP_CRON') && ALTERNATE_WP_CRON) { ?>
299
- Using the alternate cron trigger. Rare configuration but should not be a problem.
300
- <?php } else { ?>
301
- Option not active, it's ok.
302
- <?php } ?>
303
- </td>
304
- </tr>
305
-
306
- <?php
307
- $condition = NEWSLETTER_CRON_INTERVAL == 300 ? 1 : 2;
308
- ?>
309
- <tr>
310
- <td><code>NEWSLETTER_CRON_INTERVAL</code></td>
311
- <td class="status">
312
- <?php tnp_status_print_flag($condition) ?>
313
- </td>
314
- <td>
315
- <?php echo NEWSLETTER_CRON_INTERVAL, ' seconds'; ?>
316
- <br><br>
317
- How often the Newsletter engine should be activated. Default 300 seconds. Different value can be set on your <code>wp-config.php</code>
318
- (not recommended).
319
- </td>
320
- </tr>
321
-
322
-
323
- <?php
324
- $condition = WP_CRON_LOCK_TIMEOUT != MINUTE_IN_SECONDS ? 2 : 1;
325
- ?>
326
- <tr>
327
- <td><code>WP_CRON_LOCK_TIMEOUT</code></td>
328
- <td class="status">
329
- <?php tnp_status_print_flag($condition) ?>
330
- </td>
331
- <td>
332
- <?php echo WP_CRON_LOCK_TIMEOUT, ' seconds'; ?>
333
-
334
- <?php if ($condition == 2) { ?>
335
- <br>
336
- A non standard (<?php echo MINUTE_IN_SECONDS ?> seconds) value is specified probably in your <code>wp-config.php</code>.
337
- <?php } ?>
338
- </td>
339
- </tr>
340
-
341
-
342
- <?php
343
- $condition = (defined('NEWSLETTER_CRON_WARNINGS') && !NEWSLETTER_CRON_WARNINGS) ? 2 : 1;
344
- ?>
345
- <tr>
346
-
347
- <td>
348
- <code>NEWSLETTER_CRON_WARNINGS</code>
349
- </td>
350
- <td class="status">
351
- <?php tnp_status_print_flag($condition) ?>
352
- </td>
353
- <td>
354
- <?php if ($condition == 2) { ?>
355
- Scheduler warnings are disabled in your <code>wp-config.php</code> with the constant <code>NEWSLETTER_CRON_WARNINGS</code> set to true.
356
- <?php } else { ?>
357
- Scheduler warnings are enabled
358
- <?php } ?>
359
- </td>
360
- </tr>
361
-
362
- <?php
363
- $condition = has_filter('pre_reschedule_event') ? 2 : 1;
364
- ?>
365
- <tr>
366
- <td><code>pre_reschedule_event</code></td>
367
- <td class="status">
368
- <?php tnp_status_print_flag($condition) ?>
369
- </td>
370
- <td>
371
- <?php if ($condition == 2) { ?>
372
- One or more plugin are filtering the jobs rescheduling. If a recurrent job (like the newsletter generation with Automated) disappers
373
- this is a good starting point.<br><br>
374
- <?php } ?>
375
- Attached functions:<br>
376
- <?php echo $system->get_hook_functions('pre_reschedule_event') ?>
377
- </td>
378
- </tr>
379
-
380
- <?php
381
- $transient = get_transient('doing_cron');
382
- ?>
383
- <tr>
384
- <td>Transient <code>doing_cron</code></td>
385
- <td class="status">
386
- <?php //tnp_status_print_flag($condition) ?>
387
- </td>
388
- <td>
389
- <?php if ($transient) { ?>
390
- <?php
391
- echo esc_html($transient);
392
- if (is_numeric($transient)) {
393
- echo ' (', $controls->print_date((int) $transient), ')';
394
- }
395
- ?>
396
- <?php } else { ?>
397
- [unset]
398
- <?php } ?>
399
- <br><br>
400
- When set it means the scheduler is executing background jobs. Install the WP Crontol plugin to have more information about
401
- your site background jobs.
402
- </td>
403
- </tr>
404
- </table>
405
-
406
- </form>
407
- </div>
408
-
409
- <?php include NEWSLETTER_DIR . '/tnp-footer.php'; ?>
410
-
411
- </div>
 
1
+ <?php
2
+ /* @var $this Newsletter */
3
+ /* @var $wpdb wpdb */
4
+
5
+ defined('ABSPATH') || exit;
6
+
7
+ wp_enqueue_script('tnp-chart');
8
+
9
+ include_once NEWSLETTER_INCLUDES_DIR . '/controls.php';
10
+ $controls = new NewsletterControls();
11
+ $system = NewsletterSystem::instance();
12
+
13
+ if ($controls->is_action('reset')) {
14
+ $system->reset_cron_stats();
15
+ $controls->add_message_done();
16
+ }
17
+
18
+ if ($controls->is_action('reschedule')) {
19
+ wp_clear_scheduled_hook('newsletter');
20
+ wp_schedule_event(time() + 30, 'newsletter', 'newsletter');
21
+ $controls->add_message_done();
22
+ }
23
+
24
+ if ($controls->is_action('trigger')) {
25
+ wp_clear_scheduled_hook('newsletter');
26
+ wp_schedule_event(time() + NEWSLETTER_CRON_INTERVAL, 'newsletter', 'newsletter');
27
+ Newsletter::instance()->hook_newsletter();
28
+ $controls->add_message_done();
29
+ }
30
+
31
+ if ($controls->is_action('test')) {
32
+ $response = wp_remote_get(site_url('/wp-cron.php') . '?' . time());
33
+ if (is_wp_error($response)) {
34
+ $controls->errors = 'Test failed: ' . esc_html($response->get_error_message());
35
+ } else if (wp_remote_retrieve_response_code($response) != 200) {
36
+ $controls->errors = 'Test failed: ' . esc_html(wp_remote_retrieve_response_message($response));
37
+ } else {
38
+ $controls->add_message('Test ok');
39
+ }
40
+
41
+ if ($controls->errors) {
42
+ $controls->errors .= '<br>Report this error to your provider saying the site cannot make an HTTP call to its wp-cron.php file and copying the error message above.';
43
+ }
44
+ }
45
+
46
+ function tnp_status_print_flag($condition, $url = '') {
47
+ switch ($condition) {
48
+ case 0: echo ' <span class="tnp-ko">KO</span>';
49
+ break;
50
+ case 1: echo '<span class="tnp-ok">OK</span>';
51
+ break;
52
+ case 2: echo '<span class="tnp-maybe">MAYBE</span>';
53
+ break;
54
+ }
55
+ if ($url) {
56
+ echo '<br><a href="', $url, '" target="_blank">Read more</a>';
57
+ }
58
+ }
59
+
60
+ $system = NewsletterSystem::instance();
61
+ ?>
62
+
63
+ <style>
64
+ #tnp-body table.widefat td {
65
+ vertical-align: top;
66
+ }
67
+ #tnp-body table.widefat td.status {
68
+ text-align: center;
69
+ }
70
+ </style>
71
+ <div class="wrap tnp-main-status" id="tnp-wrap">
72
+
73
+ <?php include NEWSLETTER_DIR . '/tnp-header.php'; ?>
74
+
75
+ <div id="tnp-heading">
76
+
77
+ <h2><?php _e('WordPress Scheduler and Newsletter Delivery Engine', 'newsletter') ?></h2>
78
+ <p>
79
+ The scheduler is a WordPress component that executes <strong>background tasks</strong>
80
+ (publish future post, run backups, send newsletters, ...).
81
+ <br>
82
+ Here some steps you can consider if the scheduler has issues.
83
+ </p>
84
+ <ul>
85
+ <li>Check the <a href="<?php echo admin_url('/site-health.php') ?>">site health panel</a> and try to solve the issues identified by WordPress in your site</li>
86
+ <li>Install the <a href="https://wordpress.org/plugins/wp-crontrol/" target="_blank">WP Crontol</a> plugin which shows all the scheduled jobs and delays</li>
87
+ <li>Configure an <a href="https://www.thenewsletterplugin.com/documentation/delivery-and-spam/newsletter-delivery-engine/" target="_blank">external cron service</a>
88
+ (if you have a license you can use our <a href="https://www.thenewsletterplugin.com/account/cron/" target="_blank">cron service</a>)</li>
89
+ </ul>
90
+ </div>
91
+
92
+ <div id="tnp-body">
93
+
94
+
95
+ <form method="post" action="">
96
+ <?php $controls->init(); ?>
97
+
98
+ <table class="widefat">
99
+ <?php
100
+ $status = $system->get_job_status();
101
+ $condition = $status == NewsletterSystem::JOB_OK ? 1 : 0;
102
+ ?>
103
+ <tr>
104
+ <td>Delivery background job</td>
105
+ <td class="status">
106
+ <?php tnp_status_print_flag($condition, 'https://www.thenewsletterplugin.com/documentation/delivery-and-spam/newsletter-delivery-engine/') ?>
107
+ </td>
108
+ <td>
109
+ <?php
110
+ switch ($status) {
111
+ case NewsletterSystem::JOB_MISSING:
112
+ echo 'The engine schedule is missing. Try to deactivate and reactivate the Newsletter plugin.';
113
+ break;
114
+ case NewsletterSystem::JOB_LATE:
115
+ echo 'The engine schedule is late. You probably need and external scheduler trigger.';
116
+ break;
117
+ case NewsletterSystem::JOB_SKIPPED:
118
+ echo 'The engine schedule has been skipped. The scheduler is overloaded or a job has fatal error and blocks the scheduler.';
119
+ break;
120
+ case NewsletterSystem::JOB_OK:
121
+ echo 'Everything seems fine!';
122
+ break;
123
+ }
124
+ ?>
125
+ <br><br>
126
+ Next run: <?php echo $controls->print_date($system->get_job_schedule(), false, true) ?>
127
+ <br><br>
128
+ <?php
129
+ if ($status == NewsletterSystem::JOB_LATE) {
130
+ $controls->button('trigger', 'Run manually');
131
+ }
132
+ ?>
133
+ </td>
134
+ </tr>
135
+
136
+
137
+ <tr>
138
+ <td>Last cron call</td>
139
+ <td class="status">&nbsp;</td>
140
+ <td>
141
+ <?php echo $controls->print_date($system->get_last_cron_call()) ?>
142
+ </td>
143
+ </tr>
144
+
145
+
146
+ <tr>
147
+ <?php
148
+ $stats = NewsletterSystem::instance()->get_cron_stats();
149
+ ?>
150
+ <td>
151
+ Cron call stats
152
+ </td>
153
+ <?php if ($stats == null) { ?>
154
+ <td class="status">
155
+ &nbsp;
156
+ </td>
157
+ <td>
158
+ Not enough data, some hours are still required.
159
+ </td>
160
+
161
+ <?php } else { ?>
162
+
163
+ <?php
164
+ $condition = $stats->good ? 1 : 0;
165
+ ?>
166
+
167
+ <td class="status">
168
+ <?php tnp_status_print_flag($condition, 'https://www.thenewsletterplugin.com/documentation/delivery-and-spam/newsletter-delivery-engine/') ?>
169
+ </td>
170
+ <td>
171
+ <?php if ($condition == 0) { ?>
172
+ The blog cron system is NOT triggered enough often.<br>
173
+ <?php } ?>
174
+
175
+ Samples <?php echo count($stats->deltas) ?>, average <?php echo $stats->avg ?>&nbsp;s, max <?php echo $stats->max ?>&nbsp;s, min <?php echo $stats->min ?>&nbsp;s
176
+
177
+ <canvas id="tnp-cron-chart" style="width: 550px; height: 180px"></canvas>
178
+ <script>
179
+ jQuery(function () {
180
+ var cronChartData = {
181
+ labels: <?php echo json_encode(range(1, count($stats->deltas))) ?>,
182
+ datasets: [
183
+ {
184
+ label: "Batch Average Time",
185
+ data: <?php echo json_encode($stats->deltas) ?>,
186
+ borderColor: '#2980b9',
187
+ fill: false
188
+ }]
189
+ };
190
+ var cronChartConfig = {
191
+ type: "line",
192
+ data: cronChartData,
193
+ options: {
194
+ responsive: false,
195
+ maintainAspectRatio: false,
196
+ scales: {
197
+ x: {
198
+ type: 'linear'
199
+ }
200
+ }
201
+ }
202
+ };
203
+ new Chart('tnp-cron-chart', cronChartConfig);
204
+ });
205
+ </script>
206
+
207
+ <?php $controls->button_reset() ?>
208
+
209
+ </td>
210
+ <?php } ?>
211
+ </tr>
212
+
213
+ <?php
214
+ $condition = $system->has_newsletter_schedule() ? 1 : 0;
215
+ $schedules = wp_get_schedules();
216
+ ?>
217
+ <tr>
218
+ <td>
219
+ Newsletter engine schedule
220
+ </td>
221
+ <td class="status"><?php tnp_status_print_flag($condition) ?></td>
222
+ <td>
223
+ <?php if (!$condition) { ?>
224
+ The Newsletter schedule is not present probably another plugin is interfering with the starndard WordPress scheuling system.<br>
225
+ You can reactivate it, but is the problem persist
226
+ <?php $controls->button('reschedule', 'Reactivate') ?>
227
+ <?php } ?>
228
+
229
+ Registered recurring schedules:<br>
230
+ <ul style="margin-left: 0em;">
231
+ <?php
232
+ if (!empty($schedules)) {
233
+ foreach ($schedules as $key => $data) {
234
+ if ($key == 'newsletter') {
235
+ echo '<li style="padding: 0; margin: 0; font-weight: bold">', esc_html($key . ' - ' . $data['interval']), ' seconds</li>';
236
+ } else {
237
+ echo '<li style="padding: 0; margin: 0;">', esc_html($key . ' - ' . $data['interval']), ' seconds</li>';
238
+ }
239
+ }
240
+ }
241
+ ?>
242
+ </ul>
243
+ </td>
244
+ </tr>
245
+
246
+
247
+ <tr>
248
+ <td>Cron URL</td>
249
+ <td class="status">&nbsp;</td>
250
+ <td>
251
+ <strong><?php echo esc_html(site_url('/wp-cron.php')) ?></strong>
252
+ <br><br>
253
+ Can be used to trigger the WordPress scheduler from an external cron service.
254
+ </td>
255
+ </tr>
256
+
257
+
258
+
259
+
260
+ <tr>
261
+ <td>
262
+ WordPress scheduler auto trigger
263
+ </td>
264
+ <td class="status">
265
+ <?php //tnp_status_print_flag($condition) ?>
266
+ </td>
267
+ <td>
268
+ <?php $controls->button_test() ?>
269
+ </td>
270
+ </tr>
271
+
272
+ <?php
273
+ $condition = (defined('DISABLE_WP_CRON') && DISABLE_WP_CRON) ? 2 : 1;
274
+ ?>
275
+ <tr>
276
+ <td>
277
+ <code>DISABLE_WP_CRON</code>
278
+ </td>
279
+ <td class="status">
280
+ <?php tnp_status_print_flag($condition) ?>
281
+ </td>
282
+ <td>
283
+ <?php if ($condition == 2) { ?>
284
+ The constant <code>DISABLE_WP_CRON</code> is set to <code>true</code> (probably in <code>wp-config.php</code>). That disables the scheduler auto triggering and it's
285
+ good ONLY if you setup an external trigger.
286
+ <?php } ?>
287
+ </td>
288
+ </tr>
289
+
290
+ <tr>
291
+ <td>
292
+ <code>ALTERNATE_WP_CRON</code>
293
+ </td>
294
+ <td class="status">
295
+ &nbsp;
296
+ </td>
297
+ <td>
298
+ <?php if (defined('ALTERNATE_WP_CRON') && ALTERNATE_WP_CRON) { ?>
299
+ Using the alternate cron trigger. Rare configuration but should not be a problem.
300
+ <?php } else { ?>
301
+ Option not active, it's ok.
302
+ <?php } ?>
303
+ </td>
304
+ </tr>
305
+
306
+ <?php
307
+ $condition = NEWSLETTER_CRON_INTERVAL == 300 ? 1 : 2;
308
+ ?>
309
+ <tr>
310
+ <td><code>NEWSLETTER_CRON_INTERVAL</code></td>
311
+ <td class="status">
312
+ <?php tnp_status_print_flag($condition) ?>
313
+ </td>
314
+ <td>
315
+ <?php echo NEWSLETTER_CRON_INTERVAL, ' seconds'; ?>
316
+ <br><br>
317
+ How often the Newsletter engine should be activated. Default 300 seconds. Different value can be set on your <code>wp-config.php</code>
318
+ (not recommended).
319
+ </td>
320
+ </tr>
321
+
322
+
323
+ <?php
324
+ $condition = WP_CRON_LOCK_TIMEOUT != MINUTE_IN_SECONDS ? 2 : 1;
325
+ ?>
326
+ <tr>
327
+ <td><code>WP_CRON_LOCK_TIMEOUT</code></td>
328
+ <td class="status">
329
+ <?php tnp_status_print_flag($condition) ?>
330
+ </td>
331
+ <td>
332
+ <?php echo WP_CRON_LOCK_TIMEOUT, ' seconds'; ?>
333
+
334
+ <?php if ($condition == 2) { ?>
335
+ <br>
336
+ A non standard (<?php echo MINUTE_IN_SECONDS ?> seconds) value is specified probably in your <code>wp-config.php</code>.
337
+ <?php } ?>
338
+ </td>
339
+ </tr>
340
+
341
+
342
+ <?php
343
+ $condition = (defined('NEWSLETTER_CRON_WARNINGS') && !NEWSLETTER_CRON_WARNINGS) ? 2 : 1;
344
+ ?>
345
+ <tr>
346
+
347
+ <td>
348
+ <code>NEWSLETTER_CRON_WARNINGS</code>
349
+ </td>
350
+ <td class="status">
351
+ <?php tnp_status_print_flag($condition) ?>
352
+ </td>
353
+ <td>
354
+ <?php if ($condition == 2) { ?>
355
+ Scheduler warnings are disabled in your <code>wp-config.php</code> with the constant <code>NEWSLETTER_CRON_WARNINGS</code> set to true.
356
+ <?php } else { ?>
357
+ Scheduler warnings are enabled
358
+ <?php } ?>
359
+ </td>
360
+ </tr>
361
+
362
+ <?php
363
+ $condition = has_filter('pre_reschedule_event') ? 2 : 1;
364
+ $functions = $system->get_hook_functions('pre_reschedule_event');
365
+ ?>
366
+ <tr>
367
+ <td><code>pre_reschedule_event</code></td>
368
+ <td class="status">
369
+ <?php tnp_status_print_flag($condition) ?>
370
+ </td>
371
+ <td>
372
+ <?php if ($condition == 2) { ?>
373
+ One or more plugin are filtering the jobs rescheduling. If a recurrent job (like the newsletter generation with Automated) disappers
374
+ this is a good starting point.<br><br>
375
+ <?php } ?>
376
+ Attached functions:<br>
377
+ <?php echo $functions?$functions:'[none]' ?>
378
+ </td>
379
+ </tr>
380
+
381
+ <?php
382
+ $transient = get_transient('doing_cron');
383
+ ?>
384
+ <tr>
385
+ <td>Transient <code>doing_cron</code></td>
386
+ <td class="status">
387
+ <?php //tnp_status_print_flag($condition) ?>
388
+ </td>
389
+ <td>
390
+ <?php if ($transient) { ?>
391
+ <?php
392
+ echo esc_html($transient);
393
+ if (is_numeric($transient)) {
394
+ echo ' (', $controls->print_date((int) $transient), ')';
395
+ }
396
+ ?>
397
+ <?php } else { ?>
398
+ [unset]
399
+ <?php } ?>
400
+ <br><br>
401
+ When set it means the scheduler is executing background jobs. Install the WP Crontol plugin to have more information about
402
+ your site background jobs.
403
+ </td>
404
+ </tr>
405
+ </table>
406
+
407
+ </form>
408
+ </div>
409
+
410
+ <?php include NEWSLETTER_DIR . '/tnp-footer.php'; ?>
411
+
412
+ </div>
plugin.php CHANGED
@@ -4,7 +4,7 @@
4
  Plugin Name: Newsletter
5
  Plugin URI: https://www.thenewsletterplugin.com/plugins/newsletter
6
  Description: Newsletter is a cool plugin to create your own subscriber list, to send newsletters, to build your business. <strong>Before update give a look to <a href="https://www.thenewsletterplugin.com/category/release">this page</a> to know what's changed.</strong>
7
- Version: 7.2.8
8
  Author: Stefano Lissa & The Newsletter Team
9
  Author URI: https://www.thenewsletterplugin.com
10
  Disclaimer: Use at your own risk. No warranty expressed or implied is provided.
@@ -37,7 +37,7 @@ if (version_compare(phpversion(), '5.6', '<')) {
37
  return;
38
  }
39
 
40
- define('NEWSLETTER_VERSION', '7.2.8');
41
 
42
  global $newsletter, $wpdb;
43
 
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.2.9
8
  Author: Stefano Lissa & The Newsletter Team
9
  Author URI: https://www.thenewsletterplugin.com
10
  Disclaimer: Use at your own risk. No warranty expressed or implied is provided.
37
  return;
38
  }
39
 
40
+ define('NEWSLETTER_VERSION', '7.2.9');
41
 
42
  global $newsletter, $wpdb;
43
 
readme.txt CHANGED
@@ -1,7 +1,7 @@
1
  === Newsletter ===
2
  Tags: newsletter, email marketing, welcome email, signup forms, contact, lead generation, marketing automation
3
  Tested up to: 5.8.1
4
- Stable tag: 7.2.8
5
  Contributors: satollo,webagile,michael-travan
6
  License: GPLv2 or later
7
  License URI: https://www.gnu.org/licenses/gpl-2.0.html
@@ -118,6 +118,14 @@ Thank you, The Newsletter Team
118
 
119
  == Changelog ==
120
 
 
 
 
 
 
 
 
 
121
  = 7.2.8 =
122
 
123
  * Fixed the print_date() when no time is provided
1
  === Newsletter ===
2
  Tags: newsletter, email marketing, welcome email, signup forms, contact, lead generation, marketing automation
3
  Tested up to: 5.8.1
4
+ Stable tag: 7.2.9
5
  Contributors: satollo,webagile,michael-travan
6
  License: GPLv2 or later
7
  License URI: https://www.gnu.org/licenses/gpl-2.0.html
118
 
119
  == Changelog ==
120
 
121
+ = 7.2.9 =
122
+
123
+ * Fixed generic action button confirmation popup not showing the message
124
+ * [SECURITY] Pre parsing of IP addresses on security panel
125
+ * [SECURITY] Comments allowed on IP address list on security panel
126
+ * [COMPOSER] Fixed preset name/subject
127
+ * Improved generated forms for accessibility
128
+
129
  = 7.2.8 =
130
 
131
  * Fixed the print_date() when no time is provided
subscription/antibot.php CHANGED
@@ -1,156 +1,183 @@
1
- <?php
2
- /* @var $this NewsletterSubscription */
3
- defined('ABSPATH') || exit;
4
-
5
- include_once NEWSLETTER_INCLUDES_DIR . '/controls.php';
6
- $controls = new NewsletterControls();
7
-
8
- if ($controls->is_action()) {
9
-
10
- if ($controls->is_action('save')) {
11
-
12
- $controls->data['ip_blacklist'] = $this->to_array($controls->data['ip_blacklist']);
13
- $controls->data['address_blacklist'] = $this->to_array($controls->data['address_blacklist']);
14
-
15
- $this->save_options($controls->data, 'antibot');
16
- $controls->add_message_saved();
17
- }
18
- } else {
19
- $controls->data = $this->get_options('antibot');
20
- }
21
- ?>
22
-
23
- <div class="wrap" id="tnp-wrap">
24
-
25
- <?php include NEWSLETTER_DIR . '/tnp-header.php'; ?>
26
-
27
- <div id="tnp-heading">
28
-
29
- <h2><?php _e('Security', 'newsletter') ?></h2>
30
- <?php $controls->page_help('https://www.thenewsletterplugin.com/documentation/subscription/antiflood') ?>
31
-
32
- </div>
33
-
34
- <div id="tnp-body">
35
-
36
- <form method="post" action="">
37
- <?php $controls->init(); ?>
38
-
39
-
40
- <p>
41
- <?php $controls->button_save() ?>
42
- </p>
43
-
44
- <div id="tabs">
45
- <ul>
46
- <li><a href="#tabs-general"><?php _e('Security', 'newsletter') ?></a></li>
47
- <li><a href="#tabs-blacklists"><?php _e('Blacklists', 'newsletter') ?></a></li>
48
- </ul>
49
-
50
- <div id="tabs-general">
51
-
52
-
53
- <table class="form-table">
54
- <tr>
55
- <th><?php _e('Disable antibot', 'newsletter') ?></th>
56
- <td>
57
- <?php $controls->yesno('disabled'); ?>
58
- <?php $controls->help('https://www.thenewsletterplugin.com/documentation/subscription/antiflood') ?>
59
- <p class="description">
60
- <?php _e('Disable for ajax form submission', 'newsletter'); ?>
61
- </p>
62
- </td>
63
- </tr>
64
-
65
- <tr>
66
- <th><?php $controls->field_label('Akismet', '/documentation/subscription/antiflood#akismet')?></th>
67
- <td>
68
- <?php
69
- $controls->select('akismet', array(
70
- 0 => __('Disabled', 'newsletter'),
71
- 1 => __('Enabled', 'newsletter')
72
- ));
73
- ?>
74
- </td>
75
- </tr>
76
-
77
- <tr>
78
- <th><?php $controls->field_label(__('Antiflood', 'newsletter'), '/documentation/subscription/antiflood#antiflood') ?></th>
79
- <td>
80
- <?php
81
- $controls->select('antiflood', array(
82
- 0 => __('Disabled', 'newsletter'),
83
- 5 => '5 ' . __('seconds', 'newsletter'),
84
- 10 => '10 ' . __('seconds', 'newsletter'),
85
- 15 => '15 ' . __('seconds', 'newsletter'),
86
- 30 => '30 ' . __('seconds', 'newsletter'),
87
- 60 => '1 ' . __('minute', 'newsletter'),
88
- 120 => '2 ' . __('minutes', 'newsletter'),
89
- 300 => '5 ' . __('minutes', 'newsletter'),
90
- 600 => '10 ' . __('minutes', 'newsletter'),
91
- 900 => '15 ' . __('minutes', 'newsletter'),
92
- 1800 => '30 ' . __('minutes', 'newsletter'),
93
- 360 => '60 ' . __('minutes', 'newsletter')
94
- ));
95
- ?>
96
- </td>
97
- </tr>
98
- <tr>
99
- <th>
100
- <?php $controls->field_label(__('Captcha', 'newsletter'), '/documentation/subscription/antiflood/#captcha') ?>
101
- </th>
102
- <td>
103
- <?php $controls->enabled('captcha'); ?>
104
- </td>
105
- </tr>
106
- <?php /*
107
- <tr>
108
- <th><?php _e('Domain check', 'newsletter') ?></th>
109
- <td>
110
- <?php
111
- $controls->yesno('domain_check');
112
- ?>
113
- </td>
114
- </tr>
115
- */ ?>
116
-
117
- </table>
118
-
119
-
120
- </div>
121
-
122
- <div id="tabs-blacklists">
123
- <table class="form-table">
124
- <tr>
125
- <th>
126
- <?php $controls->field_label(__('IP black list', 'newsletter'), '/documentation/subscription/antiflood/#ip-blacklist') ?>
127
- </th>
128
- <td>
129
- <?php $controls->textarea('ip_blacklist'); ?>
130
- <p class="description"><?php _e('One per line', 'newsletter') ?></p>
131
- </td>
132
- </tr>
133
- <tr>
134
- <th>
135
- <?php $controls->field_label(__('Address black list', 'newsletter'), '/documentation/subscription/antiflood/#email-blacklist') ?>
136
- </th>
137
- <td>
138
- <?php $controls->textarea('address_blacklist'); ?>
139
- <p class="description"><?php _e('One per line', 'newsletter') ?></p>
140
- </td>
141
- </tr>
142
- </table>
143
- </div>
144
-
145
- </div>
146
-
147
- <p>
148
- <?php $controls->button_save() ?>
149
- </p>
150
-
151
- </form>
152
- </div>
153
-
154
- <?php include NEWSLETTER_DIR . '/tnp-footer.php'; ?>
155
-
156
- </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /* @var $this NewsletterSubscription */
3
+ defined('ABSPATH') || exit;
4
+
5
+ include_once NEWSLETTER_INCLUDES_DIR . '/controls.php';
6
+ $controls = new NewsletterControls();
7
+
8
+ if ($controls->is_action()) {
9
+
10
+ if ($controls->is_action('save')) {
11
+ // Processing IPs
12
+ $list = $this->to_array($controls->data['ip_blacklist']);
13
+ $controls->data['ip_blacklist'] = [];
14
+ foreach ($list as $item) {
15
+ $item = trim($item);
16
+ if (substr($item, 0, 1) === '#') {
17
+ $controls->data['ip_blacklist'][] = $item;
18
+ continue;
19
+ }
20
+ $item = preg_replace( '|[^0-9a-fA-F:./]|', '', $item);
21
+ if (empty($item)) {
22
+ continue;
23
+ }
24
+ if (strpos($item, '/', 2)) {
25
+ list($ip, $bits) = explode('/', $item);
26
+ $bits = (int)$bits;
27
+ if (!$bits) continue;
28
+ $item = $ip . '/' . $bits;
29
+ } else {
30
+
31
+ }
32
+ $controls->data['ip_blacklist'][] = $item;
33
+ }
34
+
35
+ //$controls->data['ip_blacklist'] = $this->to_array($controls->data['ip_blacklist']);
36
+ $controls->data['address_blacklist'] = $this->to_array($controls->data['address_blacklist']);
37
+
38
+ $this->save_options($controls->data, 'antibot');
39
+ $controls->add_message_saved();
40
+ }
41
+ } else {
42
+ $controls->data = $this->get_options('antibot');
43
+ }
44
+ ?>
45
+
46
+ <div class="wrap" id="tnp-wrap">
47
+
48
+ <?php include NEWSLETTER_DIR . '/tnp-header.php'; ?>
49
+
50
+ <div id="tnp-heading">
51
+
52
+ <h2><?php _e('Security', 'newsletter') ?></h2>
53
+ <?php $controls->page_help('https://www.thenewsletterplugin.com/documentation/subscription/antiflood') ?>
54
+
55
+ </div>
56
+
57
+ <div id="tnp-body">
58
+
59
+ <form method="post" action="">
60
+ <?php $controls->init(); ?>
61
+
62
+
63
+ <p>
64
+ <?php $controls->button_save() ?>
65
+ </p>
66
+
67
+ <div id="tabs">
68
+ <ul>
69
+ <li><a href="#tabs-general"><?php _e('Security', 'newsletter') ?></a></li>
70
+ <li><a href="#tabs-blacklists"><?php _e('Blacklists', 'newsletter') ?></a></li>
71
+ </ul>
72
+
73
+ <div id="tabs-general">
74
+
75
+
76
+ <table class="form-table">
77
+ <tr>
78
+ <th><?php _e('Disable antibot', 'newsletter') ?></th>
79
+ <td>
80
+ <?php $controls->yesno('disabled'); ?>
81
+ <?php $controls->help('https://www.thenewsletterplugin.com/documentation/subscription/antiflood') ?>
82
+ <p class="description">
83
+ <?php _e('Disable for ajax form submission', 'newsletter'); ?>
84
+ </p>
85
+ </td>
86
+ </tr>
87
+
88
+ <tr>
89
+ <th><?php $controls->field_label('Akismet', '/documentation/subscription/antiflood#akismet')?></th>
90
+ <td>
91
+ <?php
92
+ $controls->select('akismet', array(
93
+ 0 => __('Disabled', 'newsletter'),
94
+ 1 => __('Enabled', 'newsletter')
95
+ ));
96
+ ?>
97
+ </td>
98
+ </tr>
99
+
100
+ <tr>
101
+ <th><?php $controls->field_label(__('Antiflood', 'newsletter'), '/documentation/subscription/antiflood#antiflood') ?></th>
102
+ <td>
103
+ <?php
104
+ $controls->select('antiflood', array(
105
+ 0 => __('Disabled', 'newsletter'),
106
+ 5 => '5 ' . __('seconds', 'newsletter'),
107
+ 10 => '10 ' . __('seconds', 'newsletter'),
108
+ 15 => '15 ' . __('seconds', 'newsletter'),
109
+ 30 => '30 ' . __('seconds', 'newsletter'),
110
+ 60 => '1 ' . __('minute', 'newsletter'),
111
+ 120 => '2 ' . __('minutes', 'newsletter'),
112
+ 300 => '5 ' . __('minutes', 'newsletter'),
113
+ 600 => '10 ' . __('minutes', 'newsletter'),
114
+ 900 => '15 ' . __('minutes', 'newsletter'),
115
+ 1800 => '30 ' . __('minutes', 'newsletter'),
116
+ 360 => '60 ' . __('minutes', 'newsletter')
117
+ ));
118
+ ?>
119
+ </td>
120
+ </tr>
121
+ <tr>
122
+ <th>
123
+ <?php $controls->field_label(__('Captcha', 'newsletter'), '/documentation/subscription/antiflood/#captcha') ?>
124
+ </th>
125
+ <td>
126
+ <?php $controls->enabled('captcha'); ?>
127
+ </td>
128
+ </tr>
129
+ <?php /*
130
+ <tr>
131
+ <th><?php _e('Domain check', 'newsletter') ?></th>
132
+ <td>
133
+ <?php
134
+ $controls->yesno('domain_check');
135
+ ?>
136
+ </td>
137
+ </tr>
138
+ */ ?>
139
+
140
+ </table>
141
+
142
+
143
+ </div>
144
+
145
+ <div id="tabs-blacklists">
146
+ <table class="form-table">
147
+ <tr>
148
+ <th>
149
+ <?php $controls->field_label(__('IP black list', 'newsletter'), '/documentation/subscription/antiflood/#ip-blacklist') ?>
150
+ </th>
151
+ <td>
152
+ <?php $controls->textarea('ip_blacklist'); ?>
153
+ <p class="description">
154
+ <?php _e('One per line', 'newsletter') ?>
155
+ IPv4 (aaa.bbb.ccc.ddd) supported. IPv6 supported. CIDR supported only for IPv4. Lines starting with # are
156
+ considered comments.
157
+ </p>
158
+ </td>
159
+ </tr>
160
+ <tr>
161
+ <th>
162
+ <?php $controls->field_label(__('Address black list', 'newsletter'), '/documentation/subscription/antiflood/#email-blacklist') ?>
163
+ </th>
164
+ <td>
165
+ <?php $controls->textarea('address_blacklist'); ?>
166
+ <p class="description"><?php _e('One per line', 'newsletter') ?></p>
167
+ </td>
168
+ </tr>
169
+ </table>
170
+ </div>
171
+
172
+ </div>
173
+
174
+ <p>
175
+ <?php $controls->button_save() ?>
176
+ </p>
177
+
178
+ </form>
179
+ </div>
180
+
181
+ <?php include NEWSLETTER_DIR . '/tnp-footer.php'; ?>
182
+
183
+ </div>
subscription/subscription.php CHANGED
@@ -1285,7 +1285,7 @@ class NewsletterSubscription extends NewsletterModule {
1285
  if (!$suffix) {
1286
  $suffix = $name;
1287
  }
1288
- $buffer = '<label for="tnp-' . $suffix . '">';
1289
  if (isset($attrs['label'])) {
1290
  if (empty($attrs['label'])) {
1291
  return;
@@ -1316,6 +1316,12 @@ class NewsletterSubscription extends NewsletterModule {
1316
  }
1317
 
1318
  function shortcode_newsletter_field($attrs, $content = '') {
 
 
 
 
 
 
1319
  $this->setup_form_options();
1320
  $language = $this->get_current_language();
1321
 
@@ -1325,9 +1331,10 @@ class NewsletterSubscription extends NewsletterModule {
1325
 
1326
  if ($name == 'email') {
1327
  $buffer .= '<div class="tnp-field tnp-field-email">';
 
1328
  $buffer .= $this->_shortcode_label('email', $attrs);
1329
 
1330
- $buffer .= '<input class="tnp-email" type="email" name="ne" value=""';
1331
  if (isset($attrs['placeholder'])) {
1332
  $buffer .= ' placeholder="' . esc_attr($attrs['placeholder']) . '"';
1333
  }
@@ -1348,7 +1355,7 @@ class NewsletterSubscription extends NewsletterModule {
1348
  $buffer .= '<div class="tnp-field tnp-field-firstname">';
1349
  $buffer .= $this->_shortcode_label('name', $attrs);
1350
 
1351
- $buffer .= '<input class="tnp-name" type="text" name="nn" value=""';
1352
  if (isset($attrs['placeholder'])) {
1353
  $buffer .= ' placeholder="' . esc_attr($attrs['placeholder']) . '"';
1354
  }
@@ -1364,7 +1371,7 @@ class NewsletterSubscription extends NewsletterModule {
1364
  $buffer .= '<div class="tnp-field tnp-field-surname">';
1365
  $buffer .= $this->_shortcode_label('surname', $attrs);
1366
 
1367
- $buffer .= '<input class="tnp-surname" type="text" name="ns" value=""';
1368
  if (isset($attrs['placeholder'])) {
1369
  $buffer .= ' placeholder="' . esc_attr($attrs['placeholder']) . '"';
1370
  }
@@ -1395,8 +1402,9 @@ class NewsletterSubscription extends NewsletterModule {
1395
  return '<input type="hidden" name="nl[]" value="' . esc_attr($list->id) . '">';
1396
  }
1397
 
1398
- $buffer .= '<div class="tnp-field tnp-field-checkbox tnp-field-list"><label for="nl' . esc_attr($list->id) . '">';
1399
- $buffer .= '<input type="checkbox" id="nl' . esc_attr($list->id) . '" name="nl[]" value="' . esc_attr($list->id) . '"';
 
1400
  if (isset($attrs['checked'])) {
1401
  $buffer .= ' checked';
1402
  }
@@ -1435,8 +1443,9 @@ class NewsletterSubscription extends NewsletterModule {
1435
  } else {
1436
 
1437
  foreach ($lists as $list) {
1438
- $buffer .= '<div class="tnp-field tnp-field-checkbox tnp-field-list"><label for="nl' . $list->id . '">';
1439
- $buffer .= '<input type="checkbox" id="nl' . $list->id . '" name="nl[]" value="' . $list->id . '"';
 
1440
  if ($list->checked) {
1441
  $buffer .= ' checked';
1442
  }
@@ -1531,8 +1540,9 @@ class NewsletterSubscription extends NewsletterModule {
1531
 
1532
  $buffer .= '<div class="tnp-field tnp-field-checkbox tnp-field-privacy">';
1533
 
1534
- $buffer .= '<input type="checkbox" name="ny" required class="tnp-privacy" id="tnp-privacy"> ';
1535
- $buffer .= '<label for="tnp-privacy">';
 
1536
  if (!empty($attrs['url'])) {
1537
  $buffer .= '<a target="_blank" href="' . esc_attr($attrs['url']) . '">';
1538
  }
1285
  if (!$suffix) {
1286
  $suffix = $name;
1287
  }
1288
+ $buffer = '<label for="' . esc_attr($attrs['id']) . '">';
1289
  if (isset($attrs['label'])) {
1290
  if (empty($attrs['label'])) {
1291
  return;
1316
  }
1317
 
1318
  function shortcode_newsletter_field($attrs, $content = '') {
1319
+ // Counter to create unique ID for checkbox and labels
1320
+ static $idx = 0;
1321
+
1322
+ $idx++;
1323
+ $attrs['id'] = 'tnp-' . $idx;
1324
+
1325
  $this->setup_form_options();
1326
  $language = $this->get_current_language();
1327
 
1331
 
1332
  if ($name == 'email') {
1333
  $buffer .= '<div class="tnp-field tnp-field-email">';
1334
+
1335
  $buffer .= $this->_shortcode_label('email', $attrs);
1336
 
1337
+ $buffer .= '<input class="tnp-email" type="email" name="ne" id="'. esc_attr($attrs['id']) . '" value=""';
1338
  if (isset($attrs['placeholder'])) {
1339
  $buffer .= ' placeholder="' . esc_attr($attrs['placeholder']) . '"';
1340
  }
1355
  $buffer .= '<div class="tnp-field tnp-field-firstname">';
1356
  $buffer .= $this->_shortcode_label('name', $attrs);
1357
 
1358
+ $buffer .= '<input class="tnp-name" type="text" name="nn" id="'. esc_attr($attrs['id']) . '" value=""';
1359
  if (isset($attrs['placeholder'])) {
1360
  $buffer .= ' placeholder="' . esc_attr($attrs['placeholder']) . '"';
1361
  }
1371
  $buffer .= '<div class="tnp-field tnp-field-surname">';
1372
  $buffer .= $this->_shortcode_label('surname', $attrs);
1373
 
1374
+ $buffer .= '<input class="tnp-surname" type="text" name="ns" id="'. esc_attr($attrs['id']) . '" value=""';
1375
  if (isset($attrs['placeholder'])) {
1376
  $buffer .= ' placeholder="' . esc_attr($attrs['placeholder']) . '"';
1377
  }
1402
  return '<input type="hidden" name="nl[]" value="' . esc_attr($list->id) . '">';
1403
  }
1404
 
1405
+ $idx++;
1406
+ $buffer .= '<div class="tnp-field tnp-field-checkbox tnp-field-list"><label for="tnp-' . $idx . '">';
1407
+ $buffer .= '<input type="checkbox" id="tnp-' . $idx . '" name="nl[]" value="' . esc_attr($list->id) . '"';
1408
  if (isset($attrs['checked'])) {
1409
  $buffer .= ' checked';
1410
  }
1443
  } else {
1444
 
1445
  foreach ($lists as $list) {
1446
+ $idx++;
1447
+ $buffer .= '<div class="tnp-field tnp-field-checkbox tnp-field-list"><label for="nl' . $idx . '">';
1448
+ $buffer .= '<input type="checkbox" id="nl' . $idx . '" name="nl[]" value="' . $list->id . '"';
1449
  if ($list->checked) {
1450
  $buffer .= ' checked';
1451
  }
1540
 
1541
  $buffer .= '<div class="tnp-field tnp-field-checkbox tnp-field-privacy">';
1542
 
1543
+ $idx++;
1544
+ $buffer .= '<input type="checkbox" name="ny" required class="tnp-privacy" id="tnp-' . $idx . '"> ';
1545
+ $buffer .= '<label for="tnp-' . $idx . '">';
1546
  if (!empty($attrs['url'])) {
1547
  $buffer .= '<a target="_blank" href="' . esc_attr($attrs['url']) . '">';
1548
  }
tnp-header.php CHANGED
@@ -1,340 +1,340 @@
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
- <script>
34
- function tnp_close_promotion() {
35
- jQuery.post(ajaxurl + "?action=tnp_hide_promotion", {id: 'june-2020'});
36
- document.getElementById('tnp-promotion-bar').style.display = 'none';
37
- }
38
- </script>
39
-
40
- <?php if (get_option('newsletter_promotion') !== 'june-2020' && time() < gmmktime(0, 0, 0, 7, 15, 2020)) { ?>
41
- <div id="tnp-promotion-bar">
42
- <a href="https://www.thenewsletterplugin.com/premium?utm_source=plugin-bar&utm_campaign=june-2020" onclick="tnp_close_promotion(); return true;" target="_blank">50% discount offer until July, 15. Check it out.</a>
43
- &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
44
- <a href="javascript:void(tnp_close_promotion('june-2020'))">No, thank you!</a>
45
- </div>
46
- <?php } ?>
47
-
48
-
49
- <div class="tnp-drowpdown" id="tnp-header">
50
- <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>
51
- <ul>
52
- <li><a href="#"><i class="fas fa-users"></i> <?php _e('Subscribers', 'newsletter') ?> <i class="fas fa-chevron-down"></i></a>
53
- <ul>
54
- <li>
55
- <a href="?page=newsletter_users_index"><i class="fas fa-search"></i> <?php _e('Search And Edit', 'newsletter') ?>
56
- <small><?php _e('Add, edit, search', 'newsletter') ?></small></a>
57
- </li>
58
-
59
- <li>
60
- <a href="?page=newsletter_profile_index"><i class="fas fa-user-circle"></i> <?php _e('Profile page', 'newsletter') ?>
61
- <small><?php _e('The subscriber personal profile editing panel', 'newsletter') ?></small></a>
62
- </li>
63
-
64
- <?php if (!class_exists('NewsletterImport')) { ?>
65
- <li>
66
- <a href="?page=newsletter_users_import"><i class="fas fa-upload"></i> <?php _e('Import', 'newsletter') ?>
67
- <small><?php _e('Import from external sources', 'newsletter') ?></small></a>
68
- </li>
69
- <?php } ?>
70
-
71
- <li>
72
- <a href="?page=newsletter_users_export"><i class="fas fa-download"></i> <?php _e('Export', 'newsletter') ?>
73
- <small><?php _e('Export your subscribers list', 'newsletter') ?></small></a>
74
- </li>
75
-
76
- <li>
77
- <a href="?page=newsletter_users_massive"><i class="fas fa-wrench"></i> <?php _e('Maintenance', 'newsletter') ?>
78
- <small><?php _e('Massive actions: change list, clean up, ...', 'newsletter') ?></small></a>
79
- </li>
80
-
81
- <li>
82
- <a href="?page=newsletter_users_statistics"><i class="fas fa-chart-bar"></i> <?php _e('Statistics', 'newsletter') ?>
83
- <small><?php _e('All about your subscribers', 'newsletter') ?></small></a>
84
- </li>
85
-
86
- <?php newsletter_print_entries('subscribers') ?>
87
- </ul>
88
- </li>
89
- <li><a href="#"><i class="fas fa-list"></i> <?php _e('List Building', 'newsletter') ?> <i class="fas fa-chevron-down"></i></a>
90
- <ul>
91
- <li>
92
- <a href="?page=newsletter_subscription_profile"><i class="fas fa-check-square"></i> <?php _e('Subscription Form Fields, Buttons, Labels', 'newsletter') ?>
93
- <small><?php _e('When and what data to collect', 'newsletter') ?></small></a>
94
- </li>
95
-
96
- <li>
97
- <a href="?page=newsletter_subscription_options"><i class="fas fa-sign-in-alt"></i> <?php _e('Subscription', 'newsletter') ?>
98
- <small><?php _e('The subscription process in detail', 'newsletter') ?></small></a>
99
- </li>
100
-
101
- <li>
102
- <a href="?page=newsletter_subscription_lists"><i class="fas fa-th-list"></i> <?php _e('Lists', 'newsletter') ?>
103
- <small><?php _e('Profile the subscribers for a better targeting', 'newsletter') ?></small></a>
104
- </li>
105
-
106
- <li>
107
- <a href="?page=newsletter_subscription_antibot"><i class="fas fa-lock"></i> <?php _e('Security', 'newsletter') ?>
108
- <small><?php _e('Spam subscriptions control', 'newsletter') ?></small></a>
109
- </li>
110
-
111
- <li>
112
- <a href="?page=newsletter_unsubscription_index"><i class="fas fa-sign-out-alt"></i> <?php _e('Unsubscription', 'newsletter') ?>
113
- <small><?php _e('How to give the last goodbye (or avoid it!)', 'newsletter') ?></small></a>
114
- </li>
115
-
116
- <?php
117
- newsletter_print_entries('subscription');
118
- ?>
119
- </ul>
120
- </li>
121
-
122
- <li>
123
- <a href="#"><i class="fas fa-newspaper"></i> <?php _e('Newsletters', 'newsletter') ?> <i class="fas fa-chevron-down"></i></a>
124
- <ul>
125
- <li>
126
- <a href="?page=newsletter_emails_composer"><i class="fas fa-plus"></i> <?php _e('Create newsletter', 'newsletter') ?>
127
- <small><?php _e('Start your new campaign', 'newsletter') ?></small></a>
128
- </li>
129
-
130
- <li>
131
- <a href="?page=newsletter_emails_index"><i class="fas fa-newspaper"></i> <?php _e('Newsletters', 'newsletter') ?>
132
- <small><?php _e('The classic "write & send" newsletters', 'newsletter') ?></small></a>
133
- </li>
134
-
135
- <li>
136
- <a href="<?php echo NewsletterStatistics::instance()->get_index_url() ?>"><i class="fas fa-chart-bar"></i> <?php _e('Statistics', 'newsletter') ?>
137
- <small><?php _e('Tracking configuration and basic data', 'newsletter') ?></small></a>
138
- </li>
139
- <?php
140
- newsletter_print_entries('newsletters');
141
- ?>
142
- </ul>
143
- </li>
144
-
145
- <li>
146
- <a href="#"><i class="fas fa-cog"></i> <?php _e('Settings', 'newsletter') ?> <i class="fas fa-chevron-down"></i></a>
147
- <ul>
148
- <?php if ($is_administrator) { ?>
149
- <li>
150
- <a href="?page=newsletter_main_main"><i class="fas fa-cogs"></i> <?php _e('General Settings', 'newsletter') ?>
151
- <small><?php _e('Delivery speed, sender details, ...', 'newsletter') ?></small></a>
152
- </li>
153
- <?php if (!class_exists('NewsletterSmtp')) { ?>
154
- <li>
155
- <a href="?page=newsletter_main_smtp"><i class="fas fa-server"></i> <?php _e('SMTP', 'newsletter') ?>
156
- <small><?php _e('External mail server', 'newsletter') ?></small>
157
- </a>
158
- </li>
159
- <?php } ?>
160
- <?php } ?>
161
-
162
- <li>
163
- <a href="?page=newsletter_main_info"><i class="fas fa-info"></i> <?php _e('Company Info', 'newsletter') ?>
164
- <small><?php _e('Social, address, logo and general info', 'newsletter') ?></small></a>
165
- </li>
166
-
167
- <li>
168
- <a href="?page=newsletter_subscription_template"><i class="fas fa-file-alt"></i> <?php _e('Messages Template', 'newsletter') ?>
169
- <small><?php _e('Change the look of your service emails', 'newsletter') ?></small></a>
170
- </li>
171
-
172
- <?php
173
- newsletter_print_entries('settings');
174
- ?>
175
- </ul>
176
- </li>
177
-
178
- <?php if ($is_administrator) { ?>
179
- <li>
180
- <a href="#"><i class="fas fa-thermometer"></i> <?php _e('System', 'newsletter') ?>
181
- <?php if ($warning) { ?>
182
- <i class="fas fa-exclamation-triangle" style="color: red;"></i>
183
- <?php } ?>
184
- </a>
185
- <ul>
186
- <li>
187
- <a href="<?php echo admin_url('site-health.php') ?> "><i class="fas fa-file"></i> <?php _e('Site health') ?>
188
- <small><?php _e('WP native site health checks', 'newsletter') ?></small></a>
189
- </li>
190
- <li>
191
- <a href="?page=newsletter_main_delivery"><i class="fas fa-file"></i> <?php _e('Delivery Diagnostic', 'newsletter') ?>
192
- <small><?php _e('Delivery analysis and test', 'newsletter') ?></small></a>
193
- </li>
194
- <li>
195
- <a href="?page=newsletter_main_scheduler"><i class="fas fa-robot"></i> <?php _e('Scheduler', 'newsletter') ?>
196
- <small><?php _e('', 'newsletter') ?></small></a>
197
- </li>
198
- <li>
199
- <a href="?page=newsletter_main_status"><i class="fas fa-file"></i> <?php _e('Status', 'newsletter') ?>
200
- <small><?php _e('Checks and parameters', 'newsletter') ?></small></a>
201
- </li>
202
-
203
- <li>
204
- <a href="?page=newsletter_main_logs"><i class="fas fa-file"></i> <?php _e('Logs', 'newsletter') ?>
205
- <small><?php _e('Plugin and addons logs', 'newsletter') ?></small></a>
206
- </li>
207
- </ul>
208
- </li>
209
- <?php } ?>
210
-
211
- <?php
212
- $license_data = Newsletter::instance()->get_license_data();
213
- $premium_url = 'https://www.thenewsletterplugin.com/premium?utm_source=plugin&utm_medium=link&utm_campaign=header&utm_content=' . urlencode($_GET['page']);
214
- ?>
215
-
216
- <?php if (empty($license_data)) { ?>
217
- <li class="tnp-professional-extensions-button"><a href="<?php echo $premium_url ?>" target="_blank">
218
- <i class="fas fa-trophy"></i> <?php _e('Get Professional Addons', 'newsletter') ?></a>
219
- </li>
220
- <?php } elseif (is_wp_error($license_data)) { ?>
221
- <li class="tnp-professional-extensions-button-red">
222
- <a href="?page=newsletter_main_main"><i class="fas fa-hand-paper" style="color: white"></i> <?php _e('Unable to check', 'newsletter') ?></a>
223
- </li>
224
-
225
- <?php } elseif ($license_data->expire == 0) { ?>
226
-
227
- <li class="tnp-professional-extensions-button"><a href="<?php echo $premium_url ?>" target="_blank">
228
- <i class="fas fa-trophy"></i> <?php _e('Get Professional Addons', 'newsletter') ?></a>
229
- </li>
230
-
231
- <?php } elseif ($license_data->expire < time()) { ?>
232
-
233
- <li class="tnp-professional-extensions-button-red">
234
- <a href="?page=newsletter_main_main"><i class="fas fa-hand-paper" style="color: white"></i> <?php _e('License expired', 'newsletter') ?></a>
235
- </li>
236
-
237
- <?php } elseif ($license_data->expire >= time()) { ?>
238
-
239
- <?php $p = class_exists('NewsletterExtensions') ? 'newsletter_extensions_index' : 'newsletter_main_extensions'; ?>
240
- <li class="tnp-professional-extensions-button">
241
- <a href="?page=<?php echo $p ?>"><i class="fas fa-check-square"></i> <?php _e('License active', 'newsletter') ?></a>
242
- </li>
243
-
244
-
245
- <?php } ?>
246
- </ul>
247
- </div>
248
-
249
- <?php if (isset($_GET['debug']) || !isset($dismissed['newsletter-shortcode'])) { ?>
250
- <?php
251
- // Check of Newsletter dedicated page
252
- if (!empty(Newsletter::instance()->options['page'])) {
253
- if (get_post_status(Newsletter::instance()->options['page']) === 'publish') {
254
- $content = get_post_field('post_content', Newsletter::instance()->options['page']);
255
- // With and without attributes
256
- if (strpos($content, '[newsletter]') === false && strpos($content, '[newsletter ') === false) {
257
- ?>
258
- <div class="tnp-notice">
259
- <a href="<?php echo $_SERVER['REQUEST_URI'] . '&noheader=1&dismiss=newsletter-shortcode' ?>" class="tnp-dismiss">&times;</a>
260
- The Newsletter dedicated page does not contain the [newsletter] shortcode. If you're using a visual composer it could be ok.
261
- <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>.
262
-
263
- </div>
264
- <?php
265
- }
266
- }
267
- }
268
- ?>
269
- <?php } ?>
270
-
271
- <?php if (isset($_GET['debug']) || !isset($dismissed['rate']) && $user_count > 300) { ?>
272
- <div class="tnp-notice">
273
- <a href="<?php echo $_SERVER['REQUEST_URI'] . '&noheader=1&dismiss=rate' ?>" class="tnp-dismiss">&times;</a>
274
-
275
- We never asked before and we're curious: <a href="http://wordpress.org/plugins/newsletter/" target="_blank">would you rate this plugin</a>?
276
- (few seconds required - account on WordPress.org required, every blog owner should have one...). <strong>Really appreciated, The Newsletter Team</strong>.
277
-
278
- </div>
279
- <?php } ?>
280
-
281
- <?php if (isset($_GET['debug']) || !isset($dismissed['newsletter-page']) && empty(Newsletter::instance()->options['page'])) { ?>
282
- <div class="tnp-notice">
283
- <a href="<?php echo $_SERVER['REQUEST_URI'] . '&noheader=1&dismiss=newsletter-page' ?>" class="tnp-dismiss">&times;</a>
284
-
285
- You should create a blog page to show the subscription form and the subscription messages. Go to the
286
- <a href="?page=newsletter_main_main">general settings panel</a> to configure it.
287
-
288
- </div>
289
- <?php } ?>
290
-
291
- <?php if (isset($_GET['debug']) || !isset($dismissed['newsletter-subscribe']) && get_option('newsletter_install_time') && get_option('newsletter_install_time') < time() - 86400 * 15) { ?>
292
- <div class="tnp-notice">
293
- <a href="<?php echo $_SERVER['REQUEST_URI'] . '&noheader=1&dismiss=newsletter-subscribe' ?>" class="tnp-dismiss">&times;</a>
294
- If you want to be informed of important updates of Newsletter, you may want to subscribe to our newsletter<br>
295
- <form action="https://www.thenewsletterplugin.com/?na=s" target="_blank" method="post">
296
- <input type="hidden" value="plugin-header" name="nr">
297
- <input type="hidden" value="3" name="nl[]">
298
- <input type="hidden" value="single" name="optin">
299
- <input type="email" name="ne" value="<?php echo esc_attr(get_option('admin_email')) ?>">
300
- <input type="submit" value="<?php esc_attr_e('Subscribe', 'newsletter') ?>">
301
- </form>
302
- </div>
303
- <?php } ?>
304
-
305
- <?php
306
- if (!defined('NEWSLETTER_CRON_WARNINGS') || NEWSLETTER_CRON_WARNINGS) {
307
- $x = NewsletterSystem::instance()->get_job_status();
308
- if ($x !== NewsletterSystem::JOB_OK) {
309
- echo '<div class="tnpc-warning">The are issues with the delivery engine. Please <a href="?page=newsletter_main_scheduler">check them here</a>.</div>';
310
- }
311
- }
312
- ?>
313
-
314
- <?php
315
- if ($_GET['page'] !== 'newsletter_emails_edit') {
316
-
317
- $last_failed_newsletters = Newsletter::instance()->get_emails_by_field('status', TNP_Email::STATUS_ERROR);
318
-
319
- foreach ($last_failed_newsletters as $newsletter) {
320
- echo '<div class="tnpc-error">';
321
- printf(__('Newsletter "%s" stopped by fatal error.', 'newsletter'), esc_html($newsletter->subject));
322
- echo '&nbsp;';
323
- $c = new NewsletterControls();
324
- $c->btn_link('?page=newsletter_emails_edit&id=' . $newsletter->id, __('Check', 'newsletter'));
325
- echo '</div>';
326
- }
327
- }
328
- ?>
329
-
330
- <div id="tnp-notification">
331
- <?php
332
- if (isset($controls)) {
333
- $controls->show();
334
- $controls->messages = '';
335
- $controls->errors = '';
336
- }
337
- ?>
338
- </div>
339
-
340
-
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
+ <script>
34
+ function tnp_close_promotion() {
35
+ jQuery.post(ajaxurl + "?action=tnp_hide_promotion", {id: 'june-2020'});
36
+ document.getElementById('tnp-promotion-bar').style.display = 'none';
37
+ }
38
+ </script>
39
+
40
+ <?php if (get_option('newsletter_promotion') !== 'june-2020' && time() < gmmktime(0, 0, 0, 7, 15, 2020)) { ?>
41
+ <div id="tnp-promotion-bar">
42
+ <a href="https://www.thenewsletterplugin.com/premium?utm_source=plugin-bar&utm_campaign=june-2020" onclick="tnp_close_promotion(); return true;" target="_blank">50% discount offer until July, 15. Check it out.</a>
43
+ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
44
+ <a href="javascript:void(tnp_close_promotion('june-2020'))">No, thank you!</a>
45
+ </div>
46
+ <?php } ?>
47
+
48
+
49
+ <div class="tnp-drowpdown" id="tnp-header">
50
+ <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>
51
+ <ul>
52
+ <li><a href="#"><i class="fas fa-users"></i> <?php _e('Subscribers', 'newsletter') ?> <i class="fas fa-chevron-down"></i></a>
53
+ <ul>
54
+ <li>
55
+ <a href="?page=newsletter_users_index"><i class="fas fa-search"></i> <?php _e('Search And Edit', 'newsletter') ?>
56
+ <small><?php _e('Add, edit, search', 'newsletter') ?></small></a>
57
+ </li>
58
+
59
+ <li>
60
+ <a href="?page=newsletter_profile_index"><i class="fas fa-user-circle"></i> <?php _e('Profile page', 'newsletter') ?>
61
+ <small><?php _e('The subscriber personal profile editing panel', 'newsletter') ?></small></a>
62
+ </li>
63
+
64
+ <?php if (!class_exists('NewsletterImport')) { ?>
65
+ <li>
66
+ <a href="?page=newsletter_users_import"><i class="fas fa-upload"></i> <?php _e('Import', 'newsletter') ?>
67
+ <small><?php _e('Import from external sources', 'newsletter') ?></small></a>
68
+ </li>
69
+ <?php } ?>
70
+
71
+ <li>
72
+ <a href="?page=newsletter_users_export"><i class="fas fa-download"></i> <?php _e('Export', 'newsletter') ?>
73
+ <small><?php _e('Export your subscribers list', 'newsletter') ?></small></a>
74
+ </li>
75
+
76
+ <li>
77
+ <a href="?page=newsletter_users_massive"><i class="fas fa-wrench"></i> <?php _e('Maintenance', 'newsletter') ?>
78
+ <small><?php _e('Massive actions: change list, clean up, ...', 'newsletter') ?></small></a>
79
+ </li>
80
+
81
+ <li>
82
+ <a href="?page=newsletter_users_statistics"><i class="fas fa-chart-bar"></i> <?php _e('Statistics', 'newsletter') ?>
83
+ <small><?php _e('All about your subscribers', 'newsletter') ?></small></a>
84
+ </li>
85
+
86
+ <?php newsletter_print_entries('subscribers') ?>
87
+ </ul>
88
+ </li>
89
+ <li><a href="#"><i class="fas fa-list"></i> <?php _e('List Building', 'newsletter') ?> <i class="fas fa-chevron-down"></i></a>
90
+ <ul>
91
+ <li>
92
+ <a href="?page=newsletter_subscription_profile"><i class="fas fa-check-square"></i> <?php _e('Subscription Form Fields, Buttons, Labels', 'newsletter') ?>
93
+ <small><?php _e('When and what data to collect', 'newsletter') ?></small></a>
94
+ </li>
95
+
96
+ <li>
97
+ <a href="?page=newsletter_subscription_options"><i class="fas fa-sign-in-alt"></i> <?php _e('Subscription', 'newsletter') ?>
98
+ <small><?php _e('The subscription process in detail', 'newsletter') ?></small></a>
99
+ </li>
100
+
101
+ <li>
102
+ <a href="?page=newsletter_subscription_lists"><i class="fas fa-th-list"></i> <?php _e('Lists', 'newsletter') ?>
103
+ <small><?php _e('Profile the subscribers for a better targeting', 'newsletter') ?></small></a>
104
+ </li>
105
+
106
+ <li>
107
+ <a href="?page=newsletter_subscription_antibot"><i class="fas fa-lock"></i> <?php _e('Security', 'newsletter') ?>
108
+ <small><?php _e('Spam subscriptions control', 'newsletter') ?></small></a>
109
+ </li>
110
+
111
+ <li>
112
+ <a href="?page=newsletter_unsubscription_index"><i class="fas fa-sign-out-alt"></i> <?php _e('Unsubscription', 'newsletter') ?>
113
+ <small><?php _e('How to give the last goodbye (or avoid it!)', 'newsletter') ?></small></a>
114
+ </li>
115
+
116
+ <?php
117
+ newsletter_print_entries('subscription');
118
+ ?>
119
+ </ul>
120
+ </li>
121
+
122
+ <li>
123
+ <a href="#"><i class="fas fa-newspaper"></i> <?php _e('Newsletters', 'newsletter') ?> <i class="fas fa-chevron-down"></i></a>
124
+ <ul>
125
+ <li>
126
+ <a href="?page=newsletter_emails_composer"><i class="fas fa-plus"></i> <?php _e('Create newsletter', 'newsletter') ?>
127
+ <small><?php _e('Start your new campaign', 'newsletter') ?></small></a>
128
+ </li>
129
+
130
+ <li>
131
+ <a href="?page=newsletter_emails_index"><i class="fas fa-newspaper"></i> <?php _e('Newsletters', 'newsletter') ?>
132
+ <small><?php _e('The classic "write & send" newsletters', 'newsletter') ?></small></a>
133
+ </li>
134
+
135
+ <li>
136
+ <a href="<?php echo NewsletterStatistics::instance()->get_index_url() ?>"><i class="fas fa-chart-bar"></i> <?php _e('Statistics', 'newsletter') ?>
137
+ <small><?php _e('Tracking configuration and basic data', 'newsletter') ?></small></a>
138
+ </li>
139
+ <?php
140
+ newsletter_print_entries('newsletters');
141
+ ?>
142
+ </ul>
143
+ </li>
144
+
145
+ <li>
146
+ <a href="#"><i class="fas fa-cog"></i> <?php _e('Settings', 'newsletter') ?> <i class="fas fa-chevron-down"></i></a>
147
+ <ul>
148
+ <?php if ($is_administrator) { ?>
149
+ <li>
150
+ <a href="?page=newsletter_main_main"><i class="fas fa-cogs"></i> <?php _e('General Settings', 'newsletter') ?>
151
+ <small><?php _e('Delivery speed, sender details, ...', 'newsletter') ?></small></a>
152
+ </li>
153
+ <?php if (!class_exists('NewsletterSmtp')) { ?>
154
+ <li>
155
+ <a href="?page=newsletter_main_smtp"><i class="fas fa-server"></i> <?php _e('SMTP', 'newsletter') ?>
156
+ <small><?php _e('External mail server', 'newsletter') ?></small>
157
+ </a>
158
+ </li>
159
+ <?php } ?>
160
+ <?php } ?>
161
+
162
+ <li>
163
+ <a href="?page=newsletter_main_info"><i class="fas fa-info"></i> <?php _e('Company Info', 'newsletter') ?>
164
+ <small><?php _e('Social, address, logo and general info', 'newsletter') ?></small></a>
165
+ </li>
166
+
167
+ <li>
168
+ <a href="?page=newsletter_subscription_template"><i class="fas fa-file-alt"></i> <?php _e('Messages Template', 'newsletter') ?>
169
+ <small><?php _e('Change the look of your service emails', 'newsletter') ?></small></a>
170
+ </li>
171
+
172
+ <?php
173
+ newsletter_print_entries('settings');
174
+ ?>
175
+ </ul>
176
+ </li>
177
+
178
+ <?php if ($is_administrator) { ?>
179
+ <li>
180
+ <a href="#"><i class="fas fa-thermometer"></i> <?php _e('System', 'newsletter') ?>
181
+ <?php if ($warning) { ?>
182
+ <i class="fas fa-exclamation-triangle" style="color: red;"></i>
183
+ <?php } ?>
184
+ </a>
185
+ <ul>
186
+ <li>
187
+ <a href="<?php echo admin_url('site-health.php') ?> "><i class="fas fa-file"></i> <?php _e('Site health') ?>
188
+ <small><?php _e('WP native site health checks', 'newsletter') ?></small></a>
189
+ </li>
190
+ <li>
191
+ <a href="?page=newsletter_main_delivery"><i class="fas fa-file"></i> <?php _e('Delivery Diagnostic', 'newsletter') ?>
192
+ <small><?php _e('Delivery analysis and test', 'newsletter') ?></small></a>
193
+ </li>
194
+ <li>
195
+ <a href="?page=newsletter_main_scheduler"><i class="fas fa-robot"></i> <?php _e('Scheduler', 'newsletter') ?>
196
+ <small><?php _e('', 'newsletter') ?></small></a>
197
+ </li>
198
+ <li>
199
+ <a href="?page=newsletter_main_status"><i class="fas fa-file"></i> <?php _e('Status', 'newsletter') ?>
200
+ <small><?php _e('Checks and parameters', 'newsletter') ?></small></a>
201
+ </li>
202
+
203
+ <li>
204
+ <a href="?page=newsletter_main_logs"><i class="fas fa-file"></i> <?php _e('Logs', 'newsletter') ?>
205
+ <small><?php _e('Plugin and addons logs', 'newsletter') ?></small></a>
206
+ </li>
207
+ </ul>
208
+ </li>
209
+ <?php } ?>
210
+
211
+ <?php
212
+ $license_data = Newsletter::instance()->get_license_data();
213
+ $premium_url = 'https://www.thenewsletterplugin.com/premium?utm_source=plugin&utm_medium=link&utm_campaign=header&utm_content=' . urlencode($_GET['page']);
214
+ ?>
215
+
216
+ <?php if (empty($license_data)) { ?>
217
+ <li class="tnp-professional-extensions-button"><a href="<?php echo $premium_url ?>" target="_blank">
218
+ <i class="fas fa-trophy"></i> <?php _e('Get Professional Addons', 'newsletter') ?></a>
219
+ </li>
220
+ <?php } elseif (is_wp_error($license_data)) { ?>
221
+ <li class="tnp-professional-extensions-button-red">
222
+ <a href="?page=newsletter_main_main"><i class="fas fa-hand-paper" style="color: white"></i> <?php _e('Unable to check', 'newsletter') ?></a>
223
+ </li>
224
+
225
+ <?php } elseif ($license_data->expire == 0) { ?>
226
+
227
+ <li class="tnp-professional-extensions-button"><a href="<?php echo $premium_url ?>" target="_blank">
228
+ <i class="fas fa-trophy"></i> <?php _e('Get Professional Addons', 'newsletter') ?></a>
229
+ </li>
230
+
231
+ <?php } elseif ($license_data->expire < time()) { ?>
232
+
233
+ <li class="tnp-professional-extensions-button-red">
234
+ <a href="?page=newsletter_main_main"><i class="fas fa-hand-paper" style="color: white"></i> <?php _e('License expired', 'newsletter') ?></a>
235
+ </li>
236
+
237
+ <?php } elseif ($license_data->expire >= time()) { ?>
238
+
239
+ <?php $p = class_exists('NewsletterExtensions') ? 'newsletter_extensions_index' : 'newsletter_main_extensions'; ?>
240
+ <li class="tnp-professional-extensions-button">
241
+ <a href="?page=<?php echo $p ?>"><i class="fas fa-check-square"></i> <?php _e('License active', 'newsletter') ?></a>
242
+ </li>
243
+
244
+
245
+ <?php } ?>
246
+ </ul>
247
+ </div>
248
+
249
+ <?php if (isset($_GET['debug']) || !isset($dismissed['newsletter-shortcode'])) { ?>
250
+ <?php
251
+ // Check of Newsletter dedicated page
252
+ if (!empty(Newsletter::instance()->options['page'])) {
253
+ if (get_post_status(Newsletter::instance()->options['page']) === 'publish') {
254
+ $content = get_post_field('post_content', Newsletter::instance()->options['page']);
255
+ // With and without attributes
256
+ if (strpos($content, '[newsletter]') === false && strpos($content, '[newsletter ') === false) {
257
+ ?>
258
+ <div class="tnp-notice">
259
+ <a href="<?php echo $_SERVER['REQUEST_URI'] . '&noheader=1&dismiss=newsletter-shortcode' ?>" class="tnp-dismiss">&times;</a>
260
+ The Newsletter dedicated page does not contain the [newsletter] shortcode. If you're using a visual composer it could be ok.
261
+ <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>.
262
+
263
+ </div>
264
+ <?php
265
+ }
266
+ }
267
+ }
268
+ ?>
269
+ <?php } ?>
270
+
271
+ <?php if (isset($_GET['debug']) || !isset($dismissed['rate']) && $user_count > 300) { ?>
272
+ <div class="tnp-notice">
273
+ <a href="<?php echo $_SERVER['REQUEST_URI'] . '&noheader=1&dismiss=rate' ?>" class="tnp-dismiss">&times;</a>
274
+
275
+ We never asked before and we're curious: <a href="http://wordpress.org/plugins/newsletter/" target="_blank">would you rate this plugin</a>?
276
+ (few seconds required - account on WordPress.org required, every blog owner should have one...). <strong>Really appreciated, The Newsletter Team</strong>.
277
+
278
+ </div>
279
+ <?php } ?>
280
+
281
+ <?php if (isset($_GET['debug']) || !isset($dismissed['newsletter-page']) && empty(Newsletter::instance()->options['page'])) { ?>
282
+ <div class="tnp-notice">
283
+ <a href="<?php echo $_SERVER['REQUEST_URI'] . '&noheader=1&dismiss=newsletter-page' ?>" class="tnp-dismiss">&times;</a>
284
+
285
+ You should create a blog page to show the subscription form and the subscription messages. Go to the
286
+ <a href="?page=newsletter_main_main">general settings panel</a> to configure it.
287
+
288
+ </div>
289
+ <?php } ?>
290
+
291
+ <?php if (isset($_GET['debug']) || !isset($dismissed['newsletter-subscribe']) && get_option('newsletter_install_time') && get_option('newsletter_install_time') < time() - 86400 * 15) { ?>
292
+ <div class="tnp-notice">
293
+ <a href="<?php echo $_SERVER['REQUEST_URI'] . '&noheader=1&dismiss=newsletter-subscribe' ?>" class="tnp-dismiss">&times;</a>
294
+ If you want to be informed of important updates of Newsletter, you may want to subscribe to our newsletter<br>
295
+ <form action="https://www.thenewsletterplugin.com/?na=s" target="_blank" method="post">
296
+ <input type="hidden" value="plugin-header" name="nr">
297
+ <input type="hidden" value="3" name="nl[]">
298
+ <input type="hidden" value="single" name="optin">
299
+ <input type="email" name="ne" value="<?php echo esc_attr(get_option('admin_email')) ?>">
300
+ <input type="submit" value="<?php esc_attr_e('Subscribe', 'newsletter') ?>">
301
+ </form>
302
+ </div>
303
+ <?php } ?>
304
+
305
+ <?php
306
+ if (!defined('NEWSLETTER_CRON_WARNINGS') || NEWSLETTER_CRON_WARNINGS) {
307
+ $x = NewsletterSystem::instance()->get_job_status();
308
+ if ($x !== NewsletterSystem::JOB_OK) {
309
+ echo '<div class="tnpc-warning">The are issues with the delivery engine. Please <a href="?page=newsletter_main_scheduler">check them here</a>.</div>';
310
+ }
311
+ }
312
+ ?>
313
+
314
+ <?php
315
+ if ($_GET['page'] !== 'newsletter_emails_edit') {
316
+
317
+ $last_failed_newsletters = Newsletter::instance()->get_emails_by_field('status', TNP_Email::STATUS_ERROR);
318
+
319
+ foreach ($last_failed_newsletters as $newsletter) {
320
+ echo '<div class="tnpc-error">';
321
+ printf(__('Newsletter "%s" stopped by fatal error.', 'newsletter'), esc_html($newsletter->subject));
322
+ echo '&nbsp;';
323
+ $c = new NewsletterControls();
324
+ $c->btn_link('?page=newsletter_emails_edit&id=' . $newsletter->id, __('Check', 'newsletter'));
325
+ echo '</div>';
326
+ }
327
+ }
328
+ ?>
329
+
330
+ <div id="tnp-notification">
331
+ <?php
332
+ if (isset($controls)) {
333
+ $controls->show();
334
+ $controls->messages = '';
335
+ $controls->errors = '';
336
+ }
337
+ ?>
338
+ </div>
339
+
340
+
users/massive.php CHANGED
@@ -216,7 +216,7 @@ if ($controls->is_action('update_inactive')) {
216
 
217
  </td>
218
  <td>
219
- <?php $controls->button_confirm('update_inactive', __('Update', 'newsletter')); ?>
220
  </td>
221
  </tr>
222
 
@@ -228,7 +228,7 @@ if ($controls->is_action('update_inactive')) {
228
  <?php $controls->language('language', false) ?> <?php _e('subscribers without a language', 'newsletter') ?>
229
  </td>
230
  <td>
231
- <?php $controls->button_confirm('language', '&raquo;'); ?>
232
  </td>
233
  </tr>
234
  <?php } ?>
216
 
217
  </td>
218
  <td>
219
+ <?php $controls->btn('update_inactive', __('Update', 'newsletter'), ['confirm'=>true]); ?>
220
  </td>
221
  </tr>
222
 
228
  <?php $controls->language('language', false) ?> <?php _e('subscribers without a language', 'newsletter') ?>
229
  </td>
230
  <td>
231
+ <?php $controls->btn('language', '&raquo;', ['confirm'=>true]); ?>
232
  </td>
233
  </tr>
234
  <?php } ?>