Newsletter - Version 7.3.0

Version Description

  • Fixed header block layout with (logo only layout)
  • Check for conflicts on newsletter saving
  • Added the subscriber complained status (actually not managed automatically)
Download this release

Release Info

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

Code changes from version 7.2.9 to 7.3.0

admin/admin.css CHANGED
@@ -1502,3 +1502,23 @@ span.tnp-email-status.tnp-email-status--error {
1502
  width: 30px!important;
1503
  height: 30px!important;
1504
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1502
  width: 30px!important;
1503
  height: 30px!important;
1504
  }
1505
+
1506
+ #tnp-body .unsubscribed {
1507
+ color: gray;
1508
+ }
1509
+
1510
+ #tnp-body .confirmed {
1511
+ color: darkgreen;
1512
+ }
1513
+
1514
+ #tnp-body .not-confirmed {
1515
+ color: darkgray;
1516
+ }
1517
+
1518
+ #tnp-body .complained {
1519
+ color: red;
1520
+ }
1521
+
1522
+ #tnp-body .bounced {
1523
+ color: darkorange;
1524
+ }
emails/blocks/header/block.php CHANGED
@@ -1,55 +1,49 @@
1
- <?php
2
-
3
- /*
4
- * Name: Header
5
- * Section: header
6
- * Description: Default header with company info
7
- */
8
-
9
- $default_options = array(
10
- 'font_family' => '',
11
- 'font_size' => '',
12
- 'font_color' => '',
13
- 'font_weight' => '',
14
- 'logo_height' => 100,
15
- 'block_padding_top' => 15,
16
- 'block_padding_bottom' => 15,
17
- 'block_padding_left' => 15,
18
- 'block_padding_right' => 15,
19
- 'block_background' => '',
20
- 'layout' => ''
21
- );
22
- $options = array_merge($default_options, $options);
23
-
24
- $text_font_family = empty($options['font_family']) ? $global_text_font_family : $options['font_family'];
25
- $text_font_size = empty($options['font_size']) ? $global_text_font_size : $options['font_size'];
26
- $text_font_color = empty($options['font_color']) ? $global_text_font_color : $options['font_color'];
27
- $text_font_weight = empty($options['font_weight']) ? $global_text_font_weight : $options['font_weight'];
28
-
29
- if (empty($info['header_logo']['id'])) {
30
- $media = false;
31
- } else {
32
- $media = tnp_get_media($info['header_logo']['id'], 'large');
33
- if ($media) {
34
- $media->alt = $info['header_title'];
35
- $media->link = home_url();
36
- $media->set_height($options['logo_height']);
37
- }
38
- }
39
-
40
- $empty = !$media && empty($info['header_sub']) && empty($info['header_title']);
41
-
42
- if ($empty) {
43
- echo '<p>Please, set your company info.</p>';
44
- } elseif ($options['layout'] === 'logo') {
45
- include __DIR__ . '/layout-logo.php';
46
- return;
47
- } elseif ($options['layout'] === 'titlemotto') {
48
- include __DIR__ . '/layout-titlemotto.php';
49
- return;
50
- } else {
51
- include __DIR__ . '/layout-default.php';
52
- return;
53
- }
54
- ?>
55
-
1
+ <?php
2
+
3
+ /*
4
+ * Name: Header
5
+ * Section: header
6
+ * Description: Default header with company info
7
+ */
8
+
9
+ $default_options = array(
10
+ 'font_family' => '',
11
+ 'font_size' => '',
12
+ 'font_color' => '',
13
+ 'font_weight' => '',
14
+ 'logo_height' => 100,
15
+ 'block_padding_top' => 15,
16
+ 'block_padding_bottom' => 15,
17
+ 'block_padding_left' => 15,
18
+ 'block_padding_right' => 15,
19
+ 'block_background' => '',
20
+ 'layout' => ''
21
+ );
22
+ $options = array_merge($default_options, $options);
23
+
24
+ if (empty($info['header_logo']['id'])) {
25
+ $media = false;
26
+ } else {
27
+ $media = tnp_get_media($info['header_logo']['id'], 'large');
28
+ if ($media) {
29
+ $media->alt = $info['header_title'];
30
+ $media->link = home_url();
31
+ }
32
+ }
33
+
34
+ $empty = !$media && empty($info['header_sub']) && empty($info['header_title']);
35
+
36
+ if ($empty) {
37
+ echo '<p>Please, set your company info.</p>';
38
+ } elseif ($options['layout'] === 'logo') {
39
+ include __DIR__ . '/layout-logo.php';
40
+ return;
41
+ } elseif ($options['layout'] === 'titlemotto') {
42
+ include __DIR__ . '/layout-titlemotto.php';
43
+ return;
44
+ } else {
45
+ include __DIR__ . '/layout-default.php';
46
+ return;
47
+ }
48
+ ?>
49
+
 
 
 
 
 
 
emails/blocks/header/layout-default.php CHANGED
@@ -1,40 +1,47 @@
1
- <?php
2
- $text_style = TNP_Composer::get_text_style($options, '', $composer);
3
- ?>
4
- <style>
5
- .text {
6
- <?php $text_style->echo_css(0.9) ?>
7
- text-decoration: none;
8
- line-height: normal;
9
- padding: 10px;
10
- }
11
-
12
- .title {
13
- <?php $text_style->echo_css(1.2) ?>
14
- text-decoration: none;
15
- line-height: normal;
16
- }
17
-
18
- .logo {
19
- <?php $text_style->echo_css() ?>
20
- line-height: normal !important;
21
- }
22
- </style>
23
-
24
- <table border="0" cellpadding="0" cellspacing="0" width="100%" style="margin: 0; border-collapse: collapse;">
25
- <tr>
26
- <td align="center" width="50%" inline-class="logo">
27
- <?php if ($media) { ?>
28
- <?php echo TNP_Composer::image($media) ?>
29
- <?php } else { ?>
30
- [logo]
31
- <?php } ?>
32
- </td>
33
- <td width="50%" align="center" style="padding: 10px">
34
- <a href="<?php echo home_url() ?>" target="_blank" inline-class="title">
35
- <?php echo esc_attr($info['header_title']) ?>
36
- </a>
37
- <div inline-class="text"><?php echo esc_attr($info['header_sub']) ?></div>
38
- </td>
39
- </tr>
40
- </table>
 
 
 
 
 
 
 
1
+ <?php
2
+ $text_style = TNP_Composer::get_text_style($options, '', $composer);
3
+ if ($media) {
4
+ $image_width = (int) (600 - $options['block_padding_left'] - $options['block_padding_right']) / 2;
5
+ if ($options['logo_width']) {
6
+ $image_width = min($options['logo_width'], $image_width);
7
+ }
8
+ $media->set_width($image_width);
9
+ }
10
+ ?>
11
+ <style>
12
+ .text {
13
+ <?php $text_style->echo_css(0.9) ?>
14
+ text-decoration: none;
15
+ line-height: normal;
16
+ padding: 10px;
17
+ }
18
+
19
+ .title {
20
+ <?php $text_style->echo_css(1.2) ?>
21
+ text-decoration: none;
22
+ line-height: normal;
23
+ }
24
+
25
+ .logo {
26
+ <?php $text_style->echo_css() ?>
27
+ line-height: normal !important;
28
+ }
29
+ </style>
30
+
31
+ <table border="0" cellpadding="0" cellspacing="0" width="100%" style="margin: 0; border-collapse: collapse;">
32
+ <tr>
33
+ <td align="center" width="50%" inline-class="logo">
34
+ <?php if ($media) { ?>
35
+ <?php echo TNP_Composer::image($media) ?>
36
+ <?php } else { ?>
37
+ [logo]
38
+ <?php } ?>
39
+ </td>
40
+ <td width="50%" align="center" style="padding: 10px">
41
+ <a href="<?php echo home_url() ?>" target="_blank" inline-class="title">
42
+ <?php echo esc_attr($info['header_title']) ?>
43
+ </a>
44
+ <div inline-class="text"><?php echo esc_attr($info['header_sub']) ?></div>
45
+ </td>
46
+ </tr>
47
+ </table>
emails/blocks/header/layout-logo.php CHANGED
@@ -1,7 +1,12 @@
1
- <?php
2
- if (!$media) {
3
- echo '<p>Set your logo on company info page, thank you.</p>';
4
- return;
5
- }
6
- ?>
7
- <a href="<?php echo esc_url($media->link) ?>" target="_blank"><img src="<?php echo $media->url ?>" width="<?php echo $media->width ?>" height="<?php echo $media->height ?>" border="0" alt="<?php echo esc_attr($media->alt) ?>" inline-class="image"></a>
 
 
 
 
 
1
+ <?php
2
+ if (!$media) {
3
+ echo '<p>Set your logo on company info page, thank you.</p>';
4
+ return;
5
+ }
6
+ $image_width = 600-$options['block_padding_left']-$options['block_padding_right'];
7
+ if ($options['logo_width']) {
8
+ $image_width = min($options['logo_width'], $image_width);
9
+ }
10
+ $media->set_width($image_width);
11
+ ?>
12
+ <a href="<?php echo esc_url($media->link) ?>" target="_blank"><img src="<?php echo $media->url ?>" width="<?php echo $media->width ?>" height="<?php echo $media->height ?>" border="0" alt="<?php echo esc_attr($media->alt) ?>" class="fluid"></a>
emails/blocks/header/options.php CHANGED
@@ -1,30 +1,30 @@
1
- <?php
2
- /* @var $options array contains all the options the current block we're ediging contains */
3
- /* @var $controls NewsletterControls */
4
- /* @var $fields NewsletterFields */
5
- ?>
6
-
7
- <p>
8
- <?php echo sprintf(__('Company data can be globally set on <a href="%s" target="_blank">company info panel</a>.', 'newsletter'), '?page=newsletter_main_info'); ?><br>
9
- Use an image block to have a single top banner.
10
- </p>
11
-
12
- <?php
13
- $fields->select('layout', __('Layout', 'newsletter'), [
14
- '' => __('Default', 'newsletter'),
15
- 'logo' => __('Only the logo', 'newsletter'),
16
- 'titlemotto' => 'Title and motto'
17
- ])
18
- ?>
19
-
20
- <?php
21
- $fields->font('font', __('Text', 'newsletter'), [
22
- 'family_default' => true,
23
- 'size_default' => true,
24
- 'weight_default' => true
25
- ])
26
- ?>
27
-
28
- <?php $fields->number('logo_height', __('Height')) ?>
29
-
30
- <?php $fields->block_commons() ?>
1
+ <?php
2
+ /* @var $options array contains all the options the current block we're ediging contains */
3
+ /* @var $controls NewsletterControls */
4
+ /* @var $fields NewsletterFields */
5
+ ?>
6
+
7
+ <p>
8
+ <?php echo sprintf(__('Company data can be globally set on <a href="%s" target="_blank">company info panel</a>.', 'newsletter'), '?page=newsletter_main_info'); ?><br>
9
+ Use an image block to have a single top banner.
10
+ </p>
11
+
12
+ <?php
13
+ $fields->select('layout', __('Layout', 'newsletter'), [
14
+ '' => __('Default', 'newsletter'),
15
+ 'logo' => __('Only the logo', 'newsletter'),
16
+ 'titlemotto' => 'Title and motto'
17
+ ])
18
+ ?>
19
+
20
+ <?php
21
+ $fields->font('font', __('Text', 'newsletter'), [
22
+ 'family_default' => true,
23
+ 'size_default' => true,
24
+ 'weight_default' => true
25
+ ])
26
+ ?>
27
+
28
+ <?php $fields->number('logo_width', __('Width')) ?>
29
+
30
+ <?php $fields->block_commons() ?>
emails/composer.php CHANGED
@@ -1,149 +1,169 @@
1
- <?php
2
- /* @var $this NewsletterEmails */
3
- defined('ABSPATH') || exit;
4
-
5
- require_once NEWSLETTER_INCLUDES_DIR . '/controls.php';
6
-
7
- $controls = new NewsletterControls();
8
- $module = NewsletterEmails::instance();
9
-
10
- wp_enqueue_style('tnpc-newsletter-style', home_url('/') . '?na=emails-composer-css');
11
-
12
- include NEWSLETTER_INCLUDES_DIR . '/codemirror.php';
13
-
14
- $email = null;
15
-
16
- if ($controls->is_action()) {
17
-
18
- if ($controls->is_action('save_preset')) {
19
- $this->admin_logger->info('Saving new preset: ' . $controls->data['subject']);
20
- // Create new preset email
21
- $email = new stdClass();
22
- TNP_Composer::update_email($email, $controls);
23
- $email->type = NewsletterEmails::PRESET_EMAIL_TYPE;
24
- $email->editor = NewsletterEmails::EDITOR_COMPOSER;
25
- $email->subject = $module->sanitize_preset_name($controls->data['subject']);
26
- $email->message = $controls->data['message'];
27
-
28
- $email = Newsletter::instance()->save_email($email);
29
-
30
- $redirect = $module->get_admin_page_url('composer');
31
- $controls->js_redirect($redirect);
32
-
33
- return;
34
- }
35
-
36
- if ($controls->is_action('update_preset') && !empty($_POST['preset_id'])) {
37
- $this->admin_logger->info('Updating preset ' . $_POST['preset_id']);
38
- $email = Newsletter::instance()->get_email((int) $_POST['preset_id']);
39
- TNP_Composer::update_email($email, $controls);
40
-
41
- if ($email->subject != sanitize_text_field($controls->data['subject'])) {
42
- $email->subject = $module->sanitize_preset_name($controls->data['subject']);
43
- }
44
-
45
- // We store only the blocks, after the TNP_Composer::update_email(...) call we have the full HTML
46
- $email->message = $controls->data['message'];
47
-
48
- $email = Newsletter::instance()->save_email($email);
49
-
50
- $redirect = $module->get_admin_page_url('composer');
51
- $controls->js_redirect($redirect);
52
- }
53
-
54
-
55
- if (empty($_GET['id'])) {
56
-
57
- $this->admin_logger->info('Saving new newsletter from composer');
58
-
59
- // Create a new email
60
- $email = new stdClass();
61
- $email->status = 'new';
62
- $email->track = Newsletter::instance()->options['track'];
63
- $email->token = $module->get_token();
64
- $email->message_text = "This email requires a modern e-mail reader but you can view the email online here:\n{email_url}.\nThank you, " . wp_specialchars_decode(get_option('blogname'), ENT_QUOTES) .
65
- "\nTo change your subscription follow: {profile_url}.";
66
- $email->editor = NewsletterEmails::EDITOR_COMPOSER;
67
- $email->type = 'message';
68
- $email->send_on = time();
69
- $email->query = "select * from " . NEWSLETTER_USERS_TABLE . " where status='C'";
70
-
71
- TNP_Composer::update_email($email, $controls);
72
-
73
- $email = Newsletter::instance()->save_email($email);
74
- } else {
75
- $this->admin_logger->info('Saving newsletter ' . $_GET['id'] . ' from composer');
76
- $email = Newsletter::instance()->get_email($_GET['id']);
77
- TNP_Composer::update_email($email, $controls);
78
- $email = Newsletter::instance()->save_email($email);
79
- }
80
-
81
- $controls->add_message_saved();
82
-
83
- if ($controls->is_action('test')) {
84
- $module->send_test_email($module->get_email($email->id), $controls);
85
- }
86
-
87
- if ( $controls->is_action( 'send-test-to-email-address' ) ) {
88
- $custom_email = sanitize_email( $_POST['test_address_email'] );
89
- if ( ! empty( $custom_email ) ) {
90
- try {
91
- $message = $module->send_test_newsletter_to_email_address( $module->get_email( $email->id ), $custom_email );
92
- $controls->messages .= $message;
93
- } catch ( Exception $e ) {
94
- $controls->errors = __( 'Newsletter should be saved before send a test', 'newsletter' );
95
- }
96
- } else {
97
- $controls->errors = __( 'Empty email address', 'newsletter' );
98
- }
99
- }
100
-
101
- if ($controls->is_action('preview')) {
102
- $redirect = $module->get_admin_page_url('edit');
103
- } else {
104
- $redirect = $module->get_admin_page_url('composer');
105
- }
106
-
107
- $controls->js_redirect($redirect . '&id=' . $email->id);
108
-
109
- } else {
110
-
111
- if (!empty($_GET['id'])) {
112
- $email = Newsletter::instance()->get_email((int) $_GET['id']);
113
- }
114
- }
115
-
116
- TNP_Composer::prepare_controls($controls, $email);
117
- ?>
118
-
119
- <div id="tnp-notification">
120
- <?php
121
- $controls->show();
122
- $controls->messages = '';
123
- $controls->errors = '';
124
- ?>
125
- </div>
126
-
127
- <div class="wrap tnp-emails-composer" id="tnp-wrap">
128
-
129
- <?php $controls->composer_load_v2(true); ?>
130
-
131
- <div id="tnp-heading" class="tnp-composer-heading">
132
- <div class="tnpc-logo">
133
- <p>The Newsletter Plugin <strong>Composer</strong></p>
134
- </div>
135
- <div class="tnpc-controls">
136
- <form method="post" action="" id="tnpc-form">
137
- <?php $controls->init(); ?>
138
-
139
- <?php $controls->composer_fields_v2(); ?>
140
-
141
- <?php $controls->button('update_preset', __('Update preset', 'newsletter'), 'tnpc_update_preset(this.form)', 'update-preset-button'); ?>
142
- <?php $controls->button('save_preset', __('Save as preset', 'newsletter'), 'tnpc_save_preset(this.form)', 'save-preset-button'); ?>
143
- <?php $controls->button_confirm('reset', __('Back to last save', 'newsletter'), 'Are you sure?'); ?>
144
- <?php $controls->button('save', __('Save', 'newsletter'), 'tnpc_save(this.form); this.form.submit();'); ?>
145
- <?php $controls->button('preview', __('Next', 'newsletter') . ' &raquo;', 'tnpc_save(this.form); this.form.submit();'); ?>
146
- </form>
147
- </div>
148
- </div>
149
- </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /* @var $this NewsletterEmails */
3
+ defined('ABSPATH') || exit;
4
+
5
+ require_once NEWSLETTER_INCLUDES_DIR . '/controls.php';
6
+
7
+ $controls = new NewsletterControls();
8
+ $module = NewsletterEmails::instance();
9
+
10
+ wp_enqueue_style('tnpc-newsletter-style', home_url('/') . '?na=emails-composer-css');
11
+
12
+ include NEWSLETTER_INCLUDES_DIR . '/codemirror.php';
13
+
14
+ $email = null;
15
+
16
+ if ($controls->is_action()) {
17
+
18
+ if ($controls->is_action('save_preset')) {
19
+ $this->admin_logger->info('Saving new preset: ' . $controls->data['subject']);
20
+ // Create new preset email
21
+ $email = new stdClass();
22
+ TNP_Composer::update_email($email, $controls);
23
+ $email->type = NewsletterEmails::PRESET_EMAIL_TYPE;
24
+ $email->editor = NewsletterEmails::EDITOR_COMPOSER;
25
+ $email->subject = $module->sanitize_preset_name($controls->data['subject']);
26
+ $email->message = $controls->data['message'];
27
+
28
+ $email = Newsletter::instance()->save_email($email);
29
+
30
+ $redirect = $module->get_admin_page_url('composer');
31
+ $controls->js_redirect($redirect);
32
+
33
+ return;
34
+ }
35
+
36
+ if ($controls->is_action('update_preset') && !empty($_POST['preset_id'])) {
37
+ $this->admin_logger->info('Updating preset ' . $_POST['preset_id']);
38
+ $email = Newsletter::instance()->get_email((int) $_POST['preset_id']);
39
+ TNP_Composer::update_email($email, $controls);
40
+
41
+ if ($email->subject != sanitize_text_field($controls->data['subject'])) {
42
+ $email->subject = $module->sanitize_preset_name($controls->data['subject']);
43
+ }
44
+
45
+ // We store only the blocks, after the TNP_Composer::update_email(...) call we have the full HTML
46
+ $email->message = $controls->data['message'];
47
+
48
+ $email = Newsletter::instance()->save_email($email);
49
+
50
+ $redirect = $module->get_admin_page_url('composer');
51
+ $controls->js_redirect($redirect);
52
+ }
53
+
54
+
55
+ if (empty($_GET['id'])) {
56
+
57
+ $this->admin_logger->info('Saving new newsletter from composer');
58
+
59
+ // Create a new email
60
+ $email = new stdClass();
61
+ $email->status = 'new';
62
+ $email->track = Newsletter::instance()->options['track'];
63
+ $email->token = $module->get_token();
64
+ $email->message_text = "This email requires a modern e-mail reader but you can view the email online here:\n{email_url}.\nThank you, " . wp_specialchars_decode(get_option('blogname'), ENT_QUOTES) .
65
+ "\nTo change your subscription follow: {profile_url}.";
66
+ $email->editor = NewsletterEmails::EDITOR_COMPOSER;
67
+ $email->type = 'message';
68
+ $email->send_on = time();
69
+ $email->query = "select * from " . NEWSLETTER_USERS_TABLE . " where status='C'";
70
+
71
+ TNP_Composer::update_email($email, $controls);
72
+
73
+ $email = Newsletter::instance()->save_email($email);
74
+ if ($controls->is_action('preview')) {
75
+ $redirect = $module->get_admin_page_url('edit');
76
+ } else {
77
+ $redirect = $module->get_admin_page_url('composer');
78
+ }
79
+
80
+ $controls->js_redirect($redirect . '&id=' . $email->id);
81
+ } else {
82
+ $this->admin_logger->info('Saving newsletter ' . $_GET['id'] . ' from composer');
83
+ $email = Newsletter::instance()->get_email($_GET['id']);
84
+ if ($email->updated != $controls->data['updated']) {
85
+ $controls->errors = 'This newsletter has been modified by someone else. Cannot save.';
86
+ if (!empty($email->options['sender_email'])) {
87
+ $controls->data['sender_email'] = $email->options['sender_email'];
88
+ } else {
89
+ $controls->data['sender_email'] = Newsletter::instance()->options['sender_email'];
90
+ }
91
+
92
+ if (!empty($email->options['sender_name'])) {
93
+ $controls->data['sender_name'] = $email->options['sender_name'];
94
+ } else {
95
+ $controls->data['sender_name'] = Newsletter::instance()->options['sender_name'];
96
+ }
97
+ } else {
98
+ TNP_Composer::update_email($email, $controls);
99
+ $email->updated = time();
100
+ $email = Newsletter::instance()->save_email($email);
101
+ TNP_Composer::prepare_controls($controls, $email);
102
+ if ($controls->is_action('save')) {
103
+ $controls->add_message_saved();
104
+ }
105
+ }
106
+ }
107
+
108
+ if ($controls->is_action('preview')) {
109
+ $redirect = $module->get_admin_page_url('edit');
110
+ $controls->js_redirect($redirect . '&id=' . $email->id);
111
+ }
112
+
113
+ if ($controls->is_action('test')) {
114
+ $module->send_test_email($module->get_email($email->id), $controls);
115
+ }
116
+
117
+ if ($controls->is_action('send-test-to-email-address')) {
118
+ $custom_email = sanitize_email($_POST['test_address_email']);
119
+ if (!empty($custom_email)) {
120
+ try {
121
+ $message = $module->send_test_newsletter_to_email_address($module->get_email($email->id), $custom_email);
122
+ $controls->messages .= $message;
123
+ } catch (Exception $e) {
124
+ $controls->errors = __('Newsletter should be saved before send a test', 'newsletter');
125
+ }
126
+ } else {
127
+ $controls->errors = __('Empty email address', 'newsletter');
128
+ }
129
+ }
130
+ } else {
131
+
132
+ if (!empty($_GET['id'])) {
133
+ $email = Newsletter::instance()->get_email((int) $_GET['id']);
134
+ }
135
+ TNP_Composer::prepare_controls($controls, $email);
136
+ }
137
+ ?>
138
+
139
+ <div id="tnp-notification">
140
+ <?php
141
+ $controls->show();
142
+ $controls->messages = '';
143
+ $controls->errors = '';
144
+ ?>
145
+ </div>
146
+
147
+ <div class="wrap tnp-emails-composer" id="tnp-wrap">
148
+
149
+ <?php $controls->composer_load_v2(true); ?>
150
+
151
+ <div id="tnp-heading" class="tnp-composer-heading">
152
+ <div class="tnpc-logo">
153
+ <p>The Newsletter Plugin <strong>Composer</strong></p>
154
+ </div>
155
+ <div class="tnpc-controls">
156
+ <form method="post" action="" id="tnpc-form">
157
+ <?php $controls->init(); ?>
158
+
159
+ <?php $controls->composer_fields_v2(); ?>
160
+
161
+ <?php $controls->button('update_preset', __('Update preset', 'newsletter'), 'tnpc_update_preset(this.form)', 'update-preset-button'); ?>
162
+ <?php $controls->button('save_preset', __('Save as preset', 'newsletter'), 'tnpc_save_preset(this.form)', 'save-preset-button'); ?>
163
+ <?php $controls->button_confirm('reset', __('Back to last save', 'newsletter'), 'Are you sure?'); ?>
164
+ <?php $controls->button('save', __('Save', 'newsletter'), 'tnpc_save(this.form); this.form.submit();'); ?>
165
+ <?php $controls->button('preview', __('Next', 'newsletter') . ' &raquo;', 'tnpc_save(this.form); this.form.submit();'); ?>
166
+ </form>
167
+ </div>
168
+ </div>
169
+ </div>
emails/edit.php CHANGED
@@ -1,591 +1,597 @@
1
- <?php
2
- /* @var $this NewsletterEmails */
3
- defined('ABSPATH') || exit;
4
-
5
- /* @var $wpdb wpdb */
6
- require_once NEWSLETTER_INCLUDES_DIR . '/controls.php';
7
- $controls = new NewsletterControls();
8
- $module = NewsletterEmails::instance();
9
-
10
- function tnp_prepare_controls($email, $controls) {
11
- $controls->data = $email;
12
-
13
- foreach ($email['options'] as $name => $value) {
14
- $controls->data['options_' . $name] = $value;
15
- }
16
- }
17
-
18
- // Always required
19
- $email = $this->get_email($_GET['id'], ARRAY_A);
20
-
21
- if (empty($email)) {
22
- echo 'Wrong email identifier';
23
- return;
24
- }
25
-
26
- $email_id = $email['id'];
27
-
28
- /* Satus changes which require a reload */
29
- if ($controls->is_action('pause')) {
30
- $this->admin_logger->info('Newsletter ' . $email_id . ' paused');
31
- $wpdb->update(NEWSLETTER_EMAILS_TABLE, array('status' => 'paused'), array('id' => $email_id));
32
- $email = $this->get_email($_GET['id'], ARRAY_A);
33
- tnp_prepare_controls($email, $controls);
34
- }
35
-
36
- if ($controls->is_action('continue')) {
37
- $this->admin_logger->info('Newsletter ' . $email_id . ' restarted');
38
- $wpdb->update(NEWSLETTER_EMAILS_TABLE, array('status' => 'sending'), array('id' => $email_id));
39
- $email = $this->get_email($_GET['id'], ARRAY_A);
40
- tnp_prepare_controls($email, $controls);
41
- }
42
-
43
- if ($controls->is_action('abort')) {
44
- $this->admin_logger->info('Newsletter ' . $email_id . ' aborted');
45
- $wpdb->query("update " . NEWSLETTER_EMAILS_TABLE . " set last_id=0, sent=0, status='new' where id=" . $email_id);
46
- $email = $this->get_email($_GET['id'], ARRAY_A);
47
- tnp_prepare_controls($email, $controls);
48
- $controls->messages = __('Delivery definitively cancelled', 'newsletter');
49
- }
50
-
51
- if ($controls->is_action('change-private')) {
52
- $data = [];
53
- $data['private'] = $controls->data['private'] ? 1 : 0;
54
- $data['id'] = $email['id'];
55
- $email = $this->save_email($data, ARRAY_A);
56
- $controls->add_message_saved();
57
-
58
- tnp_prepare_controls($email, $controls);
59
- }
60
-
61
-
62
- $editor_type = $this->get_editor_type($email);
63
-
64
- // Backward compatibility: preferences conversion
65
- if (!$controls->is_action()) {
66
- if (!isset($email['options']['lists'])) {
67
-
68
- $options_profile = get_option('newsletter_profile');
69
-
70
- if (empty($controls->data['preferences_status_operator'])) {
71
- $email['options']['lists_operator'] = 'or';
72
- } else {
73
- $email['options']['lists_operator'] = 'and';
74
- }
75
- $controls->data['options_lists'] = array();
76
- $controls->data['options_lists_exclude'] = array();
77
-
78
- if (!empty($email['preferences'])) {
79
- $preferences = explode(',', $email['preferences']);
80
- $value = empty($email['options']['preferences_status']) ? 'on' : 'off';
81
-
82
- foreach ($preferences as $x) {
83
- if ($value == 'on') {
84
- $controls->data['options_lists'][] = $x;
85
- } else {
86
- $controls->data['options_lists_exclude'][] = $x;
87
- }
88
- }
89
- }
90
- }
91
- }
92
- // End backward compatibility
93
-
94
- if (!$controls->is_action()) {
95
- tnp_prepare_controls($email, $controls);
96
- }
97
-
98
- if ($controls->is_action('html')) {
99
-
100
- $this->admin_logger->info('Newsletter ' . $email_id . ' converted to HTML');
101
-
102
- $data = [];
103
- $data['editor'] = NewsletterEmails::EDITOR_HTML;
104
- $data['id'] = $email_id;
105
-
106
- // Backward compatibility: clean up the composer flag
107
- $data['options'] = $email['options'];
108
- unset($data['options']['composer']);
109
- // End backward compatibility
110
-
111
- $email = $this->save_email($data, ARRAY_A);
112
- $controls->messages = 'You can now edit the newsletter as pure HTML';
113
-
114
- tnp_prepare_controls($email, $controls);
115
-
116
- $editor_type = NewsletterEmails::EDITOR_HTML;
117
- }
118
-
119
-
120
-
121
- if ($controls->is_action('test') || $controls->is_action('save') || $controls->is_action('send') || $controls->is_action('schedule')) {
122
-
123
- if ($controls->is_action('save')) {
124
- $this->admin_logger->info('Saving newsletter: ' . $email_id);
125
- } else if ($controls->is_action('send')) {
126
- $this->admin_logger->info('Sending newsletter: ' . $email_id);
127
- } else if ($controls->is_action('schedule')) {
128
- $this->admin_logger->info('Scheduling newsletter: ' . $email_id);
129
- }
130
-
131
- $email['subject'] = $controls->data['subject'];
132
- $email['track'] = $controls->data['track'];
133
- $email['editor'] = $editor_type;
134
- $email['private'] = $controls->data['private'];
135
- $email['message_text'] = $controls->data['message_text'];
136
- if ($controls->is_action('send')) {
137
- $email['send_on'] = time();
138
- } else {
139
- // Patch, empty on continuation
140
- if (!empty($controls->data['send_on'])) {
141
- $email['send_on'] = $controls->data['send_on'];
142
- }
143
- }
144
-
145
- // Reset and refill the options
146
- // Try without the reset and let's see where the problems are
147
- //$email['options'] = array();
148
- // Reset only specific keys
149
- unset($email['options']['lists']);
150
- unset($email['options']['lists_operator']);
151
- unset($email['options']['lists_exclude']);
152
- unset($email['options']['sex']);
153
- for ($i = 1; $i <= 20; $i++) {
154
- unset($email['options']["profile_$i"]);
155
- }
156
-
157
- // Patch for Geo addon to be solved with a filter
158
- unset($email['options']['countries']);
159
- unset($email['options']['regions']);
160
- unset($email['options']['cities']);
161
-
162
- foreach ($controls->data as $name => $value) {
163
- if (strpos($name, 'options_') === 0) {
164
- $email['options'][substr($name, 8)] = $value;
165
- }
166
- }
167
-
168
- // Before send, we build the query to extract subscriber, so the delivery engine does not
169
- // have to worry about the email parameters
170
- if ($email['options']['status'] == 'S') {
171
- $query = "select * from " . NEWSLETTER_USERS_TABLE . " where status='S'";
172
- } else {
173
- $query = "select * from " . NEWSLETTER_USERS_TABLE . " where status='C'";
174
- }
175
-
176
- if ($email['options']['wp_users'] == '1') {
177
- $query .= " and wp_user_id<>0";
178
- }
179
-
180
- if (!empty($email['options']['language'])) {
181
- $query .= " and language='" . esc_sql((string) $email['options']['language']) . "'";
182
- }
183
-
184
-
185
- $list_where = array();
186
- if (isset($email['options']['lists']) && count($email['options']['lists'])) {
187
- foreach ($email['options']['lists'] as $list) {
188
- $list = (int) $list;
189
- $list_where[] = 'list_' . $list . '=1';
190
- }
191
- }
192
-
193
- if (!empty($list_where)) {
194
- if (isset($email['options']['lists_operator']) && $email['options']['lists_operator'] == 'and') {
195
- $query .= ' and (' . implode(' and ', $list_where) . ')';
196
- } else {
197
- $query .= ' and (' . implode(' or ', $list_where) . ')';
198
- }
199
- }
200
-
201
- // Excluded lists
202
- $list_where = array();
203
- if (isset($email['options']['lists_exclude']) && count($email['options']['lists_exclude'])) {
204
- foreach ($email['options']['lists_exclude'] as $list) {
205
- $list = (int) $list;
206
- $list_where[] = 'list_' . $list . '=0';
207
- }
208
- }
209
- if (!empty($list_where)) {
210
- // Must not be in one of the excluded lists
211
- $query .= ' and (' . implode(' and ', $list_where) . ')';
212
- }
213
-
214
- // Gender
215
- if (isset($email['options']['sex'])) {
216
- $sex = $email['options']['sex'];
217
- if (is_array($sex) && count($sex)) {
218
- $query .= " and sex in (";
219
- foreach ($sex as $x) {
220
- $query .= "'" . esc_sql((string) $x) . "', ";
221
- }
222
- $query = substr($query, 0, -2);
223
- $query .= ")";
224
- }
225
- }
226
-
227
- // Profile fields filter
228
- $profile_clause = array();
229
- for ($i = 1; $i <= 20; $i++) {
230
- if (isset($email["options"]["profile_$i"]) && count($email["options"]["profile_$i"])) {
231
- $profile_clause[] = 'profile_' . $i . " IN ('" . implode("','", esc_sql($email["options"]["profile_$i"])) . "') ";
232
- }
233
- }
234
-
235
- if (!empty($profile_clause)) {
236
- $query .= ' and (' . implode(' and ', $profile_clause) . ')';
237
- }
238
-
239
- // Temporary save to have an object and call the query filter
240
- $e = Newsletter::instance()->save_email($email);
241
- $query = apply_filters('newsletter_emails_email_query', $query, $e);
242
-
243
- $email['query'] = $query;
244
- if ($email['status'] == 'sent') {
245
- $email['total'] = $email['sent'];
246
- } else {
247
- $email['total'] = $wpdb->get_var(str_replace('*', 'count(*)', $query));
248
- }
249
-
250
- if ($controls->is_action('send') && $controls->data['send_on'] < time()) {
251
- $controls->data['send_on'] = time();
252
- }
253
-
254
- $email = Newsletter::instance()->save_email($email, ARRAY_A);
255
-
256
- tnp_prepare_controls($email, $controls);
257
-
258
- if ($email === false) {
259
- $controls->errors = 'Unable to save. Try to deactivate and reactivate the plugin may be the database is out of sync.';
260
- }
261
-
262
- $controls->add_message_saved();
263
- }
264
-
265
- if ($controls->is_action('send') || $controls->is_action('schedule')) {
266
-
267
- NewsletterStatistics::instance()->reset_stats($email);
268
-
269
- if ($email['subject'] == '') {
270
- $controls->errors = __('A subject is required to send', 'newsletter');
271
- } else {
272
- $wpdb->update(NEWSLETTER_EMAILS_TABLE, array('status' => TNP_Email::STATUS_SENDING), array('id' => $email_id));
273
- $email['status'] = TNP_Email::STATUS_SENDING;
274
- if ($controls->is_action('send')) {
275
- $controls->messages = __('Now sending.', 'newsletter');
276
- } else {
277
- $controls->messages = __('Scheduled.', 'newsletter');
278
- }
279
- }
280
- }
281
-
282
- if (isset($email['options']['status']) && $email['options']['status'] == 'S') {
283
- $controls->warnings[] = __('This newsletter will be sent to not confirmed subscribers.', 'newsletter');
284
- }
285
-
286
- if (strpos($email['message'], '{profile_url}') === false && strpos($email['message'], '{unsubscription_url}') === false && strpos($email['message'], '{unsubscription_confirm_url}') === false) {
287
- $controls->warnings[] = __('The message is missing the subscriber profile or cancellation link.', 'newsletter');
288
- }
289
-
290
- if (TNP_Email::STATUS_ERROR === $email['status'] && isset($email['options']['error_message'])) {
291
- $controls->errors .= sprintf(__('Stopped by fatal error: %s', 'newsletter'), esc_html($email['options']['error_message']));
292
- }
293
-
294
-
295
- if ($email['status'] != 'sent') {
296
- $subscriber_count = $wpdb->get_var(str_replace('*', 'count(*)', $email['query']));
297
- } else {
298
- $subscriber_count = $email['sent'];
299
- }
300
- ?>
301
- <style>
302
- .select2-container {
303
- max-width: 500px;
304
- display: block;
305
- margin: 1px;
306
- margin-top: 5px;
307
- }
308
- </style>
309
-
310
- <div class="wrap tnp-emails tnp-emails-edit" id="tnp-wrap">
311
-
312
- <?php include NEWSLETTER_DIR . '/tnp-header.php'; ?>
313
-
314
- <div id="tnp-heading">
315
-
316
- <h2><?php _e('Edit Newsletter', 'newsletter') ?></h2>
317
-
318
- </div>
319
-
320
- <div id="tnp-body">
321
- <form method="post" action="" id="newsletter-form">
322
- <?php $controls->init(array('cookie_name' => 'newsletter_emails_edit_tab')); ?>
323
- <div class="tnp-status-header">
324
-
325
- <div class="tnp-two-thirds">
326
-
327
- <div class="tnp-submit">
328
-
329
- <?php if ($email['status'] == 'sending' || $email['status'] == 'sent') { ?>
330
-
331
- <?php $controls->button_back('?page=newsletter_emails_index') ?>
332
-
333
- <?php } else { ?>
334
-
335
- <a class="button-primary" href="<?php echo $module->get_editor_url($email_id, $editor_type) ?>">
336
- <i class="fas fa-edit"></i> <?php _e('Edit', 'newsletter') ?>
337
- </a>
338
-
339
- <?php } ?>
340
-
341
- <?php if ($email['status'] != 'sending' && $email['status'] != 'sent') $controls->button_save(); ?>
342
- <?php if ($email['status'] == 'new') $controls->button_confirm('send', __('Send now', 'newsletter'), __('Start real delivery?', 'newsletter')); ?>
343
- <?php if ($email['status'] == 'sending') $controls->button_confirm('pause', __('Pause', 'newsletter'), __('Pause the delivery?', 'newsletter')); ?>
344
- <?php if ($email['status'] == 'paused' || $email['status'] == 'error') $controls->button_confirm('continue', __('Continue', 'newsletter'), 'Continue the delivery?'); ?>
345
- <?php if ($email['status'] == 'paused') $controls->button_confirm('abort', __('Stop', 'newsletter'), __('This totally stop the delivery, ok?', 'newsletter')); ?>
346
- <?php if ($email['status'] == 'new' || ( $email['status'] == 'paused' && $email['send_on'] > time() )) { ?>
347
- <a id="tnp-schedule-button" class="button-secondary" href="javascript:tnp_toggle_schedule()"><i class="far fa-clock"></i> <?php _e("Schedule") ?></a>
348
- <span id="tnp-schedule" style="display: none;">
349
- <?php $controls->datetime('send_on') ?>
350
- <?php $controls->button_confirm('schedule', __('Schedule', 'newsletter'), __('Schedule delivery?', 'newsletter')); ?>
351
- <a class="button-secondary tnp-button-cancel" href="javascript:tnp_toggle_schedule()"><?php _e("Cancel") ?></a>
352
- </span>
353
- <?php } ?>
354
- </div>
355
-
356
- <?php $controls->text('subject', null, 'Subject'); ?>
357
- &nbsp;&nbsp;&nbsp;
358
- <i class="far fa-lightbulb" data-tnp-modal-target="#subject-ideas-modal" style="color: #fff; font-size: 24px"></i>
359
- </div>
360
-
361
- <div class="tnp-one-third">
362
-
363
- <div id="tnp-nl-status">
364
- <span class="tnp-nl-status-title"><?php _e("Status:") ?></span>
365
- <span class="tnp-nl-status-title-value"><?php _e("") ?> <?php $module->show_email_status_label($email) ?></span>
366
-
367
- <?php $module->show_email_progress_bar($email, array('numbers' => $email['status'] == 'sent' ? false : true)) ?>
368
-
369
- <?php if ($email['status'] == 'sent' || $email['status'] == 'sending') { ?>
370
- <div class="tnp-nl-status-row">
371
- <span class="tnp-nl-status-schedule-value"><?php
372
- if ($email['status'] == 'sent') {
373
- echo __('Sent on'), ' ', $module->format_date($email['send_on']);
374
- } else if ($email['status'] == 'sending' && $email['send_on'] > time()) {
375
- echo __('Scheduled on'), ' ', $module->format_date($email['send_on']);
376
- }
377
- ?></span>
378
- </div>
379
- <?php } ?>
380
- <div class="tnp-nl-status-row">
381
- <span class="tnp-nl-status-schedule-targeting"><?php _e('Targeted subscribers', 'newsletter') ?>:</span>
382
- <span class="tnp-nl-status-schedule-value"><?php echo $subscriber_count ?></span>
383
- </div>
384
-
385
- </div>
386
- </div>
387
-
388
- </div>
389
-
390
- <div id="tabs">
391
-
392
- <ul>
393
- <li><a href="#tabs-options"><?php _e('Sending Options', 'newsletter') ?></a></li>
394
- <li><a href="#tabs-advanced"><?php _e('Advanced', 'newsletter') ?></a></li>
395
- <li><a href="#tabs-preview"><?php _e('Preview', 'newsletter') ?></a></li>
396
- </ul>
397
-
398
-
399
- <div id="tabs-options" class="tnp-list-conditions">
400
- <p>
401
- <?php $controls->panel_help('https://www.thenewsletterplugin.com/documentation/newsletter-targeting') ?>
402
- </p>
403
-
404
- <p>
405
- <?php _e('Leaving all multichoice options unselected is like to select all them', 'newsletter'); ?>
406
- </p>
407
- <table class="form-table">
408
- <tr>
409
- <th><?php _e('Lists', 'newsletter') ?></th>
410
- <td>
411
- <?php
412
- $lists = $controls->get_list_options();
413
- ?>
414
- <?php $controls->select('options_lists_operator', array('or' => __('Match at least one of', 'newsletter'), 'and' => __('Match all of', 'newsletter'))); ?>
415
-
416
- <?php $controls->select2('options_lists', $lists, null, true, null, __('All', 'newsletter')); ?>
417
-
418
- <br>
419
- <?php _e('must not in one of', 'newsletter') ?>
420
-
421
- <?php $controls->select2('options_lists_exclude', $lists, null, true, null, __('None', 'newsletter')); ?>
422
- </td>
423
- </tr>
424
-
425
- <tr>
426
- <th><?php _e('Language', 'newsletter') ?></th>
427
- <td>
428
- <?php $controls->language('options_language'); ?>
429
- </td>
430
- </tr>
431
-
432
- <tr>
433
- <th><?php _e('Gender', 'newsletter') ?></th>
434
- <td>
435
- <?php $controls->checkboxes_group('options_sex', array('f' => 'Women', 'm' => 'Men', 'n' => 'Not specified')); ?>
436
- </td>
437
- </tr>
438
- <tr>
439
- <th><?php _e('Status', 'newsletter') ?></th>
440
- <td>
441
- <?php $controls->select('options_status', array('C' => __('Confirmed', 'newsletter'), 'S' => __('Not confirmed', 'newsletter'))); ?>
442
-
443
- </td>
444
- </tr>
445
- <tr>
446
- <th><?php _e('Only to subscribers linked to WP users', 'newsletter') ?></th>
447
- <td>
448
- <?php $controls->yesno('options_wp_users'); ?>
449
- </td>
450
- </tr>
451
- <?php
452
- $fields = TNP_Profile_Service::get_profiles('', TNP_Profile::TYPE_SELECT);
453
- ?>
454
- <?php if (!empty($fields)) { ?>
455
- <tr>
456
- <th><?php _e('Profile fields', 'newsletter') ?></th>
457
- <td>
458
- <?php foreach ($fields as $profile) { ?>
459
- <?php echo esc_html($profile->name), ' ', __('is one of:', 'newsletter') ?>
460
- <?php $controls->select2("options_profile_$profile->id", $profile->options, null, true, null, __('Do not filter by this field', 'newsletter')); ?>
461
- <br>
462
- <?php } ?>
463
- <p class="description">
464
-
465
- </p>
466
- </td>
467
- </tr>
468
- <?php } ?>
469
- </table>
470
-
471
- <?php do_action('newsletter_emails_edit_target', $module->get_email($email_id), $controls) ?>
472
-
473
- </div>
474
-
475
-
476
- <div id="tabs-advanced">
477
-
478
- <table class="form-table">
479
- <tr>
480
- <th><?php _e('Keep private', 'newsletter') ?></th>
481
- <td>
482
- <?php $controls->yesno('private'); ?>
483
- <?php if ($email['status'] == 'sent') { ?>
484
- <?php $controls->button('change-private', __('Toggle')) ?>
485
- <?php } ?>
486
- <p class="description">
487
- <?php _e('Hide/show from public sent newsletter list.', 'newsletter') ?>
488
- <?php _e('Required', 'newsletter') ?>: <a href="" target="_blank">Newsletter Archive Extension</a>
489
- </p>
490
- </td>
491
- </tr>
492
- <tr>
493
- <th><?php _e('Track clicks and message opening', 'newsletter') ?></th>
494
- <td>
495
- <?php $controls->yesno('track'); ?>
496
- </td>
497
- </tr>
498
- <tr>
499
- <th><?php _e('Sender email address', 'newsletter') ?></th>
500
- <td>
501
- <?php $controls->text_email('options_sender_email', 40); ?>
502
- <div class="tnpc-hint">
503
- Original: <?php echo esc_html(Newsletter::instance()->get_sender_email()) ?>.<br>
504
- If you use a delivery service, be sure to use a validated email address.
505
- </div>
506
- </td>
507
- </tr>
508
- <tr>
509
- <th>
510
- <?php _e('Sender name', 'newsletter') ?>
511
- </th>
512
- <td>
513
- <?php $controls->text('options_sender_name', 40); ?>
514
- <div class="tnpc-hint">
515
- Original: <?php echo esc_html(Newsletter::instance()->get_sender_name()) ?>
516
- </div>
517
- </td>
518
- </tr>
519
- </table>
520
-
521
- <?php do_action('newsletter_emails_edit_other', $module->get_email($email_id), $controls) ?>
522
-
523
- <table class="form-table">
524
- <tr>
525
- <th>Query (tech)</th>
526
- <td><?php echo esc_html($email['query']); ?></td>
527
- </tr>
528
- <tr>
529
- <th>Token (tech)</th>
530
- <td><?php echo esc_html($email['token']); ?></td>
531
- </tr>
532
- <tr>
533
- <th>This is the textual version of your newsletter. If you empty it, only an HTML version will be sent but is an anti-spam best practice to include a text only version.</th>
534
- <td>
535
- <?php if (Newsletter::instance()->options['phpmailer'] == 0) { ?>
536
- <p class="tnp-tab-warning">The text part is sent only when Newsletter manages directly the sending process. <a href="admin.php?page=newsletter_main_main" target="_blank">See the main settings</a>.</p>
537
- <?php } ?>
538
- <?php $controls->textarea_fixed('message_text', '100%', '500'); ?>
539
- </td>
540
- </tr>
541
- </table>
542
- </div>
543
-
544
-
545
- <div id="tabs-preview">
546
-
547
- <div class="tnpc-preview">
548
- <!-- Flat Laptop Browser -->
549
- <div class="fake-browser-ui">
550
- <div class="frame">
551
- <span class="bt-1"></span>
552
- <span class="bt-2"></span>
553
- <span class="bt-3"></span>
554
- </div>
555
- <iframe id="tnpc-preview-desktop" src="" width="700" height="520" alt="" frameborder="0"></iframe>
556
- </div>
557
-
558
- <!-- Flat Mobile Browser -->
559
- <div class="fake-mobile-browser-ui">
560
- <iframe id="tnpc-preview-mobile" src="" width="320" height="445" alt="" frameborder="0"></iframe>
561
- <div class="frame">
562
- <span class="bt-4"></span>
563
- </div>
564
- </div>
565
- </div>
566
-
567
- <script type="text/javascript">
568
- preview_url = ajaxurl + "?action=tnpc_preview&id=<?php echo $email_id ?>";
569
- jQuery('#tnpc-preview-desktop, #tnpc-preview-mobile').attr("src", preview_url);
570
- setTimeout(function () {
571
- jQuery('#tnpc-preview-desktop, #tnpc-preview-mobile').contents().find("a").click(function (e) {
572
- e.preventDefault();
573
- })
574
- }, 500);
575
- </script>
576
-
577
- <p>
578
- <?php if ($editor_type != NewsletterEmails::EDITOR_HTML && $email['status'] != 'sending' && $email['status'] != 'sent') $controls->button_confirm('html', __('Convert to HTML newsletter', 'newsletter'), 'Attention: no way back!'); ?>
579
- </p>
580
- </div>
581
-
582
- </div>
583
-
584
- </form>
585
- </div>
586
-
587
- <?php include NEWSLETTER_DIR . '/emails/subjects.php'; ?>
588
-
589
- <?php include NEWSLETTER_DIR . '/tnp-footer.php'; ?>
590
-
591
- </div>
 
 
 
 
 
 
1
+ <?php
2
+ /* @var $this NewsletterEmails */
3
+ defined('ABSPATH') || exit;
4
+
5
+ /* @var $wpdb wpdb */
6
+ require_once NEWSLETTER_INCLUDES_DIR . '/controls.php';
7
+ $controls = new NewsletterControls();
8
+ $module = NewsletterEmails::instance();
9
+
10
+ function tnp_prepare_controls($email, $controls) {
11
+ $controls->data = $email;
12
+
13
+ foreach ($email['options'] as $name => $value) {
14
+ $controls->data['options_' . $name] = $value;
15
+ }
16
+ }
17
+
18
+ // Always required
19
+ $email = $this->get_email($_GET['id'], ARRAY_A);
20
+
21
+ if (empty($email)) {
22
+ echo 'Wrong email identifier';
23
+ return;
24
+ }
25
+
26
+ $email_id = $email['id'];
27
+
28
+ /* Satus changes which require a reload */
29
+ if ($controls->is_action('pause')) {
30
+ $this->admin_logger->info('Newsletter ' . $email_id . ' paused');
31
+ $wpdb->update(NEWSLETTER_EMAILS_TABLE, array('status' => 'paused'), array('id' => $email_id));
32
+ $email = $this->get_email($_GET['id'], ARRAY_A);
33
+ tnp_prepare_controls($email, $controls);
34
+ }
35
+
36
+ if ($controls->is_action('continue')) {
37
+ $this->admin_logger->info('Newsletter ' . $email_id . ' restarted');
38
+ $wpdb->update(NEWSLETTER_EMAILS_TABLE, array('status' => 'sending'), array('id' => $email_id));
39
+ $email = $this->get_email($_GET['id'], ARRAY_A);
40
+ tnp_prepare_controls($email, $controls);
41
+ }
42
+
43
+ if ($controls->is_action('abort')) {
44
+ $this->admin_logger->info('Newsletter ' . $email_id . ' aborted');
45
+ $wpdb->query("update " . NEWSLETTER_EMAILS_TABLE . " set last_id=0, sent=0, status='new' where id=" . $email_id);
46
+ $email = $this->get_email($_GET['id'], ARRAY_A);
47
+ tnp_prepare_controls($email, $controls);
48
+ $controls->messages = __('Delivery definitively cancelled', 'newsletter');
49
+ }
50
+
51
+ if ($controls->is_action('change-private')) {
52
+ $data = [];
53
+ $data['private'] = $controls->data['private'] ? 1 : 0;
54
+ $data['id'] = $email['id'];
55
+ $email = $this->save_email($data, ARRAY_A);
56
+ $controls->add_message_saved();
57
+
58
+ tnp_prepare_controls($email, $controls);
59
+ }
60
+
61
+
62
+ $editor_type = $this->get_editor_type($email);
63
+
64
+ // Backward compatibility: preferences conversion
65
+ if (!$controls->is_action()) {
66
+ if (!isset($email['options']['lists'])) {
67
+
68
+ $options_profile = get_option('newsletter_profile');
69
+
70
+ if (empty($controls->data['preferences_status_operator'])) {
71
+ $email['options']['lists_operator'] = 'or';
72
+ } else {
73
+ $email['options']['lists_operator'] = 'and';
74
+ }
75
+ $controls->data['options_lists'] = array();
76
+ $controls->data['options_lists_exclude'] = array();
77
+
78
+ if (!empty($email['preferences'])) {
79
+ $preferences = explode(',', $email['preferences']);
80
+ $value = empty($email['options']['preferences_status']) ? 'on' : 'off';
81
+
82
+ foreach ($preferences as $x) {
83
+ if ($value == 'on') {
84
+ $controls->data['options_lists'][] = $x;
85
+ } else {
86
+ $controls->data['options_lists_exclude'][] = $x;
87
+ }
88
+ }
89
+ }
90
+ }
91
+ }
92
+ // End backward compatibility
93
+
94
+ if (!$controls->is_action()) {
95
+ tnp_prepare_controls($email, $controls);
96
+ }
97
+
98
+ if ($controls->is_action('html')) {
99
+
100
+ $this->admin_logger->info('Newsletter ' . $email_id . ' converted to HTML');
101
+
102
+ $data = [];
103
+ $data['editor'] = NewsletterEmails::EDITOR_HTML;
104
+ $data['id'] = $email_id;
105
+
106
+ // Backward compatibility: clean up the composer flag
107
+ $data['options'] = $email['options'];
108
+ unset($data['options']['composer']);
109
+ // End backward compatibility
110
+
111
+ $email = $this->save_email($data, ARRAY_A);
112
+ $controls->messages = 'You can now edit the newsletter as pure HTML';
113
+
114
+ tnp_prepare_controls($email, $controls);
115
+
116
+ $editor_type = NewsletterEmails::EDITOR_HTML;
117
+ }
118
+
119
+
120
+
121
+ if ($controls->is_action('test') || $controls->is_action('save') || $controls->is_action('send') || $controls->is_action('schedule')) {
122
+
123
+ if ($email['updated'] != $controls->data['updated']) {
124
+ $controls->errors = 'This newsletter has been modified by someone else. Cannot save.';
125
+ } else {
126
+ $email['updated'] = time();
127
+ if ($controls->is_action('save')) {
128
+ $this->admin_logger->info('Saving newsletter: ' . $email_id);
129
+ } else if ($controls->is_action('send')) {
130
+ $this->admin_logger->info('Sending newsletter: ' . $email_id);
131
+ } else if ($controls->is_action('schedule')) {
132
+ $this->admin_logger->info('Scheduling newsletter: ' . $email_id);
133
+ }
134
+
135
+ $email['subject'] = $controls->data['subject'];
136
+ $email['track'] = $controls->data['track'];
137
+ $email['editor'] = $editor_type;
138
+ $email['private'] = $controls->data['private'];
139
+ $email['message_text'] = $controls->data['message_text'];
140
+ if ($controls->is_action('send')) {
141
+ $email['send_on'] = time();
142
+ } else {
143
+ // Patch, empty on continuation
144
+ if (!empty($controls->data['send_on'])) {
145
+ $email['send_on'] = $controls->data['send_on'];
146
+ }
147
+ }
148
+
149
+ // Reset and refill the options
150
+ // Try without the reset and let's see where the problems are
151
+ //$email['options'] = array();
152
+ // Reset only specific keys
153
+ unset($email['options']['lists']);
154
+ unset($email['options']['lists_operator']);
155
+ unset($email['options']['lists_exclude']);
156
+ unset($email['options']['sex']);
157
+ for ($i = 1; $i <= 20; $i++) {
158
+ unset($email['options']["profile_$i"]);
159
+ }
160
+
161
+ // Patch for Geo addon to be solved with a filter
162
+ unset($email['options']['countries']);
163
+ unset($email['options']['regions']);
164
+ unset($email['options']['cities']);
165
+
166
+ foreach ($controls->data as $name => $value) {
167
+ if (strpos($name, 'options_') === 0) {
168
+ $email['options'][substr($name, 8)] = $value;
169
+ }
170
+ }
171
+
172
+ // Before send, we build the query to extract subscriber, so the delivery engine does not
173
+ // have to worry about the email parameters
174
+ if ($email['options']['status'] == 'S') {
175
+ $query = "select * from " . NEWSLETTER_USERS_TABLE . " where status='S'";
176
+ } else {
177
+ $query = "select * from " . NEWSLETTER_USERS_TABLE . " where status='C'";
178
+ }
179
+
180
+ if ($email['options']['wp_users'] == '1') {
181
+ $query .= " and wp_user_id<>0";
182
+ }
183
+
184
+ if (!empty($email['options']['language'])) {
185
+ $query .= " and language='" . esc_sql((string) $email['options']['language']) . "'";
186
+ }
187
+
188
+
189
+ $list_where = array();
190
+ if (isset($email['options']['lists']) && count($email['options']['lists'])) {
191
+ foreach ($email['options']['lists'] as $list) {
192
+ $list = (int) $list;
193
+ $list_where[] = 'list_' . $list . '=1';
194
+ }
195
+ }
196
+
197
+ if (!empty($list_where)) {
198
+ if (isset($email['options']['lists_operator']) && $email['options']['lists_operator'] == 'and') {
199
+ $query .= ' and (' . implode(' and ', $list_where) . ')';
200
+ } else {
201
+ $query .= ' and (' . implode(' or ', $list_where) . ')';
202
+ }
203
+ }
204
+
205
+ // Excluded lists
206
+ $list_where = array();
207
+ if (isset($email['options']['lists_exclude']) && count($email['options']['lists_exclude'])) {
208
+ foreach ($email['options']['lists_exclude'] as $list) {
209
+ $list = (int) $list;
210
+ $list_where[] = 'list_' . $list . '=0';
211
+ }
212
+ }
213
+ if (!empty($list_where)) {
214
+ // Must not be in one of the excluded lists
215
+ $query .= ' and (' . implode(' and ', $list_where) . ')';
216
+ }
217
+
218
+ // Gender
219
+ if (isset($email['options']['sex'])) {
220
+ $sex = $email['options']['sex'];
221
+ if (is_array($sex) && count($sex)) {
222
+ $query .= " and sex in (";
223
+ foreach ($sex as $x) {
224
+ $query .= "'" . esc_sql((string) $x) . "', ";
225
+ }
226
+ $query = substr($query, 0, -2);
227
+ $query .= ")";
228
+ }
229
+ }
230
+
231
+ // Profile fields filter
232
+ $profile_clause = array();
233
+ for ($i = 1; $i <= 20; $i++) {
234
+ if (isset($email["options"]["profile_$i"]) && count($email["options"]["profile_$i"])) {
235
+ $profile_clause[] = 'profile_' . $i . " IN ('" . implode("','", esc_sql($email["options"]["profile_$i"])) . "') ";
236
+ }
237
+ }
238
+
239
+ if (!empty($profile_clause)) {
240
+ $query .= ' and (' . implode(' and ', $profile_clause) . ')';
241
+ }
242
+
243
+ // Temporary save to have an object and call the query filter
244
+ $e = Newsletter::instance()->save_email($email);
245
+ $query = apply_filters('newsletter_emails_email_query', $query, $e);
246
+
247
+ $email['query'] = $query;
248
+ if ($email['status'] == 'sent') {
249
+ $email['total'] = $email['sent'];
250
+ } else {
251
+ $email['total'] = $wpdb->get_var(str_replace('*', 'count(*)', $query));
252
+ }
253
+
254
+ if ($controls->is_action('send') && $controls->data['send_on'] < time()) {
255
+ $controls->data['send_on'] = time();
256
+ }
257
+
258
+ $email = Newsletter::instance()->save_email($email, ARRAY_A);
259
+
260
+ tnp_prepare_controls($email, $controls);
261
+
262
+ if ($email === false) {
263
+ $controls->errors = 'Unable to save. Try to deactivate and reactivate the plugin may be the database is out of sync.';
264
+ }
265
+
266
+ $controls->add_message_saved();
267
+ }
268
+ }
269
+
270
+ if (empty($controls->errors) && ($controls->is_action('send') || $controls->is_action('schedule'))) {
271
+
272
+ NewsletterStatistics::instance()->reset_stats($email);
273
+
274
+ if ($email['subject'] == '') {
275
+ $controls->errors = __('A subject is required to send', 'newsletter');
276
+ } else {
277
+ $wpdb->update(NEWSLETTER_EMAILS_TABLE, array('status' => TNP_Email::STATUS_SENDING), array('id' => $email_id));
278
+ $email['status'] = TNP_Email::STATUS_SENDING;
279
+ if ($controls->is_action('send')) {
280
+ $controls->messages = __('Now sending.', 'newsletter');
281
+ } else {
282
+ $controls->messages = __('Scheduled.', 'newsletter');
283
+ }
284
+ }
285
+ }
286
+
287
+ if (isset($email['options']['status']) && $email['options']['status'] == 'S') {
288
+ $controls->warnings[] = __('This newsletter will be sent to not confirmed subscribers.', 'newsletter');
289
+ }
290
+
291
+ if (strpos($email['message'], '{profile_url}') === false && strpos($email['message'], '{unsubscription_url}') === false && strpos($email['message'], '{unsubscription_confirm_url}') === false) {
292
+ $controls->warnings[] = __('The message is missing the subscriber profile or cancellation link.', 'newsletter');
293
+ }
294
+
295
+ if (TNP_Email::STATUS_ERROR === $email['status'] && isset($email['options']['error_message'])) {
296
+ $controls->errors .= sprintf(__('Stopped by fatal error: %s', 'newsletter'), esc_html($email['options']['error_message']));
297
+ }
298
+
299
+
300
+ if ($email['status'] != 'sent') {
301
+ $subscriber_count = $wpdb->get_var(str_replace('*', 'count(*)', $email['query']));
302
+ } else {
303
+ $subscriber_count = $email['sent'];
304
+ }
305
+ ?>
306
+ <style>
307
+ .select2-container {
308
+ max-width: 500px;
309
+ display: block;
310
+ margin: 1px;
311
+ margin-top: 5px;
312
+ }
313
+ </style>
314
+
315
+ <div class="wrap tnp-emails tnp-emails-edit" id="tnp-wrap">
316
+
317
+ <?php include NEWSLETTER_DIR . '/tnp-header.php'; ?>
318
+
319
+ <div id="tnp-heading">
320
+
321
+ <h2><?php _e('Edit Newsletter', 'newsletter') ?></h2>
322
+
323
+ </div>
324
+
325
+ <div id="tnp-body">
326
+ <form method="post" action="" id="newsletter-form">
327
+ <?php $controls->init(array('cookie_name' => 'newsletter_emails_edit_tab')); ?>
328
+ <?php $controls->hidden('updated') ?>
329
+ <div class="tnp-status-header">
330
+
331
+ <div class="tnp-two-thirds">
332
+
333
+ <div class="tnp-submit">
334
+
335
+ <?php if ($email['status'] == 'sending' || $email['status'] == 'sent') { ?>
336
+
337
+ <?php $controls->button_back('?page=newsletter_emails_index') ?>
338
+
339
+ <?php } else { ?>
340
+
341
+ <a class="button-primary" href="<?php echo $module->get_editor_url($email_id, $editor_type) ?>">
342
+ <i class="fas fa-edit"></i> <?php _e('Edit', 'newsletter') ?>
343
+ </a>
344
+
345
+ <?php } ?>
346
+
347
+ <?php if ($email['status'] != 'sending' && $email['status'] != 'sent') $controls->button_save(); ?>
348
+ <?php if ($email['status'] == 'new') $controls->button_confirm('send', __('Send now', 'newsletter'), __('Start real delivery?', 'newsletter')); ?>
349
+ <?php if ($email['status'] == 'sending') $controls->button_confirm('pause', __('Pause', 'newsletter'), __('Pause the delivery?', 'newsletter')); ?>
350
+ <?php if ($email['status'] == 'paused' || $email['status'] == 'error') $controls->button_confirm('continue', __('Continue', 'newsletter'), 'Continue the delivery?'); ?>
351
+ <?php if ($email['status'] == 'paused') $controls->button_confirm('abort', __('Stop', 'newsletter'), __('This totally stop the delivery, ok?', 'newsletter')); ?>
352
+ <?php if ($email['status'] == 'new' || ( $email['status'] == 'paused' && $email['send_on'] > time() )) { ?>
353
+ <a id="tnp-schedule-button" class="button-secondary" href="javascript:tnp_toggle_schedule()"><i class="far fa-clock"></i> <?php _e("Schedule") ?></a>
354
+ <span id="tnp-schedule" style="display: none;">
355
+ <?php $controls->datetime('send_on') ?>
356
+ <?php $controls->button_confirm('schedule', __('Schedule', 'newsletter'), __('Schedule delivery?', 'newsletter')); ?>
357
+ <a class="button-secondary tnp-button-cancel" href="javascript:tnp_toggle_schedule()"><?php _e("Cancel") ?></a>
358
+ </span>
359
+ <?php } ?>
360
+ </div>
361
+
362
+ <?php $controls->text('subject', null, 'Subject'); ?>
363
+ &nbsp;&nbsp;&nbsp;
364
+ <i class="far fa-lightbulb" data-tnp-modal-target="#subject-ideas-modal" style="color: #fff; font-size: 24px"></i>
365
+ </div>
366
+
367
+ <div class="tnp-one-third">
368
+
369
+ <div id="tnp-nl-status">
370
+ <span class="tnp-nl-status-title"><?php _e("Status:") ?></span>
371
+ <span class="tnp-nl-status-title-value"><?php _e("") ?> <?php $module->show_email_status_label($email) ?></span>
372
+
373
+ <?php $module->show_email_progress_bar($email, array('numbers' => $email['status'] == 'sent' ? false : true)) ?>
374
+
375
+ <?php if ($email['status'] == 'sent' || $email['status'] == 'sending') { ?>
376
+ <div class="tnp-nl-status-row">
377
+ <span class="tnp-nl-status-schedule-value"><?php
378
+ if ($email['status'] == 'sent') {
379
+ echo __('Sent on'), ' ', $module->format_date($email['send_on']);
380
+ } else if ($email['status'] == 'sending' && $email['send_on'] > time()) {
381
+ echo __('Scheduled on'), ' ', $module->format_date($email['send_on']);
382
+ }
383
+ ?></span>
384
+ </div>
385
+ <?php } ?>
386
+ <div class="tnp-nl-status-row">
387
+ <span class="tnp-nl-status-schedule-targeting"><?php _e('Targeted subscribers', 'newsletter') ?>:</span>
388
+ <span class="tnp-nl-status-schedule-value"><?php echo $subscriber_count ?></span>
389
+ </div>
390
+
391
+ </div>
392
+ </div>
393
+
394
+ </div>
395
+
396
+ <div id="tabs">
397
+
398
+ <ul>
399
+ <li><a href="#tabs-options"><?php _e('Sending Options', 'newsletter') ?></a></li>
400
+ <li><a href="#tabs-advanced"><?php _e('Advanced', 'newsletter') ?></a></li>
401
+ <li><a href="#tabs-preview"><?php _e('Preview', 'newsletter') ?></a></li>
402
+ </ul>
403
+
404
+
405
+ <div id="tabs-options" class="tnp-list-conditions">
406
+ <p>
407
+ <?php $controls->panel_help('https://www.thenewsletterplugin.com/documentation/newsletter-targeting') ?>
408
+ </p>
409
+
410
+ <p>
411
+ <?php _e('Leaving all multichoice options unselected is like to select all them', 'newsletter'); ?>
412
+ </p>
413
+ <table class="form-table">
414
+ <tr>
415
+ <th><?php _e('Lists', 'newsletter') ?></th>
416
+ <td>
417
+ <?php
418
+ $lists = $controls->get_list_options();
419
+ ?>
420
+ <?php $controls->select('options_lists_operator', array('or' => __('Match at least one of', 'newsletter'), 'and' => __('Match all of', 'newsletter'))); ?>
421
+
422
+ <?php $controls->select2('options_lists', $lists, null, true, null, __('All', 'newsletter')); ?>
423
+
424
+ <br>
425
+ <?php _e('must not in one of', 'newsletter') ?>
426
+
427
+ <?php $controls->select2('options_lists_exclude', $lists, null, true, null, __('None', 'newsletter')); ?>
428
+ </td>
429
+ </tr>
430
+
431
+ <tr>
432
+ <th><?php _e('Language', 'newsletter') ?></th>
433
+ <td>
434
+ <?php $controls->language('options_language'); ?>
435
+ </td>
436
+ </tr>
437
+
438
+ <tr>
439
+ <th><?php _e('Gender', 'newsletter') ?></th>
440
+ <td>
441
+ <?php $controls->checkboxes_group('options_sex', array('f' => 'Women', 'm' => 'Men', 'n' => 'Not specified')); ?>
442
+ </td>
443
+ </tr>
444
+ <tr>
445
+ <th><?php _e('Status', 'newsletter') ?></th>
446
+ <td>
447
+ <?php $controls->select('options_status', array('C' => __('Confirmed', 'newsletter'), 'S' => __('Not confirmed', 'newsletter'))); ?>
448
+
449
+ </td>
450
+ </tr>
451
+ <tr>
452
+ <th><?php _e('Only to subscribers linked to WP users', 'newsletter') ?></th>
453
+ <td>
454
+ <?php $controls->yesno('options_wp_users'); ?>
455
+ </td>
456
+ </tr>
457
+ <?php
458
+ $fields = TNP_Profile_Service::get_profiles('', TNP_Profile::TYPE_SELECT);
459
+ ?>
460
+ <?php if (!empty($fields)) { ?>
461
+ <tr>
462
+ <th><?php _e('Profile fields', 'newsletter') ?></th>
463
+ <td>
464
+ <?php foreach ($fields as $profile) { ?>
465
+ <?php echo esc_html($profile->name), ' ', __('is one of:', 'newsletter') ?>
466
+ <?php $controls->select2("options_profile_$profile->id", $profile->options, null, true, null, __('Do not filter by this field', 'newsletter')); ?>
467
+ <br>
468
+ <?php } ?>
469
+ <p class="description">
470
+
471
+ </p>
472
+ </td>
473
+ </tr>
474
+ <?php } ?>
475
+ </table>
476
+
477
+ <?php do_action('newsletter_emails_edit_target', $module->get_email($email_id), $controls) ?>
478
+
479
+ </div>
480
+
481
+
482
+ <div id="tabs-advanced">
483
+
484
+ <table class="form-table">
485
+ <tr>
486
+ <th><?php _e('Keep private', 'newsletter') ?></th>
487
+ <td>
488
+ <?php $controls->yesno('private'); ?>
489
+ <?php if ($email['status'] == 'sent') { ?>
490
+ <?php $controls->button('change-private', __('Toggle')) ?>
491
+ <?php } ?>
492
+ <p class="description">
493
+ <?php _e('Hide/show from public sent newsletter list.', 'newsletter') ?>
494
+ <?php _e('Required', 'newsletter') ?>: <a href="" target="_blank">Newsletter Archive Extension</a>
495
+ </p>
496
+ </td>
497
+ </tr>
498
+ <tr>
499
+ <th><?php _e('Track clicks and message opening', 'newsletter') ?></th>
500
+ <td>
501
+ <?php $controls->yesno('track'); ?>
502
+ </td>
503
+ </tr>
504
+ <tr>
505
+ <th><?php _e('Sender email address', 'newsletter') ?></th>
506
+ <td>
507
+ <?php $controls->text_email('options_sender_email', 40); ?>
508
+ <div class="tnpc-hint">
509
+ Original: <?php echo esc_html(Newsletter::instance()->get_sender_email()) ?>.<br>
510
+ If you use a delivery service, be sure to use a validated email address.
511
+ </div>
512
+ </td>
513
+ </tr>
514
+ <tr>
515
+ <th>
516
+ <?php _e('Sender name', 'newsletter') ?>
517
+ </th>
518
+ <td>
519
+ <?php $controls->text('options_sender_name', 40); ?>
520
+ <div class="tnpc-hint">
521
+ Original: <?php echo esc_html(Newsletter::instance()->get_sender_name()) ?>
522
+ </div>
523
+ </td>
524
+ </tr>
525
+ </table>
526
+
527
+ <?php do_action('newsletter_emails_edit_other', $module->get_email($email_id), $controls) ?>
528
+
529
+ <table class="form-table">
530
+ <tr>
531
+ <th>Query (tech)</th>
532
+ <td><?php echo esc_html($email['query']); ?></td>
533
+ </tr>
534
+ <tr>
535
+ <th>Token (tech)</th>
536
+ <td><?php echo esc_html($email['token']); ?></td>
537
+ </tr>
538
+ <tr>
539
+ <th>This is the textual version of your newsletter. If you empty it, only an HTML version will be sent but is an anti-spam best practice to include a text only version.</th>
540
+ <td>
541
+ <?php if (Newsletter::instance()->options['phpmailer'] == 0) { ?>
542
+ <p class="tnp-tab-warning">The text part is sent only when Newsletter manages directly the sending process. <a href="admin.php?page=newsletter_main_main" target="_blank">See the main settings</a>.</p>
543
+ <?php } ?>
544
+ <?php $controls->textarea_fixed('message_text', '100%', '500'); ?>
545
+ </td>
546
+ </tr>
547
+ </table>
548
+ </div>
549
+
550
+
551
+ <div id="tabs-preview">
552
+
553
+ <div class="tnpc-preview">
554
+ <!-- Flat Laptop Browser -->
555
+ <div class="fake-browser-ui">
556
+ <div class="frame">
557
+ <span class="bt-1"></span>
558
+ <span class="bt-2"></span>
559
+ <span class="bt-3"></span>
560
+ </div>
561
+ <iframe id="tnpc-preview-desktop" src="" width="700" height="520" alt="" frameborder="0"></iframe>
562
+ </div>
563
+
564
+ <!-- Flat Mobile Browser -->
565
+ <div class="fake-mobile-browser-ui">
566
+ <iframe id="tnpc-preview-mobile" src="" width="320" height="445" alt="" frameborder="0"></iframe>
567
+ <div class="frame">
568
+ <span class="bt-4"></span>
569
+ </div>
570
+ </div>
571
+ </div>
572
+
573
+ <script type="text/javascript">
574
+ preview_url = ajaxurl + "?action=tnpc_preview&id=<?php echo $email_id ?>";
575
+ jQuery('#tnpc-preview-desktop, #tnpc-preview-mobile').attr("src", preview_url);
576
+ setTimeout(function () {
577
+ jQuery('#tnpc-preview-desktop, #tnpc-preview-mobile').contents().find("a").click(function (e) {
578
+ e.preventDefault();
579
+ })
580
+ }, 500);
581
+ </script>
582
+
583
+ <p>
584
+ <?php if ($editor_type != NewsletterEmails::EDITOR_HTML && $email['status'] != 'sending' && $email['status'] != 'sent') $controls->button_confirm('html', __('Convert to HTML newsletter', 'newsletter'), 'Attention: no way back!'); ?>
585
+ </p>
586
+ </div>
587
+
588
+ </div>
589
+
590
+ </form>
591
+ </div>
592
+
593
+ <?php include NEWSLETTER_DIR . '/emails/subjects.php'; ?>
594
+
595
+ <?php include NEWSLETTER_DIR . '/tnp-footer.php'; ?>
596
+
597
+ </div>
emails/emails.php CHANGED
@@ -1,1410 +1,1410 @@
1
- <?php
2
-
3
- defined('ABSPATH') || exit;
4
-
5
- class NewsletterEmails extends NewsletterModule {
6
-
7
- static $instance;
8
-
9
- const EDITOR_COMPOSER = 2;
10
- const EDITOR_HTML = 1;
11
- const EDITOR_TINYMCE = 0;
12
-
13
- static $PRESETS_LIST;
14
-
15
- const PRESET_EMAIL_TYPE = 'composer_template';
16
-
17
- // Cache
18
- var $blocks = null;
19
-
20
- /**
21
- * @return NewsletterEmails
22
- */
23
- static function instance() {
24
- if (self::$instance == null) {
25
- self::$instance = new NewsletterEmails();
26
- }
27
-
28
- return self::$instance;
29
- }
30
-
31
- function __construct() {
32
- self::$PRESETS_LIST = array("cta", "invite", "announcement", "posts", "sales", "product", "tour", "simple");
33
- $this->themes = new NewsletterThemes('emails');
34
- parent::__construct('emails', '1.1.5');
35
- add_action('newsletter_action', array($this, 'hook_newsletter_action'), 13, 3);
36
-
37
- if (is_admin()) {
38
- if (defined('DOING_AJAX') && DOING_AJAX) {
39
- add_action('wp_ajax_tnpc_render', array($this, 'tnpc_render_callback'));
40
- add_action('wp_ajax_tnpc_preview', array($this, 'tnpc_preview_callback'));
41
- add_action('wp_ajax_tnpc_css', array($this, 'tnpc_css_callback'));
42
- add_action('wp_ajax_tnpc_options', array($this, 'hook_wp_ajax_tnpc_options'));
43
- add_action('wp_ajax_tnpc_get_all_presets', array($this, 'ajax_get_all_presets'));
44
- add_action('wp_ajax_tnpc_get_preset', array($this, 'ajax_get_preset'));
45
- add_action('wp_ajax_tnpc_delete_preset', array($this, 'hook_wp_ajax_tnpc_delete_preset'));
46
- add_action('wp_ajax_tnpc_regenerate_email', array($this, 'hook_wp_ajax_tnpc_regenerate_email'));
47
- }
48
- // Thank you to plugins which add the WP editor on other admin plugin pages...
49
- if (isset($_GET['page']) && $_GET['page'] == 'newsletter_emails_edit') {
50
- global $wp_actions;
51
- $wp_actions['wp_enqueue_editor'] = 1;
52
- }
53
- }
54
- }
55
-
56
- function options_decode($options) {
57
-
58
- // Start compatibility
59
- if (is_string($options) && strpos($options, 'options[') !== false) {
60
- $opts = array();
61
- parse_str($options, $opts);
62
- $options = $opts['options'];
63
- }
64
- // End compatibility
65
-
66
- if (is_array($options)) {
67
- return $options;
68
- }
69
-
70
- $tmp = json_decode($options, true);
71
- if (is_null($tmp)) {
72
- return json_decode(base64_decode($options), true);
73
- } else {
74
- return $tmp;
75
- }
76
- }
77
-
78
- /**
79
- *
80
- * @param array $options Options array
81
- */
82
- function options_encode($options) {
83
- return base64_encode(json_encode($options, JSON_HEX_TAG | JSON_HEX_AMP));
84
- }
85
-
86
- function hook_wp_ajax_tnpc_options() {
87
- global $wpdb;
88
-
89
- // TODO: Uniform to use id everywhere
90
- // if (!isset($_REQUEST['id'])) {
91
- // $_REQUEST['id'] = $_REQUEST['b'];
92
- // }
93
-
94
- $block = $this->get_block($_REQUEST['id']);
95
- if (!$block) {
96
- die('Block not found with id ' . esc_html($_REQUEST['id']));
97
- }
98
-
99
- if (!class_exists('NewsletterControls')) {
100
- include NEWSLETTER_INCLUDES_DIR . '/controls.php';
101
- }
102
-
103
- $options = $this->options_decode(stripslashes_deep($_REQUEST['options']));
104
- $composer = isset($_POST['composer']) ? $_POST['composer'] : [];
105
-
106
- $context = array('type' => '');
107
- if (isset($_REQUEST['context_type'])) {
108
- $context['type'] = $_REQUEST['context_type'];
109
- }
110
-
111
- $controls = new NewsletterControls($options);
112
- $fields = new NewsletterFields($controls);
113
-
114
- $controls->init();
115
- echo '<input type="hidden" name="action" value="tnpc_render">';
116
- echo '<input type="hidden" name="id" value="' . esc_attr($_REQUEST['id']) . '">';
117
- echo '<input type="hidden" name="context_type" value="' . esc_attr($context['type']) . '">';
118
- $inline_edits = '';
119
- if (isset($controls->data['inline_edits'])) {
120
- $inline_edits = $controls->data['inline_edits'];
121
- }
122
- echo '<input type="hidden" name="options[inline_edits]" value="', esc_attr($this->options_encode($inline_edits)), '">';
123
-
124
- ob_start();
125
- include $block['dir'] . '/options.php';
126
- $content = ob_get_clean();
127
- echo "<h2>", esc_html($block["name"]), "</h2>";
128
- echo $content;
129
- wp_die();
130
- }
131
-
132
- /**
133
- * Retrieves the presets list (no id in GET) or a specific preset id in GET)
134
- */
135
- public function ajax_get_all_presets() {
136
- wp_send_json_success($this->get_all_preset());
137
- }
138
-
139
- public function ajax_get_preset() {
140
-
141
- if (empty($_REQUEST['id'])) {
142
- wp_send_json_error([
143
- 'msg' => __('Invalid preset ID')
144
- ]);
145
- }
146
-
147
- $preset_id = $_REQUEST['id'];
148
- $preset_content = $this->get_preset_content($preset_id);
149
- $global_options = $this->get_preset_global_options($preset_id);
150
-
151
- wp_send_json_success([
152
- 'content' => $preset_content,
153
- 'globalOptions' => $global_options,
154
- ]);
155
- }
156
-
157
- private function get_preset_content($preset_id) {
158
-
159
- $content = '';
160
-
161
- if ($this->is_a_tnp_default_preset($preset_id)) {
162
-
163
- // Get preset from file
164
- $preset = $this->get_preset_from_file($preset_id);
165
-
166
- foreach ($preset->blocks as $item) {
167
- ob_start();
168
- $this->render_block($item->block, true, (array) $item->options);
169
- $content .= trim(ob_get_clean());
170
- }
171
- } else {
172
-
173
- // Get preset from db
174
- $preset_email = $this->get_email(intval($preset_id));
175
- $global_options = $this->extract_global_options_from($preset_email);
176
- $content = $this->regenerate_email_blocks($preset_email->message, $global_options);
177
- }
178
-
179
- return $content;
180
- }
181
-
182
- private function get_preset_global_options($preset_id) {
183
-
184
- if ($this->is_a_tnp_default_preset($preset_id)) {
185
- return [];
186
- }
187
-
188
- // Get preset from db
189
- $preset_email = $this->get_email(intval($preset_id));
190
- $global_options = $this->extract_global_options_from($preset_email);
191
-
192
- return $global_options;
193
- }
194
-
195
- private function extract_global_options_from($email) {
196
- $global_options = [];
197
- foreach ($email->options as $global_option_name => $global_option) {
198
- if (strpos($global_option_name, 'composer_') === 0) {
199
- $global_options[str_replace('composer_', '', $global_option_name)] = $global_option;
200
- }
201
- }
202
-
203
- return $global_options;
204
- }
205
-
206
- private function is_a_tnp_default_preset($preset_id) {
207
- return in_array($preset_id, self::$PRESETS_LIST);
208
- }
209
-
210
- private function get_all_preset() {
211
-
212
- $content = "<div class='tnpc-preset-container'>";
213
-
214
- if ($this->is_normal_context_request()) {
215
- $content .= "<div class='tnpc-preset-legacy-themes'><a href='" . $this->get_admin_page_url('theme') . "'>" . __('Looking for legacy themes?', 'newsletter') . "</a></div>";
216
- }
217
-
218
- // LOAD USER PRESETS
219
- $user_preset_list = $this->get_emails(self::PRESET_EMAIL_TYPE);
220
-
221
- foreach ($user_preset_list as $user_preset) {
222
-
223
- $default_icon_url = NEWSLETTER_URL . "/emails/presets/default-icon.png?ver=2";
224
- $preset_name = $user_preset->subject;
225
- $delete_preset_text = __('Delete', 'newsletter');
226
- $edit_preset_text = __('Edit', 'newsletter');
227
-
228
- // esc_js() assumes the string will be in single quote (arghhh!!!)
229
- $onclick_edit = 'tnpc_edit_preset(' . ((int) $user_preset->id) . ', \'' . esc_js($preset_name) . '\', event)';
230
- $onclick_delete = 'tnpc_delete_preset(' . ((int) $user_preset->id) . ', \'' . esc_js($preset_name) . '\', event)';
231
- $onclick_load = 'tnpc_load_preset(' . ((int) $user_preset->id) . ', \'' . esc_js($preset_name) . '\', event)';
232
-
233
- $content .= "<div class='tnpc-preset' onclick='" . esc_attr($onclick_load) . "'>\n";
234
- $content .= "<img src='$default_icon_url' title='" . esc_attr($preset_name) . "' alt='" . esc_attr($preset_name) . "'>\n";
235
- $content .= "<span class='tnpc-preset-label'>" . esc_html($user_preset->subject) . "</span>\n";
236
- $content .= "<span class='tnpc-delete-preset' onclick='" . esc_attr($onclick_delete) . "' title='" . esc_attr($delete_preset_text) . "'><i class='fas fa-times'></i></span>\n";
237
- $content .= "<span class='tnpc-edit-preset' onclick='" . esc_attr($onclick_edit) . "' title='" . esc_attr($edit_preset_text) . "'><i class='fas fa-pencil-alt'></i></span>\n";
238
- $content .= "</div>";
239
- }
240
-
241
- // LOAD TNP PRESETS
242
- foreach (self::$PRESETS_LIST as $id) {
243
- $preset = $this->get_preset_from_file($id);
244
- $preset_name = esc_html($preset->name);
245
- $content .= "<div class='tnpc-preset' onclick='tnpc_load_preset(\"$id\")'>";
246
- $content .= "<img src='$preset->icon' title='$preset_name' alt='$preset_name'/>";
247
- $content .= "<span class='tnpc-preset-label'>$preset_name</span>";
248
- $content .= "</div>";
249
- }
250
-
251
- if ($this->is_normal_context_request()) {
252
- $content .= $this->get_automated_spot_element();
253
- $content .= $this->get_autoresponder_spot_element();
254
- $content .= $this->get_raw_html_preset_element();
255
- }
256
-
257
- return $content;
258
- }
259
-
260
- private function is_normal_context_request() {
261
- return empty($_REQUEST['context_type']);
262
- }
263
-
264
- private function is_automated_context_request() {
265
- return isset($_REQUEST['context_type']) && $_REQUEST['context_type'] === 'automated';
266
- }
267
-
268
- private function is_autoresponder_context_request() {
269
- return isset($_REQUEST['context_type']) && $_REQUEST['context_type'] === 'autoresponder';
270
- }
271
-
272
- private function get_automated_spot_element() {
273
- $result = "<div class='tnpc-preset'>";
274
- if (class_exists('NewsletterAutomated')) {
275
- $result .= "<a href='?page=newsletter_automated_index'>";
276
- } else {
277
- $result .= "<a href='https://www.thenewsletterplugin.com/automated?utm_source=plugin&utm_campaign=automated&utm_medium=composer'>";
278
- }
279
- $result .= "<img src='" . plugins_url('newsletter') . "/emails/images/automated.png' title='Automated addon' alt='Automated'/>";
280
- $result .= "<span class='tnpc-preset-label'>Daily, weekly and monthly newsletters</span></a>";
281
- $result .= "</div>";
282
-
283
- return $result;
284
- }
285
-
286
- private function get_autoresponder_spot_element() {
287
- $result = "<div class='tnpc-preset'>";
288
- if (class_exists('NewsletterAutoresponder')) {
289
- $result .= "<a href='?page=newsletter_autoresponder_index'>";
290
- } else {
291
- $result .= "<a href='https://www.thenewsletterplugin.com/autoresponder?utm_source=plugin&utm_campaign=autoresponder&utm_medium=composer' target='_blank'>";
292
- }
293
- $result .= "<img src='" . plugins_url('newsletter') . "/emails/images/autoresponder.png' title='Autoresponder addon' alt='Autoresponder'/>";
294
- $result .= "<span class='tnpc-preset-label'>Autoresponders</span></a>";
295
- $result .= "</div>";
296
-
297
- return $result;
298
- }
299
-
300
- private function get_raw_html_preset_element() {
301
-
302
- $result = "<div class='tnpc-preset tnpc-preset-html' onclick='location.href=\"" . wp_nonce_url('admin.php?page=newsletter_emails_new&id=rawhtml', 'newsletter-new') . "\"'>";
303
- $result .= "<img src='" . plugins_url('newsletter') . "/emails/images/rawhtml.png' title='RAW HTML' alt='RAW'/>";
304
- $result .= "<span class='tnpc-preset-label'>Raw HTML</span>";
305
- $result .= "</div>";
306
-
307
- $result .= "<div class='clear'></div>";
308
- $result .= "</div>";
309
-
310
- return $result;
311
- }
312
-
313
- /**
314
- * Check if the preset name exists and adds an incremental suffix if the name exists.
315
- *
316
- * @param string $name
317
- *
318
- * @return string
319
- */
320
- public function sanitize_preset_name($name) {
321
- global $wpdb;
322
-
323
- $name = empty($name) ? __('Empty name preset', 'newsletter') : $name;
324
- $name = sanitize_text_field($name);
325
- $type = self::PRESET_EMAIL_TYPE;
326
- $count = (int) $wpdb->get_var("SELECT COUNT(*) FROM " . NEWSLETTER_EMAILS_TABLE . " WHERE type='$type' and subject='$name'");
327
-
328
- $name = $count > 0 ? $name . " - " . ($count + 1) : $name;
329
-
330
- return $name;
331
- }
332
-
333
- function has_dynamic_blocks($theme) {
334
- preg_match_all('/data-json="(.*?)"/m', $theme, $matches, PREG_PATTERN_ORDER);
335
- foreach ($matches[1] as $match) {
336
- $a = html_entity_decode($match, ENT_QUOTES, 'UTF-8');
337
- $options = $this->options_decode($a);
338
-
339
- $block = $this->get_block($options['block_id']);
340
- if (!$block) {
341
- continue;
342
- }
343
- if ($block['type'] == 'dynamic') {
344
- return true;
345
- }
346
- }
347
- return false;
348
- }
349
-
350
- /**
351
- * Regenerates a saved composed email rendering each block. Regeneration is
352
- * conditioned (possibly) by the context. The context is usually passed to blocks
353
- * so they can act in the right manner.
354
- *
355
- * $context contains a type and, for automated, the last_run.
356
- *
357
- * $email can actually be even a string containing the full newsletter HTML code.
358
- *
359
- * @param TNP_Email $email (Rinominare)
360
- * @return string
361
- */
362
- function regenerate($email, $context = []) {
363
-
364
- // Cannot be removed due to compatibility issues with old Automated versions
365
- if (is_object($email)) {
366
- $theme = $email->message;
367
- } else {
368
- $theme = $email;
369
- }
370
-
371
- //$this->logger->debug('Starting email regeneration');
372
- //$this->logger->debug($context);
373
-
374
- if (empty($theme)) {
375
- $this->logger->debug('The email was empty');
376
- return array('body' => '', 'subject' => '');
377
- }
378
-
379
- $context = array_merge(['last_run' => 0, 'type' => ''], $context);
380
-
381
- preg_match_all('/data-json="(.*?)"/m', $theme, $matches, PREG_PATTERN_ORDER);
382
-
383
- $result = '';
384
- $subject = '';
385
-
386
- foreach ($matches[1] as $match) {
387
- $a = html_entity_decode($match, ENT_QUOTES, 'UTF-8');
388
- $options = $this->options_decode($a);
389
-
390
- $block = $this->get_block($options['block_id']);
391
- if (!$block) {
392
- $this->logger->debug('Unable to load the block ' . $options['block_id']);
393
- //continue;
394
- }
395
-
396
- ob_start();
397
- $out = $this->render_block($options['block_id'], true, $options, $context);
398
- if (is_array($out)) {
399
- if ($out['return_empty_message'] || $out['stop']) {
400
- if (is_object($email)) {
401
- return false;
402
- }
403
- return array();
404
- }
405
- if ($out['skip']) {
406
- if (NEWSLETTER_DEBUG) {
407
- $result .= 'Block removed by request';
408
- }
409
- continue;
410
- }
411
- if (empty($subject) && !empty($out['subject'])) {
412
- $subject = $out['subject'];
413
- }
414
- }
415
- $block_html = ob_get_clean();
416
- $result .= $block_html;
417
- }
418
-
419
- // We need to keep the CSS/HEAD part, the regenearion is only about blocks
420
-
421
- if (is_object($email)) {
422
- $result = TNP_Composer::get_main_wrapper_open($email) . $result . TNP_Composer::get_main_wrapper_close($email);
423
- }
424
-
425
- $x = strpos($theme, '<body');
426
- if ($x !== false) {
427
- $x = strpos($theme, '>', $x);
428
- $result = substr($theme, 0, $x + 1) . $result . '</body></html>';
429
- } else {
430
-
431
- }
432
-
433
- if (is_object($email)) {
434
- $email->message = $result;
435
- $email->subject = $subject;
436
- return true;
437
- }
438
-
439
- // Kept for compatibility
440
- return array('body' => $result, 'subject' => $subject);
441
- }
442
-
443
- function remove_block_data($text) {
444
- // TODO: Lavorare!
445
- return $text;
446
- }
447
-
448
- static function get_outlook_wrapper_open($width = 600) {
449
- return '<!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" align="center" cellspacing="0" width="' . $width . '"><tr><td width="' . $width . '" style="vertical-align:top;width:' . $width . 'px;"><![endif]-->';
450
- }
451
-
452
- static function get_outlook_wrapper_close() {
453
- return "<!--[if mso | IE]></td></tr></table><![endif]-->";
454
- }
455
-
456
- function hook_safe_style_css($rules) {
457
- $rules[] = 'display';
458
- return $rules;
459
- }
460
-
461
- /**
462
- * Renders a block identified by its id, using the block options and adding a wrapper
463
- * if required (for the first block rendering).
464
- *
465
- * @param string $block_id
466
- * @param boolean $wrapper
467
- * @param array $options
468
- * @param array $context
469
- * @param array $composer
470
- */
471
- function render_block($block_id = null, $wrapper = false, $options = [], $context = [], $composer = []) {
472
- static $kses_style_filter = false;
473
- include_once NEWSLETTER_INCLUDES_DIR . '/helper.php';
474
-
475
- //Remove 'options_composer_' prefix
476
- $composer_defaults = [];
477
- foreach (TNP_Composer::get_global_style_defaults() as $global_option_name => $global_option) {
478
- $composer_defaults[str_replace('options_composer_', '', $global_option_name)] = $global_option;
479
- }
480
- $composer = array_merge($composer_defaults, $composer);
481
-
482
- $width = 600;
483
- $font_family = 'Helvetica, Arial, sans-serif';
484
-
485
- $global_title_font_family = $composer['title_font_family'];
486
- $global_title_font_size = $composer['title_font_size'];
487
- $global_title_font_color = $composer['title_font_color'];
488
- $global_title_font_weight = $composer['title_font_weight'];
489
-
490
- $global_text_font_family = $composer['text_font_family'];
491
- $global_text_font_size = $composer['text_font_size'];
492
- $global_text_font_color = $composer['text_font_color'];
493
- $global_text_font_weight = $composer['text_font_weight'];
494
-
495
- $global_button_font_family = $composer['button_font_family'];
496
- $global_button_font_size = $composer['button_font_size'];
497
- $global_button_font_color = $composer['button_font_color'];
498
- $global_button_font_weight = $composer['button_font_weight'];
499
- $global_button_background_color = $composer['button_background_color'];
500
-
501
- $global_block_background = $composer['block_background'];
502
-
503
- $info = Newsletter::instance()->get_options('info');
504
-
505
- // Just in case...
506
- if (!is_array($options)) {
507
- $options = array();
508
- }
509
-
510
- // This code filters the HTML to remove javascript and unsecure attributes and enable the
511
- // "display" rule for CSS which is needed in blocks to force specific "block" or "inline" or "table".
512
- add_filter('safe_style_css', [$this, 'hook_safe_style_css'], 9999);
513
- $options = wp_kses_post_deep($options);
514
- remove_filter('safe_style_css', [$this, 'hook_safe_style_css']);
515
-
516
- $block_options = get_option('newsletter_main');
517
-
518
- $block = $this->get_block($block_id);
519
-
520
- if (!isset($context['type']))
521
- $context['type'] = '';
522
-
523
- // Block not found
524
- if (!$block) {
525
- if ($wrapper) {
526
- echo '<table border="0" cellpadding="0" cellspacing="0" align="center" width="100%" style="border-collapse: collapse; width: 100%;" class="tnpc-row tnpc-row-block" data-id="', esc_attr($block_id), '">';
527
- echo '<tr>';
528
- echo '<td data-options="" bgcolor="#ffffff" align="center" style="padding: 0; font-family: Helvetica, Arial, sans-serif;" class="edit-block">';
529
- }
530
- echo $this->get_outlook_wrapper_open($width);
531
-
532
- echo '<p>Ops, this block type is no more registered!</p>';
533
-
534
- echo $this->get_outlook_wrapper_close();
535
-
536
- if ($wrapper) {
537
- echo '</td></tr></table>';
538
- }
539
- return;
540
- }
541
-
542
- $out = ['subject' => '', 'return_empty_message' => false, 'stop' => false, 'skip' => false];
543
-
544
- $dir = is_rtl() ? 'rtl' : 'ltr';
545
- $align_left = is_rtl() ? 'right' : 'left';
546
- $align_right = is_rtl() ? 'left' : 'right';
547
-
548
- ob_start();
549
- $logger = $this->logger;
550
- include $block['dir'] . '/block.php';
551
- $content = trim(ob_get_clean());
552
-
553
- if (empty($content)) {
554
- return $out;
555
- }
556
-
557
- $common_defaults = array(
558
- 'block_padding_top' => 0,
559
- 'block_padding_bottom' => 0,
560
- 'block_padding_right' => 0,
561
- 'block_padding_left' => 0,
562
- 'block_background' => '',
563
- 'block_background_2' => '',
564
- 'block_width' => 600,
565
- 'block_align' => 'center'
566
- );
567
-
568
- $options = array_merge($common_defaults, $options);
569
-
570
- // Obsolete
571
- $content = str_replace('{width}', $width, $content);
572
-
573
- $content = $this->inline_css($content, true);
574
-
575
- // CSS driven by the block
576
- // Requited for the server side parsing and rendering
577
- $options['block_id'] = $block_id;
578
-
579
- $options['block_padding_top'] = (int) str_replace('px', '', $options['block_padding_top']);
580
- $options['block_padding_bottom'] = (int) str_replace('px', '', $options['block_padding_bottom']);
581
- $options['block_padding_right'] = (int) str_replace('px', '', $options['block_padding_right']);
582
- $options['block_padding_left'] = (int) str_replace('px', '', $options['block_padding_left']);
583
-
584
- $block_background = empty($options['block_background']) ? $global_block_background : $options['block_background'];
585
-
586
- // Internal TD wrapper
587
- $style = 'text-align: center; ';
588
- $style .= 'width: 100% !important; ';
589
- $style .= 'line-height: normal !important; ';
590
- $style .= 'letter-spacing: normal; ';
591
- $style .= 'padding-top: ' . $options['block_padding_top'] . 'px; ';
592
- $style .= 'padding-left: ' . $options['block_padding_left'] . 'px; ';
593
- $style .= 'padding-right: ' . $options['block_padding_right'] . 'px; ';
594
- $style .= 'padding-bottom: ' . $options['block_padding_bottom'] . 'px; ';
595
- $style .= 'background-color: ' . $block_background . ';';
596
-
597
- if (isset($options['block_background_gradient'])) {
598
- $style .= 'background: linear-gradient(180deg, ' . $block_background . ' 0%, ' . $options['block_background_2'] . ' 100%);';
599
- }
600
-
601
- $data = $this->options_encode($options);
602
- // First time block creation wrapper
603
- if ($wrapper) {
604
- echo '<table border="0" cellpadding="0" cellspacing="0" align="center" width="100%" style="border-collapse: collapse; width: 100%;" class="tnpc-row tnpc-row-block" data-id="', esc_attr($block_id), '">', "\n";
605
- echo "<tr>";
606
- echo '<td align="center" style="padding: 0;" class="edit-block">', "\n";
607
- }
608
-
609
- // Container that fixes the width and makes the block responsive
610
- echo $this->get_outlook_wrapper_open($options['block_width']);
611
-
612
- echo '<table type="options" data-json="', esc_attr($data), '" class="tnpc-block-content" border="0" cellpadding="0" align="center" cellspacing="0" width="100%" style="width: 100%!important; max-width: ', $options['block_width'], 'px!important">', "\n";
613
- echo "<tr>";
614
- echo '<td align="', esc_attr($options['block_align']), '" style="', $style, '" bgcolor="', $block_background, '" width="100%">';
615
-
616
- //echo "<!-- block generated content -->\n";
617
- echo trim($content);
618
- //echo "\n<!-- /block generated content -->\n";
619
-
620
- echo "</td></tr></table>";
621
- echo $this->get_outlook_wrapper_close();
622
-
623
- // First time block creation wrapper
624
- if ($wrapper) {
625
- echo "</td></tr></table>";
626
- }
627
-
628
- return $out;
629
- }
630
-
631
- /**
632
- * Ajax call to render a block with a new set of options after the settings popup
633
- * has been saved.
634
- *
635
- * @param type $block_id
636
- * @param type $wrapper
637
- */
638
- function tnpc_render_callback() {
639
- if (!check_ajax_referer('save')) {
640
- $this->dienow('Expired request');
641
- }
642
-
643
- $block_id = $_POST['id'];
644
- $wrapper = isset($_POST['full']);
645
- $options = $this->restore_options_from_request();
646
-
647
- $this->render_block($block_id, $wrapper, $options, [], $_POST['composer']);
648
- wp_die();
649
- }
650
-
651
- function hook_wp_ajax_tnpc_regenerate_email() {
652
-
653
- $content = stripslashes($_POST['content']);
654
- $global_options = $_POST['composer'];
655
-
656
- $regenerated_content = $this->regenerate_email_blocks($content, $global_options);
657
-
658
- wp_send_json_success([
659
- 'content' => $regenerated_content,
660
- 'message' => __('Successfully updated', 'newsletter')
661
- ]);
662
- }
663
-
664
- private function regenerate_email_blocks($content, $global_options) {
665
-
666
- $raw_block_options = $this->extract_encoded_blocks_options($content);
667
-
668
- $regenerated_content = '';
669
-
670
- foreach ($raw_block_options as $raw_block_option) {
671
-
672
- /* $a = html_entity_decode( $raw_block_option, ENT_QUOTES, 'UTF-8' );
673
- $block_options = $this->options_decode( $a ); */
674
-
675
- $block_options = $this->options_decode($raw_block_option);
676
-
677
- $block = $this->get_block($block_options['block_id']);
678
- if (!$block) {
679
- $this->logger->debug('Unable to load the block ' . $block_options['block_id']);
680
- }
681
-
682
- ob_start();
683
- $this->render_block($block_options['block_id'], true, $block_options, [], $global_options);
684
- $block_html = ob_get_clean();
685
-
686
- $regenerated_content .= $block_html;
687
- }
688
-
689
- return $regenerated_content;
690
- }
691
-
692
- /**
693
- * @param string $html_email_content Email html content
694
- *
695
- * @return string[] Encoded options of email blocks
696
- */
697
- private function extract_encoded_blocks_options($html_email_content) {
698
-
699
- preg_match_all('/data-json="(.*?)"/m', $html_email_content, $raw_block_options, PREG_PATTERN_ORDER);
700
-
701
- return $raw_block_options[1];
702
- }
703
-
704
- function tnpc_preview_callback() {
705
- $email = Newsletter::instance()->get_email($_REQUEST['id'], ARRAY_A);
706
-
707
- if (empty($email)) {
708
- echo 'Wrong email identifier';
709
- return;
710
- }
711
-
712
- echo $email['message'];
713
-
714
- wp_die();
715
- }
716
-
717
- function tnpc_css_callback() {
718
- include NEWSLETTER_DIR . '/emails/tnp-composer/css/newsletter.css';
719
- wp_die();
720
- }
721
-
722
- /** Returns the correct admin page to edit the newsletter with the correct editor. */
723
- function get_editor_url($email_id, $editor_type) {
724
- switch ($editor_type) {
725
- case NewsletterEmails::EDITOR_COMPOSER:
726
- return admin_url("admin.php") . '?page=newsletter_emails_composer&id=' . $email_id;
727
- case NewsletterEmails::EDITOR_HTML:
728
- return admin_url("admin.php") . '?page=newsletter_emails_editorhtml&id=' . $email_id;
729
- case NewsletterEmails::EDITOR_TINYMCE:
730
- return admin_url("admin.php") . '?page=newsletter_emails_editortinymce&id=' . $email_id;
731
- }
732
- }
733
-
734
- /**
735
- * Returns the button linked to the correct "edit" page for the passed newsletter. The edit page can be an editor
736
- * or the targeting page (it depends on newsletter status).
737
- *
738
- * @param TNP_Email $email
739
- */
740
- function get_edit_button($email, $only_icon = false) {
741
-
742
- $editor_type = $this->get_editor_type($email);
743
- if ($email->status == 'new') {
744
- $edit_url = $this->get_editor_url($email->id, $editor_type);
745
- } else {
746
- $edit_url = 'admin.php?page=newsletter_emails_edit&id=' . $email->id;
747
- }
748
- switch ($editor_type) {
749
- case NewsletterEmails::EDITOR_COMPOSER:
750
- $icon_class = 'th-large';
751
- break;
752
- case NewsletterEmails::EDITOR_HTML:
753
- $icon_class = 'code';
754
- break;
755
- default:
756
- $icon_class = 'edit';
757
- break;
758
- }
759
- if ($only_icon) {
760
- return '<a class="button-primary" href="' . $edit_url . '" title="' . esc_attr__('Edit', 'newsletter') . '">' .
761
- '<i class="fas fa-' . $icon_class . '"></i></a>';
762
- } else {
763
- return '<a class="button-primary" href="' . $edit_url . '" title="' . esc_attr__('Edit', 'newsletter') . '">' .
764
- '<i class="fas fa-' . $icon_class . '"></i> ' . __('Edit', 'newsletter') . '</a>';
765
- }
766
- }
767
-
768
- /** Returns the correct editor type for the provided newsletter. Contains backward compatibility code. */
769
- function get_editor_type($email) {
770
- $email = (object) $email;
771
- $editor_type = $email->editor;
772
-
773
- // Backward compatibility
774
- $email_options = maybe_unserialize($email->options);
775
- if (isset($email_options['composer'])) {
776
- $editor_type = NewsletterEmails::EDITOR_COMPOSER;
777
- }
778
- // End backward compatibility
779
-
780
- return $editor_type;
781
- }
782
-
783
- /**
784
- *
785
- * @param type $action
786
- * @param type $user
787
- * @param type $email
788
- * @return type
789
- * @global wpdb $wpdb
790
- */
791
- function hook_newsletter_action($action, $user, $email) {
792
- global $wpdb;
793
-
794
- switch ($action) {
795
- case 'v':
796
- case 'view':
797
- $id = $_GET['id'];
798
- if ($id == 'last') {
799
- $email = $wpdb->get_row("select * from " . NEWSLETTER_EMAILS_TABLE . " where private=0 and type='message' and status='sent' order by send_on desc limit 1");
800
- } else {
801
- $email = $this->get_email($_GET['id']);
802
- }
803
- if (empty($email)) {
804
- header("HTTP/1.0 404 Not Found");
805
- die('Email not found');
806
- }
807
-
808
- if (!Newsletter::instance()->is_allowed()) {
809
-
810
- if ($email->status == 'new') {
811
- header("HTTP/1.0 404 Not Found");
812
- die('Not sent yet');
813
- }
814
-
815
- if ($email->private == 1) {
816
- if (!$user) {
817
- header("HTTP/1.0 404 Not Found");
818
- die('No available for online view');
819
- }
820
- $sent = $wpdb->get_row($wpdb->prepare("select * from " . NEWSLETTER_SENT_TABLE . " where email_id=%d and user_id=%d limit 1", $email->id, $user->id));
821
- if (!$sent) {
822
- header("HTTP/1.0 404 Not Found");
823
- die('No available for online view');
824
- }
825
- }
826
- }
827
-
828
-
829
- header('Content-Type: text/html;charset=UTF-8');
830
- header('X-Robots-Tag: noindex,nofollow,noarchive');
831
- header('Cache-Control: no-cache,no-store,private');
832
-
833
- echo $this->replace($email->message, $user, $email);
834
-
835
- die();
836
- break;
837
-
838
- case 'emails-css':
839
- $email_id = (int) $_GET['id'];
840
-
841
- $body = Newsletter::instance()->get_email_field($email_id, 'message');
842
-
843
- $x = strpos($body, '<style');
844
- if ($x === false)
845
- return;
846
-
847
- $x = strpos($body, '>', $x);
848
- $y = strpos($body, '</style>');
849
-
850
- header('Content-Type: text/css;charset=UTF-8');
851
-
852
- echo substr($body, $x + 1, $y - $x - 1);
853
-
854
- die();
855
- break;
856
-
857
- case 'emails-composer-css':
858
- header('Cache: no-cache');
859
- header('Content-Type: text/css');
860
- echo $this->get_composer_css();
861
- die();
862
- break;
863
-
864
- case 'emails-preview':
865
- if (!Newsletter::instance()->is_allowed()) {
866
- die('Not enough privileges');
867
- }
868
-
869
- if (!check_admin_referer('view')) {
870
- die();
871
- }
872
-
873
- $theme_id = $_GET['id'];
874
- $theme = $this->themes->get_theme($theme_id);
875
-
876
- // Used by theme code
877
- $theme_options = $this->themes->get_options($theme_id);
878
-
879
- $theme_url = $theme['url'];
880
-
881
- header('Content-Type: text/html;charset=UTF-8');
882
-
883
- include $theme['dir'] . '/theme.php';
884
-
885
- die();
886
- break;
887
-
888
- case 'emails-preview-text':
889
- header('Content-Type: text/plain;charset=UTF-8');
890
- if (!Newsletter::instance()->is_allowed()) {
891
- die('Not enough privileges');
892
- }
893
-
894
- if (!check_admin_referer('view')) {
895
- die();
896
- }
897
-
898
- // Used by theme code
899
- $theme_options = $this->get_current_theme_options();
900
-
901
- $file = include $theme['dir'] . '/theme-text.php';
902
-
903
- if (is_file($file)) {
904
- include $file;
905
- }
906
-
907
- die();
908
- break;
909
-
910
-
911
- case 'emails-create':
912
- // Newsletter from themes are created on frontend context because sometime WP themes change the way the content,
913
- // excerpt, thumbnail are extracted.
914
- if (!Newsletter::instance()->is_allowed()) {
915
- die('Not enough privileges');
916
- }
917
-
918
- require_once NEWSLETTER_INCLUDES_DIR . '/controls.php';
919
- $controls = new NewsletterControls();
920
-
921
- if (!$controls->is_action('create')) {
922
- die('Wrong call');
923
- }
924
-
925
- $theme_id = $controls->data['id'];
926
- $theme = $this->themes->get_theme($theme_id);
927
-
928
- if (!$theme) {
929
- die('invalid theme');
930
- }
931
-
932
- $this->themes->save_options($theme_id, $controls->data);
933
-
934
- $email = array();
935
- $email['status'] = 'new';
936
- $email['subject'] = ''; //__('Here the email subject', 'newsletter');
937
- $email['track'] = 1;
938
- $email['send_on'] = time();
939
- $email['editor'] = NewsletterEmails::EDITOR_TINYMCE;
940
- $email['type'] = 'message';
941
-
942
- $theme_options = $this->themes->get_options($theme_id);
943
-
944
- $theme_url = $theme['url'];
945
- $theme_subject = '';
946
-
947
- ob_start();
948
- include $theme['dir'] . '/theme.php';
949
- $email['message'] = ob_get_clean();
950
-
951
-
952
- if (!empty($theme_subject)) {
953
- $email['subject'] = $theme_subject;
954
- }
955
-
956
- if (file_exists($theme['dir'] . '/theme-text.php')) {
957
- ob_start();
958
- include $theme['dir'] . '/theme-text.php';
959
- $email['message_text'] = ob_get_clean();
960
- } else {
961
- $email['message_text'] = 'You need a modern email client to read this email. Read it online: {email_url}.';
962
- }
963
-
964
- $email = $this->save_email($email);
965
-
966
- $edit_url = $this->get_editor_url($email->id, $email->editor);
967
-
968
- header('Location: ' . $edit_url);
969
-
970
- die();
971
- break;
972
- }
973
- }
974
-
975
- function admin_menu() {
976
- $this->add_menu_page('index', 'Newsletters');
977
- $this->add_admin_page('list', 'Email List');
978
- $this->add_admin_page('new', 'Email New');
979
- $this->add_admin_page('edit', 'Email Edit');
980
- $this->add_admin_page('theme', 'Email Themes');
981
- $this->add_admin_page('composer', 'The Composer');
982
- $this->add_admin_page('editorhtml', 'HTML Editor');
983
- $this->add_admin_page('editortinymce', 'TinyMCE Editor');
984
- }
985
-
986
- /**
987
- * Builds a block data structure starting from the folder containing the block
988
- * files.
989
- *
990
- * @param string $dir
991
- * @return array | WP_Error
992
- */
993
- function build_block($dir) {
994
- $dir = realpath($dir);
995
- $dir = wp_normalize_path($dir);
996
- $full_file = $dir . '/block.php';
997
- if (!is_file($full_file)) {
998
- return new WP_Error('1', 'Missing block.php file in ' . $dir);
999
- }
1000
-
1001
- $relative_dir = substr($dir, strlen(WP_CONTENT_DIR));
1002
- $file = basename($dir);
1003
-
1004
- $data = get_file_data($full_file, ['name' => 'Name', 'section' => 'Section', 'description' => 'Description', 'type' => 'Type']);
1005
- $defaults = ['section' => 'content', 'name' => ucfirst($file), 'descritpion' => '', 'icon' => plugins_url('newsletter') . '/admin/images/block-icon.png'];
1006
- $data = array_merge($defaults, $data);
1007
-
1008
- if (is_file($dir . '/icon.png')) {
1009
- $data['icon'] = content_url($relative_dir . '/icon.png');
1010
- }
1011
-
1012
- $data['id'] = sanitize_key($file);
1013
-
1014
- // Absolute path of the block files
1015
- $data['dir'] = $dir;
1016
- $data['url'] = content_url($relative_dir);
1017
-
1018
- return $data;
1019
- }
1020
-
1021
- /**
1022
- *
1023
- * @param type $dir
1024
- * @return type
1025
- */
1026
- function scan_blocks_dir($dir) {
1027
- $dir = realpath($dir);
1028
- if (!$dir) {
1029
- return [];
1030
- }
1031
- $dir = wp_normalize_path($dir);
1032
-
1033
- $list = [];
1034
- $handle = opendir($dir);
1035
- while ($file = readdir($handle)) {
1036
-
1037
- $data = $this->build_block($dir . '/' . $file);
1038
-
1039
- if (is_wp_error($data)) {
1040
- $this->logger->error($data);
1041
- continue;
1042
- }
1043
- $list[$data['id']] = $data;
1044
- }
1045
- closedir($handle);
1046
- return $list;
1047
- }
1048
-
1049
- /**
1050
- * Array of arrays with every registered block and legacy block converted to the new
1051
- * format.
1052
- *
1053
- * @return array
1054
- */
1055
- function get_blocks() {
1056
-
1057
- if (!is_null($this->blocks)) {
1058
- return $this->blocks;
1059
- }
1060
-
1061
- $this->blocks = $this->scan_blocks_dir(__DIR__ . '/blocks');
1062
-
1063
- $extended = $this->scan_blocks_dir(WP_CONTENT_DIR . '/extensions/newsletter/blocks');
1064
-
1065
- $this->blocks = array_merge($extended, $this->blocks);
1066
-
1067
- $dirs = apply_filters('newsletter_blocks_dir', array());
1068
-
1069
- //$this->logger->debug('Block dirs:');
1070
- //$this->logger->debug($dirs);
1071
-
1072
- foreach ($dirs as $dir) {
1073
- $list = $this->scan_blocks_dir($dir);
1074
- $this->blocks = array_merge($list, $this->blocks);
1075
- }
1076
-
1077
- do_action('newsletter_register_blocks');
1078
-
1079
- foreach (TNP_Composer::$block_dirs as $dir) {
1080
- $block = $this->build_block($dir);
1081
- if (is_wp_error($block)) {
1082
- $this->logger->error($block);
1083
- continue;
1084
- }
1085
- if (!isset($this->blocks[$block['id']])) {
1086
- $this->blocks[$block['id']] = $block;
1087
- } else {
1088
- $this->logger->error('The block "' . $block['id'] . '" has already been registered');
1089
- }
1090
- }
1091
-
1092
- $this->blocks = array_reverse($this->blocks);
1093
- return $this->blocks;
1094
- }
1095
-
1096
- /**
1097
- * Return a single block (associative array) checking for legacy ID as well.
1098
- *
1099
- * @param string $id
1100
- * @return array
1101
- */
1102
- function get_block($id) {
1103
- switch ($id) {
1104
- case 'content-03-text.block':
1105
- $id = 'text';
1106
- break;
1107
- case 'footer-03-social.block':
1108
- $id = 'social';
1109
- break;
1110
- case 'footer-02-canspam.block':
1111
- $id = 'canspam';
1112
- break;
1113
- case 'content-05-image.block':
1114
- $id = 'image';
1115
- break;
1116
- case 'header-01-header.block':
1117
- $id = 'header';
1118
- break;
1119
- case 'footer-01-footer.block':
1120
- $id = 'footer';
1121
- break;
1122
- case 'content-02-heading.block':
1123
- $id = 'heading';
1124
- break;
1125
- case 'content-07-twocols.block':
1126
- case 'content-06-posts.block':
1127
- $id = 'posts';
1128
- break;
1129
- case 'content-04-cta.block':
1130
- $id = 'cta';
1131
- break;
1132
- case 'content-01-hero.block':
1133
- $id = 'hero';
1134
- break;
1135
- // case 'content-02-heading.block': $id = '/plugins/newsletter/emails/blocks/heading';
1136
- // break;
1137
- }
1138
-
1139
- // Conversion for old full path ID
1140
- $id = sanitize_key(basename($id));
1141
-
1142
- // TODO: Correct id for compatibility
1143
- $blocks = $this->get_blocks();
1144
- if (!isset($blocks[$id])) {
1145
- return null;
1146
- }
1147
- return $blocks[$id];
1148
- }
1149
-
1150
- function scan_presets_dir($dir = null) {
1151
-
1152
- if (is_null($dir)) {
1153
- $dir = __DIR__ . '/presets';
1154
- }
1155
-
1156
- if (!is_dir($dir)) {
1157
- return array();
1158
- }
1159
-
1160
- $handle = opendir($dir);
1161
- $list = array();
1162
- $relative_dir = substr($dir, strlen(WP_CONTENT_DIR));
1163
- while ($file = readdir($handle)) {
1164
-
1165
- if ($file == '.' || $file == '..')
1166
- continue;
1167
-
1168
- // The block unique key, we should find out how to build it, maybe an hash of the (relative) dir?
1169
- $preset_id = sanitize_key($file);
1170
-
1171
- $full_file = $dir . '/' . $file . '/preset.json';
1172
-
1173
- if (!is_file($full_file)) {
1174
- continue;
1175
- }
1176
-
1177
- $icon = content_url($relative_dir . '/' . $file . '/icon.png');
1178
-
1179
- $list[$preset_id] = $icon;
1180
- }
1181
- closedir($handle);
1182
- return $list;
1183
- }
1184
-
1185
- function get_preset_from_file($id, $dir = null) {
1186
-
1187
- if (is_null($dir)) {
1188
- $dir = __DIR__ . '/presets';
1189
- }
1190
-
1191
- $id = $this->sanitize_file_name($id);
1192
-
1193
- if (!is_dir($dir . '/' . $id) || !in_array($id, self::$PRESETS_LIST)) {
1194
- return array();
1195
- }
1196
-
1197
- $json_content = file_get_contents("$dir/$id/preset.json");
1198
- $json_content = str_replace("{placeholder_base_url}", plugins_url('newsletter') . '/emails/presets', $json_content);
1199
- $json = json_decode($json_content);
1200
- $json->icon = NEWSLETTER_URL . "/emails/presets/$id/icon.png?ver=2";
1201
-
1202
- return $json;
1203
- }
1204
-
1205
- function get_composer_css() {
1206
- $css = file_get_contents(__DIR__ . '/tnp-composer/css/newsletter.css');
1207
- $css .= "\n\n";
1208
- $css .= file_get_contents(__DIR__ . '/tnp-composer/css/backend.css');
1209
- $blocks = $this->get_blocks();
1210
- foreach ($blocks as $block) {
1211
- if (!file_exists($block['dir'] . '/style.css')) {
1212
- continue;
1213
- }
1214
- $css .= "\n\n";
1215
- $css .= "/* " . $block['name'] . " */\n";
1216
- $css .= file_get_contents($block['dir'] . '/style.css');
1217
- }
1218
- return $css;
1219
- }
1220
-
1221
- /**
1222
- * Send an email to the test subscribers.
1223
- *
1224
- * @param TNP_Email $email Could be any object with the TNP_Email attributes
1225
- * @param NewsletterControls $controls
1226
- */
1227
- function send_test_email($email, $controls) {
1228
- if (!$email) {
1229
- $controls->errors = __('Newsletter should be saved before send a test', 'newsletter');
1230
- return;
1231
- }
1232
-
1233
- $original_subject = $email->subject;
1234
- $this->set_test_subject_to($email);
1235
-
1236
- $users = NewsletterUsers::instance()->get_test_users();
1237
- if (count($users) == 0) {
1238
- $controls->errors = '' . __('There are no test subscribers to send to', 'newsletter') .
1239
- '. <a href="https://www.thenewsletterplugin.com/plugins/newsletter/subscribers-module#test" target="_blank"><strong>' .
1240
- __('Read more', 'newsletter') . '</strong></a>.';
1241
- } else {
1242
- $r = Newsletter::instance()->send($email, $users, true);
1243
- $emails = array();
1244
- foreach ($users as $user) {
1245
- $emails[] = '<a href="admin.php?page=newsletter_users_edit&id=' . $user->id . '" target="_blank">' . $user->email . '</a>';
1246
- }
1247
- if (is_wp_error($r)) {
1248
- $controls->errors = 'Something went wrong. Check the error logs on status page.<br>';
1249
- $controls->errors .= __('Test subscribers:', 'newsletter');
1250
- $controls->errors .= ' ' . implode(', ', $emails);
1251
- $controls->errors .= '<br>';
1252
- $controls->errors .= '<strong>' . esc_html($r->get_error_message()) . '</strong><br>';
1253
- $controls->errors .= '<a href="https://www.thenewsletterplugin.com/documentation/email-sending-issues" target="_blank"><strong>' . __('Read more about delivery issues', 'newsletter') . '</strong></a>.';
1254
- } else {
1255
- $controls->messages = __('Test subscribers:', 'newsletter');
1256
-
1257
- $controls->messages .= ' ' . implode(', ', $emails);
1258
- $controls->messages .= '.<br>';
1259
- $controls->messages .= '<a href="https://www.thenewsletterplugin.com/documentation/subscribers#test" target="_blank"><strong>' .
1260
- __('Read more about test subscribers', 'newsletter') . '</strong></a>.<br>';
1261
- $controls->messages .= '<a href="https://www.thenewsletterplugin.com/documentation/email-sending-issues" target="_blank"><strong>' . __('Read more about delivery issues', 'newsletter') . '</strong></a>.';
1262
- }
1263
- }
1264
- $email->subject = $original_subject;
1265
- }
1266
-
1267
- /**
1268
- * Send an email to the test subscribers.
1269
- *
1270
- * @param TNP_Email $email Could be any object with the TNP_Email attributes
1271
- * @param string $email_address
1272
- *
1273
- * @throws Exception
1274
- */
1275
- function send_test_newsletter_to_email_address( $email, $email_address ) {
1276
-
1277
- if ( ! $email ) {
1278
- throw new Exception( __( 'Newsletter should be saved before send a test', 'newsletter' ) );
1279
- }
1280
-
1281
- $this->set_test_subject_to( $email );
1282
-
1283
- $dummy_subscriber = $this->make_dummy_subscriber();
1284
- $dummy_subscriber->email = $email_address;
1285
-
1286
- $result = Newsletter::instance()->send( $email, [ $dummy_subscriber ], true );
1287
-
1288
- $email = '<a href="admin.php?page=newsletter_users_edit&id=' . $dummy_subscriber->id . '" target="_blank">' . $dummy_subscriber->email . '</a>';
1289
-
1290
- if ( is_wp_error( $result ) ) {
1291
- $error_message = 'Something went wrong. Check the error logs on status page.<br>';
1292
- $error_message .= __( 'Test subscribers:', 'newsletter' );
1293
- $error_message .= ' ' . $email;
1294
- $error_message .= '<br>';
1295
- $error_message .= '<strong>' . esc_html( $result->get_error_message() ) . '</strong><br>';
1296
- $error_message .= '<a href="https://www.thenewsletterplugin.com/documentation/email-sending-issues" target="_blank"><strong>' . __( 'Read more about delivery issues', 'newsletter' ) . '</strong></a>.';
1297
- throw new Exception( $error_message );
1298
- }
1299
-
1300
- $messages = __( 'Test subscribers:', 'newsletter' );
1301
-
1302
- $messages .= ' ' . $email;
1303
- $messages .= '.<br>';
1304
- $messages .= '<a href="https://www.thenewsletterplugin.com/documentation/subscribers#test" target="_blank"><strong>' .
1305
- __( 'Read more about test subscribers', 'newsletter' ) . '</strong></a>.<br>';
1306
- $messages .= '<a href="https://www.thenewsletterplugin.com/documentation/email-sending-issues" target="_blank"><strong>' . __( 'Read more about delivery issues', 'newsletter' ) . '</strong></a>.';
1307
-
1308
- return $messages;
1309
- }
1310
-
1311
- private function set_test_subject_to($email) {
1312
- if ( $email->subject == '' ) {
1313
- $email->subject = '[TEST] Dummy subject, it was empty (remember to set it)';
1314
- } else {
1315
- $email->subject = $email->subject . ' (TEST)';
1316
- }
1317
- }
1318
-
1319
- private function make_dummy_subscriber() {
1320
- $dummy_user = new TNP_User();
1321
- $dummy_user->id = 0;
1322
- $dummy_user->email = 'john.doe@example.org';
1323
- $dummy_user->name = 'John';
1324
- $dummy_user->surname = 'Doe';
1325
- $dummy_user->sex = 'n';
1326
- $dummy_user->language = '';
1327
- $dummy_user->ip = '';
1328
-
1329
- for ( $i = 1; $i <= NEWSLETTER_PROFILE_MAX; $i ++ ) {
1330
- $profile_key = "profile_$i";
1331
- $dummy_user->$profile_key = '';
1332
- }
1333
-
1334
- return $dummy_user;
1335
- }
1336
-
1337
- function restore_options_from_request() {
1338
-
1339
- require_once NEWSLETTER_INCLUDES_DIR . '/controls.php';
1340
- $controls = new NewsletterControls();
1341
- $options = $controls->data;
1342
-
1343
- if (isset($_POST['options']) && is_array($_POST['options'])) {
1344
- // Get all block options
1345
- //$options = stripslashes_deep($_POST['options']);
1346
-
1347
- // Deserialize inline edits when
1348
- // render is preformed on saving block options
1349
- if (isset($options['inline_edits']) && !is_array($options['inline_edits'])) {
1350
- $options['inline_edits'] = $this->options_decode($options['inline_edits']);
1351
- }
1352
-
1353
- // Restore inline edits from data-json
1354
- // coming from inline editing
1355
- // and merge with current inline edit
1356
- if (isset($_POST['encoded_options'])) {
1357
- $decoded_options = $this->options_decode($_POST['encoded_options']);
1358
-
1359
- $to_merge_inline_edits = [];
1360
-
1361
- if (isset($decoded_options['inline_edits'])) {
1362
- foreach ($decoded_options['inline_edits'] as $decoded_inline_edit) {
1363
- $to_merge_inline_edits[$decoded_inline_edit['post_id'] . $decoded_inline_edit['type']] = $decoded_inline_edit;
1364
- }
1365
- }
1366
-
1367
- //Overwrite with new edited content
1368
- if (isset($options['inline_edits'])) {
1369
- foreach ($options['inline_edits'] as $inline_edit) {
1370
- $to_merge_inline_edits[$inline_edit['post_id'] . $inline_edit['type']] = $inline_edit;
1371
- }
1372
- }
1373
-
1374
- $options['inline_edits'] = array_values($to_merge_inline_edits);
1375
- $options = array_merge($decoded_options, $options);
1376
- }
1377
-
1378
- return $options;
1379
- }
1380
-
1381
- return array();
1382
- }
1383
-
1384
- public function hook_wp_ajax_tnpc_delete_preset() {
1385
-
1386
- if (!wp_verify_nonce($_POST['_wpnonce'], 'preset')) {
1387
- wp_send_json_error('Expired request');
1388
- }
1389
-
1390
- $preset_id = (int) $_REQUEST['presetId'];
1391
-
1392
- $newsletter = Newsletter::instance();
1393
-
1394
- if ($preset_id > 0) {
1395
- $preset = $newsletter->get_email($preset_id);
1396
-
1397
- if ($preset && $preset->type === self::PRESET_EMAIL_TYPE) {
1398
- Newsletter::instance()->delete_email($preset_id);
1399
- wp_send_json_success();
1400
- } else {
1401
- wp_send_json_error(__('Is not a preset!', 'newsletter'));
1402
- }
1403
- } else {
1404
- wp_send_json_error();
1405
- }
1406
- }
1407
-
1408
- }
1409
-
1410
- NewsletterEmails::instance();
1
+ <?php
2
+
3
+ defined('ABSPATH') || exit;
4
+
5
+ class NewsletterEmails extends NewsletterModule {
6
+
7
+ static $instance;
8
+
9
+ const EDITOR_COMPOSER = 2;
10
+ const EDITOR_HTML = 1;
11
+ const EDITOR_TINYMCE = 0;
12
+
13
+ static $PRESETS_LIST;
14
+
15
+ const PRESET_EMAIL_TYPE = 'composer_template';
16
+
17
+ // Cache
18
+ var $blocks = null;
19
+
20
+ /**
21
+ * @return NewsletterEmails
22
+ */
23
+ static function instance() {
24
+ if (self::$instance == null) {
25
+ self::$instance = new NewsletterEmails();
26
+ }
27
+
28
+ return self::$instance;
29
+ }
30
+
31
+ function __construct() {
32
+ self::$PRESETS_LIST = array("cta", "invite", "announcement", "posts", "sales", "product", "tour", "simple");
33
+ $this->themes = new NewsletterThemes('emails');
34
+ parent::__construct('emails', '1.1.5');
35
+ add_action('newsletter_action', array($this, 'hook_newsletter_action'), 13, 3);
36
+
37
+ if (is_admin()) {
38
+ if (defined('DOING_AJAX') && DOING_AJAX) {
39
+ add_action('wp_ajax_tnpc_render', array($this, 'tnpc_render_callback'));
40
+ add_action('wp_ajax_tnpc_preview', array($this, 'tnpc_preview_callback'));
41
+ add_action('wp_ajax_tnpc_css', array($this, 'tnpc_css_callback'));
42
+ add_action('wp_ajax_tnpc_options', array($this, 'hook_wp_ajax_tnpc_options'));
43
+ add_action('wp_ajax_tnpc_get_all_presets', array($this, 'ajax_get_all_presets'));
44
+ add_action('wp_ajax_tnpc_get_preset', array($this, 'ajax_get_preset'));
45
+ add_action('wp_ajax_tnpc_delete_preset', array($this, 'hook_wp_ajax_tnpc_delete_preset'));
46
+ add_action('wp_ajax_tnpc_regenerate_email', array($this, 'hook_wp_ajax_tnpc_regenerate_email'));
47
+ }
48
+ // Thank you to plugins which add the WP editor on other admin plugin pages...
49
+ if (isset($_GET['page']) && $_GET['page'] == 'newsletter_emails_edit') {
50
+ global $wp_actions;
51
+ $wp_actions['wp_enqueue_editor'] = 1;
52
+ }
53
+ }
54
+ }
55
+
56
+ function options_decode($options) {
57
+
58
+ // Start compatibility
59
+ if (is_string($options) && strpos($options, 'options[') !== false) {
60
+ $opts = array();
61
+ parse_str($options, $opts);
62
+ $options = $opts['options'];
63
+ }
64
+ // End compatibility
65
+
66
+ if (is_array($options)) {
67
+ return $options;
68
+ }
69
+
70
+ $tmp = json_decode($options, true);
71
+ if (is_null($tmp)) {
72
+ return json_decode(base64_decode($options), true);
73
+ } else {
74
+ return $tmp;
75
+ }
76
+ }
77
+
78
+ /**
79
+ *
80
+ * @param array $options Options array
81
+ */
82
+ function options_encode($options) {
83
+ return base64_encode(json_encode($options, JSON_HEX_TAG | JSON_HEX_AMP));
84
+ }
85
+
86
+ function hook_wp_ajax_tnpc_options() {
87
+ global $wpdb;
88
+
89
+ // TODO: Uniform to use id everywhere
90
+ // if (!isset($_REQUEST['id'])) {
91
+ // $_REQUEST['id'] = $_REQUEST['b'];
92
+ // }
93
+
94
+ $block = $this->get_block($_REQUEST['id']);
95
+ if (!$block) {
96
+ die('Block not found with id ' . esc_html($_REQUEST['id']));
97
+ }
98
+
99
+ if (!class_exists('NewsletterControls')) {
100
+ include NEWSLETTER_INCLUDES_DIR . '/controls.php';
101
+ }
102
+
103
+ $options = $this->options_decode(stripslashes_deep($_REQUEST['options']));
104
+ $composer = isset($_POST['composer']) ? $_POST['composer'] : [];
105
+
106
+ $context = array('type' => '');
107
+ if (isset($_REQUEST['context_type'])) {
108
+ $context['type'] = $_REQUEST['context_type'];
109
+ }
110
+
111
+ $controls = new NewsletterControls($options);
112
+ $fields = new NewsletterFields($controls);
113
+
114
+ $controls->init();
115
+ echo '<input type="hidden" name="action" value="tnpc_render">';
116
+ echo '<input type="hidden" name="id" value="' . esc_attr($_REQUEST['id']) . '">';
117
+ echo '<input type="hidden" name="context_type" value="' . esc_attr($context['type']) . '">';
118
+ $inline_edits = '';
119
+ if (isset($controls->data['inline_edits'])) {
120
+ $inline_edits = $controls->data['inline_edits'];
121
+ }
122
+ echo '<input type="hidden" name="options[inline_edits]" value="', esc_attr($this->options_encode($inline_edits)), '">';
123
+
124
+ ob_start();
125
+ include $block['dir'] . '/options.php';
126
+ $content = ob_get_clean();
127
+ echo "<h2>", esc_html($block["name"]), "</h2>";
128
+ echo $content;
129
+ wp_die();
130
+ }
131
+
132
+ /**
133
+ * Retrieves the presets list (no id in GET) or a specific preset id in GET)
134
+ */
135
+ public function ajax_get_all_presets() {
136
+ wp_send_json_success($this->get_all_preset());
137
+ }
138
+
139
+ public function ajax_get_preset() {
140
+
141
+ if (empty($_REQUEST['id'])) {
142
+ wp_send_json_error([
143
+ 'msg' => __('Invalid preset ID')
144
+ ]);
145
+ }
146
+
147
+ $preset_id = $_REQUEST['id'];
148
+ $preset_content = $this->get_preset_content($preset_id);
149
+ $global_options = $this->get_preset_global_options($preset_id);
150
+
151
+ wp_send_json_success([
152
+ 'content' => $preset_content,
153
+ 'globalOptions' => $global_options,
154
+ ]);
155
+ }
156
+
157
+ private function get_preset_content($preset_id) {
158
+
159
+ $content = '';
160
+
161
+ if ($this->is_a_tnp_default_preset($preset_id)) {
162
+
163
+ // Get preset from file
164
+ $preset = $this->get_preset_from_file($preset_id);
165
+
166
+ foreach ($preset->blocks as $item) {
167
+ ob_start();
168
+ $this->render_block($item->block, true, (array) $item->options);
169
+ $content .= trim(ob_get_clean());
170
+ }
171
+ } else {
172
+
173
+ // Get preset from db
174
+ $preset_email = $this->get_email(intval($preset_id));
175
+ $global_options = $this->extract_global_options_from($preset_email);
176
+ $content = $this->regenerate_email_blocks($preset_email->message, $global_options);
177
+ }
178
+
179
+ return $content;
180
+ }
181
+
182
+ private function get_preset_global_options($preset_id) {
183
+
184
+ if ($this->is_a_tnp_default_preset($preset_id)) {
185
+ return [];
186
+ }
187
+
188
+ // Get preset from db
189
+ $preset_email = $this->get_email(intval($preset_id));
190
+ $global_options = $this->extract_global_options_from($preset_email);
191
+
192
+ return $global_options;
193
+ }
194
+
195
+ private function extract_global_options_from($email) {
196
+ $global_options = [];
197
+ foreach ($email->options as $global_option_name => $global_option) {
198
+ if (strpos($global_option_name, 'composer_') === 0) {
199
+ $global_options[str_replace('composer_', '', $global_option_name)] = $global_option;
200
+ }
201
+ }
202
+
203
+ return $global_options;
204
+ }
205
+
206
+ private function is_a_tnp_default_preset($preset_id) {
207
+ return in_array($preset_id, self::$PRESETS_LIST);
208
+ }
209
+
210
+ private function get_all_preset() {
211
+
212
+ $content = "<div class='tnpc-preset-container'>";
213
+
214
+ if ($this->is_normal_context_request()) {
215
+ $content .= "<div class='tnpc-preset-legacy-themes'><a href='" . $this->get_admin_page_url('theme') . "'>" . __('Looking for legacy themes?', 'newsletter') . "</a></div>";
216
+ }
217
+
218
+ // LOAD USER PRESETS
219
+ $user_preset_list = $this->get_emails(self::PRESET_EMAIL_TYPE);
220
+
221
+ foreach ($user_preset_list as $user_preset) {
222
+
223
+ $default_icon_url = NEWSLETTER_URL . "/emails/presets/default-icon.png?ver=2";
224
+ $preset_name = $user_preset->subject;
225
+ $delete_preset_text = __('Delete', 'newsletter');
226
+ $edit_preset_text = __('Edit', 'newsletter');
227
+
228
+ // esc_js() assumes the string will be in single quote (arghhh!!!)
229
+ $onclick_edit = 'tnpc_edit_preset(' . ((int) $user_preset->id) . ', \'' . esc_js($preset_name) . '\', event)';
230
+ $onclick_delete = 'tnpc_delete_preset(' . ((int) $user_preset->id) . ', \'' . esc_js($preset_name) . '\', event)';
231
+ $onclick_load = 'tnpc_load_preset(' . ((int) $user_preset->id) . ', \'' . esc_js($preset_name) . '\', event)';
232
+
233
+ $content .= "<div class='tnpc-preset' onclick='" . esc_attr($onclick_load) . "'>\n";
234
+ $content .= "<img src='$default_icon_url' title='" . esc_attr($preset_name) . "' alt='" . esc_attr($preset_name) . "'>\n";
235
+ $content .= "<span class='tnpc-preset-label'>" . esc_html($user_preset->subject) . "</span>\n";
236
+ $content .= "<span class='tnpc-delete-preset' onclick='" . esc_attr($onclick_delete) . "' title='" . esc_attr($delete_preset_text) . "'><i class='fas fa-times'></i></span>\n";
237
+ $content .= "<span class='tnpc-edit-preset' onclick='" . esc_attr($onclick_edit) . "' title='" . esc_attr($edit_preset_text) . "'><i class='fas fa-pencil-alt'></i></span>\n";
238
+ $content .= "</div>";
239
+ }
240
+
241
+ // LOAD TNP PRESETS
242
+ foreach (self::$PRESETS_LIST as $id) {
243
+ $preset = $this->get_preset_from_file($id);
244
+ $preset_name = esc_html($preset->name);
245
+ $content .= "<div class='tnpc-preset' onclick='tnpc_load_preset(\"$id\")'>";
246
+ $content .= "<img src='$preset->icon' title='$preset_name' alt='$preset_name'/>";
247
+ $content .= "<span class='tnpc-preset-label'>$preset_name</span>";
248
+ $content .= "</div>";
249
+ }
250
+
251
+ if ($this->is_normal_context_request()) {
252
+ $content .= $this->get_automated_spot_element();
253
+ $content .= $this->get_autoresponder_spot_element();
254
+ $content .= $this->get_raw_html_preset_element();
255
+ }
256
+
257
+ return $content;
258
+ }
259
+
260
+ private function is_normal_context_request() {
261
+ return empty($_REQUEST['context_type']);
262
+ }
263
+
264
+ private function is_automated_context_request() {
265
+ return isset($_REQUEST['context_type']) && $_REQUEST['context_type'] === 'automated';
266
+ }
267
+
268
+ private function is_autoresponder_context_request() {
269
+ return isset($_REQUEST['context_type']) && $_REQUEST['context_type'] === 'autoresponder';
270
+ }
271
+
272
+ private function get_automated_spot_element() {
273
+ $result = "<div class='tnpc-preset'>";
274
+ if (class_exists('NewsletterAutomated')) {
275
+ $result .= "<a href='?page=newsletter_automated_index'>";
276
+ } else {
277
+ $result .= "<a href='https://www.thenewsletterplugin.com/automated?utm_source=composer&utm_campaign=plugin&utm_medium=automated'>";
278
+ }
279
+ $result .= "<img src='" . plugins_url('newsletter') . "/emails/images/automated.png' title='Automated addon' alt='Automated'/>";
280
+ $result .= "<span class='tnpc-preset-label'>Daily, weekly and monthly newsletters</span></a>";
281
+ $result .= "</div>";
282
+
283
+ return $result;
284
+ }
285
+
286
+ private function get_autoresponder_spot_element() {
287
+ $result = "<div class='tnpc-preset'>";
288
+ if (class_exists('NewsletterAutoresponder')) {
289
+ $result .= "<a href='?page=newsletter_autoresponder_index'>";
290
+ } else {
291
+ $result .= "<a href='https://www.thenewsletterplugin.com/autoresponder?utm_source=composer&utm_campaign=plugin&utm_medium=autoresponder' target='_blank'>";
292
+ }
293
+ $result .= "<img src='" . plugins_url('newsletter') . "/emails/images/autoresponder.png' title='Autoresponder addon' alt='Autoresponder'/>";
294
+ $result .= "<span class='tnpc-preset-label'>Autoresponders</span></a>";
295
+ $result .= "</div>";
296
+
297
+ return $result;
298
+ }
299
+
300
+ private function get_raw_html_preset_element() {
301
+
302
+ $result = "<div class='tnpc-preset tnpc-preset-html' onclick='location.href=\"" . wp_nonce_url('admin.php?page=newsletter_emails_new&id=rawhtml', 'newsletter-new') . "\"'>";
303
+ $result .= "<img src='" . plugins_url('newsletter') . "/emails/images/rawhtml.png' title='RAW HTML' alt='RAW'/>";
304
+ $result .= "<span class='tnpc-preset-label'>Raw HTML</span>";
305
+ $result .= "</div>";
306
+
307
+ $result .= "<div class='clear'></div>";
308
+ $result .= "</div>";
309
+
310
+ return $result;
311
+ }
312
+
313
+ /**
314
+ * Check if the preset name exists and adds an incremental suffix if the name exists.
315
+ *
316
+ * @param string $name
317
+ *
318
+ * @return string
319
+ */
320
+ public function sanitize_preset_name($name) {
321
+ global $wpdb;
322
+
323
+ $name = empty($name) ? __('Empty name preset', 'newsletter') : $name;
324
+ $name = sanitize_text_field($name);
325
+ $type = self::PRESET_EMAIL_TYPE;
326
+ $count = (int) $wpdb->get_var("SELECT COUNT(*) FROM " . NEWSLETTER_EMAILS_TABLE . " WHERE type='$type' and subject='$name'");
327
+
328
+ $name = $count > 0 ? $name . " - " . ($count + 1) : $name;
329
+
330
+ return $name;
331
+ }
332
+
333
+ function has_dynamic_blocks($theme) {
334
+ preg_match_all('/data-json="(.*?)"/m', $theme, $matches, PREG_PATTERN_ORDER);
335
+ foreach ($matches[1] as $match) {
336
+ $a = html_entity_decode($match, ENT_QUOTES, 'UTF-8');
337
+ $options = $this->options_decode($a);
338
+
339
+ $block = $this->get_block($options['block_id']);
340
+ if (!$block) {
341
+ continue;
342
+ }
343
+ if ($block['type'] == 'dynamic') {
344
+ return true;
345
+ }
346
+ }
347
+ return false;
348
+ }
349
+
350
+ /**
351
+ * Regenerates a saved composed email rendering each block. Regeneration is
352
+ * conditioned (possibly) by the context. The context is usually passed to blocks
353
+ * so they can act in the right manner.
354
+ *
355
+ * $context contains a type and, for automated, the last_run.
356
+ *
357
+ * $email can actually be even a string containing the full newsletter HTML code.
358
+ *
359
+ * @param TNP_Email $email (Rinominare)
360
+ * @return string
361
+ */
362
+ function regenerate($email, $context = []) {
363
+
364
+ // Cannot be removed due to compatibility issues with old Automated versions
365
+ if (is_object($email)) {
366
+ $theme = $email->message;
367
+ } else {
368
+ $theme = $email;
369
+ }
370
+
371
+ //$this->logger->debug('Starting email regeneration');
372
+ //$this->logger->debug($context);
373
+
374
+ if (empty($theme)) {
375
+ $this->logger->debug('The email was empty');
376
+ return array('body' => '', 'subject' => '');
377
+ }
378
+
379
+ $context = array_merge(['last_run' => 0, 'type' => ''], $context);
380
+
381
+ preg_match_all('/data-json="(.*?)"/m', $theme, $matches, PREG_PATTERN_ORDER);
382
+
383
+ $result = '';
384
+ $subject = '';
385
+
386
+ foreach ($matches[1] as $match) {
387
+ $a = html_entity_decode($match, ENT_QUOTES, 'UTF-8');
388
+ $options = $this->options_decode($a);
389
+
390
+ $block = $this->get_block($options['block_id']);
391
+ if (!$block) {
392
+ $this->logger->debug('Unable to load the block ' . $options['block_id']);
393
+ //continue;
394
+ }
395
+
396
+ ob_start();
397
+ $out = $this->render_block($options['block_id'], true, $options, $context);
398
+ if (is_array($out)) {
399
+ if ($out['return_empty_message'] || $out['stop']) {
400
+ if (is_object($email)) {
401
+ return false;
402
+ }
403
+ return array();
404
+ }
405
+ if ($out['skip']) {
406
+ if (NEWSLETTER_DEBUG) {
407
+ $result .= 'Block removed by request';
408
+ }
409
+ continue;
410
+ }
411
+ if (empty($subject) && !empty($out['subject'])) {
412
+ $subject = $out['subject'];
413
+ }
414
+ }
415
+ $block_html = ob_get_clean();
416
+ $result .= $block_html;
417
+ }
418
+
419
+ // We need to keep the CSS/HEAD part, the regenearion is only about blocks
420
+
421
+ if (is_object($email)) {
422
+ $result = TNP_Composer::get_main_wrapper_open($email) . $result . TNP_Composer::get_main_wrapper_close($email);
423
+ }
424
+
425
+ $x = strpos($theme, '<body');
426
+ if ($x !== false) {
427
+ $x = strpos($theme, '>', $x);
428
+ $result = substr($theme, 0, $x + 1) . $result . '</body></html>';
429
+ } else {
430
+
431
+ }
432
+
433
+ if (is_object($email)) {
434
+ $email->message = $result;
435
+ $email->subject = $subject;
436
+ return true;
437
+ }
438
+
439
+ // Kept for compatibility
440
+ return array('body' => $result, 'subject' => $subject);
441
+ }
442
+
443
+ function remove_block_data($text) {
444
+ // TODO: Lavorare!
445
+ return $text;
446
+ }
447
+
448
+ static function get_outlook_wrapper_open($width = 600) {
449
+ return '<!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" align="center" cellspacing="0" width="' . $width . '"><tr><td width="' . $width . '" style="vertical-align:top;width:' . $width . 'px;"><![endif]-->';
450
+ }
451
+
452
+ static function get_outlook_wrapper_close() {
453
+ return "<!--[if mso | IE]></td></tr></table><![endif]-->";
454
+ }
455
+
456
+ function hook_safe_style_css($rules) {
457
+ $rules[] = 'display';
458
+ return $rules;
459
+ }
460
+
461
+ /**
462
+ * Renders a block identified by its id, using the block options and adding a wrapper
463
+ * if required (for the first block rendering).
464
+ *
465
+ * @param string $block_id
466
+ * @param boolean $wrapper
467
+ * @param array $options
468
+ * @param array $context
469
+ * @param array $composer
470
+ */
471
+ function render_block($block_id = null, $wrapper = false, $options = [], $context = [], $composer = []) {
472
+ static $kses_style_filter = false;
473
+ include_once NEWSLETTER_INCLUDES_DIR . '/helper.php';
474
+
475
+ //Remove 'options_composer_' prefix
476
+ $composer_defaults = [];
477
+ foreach (TNP_Composer::get_global_style_defaults() as $global_option_name => $global_option) {
478
+ $composer_defaults[str_replace('options_composer_', '', $global_option_name)] = $global_option;
479
+ }
480
+ $composer = array_merge($composer_defaults, $composer);
481
+
482
+ $width = 600;
483
+ $font_family = 'Helvetica, Arial, sans-serif';
484
+
485
+ $global_title_font_family = $composer['title_font_family'];
486
+ $global_title_font_size = $composer['title_font_size'];
487
+ $global_title_font_color = $composer['title_font_color'];
488
+ $global_title_font_weight = $composer['title_font_weight'];
489
+
490
+ $global_text_font_family = $composer['text_font_family'];
491
+ $global_text_font_size = $composer['text_font_size'];
492
+ $global_text_font_color = $composer['text_font_color'];
493
+ $global_text_font_weight = $composer['text_font_weight'];
494
+
495
+ $global_button_font_family = $composer['button_font_family'];
496
+ $global_button_font_size = $composer['button_font_size'];
497
+ $global_button_font_color = $composer['button_font_color'];
498
+ $global_button_font_weight = $composer['button_font_weight'];
499
+ $global_button_background_color = $composer['button_background_color'];
500
+
501
+ $global_block_background = $composer['block_background'];
502
+
503
+ $info = Newsletter::instance()->get_options('info');
504
+
505
+ // Just in case...
506
+ if (!is_array($options)) {
507
+ $options = array();
508
+ }
509
+
510
+ // This code filters the HTML to remove javascript and unsecure attributes and enable the
511
+ // "display" rule for CSS which is needed in blocks to force specific "block" or "inline" or "table".
512
+ add_filter('safe_style_css', [$this, 'hook_safe_style_css'], 9999);
513
+ $options = wp_kses_post_deep($options);
514
+ remove_filter('safe_style_css', [$this, 'hook_safe_style_css']);
515
+
516
+ $block_options = get_option('newsletter_main');
517
+
518
+ $block = $this->get_block($block_id);
519
+
520
+ if (!isset($context['type']))
521
+ $context['type'] = '';
522
+
523
+ // Block not found
524
+ if (!$block) {
525
+ if ($wrapper) {
526
+ echo '<table border="0" cellpadding="0" cellspacing="0" align="center" width="100%" style="border-collapse: collapse; width: 100%;" class="tnpc-row tnpc-row-block" data-id="', esc_attr($block_id), '">';
527
+ echo '<tr>';
528
+ echo '<td data-options="" bgcolor="#ffffff" align="center" style="padding: 0; font-family: Helvetica, Arial, sans-serif;" class="edit-block">';
529
+ }
530
+ echo $this->get_outlook_wrapper_open($width);
531
+
532
+ echo '<p>Ops, this block type is no more registered!</p>';
533
+
534
+ echo $this->get_outlook_wrapper_close();
535
+
536
+ if ($wrapper) {
537
+ echo '</td></tr></table>';
538
+ }
539
+ return;
540
+ }
541
+
542
+ $out = ['subject' => '', 'return_empty_message' => false, 'stop' => false, 'skip' => false];
543
+
544
+ $dir = is_rtl() ? 'rtl' : 'ltr';
545
+ $align_left = is_rtl() ? 'right' : 'left';
546
+ $align_right = is_rtl() ? 'left' : 'right';
547
+
548
+ ob_start();
549
+ $logger = $this->logger;
550
+ include $block['dir'] . '/block.php';
551
+ $content = trim(ob_get_clean());
552
+
553
+ if (empty($content)) {
554
+ return $out;
555
+ }
556
+
557
+ $common_defaults = array(
558
+ 'block_padding_top' => 0,
559
+ 'block_padding_bottom' => 0,
560
+ 'block_padding_right' => 0,
561
+ 'block_padding_left' => 0,
562
+ 'block_background' => '',
563
+ 'block_background_2' => '',
564
+ 'block_width' => 600,
565
+ 'block_align' => 'center'
566
+ );
567
+
568
+ $options = array_merge($common_defaults, $options);
569
+
570
+ // Obsolete
571
+ $content = str_replace('{width}', $width, $content);
572
+
573
+ $content = $this->inline_css($content, true);
574
+
575
+ // CSS driven by the block
576
+ // Requited for the server side parsing and rendering
577
+ $options['block_id'] = $block_id;
578
+
579
+ $options['block_padding_top'] = (int) str_replace('px', '', $options['block_padding_top']);
580
+ $options['block_padding_bottom'] = (int) str_replace('px', '', $options['block_padding_bottom']);
581
+ $options['block_padding_right'] = (int) str_replace('px', '', $options['block_padding_right']);
582
+ $options['block_padding_left'] = (int) str_replace('px', '', $options['block_padding_left']);
583
+
584
+ $block_background = empty($options['block_background']) ? $global_block_background : $options['block_background'];
585
+
586
+ // Internal TD wrapper
587
+ $style = 'text-align: center; ';
588
+ $style .= 'width: 100% !important; ';
589
+ $style .= 'line-height: normal !important; ';
590
+ $style .= 'letter-spacing: normal; ';
591
+ $style .= 'padding-top: ' . $options['block_padding_top'] . 'px; ';
592
+ $style .= 'padding-left: ' . $options['block_padding_left'] . 'px; ';
593
+ $style .= 'padding-right: ' . $options['block_padding_right'] . 'px; ';
594
+ $style .= 'padding-bottom: ' . $options['block_padding_bottom'] . 'px; ';
595
+ $style .= 'background-color: ' . $block_background . ';';
596
+
597
+ if (isset($options['block_background_gradient'])) {
598
+ $style .= 'background: linear-gradient(180deg, ' . $block_background . ' 0%, ' . $options['block_background_2'] . ' 100%);';
599
+ }
600
+
601
+ $data = $this->options_encode($options);
602
+ // First time block creation wrapper
603
+ if ($wrapper) {
604
+ echo '<table border="0" cellpadding="0" cellspacing="0" align="center" width="100%" style="border-collapse: collapse; width: 100%;" class="tnpc-row tnpc-row-block" data-id="', esc_attr($block_id), '">', "\n";
605
+ echo "<tr>";
606
+ echo '<td align="center" style="padding: 0;" class="edit-block">', "\n";
607
+ }
608
+
609
+ // Container that fixes the width and makes the block responsive
610
+ echo $this->get_outlook_wrapper_open($options['block_width']);
611
+
612
+ echo '<table type="options" data-json="', esc_attr($data), '" class="tnpc-block-content" border="0" cellpadding="0" align="center" cellspacing="0" width="100%" style="width: 100%!important; max-width: ', $options['block_width'], 'px!important">', "\n";
613
+ echo "<tr>";
614
+ echo '<td align="', esc_attr($options['block_align']), '" style="', $style, '" bgcolor="', $block_background, '" width="100%">';
615
+
616
+ //echo "<!-- block generated content -->\n";
617
+ echo trim($content);
618
+ //echo "\n<!-- /block generated content -->\n";
619
+
620
+ echo "</td></tr></table>";
621
+ echo $this->get_outlook_wrapper_close();
622
+
623
+ // First time block creation wrapper
624
+ if ($wrapper) {
625
+ echo "</td></tr></table>";
626
+ }
627
+
628
+ return $out;
629
+ }
630
+
631
+ /**
632
+ * Ajax call to render a block with a new set of options after the settings popup
633
+ * has been saved.
634
+ *
635
+ * @param type $block_id
636
+ * @param type $wrapper
637
+ */
638
+ function tnpc_render_callback() {
639
+ if (!check_ajax_referer('save')) {
640
+ $this->dienow('Expired request');
641
+ }
642
+
643
+ $block_id = $_POST['id'];
644
+ $wrapper = isset($_POST['full']);
645
+ $options = $this->restore_options_from_request();
646
+
647
+ $this->render_block($block_id, $wrapper, $options, [], $_POST['composer']);
648
+ wp_die();
649
+ }
650
+
651
+ function hook_wp_ajax_tnpc_regenerate_email() {
652
+
653
+ $content = stripslashes($_POST['content']);
654
+ $global_options = $_POST['composer'];
655
+
656
+ $regenerated_content = $this->regenerate_email_blocks($content, $global_options);
657
+
658
+ wp_send_json_success([
659
+ 'content' => $regenerated_content,
660
+ 'message' => __('Successfully updated', 'newsletter')
661
+ ]);
662
+ }
663
+
664
+ private function regenerate_email_blocks($content, $global_options) {
665
+
666
+ $raw_block_options = $this->extract_encoded_blocks_options($content);
667
+
668
+ $regenerated_content = '';
669
+
670
+ foreach ($raw_block_options as $raw_block_option) {
671
+
672
+ /* $a = html_entity_decode( $raw_block_option, ENT_QUOTES, 'UTF-8' );
673
+ $block_options = $this->options_decode( $a ); */
674
+
675
+ $block_options = $this->options_decode($raw_block_option);
676
+
677
+ $block = $this->get_block($block_options['block_id']);
678
+ if (!$block) {
679
+ $this->logger->debug('Unable to load the block ' . $block_options['block_id']);
680
+ }
681
+
682
+ ob_start();
683
+ $this->render_block($block_options['block_id'], true, $block_options, [], $global_options);
684
+ $block_html = ob_get_clean();
685
+
686
+ $regenerated_content .= $block_html;
687
+ }
688
+
689
+ return $regenerated_content;
690
+ }
691
+
692
+ /**
693
+ * @param string $html_email_content Email html content
694
+ *
695
+ * @return string[] Encoded options of email blocks
696
+ */
697
+ private function extract_encoded_blocks_options($html_email_content) {
698
+
699
+ preg_match_all('/data-json="(.*?)"/m', $html_email_content, $raw_block_options, PREG_PATTERN_ORDER);
700
+
701
+ return $raw_block_options[1];
702
+ }
703
+
704
+ function tnpc_preview_callback() {
705
+ $email = Newsletter::instance()->get_email($_REQUEST['id'], ARRAY_A);
706
+
707
+ if (empty($email)) {
708
+ echo 'Wrong email identifier';
709
+ return;
710
+ }
711
+
712
+ echo $email['message'];
713
+
714
+ wp_die();
715
+ }
716
+
717
+ function tnpc_css_callback() {
718
+ include NEWSLETTER_DIR . '/emails/tnp-composer/css/newsletter.css';
719
+ wp_die();
720
+ }
721
+
722
+ /** Returns the correct admin page to edit the newsletter with the correct editor. */
723
+ function get_editor_url($email_id, $editor_type) {
724
+ switch ($editor_type) {
725
+ case NewsletterEmails::EDITOR_COMPOSER:
726
+ return admin_url("admin.php") . '?page=newsletter_emails_composer&id=' . $email_id;
727
+ case NewsletterEmails::EDITOR_HTML:
728
+ return admin_url("admin.php") . '?page=newsletter_emails_editorhtml&id=' . $email_id;
729
+ case NewsletterEmails::EDITOR_TINYMCE:
730
+ return admin_url("admin.php") . '?page=newsletter_emails_editortinymce&id=' . $email_id;
731
+ }
732
+ }
733
+
734
+ /**
735
+ * Returns the button linked to the correct "edit" page for the passed newsletter. The edit page can be an editor
736
+ * or the targeting page (it depends on newsletter status).
737
+ *
738
+ * @param TNP_Email $email
739
+ */
740
+ function get_edit_button($email, $only_icon = false) {
741
+
742
+ $editor_type = $this->get_editor_type($email);
743
+ if ($email->status == 'new') {
744
+ $edit_url = $this->get_editor_url($email->id, $editor_type);
745
+ } else {
746
+ $edit_url = 'admin.php?page=newsletter_emails_edit&id=' . $email->id;
747
+ }
748
+ switch ($editor_type) {
749
+ case NewsletterEmails::EDITOR_COMPOSER:
750
+ $icon_class = 'th-large';
751
+ break;
752
+ case NewsletterEmails::EDITOR_HTML:
753
+ $icon_class = 'code';
754
+ break;
755
+ default:
756
+ $icon_class = 'edit';
757
+ break;
758
+ }
759
+ if ($only_icon) {
760
+ return '<a class="button-primary" href="' . $edit_url . '" title="' . esc_attr__('Edit', 'newsletter') . '">' .
761
+ '<i class="fas fa-' . $icon_class . '"></i></a>';
762
+ } else {
763
+ return '<a class="button-primary" href="' . $edit_url . '" title="' . esc_attr__('Edit', 'newsletter') . '">' .
764
+ '<i class="fas fa-' . $icon_class . '"></i> ' . __('Edit', 'newsletter') . '</a>';
765
+ }
766
+ }
767
+
768
+ /** Returns the correct editor type for the provided newsletter. Contains backward compatibility code. */
769
+ function get_editor_type($email) {
770
+ $email = (object) $email;
771
+ $editor_type = $email->editor;
772
+
773
+ // Backward compatibility
774
+ $email_options = maybe_unserialize($email->options);
775
+ if (isset($email_options['composer'])) {
776
+ $editor_type = NewsletterEmails::EDITOR_COMPOSER;
777
+ }
778
+ // End backward compatibility
779
+
780
+ return $editor_type;
781
+ }
782
+
783
+ /**
784
+ *
785
+ * @param type $action
786
+ * @param type $user
787
+ * @param type $email
788
+ * @return type
789
+ * @global wpdb $wpdb
790
+ */
791
+ function hook_newsletter_action($action, $user, $email) {
792
+ global $wpdb;
793
+
794
+ switch ($action) {
795
+ case 'v':
796
+ case 'view':
797
+ $id = $_GET['id'];
798
+ if ($id == 'last') {
799
+ $email = $wpdb->get_row("select * from " . NEWSLETTER_EMAILS_TABLE . " where private=0 and type='message' and status='sent' order by send_on desc limit 1");
800
+ } else {
801
+ $email = $this->get_email($_GET['id']);
802
+ }
803
+ if (empty($email)) {
804
+ header("HTTP/1.0 404 Not Found");
805
+ die('Email not found');
806
+ }
807
+
808
+ if (!Newsletter::instance()->is_allowed()) {
809
+
810
+ if ($email->status == 'new') {
811
+ header("HTTP/1.0 404 Not Found");
812
+ die('Not sent yet');
813
+ }
814
+
815
+ if ($email->private == 1) {
816
+ if (!$user) {
817
+ header("HTTP/1.0 404 Not Found");
818
+ die('No available for online view');
819
+ }
820
+ $sent = $wpdb->get_row($wpdb->prepare("select * from " . NEWSLETTER_SENT_TABLE . " where email_id=%d and user_id=%d limit 1", $email->id, $user->id));
821
+ if (!$sent) {
822
+ header("HTTP/1.0 404 Not Found");
823
+ die('No available for online view');
824
+ }
825
+ }
826
+ }
827
+
828
+
829
+ header('Content-Type: text/html;charset=UTF-8');
830
+ header('X-Robots-Tag: noindex,nofollow,noarchive');
831
+ header('Cache-Control: no-cache,no-store,private');
832
+
833
+ echo $this->replace($email->message, $user, $email);
834
+
835
+ die();
836
+ break;
837
+
838
+ case 'emails-css':
839
+ $email_id = (int) $_GET['id'];
840
+
841
+ $body = Newsletter::instance()->get_email_field($email_id, 'message');
842
+
843
+ $x = strpos($body, '<style');
844
+ if ($x === false)
845
+ return;
846
+
847
+ $x = strpos($body, '>', $x);
848
+ $y = strpos($body, '</style>');
849
+
850
+ header('Content-Type: text/css;charset=UTF-8');
851
+
852
+ echo substr($body, $x + 1, $y - $x - 1);
853
+
854
+ die();
855
+ break;
856
+
857
+ case 'emails-composer-css':
858
+ header('Cache: no-cache');
859
+ header('Content-Type: text/css');
860
+ echo $this->get_composer_css();
861
+ die();
862
+ break;
863
+
864
+ case 'emails-preview':
865
+ if (!Newsletter::instance()->is_allowed()) {
866
+ die('Not enough privileges');
867
+ }
868
+
869
+ if (!check_admin_referer('view')) {
870
+ die();
871
+ }
872
+
873
+ $theme_id = $_GET['id'];
874
+ $theme = $this->themes->get_theme($theme_id);
875
+
876
+ // Used by theme code
877
+ $theme_options = $this->themes->get_options($theme_id);
878
+
879
+ $theme_url = $theme['url'];
880
+
881
+ header('Content-Type: text/html;charset=UTF-8');
882
+
883
+ include $theme['dir'] . '/theme.php';
884
+
885
+ die();
886
+ break;
887
+
888
+ case 'emails-preview-text':
889
+ header('Content-Type: text/plain;charset=UTF-8');
890
+ if (!Newsletter::instance()->is_allowed()) {
891
+ die('Not enough privileges');
892
+ }
893
+
894
+ if (!check_admin_referer('view')) {
895
+ die();
896
+ }
897
+
898
+ // Used by theme code
899
+ $theme_options = $this->get_current_theme_options();
900
+
901
+ $file = include $theme['dir'] . '/theme-text.php';
902
+
903
+ if (is_file($file)) {
904
+ include $file;
905
+ }
906
+
907
+ die();
908
+ break;
909
+
910
+
911
+ case 'emails-create':
912
+ // Newsletter from themes are created on frontend context because sometime WP themes change the way the content,
913
+ // excerpt, thumbnail are extracted.
914
+ if (!Newsletter::instance()->is_allowed()) {
915
+ die('Not enough privileges');
916
+ }
917
+
918
+ require_once NEWSLETTER_INCLUDES_DIR . '/controls.php';
919
+ $controls = new NewsletterControls();
920
+
921
+ if (!$controls->is_action('create')) {
922
+ die('Wrong call');
923
+ }
924
+
925
+ $theme_id = $controls->data['id'];
926
+ $theme = $this->themes->get_theme($theme_id);
927
+
928
+ if (!$theme) {
929
+ die('invalid theme');
930
+ }
931
+
932
+ $this->themes->save_options($theme_id, $controls->data);
933
+
934
+ $email = array();
935
+ $email['status'] = 'new';
936
+ $email['subject'] = ''; //__('Here the email subject', 'newsletter');
937
+ $email['track'] = 1;
938
+ $email['send_on'] = time();
939
+ $email['editor'] = NewsletterEmails::EDITOR_TINYMCE;
940
+ $email['type'] = 'message';
941
+
942
+ $theme_options = $this->themes->get_options($theme_id);
943
+
944
+ $theme_url = $theme['url'];
945
+ $theme_subject = '';
946
+
947
+ ob_start();
948
+ include $theme['dir'] . '/theme.php';
949
+ $email['message'] = ob_get_clean();
950
+
951
+
952
+ if (!empty($theme_subject)) {
953
+ $email['subject'] = $theme_subject;
954
+ }
955
+
956
+ if (file_exists($theme['dir'] . '/theme-text.php')) {
957
+ ob_start();
958
+ include $theme['dir'] . '/theme-text.php';
959
+ $email['message_text'] = ob_get_clean();
960
+ } else {
961
+ $email['message_text'] = 'You need a modern email client to read this email. Read it online: {email_url}.';
962
+ }
963
+
964
+ $email = $this->save_email($email);
965
+
966
+ $edit_url = $this->get_editor_url($email->id, $email->editor);
967
+
968
+ header('Location: ' . $edit_url);
969
+
970
+ die();
971
+ break;
972
+ }
973
+ }
974
+
975
+ function admin_menu() {
976
+ $this->add_menu_page('index', 'Newsletters');
977
+ $this->add_admin_page('list', 'Email List');
978
+ $this->add_admin_page('new', 'Email New');
979
+ $this->add_admin_page('edit', 'Email Edit');
980
+ $this->add_admin_page('theme', 'Email Themes');
981
+ $this->add_admin_page('composer', 'The Composer');
982
+ $this->add_admin_page('editorhtml', 'HTML Editor');
983
+ $this->add_admin_page('editortinymce', 'TinyMCE Editor');
984
+ }
985
+
986
+ /**
987
+ * Builds a block data structure starting from the folder containing the block
988
+ * files.
989
+ *
990
+ * @param string $dir
991
+ * @return array | WP_Error
992
+ */
993
+ function build_block($dir) {
994
+ $dir = realpath($dir);
995
+ $dir = wp_normalize_path($dir);
996
+ $full_file = $dir . '/block.php';
997
+ if (!is_file($full_file)) {
998
+ return new WP_Error('1', 'Missing block.php file in ' . $dir);
999
+ }
1000
+
1001
+ $relative_dir = substr($dir, strlen(WP_CONTENT_DIR));
1002
+ $file = basename($dir);
1003
+
1004
+ $data = get_file_data($full_file, ['name' => 'Name', 'section' => 'Section', 'description' => 'Description', 'type' => 'Type']);
1005
+ $defaults = ['section' => 'content', 'name' => ucfirst($file), 'descritpion' => '', 'icon' => plugins_url('newsletter') . '/admin/images/block-icon.png'];
1006
+ $data = array_merge($defaults, $data);
1007
+
1008
+ if (is_file($dir . '/icon.png')) {
1009
+ $data['icon'] = content_url($relative_dir . '/icon.png');
1010
+ }
1011
+
1012
+ $data['id'] = sanitize_key($file);
1013
+
1014
+ // Absolute path of the block files
1015
+ $data['dir'] = $dir;
1016
+ $data['url'] = content_url($relative_dir);
1017
+
1018
+ return $data;
1019
+ }
1020
+
1021
+ /**
1022
+ *
1023
+ * @param type $dir
1024
+ * @return type
1025
+ */
1026
+ function scan_blocks_dir($dir) {
1027
+ $dir = realpath($dir);
1028
+ if (!$dir) {
1029
+ return [];
1030
+ }
1031
+ $dir = wp_normalize_path($dir);
1032
+
1033
+ $list = [];
1034
+ $handle = opendir($dir);
1035
+ while ($file = readdir($handle)) {
1036
+
1037
+ $data = $this->build_block($dir . '/' . $file);
1038
+
1039
+ if (is_wp_error($data)) {
1040
+ $this->logger->error($data);
1041
+ continue;
1042
+ }
1043
+ $list[$data['id']] = $data;
1044
+ }
1045
+ closedir($handle);
1046
+ return $list;
1047
+ }
1048
+
1049
+ /**
1050
+ * Array of arrays with every registered block and legacy block converted to the new
1051
+ * format.
1052
+ *
1053
+ * @return array
1054
+ */
1055
+ function get_blocks() {
1056
+
1057
+ if (!is_null($this->blocks)) {
1058
+ return $this->blocks;
1059
+ }
1060
+
1061
+ $this->blocks = $this->scan_blocks_dir(__DIR__ . '/blocks');
1062
+
1063
+ $extended = $this->scan_blocks_dir(WP_CONTENT_DIR . '/extensions/newsletter/blocks');
1064
+
1065
+ $this->blocks = array_merge($extended, $this->blocks);
1066
+
1067
+ $dirs = apply_filters('newsletter_blocks_dir', array());
1068
+
1069
+ //$this->logger->debug('Block dirs:');
1070
+ //$this->logger->debug($dirs);
1071
+
1072
+ foreach ($dirs as $dir) {
1073
+ $list = $this->scan_blocks_dir($dir);
1074
+ $this->blocks = array_merge($list, $this->blocks);
1075
+ }
1076
+
1077
+ do_action('newsletter_register_blocks');
1078
+
1079
+ foreach (TNP_Composer::$block_dirs as $dir) {
1080
+ $block = $this->build_block($dir);
1081
+ if (is_wp_error($block)) {
1082
+ $this->logger->error($block);
1083
+ continue;
1084
+ }
1085
+ if (!isset($this->blocks[$block['id']])) {
1086
+ $this->blocks[$block['id']] = $block;
1087
+ } else {
1088
+ $this->logger->error('The block "' . $block['id'] . '" has already been registered');
1089
+ }
1090
+ }
1091
+
1092
+ $this->blocks = array_reverse($this->blocks);
1093
+ return $this->blocks;
1094
+ }
1095
+
1096
+ /**
1097
+ * Return a single block (associative array) checking for legacy ID as well.
1098
+ *
1099
+ * @param string $id
1100
+ * @return array
1101
+ */
1102
+ function get_block($id) {
1103
+ switch ($id) {
1104
+ case 'content-03-text.block':
1105
+ $id = 'text';
1106
+ break;
1107
+ case 'footer-03-social.block':
1108
+ $id = 'social';
1109
+ break;
1110
+ case 'footer-02-canspam.block':
1111
+ $id = 'canspam';
1112
+ break;
1113
+ case 'content-05-image.block':
1114
+ $id = 'image';
1115
+ break;
1116
+ case 'header-01-header.block':
1117
+ $id = 'header';
1118
+ break;
1119
+ case 'footer-01-footer.block':
1120
+ $id = 'footer';
1121
+ break;
1122
+ case 'content-02-heading.block':
1123
+ $id = 'heading';
1124
+ break;
1125
+ case 'content-07-twocols.block':
1126
+ case 'content-06-posts.block':
1127
+ $id = 'posts';
1128
+ break;
1129
+ case 'content-04-cta.block':
1130
+ $id = 'cta';
1131
+ break;
1132
+ case 'content-01-hero.block':
1133
+ $id = 'hero';
1134
+ break;
1135
+ // case 'content-02-heading.block': $id = '/plugins/newsletter/emails/blocks/heading';
1136
+ // break;
1137
+ }
1138
+
1139
+ // Conversion for old full path ID
1140
+ $id = sanitize_key(basename($id));
1141
+
1142
+ // TODO: Correct id for compatibility
1143
+ $blocks = $this->get_blocks();
1144
+ if (!isset($blocks[$id])) {
1145
+ return null;
1146
+ }
1147
+ return $blocks[$id];
1148
+ }
1149
+
1150
+ function scan_presets_dir($dir = null) {
1151
+
1152
+ if (is_null($dir)) {
1153
+ $dir = __DIR__ . '/presets';
1154
+ }
1155
+
1156
+ if (!is_dir($dir)) {
1157
+ return array();
1158
+ }
1159
+
1160
+ $handle = opendir($dir);
1161
+ $list = array();
1162
+ $relative_dir = substr($dir, strlen(WP_CONTENT_DIR));
1163
+ while ($file = readdir($handle)) {
1164
+
1165
+ if ($file == '.' || $file == '..')
1166
+ continue;
1167
+
1168
+ // The block unique key, we should find out how to build it, maybe an hash of the (relative) dir?
1169
+ $preset_id = sanitize_key($file);
1170
+
1171
+ $full_file = $dir . '/' . $file . '/preset.json';
1172
+
1173
+ if (!is_file($full_file)) {
1174
+ continue;
1175
+ }
1176
+
1177
+ $icon = content_url($relative_dir . '/' . $file . '/icon.png');
1178
+
1179
+ $list[$preset_id] = $icon;
1180
+ }
1181
+ closedir($handle);
1182
+ return $list;
1183
+ }
1184
+
1185
+ function get_preset_from_file($id, $dir = null) {
1186
+
1187
+ if (is_null($dir)) {
1188
+ $dir = __DIR__ . '/presets';
1189
+ }
1190
+
1191
+ $id = $this->sanitize_file_name($id);
1192
+
1193
+ if (!is_dir($dir . '/' . $id) || !in_array($id, self::$PRESETS_LIST)) {
1194
+ return array();
1195
+ }
1196
+
1197
+ $json_content = file_get_contents("$dir/$id/preset.json");
1198
+ $json_content = str_replace("{placeholder_base_url}", plugins_url('newsletter') . '/emails/presets', $json_content);
1199
+ $json = json_decode($json_content);
1200
+ $json->icon = NEWSLETTER_URL . "/emails/presets/$id/icon.png?ver=2";
1201
+
1202
+ return $json;
1203
+ }
1204
+
1205
+ function get_composer_css() {
1206
+ $css = file_get_contents(__DIR__ . '/tnp-composer/css/newsletter.css');
1207
+ $css .= "\n\n";
1208
+ $css .= file_get_contents(__DIR__ . '/tnp-composer/css/backend.css');
1209
+ $blocks = $this->get_blocks();
1210
+ foreach ($blocks as $block) {
1211
+ if (!file_exists($block['dir'] . '/style.css')) {
1212
+ continue;
1213
+ }
1214
+ $css .= "\n\n";
1215
+ $css .= "/* " . $block['name'] . " */\n";
1216
+ $css .= file_get_contents($block['dir'] . '/style.css');
1217
+ }
1218
+ return $css;
1219
+ }
1220
+
1221
+ /**
1222
+ * Send an email to the test subscribers.
1223
+ *
1224
+ * @param TNP_Email $email Could be any object with the TNP_Email attributes
1225
+ * @param NewsletterControls $controls
1226
+ */
1227
+ function send_test_email($email, $controls) {
1228
+ if (!$email) {
1229
+ $controls->errors = __('Newsletter should be saved before send a test', 'newsletter');
1230
+ return;
1231
+ }
1232
+
1233
+ $original_subject = $email->subject;
1234
+ $this->set_test_subject_to($email);
1235
+
1236
+ $users = NewsletterUsers::instance()->get_test_users();
1237
+ if (count($users) == 0) {
1238
+ $controls->errors = '' . __('There are no test subscribers to send to', 'newsletter') .
1239
+ '. <a href="https://www.thenewsletterplugin.com/plugins/newsletter/subscribers-module#test" target="_blank"><strong>' .
1240
+ __('Read more', 'newsletter') . '</strong></a>.';
1241
+ } else {
1242
+ $r = Newsletter::instance()->send($email, $users, true);
1243
+ $emails = array();
1244
+ foreach ($users as $user) {
1245
+ $emails[] = '<a href="admin.php?page=newsletter_users_edit&id=' . $user->id . '" target="_blank">' . $user->email . '</a>';
1246
+ }
1247
+ if (is_wp_error($r)) {
1248
+ $controls->errors = 'Something went wrong. Check the error logs on status page.<br>';
1249
+ $controls->errors .= __('Test subscribers:', 'newsletter');
1250
+ $controls->errors .= ' ' . implode(', ', $emails);
1251
+ $controls->errors .= '<br>';
1252
+ $controls->errors .= '<strong>' . esc_html($r->get_error_message()) . '</strong><br>';
1253
+ $controls->errors .= '<a href="https://www.thenewsletterplugin.com/documentation/email-sending-issues" target="_blank"><strong>' . __('Read more about delivery issues', 'newsletter') . '</strong></a>.';
1254
+ } else {
1255
+ $controls->messages = __('Test subscribers:', 'newsletter');
1256
+
1257
+ $controls->messages .= ' ' . implode(', ', $emails);
1258
+ $controls->messages .= '.<br>';
1259
+ $controls->messages .= '<a href="https://www.thenewsletterplugin.com/documentation/subscribers#test" target="_blank"><strong>' .
1260
+ __('Read more about test subscribers', 'newsletter') . '</strong></a>.<br>';
1261
+ $controls->messages .= '<a href="https://www.thenewsletterplugin.com/documentation/email-sending-issues" target="_blank"><strong>' . __('Read more about delivery issues', 'newsletter') . '</strong></a>.';
1262
+ }
1263
+ }
1264
+ $email->subject = $original_subject;
1265
+ }
1266
+
1267
+ /**
1268
+ * Send an email to the test subscribers.
1269
+ *
1270
+ * @param TNP_Email $email Could be any object with the TNP_Email attributes
1271
+ * @param string $email_address
1272
+ *
1273
+ * @throws Exception
1274
+ */
1275
+ function send_test_newsletter_to_email_address( $email, $email_address ) {
1276
+
1277
+ if ( ! $email ) {
1278
+ throw new Exception( __( 'Newsletter should be saved before send a test', 'newsletter' ) );
1279
+ }
1280
+
1281
+ $this->set_test_subject_to( $email );
1282
+
1283
+ $dummy_subscriber = $this->make_dummy_subscriber();
1284
+ $dummy_subscriber->email = $email_address;
1285
+
1286
+ $result = Newsletter::instance()->send( $email, [ $dummy_subscriber ], true );
1287
+
1288
+ $email = '<a href="admin.php?page=newsletter_users_edit&id=' . $dummy_subscriber->id . '" target="_blank">' . $dummy_subscriber->email . '</a>';
1289
+
1290
+ if ( is_wp_error( $result ) ) {
1291
+ $error_message = 'Something went wrong. Check the error logs on status page.<br>';
1292
+ $error_message .= __( 'Test subscribers:', 'newsletter' );
1293
+ $error_message .= ' ' . $email;
1294
+ $error_message .= '<br>';
1295
+ $error_message .= '<strong>' . esc_html( $result->get_error_message() ) . '</strong><br>';
1296
+ $error_message .= '<a href="https://www.thenewsletterplugin.com/documentation/email-sending-issues" target="_blank"><strong>' . __( 'Read more about delivery issues', 'newsletter' ) . '</strong></a>.';
1297
+ throw new Exception( $error_message );
1298
+ }
1299
+
1300
+ $messages = __( 'Test subscribers:', 'newsletter' );
1301
+
1302
+ $messages .= ' ' . $email;
1303
+ $messages .= '.<br>';
1304
+ $messages .= '<a href="https://www.thenewsletterplugin.com/documentation/subscribers#test" target="_blank"><strong>' .
1305
+ __( 'Read more about test subscribers', 'newsletter' ) . '</strong></a>.<br>';
1306
+ $messages .= '<a href="https://www.thenewsletterplugin.com/documentation/email-sending-issues" target="_blank"><strong>' . __( 'Read more about delivery issues', 'newsletter' ) . '</strong></a>.';
1307
+
1308
+ return $messages;
1309
+ }
1310
+
1311
+ private function set_test_subject_to($email) {
1312
+ if ( $email->subject == '' ) {
1313
+ $email->subject = '[TEST] Dummy subject, it was empty (remember to set it)';
1314
+ } else {
1315
+ $email->subject = $email->subject . ' (TEST)';
1316
+ }
1317
+ }
1318
+
1319
+ private function make_dummy_subscriber() {
1320
+ $dummy_user = new TNP_User();
1321
+ $dummy_user->id = 0;
1322
+ $dummy_user->email = 'john.doe@example.org';
1323
+ $dummy_user->name = 'John';
1324
+ $dummy_user->surname = 'Doe';
1325
+ $dummy_user->sex = 'n';
1326
+ $dummy_user->language = '';
1327
+ $dummy_user->ip = '';
1328
+
1329
+ for ( $i = 1; $i <= NEWSLETTER_PROFILE_MAX; $i ++ ) {
1330
+ $profile_key = "profile_$i";
1331
+ $dummy_user->$profile_key = '';
1332
+ }
1333
+
1334
+ return $dummy_user;
1335
+ }
1336
+
1337
+ function restore_options_from_request() {
1338
+
1339
+ require_once NEWSLETTER_INCLUDES_DIR . '/controls.php';
1340
+ $controls = new NewsletterControls();
1341
+ $options = $controls->data;
1342
+
1343
+ if (isset($_POST['options']) && is_array($_POST['options'])) {
1344
+ // Get all block options
1345
+ //$options = stripslashes_deep($_POST['options']);
1346
+
1347
+ // Deserialize inline edits when
1348
+ // render is preformed on saving block options
1349
+ if (isset($options['inline_edits']) && !is_array($options['inline_edits'])) {
1350
+ $options['inline_edits'] = $this->options_decode($options['inline_edits']);
1351
+ }
1352
+
1353
+ // Restore inline edits from data-json
1354
+ // coming from inline editing
1355
+ // and merge with current inline edit
1356
+ if (isset($_POST['encoded_options'])) {
1357
+ $decoded_options = $this->options_decode($_POST['encoded_options']);
1358
+
1359
+ $to_merge_inline_edits = [];
1360
+
1361
+ if (isset($decoded_options['inline_edits'])) {
1362
+ foreach ($decoded_options['inline_edits'] as $decoded_inline_edit) {
1363
+ $to_merge_inline_edits[$decoded_inline_edit['post_id'] . $decoded_inline_edit['type']] = $decoded_inline_edit;
1364
+ }
1365
+ }
1366
+
1367
+ //Overwrite with new edited content
1368
+ if (isset($options['inline_edits'])) {
1369
+ foreach ($options['inline_edits'] as $inline_edit) {
1370
+ $to_merge_inline_edits[$inline_edit['post_id'] . $inline_edit['type']] = $inline_edit;
1371
+ }
1372
+ }
1373
+
1374
+ $options['inline_edits'] = array_values($to_merge_inline_edits);
1375
+ $options = array_merge($decoded_options, $options);
1376
+ }
1377
+
1378
+ return $options;
1379
+ }
1380
+
1381
+ return array();
1382
+ }
1383
+
1384
+ public function hook_wp_ajax_tnpc_delete_preset() {
1385
+
1386
+ if (!wp_verify_nonce($_POST['_wpnonce'], 'preset')) {
1387
+ wp_send_json_error('Expired request');
1388
+ }
1389
+
1390
+ $preset_id = (int) $_REQUEST['presetId'];
1391
+
1392
+ $newsletter = Newsletter::instance();
1393
+
1394
+ if ($preset_id > 0) {
1395
+ $preset = $newsletter->get_email($preset_id);
1396
+
1397
+ if ($preset && $preset->type === self::PRESET_EMAIL_TYPE) {
1398
+ Newsletter::instance()->delete_email($preset_id);
1399
+ wp_send_json_success();
1400
+ } else {
1401
+ wp_send_json_error(__('Is not a preset!', 'newsletter'));
1402
+ }
1403
+ } else {
1404
+ wp_send_json_error();
1405
+ }
1406
+ }
1407
+
1408
+ }
1409
+
1410
+ NewsletterEmails::instance();
emails/tnp-composer/css/newsletter.css CHANGED
@@ -13,8 +13,9 @@ img.aligncenter { display: block; margin: 0 auto;}
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
 
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 { max-width: 100%!important }
17
  img[class="responsive"] { max-width: 100%!important; }
18
+ /* "width: auto" restores the natural dimensions forced with attributes for Outlook */
19
  .fluid { max-width: 100%!important; width: auto; }
20
  img[class="fluid"] { max-width: 100%!important; width: auto; }
21
 
includes/composer.php CHANGED
@@ -272,6 +272,8 @@ class TNP_Composer {
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'])) {
272
 
273
  $controls->data['message'] = TNP_Composer::unwrap_email($email->message);
274
  $controls->data['subject'] = $email->subject;
275
+ $controls->data['updated'] = $email->updated;
276
+
277
  }
278
 
279
  if (!empty($email->options['sender_email'])) {
includes/controls.php CHANGED
@@ -2072,6 +2072,7 @@ tnp_controls_init();
2072
  $this->hidden('subject');
2073
  $this->hidden('message');
2074
  $this->hidden('options_preheader');
 
2075
 
2076
  //$preheader_value = $this->get_value('options_preheader');
2077
  // echo '<input name="options[preheader]" id="options-preheader" type="hidden" value="', esc_attr($preheader_value), '">';
2072
  $this->hidden('subject');
2073
  $this->hidden('message');
2074
  $this->hidden('options_preheader');
2075
+ $this->hidden('updated');
2076
 
2077
  //$preheader_value = $this->get_value('options_preheader');
2078
  // echo '<input name="options[preheader]" id="options-preheader" type="hidden" value="', esc_attr($preheader_value), '">';
includes/logger.php CHANGED
@@ -1,106 +1,106 @@
1
- <?php
2
-
3
- defined('ABSPATH') || exit;
4
-
5
- if (!defined('NEWSLETTER_LOG_DIR')) {
6
- define('NEWSLETTER_LOG_DIR', WP_CONTENT_DIR . '/logs/newsletter');
7
- }
8
-
9
- class NewsletterLogger {
10
-
11
- const NONE = 0;
12
- const FATAL = 1;
13
- const ERROR = 2;
14
- const INFO = 3;
15
- const DEBUG = 4;
16
-
17
- var $level;
18
- var $module;
19
- var $file;
20
-
21
- function __construct($module) {
22
- $this->module = $module;
23
- if (defined('NEWSLETTER_LOG_LEVEL'))
24
- $this->level = NEWSLETTER_LOG_LEVEL;
25
- else
26
- $this->level = (int) get_option('newsletter_log_level', self::ERROR);
27
-
28
- $secret = get_option('newsletter_logger_secret', '');
29
- if (strlen($secret) < 8) {
30
- $secret = NewsletterModule::get_token(8);
31
- update_option('newsletter_logger_secret', $secret);
32
- }
33
-
34
- if (!wp_mkdir_p(NEWSLETTER_LOG_DIR)) {
35
- $this->level = self::NONE;
36
- }
37
-
38
- $this->file = NEWSLETTER_LOG_DIR . '/' . $module . '-' . date('Y-m') . '-' . $secret . '.txt';
39
- }
40
-
41
- /**
42
- *
43
- * @param string|WP_Error|array|stdClass $text
44
- * @param int $level
45
- */
46
- function log($text, $level = self::ERROR) {
47
- global $current_user;
48
-
49
- if ($level != self::FATAL && $this->level < $level) {
50
- return;
51
- }
52
-
53
- if (defined('DOING_CRON') && DOING_CRON) {
54
- $user = '[cron]';
55
- } else if ($current_user) {
56
- $user = $current_user->user_login;
57
- } else {
58
- $user = '[no user]';
59
- }
60
-
61
- $time = date('d-m-Y H:i:s ');
62
- switch ($level) {
63
- case self::FATAL: $time .= '- FATAL';
64
- break;
65
- case self::ERROR: $time .= '- ERROR';
66
- break;
67
- case self::INFO: $time .= '- INFO ';
68
- break;
69
- case self::DEBUG: $time .= '- DEBUG';
70
- break;
71
- }
72
- if (is_wp_error($text)) {
73
- /* @var $text WP_Error */
74
- $text = $text->get_error_message() . ' (' . $text->get_error_code() . ') - ' . print_r($text->get_error_data(), true);
75
- } else {
76
- if (is_array($text) || is_object($text)) {
77
- $text = print_r($text, true);
78
- }
79
- }
80
-
81
- $memory_limit = size_format(wp_convert_hr_to_bytes(ini_get('memory_limit')));
82
-
83
- // The "logs" dir is created on Newsletter constructor.
84
- $res = @file_put_contents($this->file, $time . ' - ' . NEWSLETTER_VERSION . ' - ' . size_format(memory_get_usage(), 1) . '/' . $memory_limit . ' - ' . $user . ' > ' . $text . "\n", FILE_APPEND | FILE_TEXT);
85
- if ($res === false) {
86
- //$this->level = self::NONE;
87
- }
88
- }
89
-
90
- function error($text) {
91
- self::log($text, self::ERROR);
92
- }
93
-
94
- function info($text) {
95
- $this->log($text, self::INFO);
96
- }
97
-
98
- function fatal($text) {
99
- $this->log($text, self::FATAL);
100
- }
101
-
102
- function debug($text) {
103
- $this->log($text, self::DEBUG);
104
- }
105
-
106
- }
1
+ <?php
2
+
3
+ defined('ABSPATH') || exit;
4
+
5
+ if (!defined('NEWSLETTER_LOG_DIR')) {
6
+ define('NEWSLETTER_LOG_DIR', WP_CONTENT_DIR . '/logs/newsletter');
7
+ }
8
+
9
+ class NewsletterLogger {
10
+
11
+ const NONE = 0;
12
+ const FATAL = 1;
13
+ const ERROR = 2;
14
+ const INFO = 3;
15
+ const DEBUG = 4;
16
+
17
+ var $level;
18
+ var $module;
19
+ var $file;
20
+
21
+ function __construct($module) {
22
+ $this->module = $module;
23
+ if (defined('NEWSLETTER_LOG_LEVEL'))
24
+ $this->level = NEWSLETTER_LOG_LEVEL;
25
+ else
26
+ $this->level = (int) get_option('newsletter_log_level', self::ERROR);
27
+
28
+ $secret = get_option('newsletter_logger_secret', '');
29
+ if (strlen($secret) < 8) {
30
+ $secret = NewsletterModule::get_token(8);
31
+ update_option('newsletter_logger_secret', $secret);
32
+ }
33
+
34
+ if (!wp_mkdir_p(NEWSLETTER_LOG_DIR)) {
35
+ $this->level = self::NONE;
36
+ }
37
+
38
+ $this->file = NEWSLETTER_LOG_DIR . '/' . $module . '-' . date('Y-m') . '-' . $secret . '.txt';
39
+ }
40
+
41
+ /**
42
+ *
43
+ * @param string|WP_Error|array|stdClass $text
44
+ * @param int $level
45
+ */
46
+ function log($text, $level = self::ERROR) {
47
+ global $current_user;
48
+
49
+ if ($level != self::FATAL && $this->level < $level) {
50
+ return;
51
+ }
52
+
53
+ if (defined('DOING_CRON') && DOING_CRON) {
54
+ $user = '[cron]';
55
+ } else if ($current_user) {
56
+ $user = $current_user->user_login;
57
+ } else {
58
+ $user = '[no user]';
59
+ }
60
+
61
+ $time = date('d-m-Y H:i:s ');
62
+ switch ($level) {
63
+ case self::FATAL: $time .= '- FATAL';
64
+ break;
65
+ case self::ERROR: $time .= '- ERROR';
66
+ break;
67
+ case self::INFO: $time .= '- INFO ';
68
+ break;
69
+ case self::DEBUG: $time .= '- DEBUG';
70
+ break;
71
+ }
72
+ if (is_wp_error($text)) {
73
+ /* @var $text WP_Error */
74
+ $text = $text->get_error_message() . ' (' . $text->get_error_code() . ') - ' . print_r($text->get_error_data(), true);
75
+ } else {
76
+ if (is_array($text) || is_object($text)) {
77
+ $text = print_r($text, true);
78
+ }
79
+ }
80
+
81
+ $memory_limit = size_format(wp_convert_hr_to_bytes(ini_get('memory_limit')));
82
+
83
+ // The "logs" dir is created on Newsletter constructor.
84
+ $res = @file_put_contents($this->file, $time . ' - ' . NEWSLETTER_VERSION . ' - ' . size_format(memory_get_usage(), 1) . '/' . $memory_limit . ' - ' . $user . ' > ' . $text . "\n", FILE_APPEND | FILE_TEXT);
85
+ if ($res === false) {
86
+ //$this->level = self::NONE;
87
+ }
88
+ }
89
+
90
+ function error($text) {
91
+ self::log($text, self::ERROR);
92
+ }
93
+
94
+ function info($text) {
95
+ $this->log($text, self::INFO);
96
+ }
97
+
98
+ function fatal($text) {
99
+ $this->log($text, self::FATAL);
100
+ }
101
+
102
+ function debug($text) {
103
+ $this->log($text, self::DEBUG);
104
+ }
105
+
106
+ }
includes/module.php CHANGED
@@ -21,6 +21,8 @@ class TNP_Media {
21
 
22
  /** Sets the width recalculating the height */
23
  public function set_width($width) {
 
 
24
  if ($this->width < $width) return;
25
  $this->height = floor(($width / $this->width) * $this->height);
26
  $this->width = $width;
@@ -319,6 +321,7 @@ class TNP_Subscription {
319
  * @property string $status The subscriber status
320
  * @property string $language The subscriber language code 2 chars lowercase
321
  * @property string $token The subscriber secret token
 
322
  */
323
  class TNP_User {
324
 
@@ -326,6 +329,7 @@ class TNP_User {
326
  const STATUS_NOT_CONFIRMED = 'S';
327
  const STATUS_UNSUBSCRIBED = 'U';
328
  const STATUS_BOUNCED = 'B';
 
329
 
330
  public static function get_status_label($status) {
331
  switch ($status) {
@@ -337,6 +341,10 @@ class TNP_User {
337
  break;
338
  case self::STATUS_BOUNCED: return __('BOUNCED', 'newsletter');
339
  break;
 
 
 
 
340
  }
341
  }
342
 
@@ -1329,18 +1337,24 @@ class NewsletterModule {
1329
  return $user->id . '-' . $user->token;
1330
  }
1331
 
1332
- function get_user_status_label($user) {
 
 
 
 
1333
  switch ($user->status) {
1334
- case 'S': return __('NOT CONFIRMED', 'newsletter');
1335
  break;
1336
- case 'C': return __('CONFIRMED', 'newsletter');
1337
  break;
1338
- case 'U': return __('UNSUBSCRIBED', 'newsletter');
1339
  break;
1340
- case 'B': return __('BOUNCED', 'newsletter');
 
 
1341
  break;
1342
  }
1343
- return '';
1344
  }
1345
 
1346
  /**
21
 
22
  /** Sets the width recalculating the height */
23
  public function set_width($width) {
24
+ $width = (int)$width;
25
+ if (empty($width)) return;
26
  if ($this->width < $width) return;
27
  $this->height = floor(($width / $this->width) * $this->height);
28
  $this->width = $width;
321
  * @property string $status The subscriber status
322
  * @property string $language The subscriber language code 2 chars lowercase
323
  * @property string $token The subscriber secret token
324
+ * @property string $country The subscriber country code 2 chars uppercase
325
  */
326
  class TNP_User {
327
 
329
  const STATUS_NOT_CONFIRMED = 'S';
330
  const STATUS_UNSUBSCRIBED = 'U';
331
  const STATUS_BOUNCED = 'B';
332
+ const STATUS_COMPLAINED = 'P';
333
 
334
  public static function get_status_label($status) {
335
  switch ($status) {
341
  break;
342
  case self::STATUS_BOUNCED: return __('BOUNCED', 'newsletter');
343
  break;
344
+ case self::STATUS_COMPLAINED: return __('COMPLAINED', 'newsletter');
345
+ break;
346
+ default:
347
+ return __('Unknown', 'newsletter');
348
  }
349
  }
350
 
1337
  return $user->id . '-' . $user->token;
1338
  }
1339
 
1340
+ function get_user_status_label($user, $html = false) {
1341
+ if (!$html) return TNP_User::get_status_label($user->status);
1342
+
1343
+ $label = TNP_User::get_status_label($user->status);
1344
+ $class = 'unknown';
1345
  switch ($user->status) {
1346
+ case TNP_User::STATUS_NOT_CONFIRMED: $class = 'not-confirmed';
1347
  break;
1348
+ case TNP_User::STATUS_CONFIRMED: $class = 'confirmed';
1349
  break;
1350
+ case TNP_User::STATUS_UNSUBSCRIBED: $class = 'unsubscribed';
1351
  break;
1352
+ case TNP_User::STATUS_BOUNCED: $class = 'bounced';
1353
+ break;
1354
+ case TNP_User::STATUS_COMPLAINED: $class = 'complained';
1355
  break;
1356
  }
1357
+ return '<span class="' . $class . '">' . esc_html($label) . '</span>';
1358
  }
1359
 
1360
  /**
main/main.php CHANGED
@@ -1,380 +1,380 @@
1
- <?php
2
- /* @var $this Newsletter */
3
- defined('ABSPATH') || exit;
4
-
5
- include_once NEWSLETTER_INCLUDES_DIR . '/controls.php';
6
- $controls = new NewsletterControls();
7
-
8
- if (!$controls->is_action()) {
9
- $controls->data = get_option('newsletter_main');
10
- if (!isset($controls->data['roles'])) {
11
- $controls->data['roles'] = array();
12
- if (!empty($controls->data['editor']))
13
- $controls->data['roles'] = 'editor';
14
- }
15
- } else {
16
-
17
- if ($controls->is_action('save')) {
18
- $errors = null;
19
-
20
- if (!isset($controls->data['roles']))
21
- $controls->data['roles'] = array();
22
-
23
- // Validation
24
- $controls->data['sender_email'] = $this->normalize_email($controls->data['sender_email']);
25
- if (!$this->is_email($controls->data['sender_email'])) {
26
- $controls->errors .= __('The sender email address is not correct.', 'newsletter') . '<br>';
27
- } else {
28
- $controls->data['sender_email'] = $this->normalize_email($controls->data['sender_email']);
29
- }
30
-
31
- if (!$this->is_email($controls->data['return_path'], true)) {
32
- $controls->errors .= __('Return path email is not correct.', 'newsletter') . '<br>';
33
- } else {
34
- $controls->data['return_path'] = $this->normalize_email($controls->data['return_path']);
35
- }
36
-
37
- $controls->data['scheduler_max'] = (int) $controls->data['scheduler_max'];
38
- if ($controls->data['scheduler_max'] < 10)
39
- $controls->data['scheduler_max'] = 10;
40
-
41
-
42
- if (!$this->is_email($controls->data['reply_to'], true)) {
43
- $controls->errors .= __('Reply to email is not correct.', 'newsletter') . '<br>';
44
- } else {
45
- $controls->data['reply_to'] = $this->normalize_email($controls->data['reply_to']);
46
- }
47
-
48
- if (!empty($controls->data['contract_key'])) {
49
- $controls->data['contract_key'] = trim($controls->data['contract_key']);
50
- }
51
-
52
- if (empty($controls->errors)) {
53
- $this->merge_options($controls->data);
54
- $controls->add_message_saved();
55
- $this->logger->debug('Main options saved');
56
- }
57
-
58
- update_option('newsletter_log_level', $controls->data['log_level']);
59
-
60
- //$this->hook_newsletter_extension_versions(true);
61
- delete_transient("tnp_extensions_json");
62
- delete_transient('newsletter_license_data');
63
- }
64
-
65
- if ($controls->is_action('create')) {
66
- $page = array();
67
- $page['post_title'] = 'Newsletter';
68
- $page['post_content'] = '[newsletter]';
69
- $page['post_status'] = 'publish';
70
- $page['post_type'] = 'page';
71
- $page['comment_status'] = 'closed';
72
- $page['ping_status'] = 'closed';
73
- $page['post_category'] = array(1);
74
-
75
- $current_language = $this->get_current_language();
76
- $this->switch_language('');
77
- // Insert the post into the database
78
- $page_id = wp_insert_post($page);
79
- $this->switch_language($current_language);
80
-
81
- $controls->data['page'] = $page_id;
82
- $this->merge_options($controls->data);
83
-
84
- $controls->messages = 'A new page has been created';
85
- }
86
- }
87
-
88
- $license_data = $this->get_license_data(true);
89
-
90
- if (is_wp_error($license_data)) {
91
- $controls->errors .= esc_html('[' . $license_data->get_error_code()) . '] - ' . esc_html($license_data->get_error_message());
92
- } else {
93
- if ($license_data !== false) {
94
- if ($license_data->expire == 0) {
95
- $controls->messages = 'Your FREE license is valid';
96
- } elseif ($license_data->expire >= time()) {
97
- $controls->messages = 'Your license is valid and expires on ' . esc_html(date('Y-m-d', $license_data->expire));
98
- } else {
99
- $controls->errors = 'Your license is expired on ' . esc_html(date('Y-m-d', $license_data->expire));
100
- }
101
- }
102
- }
103
-
104
- $return_path = $this->options['return_path'];
105
-
106
- if (!empty($return_path)) {
107
- list($return_path_local, $return_path_domain) = explode('@', $return_path);
108
-
109
- $sender = $this->options['sender_email'];
110
- list($sender_local, $sender_domain) = explode('@', $sender);
111
-
112
- if ($sender_domain != $return_path_domain) {
113
- $controls->warnings[] = __('Your Return Path domain is different from your Sender domain. Providers may require them to match.', 'newsletter');
114
- }
115
- }
116
- ?>
117
-
118
- <?php include NEWSLETTER_INCLUDES_DIR . '/codemirror.php'; ?>
119
- <style>
120
- .CodeMirror {
121
- border: 1px solid #ddd;
122
- }
123
- </style>
124
-
125
- <script>
126
- jQuery(function () {
127
- var editor = CodeMirror.fromTextArea(document.getElementById("options-css"), {
128
- lineNumbers: true,
129
- mode: 'css',
130
- extraKeys: {"Ctrl-Space": "autocomplete"}
131
- });
132
- });
133
- </script>
134
-
135
- <div class="wrap" id="tnp-wrap">
136
-
137
- <?php include NEWSLETTER_DIR . '/tnp-header.php'; ?>
138
-
139
- <div id="tnp-heading">
140
-
141
- <h2><?php _e('General Settings', 'newsletter') ?></h2>
142
-
143
- </div>
144
- <div id="tnp-body" class="tnp-main-main">
145
-
146
-
147
- <form method="post" action="">
148
- <?php $controls->init(); ?>
149
-
150
- <div id="tabs">
151
-
152
- <ul>
153
- <li><a href="#tabs-basic"><?php _e('Basic Settings', 'newsletter') ?></a></li>
154
- <li><a href="#tabs-speed"><?php _e('Delivery Speed', 'newsletter') ?></a></li>
155
- <li><a href="#tabs-advanced"><?php _e('Advanced Settings', 'newsletter') ?></a></li>
156
- </ul>
157
-
158
- <div id="tabs-basic">
159
-
160
- <p>
161
- <?php $controls->panel_help('https://www.thenewsletterplugin.com/plugins/newsletter/newsletter-configuration') ?>
162
- </p>
163
-
164
-
165
- <table class="form-table">
166
-
167
- <tr>
168
- <th>
169
- <?php _e('Sender email address', 'newsletter') ?>
170
- <?php $controls->field_help('https://www.thenewsletterplugin.com/documentation/installation/newsletter-configuration/#sender') ?>
171
- </th>
172
- <td>
173
- <?php $controls->text_email('sender_email', 40); ?>
174
-
175
- </td>
176
- </tr>
177
- <tr>
178
- <th>
179
- <?php _e('Sender name', 'newsletter') ?>
180
- </th>
181
- <td>
182
- <?php $controls->text('sender_name', 40); ?>
183
- </td>
184
- </tr>
185
-
186
- <tr>
187
- <th>
188
- <?php _e('Return path', 'newsletter') ?>
189
- <?php $controls->field_help('https://www.thenewsletterplugin.com/documentation/installation/newsletter-configuration/#return-path') ?>
190
- </th>
191
- <td>
192
- <?php $controls->text_email('return_path', 40); ?>
193
-
194
- </td>
195
- </tr>
196
- <tr>
197
- <th>
198
- <?php _e('Reply to', 'newsletter') ?>
199
- <?php $controls->field_help('https://www.thenewsletterplugin.com/documentation/installation/newsletter-configuration/#reply-to') ?>
200
- </th>
201
- <td>
202
- <?php $controls->text_email('reply_to', 40); ?>
203
-
204
- </td>
205
- </tr>
206
- <tr>
207
- <th>
208
- <?php _e('Dedicated page', 'newsletter') ?>
209
- <?php $controls->field_help('https://www.thenewsletterplugin.com/documentation/installation/newsletter-configuration/#dedicated-page') ?>
210
- </th>
211
- <td>
212
- <?php $controls->page('page', __('Unstyled page', 'newsletter'), '', true); ?>
213
- <?php
214
- if (empty($controls->data['page'])) {
215
- $controls->button('create', __('Create the page', 'newsletter'));
216
- }
217
- ?>
218
- </td>
219
- </tr>
220
-
221
- <tr>
222
- <th><?php _e('License key', 'newsletter') ?></th>
223
- <td>
224
- <?php if (defined('NEWSLETTER_LICENSE_KEY')) { ?>
225
- <?php _e('A license key is set', 'newsletter') ?>
226
- <?php } else { ?>
227
- <?php $controls->text('contract_key', 40); ?>
228
- <p class="description">
229
- <?php printf(__('Find it in <a href="%s" target="_blank">your account</a> page', 'newsletter'), "https://www.thenewsletterplugin.com/account") ?>
230
- </p>
231
- <?php } ?>
232
- </td>
233
- </tr>
234
-
235
- </table>
236
- </div>
237
-
238
- <div id="tabs-speed">
239
-
240
- <table class="form-table">
241
- <tr>
242
- <th>
243
- <?php _e('Max emails per hour', 'newsletter') ?>
244
- <?php $controls->field_help('https://www.thenewsletterplugin.com/documentation/installation/newsletter-configuration/#speed') ?>
245
- </th>
246
- <td>
247
- <?php $controls->text('scheduler_max', 5); ?> (min. 10)
248
- <p class="description">
249
- <a href="?page=newsletter_main_status#tnp-speed">See the collected statistics</a>
250
- </td>
251
- </tr>
252
- </table>
253
-
254
- <?php do_action('newsletter_panel_main_speed', $controls) ?>
255
- </div>
256
-
257
-
258
- <div id="tabs-advanced">
259
-
260
- <p>
261
- <?php $controls->panel_help('https://www.thenewsletterplugin.com/plugins/newsletter/newsletter-configuration#advanced') ?>
262
- </p>
263
-
264
- <table class="form-table">
265
- <tr>
266
- <th><?php _e('Allowed roles', 'newsletter') ?></th>
267
- <td>
268
- <?php
269
- $wp_roles = get_editable_roles();
270
- $roles = array();
271
- foreach ($wp_roles as $key => $wp_role) {
272
- if ($key == 'administrator')
273
- continue;
274
- if ($key == 'subscriber')
275
- continue;
276
- $roles[$key] = $wp_role['name'];
277
- }
278
- $controls->checkboxes('roles', $roles);
279
- ?>
280
-
281
- </td>
282
- </tr>
283
-
284
- <tr>
285
- <th>
286
- <?php _e('Tracking default', 'newsletter') ?>
287
- <?php $controls->field_help('https://www.thenewsletterplugin.com/plugins/newsletter/newsletter-configuration#tracking') ?>
288
- </th>
289
- <td>
290
- <?php $controls->yesno('track'); ?>
291
- </td>
292
- </tr>
293
-
294
- <tr>
295
- <th>
296
- <?php _e('Execute shortcodes on newsletters', 'newsletter') ?>
297
- <?php $controls->field_help("https://www.thenewsletterplugin.com/documentation/newsletter-configuration#shortcodes") ?>
298
- </th>
299
- <td>
300
- <?php $controls->yesno('do_shortcodes', 40); ?>
301
- </td>
302
- </tr>
303
-
304
- <tr>
305
- <th>
306
- <?php _e('Log level', 'newsletter') ?>
307
- <?php $controls->field_help('https://www.thenewsletterplugin.com/plugins/newsletter/newsletter-configuration#log') ?>
308
- </th>
309
- <td>
310
- <?php $controls->log_level('log_level'); ?>
311
- </td>
312
- </tr>
313
-
314
- <tr>
315
- <th><?php _e('Standard styles', 'newsletter') ?></th>
316
- <td>
317
- <?php $controls->disabled('css_disabled'); ?>
318
- </td>
319
- </tr>
320
-
321
- <tr>
322
- <th><?php _e('Custom styles', 'newsletter') ?></th>
323
- <td>
324
- <?php if (apply_filters('newsletter_enqueue_style', true) === false) { ?>
325
- <p><strong>Warning: Newsletter styles and custom styles are disable by your theme or a plugin.</strong></p>
326
- <?php } ?>
327
- <?php $controls->textarea('css'); ?>
328
- </td>
329
- </tr>
330
-
331
-
332
- <tr>
333
- <th><?php _e('IP addresses', 'newsletter') ?></th>
334
- <td>
335
- <?php $controls->select('ip', array('' => __('Store', 'newsletter'), 'anonymize' => __('Anonymize', 'newsletter'), 'skip' => __('Do not store', 'newsletter'))); ?>
336
- </td>
337
- </tr>
338
-
339
-
340
-
341
- <tr>
342
- <th>
343
- <?php _e('Debug mode', 'newsletter') ?>
344
- <?php $controls->field_help('https://www.thenewsletterplugin.com/plugins/newsletter/newsletter-configuration#debug') ?>
345
- </th>
346
- <td>
347
- <?php $controls->yesno('debug', 40); ?>
348
- </td>
349
- </tr>
350
-
351
- <tr>
352
- <th>
353
- <?php _e('Email encoding', 'newsletter') ?>
354
- <?php $controls->field_help('https://www.thenewsletterplugin.com/plugins/newsletter/newsletter-configuration#encoding') ?>
355
- </th>
356
- <td>
357
- <?php $controls->select('content_transfer_encoding', array('' => 'Default', '8bit' => '8 bit', 'base64' => 'Base 64', 'binary' => 'Binary', 'quoted-printable' => 'Quoted printable', '7bit' => '7 bit')); ?>
358
- </td>
359
- </tr>
360
-
361
-
362
- </table>
363
-
364
- </div>
365
-
366
-
367
- </div> <!-- tabs -->
368
-
369
- <div class="tnp-buttons">
370
- <?php $controls->button_save(); ?>
371
- </div>
372
-
373
- </form>
374
-
375
- </div>
376
-
377
- <?php include NEWSLETTER_DIR . '/tnp-footer.php'; ?>
378
-
379
- </div>
380
-
1
+ <?php
2
+ /* @var $this Newsletter */
3
+ defined('ABSPATH') || exit;
4
+
5
+ include_once NEWSLETTER_INCLUDES_DIR . '/controls.php';
6
+ $controls = new NewsletterControls();
7
+
8
+ if (!$controls->is_action()) {
9
+ $controls->data = get_option('newsletter_main');
10
+ if (!isset($controls->data['roles'])) {
11
+ $controls->data['roles'] = array();
12
+ if (!empty($controls->data['editor']))
13
+ $controls->data['roles'] = 'editor';
14
+ }
15
+ } else {
16
+
17
+ if ($controls->is_action('save')) {
18
+ $errors = null;
19
+
20
+ if (!isset($controls->data['roles']))
21
+ $controls->data['roles'] = array();
22
+
23
+ // Validation
24
+ $controls->data['sender_email'] = $this->normalize_email($controls->data['sender_email']);
25
+ if (!$this->is_email($controls->data['sender_email'])) {
26
+ $controls->errors .= __('The sender email address is not correct.', 'newsletter') . '<br>';
27
+ } else {
28
+ $controls->data['sender_email'] = $this->normalize_email($controls->data['sender_email']);
29
+ }
30
+
31
+ if (!$this->is_email($controls->data['return_path'], true)) {
32
+ $controls->errors .= __('Return path email is not correct.', 'newsletter') . '<br>';
33
+ } else {
34
+ $controls->data['return_path'] = $this->normalize_email($controls->data['return_path']);
35
+ }
36
+
37
+ $controls->data['scheduler_max'] = (int) $controls->data['scheduler_max'];
38
+ if ($controls->data['scheduler_max'] < 10)
39
+ $controls->data['scheduler_max'] = 10;
40
+
41
+
42
+ if (!$this->is_email($controls->data['reply_to'], true)) {
43
+ $controls->errors .= __('Reply to email is not correct.', 'newsletter') . '<br>';
44
+ } else {
45
+ $controls->data['reply_to'] = $this->normalize_email($controls->data['reply_to']);
46
+ }
47
+
48
+ if (!empty($controls->data['contract_key'])) {
49
+ $controls->data['contract_key'] = trim($controls->data['contract_key']);
50
+ }
51
+
52
+ if (empty($controls->errors)) {
53
+ $this->merge_options($controls->data);
54
+ $controls->add_message_saved();
55
+ $this->logger->debug('Main options saved');
56
+ }
57
+
58
+ update_option('newsletter_log_level', $controls->data['log_level']);
59
+
60
+ //$this->hook_newsletter_extension_versions(true);
61
+ delete_transient("tnp_extensions_json");
62
+ delete_transient('newsletter_license_data');
63
+ }
64
+
65
+ if ($controls->is_action('create')) {
66
+ $page = array();
67
+ $page['post_title'] = 'Newsletter';
68
+ $page['post_content'] = '[newsletter]';
69
+ $page['post_status'] = 'publish';
70
+ $page['post_type'] = 'page';
71
+ $page['comment_status'] = 'closed';
72
+ $page['ping_status'] = 'closed';
73
+ $page['post_category'] = array(1);
74
+
75
+ $current_language = $this->get_current_language();
76
+ $this->switch_language('');
77
+ // Insert the post into the database
78
+ $page_id = wp_insert_post($page);
79
+ $this->switch_language($current_language);
80
+
81
+ $controls->data['page'] = $page_id;
82
+ $this->merge_options($controls->data);
83
+
84
+ $controls->messages = 'A new page has been created';
85
+ }
86
+ }
87
+
88
+ $license_data = $this->get_license_data(true);
89
+
90
+ if (is_wp_error($license_data)) {
91
+ $controls->errors .= esc_html('[' . $license_data->get_error_code()) . '] - ' . esc_html($license_data->get_error_message());
92
+ } else {
93
+ if ($license_data !== false) {
94
+ if ($license_data->expire == 0) {
95
+ $controls->messages = 'Your FREE license is valid';
96
+ } elseif ($license_data->expire >= time()) {
97
+ $controls->messages = 'Your license is valid and expires on ' . esc_html(date('Y-m-d', $license_data->expire));
98
+ } else {
99
+ $controls->errors = 'Your license is expired on ' . esc_html(date('Y-m-d', $license_data->expire));
100
+ }
101
+ }
102
+ }
103
+
104
+ $return_path = $this->options['return_path'];
105
+
106
+ if (!empty($return_path)) {
107
+ list($return_path_local, $return_path_domain) = explode('@', $return_path);
108
+
109
+ $sender = $this->options['sender_email'];
110
+ list($sender_local, $sender_domain) = explode('@', $sender);
111
+
112
+ if ($sender_domain != $return_path_domain) {
113
+ $controls->warnings[] = __('Your Return Path domain is different from your Sender domain. Providers may require them to match.', 'newsletter');
114
+ }
115
+ }
116
+ ?>
117
+
118
+ <?php include NEWSLETTER_INCLUDES_DIR . '/codemirror.php'; ?>
119
+ <style>
120
+ .CodeMirror {
121
+ border: 1px solid #ddd;
122
+ }
123
+ </style>
124
+
125
+ <script>
126
+ jQuery(function () {
127
+ var editor = CodeMirror.fromTextArea(document.getElementById("options-css"), {
128
+ lineNumbers: true,
129
+ mode: 'css',
130
+ extraKeys: {"Ctrl-Space": "autocomplete"}
131
+ });
132
+ });
133
+ </script>
134
+
135
+ <div class="wrap" id="tnp-wrap">
136
+
137
+ <?php include NEWSLETTER_DIR . '/tnp-header.php'; ?>
138
+
139
+ <div id="tnp-heading">
140
+
141
+ <h2><?php _e('General Settings', 'newsletter') ?></h2>
142
+
143
+ </div>
144
+ <div id="tnp-body" class="tnp-main-main">
145
+
146
+
147
+ <form method="post" action="">
148
+ <?php $controls->init(); ?>
149
+
150
+ <div id="tabs">
151
+
152
+ <ul>
153
+ <li><a href="#tabs-basic"><?php _e('Basic Settings', 'newsletter') ?></a></li>
154
+ <li><a href="#tabs-speed"><?php _e('Delivery Speed', 'newsletter') ?></a></li>
155
+ <li><a href="#tabs-advanced"><?php _e('Advanced Settings', 'newsletter') ?></a></li>
156
+ </ul>
157
+
158
+ <div id="tabs-basic">
159
+
160
+ <p>
161
+ <?php $controls->panel_help('https://www.thenewsletterplugin.com/plugins/newsletter/newsletter-configuration') ?>
162
+ </p>
163
+
164
+
165
+ <table class="form-table">
166
+
167
+ <tr>
168
+ <th>
169
+ <?php _e('Sender email address', 'newsletter') ?>
170
+ <?php $controls->field_help('https://www.thenewsletterplugin.com/documentation/installation/newsletter-configuration/#sender') ?>
171
+ </th>
172
+ <td>
173
+ <?php $controls->text_email('sender_email', 40); ?>
174
+
175
+ </td>
176
+ </tr>
177
+ <tr>
178
+ <th>
179
+ <?php _e('Sender name', 'newsletter') ?>
180
+ </th>
181
+ <td>
182
+ <?php $controls->text('sender_name', 40); ?>
183
+ </td>
184
+ </tr>
185
+
186
+ <tr>
187
+ <th>
188
+ <?php _e('Return path', 'newsletter') ?>
189
+ <?php $controls->field_help('https://www.thenewsletterplugin.com/documentation/installation/newsletter-configuration/#return-path') ?>
190
+ </th>
191
+ <td>
192
+ <?php $controls->text_email('return_path', 40); ?>
193
+
194
+ </td>
195
+ </tr>
196
+ <tr>
197
+ <th>
198
+ <?php _e('Reply to', 'newsletter') ?>
199
+ <?php $controls->field_help('https://www.thenewsletterplugin.com/documentation/installation/newsletter-configuration/#reply-to') ?>
200
+ </th>
201
+ <td>
202
+ <?php $controls->text_email('reply_to', 40); ?>
203
+
204
+ </td>
205
+ </tr>
206
+ <tr>
207
+ <th>
208
+ <?php _e('Dedicated page', 'newsletter') ?>
209
+ <?php $controls->field_help('https://www.thenewsletterplugin.com/documentation/installation/newsletter-configuration/#dedicated-page') ?>
210
+ </th>
211
+ <td>
212
+ <?php $controls->page('page', __('Unstyled page', 'newsletter'), '', true); ?>
213
+ <?php
214
+ if (empty($controls->data['page'])) {
215
+ $controls->button('create', __('Create the page', 'newsletter'));
216
+ }
217
+ ?>
218
+ </td>
219
+ </tr>
220
+
221
+ <tr>
222
+ <th><?php _e('License key', 'newsletter') ?></th>
223
+ <td>
224
+ <?php if (defined('NEWSLETTER_LICENSE_KEY')) { ?>
225
+ <?php _e('A license key is set', 'newsletter') ?>
226
+ <?php } else { ?>
227
+ <?php $controls->text('contract_key', 40); ?>
228
+ <p class="description">
229
+ <?php printf(__('Find it in <a href="%s" target="_blank">your account</a> page', 'newsletter'), "https://www.thenewsletterplugin.com/account") ?>
230
+ </p>
231
+ <?php } ?>
232
+ </td>
233
+ </tr>
234
+
235
+ </table>
236
+ </div>
237
+
238
+ <div id="tabs-speed">
239
+
240
+ <table class="form-table">
241
+ <tr>
242
+ <th>
243
+ <?php _e('Max emails per hour', 'newsletter') ?>
244
+ <?php $controls->field_help('https://www.thenewsletterplugin.com/documentation/installation/newsletter-configuration/#speed') ?>
245
+ </th>
246
+ <td>
247
+ <?php $controls->text('scheduler_max', 5); ?> (min. 10)
248
+ <p class="description">
249
+ <a href="?page=newsletter_main_status#tnp-speed">See the collected statistics</a>
250
+ </td>
251
+ </tr>
252
+ </table>
253
+
254
+ <?php do_action('newsletter_panel_main_speed', $controls) ?>
255
+ </div>
256
+
257
+
258
+ <div id="tabs-advanced">
259
+
260
+ <p>
261
+ <?php $controls->panel_help('https://www.thenewsletterplugin.com/plugins/newsletter/newsletter-configuration#advanced') ?>
262
+ </p>
263
+
264
+ <table class="form-table">
265
+ <tr>
266
+ <th><?php _e('Allowed roles', 'newsletter') ?></th>
267
+ <td>
268
+ <?php
269
+ $wp_roles = get_editable_roles();
270
+ $roles = array();
271
+ foreach ($wp_roles as $key => $wp_role) {
272
+ if ($key == 'administrator')
273
+ continue;
274
+ if ($key == 'subscriber')
275
+ continue;
276
+ $roles[$key] = $wp_role['name'];
277
+ }
278
+ $controls->checkboxes('roles', $roles);
279
+ ?>
280
+
281
+ </td>
282
+ </tr>
283
+
284
+ <tr>
285
+ <th>
286
+ <?php _e('Tracking default', 'newsletter') ?>
287
+ <?php $controls->field_help('https://www.thenewsletterplugin.com/plugins/newsletter/newsletter-configuration#tracking') ?>
288
+ </th>
289
+ <td>
290
+ <?php $controls->yesno('track'); ?>
291
+ </td>
292
+ </tr>
293
+
294
+ <tr>
295
+ <th>
296
+ <?php _e('Execute shortcodes on newsletters', 'newsletter') ?>
297
+ <?php $controls->field_help("https://www.thenewsletterplugin.com/documentation/newsletter-configuration#shortcodes") ?>
298
+ </th>
299
+ <td>
300
+ <?php $controls->yesno('do_shortcodes', 40); ?>
301
+ </td>
302
+ </tr>
303
+
304
+ <tr>
305
+ <th>
306
+ <?php _e('Log level', 'newsletter') ?>
307
+ <?php $controls->field_help('https://www.thenewsletterplugin.com/plugins/newsletter/newsletter-configuration#log') ?>
308
+ </th>
309
+ <td>
310
+ <?php $controls->log_level('log_level'); ?>
311
+ </td>
312
+ </tr>
313
+
314
+ <tr>
315
+ <th><?php _e('Standard styles', 'newsletter') ?></th>
316
+ <td>
317
+ <?php $controls->disabled('css_disabled'); ?>
318
+ </td>
319
+ </tr>
320
+
321
+ <tr>
322
+ <th><?php _e('Custom styles', 'newsletter') ?></th>
323
+ <td>
324
+ <?php if (apply_filters('newsletter_enqueue_style', true) === false) { ?>
325
+ <p><strong>Warning: Newsletter styles and custom styles are disable by your theme or a plugin.</strong></p>
326
+ <?php } ?>
327
+ <?php $controls->textarea('css'); ?>
328
+ </td>
329
+ </tr>
330
+
331
+
332
+ <tr>
333
+ <th><?php _e('IP addresses', 'newsletter') ?></th>
334
+ <td>
335
+ <?php $controls->select('ip', array('' => __('Store', 'newsletter'), 'anonymize' => __('Anonymize', 'newsletter'), 'skip' => __('Do not store', 'newsletter'))); ?>
336
+ </td>
337
+ </tr>
338
+
339
+
340
+
341
+ <tr>
342
+ <th>
343
+ <?php _e('Debug mode', 'newsletter') ?>
344
+ <?php $controls->field_help('https://www.thenewsletterplugin.com/plugins/newsletter/newsletter-configuration#debug') ?>
345
+ </th>
346
+ <td>
347
+ <?php $controls->yesno('debug', 40); ?>
348
+ </td>
349
+ </tr>
350
+
351
+ <tr>
352
+ <th>
353
+ <?php _e('Email encoding', 'newsletter') ?>
354
+ <?php $controls->field_help('https://www.thenewsletterplugin.com/plugins/newsletter/newsletter-configuration#encoding') ?>
355
+ </th>
356
+ <td>
357
+ <?php $controls->select('content_transfer_encoding', array('' => 'Default', '8bit' => '8 bit', 'base64' => 'Base 64', 'binary' => 'Binary', 'quoted-printable' => 'Quoted printable', '7bit' => '7 bit')); ?>
358
+ </td>
359
+ </tr>
360
+
361
+
362
+ </table>
363
+
364
+ </div>
365
+
366
+
367
+ </div> <!-- tabs -->
368
+
369
+ <div class="tnp-buttons">
370
+ <?php $controls->button_save(); ?>
371
+ </div>
372
+
373
+ </form>
374
+
375
+ </div>
376
+
377
+ <?php include NEWSLETTER_DIR . '/tnp-footer.php'; ?>
378
+
379
+ </div>
380
+
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.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,7 +37,7 @@ if (version_compare(phpversion(), '5.6', '<')) {
37
  return;
38
  }
39
 
40
- define('NEWSLETTER_VERSION', '7.2.9');
41
 
42
  global $newsletter, $wpdb;
43
 
@@ -134,7 +134,7 @@ class Newsletter extends NewsletterModule {
134
 
135
  $this->time_start = time();
136
 
137
- parent::__construct('main', '1.6.6', null, array('info', 'smtp'));
138
 
139
  $max = $this->options['scheduler_max'];
140
  if (!is_numeric($max)) {
@@ -327,6 +327,7 @@ class Newsletter extends NewsletterModule {
327
  `unsub_count` int(10) unsigned NOT NULL DEFAULT '0',
328
  `error_count` int(10) unsigned NOT NULL DEFAULT '0',
329
  `stats_time` int(10) unsigned NOT NULL DEFAULT '0',
 
330
  PRIMARY KEY (`id`)
331
  ) $charset_collate;";
332
 
4
  Plugin Name: Newsletter
5
  Plugin URI: https://www.thenewsletterplugin.com/plugins/newsletter
6
  Description: Newsletter is a cool plugin to create your own subscriber list, to send newsletters, to build your business. <strong>Before update give a look to <a href="https://www.thenewsletterplugin.com/category/release">this page</a> to know what's changed.</strong>
7
+ Version: 7.3.0
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.3.0');
41
 
42
  global $newsletter, $wpdb;
43
 
134
 
135
  $this->time_start = time();
136
 
137
+ parent::__construct('main', '1.6.7', null, array('info', 'smtp'));
138
 
139
  $max = $this->options['scheduler_max'];
140
  if (!is_numeric($max)) {
327
  `unsub_count` int(10) unsigned NOT NULL DEFAULT '0',
328
  `error_count` int(10) unsigned NOT NULL DEFAULT '0',
329
  `stats_time` int(10) unsigned NOT NULL DEFAULT '0',
330
+ `updated` int(10) unsigned NOT NULL DEFAULT '0',
331
  PRIMARY KEY (`id`)
332
  ) $charset_collate;";
333
 
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.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,6 +118,12 @@ Thank you, The Newsletter Team
118
 
119
  == Changelog ==
120
 
 
 
 
 
 
 
121
  = 7.2.9 =
122
 
123
  * Fixed generic action button confirmation popup not showing the message
1
  === Newsletter ===
2
  Tags: newsletter, email marketing, welcome email, signup forms, contact, lead generation, marketing automation
3
  Tested up to: 5.8.1
4
+ Stable tag: 7.3.0
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.3.0 =
122
+
123
+ * Fixed header block layout with (logo only layout)
124
+ * Check for conflicts on newsletter saving
125
+ * Added the subscriber complained status (actually not managed automatically)
126
+
127
  = 7.2.9 =
128
 
129
  * Fixed generic action button confirmation popup not showing the message
statistics/index.php CHANGED
@@ -64,7 +64,7 @@ if (empty($emails)) {
64
  <?php include NEWSLETTER_DIR . '/tnp-header.php' ?>
65
  <div id="tnp-heading">
66
  <h2><?php _e('Overall basic statistics (last 20 newsletters)', 'newsletter') ?></h2>
67
- <p>More details, including Automated and Autoresponder newsletter statistics are available with the <a href="https://www.thenewsletterplugin.com/reports?utm_source=plugin&utm_campaign=newsletter-reports" target="_blank">Reports Addon</a>.</p>
68
  </div>
69
 
70
  <div id="tnp-body" class="tnp-statistics">
64
  <?php include NEWSLETTER_DIR . '/tnp-header.php' ?>
65
  <div id="tnp-heading">
66
  <h2><?php _e('Overall basic statistics (last 20 newsletters)', 'newsletter') ?></h2>
67
+ <p>More details, including Automated and Autoresponder newsletter statistics are available with the <a href="https://www.thenewsletterplugin.com/reports?utm_source=reports&utm_campaign=plugin" target="_blank">Reports Addon</a>.</p>
68
  </div>
69
 
70
  <div id="tnp-body" class="tnp-statistics">
statistics/view.php CHANGED
@@ -73,7 +73,7 @@ if (empty($email->track)) {
73
  <div class="tnp-widget tnp-inactive">
74
  <h3>Clicked URLs</h3>
75
  <div class="tnp-placeholder">
76
- <a href="https://www.thenewsletterplugin.com/premium?utm_source=plugin&utm_medium=link&utm_content=worldmap&utm_campaign=newsletter-reports" target="_blank">
77
  <img src="<?php echo plugins_url('newsletter') ?>/statistics/images/clicked-urls@2x.png">
78
  </a>
79
  </div>
@@ -84,7 +84,7 @@ if (empty($email->track)) {
84
  <div class="tnp-widget tnp-inactive">
85
  <h3>World Map</h3>
86
  <div class="tnp-placeholder">
87
- <a href="https://www.thenewsletterplugin.com/premium?utm_source=plugin&utm_medium=link&utm_content=worldmap&utm_campaign=newsletter-reports" target="_blank">
88
  <img src="<?php echo plugins_url('newsletter') ?>/statistics/images/world-map@2x.png">
89
  </a>
90
  </div>
@@ -96,7 +96,7 @@ if (empty($email->track)) {
96
  <div class="tnp-widget tnp-inactive">
97
  <h3>Interactions</h3>
98
  <div class="tnp-placeholder">
99
- <a href="https://www.thenewsletterplugin.com/premium?utm_source=plugin&utm_medium=link&utm_content=worldmap&utm_campaign=newsletter-reports" target="_blank">
100
  <img src="<?php echo plugins_url('newsletter') ?>/statistics/images/interactions@2x.png">
101
  </a>
102
  </div>
73
  <div class="tnp-widget tnp-inactive">
74
  <h3>Clicked URLs</h3>
75
  <div class="tnp-placeholder">
76
+ <a href="https://www.thenewsletterplugin.com/premium?utm_source=reports&utm_medium=urls&utm_campaign=plugin" target="_blank">
77
  <img src="<?php echo plugins_url('newsletter') ?>/statistics/images/clicked-urls@2x.png">
78
  </a>
79
  </div>
84
  <div class="tnp-widget tnp-inactive">
85
  <h3>World Map</h3>
86
  <div class="tnp-placeholder">
87
+ <a href="https://www.thenewsletterplugin.com/premium?utm_source=reports&utm_medium=map&utm_campaign=plugin" target="_blank">
88
  <img src="<?php echo plugins_url('newsletter') ?>/statistics/images/world-map@2x.png">
89
  </a>
90
  </div>
96
  <div class="tnp-widget tnp-inactive">
97
  <h3>Interactions</h3>
98
  <div class="tnp-placeholder">
99
+ <a href="https://www.thenewsletterplugin.com/premium?utm_source=reports&utm_medium=interactions&utm_campaign=plugin" target="_blank">
100
  <img src="<?php echo plugins_url('newsletter') ?>/statistics/images/interactions@2x.png">
101
  </a>
102
  </div>
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=header&utm_medium=link&utm_campaign=plugin&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/edit.php CHANGED
@@ -146,30 +146,37 @@ function percentValue($value, $total) {
146
  <tr>
147
  <th><?php _e('Status', 'newsletter'); ?></th>
148
  <td>
149
- <?php $controls->select('status', array('C' => __('Confirmed', 'newsletter'), 'S' => __('Not confirmed', 'newsletter'),
150
- 'U' => __('Unsubscribed', 'newsletter'), 'B' => __('Bounced', 'newsletter'))); ?>
 
 
 
 
 
 
 
151
  </td>
152
  </tr>
153
  <tr>
154
  <th><?php _e('Language', 'newsletter'); ?></th>
155
  <td>
156
- <?php $controls->language('language', __('None', 'newsletter') ); ?>
157
  </td>
158
  </tr>
159
  <tr>
160
  <th><?php _e('Test subscriber', 'newsletter'); ?>
161
  <br><?php $controls->help('https://www.thenewsletterplugin.com/documentation/subscribers#test-subscribers') ?></th>
162
  <td>
163
- <?php $controls->yesno('test'); ?>
164
  </td>
165
  </tr>
166
 
167
- <?php do_action('newsletter_user_edit_extra', $controls); ?>
168
 
169
  <tr>
170
  <th>Feed by mail</th>
171
  <td>
172
- <?php $controls->yesno('feed'); ?>
173
  </td>
174
  </tr>
175
  </table>
@@ -179,7 +186,7 @@ function percentValue($value, $total) {
179
  <tr>
180
  <th><?php _e('Lists', 'newsletter') ?><br><?php echo $controls->help('https://www.thenewsletterplugin.com/plugins/newsletter/newsletter-preferences') ?></th>
181
  <td>
182
- <?php $controls->preferences('list'); ?>
183
  </td>
184
  </tr>
185
  </table>
@@ -217,49 +224,49 @@ function percentValue($value, $total) {
217
  <tr>
218
  <th>ID</th>
219
  <td>
220
- <?php $controls->value('id'); ?>
221
  </td>
222
  </tr>
223
  <tr>
224
  <th><?php _e('Created', 'newsletter') ?></th>
225
  <td>
226
- <?php echo $controls->print_date(strtotime($controls->data['created'])); ?>
227
  </td>
228
  </tr>
229
  <tr>
230
  <th><?php _e('Referrer', 'newsletter') ?></th>
231
  <td>
232
- <?php echo $controls->value('referrer'); ?>
233
  </td>
234
  </tr>
235
  <tr>
236
  <th><?php _e('Last activity', 'newsletter') ?></th>
237
  <td>
238
- <?php echo $controls->print_date($controls->data['last_activity']); ?>
239
  </td>
240
  </tr>
241
  <tr>
242
  <th><?php _e('WP user ID', 'newsletter') ?></th>
243
  <td>
244
- <?php $controls->text('wp_user_id'); ?>
245
  </td>
246
  </tr>
247
  <tr>
248
  <th><?php _e('IP address', 'newsletter'); ?></th>
249
  <td>
250
- <?php $controls->value('ip'); ?>
251
  </td>
252
  </tr>
253
  <tr>
254
  <th><?php _e('Secret token', 'newsletter'); ?></th>
255
  <td>
256
- <?php $controls->text('token', 50); ?>
257
  </td>
258
  </tr>
259
  <tr>
260
  <th><?php _e('Profile URL', 'newsletter'); ?></th>
261
  <td>
262
- <?php $profile_url = NewsletterProfile::instance()->get_profile_url($user) ?>
263
  <a href='<?php echo $profile_url ?>' target="_blank"><?php echo $profile_url ?></a>
264
  </td>
265
  </tr>
@@ -267,7 +274,7 @@ function percentValue($value, $total) {
267
  </table>
268
  </div>
269
  <div id="tabs-newsletters" class="tnp-tab">
270
- <?php if (!has_action('newsletter_user_newsletters_tab') && !has_action('newsletter_users_edit_newsletters')) { ?>
271
  <div class="tnp-tab-notice">
272
  This panel requires the <a href="https://www.thenewsletterplugin.com/plugins/newsletter/reports-module" target="_blank">Reports Extension 4+</a>.
273
  </div>
@@ -285,7 +292,7 @@ function percentValue($value, $total) {
285
  ?>
286
  <?php if (empty($logs)) { ?>
287
  <p>No logs available</p>
288
- <?php } else { ?>
289
  <p>Only public lists are recorded.</p>
290
  <table class="widefat" style="width: auto">
291
  <thead>
146
  <tr>
147
  <th><?php _e('Status', 'newsletter'); ?></th>
148
  <td>
149
+ <?php
150
+ $controls->select('status', [
151
+ 'C' => TNP_User::get_status_label('C'),
152
+ 'S' => TNP_User::get_status_label('S'),
153
+ 'U' => TNP_User::get_status_label('U'),
154
+ 'B' => TNP_User::get_status_label('B'),
155
+ 'P' => TNP_User::get_status_label('P')
156
+ ]);
157
+ ?>
158
  </td>
159
  </tr>
160
  <tr>
161
  <th><?php _e('Language', 'newsletter'); ?></th>
162
  <td>
163
+ <?php $controls->language('language', __('None', 'newsletter')); ?>
164
  </td>
165
  </tr>
166
  <tr>
167
  <th><?php _e('Test subscriber', 'newsletter'); ?>
168
  <br><?php $controls->help('https://www.thenewsletterplugin.com/documentation/subscribers#test-subscribers') ?></th>
169
  <td>
170
+ <?php $controls->yesno('test'); ?>
171
  </td>
172
  </tr>
173
 
174
+ <?php do_action('newsletter_user_edit_extra', $controls); ?>
175
 
176
  <tr>
177
  <th>Feed by mail</th>
178
  <td>
179
+ <?php $controls->yesno('feed'); ?>
180
  </td>
181
  </tr>
182
  </table>
186
  <tr>
187
  <th><?php _e('Lists', 'newsletter') ?><br><?php echo $controls->help('https://www.thenewsletterplugin.com/plugins/newsletter/newsletter-preferences') ?></th>
188
  <td>
189
+ <?php $controls->preferences('list'); ?>
190
  </td>
191
  </tr>
192
  </table>
224
  <tr>
225
  <th>ID</th>
226
  <td>
227
+ <?php $controls->value('id'); ?>
228
  </td>
229
  </tr>
230
  <tr>
231
  <th><?php _e('Created', 'newsletter') ?></th>
232
  <td>
233
+ <?php echo $controls->print_date(strtotime($controls->data['created'])); ?>
234
  </td>
235
  </tr>
236
  <tr>
237
  <th><?php _e('Referrer', 'newsletter') ?></th>
238
  <td>
239
+ <?php echo $controls->value('referrer'); ?>
240
  </td>
241
  </tr>
242
  <tr>
243
  <th><?php _e('Last activity', 'newsletter') ?></th>
244
  <td>
245
+ <?php echo $controls->print_date($controls->data['last_activity']); ?>
246
  </td>
247
  </tr>
248
  <tr>
249
  <th><?php _e('WP user ID', 'newsletter') ?></th>
250
  <td>
251
+ <?php $controls->text('wp_user_id'); ?>
252
  </td>
253
  </tr>
254
  <tr>
255
  <th><?php _e('IP address', 'newsletter'); ?></th>
256
  <td>
257
+ <?php $controls->value('ip'); ?>
258
  </td>
259
  </tr>
260
  <tr>
261
  <th><?php _e('Secret token', 'newsletter'); ?></th>
262
  <td>
263
+ <?php $controls->text('token', 50); ?>
264
  </td>
265
  </tr>
266
  <tr>
267
  <th><?php _e('Profile URL', 'newsletter'); ?></th>
268
  <td>
269
+ <?php $profile_url = NewsletterProfile::instance()->get_profile_url($user) ?>
270
  <a href='<?php echo $profile_url ?>' target="_blank"><?php echo $profile_url ?></a>
271
  </td>
272
  </tr>
274
  </table>
275
  </div>
276
  <div id="tabs-newsletters" class="tnp-tab">
277
+ <?php if (!has_action('newsletter_user_newsletters_tab') && !has_action('newsletter_users_edit_newsletters')) { ?>
278
  <div class="tnp-tab-notice">
279
  This panel requires the <a href="https://www.thenewsletterplugin.com/plugins/newsletter/reports-module" target="_blank">Reports Extension 4+</a>.
280
  </div>
292
  ?>
293
  <?php if (empty($logs)) { ?>
294
  <p>No logs available</p>
295
+ <?php } else { ?>
296
  <p>Only public lists are recorded.</p>
297
  <table class="widefat" style="width: auto">
298
  <thead>
users/index.php CHANGED
@@ -200,7 +200,7 @@ $controls->data['search_page'] ++;
200
 
201
  <td>
202
  <small>
203
- <?php echo $this->get_user_status_label($s) ?>
204
  </small>
205
  </td>
206
 
200
 
201
  <td>
202
  <small>
203
+ <?php echo $this->get_user_status_label($s, true) ?>
204
  </small>
205
  </td>
206
 
users/massive.php CHANGED
@@ -1,281 +1,296 @@
1
- <?php
2
- /* @var $wpdb wpdb */
3
- /* @var $this NewsletterUsers */
4
-
5
- defined('ABSPATH') || exit;
6
-
7
- include_once NEWSLETTER_INCLUDES_DIR . '/controls.php';
8
-
9
- $controls = new NewsletterControls();
10
-
11
- if ($controls->is_action('remove_unconfirmed')) {
12
- $r = $wpdb->query("delete from " . NEWSLETTER_USERS_TABLE . " where status='S'");
13
- $controls->messages = __('Subscribers not confirmed deleted: ', 'newsletter') . $r . '.';
14
- }
15
-
16
- if ($controls->is_action('remove_unsubscribed')) {
17
- $r = $wpdb->query("delete from " . NEWSLETTER_USERS_TABLE . " where status='U'");
18
- $controls->messages = __('Subscribers unsubscribed deleted: ', 'newsletter') . $r . '.';
19
- }
20
-
21
- if ($controls->is_action('remove_bounced')) {
22
- $r = $wpdb->query("delete from " . NEWSLETTER_USERS_TABLE . " where status='B'");
23
- $controls->messages = __('Subscribers bounced deleted: ', 'newsletter') . $r . '.';
24
- }
25
-
26
- if ($controls->is_action('unconfirm_all')) {
27
- $r = $wpdb->query("update " . NEWSLETTER_USERS_TABLE . " set status='S' where status='C'");
28
- $controls->messages = __('Subscribers changed to not confirmed: ', 'newsletter') . $r . '.';
29
- }
30
-
31
- if ($controls->is_action('confirm_all')) {
32
- $r = $wpdb->query("update " . NEWSLETTER_USERS_TABLE . " set status='C' where status='S'");
33
- $controls->messages = __('Subscribers changed to confirmed: ', 'newsletter') . $r . '.';
34
- }
35
-
36
- if ($controls->is_action('remove_all')) {
37
- $r = $wpdb->query("delete from " . NEWSLETTER_USERS_TABLE);
38
- $controls->messages = __('Subscribers deleted: ', 'newsletter') . $r . '.';
39
- }
40
-
41
- if ($controls->is_action('list_add')) {
42
- $r = $wpdb->query("update " . NEWSLETTER_USERS_TABLE . " set list_" . ((int) $controls->data['list']) . "=1");
43
- $controls->messages = $r . ' ' . __('added to list', 'newsletter') . ' ' . $controls->data['list'];
44
- }
45
-
46
- if ($controls->is_action('list_remove')) {
47
- $r = $wpdb->query("update " . NEWSLETTER_USERS_TABLE . " set list_" . ((int) $controls->data['list']) . "=0");
48
- $controls->messages = $r . ' ' . __('removed from list', 'newsletter') . ' ' . $controls->data['list'];
49
- }
50
-
51
- if ($controls->is_action('list_delete')) {
52
- $count = $wpdb->query("delete from " . NEWSLETTER_USERS_TABLE . " where list_" . ((int) $controls->data['list']) . "<>0");
53
- $this->clean_sent_table();
54
- $this->clean_stats_table();
55
-
56
- $controls->messages = $count . ' ' . __('deleted', 'newsletter');
57
- }
58
-
59
- if ($controls->is_action('language')) {
60
- $count = $wpdb->query($wpdb->prepare("update " . NEWSLETTER_USERS_TABLE . " set language=%s where language=''", $controls->data['language']));
61
- $controls->add_message_done();
62
- }
63
-
64
- if ($controls->is_action('list_manage')) {
65
- if ($controls->data['list_action'] == 'move') {
66
- $wpdb->query("update " . NEWSLETTER_USERS_TABLE . ' set list_' . ((int) $controls->data['list_1']) . '=0, list_' . ((int) $controls->data['list_2']) . '=1' .
67
- ' where list_' . $controls->data['list_1'] . '=1');
68
- }
69
-
70
- if ($controls->data['list_action'] == 'add') {
71
- $wpdb->query("update " . NEWSLETTER_USERS_TABLE . ' set list_' . ((int) $controls->data['list_2']) . '=1' .
72
- ' where list_' . $controls->data['list_1'] . '=1');
73
- }
74
- }
75
-
76
- if ($controls->is_action('list_none')) {
77
- $where = '1=1';
78
-
79
- for ($i = 1; $i <= NEWSLETTER_LIST_MAX; $i++) {
80
- $where .= ' and list_' . $i . '=0';
81
- }
82
-
83
- $count = $wpdb->query("update " . NEWSLETTER_USERS_TABLE . ' set list_' . ((int) $controls->data['list_3']) . '=1' .
84
- ' where ' . $where);
85
- $controls->messages = $count . ' subscribers updated';
86
- }
87
-
88
- if ($controls->is_action('update_inactive')) {
89
- //Update users 'last_activity' column
90
- $wpdb->query("update `{$wpdb->prefix}newsletter` n join (select user_id, max(s.time) as max_time from `{$wpdb->prefix}newsletter_sent` s where s.open>0 group by user_id) as ss
91
- on n.id=ss.user_id set last_activity=ss.max_time");
92
-
93
- $inactive_time = (int) $controls->data['inactive_time'];
94
-
95
- $where = 'last_activity > 0 and last_activity<' . (time() - $inactive_time * 30 * 24 * 3600);
96
-
97
- $count = $wpdb->query("update " . NEWSLETTER_USERS_TABLE . ' set list_' . ((int) $controls->data['list_inactive']) . '=1 where ' . $where);
98
- $controls->messages = $count . ' subscribers updated';
99
- }
100
- ?>
101
-
102
- <div class="wrap tnp-users tnp-users-massive" id="tnp-wrap">
103
-
104
- <?php include NEWSLETTER_DIR . '/tnp-header.php'; ?>
105
-
106
- <div id="tnp-heading">
107
-
108
- <h2><?php _e('Subscribers Maintenance', 'newsletter') ?></h2>
109
- <p><?php _e('Please, backup before run a massive action.', 'newsletter') ?></p>
110
-
111
- </div>
112
-
113
- <div id="tnp-body">
114
-
115
- <?php if (!empty($results)) { ?>
116
-
117
- <h3>Results</h3>
118
-
119
- <textarea wrap="off" style="width: 100%; height: 150px; font-size: 11px; font-family: monospace"><?php echo htmlspecialchars($results) ?></textarea>
120
-
121
- <?php } ?>
122
-
123
-
124
- <form method="post" action="">
125
- <?php $controls->init(); ?>
126
-
127
- <div id="tabs">
128
- <ul>
129
- <li><a href="#tabs-1"><?php _e('General', 'newsletter') ?></a></li>
130
- <li><a href="#tabs-2"><?php _e('Lists', 'newsletter') ?></a></li>
131
- </ul>
132
-
133
- <div id="tabs-1">
134
- <table class="widefat" style="width: auto">
135
- <thead>
136
- <tr>
137
- <th><?php _e('Status', 'newsletter') ?></th>
138
- <th><?php _e('Total', 'newsletter') ?></th>
139
- <th><?php _e('Actions', 'newsletter') ?></th>
140
- </tr>
141
- </thead>
142
- <tr>
143
- <td><?php _e('Total', 'newsletter') ?></td>
144
- <td>
145
- <?php echo $wpdb->get_var("select count(*) from " . NEWSLETTER_USERS_TABLE); ?>
146
- </td>
147
- <td nowrap>
148
- <?php $controls->button_confirm('remove_all', __('Delete all', 'newsletter'), __('Are you sure you want to remove ALL subscribers?', 'newsletter')); ?>
149
- </td>
150
- </tr>
151
- <tr>
152
- <td><?php _e('Confirmed', 'newsletter') ?></td>
153
- <td>
154
- <?php echo $wpdb->get_var("select count(*) from " . NEWSLETTER_USERS_TABLE . " where status='C'"); ?>
155
- </td>
156
- <td nowrap>
157
- <?php $controls->button_confirm('unconfirm_all', __('Unconfirm all', 'newsletter')); ?>
158
- </td>
159
- </tr>
160
- <tr>
161
- <td>Not confirmed</td>
162
- <td>
163
- <?php echo $wpdb->get_var("select count(*) from " . NEWSLETTER_USERS_TABLE . " where status='S'"); ?>
164
- </td>
165
- <td nowrap>
166
- <?php $controls->button_confirm('remove_unconfirmed', __('Delete all not confirmed', 'newsletter'), __('Are you sure you want to delete ALL not confirmed subscribers?', 'newsletter')); ?>
167
- <?php $controls->button_confirm('confirm_all', __('Confirm all', 'newsletter'), __('Are you sure you want to mark ALL subscribers as confirmed?', 'newsletter')); ?>
168
- <p class="description">
169
- <a href="https://www.thenewsletterplugin.com/plugins/newsletter/subscribers-module#resend-activation" target="_blank"><?php _e('We have some tips about global actions, read more.', 'newsletter') ?></a>
170
- </p>
171
- </td>
172
- </tr>
173
- <tr>
174
- <td><?php _e('Unsubscribed', 'newsletter') ?></td>
175
- <td>
176
- <?php echo $wpdb->get_var("select count(*) from " . NEWSLETTER_USERS_TABLE . " where status='U'"); ?>
177
- </td>
178
- <td>
179
- <?php $controls->button_confirm('remove_unsubscribed', __('Delete all unsubscribed', 'newsletter')); ?>
180
- </td>
181
- </tr>
182
-
183
- <tr>
184
- <td><?php _e('Bounced', 'newsletter') ?></td>
185
- <td>
186
- <?php echo $wpdb->get_var("select count(*) from " . NEWSLETTER_USERS_TABLE . " where status='B'"); ?>
187
- </td>
188
- <td>
189
- <?php $controls->button_confirm('remove_bounced', __('Delete all bounced', 'newsletter')); ?>
190
- </td>
191
- </tr>
192
- <tr>
193
- <td>
194
- <?php _e('Inactive since', 'newsletter') ?>
195
- <?php $controls->field_help('https://www.thenewsletterplugin.com/documentation/subscribers-and-management/subscribers/#inactive')?>
196
- </td>
197
- <td>
198
- <?php
199
- $controls->select('inactive_time', array(
200
- '3' => '3 ' . __('months', 'newsletter'),
201
- '6' => '6 ' . __('months', 'newsletter'),
202
- '12' => '1 ' . __('year', 'newsletter'),
203
- '24' => '2 ' . __('years', 'newsletter'),
204
- '36' => '3 ' . __('years', 'newsletter'),
205
- '48' => '4 ' . __('years', 'newsletter'),
206
- '60' => '5 ' . __('years', 'newsletter'),
207
- '72' => '6 ' . __('years', 'newsletter'),
208
- '84' => '7 ' . __('years', 'newsletter'),
209
- '96' => '8 ' . __('years', 'newsletter'),
210
- '108' => '9 ' . __('years', 'newsletter'),
211
- '120' => '10 ' . __('years', 'newsletter')
212
- ))
213
- ?>
214
- add to
215
- <?php $controls->lists_select('list_inactive'); ?>
216
-
217
- </td>
218
- <td>
219
- <?php $controls->btn('update_inactive', __('Update', 'newsletter'), ['confirm'=>true]); ?>
220
- </td>
221
- </tr>
222
-
223
- <?php if ($this->is_multilanguage()) { ?>
224
- <tr>
225
- <td>Language</td>
226
- <td>
227
- <?php _e('Set to', 'newsletter') ?>
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 } ?>
235
- </table>
236
-
237
-
238
- </div>
239
-
240
-
241
- <div id="tabs-2">
242
- <table class="form-table">
243
- <tr>
244
- <th>&nbsp;</th>
245
- <td>
246
- <?php $controls->lists_select('list') ?>:
247
- <?php $controls->button_confirm('list_add', 'Activate for everyone'); ?>
248
- <?php $controls->button_confirm('list_remove', 'Deactivate for everyone'); ?>
249
- <?php $controls->button_confirm('list_delete', 'Delete everyone in that list'); ?>
250
- <br><br>
251
- <?php $controls->select('list_action', array('move' => 'Change', 'add' => 'Add')); ?>
252
- <?php _e('all subscribers in', 'newsletter') ?> <?php $controls->lists_select('list_1'); ?>
253
- <?php _e('to', 'newsletter') ?> <?php $controls->lists_select('list_2'); ?>
254
- <?php $controls->button_confirm('list_manage', '&raquo;'); ?>
255
- <p class="description">
256
- If you choose to <strong>delete</strong> users in a list, they will be
257
- <strong>physically deleted</strong> from the database (no way back).
258
- </p>
259
- </td>
260
- </tr>
261
- <tr>
262
- <th>&nbsp;</th>
263
- <td>
264
- <?php _e('Add to list', 'newsletter') ?>
265
- <?php $controls->lists_select('list_3') ?> <?php _e('subscribers without a list', 'newsletter') ?> <?php $controls->button_confirm('list_none', '&raquo;'); ?>
266
- </td>
267
- </tr>
268
-
269
-
270
-
271
- </table>
272
- </div>
273
-
274
- </div>
275
-
276
- </form>
277
- </div>
278
-
279
- <?php include NEWSLETTER_DIR . '/tnp-footer.php'; ?>
280
-
281
- </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /* @var $wpdb wpdb */
3
+ /* @var $this NewsletterUsers */
4
+
5
+ defined('ABSPATH') || exit;
6
+
7
+ include_once NEWSLETTER_INCLUDES_DIR . '/controls.php';
8
+
9
+ $controls = new NewsletterControls();
10
+
11
+ if ($controls->is_action('remove_unconfirmed')) {
12
+ $r = $wpdb->query("delete from " . NEWSLETTER_USERS_TABLE . " where status='S'");
13
+ $controls->messages = __('Subscribers not confirmed deleted: ', 'newsletter') . $r . '.';
14
+ }
15
+
16
+ if ($controls->is_action('remove_unsubscribed')) {
17
+ $r = $wpdb->query("delete from " . NEWSLETTER_USERS_TABLE . " where status='U'");
18
+ $controls->messages = __('Subscribers unsubscribed deleted: ', 'newsletter') . $r . '.';
19
+ }
20
+
21
+ if ($controls->is_action('remove_complained')) {
22
+ $r = $wpdb->query("delete from " . NEWSLETTER_USERS_TABLE . " where status='P'");
23
+ $controls->messages = __('Subscribers complained deleted: ', 'newsletter') . $r . '.';
24
+ }
25
+
26
+ if ($controls->is_action('remove_bounced')) {
27
+ $r = $wpdb->query("delete from " . NEWSLETTER_USERS_TABLE . " where status='B'");
28
+ $controls->messages = __('Subscribers bounced deleted: ', 'newsletter') . $r . '.';
29
+ }
30
+
31
+ if ($controls->is_action('unconfirm_all')) {
32
+ $r = $wpdb->query("update " . NEWSLETTER_USERS_TABLE . " set status='S' where status='C'");
33
+ $controls->messages = __('Subscribers changed to not confirmed: ', 'newsletter') . $r . '.';
34
+ }
35
+
36
+ if ($controls->is_action('confirm_all')) {
37
+ $r = $wpdb->query("update " . NEWSLETTER_USERS_TABLE . " set status='C' where status='S'");
38
+ $controls->messages = __('Subscribers changed to confirmed: ', 'newsletter') . $r . '.';
39
+ }
40
+
41
+ if ($controls->is_action('remove_all')) {
42
+ $r = $wpdb->query("delete from " . NEWSLETTER_USERS_TABLE);
43
+ $controls->messages = __('Subscribers deleted: ', 'newsletter') . $r . '.';
44
+ }
45
+
46
+ if ($controls->is_action('list_add')) {
47
+ $r = $wpdb->query("update " . NEWSLETTER_USERS_TABLE . " set list_" . ((int) $controls->data['list']) . "=1");
48
+ $controls->messages = $r . ' ' . __('added to list', 'newsletter') . ' ' . $controls->data['list'];
49
+ }
50
+
51
+ if ($controls->is_action('list_remove')) {
52
+ $r = $wpdb->query("update " . NEWSLETTER_USERS_TABLE . " set list_" . ((int) $controls->data['list']) . "=0");
53
+ $controls->messages = $r . ' ' . __('removed from list', 'newsletter') . ' ' . $controls->data['list'];
54
+ }
55
+
56
+ if ($controls->is_action('list_delete')) {
57
+ $count = $wpdb->query("delete from " . NEWSLETTER_USERS_TABLE . " where list_" . ((int) $controls->data['list']) . "<>0");
58
+ $this->clean_sent_table();
59
+ $this->clean_stats_table();
60
+
61
+ $controls->messages = $count . ' ' . __('deleted', 'newsletter');
62
+ }
63
+
64
+ if ($controls->is_action('language')) {
65
+ $count = $wpdb->query($wpdb->prepare("update " . NEWSLETTER_USERS_TABLE . " set language=%s where language=''", $controls->data['language']));
66
+ $controls->add_message_done();
67
+ }
68
+
69
+ if ($controls->is_action('list_manage')) {
70
+ if ($controls->data['list_action'] == 'move') {
71
+ $wpdb->query("update " . NEWSLETTER_USERS_TABLE . ' set list_' . ((int) $controls->data['list_1']) . '=0, list_' . ((int) $controls->data['list_2']) . '=1' .
72
+ ' where list_' . $controls->data['list_1'] . '=1');
73
+ }
74
+
75
+ if ($controls->data['list_action'] == 'add') {
76
+ $wpdb->query("update " . NEWSLETTER_USERS_TABLE . ' set list_' . ((int) $controls->data['list_2']) . '=1' .
77
+ ' where list_' . $controls->data['list_1'] . '=1');
78
+ }
79
+ }
80
+
81
+ if ($controls->is_action('list_none')) {
82
+ $where = '1=1';
83
+
84
+ for ($i = 1; $i <= NEWSLETTER_LIST_MAX; $i++) {
85
+ $where .= ' and list_' . $i . '=0';
86
+ }
87
+
88
+ $count = $wpdb->query("update " . NEWSLETTER_USERS_TABLE . ' set list_' . ((int) $controls->data['list_3']) . '=1' .
89
+ ' where ' . $where);
90
+ $controls->messages = $count . ' subscribers updated';
91
+ }
92
+
93
+ if ($controls->is_action('update_inactive')) {
94
+ //Update users 'last_activity' column
95
+ $wpdb->query("update `{$wpdb->prefix}newsletter` n join (select user_id, max(s.time) as max_time from `{$wpdb->prefix}newsletter_sent` s where s.open>0 group by user_id) as ss
96
+ on n.id=ss.user_id set last_activity=ss.max_time");
97
+
98
+ $inactive_time = (int) $controls->data['inactive_time'];
99
+
100
+ $where = 'last_activity > 0 and last_activity<' . (time() - $inactive_time * 30 * 24 * 3600);
101
+
102
+ $count = $wpdb->query("update " . NEWSLETTER_USERS_TABLE . ' set list_' . ((int) $controls->data['list_inactive']) . '=1 where ' . $where);
103
+ $controls->messages = $count . ' subscribers updated';
104
+ }
105
+ ?>
106
+
107
+ <div class="wrap tnp-users tnp-users-massive" id="tnp-wrap">
108
+
109
+ <?php include NEWSLETTER_DIR . '/tnp-header.php'; ?>
110
+
111
+ <div id="tnp-heading">
112
+
113
+ <h2><?php _e('Subscribers Maintenance', 'newsletter') ?></h2>
114
+ <p><?php _e('Please, backup before run a massive action.', 'newsletter') ?></p>
115
+
116
+ </div>
117
+
118
+ <div id="tnp-body">
119
+
120
+ <?php if (!empty($results)) { ?>
121
+
122
+ <h3>Results</h3>
123
+
124
+ <textarea wrap="off" style="width: 100%; height: 150px; font-size: 11px; font-family: monospace"><?php echo htmlspecialchars($results) ?></textarea>
125
+
126
+ <?php } ?>
127
+
128
+
129
+ <form method="post" action="">
130
+ <?php $controls->init(); ?>
131
+
132
+ <div id="tabs">
133
+ <ul>
134
+ <li><a href="#tabs-1"><?php _e('General', 'newsletter') ?></a></li>
135
+ <li><a href="#tabs-2"><?php _e('Lists', 'newsletter') ?></a></li>
136
+ </ul>
137
+
138
+ <div id="tabs-1">
139
+ <table class="widefat" style="width: auto">
140
+ <thead>
141
+ <tr>
142
+ <th><?php _e('Status', 'newsletter') ?></th>
143
+ <th><?php _e('Total', 'newsletter') ?></th>
144
+ <th><?php _e('Actions', 'newsletter') ?></th>
145
+ </tr>
146
+ </thead>
147
+ <tr>
148
+ <td><?php _e('Total', 'newsletter') ?></td>
149
+ <td>
150
+ <?php echo $wpdb->get_var("select count(*) from " . NEWSLETTER_USERS_TABLE); ?>
151
+ </td>
152
+ <td nowrap>
153
+ <?php $controls->button_confirm('remove_all', __('Delete all', 'newsletter'), __('Are you sure you want to remove ALL subscribers?', 'newsletter')); ?>
154
+ </td>
155
+ </tr>
156
+ <tr>
157
+ <td><?php _e('Confirmed', 'newsletter') ?></td>
158
+ <td>
159
+ <?php echo $wpdb->get_var("select count(*) from " . NEWSLETTER_USERS_TABLE . " where status='C'"); ?>
160
+ </td>
161
+ <td nowrap>
162
+ <?php $controls->button_confirm('unconfirm_all', __('Unconfirm all', 'newsletter')); ?>
163
+ </td>
164
+ </tr>
165
+ <tr>
166
+ <td>Not confirmed</td>
167
+ <td>
168
+ <?php echo $wpdb->get_var("select count(*) from " . NEWSLETTER_USERS_TABLE . " where status='S'"); ?>
169
+ </td>
170
+ <td nowrap>
171
+ <?php $controls->button_confirm('remove_unconfirmed', __('Delete all not confirmed', 'newsletter'), __('Are you sure you want to delete ALL not confirmed subscribers?', 'newsletter')); ?>
172
+ <?php $controls->button_confirm('confirm_all', __('Confirm all', 'newsletter'), __('Are you sure you want to mark ALL subscribers as confirmed?', 'newsletter')); ?>
173
+ <p class="description">
174
+ <a href="https://www.thenewsletterplugin.com/plugins/newsletter/subscribers-module#resend-activation" target="_blank"><?php _e('We have some tips about global actions, read more.', 'newsletter') ?></a>
175
+ </p>
176
+ </td>
177
+ </tr>
178
+ <tr>
179
+ <td><?php _e('Unsubscribed', 'newsletter') ?></td>
180
+ <td>
181
+ <?php echo $wpdb->get_var("select count(*) from " . NEWSLETTER_USERS_TABLE . " where status='U'"); ?>
182
+ </td>
183
+ <td>
184
+ <?php $controls->button_confirm('remove_unsubscribed', __('Delete all', 'newsletter')); ?>
185
+ </td>
186
+ </tr>
187
+
188
+ <tr>
189
+ <td><?php _e('Bounced', 'newsletter') ?></td>
190
+ <td>
191
+ <?php echo $wpdb->get_var("select count(*) from " . NEWSLETTER_USERS_TABLE . " where status='B'"); ?>
192
+ </td>
193
+ <td>
194
+ <?php $controls->button_confirm('remove_bounced', __('Delete all', 'newsletter')); ?>
195
+ </td>
196
+ </tr>
197
+
198
+ <tr>
199
+ <td><?php _e('Complained', 'newsletter') ?></td>
200
+ <td>
201
+ <?php echo $wpdb->get_var("select count(*) from " . NEWSLETTER_USERS_TABLE . " where status='P'"); ?>
202
+ </td>
203
+ <td>
204
+ <?php $controls->button_confirm('remove_complained', __('Delete all', 'newsletter')); ?>
205
+ </td>
206
+ </tr>
207
+ <tr>
208
+ <td>
209
+ <?php _e('Inactive since', 'newsletter') ?>
210
+ <?php $controls->field_help('https://www.thenewsletterplugin.com/documentation/subscribers-and-management/subscribers/#inactive')?>
211
+ </td>
212
+ <td>
213
+ <?php
214
+ $controls->select('inactive_time', array(
215
+ '3' => '3 ' . __('months', 'newsletter'),
216
+ '6' => '6 ' . __('months', 'newsletter'),
217
+ '12' => '1 ' . __('year', 'newsletter'),
218
+ '24' => '2 ' . __('years', 'newsletter'),
219
+ '36' => '3 ' . __('years', 'newsletter'),
220
+ '48' => '4 ' . __('years', 'newsletter'),
221
+ '60' => '5 ' . __('years', 'newsletter'),
222
+ '72' => '6 ' . __('years', 'newsletter'),
223
+ '84' => '7 ' . __('years', 'newsletter'),
224
+ '96' => '8 ' . __('years', 'newsletter'),
225
+ '108' => '9 ' . __('years', 'newsletter'),
226
+ '120' => '10 ' . __('years', 'newsletter')
227
+ ))
228
+ ?>
229
+ add to
230
+ <?php $controls->lists_select('list_inactive'); ?>
231
+
232
+ </td>
233
+ <td>
234
+ <?php $controls->btn('update_inactive', __('Update', 'newsletter'), ['confirm'=>true]); ?>
235
+ </td>
236
+ </tr>
237
+
238
+ <?php if ($this->is_multilanguage()) { ?>
239
+ <tr>
240
+ <td>Language</td>
241
+ <td>
242
+ <?php _e('Set to', 'newsletter') ?>
243
+ <?php $controls->language('language', false) ?> <?php _e('subscribers without a language', 'newsletter') ?>
244
+ </td>
245
+ <td>
246
+ <?php $controls->btn('language', '&raquo;', ['confirm'=>true]); ?>
247
+ </td>
248
+ </tr>
249
+ <?php } ?>
250
+ </table>
251
+
252
+
253
+ </div>
254
+
255
+
256
+ <div id="tabs-2">
257
+ <table class="form-table">
258
+ <tr>
259
+ <th>&nbsp;</th>
260
+ <td>
261
+ <?php $controls->lists_select('list') ?>:
262
+ <?php $controls->button_confirm('list_add', 'Activate for everyone'); ?>
263
+ <?php $controls->button_confirm('list_remove', 'Deactivate for everyone'); ?>
264
+ <?php $controls->button_confirm('list_delete', 'Delete everyone in that list'); ?>
265
+ <br><br>
266
+ <?php $controls->select('list_action', array('move' => 'Change', 'add' => 'Add')); ?>
267
+ <?php _e('all subscribers in', 'newsletter') ?> <?php $controls->lists_select('list_1'); ?>
268
+ <?php _e('to', 'newsletter') ?> <?php $controls->lists_select('list_2'); ?>
269
+ <?php $controls->button_confirm('list_manage', '&raquo;'); ?>
270
+ <p class="description">
271
+ If you choose to <strong>delete</strong> users in a list, they will be
272
+ <strong>physically deleted</strong> from the database (no way back).
273
+ </p>
274
+ </td>
275
+ </tr>
276
+ <tr>
277
+ <th>&nbsp;</th>
278
+ <td>
279
+ <?php _e('Add to list', 'newsletter') ?>
280
+ <?php $controls->lists_select('list_3') ?> <?php _e('subscribers without a list', 'newsletter') ?> <?php $controls->button_confirm('list_none', '&raquo;'); ?>
281
+ </td>
282
+ </tr>
283
+
284
+
285
+
286
+ </table>
287
+ </div>
288
+
289
+ </div>
290
+
291
+ </form>
292
+ </div>
293
+
294
+ <?php include NEWSLETTER_DIR . '/tnp-footer.php'; ?>
295
+
296
+ </div>
users/statistics.php CHANGED
@@ -86,6 +86,12 @@ $options_profile = get_option('newsletter_profile');
86
  <?php echo $wpdb->get_var("select count(*) from " . NEWSLETTER_USERS_TABLE . " where status='B'"); ?>
87
  </td>
88
  </tr>
 
 
 
 
 
 
89
  </tbody>
90
  </table>
91
 
@@ -165,7 +171,7 @@ $options_profile = get_option('newsletter_profile');
165
  <?php $controls->panel_help('https://www.thenewsletterplugin.com/documentation/subscribers-statistics#referrer') ?>
166
  </p>
167
  <?php
168
- $list = $wpdb->get_results("select referrer, SUM(if(status='C', 1, 0)) as confirmed, SUM(if(status='S', 1, 0)) as unconfirmed, SUM(if(status='B', 1, 0)) as bounced, SUM(if(status='U', 1, 0)) as unsubscribed from " . NEWSLETTER_USERS_TABLE . " group by referrer order by confirmed desc");
169
  ?>
170
  <table class="widefat" style="width: auto">
171
  <thead>
@@ -175,6 +181,7 @@ $options_profile = get_option('newsletter_profile');
175
  <th><?php _e('Not confirmed', 'newsletter') ?></th>
176
  <th><?php _e('Unsubscribed', 'newsletter') ?></th>
177
  <th><?php _e('Bounced', 'newsletter') ?></th>
 
178
  </tr>
179
  </thead>
180
  <tbody>
@@ -185,6 +192,7 @@ $options_profile = get_option('newsletter_profile');
185
  <td><?php echo $row->unconfirmed; ?></td>
186
  <td><?php echo $row->unsubscribed; ?></td>
187
  <td><?php echo $row->bounced; ?></td>
 
188
  </tr>
189
  <?php } ?>
190
  </tbody>
86
  <?php echo $wpdb->get_var("select count(*) from " . NEWSLETTER_USERS_TABLE . " where status='B'"); ?>
87
  </td>
88
  </tr>
89
+ <tr>
90
+ <td><?php _e('Complained', 'newsletter') ?></td>
91
+ <td>
92
+ <?php echo $wpdb->get_var("select count(*) from " . NEWSLETTER_USERS_TABLE . " where status='P'"); ?>
93
+ </td>
94
+ </tr>
95
  </tbody>
96
  </table>
97
 
171
  <?php $controls->panel_help('https://www.thenewsletterplugin.com/documentation/subscribers-statistics#referrer') ?>
172
  </p>
173
  <?php
174
+ $list = $wpdb->get_results("select referrer, SUM(if(status='C', 1, 0)) as confirmed, SUM(if(status='S', 1, 0)) as unconfirmed, SUM(if(status='B', 1, 0)) as bounced, SUM(if(status='U', 1, 0)) as unsubscribed, SUM(if(status='P', 1, 0)) as complained from " . NEWSLETTER_USERS_TABLE . " group by referrer order by confirmed desc");
175
  ?>
176
  <table class="widefat" style="width: auto">
177
  <thead>
181
  <th><?php _e('Not confirmed', 'newsletter') ?></th>
182
  <th><?php _e('Unsubscribed', 'newsletter') ?></th>
183
  <th><?php _e('Bounced', 'newsletter') ?></th>
184
+ <th><?php _e('Complained', 'newsletter') ?></th>
185
  </tr>
186
  </thead>
187
  <tbody>
192
  <td><?php echo $row->unconfirmed; ?></td>
193
  <td><?php echo $row->unsubscribed; ?></td>
194
  <td><?php echo $row->bounced; ?></td>
195
+ <td><?php echo $row->complained; ?></td>
196
  </tr>
197
  <?php } ?>
198
  </tbody>