Newsletter - Version 7.5.6

Version Description

  • Added new filter for multilanguage support for GTranslate (experimental)
  • Added link to the backup/recovery documentation
Download this release

Release Info

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

Code changes from version 7.5.5 to 7.5.6

Files changed (7) hide show
  1. emails/emails.php +1365 -1365
  2. includes/addon.php +396 -396
  3. includes/controls.php +1 -1
  4. includes/module.php +1 -1
  5. plugin.php +2 -2
  6. readme.txt +6 -1
  7. tnp-header.php +349 -345
emails/emails.php CHANGED
@@ -1,1365 +1,1365 @@
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
- add_action('newsletter_init', [$this, 'hook_newsletter_init']);
37
-
38
- if (is_admin()) {
39
- // Thank you to plugins which add the WP editor on other admin plugin pages...
40
- if (isset($_GET['page']) && $_GET['page'] == 'newsletter_emails_edit') {
41
- global $wp_actions;
42
- $wp_actions['wp_enqueue_editor'] = 1;
43
- }
44
- }
45
- }
46
-
47
- function hook_newsletter_init() {
48
- if (is_admin()) {
49
- if (defined('DOING_AJAX') && DOING_AJAX) {
50
- if (Newsletter::instance()->is_allowed()) {
51
- add_action('wp_ajax_tnpc_render', array($this, 'tnpc_render_callback'));
52
- add_action('wp_ajax_tnpc_preview', array($this, 'tnpc_preview_callback'));
53
- add_action('wp_ajax_tnpc_css', array($this, 'tnpc_css_callback'));
54
- add_action('wp_ajax_tnpc_options', array($this, 'hook_wp_ajax_tnpc_options'));
55
- add_action('wp_ajax_tnpc_get_all_presets', array($this, 'ajax_get_all_presets'));
56
- add_action('wp_ajax_tnpc_get_preset', array($this, 'ajax_get_preset'));
57
- add_action('wp_ajax_tnpc_delete_preset', array($this, 'hook_wp_ajax_tnpc_delete_preset'));
58
- add_action('wp_ajax_tnpc_regenerate_email', array($this, 'hook_wp_ajax_tnpc_regenerate_email'));
59
- }
60
- }
61
- }
62
- }
63
-
64
- function options_decode($options) {
65
- // Old "query string" format
66
- if (is_string($options) && strpos($options, 'options[') !== false) {
67
- $opts = [];
68
- parse_str($options, $opts);
69
- $options = $opts['options'];
70
- }
71
-
72
- if (is_array($options)) {
73
- return $options;
74
- }
75
-
76
- // Json data should be base64 encoded, but for short time it wasn't
77
- $tmp = json_decode($options, true);
78
- if (is_null($tmp)) {
79
- return json_decode(base64_decode($options), true);
80
- } else {
81
- return $tmp;
82
- }
83
- }
84
-
85
- /**
86
- *
87
- * @param array $options Options array
88
- */
89
- function options_encode($options) {
90
- return base64_encode(json_encode($options, JSON_HEX_TAG | JSON_HEX_AMP));
91
- }
92
-
93
- /**
94
- * Builds and returns the HTML with the form fields of a specific block.
95
- *
96
- * @global wpdb $wpdb
97
- */
98
- function hook_wp_ajax_tnpc_options() {
99
- global $wpdb;
100
-
101
- $block = $this->get_block($_REQUEST['id']);
102
- if (!$block) {
103
- die('Block not found with id ' . esc_html($_REQUEST['id']));
104
- }
105
-
106
- if (!class_exists('NewsletterControls')) {
107
- include NEWSLETTER_INCLUDES_DIR . '/controls.php';
108
- }
109
-
110
- $options = $this->options_decode(stripslashes_deep($_REQUEST['options']));
111
- $composer = isset($_POST['composer']) ? $_POST['composer'] : [];
112
-
113
- $context = array('type' => '');
114
- if (isset($_REQUEST['context_type'])) {
115
- $context['type'] = $_REQUEST['context_type'];
116
- }
117
-
118
- $controls = new NewsletterControls($options);
119
- $fields = new NewsletterFields($controls);
120
-
121
- $controls->init();
122
- echo '<input type="hidden" name="action" value="tnpc_render">';
123
- echo '<input type="hidden" name="id" value="' . esc_attr($_REQUEST['id']) . '">';
124
- echo '<input type="hidden" name="context_type" value="' . esc_attr($context['type']) . '">';
125
- $inline_edits = '';
126
- if (isset($controls->data['inline_edits'])) {
127
- $inline_edits = $controls->data['inline_edits'];
128
- }
129
- echo '<input type="hidden" name="options[inline_edits]" value="', esc_attr($this->options_encode($inline_edits)), '">';
130
- echo "<h2>", esc_html($block["name"]), "</h2>";
131
- include $block['dir'] . '/options.php';
132
- wp_die();
133
- }
134
-
135
- /**
136
- * Retrieves the presets list (no id in GET) or a specific preset id in GET)
137
- */
138
- public function ajax_get_all_presets() {
139
- wp_send_json_success($this->get_all_preset());
140
- }
141
-
142
- public function ajax_get_preset() {
143
-
144
- if (empty($_REQUEST['id'])) {
145
- wp_send_json_error([
146
- 'msg' => __('Invalid preset ID')
147
- ]);
148
- }
149
-
150
- $preset_id = $_REQUEST['id'];
151
- $preset_content = $this->get_preset_content($preset_id);
152
- $global_options = $this->get_preset_global_options($preset_id);
153
-
154
- wp_send_json_success([
155
- 'content' => $preset_content,
156
- 'globalOptions' => $global_options,
157
- ]);
158
- }
159
-
160
- private function get_preset_content($preset_id) {
161
-
162
- $content = '';
163
-
164
- if ($this->is_a_tnp_default_preset($preset_id)) {
165
-
166
- // Get preset from file
167
- $preset = $this->get_preset_from_file($preset_id);
168
-
169
- foreach ($preset->blocks as $item) {
170
- ob_start();
171
- $this->render_block($item->block, true, (array) $item->options);
172
- $content .= trim(ob_get_clean());
173
- }
174
- } else {
175
-
176
- // Get preset from db
177
- $preset_email = $this->get_email(intval($preset_id));
178
- $global_options = $this->extract_global_options_from($preset_email);
179
- $content = $this->regenerate_email_blocks($preset_email->message, $global_options);
180
- }
181
-
182
- return $content;
183
- }
184
-
185
- private function get_preset_global_options($preset_id) {
186
-
187
- if ($this->is_a_tnp_default_preset($preset_id)) {
188
- return [];
189
- }
190
-
191
- // Get preset from db
192
- $preset_email = $this->get_email(intval($preset_id));
193
- $global_options = $this->extract_global_options_from($preset_email);
194
-
195
- return $global_options;
196
- }
197
-
198
- private function extract_global_options_from($email) {
199
- $global_options = [];
200
- foreach ($email->options as $global_option_name => $global_option) {
201
- if (strpos($global_option_name, 'composer_') === 0) {
202
- $global_options[str_replace('composer_', '', $global_option_name)] = $global_option;
203
- }
204
- }
205
-
206
- return $global_options;
207
- }
208
-
209
- private function is_a_tnp_default_preset($preset_id) {
210
- return in_array($preset_id, self::$PRESETS_LIST);
211
- }
212
-
213
- private function get_all_preset() {
214
-
215
- $content = "<div class='tnpc-preset-container'>";
216
-
217
- if ($this->is_normal_context_request()) {
218
- $content .= "<div class='tnpc-preset-legacy-themes'><a href='" . $this->get_admin_page_url('theme') . "'>" . __('Looking for legacy themes?', 'newsletter') . "</a></div>";
219
- }
220
-
221
- // LOAD USER PRESETS
222
- $user_preset_list = $this->get_emails(self::PRESET_EMAIL_TYPE);
223
-
224
- foreach ($user_preset_list as $user_preset) {
225
-
226
- $default_icon_url = NEWSLETTER_URL . "/emails/presets/default-icon.png?ver=2";
227
- $preset_name = $user_preset->subject;
228
- $delete_preset_text = __('Delete', 'newsletter');
229
- $edit_preset_text = __('Edit', 'newsletter');
230
-
231
- // esc_js() assumes the string will be in single quote (arghhh!!!)
232
- $onclick_edit = 'tnpc_edit_preset(' . ((int) $user_preset->id) . ', \'' . esc_js($preset_name) . '\', event)';
233
- $onclick_delete = 'tnpc_delete_preset(' . ((int) $user_preset->id) . ', \'' . esc_js($preset_name) . '\', event)';
234
- $onclick_load = 'tnpc_load_preset(' . ((int) $user_preset->id) . ', \'' . esc_js($preset_name) . '\', event)';
235
-
236
- $content .= "<div class='tnpc-preset' onclick='" . esc_attr($onclick_load) . "'>\n";
237
- $content .= "<img src='$default_icon_url' title='" . esc_attr($preset_name) . "' alt='" . esc_attr($preset_name) . "'>\n";
238
- $content .= "<span class='tnpc-preset-label'>" . esc_html($user_preset->subject) . "</span>\n";
239
- $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";
240
- $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";
241
- $content .= "</div>";
242
- }
243
-
244
- // LOAD TNP PRESETS
245
- foreach (self::$PRESETS_LIST as $id) {
246
- $preset = $this->get_preset_from_file($id);
247
- $preset_name = esc_html($preset->name);
248
- $content .= "<div class='tnpc-preset' onclick='tnpc_load_preset(\"$id\")'>";
249
- $content .= "<img src='$preset->icon' title='$preset_name' alt='$preset_name'/>";
250
- $content .= "<span class='tnpc-preset-label'>$preset_name</span>";
251
- $content .= "</div>";
252
- }
253
-
254
- if ($this->is_normal_context_request()) {
255
- $content .= $this->get_automated_spot_element();
256
- $content .= $this->get_autoresponder_spot_element();
257
- $content .= $this->get_raw_html_preset_element();
258
- }
259
-
260
- return $content;
261
- }
262
-
263
- private function is_normal_context_request() {
264
- return empty($_REQUEST['context_type']);
265
- }
266
-
267
- private function is_automated_context_request() {
268
- return isset($_REQUEST['context_type']) && $_REQUEST['context_type'] === 'automated';
269
- }
270
-
271
- private function is_autoresponder_context_request() {
272
- return isset($_REQUEST['context_type']) && $_REQUEST['context_type'] === 'autoresponder';
273
- }
274
-
275
- private function get_automated_spot_element() {
276
- $result = "<div class='tnpc-preset'>";
277
- if (class_exists('NewsletterAutomated')) {
278
- $result .= "<a href='?page=newsletter_automated_index'>";
279
- } else {
280
- $result .= "<a href='https://www.thenewsletterplugin.com/automated?utm_source=composer&utm_campaign=plugin&utm_medium=automated'>";
281
- }
282
- $result .= "<img src='" . plugins_url('newsletter') . "/emails/images/automated.png' title='Automated addon' alt='Automated'/>";
283
- $result .= "<span class='tnpc-preset-label'>Daily, weekly and monthly newsletters</span></a>";
284
- $result .= "</div>";
285
-
286
- return $result;
287
- }
288
-
289
- private function get_autoresponder_spot_element() {
290
- $result = "<div class='tnpc-preset'>";
291
- if (class_exists('NewsletterAutoresponder')) {
292
- $result .= "<a href='?page=newsletter_autoresponder_index'>";
293
- } else {
294
- $result .= "<a href='https://www.thenewsletterplugin.com/autoresponder?utm_source=composer&utm_campaign=plugin&utm_medium=autoresponder' target='_blank'>";
295
- }
296
- $result .= "<img src='" . plugins_url('newsletter') . "/emails/images/autoresponder.png' title='Autoresponder addon' alt='Autoresponder'/>";
297
- $result .= "<span class='tnpc-preset-label'>Autoresponders</span></a>";
298
- $result .= "</div>";
299
-
300
- return $result;
301
- }
302
-
303
- private function get_raw_html_preset_element() {
304
-
305
- $result = "<div class='tnpc-preset tnpc-preset-html' onclick='location.href=\"" . wp_nonce_url('admin.php?page=newsletter_emails_new&id=rawhtml', 'newsletter-new') . "\"'>";
306
- $result .= "<img src='" . plugins_url('newsletter') . "/emails/images/rawhtml.png' title='RAW HTML' alt='RAW'/>";
307
- $result .= "<span class='tnpc-preset-label'>Raw HTML</span>";
308
- $result .= "</div>";
309
-
310
- $result .= "<div class='clear'></div>";
311
- $result .= "</div>";
312
-
313
- return $result;
314
- }
315
-
316
- function has_dynamic_blocks($theme) {
317
- preg_match_all('/data-json="(.*?)"/m', $theme, $matches, PREG_PATTERN_ORDER);
318
- foreach ($matches[1] as $match) {
319
- $a = html_entity_decode($match, ENT_QUOTES, 'UTF-8');
320
- $options = $this->options_decode($a);
321
-
322
- $block = $this->get_block($options['block_id']);
323
- if (!$block) {
324
- continue;
325
- }
326
- if ($block['type'] == 'dynamic') {
327
- return true;
328
- }
329
- }
330
- return false;
331
- }
332
-
333
- /**
334
- * Regenerates a saved composed email rendering each block. Regeneration is
335
- * conditioned (possibly) by the context. The context is usually passed to blocks
336
- * so they can act in the right manner.
337
- *
338
- * $context contains a type and, for automated, the last_run.
339
- *
340
- * $email can actually be even a string containing the full newsletter HTML code.
341
- *
342
- * @param TNP_Email $email
343
- * @return string
344
- */
345
- function regenerate($email, $context = []) {
346
-
347
- $context = array_merge(['last_run' => 0, 'type' => ''], $context);
348
-
349
- $composer = [];
350
- foreach ($email->options as $k=>$v) {
351
- if (strpos($k, 'composer_') !== 0) continue;
352
- $composer[substr($k, 9)] = $v;
353
- }
354
-
355
-
356
- preg_match_all('/data-json="(.*?)"/m', $email->message, $matches, PREG_PATTERN_ORDER);
357
-
358
- $result = '';
359
- $subject = '';
360
-
361
- foreach ($matches[1] as $match) {
362
- $a = html_entity_decode($match, ENT_QUOTES, 'UTF-8');
363
- $options = $this->options_decode($a);
364
-
365
- $block = $this->get_block($options['block_id']);
366
- if (!$block) {
367
- $this->logger->debug('Unable to load the block ' . $options['block_id']);
368
- //continue;
369
- }
370
-
371
- ob_start();
372
- $out = $this->render_block($options['block_id'], true, $options, $context, $composer);
373
- if (is_array($out)) {
374
- if ($out['return_empty_message'] || $out['stop']) {
375
- return false;
376
- }
377
- if ($out['skip']) {
378
- continue;
379
- }
380
- if (empty($subject) && !empty($out['subject'])) {
381
- $subject = $out['subject'];
382
- }
383
- }
384
- $block_html = ob_get_clean();
385
- $result .= $block_html;
386
- }
387
-
388
- $email->message = TNP_Composer::get_html_open($email) . TNP_Composer::get_main_wrapper_open($email) .
389
- $result . TNP_Composer::get_main_wrapper_close($email) . TNP_Composer::get_html_close($email);
390
- $email->subject = $subject;
391
- return true;
392
- }
393
-
394
- function remove_block_data($text) {
395
- // TODO: Lavorare!
396
- return $text;
397
- }
398
-
399
- static function get_outlook_wrapper_open($width = 600) {
400
- 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]-->';
401
- }
402
-
403
- static function get_outlook_wrapper_close() {
404
- return "<!--[if mso | IE]></td></tr></table><![endif]-->";
405
- }
406
-
407
- function hook_safe_style_css($rules) {
408
- $rules[] = 'display';
409
- return $rules;
410
- }
411
-
412
- /**
413
- * Renders a block identified by its id, using the block options and adding a wrapper
414
- * if required (for the first block rendering).
415
- *
416
- * @param string $block_id
417
- * @param boolean $wrapper
418
- * @param array $options
419
- * @param array $context
420
- * @param array $composer
421
- */
422
- function render_block($block_id = null, $wrapper = false, $options = [], $context = [], $composer = []) {
423
- static $kses_style_filter = false;
424
- include_once NEWSLETTER_INCLUDES_DIR . '/helper.php';
425
-
426
- //Remove 'options_composer_' prefix
427
- $composer_defaults = [];
428
- foreach (TNP_Composer::get_global_style_defaults() as $global_option_name => $global_option) {
429
- $composer_defaults[str_replace('options_composer_', '', $global_option_name)] = $global_option;
430
- }
431
- $composer = array_merge($composer_defaults, $composer);
432
-
433
- $width = 600;
434
- $font_family = 'Helvetica, Arial, sans-serif';
435
-
436
- $global_title_font_family = $composer['title_font_family'];
437
- $global_title_font_size = $composer['title_font_size'];
438
- $global_title_font_color = $composer['title_font_color'];
439
- $global_title_font_weight = $composer['title_font_weight'];
440
-
441
- $global_text_font_family = $composer['text_font_family'];
442
- $global_text_font_size = $composer['text_font_size'];
443
- $global_text_font_color = $composer['text_font_color'];
444
- $global_text_font_weight = $composer['text_font_weight'];
445
-
446
- $global_button_font_family = $composer['button_font_family'];
447
- $global_button_font_size = $composer['button_font_size'];
448
- $global_button_font_color = $composer['button_font_color'];
449
- $global_button_font_weight = $composer['button_font_weight'];
450
- $global_button_background_color = $composer['button_background_color'];
451
-
452
- $global_block_background = $composer['block_background'];
453
-
454
- $info = Newsletter::instance()->get_options('info');
455
-
456
- // Just in case...
457
- if (!is_array($options)) {
458
- $options = array();
459
- }
460
-
461
- // This code filters the HTML to remove javascript and unsecure attributes and enable the
462
- // "display" rule for CSS which is needed in blocks to force specific "block" or "inline" or "table".
463
- add_filter('safe_style_css', [$this, 'hook_safe_style_css'], 9999);
464
- $options = wp_kses_post_deep($options);
465
- remove_filter('safe_style_css', [$this, 'hook_safe_style_css']);
466
-
467
- $block_options = get_option('newsletter_main');
468
-
469
- $block = $this->get_block($block_id);
470
-
471
- if (!isset($context['type']))
472
- $context['type'] = '';
473
-
474
- // Block not found
475
- if (!$block) {
476
- if ($wrapper) {
477
- 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), '">';
478
- echo '<tr>';
479
- echo '<td data-options="" bgcolor="#ffffff" align="center" style="padding: 0; font-family: Helvetica, Arial, sans-serif;" class="edit-block">';
480
- }
481
- echo $this->get_outlook_wrapper_open($width);
482
-
483
- echo '<p>Ops, this block type is not avalable.</p>';
484
-
485
- echo $this->get_outlook_wrapper_close();
486
-
487
- if ($wrapper) {
488
- echo '</td></tr></table>';
489
- }
490
- return;
491
- }
492
-
493
- $out = ['subject' => '', 'return_empty_message' => false, 'stop' => false, 'skip' => false];
494
-
495
- $dir = is_rtl() ? 'rtl' : 'ltr';
496
- $align_left = is_rtl() ? 'right' : 'left';
497
- $align_right = is_rtl() ? 'left' : 'right';
498
-
499
- ob_start();
500
- $logger = $this->logger;
501
- include $block['dir'] . '/block.php';
502
- $content = trim(ob_get_clean());
503
-
504
- if (empty($content)) {
505
- return $out;
506
- }
507
-
508
- $common_defaults = array(
509
- 'block_padding_top' => 0,
510
- 'block_padding_bottom' => 0,
511
- 'block_padding_right' => 0,
512
- 'block_padding_left' => 0,
513
- 'block_background' => '',
514
- 'block_background_2' => '',
515
- 'block_width' => 600,
516
- 'block_align' => 'center'
517
- );
518
-
519
- $options = array_merge($common_defaults, $options);
520
-
521
- // Obsolete
522
- $content = str_replace('{width}', $width, $content);
523
-
524
- $content = $this->inline_css($content, true);
525
-
526
- // CSS driven by the block
527
- // Requited for the server side parsing and rendering
528
- $options['block_id'] = $block_id;
529
-
530
- $options['block_padding_top'] = (int) str_replace('px', '', $options['block_padding_top']);
531
- $options['block_padding_bottom'] = (int) str_replace('px', '', $options['block_padding_bottom']);
532
- $options['block_padding_right'] = (int) str_replace('px', '', $options['block_padding_right']);
533
- $options['block_padding_left'] = (int) str_replace('px', '', $options['block_padding_left']);
534
-
535
- $block_background = empty($options['block_background']) ? $global_block_background : $options['block_background'];
536
-
537
- // Internal TD wrapper
538
- $style = 'text-align: center; ';
539
- $style .= 'width: 100% !important; ';
540
- $style .= 'line-height: normal !important; ';
541
- $style .= 'letter-spacing: normal; ';
542
- $style .= 'padding-top: ' . $options['block_padding_top'] . 'px; ';
543
- $style .= 'padding-left: ' . $options['block_padding_left'] . 'px; ';
544
- $style .= 'padding-right: ' . $options['block_padding_right'] . 'px; ';
545
- $style .= 'padding-bottom: ' . $options['block_padding_bottom'] . 'px; ';
546
- $style .= 'background-color: ' . $block_background . ';';
547
-
548
- if (isset($options['block_background_gradient'])) {
549
- $style .= 'background: linear-gradient(180deg, ' . $block_background . ' 0%, ' . $options['block_background_2'] . ' 100%);';
550
- }
551
-
552
- $data = $this->options_encode($options);
553
- // First time block creation wrapper
554
- if ($wrapper) {
555
- 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";
556
- echo "<tr>";
557
- echo '<td align="center" style="padding: 0;" class="edit-block">', "\n";
558
- }
559
-
560
- // Container that fixes the width and makes the block responsive
561
- echo $this->get_outlook_wrapper_open($options['block_width']);
562
-
563
- 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";
564
- echo "<tr>";
565
- echo '<td align="', esc_attr($options['block_align']), '" style="', esc_attr($style), '" bgcolor="', esc_attr($block_background), '" width="100%">';
566
-
567
- //echo "<!-- block generated content -->\n";
568
- echo trim($content);
569
- //echo "\n<!-- /block generated content -->\n";
570
-
571
- echo "</td></tr></table>";
572
- echo $this->get_outlook_wrapper_close();
573
-
574
- // First time block creation wrapper
575
- if ($wrapper) {
576
- echo "</td></tr></table>";
577
- }
578
-
579
- return $out;
580
- }
581
-
582
- /**
583
- * Ajax call to render a block with a new set of options after the settings popup
584
- * has been saved.
585
- *
586
- * @param type $block_id
587
- * @param type $wrapper
588
- */
589
- function tnpc_render_callback() {
590
- if (!check_ajax_referer('save')) {
591
- $this->dienow('Expired request');
592
- }
593
-
594
- $block_id = $_POST['id'];
595
- $wrapper = isset($_POST['full']);
596
- $options = $this->restore_options_from_request();
597
-
598
- $this->render_block($block_id, $wrapper, $options, [], $_POST['composer']);
599
- wp_die();
600
- }
601
-
602
- function hook_wp_ajax_tnpc_regenerate_email() {
603
-
604
- $content = stripslashes($_POST['content']);
605
- $global_options = $_POST['composer'];
606
-
607
- $regenerated_content = $this->regenerate_email_blocks($content, $global_options);
608
-
609
- wp_send_json_success([
610
- 'content' => $regenerated_content,
611
- 'message' => __('Successfully updated', 'newsletter')
612
- ]);
613
- }
614
-
615
- private function regenerate_email_blocks($content, $global_options) {
616
-
617
- $raw_block_options = $this->extract_encoded_blocks_options($content);
618
-
619
- $regenerated_content = '';
620
-
621
- foreach ($raw_block_options as $raw_block_option) {
622
-
623
- /* $a = html_entity_decode( $raw_block_option, ENT_QUOTES, 'UTF-8' );
624
- $block_options = $this->options_decode( $a ); */
625
-
626
- $block_options = $this->options_decode($raw_block_option);
627
-
628
- $block = $this->get_block($block_options['block_id']);
629
- if (!$block) {
630
- $this->logger->debug('Unable to load the block ' . $block_options['block_id']);
631
- }
632
-
633
- ob_start();
634
- $this->render_block($block_options['block_id'], true, $block_options, [], $global_options);
635
- $block_html = ob_get_clean();
636
-
637
- $regenerated_content .= $block_html;
638
- }
639
-
640
- return $regenerated_content;
641
- }
642
-
643
- /**
644
- * @param string $html_email_content Email html content
645
- *
646
- * @return string[] Encoded options of email blocks
647
- */
648
- private function extract_encoded_blocks_options($html_email_content) {
649
-
650
- preg_match_all('/data-json="(.*?)"/m', $html_email_content, $raw_block_options, PREG_PATTERN_ORDER);
651
-
652
- return $raw_block_options[1];
653
- }
654
-
655
- function tnpc_preview_callback() {
656
- $email = Newsletter::instance()->get_email($_REQUEST['id'], ARRAY_A);
657
-
658
- if (empty($email)) {
659
- echo 'Wrong email identifier';
660
- return;
661
- }
662
-
663
- echo $email['message'];
664
-
665
- wp_die();
666
- }
667
-
668
- function tnpc_css_callback() {
669
- include NEWSLETTER_DIR . '/emails/tnp-composer/css/newsletter.css';
670
- wp_die();
671
- }
672
-
673
- /** Returns the correct admin page to edit the newsletter with the correct editor. */
674
- function get_editor_url($email_id, $editor_type) {
675
- switch ($editor_type) {
676
- case NewsletterEmails::EDITOR_COMPOSER:
677
- return admin_url("admin.php") . '?page=newsletter_emails_composer&id=' . $email_id;
678
- case NewsletterEmails::EDITOR_HTML:
679
- return admin_url("admin.php") . '?page=newsletter_emails_editorhtml&id=' . $email_id;
680
- case NewsletterEmails::EDITOR_TINYMCE:
681
- return admin_url("admin.php") . '?page=newsletter_emails_editortinymce&id=' . $email_id;
682
- }
683
- }
684
-
685
- /**
686
- * Returns the button linked to the correct "edit" page for the passed newsletter. The edit page can be an editor
687
- * or the targeting page (it depends on newsletter status).
688
- *
689
- * @param TNP_Email $email
690
- */
691
- function get_edit_button($email, $only_icon = false) {
692
-
693
- $editor_type = $this->get_editor_type($email);
694
- if ($email->status == 'new') {
695
- $edit_url = $this->get_editor_url($email->id, $editor_type);
696
- } else {
697
- $edit_url = 'admin.php?page=newsletter_emails_edit&id=' . $email->id;
698
- }
699
- switch ($editor_type) {
700
- case NewsletterEmails::EDITOR_COMPOSER:
701
- $icon_class = 'th-large';
702
- break;
703
- case NewsletterEmails::EDITOR_HTML:
704
- $icon_class = 'code';
705
- break;
706
- default:
707
- $icon_class = 'edit';
708
- break;
709
- }
710
- if ($only_icon) {
711
- return '<a class="button-primary" href="' . $edit_url . '" title="' . esc_attr__('Edit', 'newsletter') . '">' .
712
- '<i class="fas fa-' . $icon_class . '"></i></a>';
713
- } else {
714
- return '<a class="button-primary" href="' . $edit_url . '" title="' . esc_attr__('Edit', 'newsletter') . '">' .
715
- '<i class="fas fa-' . $icon_class . '"></i> ' . __('Edit', 'newsletter') . '</a>';
716
- }
717
- }
718
-
719
- /** Returns the correct editor type for the provided newsletter. Contains backward compatibility code. */
720
- function get_editor_type($email) {
721
- $email = (object) $email;
722
- $editor_type = $email->editor;
723
-
724
- // Backward compatibility
725
- $email_options = maybe_unserialize($email->options);
726
- if (isset($email_options['composer'])) {
727
- $editor_type = NewsletterEmails::EDITOR_COMPOSER;
728
- }
729
- // End backward compatibility
730
-
731
- return $editor_type;
732
- }
733
-
734
- /**
735
- *
736
- * @param type $action
737
- * @param type $user
738
- * @param type $email
739
- * @return type
740
- * @global wpdb $wpdb
741
- */
742
- function hook_newsletter_action($action, $user, $email) {
743
- global $wpdb;
744
-
745
- switch ($action) {
746
- case 'v':
747
- case 'view':
748
- $id = $_GET['id'];
749
- if ($id == 'last') {
750
- $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");
751
- } else {
752
- $email = $this->get_email($_GET['id']);
753
- }
754
- if (empty($email)) {
755
- header("HTTP/1.0 404 Not Found");
756
- die('Email not found');
757
- }
758
-
759
- if (!Newsletter::instance()->is_allowed()) {
760
-
761
- if ($email->status == 'new') {
762
- header("HTTP/1.0 404 Not Found");
763
- die('Not sent yet');
764
- }
765
-
766
- if ($email->private == 1) {
767
- if (!$user) {
768
- header("HTTP/1.0 404 Not Found");
769
- die('No available for online view');
770
- }
771
- $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));
772
- if (!$sent) {
773
- header("HTTP/1.0 404 Not Found");
774
- die('No available for online view');
775
- }
776
- }
777
- }
778
-
779
-
780
- header('Content-Type: text/html;charset=UTF-8');
781
- header('X-Robots-Tag: noindex,nofollow,noarchive');
782
- header('Cache-Control: no-cache,no-store,private');
783
-
784
- echo $this->replace($email->message, $user, $email);
785
-
786
- die();
787
- break;
788
-
789
- case 'emails-css':
790
- $email_id = (int) $_GET['id'];
791
-
792
- $body = Newsletter::instance()->get_email_field($email_id, 'message');
793
-
794
- $x = strpos($body, '<style');
795
- if ($x === false)
796
- return;
797
-
798
- $x = strpos($body, '>', $x);
799
- $y = strpos($body, '</style>');
800
-
801
- header('Content-Type: text/css;charset=UTF-8');
802
-
803
- echo substr($body, $x + 1, $y - $x - 1);
804
-
805
- die();
806
- break;
807
-
808
- case 'emails-composer-css':
809
- header('Cache: no-cache');
810
- header('Content-Type: text/css');
811
- echo $this->get_composer_css();
812
- die();
813
- break;
814
-
815
- case 'emails-preview':
816
- if (!Newsletter::instance()->is_allowed()) {
817
- die('Not enough privileges');
818
- }
819
-
820
- if (!check_admin_referer('view')) {
821
- die();
822
- }
823
-
824
- $theme_id = $_GET['id'];
825
- $theme = $this->themes->get_theme($theme_id);
826
-
827
- // Used by theme code
828
- $theme_options = $this->themes->get_options($theme_id);
829
-
830
- $theme_url = $theme['url'];
831
-
832
- header('Content-Type: text/html;charset=UTF-8');
833
-
834
- include $theme['dir'] . '/theme.php';
835
-
836
- die();
837
- break;
838
-
839
- case 'emails-preview-text':
840
- header('Content-Type: text/plain;charset=UTF-8');
841
- if (!Newsletter::instance()->is_allowed()) {
842
- die('Not enough privileges');
843
- }
844
-
845
- if (!check_admin_referer('view')) {
846
- die();
847
- }
848
-
849
- // Used by theme code
850
- $theme_options = $this->get_current_theme_options();
851
-
852
- $file = include $theme['dir'] . '/theme-text.php';
853
-
854
- if (is_file($file)) {
855
- include $file;
856
- }
857
-
858
- die();
859
- break;
860
-
861
- case 'emails-create':
862
- // Newsletter from themes are created on frontend context because sometime WP themes change the way the content,
863
- // excerpt, thumbnail are extracted.
864
- if (!Newsletter::instance()->is_allowed()) {
865
- die('Not enough privileges');
866
- }
867
-
868
- require_once NEWSLETTER_INCLUDES_DIR . '/controls.php';
869
- $controls = new NewsletterControls();
870
-
871
- if (!$controls->is_action('create')) {
872
- die('Wrong call');
873
- }
874
-
875
- $theme_id = $controls->data['id'];
876
- $theme = $this->themes->get_theme($theme_id);
877
-
878
- if (!$theme) {
879
- die('invalid theme');
880
- }
881
-
882
- $this->themes->save_options($theme_id, $controls->data);
883
-
884
- $email = array();
885
- $email['status'] = 'new';
886
- $email['subject'] = ''; //__('Here the email subject', 'newsletter');
887
- $email['track'] = Newsletter::instance()->options['track'];
888
- $email['send_on'] = time();
889
- $email['editor'] = NewsletterEmails::EDITOR_TINYMCE;
890
- $email['type'] = 'message';
891
-
892
- $theme_options = $this->themes->get_options($theme_id);
893
-
894
- $theme_url = $theme['url'];
895
- $theme_subject = '';
896
-
897
- ob_start();
898
- include $theme['dir'] . '/theme.php';
899
- $email['message'] = ob_get_clean();
900
-
901
- if (!empty($theme_subject)) {
902
- $email['subject'] = $theme_subject;
903
- }
904
-
905
- if (file_exists($theme['dir'] . '/theme-text.php')) {
906
- ob_start();
907
- include $theme['dir'] . '/theme-text.php';
908
- $email['message_text'] = ob_get_clean();
909
- } else {
910
- $email['message_text'] = 'You need a modern email client to read this email. Read it online: {email_url}.';
911
- }
912
-
913
- $email = $this->save_email($email);
914
-
915
- $edit_url = $this->get_editor_url($email->id, $email->editor);
916
-
917
- header('Location: ' . $edit_url);
918
-
919
- die();
920
- break;
921
- }
922
- }
923
-
924
- function admin_menu() {
925
- $this->add_menu_page('index', 'Newsletters');
926
- $this->add_admin_page('list', 'Email List');
927
- $this->add_admin_page('new', 'Email New');
928
- $this->add_admin_page('edit', 'Email Edit');
929
- $this->add_admin_page('theme', 'Email Themes');
930
- $this->add_admin_page('composer', 'The Composer');
931
- $this->add_admin_page('editorhtml', 'HTML Editor');
932
- $this->add_admin_page('editortinymce', 'TinyMCE Editor');
933
- }
934
-
935
- /**
936
- * Builds a block data structure starting from the folder containing the block
937
- * files.
938
- *
939
- * @param string $dir
940
- * @return array | WP_Error
941
- */
942
- function build_block($dir) {
943
- $dir = realpath($dir);
944
- $dir = wp_normalize_path($dir);
945
- $full_file = $dir . '/block.php';
946
- if (!is_file($full_file)) {
947
- return new WP_Error('1', 'Missing block.php file in ' . $dir);
948
- }
949
-
950
- $wp_content_dir = wp_normalize_path(realpath(WP_CONTENT_DIR));
951
-
952
- $relative_dir = substr($dir, strlen($wp_content_dir));
953
- $file = basename($dir);
954
-
955
- $data = get_file_data($full_file, ['name' => 'Name', 'section' => 'Section', 'description' => 'Description', 'type' => 'Type']);
956
- $defaults = ['section' => 'content', 'name' => ucfirst($file), 'descritpion' => '', 'icon' => plugins_url('newsletter') . '/admin/images/block-icon.png'];
957
- $data = array_merge($defaults, $data);
958
-
959
- if (is_file($dir . '/icon.png')) {
960
- $data['icon'] = content_url($relative_dir . '/icon.png');
961
- }
962
-
963
- $data['id'] = sanitize_key($file);
964
-
965
- // Absolute path of the block files
966
- $data['dir'] = $dir;
967
- $data['url'] = content_url($relative_dir);
968
-
969
- return $data;
970
- }
971
-
972
- /**
973
- *
974
- * @param type $dir
975
- * @return type
976
- */
977
- function scan_blocks_dir($dir) {
978
- $dir = realpath($dir);
979
- if (!$dir) {
980
- return [];
981
- }
982
- $dir = wp_normalize_path($dir);
983
-
984
- $list = [];
985
- $handle = opendir($dir);
986
- while ($file = readdir($handle)) {
987
-
988
- $data = $this->build_block($dir . '/' . $file);
989
-
990
- if (is_wp_error($data)) {
991
- $this->logger->error($data);
992
- continue;
993
- }
994
- $list[$data['id']] = $data;
995
- }
996
- closedir($handle);
997
- return $list;
998
- }
999
-
1000
- /**
1001
- * Array of arrays with every registered block and legacy block converted to the new
1002
- * format.
1003
- *
1004
- * @return array
1005
- */
1006
- function get_blocks() {
1007
-
1008
- if (!is_null($this->blocks)) {
1009
- return $this->blocks;
1010
- }
1011
-
1012
- $this->blocks = $this->scan_blocks_dir(__DIR__ . '/blocks');
1013
-
1014
- $extended = $this->scan_blocks_dir(WP_CONTENT_DIR . '/extensions/newsletter/blocks');
1015
-
1016
- $this->blocks = array_merge($extended, $this->blocks);
1017
-
1018
- $dirs = apply_filters('newsletter_blocks_dir', array());
1019
-
1020
- //$this->logger->debug('Block dirs:');
1021
- //$this->logger->debug($dirs);
1022
-
1023
- foreach ($dirs as $dir) {
1024
- $list = $this->scan_blocks_dir($dir);
1025
- $this->blocks = array_merge($list, $this->blocks);
1026
- }
1027
-
1028
- do_action('newsletter_register_blocks');
1029
-
1030
- foreach (TNP_Composer::$block_dirs as $dir) {
1031
- $block = $this->build_block($dir);
1032
- if (is_wp_error($block)) {
1033
- $this->logger->error($block);
1034
- continue;
1035
- }
1036
- if (!isset($this->blocks[$block['id']])) {
1037
- $this->blocks[$block['id']] = $block;
1038
- } else {
1039
- $this->logger->error('The block "' . $block['id'] . '" has already been registered');
1040
- }
1041
- }
1042
-
1043
- $this->blocks = array_reverse($this->blocks);
1044
- return $this->blocks;
1045
- }
1046
-
1047
- /**
1048
- * Return a single block (associative array) checking for legacy ID as well.
1049
- *
1050
- * @param string $id
1051
- * @return array
1052
- */
1053
- function get_block($id) {
1054
- switch ($id) {
1055
- case 'content-03-text.block':
1056
- $id = 'text';
1057
- break;
1058
- case 'footer-03-social.block':
1059
- $id = 'social';
1060
- break;
1061
- case 'footer-02-canspam.block':
1062
- $id = 'canspam';
1063
- break;
1064
- case 'content-05-image.block':
1065
- $id = 'image';
1066
- break;
1067
- case 'header-01-header.block':
1068
- $id = 'header';
1069
- break;
1070
- case 'footer-01-footer.block':
1071
- $id = 'footer';
1072
- break;
1073
- case 'content-02-heading.block':
1074
- $id = 'heading';
1075
- break;
1076
- case 'content-07-twocols.block':
1077
- case 'content-06-posts.block':
1078
- $id = 'posts';
1079
- break;
1080
- case 'content-04-cta.block':
1081
- $id = 'cta';
1082
- break;
1083
- case 'content-01-hero.block':
1084
- $id = 'hero';
1085
- break;
1086
- // case 'content-02-heading.block': $id = '/plugins/newsletter/emails/blocks/heading';
1087
- // break;
1088
- }
1089
-
1090
- // Conversion for old full path ID
1091
- $id = sanitize_key(basename($id));
1092
-
1093
- // TODO: Correct id for compatibility
1094
- $blocks = $this->get_blocks();
1095
- if (!isset($blocks[$id])) {
1096
- return null;
1097
- }
1098
- return $blocks[$id];
1099
- }
1100
-
1101
- function scan_presets_dir($dir = null) {
1102
-
1103
- if (is_null($dir)) {
1104
- $dir = __DIR__ . '/presets';
1105
- }
1106
-
1107
- if (!is_dir($dir)) {
1108
- return array();
1109
- }
1110
-
1111
- $handle = opendir($dir);
1112
- $list = array();
1113
- $relative_dir = substr($dir, strlen(WP_CONTENT_DIR));
1114
- while ($file = readdir($handle)) {
1115
-
1116
- if ($file == '.' || $file == '..')
1117
- continue;
1118
-
1119
- // The block unique key, we should find out how to build it, maybe an hash of the (relative) dir?
1120
- $preset_id = sanitize_key($file);
1121
-
1122
- $full_file = $dir . '/' . $file . '/preset.json';
1123
-
1124
- if (!is_file($full_file)) {
1125
- continue;
1126
- }
1127
-
1128
- $icon = content_url($relative_dir . '/' . $file . '/icon.png');
1129
-
1130
- $list[$preset_id] = $icon;
1131
- }
1132
- closedir($handle);
1133
- return $list;
1134
- }
1135
-
1136
- function get_preset_from_file($id, $dir = null) {
1137
-
1138
- if (is_null($dir)) {
1139
- $dir = __DIR__ . '/presets';
1140
- }
1141
-
1142
- $id = $this->sanitize_file_name($id);
1143
-
1144
- if (!is_dir($dir . '/' . $id) || !in_array($id, self::$PRESETS_LIST)) {
1145
- return array();
1146
- }
1147
-
1148
- $json_content = file_get_contents("$dir/$id/preset.json");
1149
- $json_content = str_replace("{placeholder_base_url}", plugins_url('newsletter') . '/emails/presets', $json_content);
1150
- $json = json_decode($json_content);
1151
- $json->icon = NEWSLETTER_URL . "/emails/presets/$id/icon.png?ver=2";
1152
-
1153
- return $json;
1154
- }
1155
-
1156
- function get_composer_css() {
1157
- $css = file_get_contents(__DIR__ . '/tnp-composer/css/newsletter.css');
1158
- $blocks = $this->get_blocks();
1159
- foreach ($blocks as $block) {
1160
- if (!file_exists($block['dir'] . '/style.css')) {
1161
- continue;
1162
- }
1163
- $css .= "\n\n";
1164
- $css .= "/* " . $block['name'] . " */\n";
1165
- $css .= file_get_contents($block['dir'] . '/style.css');
1166
- }
1167
- return $css;
1168
- }
1169
-
1170
- function get_composer_backend_css() {
1171
- $css = file_get_contents(__DIR__ . '/tnp-composer/css/backend.css');
1172
- $css .= "\n\n";
1173
- $css .= $this->get_composer_css();
1174
- return $css;
1175
- }
1176
-
1177
- /**
1178
- * Send an email to the test subscribers.
1179
- *
1180
- * @param TNP_Email $email Could be any object with the TNP_Email attributes
1181
- * @param NewsletterControls $controls
1182
- */
1183
- function send_test_email($email, $controls) {
1184
- if (!$email) {
1185
- $controls->errors = __('Newsletter should be saved before send a test', 'newsletter');
1186
- return;
1187
- }
1188
-
1189
- $original_subject = $email->subject;
1190
- $this->set_test_subject_to($email);
1191
-
1192
- $users = NewsletterUsers::instance()->get_test_users();
1193
- if (count($users) == 0) {
1194
- $controls->errors = '' . __('There are no test subscribers to send to', 'newsletter') .
1195
- '. <a href="https://www.thenewsletterplugin.com/plugins/newsletter/subscribers-module#test" target="_blank"><strong>' .
1196
- __('Read more', 'newsletter') . '</strong></a>.';
1197
- } else {
1198
- $r = Newsletter::instance()->send($email, $users, true);
1199
- $emails = array();
1200
- foreach ($users as $user) {
1201
- $emails[] = '<a href="admin.php?page=newsletter_users_edit&id=' . $user->id . '" target="_blank">' . $user->email . '</a>';
1202
- }
1203
- if (is_wp_error($r)) {
1204
- $controls->errors = 'Something went wrong. Check the error logs on status page.<br>';
1205
- $controls->errors .= __('Test subscribers:', 'newsletter');
1206
- $controls->errors .= ' ' . implode(', ', $emails);
1207
- $controls->errors .= '<br>';
1208
- $controls->errors .= '<strong>' . esc_html($r->get_error_message()) . '</strong><br>';
1209
- $controls->errors .= '<a href="https://www.thenewsletterplugin.com/documentation/email-sending-issues" target="_blank"><strong>' . __('Read more about delivery issues', 'newsletter') . '</strong></a>.';
1210
- } else {
1211
- $controls->messages = __('Test subscribers:', 'newsletter');
1212
-
1213
- $controls->messages .= ' ' . implode(', ', $emails);
1214
- $controls->messages .= '.<br>';
1215
- $controls->messages .= '<a href="https://www.thenewsletterplugin.com/documentation/subscribers#test" target="_blank"><strong>' .
1216
- __('Read more about test subscribers', 'newsletter') . '</strong></a>.<br>';
1217
- $controls->messages .= '<a href="https://www.thenewsletterplugin.com/documentation/email-sending-issues" target="_blank"><strong>' . __('Read more about delivery issues', 'newsletter') . '</strong></a>.';
1218
- }
1219
- }
1220
- $email->subject = $original_subject;
1221
- }
1222
-
1223
- /**
1224
- * Send an email to the test subscribers.
1225
- *
1226
- * @param TNP_Email $email Could be any object with the TNP_Email attributes
1227
- * @param string $email_address
1228
- *
1229
- * @throws Exception
1230
- */
1231
- function send_test_newsletter_to_email_address($email, $email_address) {
1232
-
1233
- if (!$email) {
1234
- throw new Exception(__('Newsletter should be saved before send a test', 'newsletter'));
1235
- }
1236
-
1237
- $this->set_test_subject_to($email);
1238
-
1239
- $dummy_subscriber = $this->make_dummy_subscriber();
1240
- $dummy_subscriber->email = $email_address;
1241
-
1242
- $result = Newsletter::instance()->send($email, [$dummy_subscriber], true);
1243
-
1244
- $email = '<a href="admin.php?page=newsletter_users_edit&id=' . $dummy_subscriber->id . '" target="_blank">' . $dummy_subscriber->email . '</a>';
1245
-
1246
- if (is_wp_error($result)) {
1247
- $error_message = 'Something went wrong. Check the error logs on status page.<br>';
1248
- $error_message .= __('Test subscribers:', 'newsletter');
1249
- $error_message .= ' ' . $email;
1250
- $error_message .= '<br>';
1251
- $error_message .= '<strong>' . esc_html($result->get_error_message()) . '</strong><br>';
1252
- $error_message .= '<a href="https://www.thenewsletterplugin.com/documentation/email-sending-issues" target="_blank"><strong>' . __('Read more about delivery issues', 'newsletter') . '</strong></a>.';
1253
- throw new Exception($error_message);
1254
- }
1255
-
1256
- $messages = __('Test subscribers:', 'newsletter');
1257
-
1258
- $messages .= ' ' . $email;
1259
- $messages .= '.<br>';
1260
- $messages .= '<a href="https://www.thenewsletterplugin.com/documentation/subscribers#test" target="_blank"><strong>' .
1261
- __('Read more about test subscribers', 'newsletter') . '</strong></a>.<br>';
1262
- $messages .= '<a href="https://www.thenewsletterplugin.com/documentation/email-sending-issues" target="_blank"><strong>' . __('Read more about delivery issues', 'newsletter') . '</strong></a>.';
1263
-
1264
- return $messages;
1265
- }
1266
-
1267
- private function set_test_subject_to($email) {
1268
- if ($email->subject == '') {
1269
- $email->subject = '[TEST] Dummy subject, it was empty (remember to set it)';
1270
- } else {
1271
- $email->subject = $email->subject . ' (TEST)';
1272
- }
1273
- }
1274
-
1275
- private function make_dummy_subscriber() {
1276
- $dummy_user = new TNP_User();
1277
- $dummy_user->id = 0;
1278
- $dummy_user->email = 'john.doe@example.org';
1279
- $dummy_user->name = 'John';
1280
- $dummy_user->surname = 'Doe';
1281
- $dummy_user->sex = 'n';
1282
- $dummy_user->language = '';
1283
- $dummy_user->ip = '';
1284
-
1285
- for ($i = 1; $i <= NEWSLETTER_PROFILE_MAX; $i++) {
1286
- $profile_key = "profile_$i";
1287
- $dummy_user->$profile_key = '';
1288
- }
1289
-
1290
- return $dummy_user;
1291
- }
1292
-
1293
- function restore_options_from_request() {
1294
-
1295
- require_once NEWSLETTER_INCLUDES_DIR . '/controls.php';
1296
- $controls = new NewsletterControls();
1297
- $options = $controls->data;
1298
-
1299
- if (isset($_POST['options']) && is_array($_POST['options'])) {
1300
- // Get all block options
1301
- //$options = stripslashes_deep($_POST['options']);
1302
- // Deserialize inline edits when
1303
- // render is preformed on saving block options
1304
- if (isset($options['inline_edits']) && !is_array($options['inline_edits'])) {
1305
- $options['inline_edits'] = $this->options_decode($options['inline_edits']);
1306
- }
1307
-
1308
- // Restore inline edits from data-json
1309
- // coming from inline editing
1310
- // and merge with current inline edit
1311
- if (isset($_POST['encoded_options'])) {
1312
- $decoded_options = $this->options_decode($_POST['encoded_options']);
1313
-
1314
- $to_merge_inline_edits = [];
1315
-
1316
- if (isset($decoded_options['inline_edits'])) {
1317
- foreach ($decoded_options['inline_edits'] as $decoded_inline_edit) {
1318
- $to_merge_inline_edits[$decoded_inline_edit['post_id'] . $decoded_inline_edit['type']] = $decoded_inline_edit;
1319
- }
1320
- }
1321
-
1322
- //Overwrite with new edited content
1323
- if (isset($options['inline_edits'])) {
1324
- foreach ($options['inline_edits'] as $inline_edit) {
1325
- $to_merge_inline_edits[$inline_edit['post_id'] . $inline_edit['type']] = $inline_edit;
1326
- }
1327
- }
1328
-
1329
- $options['inline_edits'] = array_values($to_merge_inline_edits);
1330
- $options = array_merge($decoded_options, $options);
1331
- }
1332
-
1333
- return $options;
1334
- }
1335
-
1336
- return array();
1337
- }
1338
-
1339
- public function hook_wp_ajax_tnpc_delete_preset() {
1340
-
1341
- if (!wp_verify_nonce($_POST['_wpnonce'], 'preset')) {
1342
- wp_send_json_error('Expired request');
1343
- }
1344
-
1345
- $preset_id = (int) $_REQUEST['presetId'];
1346
-
1347
- $newsletter = Newsletter::instance();
1348
-
1349
- if ($preset_id > 0) {
1350
- $preset = $newsletter->get_email($preset_id);
1351
-
1352
- if ($preset && $preset->type === self::PRESET_EMAIL_TYPE) {
1353
- Newsletter::instance()->delete_email($preset_id);
1354
- wp_send_json_success();
1355
- } else {
1356
- wp_send_json_error(__('Is not a preset!', 'newsletter'));
1357
- }
1358
- } else {
1359
- wp_send_json_error();
1360
- }
1361
- }
1362
-
1363
- }
1364
-
1365
- 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
+ add_action('newsletter_init', [$this, 'hook_newsletter_init']);
37
+
38
+ if (is_admin()) {
39
+ // Thank you to plugins which add the WP editor on other admin plugin pages...
40
+ if (isset($_GET['page']) && $_GET['page'] == 'newsletter_emails_edit') {
41
+ global $wp_actions;
42
+ $wp_actions['wp_enqueue_editor'] = 1;
43
+ }
44
+ }
45
+ }
46
+
47
+ function hook_newsletter_init() {
48
+ if (is_admin()) {
49
+ if (defined('DOING_AJAX') && DOING_AJAX) {
50
+ if (Newsletter::instance()->is_allowed()) {
51
+ add_action('wp_ajax_tnpc_render', array($this, 'tnpc_render_callback'));
52
+ add_action('wp_ajax_tnpc_preview', array($this, 'tnpc_preview_callback'));
53
+ add_action('wp_ajax_tnpc_css', array($this, 'tnpc_css_callback'));
54
+ add_action('wp_ajax_tnpc_options', array($this, 'hook_wp_ajax_tnpc_options'));
55
+ add_action('wp_ajax_tnpc_get_all_presets', array($this, 'ajax_get_all_presets'));
56
+ add_action('wp_ajax_tnpc_get_preset', array($this, 'ajax_get_preset'));
57
+ add_action('wp_ajax_tnpc_delete_preset', array($this, 'hook_wp_ajax_tnpc_delete_preset'));
58
+ add_action('wp_ajax_tnpc_regenerate_email', array($this, 'hook_wp_ajax_tnpc_regenerate_email'));
59
+ }
60
+ }
61
+ }
62
+ }
63
+
64
+ function options_decode($options) {
65
+ // Old "query string" format
66
+ if (is_string($options) && strpos($options, 'options[') !== false) {
67
+ $opts = [];
68
+ parse_str($options, $opts);
69
+ $options = $opts['options'];
70
+ }
71
+
72
+ if (is_array($options)) {
73
+ return $options;
74
+ }
75
+
76
+ // Json data should be base64 encoded, but for short time it wasn't
77
+ $tmp = json_decode($options, true);
78
+ if (is_null($tmp)) {
79
+ return json_decode(base64_decode($options), true);
80
+ } else {
81
+ return $tmp;
82
+ }
83
+ }
84
+
85
+ /**
86
+ *
87
+ * @param array $options Options array
88
+ */
89
+ function options_encode($options) {
90
+ return base64_encode(json_encode($options, JSON_HEX_TAG | JSON_HEX_AMP));
91
+ }
92
+
93
+ /**
94
+ * Builds and returns the HTML with the form fields of a specific block.
95
+ *
96
+ * @global wpdb $wpdb
97
+ */
98
+ function hook_wp_ajax_tnpc_options() {
99
+ global $wpdb;
100
+
101
+ $block = $this->get_block($_REQUEST['id']);
102
+ if (!$block) {
103
+ die('Block not found with id ' . esc_html($_REQUEST['id']));
104
+ }
105
+
106
+ if (!class_exists('NewsletterControls')) {
107
+ include NEWSLETTER_INCLUDES_DIR . '/controls.php';
108
+ }
109
+
110
+ $options = $this->options_decode(stripslashes_deep($_REQUEST['options']));
111
+ $composer = isset($_POST['composer']) ? $_POST['composer'] : [];
112
+
113
+ $context = array('type' => '');
114
+ if (isset($_REQUEST['context_type'])) {
115
+ $context['type'] = $_REQUEST['context_type'];
116
+ }
117
+
118
+ $controls = new NewsletterControls($options);
119
+ $fields = new NewsletterFields($controls);
120
+
121
+ $controls->init();
122
+ echo '<input type="hidden" name="action" value="tnpc_render">';
123
+ echo '<input type="hidden" name="id" value="' . esc_attr($_REQUEST['id']) . '">';
124
+ echo '<input type="hidden" name="context_type" value="' . esc_attr($context['type']) . '">';
125
+ $inline_edits = '';
126
+ if (isset($controls->data['inline_edits'])) {
127
+ $inline_edits = $controls->data['inline_edits'];
128
+ }
129
+ echo '<input type="hidden" name="options[inline_edits]" value="', esc_attr($this->options_encode($inline_edits)), '">';
130
+ echo "<h2>", esc_html($block["name"]), "</h2>";
131
+ include $block['dir'] . '/options.php';
132
+ wp_die();
133
+ }
134
+
135
+ /**
136
+ * Retrieves the presets list (no id in GET) or a specific preset id in GET)
137
+ */
138
+ public function ajax_get_all_presets() {
139
+ wp_send_json_success($this->get_all_preset());
140
+ }
141
+
142
+ public function ajax_get_preset() {
143
+
144
+ if (empty($_REQUEST['id'])) {
145
+ wp_send_json_error([
146
+ 'msg' => __('Invalid preset ID')
147
+ ]);
148
+ }
149
+
150
+ $preset_id = $_REQUEST['id'];
151
+ $preset_content = $this->get_preset_content($preset_id);
152
+ $global_options = $this->get_preset_global_options($preset_id);
153
+
154
+ wp_send_json_success([
155
+ 'content' => $preset_content,
156
+ 'globalOptions' => $global_options,
157
+ ]);
158
+ }
159
+
160
+ private function get_preset_content($preset_id) {
161
+
162
+ $content = '';
163
+
164
+ if ($this->is_a_tnp_default_preset($preset_id)) {
165
+
166
+ // Get preset from file
167
+ $preset = $this->get_preset_from_file($preset_id);
168
+
169
+ foreach ($preset->blocks as $item) {
170
+ ob_start();
171
+ $this->render_block($item->block, true, (array) $item->options);
172
+ $content .= trim(ob_get_clean());
173
+ }
174
+ } else {
175
+
176
+ // Get preset from db
177
+ $preset_email = $this->get_email(intval($preset_id));
178
+ $global_options = $this->extract_global_options_from($preset_email);
179
+ $content = $this->regenerate_email_blocks($preset_email->message, $global_options);
180
+ }
181
+
182
+ return $content;
183
+ }
184
+
185
+ private function get_preset_global_options($preset_id) {
186
+
187
+ if ($this->is_a_tnp_default_preset($preset_id)) {
188
+ return [];
189
+ }
190
+
191
+ // Get preset from db
192
+ $preset_email = $this->get_email(intval($preset_id));
193
+ $global_options = $this->extract_global_options_from($preset_email);
194
+
195
+ return $global_options;
196
+ }
197
+
198
+ private function extract_global_options_from($email) {
199
+ $global_options = [];
200
+ foreach ($email->options as $global_option_name => $global_option) {
201
+ if (strpos($global_option_name, 'composer_') === 0) {
202
+ $global_options[str_replace('composer_', '', $global_option_name)] = $global_option;
203
+ }
204
+ }
205
+
206
+ return $global_options;
207
+ }
208
+
209
+ private function is_a_tnp_default_preset($preset_id) {
210
+ return in_array($preset_id, self::$PRESETS_LIST);
211
+ }
212
+
213
+ private function get_all_preset() {
214
+
215
+ $content = "<div class='tnpc-preset-container'>";
216
+
217
+ if ($this->is_normal_context_request()) {
218
+ $content .= "<div class='tnpc-preset-legacy-themes'><a href='" . $this->get_admin_page_url('theme') . "'>" . __('Looking for legacy themes?', 'newsletter') . "</a></div>";
219
+ }
220
+
221
+ // LOAD USER PRESETS
222
+ $user_preset_list = $this->get_emails(self::PRESET_EMAIL_TYPE);
223
+
224
+ foreach ($user_preset_list as $user_preset) {
225
+
226
+ $default_icon_url = NEWSLETTER_URL . "/emails/presets/default-icon.png?ver=2";
227
+ $preset_name = $user_preset->subject;
228
+ $delete_preset_text = __('Delete', 'newsletter');
229
+ $edit_preset_text = __('Edit', 'newsletter');
230
+
231
+ // esc_js() assumes the string will be in single quote (arghhh!!!)
232
+ $onclick_edit = 'tnpc_edit_preset(' . ((int) $user_preset->id) . ', \'' . esc_js($preset_name) . '\', event)';
233
+ $onclick_delete = 'tnpc_delete_preset(' . ((int) $user_preset->id) . ', \'' . esc_js($preset_name) . '\', event)';
234
+ $onclick_load = 'tnpc_load_preset(' . ((int) $user_preset->id) . ', \'' . esc_js($preset_name) . '\', event)';
235
+
236
+ $content .= "<div class='tnpc-preset' onclick='" . esc_attr($onclick_load) . "'>\n";
237
+ $content .= "<img src='$default_icon_url' title='" . esc_attr($preset_name) . "' alt='" . esc_attr($preset_name) . "'>\n";
238
+ $content .= "<span class='tnpc-preset-label'>" . esc_html($user_preset->subject) . "</span>\n";
239
+ $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";
240
+ $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";
241
+ $content .= "</div>";
242
+ }
243
+
244
+ // LOAD TNP PRESETS
245
+ foreach (self::$PRESETS_LIST as $id) {
246
+ $preset = $this->get_preset_from_file($id);
247
+ $preset_name = esc_html($preset->name);
248
+ $content .= "<div class='tnpc-preset' onclick='tnpc_load_preset(\"$id\")'>";
249
+ $content .= "<img src='$preset->icon' title='$preset_name' alt='$preset_name'/>";
250
+ $content .= "<span class='tnpc-preset-label'>$preset_name</span>";
251
+ $content .= "</div>";
252
+ }
253
+
254
+ if ($this->is_normal_context_request()) {
255
+ $content .= $this->get_automated_spot_element();
256
+ $content .= $this->get_autoresponder_spot_element();
257
+ $content .= $this->get_raw_html_preset_element();
258
+ }
259
+
260
+ return $content;
261
+ }
262
+
263
+ private function is_normal_context_request() {
264
+ return empty($_REQUEST['context_type']);
265
+ }
266
+
267
+ private function is_automated_context_request() {
268
+ return isset($_REQUEST['context_type']) && $_REQUEST['context_type'] === 'automated';
269
+ }
270
+
271
+ private function is_autoresponder_context_request() {
272
+ return isset($_REQUEST['context_type']) && $_REQUEST['context_type'] === 'autoresponder';
273
+ }
274
+
275
+ private function get_automated_spot_element() {
276
+ $result = "<div class='tnpc-preset'>";
277
+ if (class_exists('NewsletterAutomated')) {
278
+ $result .= "<a href='?page=newsletter_automated_index'>";
279
+ } else {
280
+ $result .= "<a href='https://www.thenewsletterplugin.com/automated?utm_source=composer&utm_campaign=plugin&utm_medium=automated'>";
281
+ }
282
+ $result .= "<img src='" . plugins_url('newsletter') . "/emails/images/automated.png' title='Automated addon' alt='Automated'/>";
283
+ $result .= "<span class='tnpc-preset-label'>Daily, weekly and monthly newsletters</span></a>";
284
+ $result .= "</div>";
285
+
286
+ return $result;
287
+ }
288
+
289
+ private function get_autoresponder_spot_element() {
290
+ $result = "<div class='tnpc-preset'>";
291
+ if (class_exists('NewsletterAutoresponder')) {
292
+ $result .= "<a href='?page=newsletter_autoresponder_index'>";
293
+ } else {
294
+ $result .= "<a href='https://www.thenewsletterplugin.com/autoresponder?utm_source=composer&utm_campaign=plugin&utm_medium=autoresponder' target='_blank'>";
295
+ }
296
+ $result .= "<img src='" . plugins_url('newsletter') . "/emails/images/autoresponder.png' title='Autoresponder addon' alt='Autoresponder'/>";
297
+ $result .= "<span class='tnpc-preset-label'>Autoresponders</span></a>";
298
+ $result .= "</div>";
299
+
300
+ return $result;
301
+ }
302
+
303
+ private function get_raw_html_preset_element() {
304
+
305
+ $result = "<div class='tnpc-preset tnpc-preset-html' onclick='location.href=\"" . wp_nonce_url('admin.php?page=newsletter_emails_new&id=rawhtml', 'newsletter-new') . "\"'>";
306
+ $result .= "<img src='" . plugins_url('newsletter') . "/emails/images/rawhtml.png' title='RAW HTML' alt='RAW'/>";
307
+ $result .= "<span class='tnpc-preset-label'>Raw HTML</span>";
308
+ $result .= "</div>";
309
+
310
+ $result .= "<div class='clear'></div>";
311
+ $result .= "</div>";
312
+
313
+ return $result;
314
+ }
315
+
316
+ function has_dynamic_blocks($theme) {
317
+ preg_match_all('/data-json="(.*?)"/m', $theme, $matches, PREG_PATTERN_ORDER);
318
+ foreach ($matches[1] as $match) {
319
+ $a = html_entity_decode($match, ENT_QUOTES, 'UTF-8');
320
+ $options = $this->options_decode($a);
321
+
322
+ $block = $this->get_block($options['block_id']);
323
+ if (!$block) {
324
+ continue;
325
+ }
326
+ if ($block['type'] == 'dynamic') {
327
+ return true;
328
+ }
329
+ }
330
+ return false;
331
+ }
332
+
333
+ /**
334
+ * Regenerates a saved composed email rendering each block. Regeneration is
335
+ * conditioned (possibly) by the context. The context is usually passed to blocks
336
+ * so they can act in the right manner.
337
+ *
338
+ * $context contains a type and, for automated, the last_run.
339
+ *
340
+ * $email can actually be even a string containing the full newsletter HTML code.
341
+ *
342
+ * @param TNP_Email $email
343
+ * @return string
344
+ */
345
+ function regenerate($email, $context = []) {
346
+
347
+ $context = array_merge(['last_run' => 0, 'type' => ''], $context);
348
+
349
+ $composer = [];
350
+ foreach ($email->options as $k=>$v) {
351
+ if (strpos($k, 'composer_') !== 0) continue;
352
+ $composer[substr($k, 9)] = $v;
353
+ }
354
+
355
+
356
+ preg_match_all('/data-json="(.*?)"/m', $email->message, $matches, PREG_PATTERN_ORDER);
357
+
358
+ $result = '';
359
+ $subject = '';
360
+
361
+ foreach ($matches[1] as $match) {
362
+ $a = html_entity_decode($match, ENT_QUOTES, 'UTF-8');
363
+ $options = $this->options_decode($a);
364
+
365
+ $block = $this->get_block($options['block_id']);
366
+ if (!$block) {
367
+ $this->logger->debug('Unable to load the block ' . $options['block_id']);
368
+ //continue;
369
+ }
370
+
371
+ ob_start();
372
+ $out = $this->render_block($options['block_id'], true, $options, $context, $composer);
373
+ if (is_array($out)) {
374
+ if ($out['return_empty_message'] || $out['stop']) {
375
+ return false;
376
+ }
377
+ if ($out['skip']) {
378
+ continue;
379
+ }
380
+ if (empty($subject) && !empty($out['subject'])) {
381
+ $subject = $out['subject'];
382
+ }
383
+ }
384
+ $block_html = ob_get_clean();
385
+ $result .= $block_html;
386
+ }
387
+
388
+ $email->message = TNP_Composer::get_html_open($email) . TNP_Composer::get_main_wrapper_open($email) .
389
+ $result . TNP_Composer::get_main_wrapper_close($email) . TNP_Composer::get_html_close($email);
390
+ $email->subject = $subject;
391
+ return true;
392
+ }
393
+
394
+ function remove_block_data($text) {
395
+ // TODO: Lavorare!
396
+ return $text;
397
+ }
398
+
399
+ static function get_outlook_wrapper_open($width = 600) {
400
+ 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]-->';
401
+ }
402
+
403
+ static function get_outlook_wrapper_close() {
404
+ return "<!--[if mso | IE]></td></tr></table><![endif]-->";
405
+ }
406
+
407
+ function hook_safe_style_css($rules) {
408
+ $rules[] = 'display';
409
+ return $rules;
410
+ }
411
+
412
+ /**
413
+ * Renders a block identified by its id, using the block options and adding a wrapper
414
+ * if required (for the first block rendering).
415
+ *
416
+ * @param string $block_id
417
+ * @param boolean $wrapper
418
+ * @param array $options
419
+ * @param array $context
420
+ * @param array $composer
421
+ */
422
+ function render_block($block_id = null, $wrapper = false, $options = [], $context = [], $composer = []) {
423
+ static $kses_style_filter = false;
424
+ include_once NEWSLETTER_INCLUDES_DIR . '/helper.php';
425
+
426
+ //Remove 'options_composer_' prefix
427
+ $composer_defaults = [];
428
+ foreach (TNP_Composer::get_global_style_defaults() as $global_option_name => $global_option) {
429
+ $composer_defaults[str_replace('options_composer_', '', $global_option_name)] = $global_option;
430
+ }
431
+ $composer = array_merge($composer_defaults, $composer);
432
+
433
+ $width = 600;
434
+ $font_family = 'Helvetica, Arial, sans-serif';
435
+
436
+ $global_title_font_family = $composer['title_font_family'];
437
+ $global_title_font_size = $composer['title_font_size'];
438
+ $global_title_font_color = $composer['title_font_color'];
439
+ $global_title_font_weight = $composer['title_font_weight'];
440
+
441
+ $global_text_font_family = $composer['text_font_family'];
442
+ $global_text_font_size = $composer['text_font_size'];
443
+ $global_text_font_color = $composer['text_font_color'];
444
+ $global_text_font_weight = $composer['text_font_weight'];
445
+
446
+ $global_button_font_family = $composer['button_font_family'];
447
+ $global_button_font_size = $composer['button_font_size'];
448
+ $global_button_font_color = $composer['button_font_color'];
449
+ $global_button_font_weight = $composer['button_font_weight'];
450
+ $global_button_background_color = $composer['button_background_color'];
451
+
452
+ $global_block_background = $composer['block_background'];
453
+
454
+ $info = Newsletter::instance()->get_options('info');
455
+
456
+ // Just in case...
457
+ if (!is_array($options)) {
458
+ $options = array();
459
+ }
460
+
461
+ // This code filters the HTML to remove javascript and unsecure attributes and enable the
462
+ // "display" rule for CSS which is needed in blocks to force specific "block" or "inline" or "table".
463
+ add_filter('safe_style_css', [$this, 'hook_safe_style_css'], 9999);
464
+ $options = wp_kses_post_deep($options);
465
+ remove_filter('safe_style_css', [$this, 'hook_safe_style_css']);
466
+
467
+ $block_options = get_option('newsletter_main');
468
+
469
+ $block = $this->get_block($block_id);
470
+
471
+ if (!isset($context['type']))
472
+ $context['type'] = '';
473
+
474
+ // Block not found
475
+ if (!$block) {
476
+ if ($wrapper) {
477
+ 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), '">';
478
+ echo '<tr>';
479
+ echo '<td data-options="" bgcolor="#ffffff" align="center" style="padding: 0; font-family: Helvetica, Arial, sans-serif;" class="edit-block">';
480
+ }
481
+ echo $this->get_outlook_wrapper_open($width);
482
+
483
+ echo '<p>Ops, this block type is not avalable.</p>';
484
+
485
+ echo $this->get_outlook_wrapper_close();
486
+
487
+ if ($wrapper) {
488
+ echo '</td></tr></table>';
489
+ }
490
+ return;
491
+ }
492
+
493
+ $out = ['subject' => '', 'return_empty_message' => false, 'stop' => false, 'skip' => false];
494
+
495
+ $dir = is_rtl() ? 'rtl' : 'ltr';
496
+ $align_left = is_rtl() ? 'right' : 'left';
497
+ $align_right = is_rtl() ? 'left' : 'right';
498
+
499
+ ob_start();
500
+ $logger = $this->logger;
501
+ include $block['dir'] . '/block.php';
502
+ $content = trim(ob_get_clean());
503
+
504
+ if (empty($content)) {
505
+ return $out;
506
+ }
507
+
508
+ $common_defaults = array(
509
+ 'block_padding_top' => 0,
510
+ 'block_padding_bottom' => 0,
511
+ 'block_padding_right' => 0,
512
+ 'block_padding_left' => 0,
513
+ 'block_background' => '',
514
+ 'block_background_2' => '',
515
+ 'block_width' => 600,
516
+ 'block_align' => 'center'
517
+ );
518
+
519
+ $options = array_merge($common_defaults, $options);
520
+
521
+ // Obsolete
522
+ $content = str_replace('{width}', $width, $content);
523
+
524
+ $content = $this->inline_css($content, true);
525
+
526
+ // CSS driven by the block
527
+ // Requited for the server side parsing and rendering
528
+ $options['block_id'] = $block_id;
529
+
530
+ $options['block_padding_top'] = (int) str_replace('px', '', $options['block_padding_top']);
531
+ $options['block_padding_bottom'] = (int) str_replace('px', '', $options['block_padding_bottom']);
532
+ $options['block_padding_right'] = (int) str_replace('px', '', $options['block_padding_right']);
533
+ $options['block_padding_left'] = (int) str_replace('px', '', $options['block_padding_left']);
534
+
535
+ $block_background = empty($options['block_background']) ? $global_block_background : $options['block_background'];
536
+
537
+ // Internal TD wrapper
538
+ $style = 'text-align: center; ';
539
+ $style .= 'width: 100% !important; ';
540
+ $style .= 'line-height: normal !important; ';
541
+ $style .= 'letter-spacing: normal; ';
542
+ $style .= 'padding-top: ' . $options['block_padding_top'] . 'px; ';
543
+ $style .= 'padding-left: ' . $options['block_padding_left'] . 'px; ';
544
+ $style .= 'padding-right: ' . $options['block_padding_right'] . 'px; ';
545
+ $style .= 'padding-bottom: ' . $options['block_padding_bottom'] . 'px; ';
546
+ $style .= 'background-color: ' . $block_background . ';';
547
+
548
+ if (isset($options['block_background_gradient'])) {
549
+ $style .= 'background: linear-gradient(180deg, ' . $block_background . ' 0%, ' . $options['block_background_2'] . ' 100%);';
550
+ }
551
+
552
+ $data = $this->options_encode($options);
553
+ // First time block creation wrapper
554
+ if ($wrapper) {
555
+ 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";
556
+ echo "<tr>";
557
+ echo '<td align="center" style="padding: 0;" class="edit-block">', "\n";
558
+ }
559
+
560
+ // Container that fixes the width and makes the block responsive
561
+ echo $this->get_outlook_wrapper_open($options['block_width']);
562
+
563
+ 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";
564
+ echo "<tr>";
565
+ echo '<td align="', esc_attr($options['block_align']), '" style="', esc_attr($style), '" bgcolor="', esc_attr($block_background), '" width="100%">';
566
+
567
+ //echo "<!-- block generated content -->\n";
568
+ echo trim($content);
569
+ //echo "\n<!-- /block generated content -->\n";
570
+
571
+ echo "</td></tr></table>";
572
+ echo $this->get_outlook_wrapper_close();
573
+
574
+ // First time block creation wrapper
575
+ if ($wrapper) {
576
+ echo "</td></tr></table>";
577
+ }
578
+
579
+ return $out;
580
+ }
581
+
582
+ /**
583
+ * Ajax call to render a block with a new set of options after the settings popup
584
+ * has been saved.
585
+ *
586
+ * @param type $block_id
587
+ * @param type $wrapper
588
+ */
589
+ function tnpc_render_callback() {
590
+ if (!check_ajax_referer('save')) {
591
+ $this->dienow('Expired request');
592
+ }
593
+
594
+ $block_id = $_POST['id'];
595
+ $wrapper = isset($_POST['full']);
596
+ $options = $this->restore_options_from_request();
597
+
598
+ $this->render_block($block_id, $wrapper, $options, [], $_POST['composer']);
599
+ wp_die();
600
+ }
601
+
602
+ function hook_wp_ajax_tnpc_regenerate_email() {
603
+
604
+ $content = stripslashes($_POST['content']);
605
+ $global_options = $_POST['composer'];
606
+
607
+ $regenerated_content = $this->regenerate_email_blocks($content, $global_options);
608
+
609
+ wp_send_json_success([
610
+ 'content' => $regenerated_content,
611
+ 'message' => __('Successfully updated', 'newsletter')
612
+ ]);
613
+ }
614
+
615
+ private function regenerate_email_blocks($content, $global_options) {
616
+
617
+ $raw_block_options = $this->extract_encoded_blocks_options($content);
618
+
619
+ $regenerated_content = '';
620
+
621
+ foreach ($raw_block_options as $raw_block_option) {
622
+
623
+ /* $a = html_entity_decode( $raw_block_option, ENT_QUOTES, 'UTF-8' );
624
+ $block_options = $this->options_decode( $a ); */
625
+
626
+ $block_options = $this->options_decode($raw_block_option);
627
+
628
+ $block = $this->get_block($block_options['block_id']);
629
+ if (!$block) {
630
+ $this->logger->debug('Unable to load the block ' . $block_options['block_id']);
631
+ }
632
+
633
+ ob_start();
634
+ $this->render_block($block_options['block_id'], true, $block_options, [], $global_options);
635
+ $block_html = ob_get_clean();
636
+
637
+ $regenerated_content .= $block_html;
638
+ }
639
+
640
+ return $regenerated_content;
641
+ }
642
+
643
+ /**
644
+ * @param string $html_email_content Email html content
645
+ *
646
+ * @return string[] Encoded options of email blocks
647
+ */
648
+ private function extract_encoded_blocks_options($html_email_content) {
649
+
650
+ preg_match_all('/data-json="(.*?)"/m', $html_email_content, $raw_block_options, PREG_PATTERN_ORDER);
651
+
652
+ return $raw_block_options[1];
653
+ }
654
+
655
+ function tnpc_preview_callback() {
656
+ $email = Newsletter::instance()->get_email($_REQUEST['id'], ARRAY_A);
657
+
658
+ if (empty($email)) {
659
+ echo 'Wrong email identifier';
660
+ return;
661
+ }
662
+
663
+ echo $email['message'];
664
+
665
+ wp_die();
666
+ }
667
+
668
+ function tnpc_css_callback() {
669
+ include NEWSLETTER_DIR . '/emails/tnp-composer/css/newsletter.css';
670
+ wp_die();
671
+ }
672
+
673
+ /** Returns the correct admin page to edit the newsletter with the correct editor. */
674
+ function get_editor_url($email_id, $editor_type) {
675
+ switch ($editor_type) {
676
+ case NewsletterEmails::EDITOR_COMPOSER:
677
+ return admin_url("admin.php") . '?page=newsletter_emails_composer&id=' . $email_id;
678
+ case NewsletterEmails::EDITOR_HTML:
679
+ return admin_url("admin.php") . '?page=newsletter_emails_editorhtml&id=' . $email_id;
680
+ case NewsletterEmails::EDITOR_TINYMCE:
681
+ return admin_url("admin.php") . '?page=newsletter_emails_editortinymce&id=' . $email_id;
682
+ }
683
+ }
684
+
685
+ /**
686
+ * Returns the button linked to the correct "edit" page for the passed newsletter. The edit page can be an editor
687
+ * or the targeting page (it depends on newsletter status).
688
+ *
689
+ * @param TNP_Email $email
690
+ */
691
+ function get_edit_button($email, $only_icon = false) {
692
+
693
+ $editor_type = $this->get_editor_type($email);
694
+ if ($email->status == 'new') {
695
+ $edit_url = $this->get_editor_url($email->id, $editor_type);
696
+ } else {
697
+ $edit_url = 'admin.php?page=newsletter_emails_edit&id=' . $email->id;
698
+ }
699
+ switch ($editor_type) {
700
+ case NewsletterEmails::EDITOR_COMPOSER:
701
+ $icon_class = 'th-large';
702
+ break;
703
+ case NewsletterEmails::EDITOR_HTML:
704
+ $icon_class = 'code';
705
+ break;
706
+ default:
707
+ $icon_class = 'edit';
708
+ break;
709
+ }
710
+ if ($only_icon) {
711
+ return '<a class="button-primary" href="' . $edit_url . '" title="' . esc_attr__('Edit', 'newsletter') . '">' .
712
+ '<i class="fas fa-' . $icon_class . '"></i></a>';
713
+ } else {
714
+ return '<a class="button-primary" href="' . $edit_url . '" title="' . esc_attr__('Edit', 'newsletter') . '">' .
715
+ '<i class="fas fa-' . $icon_class . '"></i> ' . __('Edit', 'newsletter') . '</a>';
716
+ }
717
+ }
718
+
719
+ /** Returns the correct editor type for the provided newsletter. Contains backward compatibility code. */
720
+ function get_editor_type($email) {
721
+ $email = (object) $email;
722
+ $editor_type = $email->editor;
723
+
724
+ // Backward compatibility
725
+ $email_options = maybe_unserialize($email->options);
726
+ if (isset($email_options['composer'])) {
727
+ $editor_type = NewsletterEmails::EDITOR_COMPOSER;
728
+ }
729
+ // End backward compatibility
730
+
731
+ return $editor_type;
732
+ }
733
+
734
+ /**
735
+ *
736
+ * @param type $action
737
+ * @param type $user
738
+ * @param type $email
739
+ * @return type
740
+ * @global wpdb $wpdb
741
+ */
742
+ function hook_newsletter_action($action, $user, $email) {
743
+ global $wpdb;
744
+
745
+ switch ($action) {
746
+ case 'v':
747
+ case 'view':
748
+ $id = $_GET['id'];
749
+ if ($id == 'last') {
750
+ $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");
751
+ } else {
752
+ $email = $this->get_email($_GET['id']);
753
+ }
754
+ if (empty($email)) {
755
+ header("HTTP/1.0 404 Not Found");
756
+ die('Email not found');
757
+ }
758
+
759
+ if (!Newsletter::instance()->is_allowed()) {
760
+
761
+ if ($email->status == 'new') {
762
+ header("HTTP/1.0 404 Not Found");
763
+ die('Not sent yet');
764
+ }
765
+
766
+ if ($email->private == 1) {
767
+ if (!$user) {
768
+ header("HTTP/1.0 404 Not Found");
769
+ die('No available for online view');
770
+ }
771
+ $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));
772
+ if (!$sent) {
773
+ header("HTTP/1.0 404 Not Found");
774
+ die('No available for online view');
775
+ }
776
+ }
777
+ }
778
+
779
+
780
+ header('Content-Type: text/html;charset=UTF-8');
781
+ header('X-Robots-Tag: noindex,nofollow,noarchive');
782
+ header('Cache-Control: no-cache,no-store,private');
783
+
784
+ echo $this->replace($email->message, $user, $email);
785
+
786
+ die();
787
+ break;
788
+
789
+ case 'emails-css':
790
+ $email_id = (int) $_GET['id'];
791
+
792
+ $body = Newsletter::instance()->get_email_field($email_id, 'message');
793
+
794
+ $x = strpos($body, '<style');
795
+ if ($x === false)
796
+ return;
797
+
798
+ $x = strpos($body, '>', $x);
799
+ $y = strpos($body, '</style>');
800
+
801
+ header('Content-Type: text/css;charset=UTF-8');
802
+
803
+ echo substr($body, $x + 1, $y - $x - 1);
804
+
805
+ die();
806
+ break;
807
+
808
+ case 'emails-composer-css':
809
+ header('Cache: no-cache');
810
+ header('Content-Type: text/css');
811
+ echo $this->get_composer_css();
812
+ die();
813
+ break;
814
+
815
+ case 'emails-preview':
816
+ if (!Newsletter::instance()->is_allowed()) {
817
+ die('Not enough privileges');
818
+ }
819
+
820
+ if (!check_admin_referer('view')) {
821
+ die();
822
+ }
823
+
824
+ $theme_id = $_GET['id'];
825
+ $theme = $this->themes->get_theme($theme_id);
826
+
827
+ // Used by theme code
828
+ $theme_options = $this->themes->get_options($theme_id);
829
+
830
+ $theme_url = $theme['url'];
831
+
832
+ header('Content-Type: text/html;charset=UTF-8');
833
+
834
+ include $theme['dir'] . '/theme.php';
835
+
836
+ die();
837
+ break;
838
+
839
+ case 'emails-preview-text':
840
+ header('Content-Type: text/plain;charset=UTF-8');
841
+ if (!Newsletter::instance()->is_allowed()) {
842
+ die('Not enough privileges');
843
+ }
844
+
845
+ if (!check_admin_referer('view')) {
846
+ die();
847
+ }
848
+
849
+ // Used by theme code
850
+ $theme_options = $this->get_current_theme_options();
851
+
852
+ $file = include $theme['dir'] . '/theme-text.php';
853
+
854
+ if (is_file($file)) {
855
+ include $file;
856
+ }
857
+
858
+ die();
859
+ break;
860
+
861
+ case 'emails-create':
862
+ // Newsletter from themes are created on frontend context because sometime WP themes change the way the content,
863
+ // excerpt, thumbnail are extracted.
864
+ if (!Newsletter::instance()->is_allowed()) {
865
+ die('Not enough privileges');
866
+ }
867
+
868
+ require_once NEWSLETTER_INCLUDES_DIR . '/controls.php';
869
+ $controls = new NewsletterControls();
870
+
871
+ if (!$controls->is_action('create')) {
872
+ die('Wrong call');
873
+ }
874
+
875
+ $theme_id = $controls->data['id'];
876
+ $theme = $this->themes->get_theme($theme_id);
877
+
878
+ if (!$theme) {
879
+ die('invalid theme');
880
+ }
881
+
882
+ $this->themes->save_options($theme_id, $controls->data);
883
+
884
+ $email = array();
885
+ $email['status'] = 'new';
886
+ $email['subject'] = ''; //__('Here the email subject', 'newsletter');
887
+ $email['track'] = Newsletter::instance()->options['track'];
888
+ $email['send_on'] = time();
889
+ $email['editor'] = NewsletterEmails::EDITOR_TINYMCE;
890
+ $email['type'] = 'message';
891
+
892
+ $theme_options = $this->themes->get_options($theme_id);
893
+
894
+ $theme_url = $theme['url'];
895
+ $theme_subject = '';
896
+
897
+ ob_start();
898
+ include $theme['dir'] . '/theme.php';
899
+ $email['message'] = ob_get_clean();
900
+
901
+ if (!empty($theme_subject)) {
902
+ $email['subject'] = $theme_subject;
903
+ }
904
+
905
+ if (file_exists($theme['dir'] . '/theme-text.php')) {
906
+ ob_start();
907
+ include $theme['dir'] . '/theme-text.php';
908
+ $email['message_text'] = ob_get_clean();
909
+ } else {
910
+ $email['message_text'] = 'You need a modern email client to read this email. Read it online: {email_url}.';
911
+ }
912
+
913
+ $email = $this->save_email($email);
914
+
915
+ $edit_url = $this->get_editor_url($email->id, $email->editor);
916
+
917
+ header('Location: ' . $edit_url);
918
+
919
+ die();
920
+ break;
921
+ }
922
+ }
923
+
924
+ function admin_menu() {
925
+ $this->add_menu_page('index', 'Newsletters');
926
+ $this->add_admin_page('list', 'Email List');
927
+ $this->add_admin_page('new', 'Email New');
928
+ $this->add_admin_page('edit', 'Email Edit');
929
+ $this->add_admin_page('theme', 'Email Themes');
930
+ $this->add_admin_page('composer', 'The Composer');
931
+ $this->add_admin_page('editorhtml', 'HTML Editor');
932
+ $this->add_admin_page('editortinymce', 'TinyMCE Editor');
933
+ }
934
+
935
+ /**
936
+ * Builds a block data structure starting from the folder containing the block
937
+ * files.
938
+ *
939
+ * @param string $dir
940
+ * @return array | WP_Error
941
+ */
942
+ function build_block($dir) {
943
+ $dir = realpath($dir);
944
+ $dir = wp_normalize_path($dir);
945
+ $full_file = $dir . '/block.php';
946
+ if (!is_file($full_file)) {
947
+ return new WP_Error('1', 'Missing block.php file in ' . $dir);
948
+ }
949
+
950
+ $wp_content_dir = wp_normalize_path(realpath(WP_CONTENT_DIR));
951
+
952
+ $relative_dir = substr($dir, strlen($wp_content_dir));
953
+ $file = basename($dir);
954
+
955
+ $data = get_file_data($full_file, ['name' => 'Name', 'section' => 'Section', 'description' => 'Description', 'type' => 'Type']);
956
+ $defaults = ['section' => 'content', 'name' => ucfirst($file), 'descritpion' => '', 'icon' => plugins_url('newsletter') . '/admin/images/block-icon.png'];
957
+ $data = array_merge($defaults, $data);
958
+
959
+ if (is_file($dir . '/icon.png')) {
960
+ $data['icon'] = content_url($relative_dir . '/icon.png');
961
+ }
962
+
963
+ $data['id'] = sanitize_key($file);
964
+
965
+ // Absolute path of the block files
966
+ $data['dir'] = $dir;
967
+ $data['url'] = content_url($relative_dir);
968
+
969
+ return $data;
970
+ }
971
+
972
+ /**
973
+ *
974
+ * @param type $dir
975
+ * @return type
976
+ */
977
+ function scan_blocks_dir($dir) {
978
+ $dir = realpath($dir);
979
+ if (!$dir) {
980
+ return [];
981
+ }
982
+ $dir = wp_normalize_path($dir);
983
+
984
+ $list = [];
985
+ $handle = opendir($dir);
986
+ while ($file = readdir($handle)) {
987
+
988
+ $data = $this->build_block($dir . '/' . $file);
989
+
990
+ if (is_wp_error($data)) {
991
+ $this->logger->error($data);
992
+ continue;
993
+ }
994
+ $list[$data['id']] = $data;
995
+ }
996
+ closedir($handle);
997
+ return $list;
998
+ }
999
+
1000
+ /**
1001
+ * Array of arrays with every registered block and legacy block converted to the new
1002
+ * format.
1003
+ *
1004
+ * @return array
1005
+ */
1006
+ function get_blocks() {
1007
+
1008
+ if (!is_null($this->blocks)) {
1009
+ return $this->blocks;
1010
+ }
1011
+
1012
+ $this->blocks = $this->scan_blocks_dir(__DIR__ . '/blocks');
1013
+
1014
+ $extended = $this->scan_blocks_dir(WP_CONTENT_DIR . '/extensions/newsletter/blocks');
1015
+
1016
+ $this->blocks = array_merge($extended, $this->blocks);
1017
+
1018
+ $dirs = apply_filters('newsletter_blocks_dir', array());
1019
+
1020
+ //$this->logger->debug('Block dirs:');
1021
+ //$this->logger->debug($dirs);
1022
+
1023
+ foreach ($dirs as $dir) {
1024
+ $list = $this->scan_blocks_dir($dir);
1025
+ $this->blocks = array_merge($list, $this->blocks);
1026
+ }
1027
+
1028
+ do_action('newsletter_register_blocks');
1029
+
1030
+ foreach (TNP_Composer::$block_dirs as $dir) {
1031
+ $block = $this->build_block($dir);
1032
+ if (is_wp_error($block)) {
1033
+ $this->logger->error($block);
1034
+ continue;
1035
+ }
1036
+ if (!isset($this->blocks[$block['id']])) {
1037
+ $this->blocks[$block['id']] = $block;
1038
+ } else {
1039
+ $this->logger->error('The block "' . $block['id'] . '" has already been registered');
1040
+ }
1041
+ }
1042
+
1043
+ $this->blocks = array_reverse($this->blocks);
1044
+ return $this->blocks;
1045
+ }
1046
+
1047
+ /**
1048
+ * Return a single block (associative array) checking for legacy ID as well.
1049
+ *
1050
+ * @param string $id
1051
+ * @return array
1052
+ */
1053
+ function get_block($id) {
1054
+ switch ($id) {
1055
+ case 'content-03-text.block':
1056
+ $id = 'text';
1057
+ break;
1058
+ case 'footer-03-social.block':
1059
+ $id = 'social';
1060
+ break;
1061
+ case 'footer-02-canspam.block':
1062
+ $id = 'canspam';
1063
+ break;
1064
+ case 'content-05-image.block':
1065
+ $id = 'image';
1066
+ break;
1067
+ case 'header-01-header.block':
1068
+ $id = 'header';
1069
+ break;
1070
+ case 'footer-01-footer.block':
1071
+ $id = 'footer';
1072
+ break;
1073
+ case 'content-02-heading.block':
1074
+ $id = 'heading';
1075
+ break;
1076
+ case 'content-07-twocols.block':
1077
+ case 'content-06-posts.block':
1078
+ $id = 'posts';
1079
+ break;
1080
+ case 'content-04-cta.block':
1081
+ $id = 'cta';
1082
+ break;
1083
+ case 'content-01-hero.block':
1084
+ $id = 'hero';
1085
+ break;
1086
+ // case 'content-02-heading.block': $id = '/plugins/newsletter/emails/blocks/heading';
1087
+ // break;
1088
+ }
1089
+
1090
+ // Conversion for old full path ID
1091
+ $id = sanitize_key(basename($id));
1092
+
1093
+ // TODO: Correct id for compatibility
1094
+ $blocks = $this->get_blocks();
1095
+ if (!isset($blocks[$id])) {
1096
+ return null;
1097
+ }
1098
+ return $blocks[$id];
1099
+ }
1100
+
1101
+ function scan_presets_dir($dir = null) {
1102
+
1103
+ if (is_null($dir)) {
1104
+ $dir = __DIR__ . '/presets';
1105
+ }
1106
+
1107
+ if (!is_dir($dir)) {
1108
+ return array();
1109
+ }
1110
+
1111
+ $handle = opendir($dir);
1112
+ $list = array();
1113
+ $relative_dir = substr($dir, strlen(WP_CONTENT_DIR));
1114
+ while ($file = readdir($handle)) {
1115
+
1116
+ if ($file == '.' || $file == '..')
1117
+ continue;
1118
+
1119
+ // The block unique key, we should find out how to build it, maybe an hash of the (relative) dir?
1120
+ $preset_id = sanitize_key($file);
1121
+
1122
+ $full_file = $dir . '/' . $file . '/preset.json';
1123
+
1124
+ if (!is_file($full_file)) {
1125
+ continue;
1126
+ }
1127
+
1128
+ $icon = content_url($relative_dir . '/' . $file . '/icon.png');
1129
+
1130
+ $list[$preset_id] = $icon;
1131
+ }
1132
+ closedir($handle);
1133
+ return $list;
1134
+ }
1135
+
1136
+ function get_preset_from_file($id, $dir = null) {
1137
+
1138
+ if (is_null($dir)) {
1139
+ $dir = __DIR__ . '/presets';
1140
+ }
1141
+
1142
+ $id = $this->sanitize_file_name($id);
1143
+
1144
+ if (!is_dir($dir . '/' . $id) || !in_array($id, self::$PRESETS_LIST)) {
1145
+ return array();
1146
+ }
1147
+
1148
+ $json_content = file_get_contents("$dir/$id/preset.json");
1149
+ $json_content = str_replace("{placeholder_base_url}", plugins_url('newsletter') . '/emails/presets', $json_content);
1150
+ $json = json_decode($json_content);
1151
+ $json->icon = NEWSLETTER_URL . "/emails/presets/$id/icon.png?ver=2";
1152
+
1153
+ return $json;
1154
+ }
1155
+
1156
+ function get_composer_css() {
1157
+ $css = file_get_contents(__DIR__ . '/tnp-composer/css/newsletter.css');
1158
+ $blocks = $this->get_blocks();
1159
+ foreach ($blocks as $block) {
1160
+ if (!file_exists($block['dir'] . '/style.css')) {
1161
+ continue;
1162
+ }
1163
+ $css .= "\n\n";
1164
+ $css .= "/* " . $block['name'] . " */\n";
1165
+ $css .= file_get_contents($block['dir'] . '/style.css');
1166
+ }
1167
+ return $css;
1168
+ }
1169
+
1170
+ function get_composer_backend_css() {
1171
+ $css = file_get_contents(__DIR__ . '/tnp-composer/css/backend.css');
1172
+ $css .= "\n\n";
1173
+ $css .= $this->get_composer_css();
1174
+ return $css;
1175
+ }
1176
+
1177
+ /**
1178
+ * Send an email to the test subscribers.
1179
+ *
1180
+ * @param TNP_Email $email Could be any object with the TNP_Email attributes
1181
+ * @param NewsletterControls $controls
1182
+ */
1183
+ function send_test_email($email, $controls) {
1184
+ if (!$email) {
1185
+ $controls->errors = __('Newsletter should be saved before send a test', 'newsletter');
1186
+ return;
1187
+ }
1188
+
1189
+ $original_subject = $email->subject;
1190
+ $this->set_test_subject_to($email);
1191
+
1192
+ $users = NewsletterUsers::instance()->get_test_users();
1193
+ if (count($users) == 0) {
1194
+ $controls->errors = '' . __('There are no test subscribers to send to', 'newsletter') .
1195
+ '. <a href="https://www.thenewsletterplugin.com/plugins/newsletter/subscribers-module#test" target="_blank"><strong>' .
1196
+ __('Read more', 'newsletter') . '</strong></a>.';
1197
+ } else {
1198
+ $r = Newsletter::instance()->send($email, $users, true);
1199
+ $emails = array();
1200
+ foreach ($users as $user) {
1201
+ $emails[] = '<a href="admin.php?page=newsletter_users_edit&id=' . $user->id . '" target="_blank">' . $user->email . '</a>';
1202
+ }
1203
+ if (is_wp_error($r)) {
1204
+ $controls->errors = 'Something went wrong. Check the error logs on status page.<br>';
1205
+ $controls->errors .= __('Test subscribers:', 'newsletter');
1206
+ $controls->errors .= ' ' . implode(', ', $emails);
1207
+ $controls->errors .= '<br>';
1208
+ $controls->errors .= '<strong>' . esc_html($r->get_error_message()) . '</strong><br>';
1209
+ $controls->errors .= '<a href="https://www.thenewsletterplugin.com/documentation/email-sending-issues" target="_blank"><strong>' . __('Read more about delivery issues', 'newsletter') . '</strong></a>.';
1210
+ } else {
1211
+ $controls->messages = __('Test subscribers:', 'newsletter');
1212
+
1213
+ $controls->messages .= ' ' . implode(', ', $emails);
1214
+ $controls->messages .= '.<br>';
1215
+ $controls->messages .= '<a href="https://www.thenewsletterplugin.com/documentation/subscribers#test" target="_blank"><strong>' .
1216
+ __('Read more about test subscribers', 'newsletter') . '</strong></a>.<br>';
1217
+ $controls->messages .= '<a href="https://www.thenewsletterplugin.com/documentation/email-sending-issues" target="_blank"><strong>' . __('Read more about delivery issues', 'newsletter') . '</strong></a>.';
1218
+ }
1219
+ }
1220
+ $email->subject = $original_subject;
1221
+ }
1222
+
1223
+ /**
1224
+ * Send an email to the test subscribers.
1225
+ *
1226
+ * @param TNP_Email $email Could be any object with the TNP_Email attributes
1227
+ * @param string $email_address
1228
+ *
1229
+ * @throws Exception
1230
+ */
1231
+ function send_test_newsletter_to_email_address($email, $email_address) {
1232
+
1233
+ if (!$email) {
1234
+ throw new Exception(__('Newsletter should be saved before send a test', 'newsletter'));
1235
+ }
1236
+
1237
+ $this->set_test_subject_to($email);
1238
+
1239
+ $dummy_subscriber = $this->make_dummy_subscriber();
1240
+ $dummy_subscriber->email = $email_address;
1241
+
1242
+ $result = Newsletter::instance()->send($email, [$dummy_subscriber], true);
1243
+
1244
+ $email = '<a href="admin.php?page=newsletter_users_edit&id=' . $dummy_subscriber->id . '" target="_blank">' . $dummy_subscriber->email . '</a>';
1245
+
1246
+ if (is_wp_error($result)) {
1247
+ $error_message = 'Something went wrong. Check the error logs on status page.<br>';
1248
+ $error_message .= __('Test subscribers:', 'newsletter');
1249
+ $error_message .= ' ' . $email;
1250
+ $error_message .= '<br>';
1251
+ $error_message .= '<strong>' . esc_html($result->get_error_message()) . '</strong><br>';
1252
+ $error_message .= '<a href="https://www.thenewsletterplugin.com/documentation/email-sending-issues" target="_blank"><strong>' . __('Read more about delivery issues', 'newsletter') . '</strong></a>.';
1253
+ throw new Exception($error_message);
1254
+ }
1255
+
1256
+ $messages = __('Test subscribers:', 'newsletter');
1257
+
1258
+ $messages .= ' ' . $email;
1259
+ $messages .= '.<br>';
1260
+ $messages .= '<a href="https://www.thenewsletterplugin.com/documentation/subscribers#test" target="_blank"><strong>' .
1261
+ __('Read more about test subscribers', 'newsletter') . '</strong></a>.<br>';
1262
+ $messages .= '<a href="https://www.thenewsletterplugin.com/documentation/email-sending-issues" target="_blank"><strong>' . __('Read more about delivery issues', 'newsletter') . '</strong></a>.';
1263
+
1264
+ return $messages;
1265
+ }
1266
+
1267
+ private function set_test_subject_to($email) {
1268
+ if ($email->subject == '') {
1269
+ $email->subject = '[TEST] Dummy subject, it was empty (remember to set it)';
1270
+ } else {
1271
+ $email->subject = $email->subject . ' (TEST)';
1272
+ }
1273
+ }
1274
+
1275
+ private function make_dummy_subscriber() {
1276
+ $dummy_user = new TNP_User();
1277
+ $dummy_user->id = 0;
1278
+ $dummy_user->email = 'john.doe@example.org';
1279
+ $dummy_user->name = 'John';
1280
+ $dummy_user->surname = 'Doe';
1281
+ $dummy_user->sex = 'n';
1282
+ $dummy_user->language = '';
1283
+ $dummy_user->ip = '';
1284
+
1285
+ for ($i = 1; $i <= NEWSLETTER_PROFILE_MAX; $i++) {
1286
+ $profile_key = "profile_$i";
1287
+ $dummy_user->$profile_key = '';
1288
+ }
1289
+
1290
+ return $dummy_user;
1291
+ }
1292
+
1293
+ function restore_options_from_request() {
1294
+
1295
+ require_once NEWSLETTER_INCLUDES_DIR . '/controls.php';
1296
+ $controls = new NewsletterControls();
1297
+ $options = $controls->data;
1298
+
1299
+ if (isset($_POST['options']) && is_array($_POST['options'])) {
1300
+ // Get all block options
1301
+ //$options = stripslashes_deep($_POST['options']);
1302
+ // Deserialize inline edits when
1303
+ // render is preformed on saving block options
1304
+ if (isset($options['inline_edits']) && !is_array($options['inline_edits'])) {
1305
+ $options['inline_edits'] = $this->options_decode($options['inline_edits']);
1306
+ }
1307
+
1308
+ // Restore inline edits from data-json
1309
+ // coming from inline editing
1310
+ // and merge with current inline edit
1311
+ if (isset($_POST['encoded_options'])) {
1312
+ $decoded_options = $this->options_decode($_POST['encoded_options']);
1313
+
1314
+ $to_merge_inline_edits = [];
1315
+
1316
+ if (isset($decoded_options['inline_edits'])) {
1317
+ foreach ($decoded_options['inline_edits'] as $decoded_inline_edit) {
1318
+ $to_merge_inline_edits[$decoded_inline_edit['post_id'] . $decoded_inline_edit['type']] = $decoded_inline_edit;
1319
+ }
1320
+ }
1321
+
1322
+ //Overwrite with new edited content
1323
+ if (isset($options['inline_edits'])) {
1324
+ foreach ($options['inline_edits'] as $inline_edit) {
1325
+ $to_merge_inline_edits[$inline_edit['post_id'] . $inline_edit['type']] = $inline_edit;
1326
+ }
1327
+ }
1328
+
1329
+ $options['inline_edits'] = array_values($to_merge_inline_edits);
1330
+ $options = array_merge($decoded_options, $options);
1331
+ }
1332
+
1333
+ return $options;
1334
+ }
1335
+
1336
+ return array();
1337
+ }
1338
+
1339
+ public function hook_wp_ajax_tnpc_delete_preset() {
1340
+
1341
+ if (!wp_verify_nonce($_POST['_wpnonce'], 'preset')) {
1342
+ wp_send_json_error('Expired request');
1343
+ }
1344
+
1345
+ $preset_id = (int) $_REQUEST['presetId'];
1346
+
1347
+ $newsletter = Newsletter::instance();
1348
+
1349
+ if ($preset_id > 0) {
1350
+ $preset = $newsletter->get_email($preset_id);
1351
+
1352
+ if ($preset && $preset->type === self::PRESET_EMAIL_TYPE) {
1353
+ Newsletter::instance()->delete_email($preset_id);
1354
+ wp_send_json_success();
1355
+ } else {
1356
+ wp_send_json_error(__('Is not a preset!', 'newsletter'));
1357
+ }
1358
+ } else {
1359
+ wp_send_json_error();
1360
+ }
1361
+ }
1362
+
1363
+ }
1364
+
1365
+ NewsletterEmails::instance();
includes/addon.php CHANGED
@@ -1,396 +1,396 @@
1
- <?php
2
-
3
- /**
4
- * User by add-ons as base-class.
5
- */
6
- class NewsletterAddon {
7
-
8
- var $logger;
9
- var $admin_logger;
10
- var $name;
11
- var $options;
12
- var $version;
13
- var $labels;
14
-
15
- public function __construct($name, $version = '0.0.0') {
16
- $this->name = $name;
17
- $this->version = $version;
18
- if (is_admin()) {
19
- $old_version = get_option('newsletter_' . $name . '_version');
20
- if ($version !== $old_version) {
21
- $this->upgrade($old_version === false);
22
- update_option('newsletter_' . $name . '_version', $version, false);
23
- }
24
- }
25
- add_action('newsletter_init', array($this, 'init'));
26
- //Load translations from specific addon /languages/ directory
27
- load_plugin_textdomain('newsletter-' . $this->name, false, 'newsletter-' . $this->name . '/languages/');
28
- }
29
-
30
- /**
31
- * Method to be overridden and invoked on version change or on first install.
32
- *
33
- * @param bool $first_install
34
- */
35
- function upgrade($first_install = false) {
36
-
37
- }
38
-
39
- /**
40
- * Method to be overridden to initialize the add-on. It is invoked when Newsletter
41
- * fires the <code>newsletter_init</code> event.
42
- */
43
- function init() {
44
-
45
- }
46
-
47
- function get_current_language() {
48
- return Newsletter::instance()->get_current_language();
49
- }
50
-
51
- function is_all_languages() {
52
- return Newsletter::instance()->is_all_languages();
53
- }
54
-
55
- function is_allowed() {
56
- return Newsletter::instance()->is_allowed();
57
- }
58
-
59
- function get_languages() {
60
- return Newsletter::instance()->get_languages();
61
- }
62
-
63
- function is_multilanguage() {
64
- return Newsletter::instance()->is_multilanguage();
65
- }
66
-
67
- /**
68
- * General logger for this add-on.
69
- *
70
- * @return NewsletterLogger
71
- */
72
- function get_logger() {
73
- if (!$this->logger) {
74
- $this->logger = new NewsletterLogger($this->name);
75
- }
76
- return $this->logger;
77
- }
78
-
79
- /**
80
- * Specific logger for administrator actions.
81
- *
82
- * @return NewsletterLogger
83
- */
84
- function get_admin_logger() {
85
- if (!$this->admin_logger) {
86
- $this->admin_logger = new NewsletterLogger($this->name . '-admin');
87
- }
88
- return $this->admin_logger;
89
- }
90
-
91
- /**
92
- * Loads and prepares the options. It can be used to late initialize the options to save some resources on
93
- * add-ons which do not need to do something on each page load.
94
- */
95
- function setup_options() {
96
- if ($this->options) {
97
- return;
98
- }
99
- $this->options = get_option('newsletter_' . $this->name, []);
100
- }
101
-
102
- /**
103
- * Retrieve the stored options, merged with the specified language set.
104
- *
105
- * @param string $language
106
- * @return array
107
- */
108
- function get_options($language = '') {
109
- if ($language) {
110
- return array_merge(get_option('newsletter_' . $this->name, []), get_option('newsletter_' . $this->name . '_' . $language, []));
111
- } else {
112
- return get_option('newsletter_' . $this->name, []);
113
- }
114
- }
115
-
116
- /**
117
- * Saved the options under the correct keys and update the internal $options
118
- * property.
119
- * @param array $options
120
- */
121
- function save_options($options, $language = '') {
122
- if ($language) {
123
- update_option('newsletter_' . $this->name . '_' . $language, $options);
124
- } else {
125
- update_option('newsletter_' . $this->name, $options);
126
- $this->options = $options;
127
- }
128
- }
129
-
130
- function merge_defaults($defaults) {
131
- $options = get_option('newsletter_' . $this->name, []);
132
- $options = array_merge($defaults, $options);
133
- $this->save_options($options);
134
- }
135
-
136
- /**
137
- *
138
- */
139
- function setup_labels() {
140
- if (!$this->labels) {
141
- $labels = [];
142
- }
143
- }
144
-
145
- function get_label($key) {
146
- if (!$this->options)
147
- $this->setup_options();
148
-
149
- if (!empty($this->options[$key])) {
150
- return $this->options[$key];
151
- }
152
-
153
- if (!$this->labels)
154
- $this->setup_labels();
155
-
156
- // We assume the required key is defined. If not there is an error elsewhere.
157
- return $this->labels[$key];
158
- }
159
-
160
- /**
161
- * Equivalent to $wpdb->query() but logs the event in case of error.
162
- *
163
- * @global wpdb $wpdb
164
- * @param string $query
165
- */
166
- function query($query) {
167
- global $wpdb;
168
-
169
- $r = $wpdb->query($query);
170
- if ($r === false) {
171
- $logger = $this->get_logger();
172
- $logger->fatal($query);
173
- $logger->fatal($wpdb->last_error);
174
- }
175
- return $r;
176
- }
177
-
178
- }
179
-
180
- /**
181
- * Used by mailer add-ons as base-class. Some specific options collected by the mailer
182
- * are interpreted automatically.
183
- *
184
- * They are:
185
- *
186
- * `enabled` if not empty it means the mailer is active and should be registered
187
- *
188
- * The options are set up in the constructor, there is no need to setup them later.
189
- */
190
- class NewsletterMailerAddon extends NewsletterAddon {
191
-
192
- var $enabled = false;
193
- var $menu_title = null;
194
- var $menu_description = null;
195
- var $dir = '';
196
-
197
- function __construct($name, $version = '0.0.0', $dir = '') {
198
- parent::__construct($name, $version);
199
- $this->dir = $dir;
200
- $this->setup_options();
201
- $this->enabled = !empty($this->options['enabled']);
202
- }
203
-
204
- /**
205
- * This method must be called as `parent::init()` is overridden.
206
- */
207
- function init() {
208
- parent::init();
209
- add_action('newsletter_register_mailer', function () {
210
- if ($this->enabled) {
211
- Newsletter::instance()->register_mailer($this->get_mailer());
212
- }
213
- });
214
-
215
- if (is_admin() && !empty($this->menu_title) && !empty($this->dir) && current_user_can('administrator')) {
216
- add_action('admin_menu', [$this, 'hook_admin_menu'], 101);
217
- add_filter('newsletter_menu_settings', [$this, 'hook_newsletter_menu_settings']);
218
- }
219
- }
220
-
221
- function hook_newsletter_menu_settings($entries) {
222
- $entries[] = array('label' => '<i class="fas fa-envelope"></i> ' . $this->menu_title, 'url' => '?page=newsletter_' . $this->name . '_index', 'description' => $this->menu_description);
223
- return $entries;
224
- }
225
-
226
- function hook_admin_menu() {
227
- add_submenu_page('newsletter_main_index', $this->menu_title, '<span class="tnp-side-menu">' . $this->menu_title . '</span>', 'manage_options', 'newsletter_' . $this->name . '_index',
228
- function () {
229
- require $this->dir . '/index.php';
230
- }
231
- );
232
- }
233
-
234
- /**
235
- * Must return an implementation of NewsletterMailer.
236
- * @return NewsletterMailer
237
- */
238
- function get_mailer() {
239
- return null;
240
- }
241
-
242
- function get_last_run() {
243
- return get_option('newsletter_' . $this->name . '_last_run', 0);
244
- }
245
-
246
- function save_last_run($time) {
247
- update_option('newsletter_' . $this->name . '_last_run', $time);
248
- }
249
-
250
- function save_options($options, $language = '') {
251
- parent::save_options($options, $language);
252
- $this->enabled = !empty($options['enabled']);
253
- }
254
-
255
- /**
256
- * Returns a TNP_Mailer_Message built to send a test message to the <code>$to</code>
257
- * email address.
258
- *
259
- * @param string $to
260
- * @param string $subject
261
- * @return TNP_Mailer_Message
262
- */
263
- static function get_test_message($to, $subject = '', $type = '') {
264
- $message = new TNP_Mailer_Message();
265
- $message->to = $to;
266
- $message->to_name = '';
267
- if (empty($type) || $type == 'html') {
268
- $message->body = file_get_contents(NEWSLETTER_DIR . '/includes/test-message.html');
269
- $message->body = str_replace('{plugin_url}', NEWSLETTER_URL, $message->body);
270
- }
271
-
272
- if (empty($type) || $type == 'text') {
273
- $message->body_text = 'This is the TEXT version of a test message. You should see this message only if you email client does not support the rich text (HTML) version.';
274
- }
275
-
276
- $message->headers['X-Newsletter-Email-Id'] = '0';
277
-
278
- if (empty($subject)) {
279
- $message->subject = '[' . get_option('blogname') . '] Test message from Newsletter (' . date(DATE_ISO8601) . ')';
280
- } else {
281
- $message->subject = $subject;
282
- }
283
-
284
- if ($type) {
285
- $message->subject .= ' - ' . $type . ' only';
286
- }
287
-
288
- $message->from = Newsletter::instance()->options['sender_email'];
289
- $message->from_name = Newsletter::instance()->options['sender_name'];
290
- return $message;
291
- }
292
-
293
- /**
294
- * Returns a set of test messages to be sent to the specified email address. Used for
295
- * turbo mode tests. Each message has a different generated subject.
296
- *
297
- * @param string $to The destination mailbox
298
- * @param int $count Number of message objects to create
299
- * @return TNP_Mailer_Message[]
300
- */
301
- function get_test_messages($to, $count, $type = '') {
302
- $messages = array();
303
- for ($i = 0; $i < $count; $i++) {
304
- $messages[] = self::get_test_message($to, '[' . get_option('blogname') . '] Test message ' . ($i + 1) . ' from Newsletter (' . date(DATE_ISO8601) . ')', $type);
305
- }
306
- return $messages;
307
- }
308
-
309
- }
310
-
311
- class NewsletterFormManagerAddon extends NewsletterAddon {
312
-
313
- var $menu_title = null;
314
- var $menu_description = null;
315
- var $dir = '';
316
-
317
- function __construct($name, $version, $dir) {
318
- parent::__construct($name, $version);
319
- $this->dir = $dir;
320
- $this->setup_options();
321
- }
322
-
323
- function init() {
324
- parent::init();
325
-
326
- if (is_admin() && !empty($this->menu_title) && !empty($this->dir) && Newsletter::instance()->is_allowed()) {
327
- add_action('admin_menu', [$this, 'hook_admin_menu'], 101);
328
- add_filter('newsletter_menu_subscription', [$this, 'hook_newsletter_menu_subscription']);
329
- }
330
- }
331
-
332
- function hook_newsletter_menu_subscription($entries) {
333
- $entries[] = array('label' => '<i class="fas fa-envelope"></i> ' . $this->menu_title, 'url' => '?page=newsletter_' . $this->name . '_index', 'description' => $this->menu_description);
334
- return $entries;
335
- }
336
-
337
- function hook_admin_menu() {
338
- add_submenu_page('newsletter_main_index', $this->menu_title, '<span class="tnp-side-menu">' . $this->menu_title . '</span>', 'exist', 'newsletter_' . $this->name . '_index',
339
- function () {
340
- require $this->dir . '/admin/index.php';
341
- }
342
- );
343
- }
344
-
345
- /**
346
- * Returns a lists of representations of forms available in the plugin subject of integration.
347
- * Usually the $fields is not set up on returned objects.
348
- * Must be implemented.
349
- *
350
- * @return TNP_FormManager_Form[] List of forms by 3rd party plugin
351
- */
352
- function get_forms() {
353
- return [];
354
- }
355
-
356
- /**
357
- * Build a form general representation of a real form from a form manager plugin extracting
358
- * only the data required to integrate. The form id is domain of the form manager plugin, so it can be
359
- * anything.
360
- * Must be implemented.
361
- *
362
- * @param mixed $form_id
363
- * @return TNP_FormManager_Form
364
- */
365
- function get_form($form_id) {
366
- return null;
367
- }
368
-
369
- /**
370
- * Saves the form mapping and integration settings.
371
- * @param mixed $form_id
372
- * @param array $data
373
- */
374
- public function save_form_options($form_id, $data) {
375
- update_option('newsletter_' . $this->name . '_' . $form_id, $data, false);
376
- }
377
-
378
- /**
379
- * Gets the form mapping and integration settings. Returns an empty array if the dataset is missing.
380
- * @param mixed $form_id
381
- * @return array
382
- */
383
- public function get_form_options($form_id) {
384
- return get_option('newsletter_' . $this->name . '_' . $form_id, []);
385
- }
386
-
387
- }
388
-
389
- class TNP_FormManager_Form {
390
-
391
- var $id = null;
392
- var $title = '';
393
- var $fields = [];
394
- var $connected = false;
395
-
396
- }
1
+ <?php
2
+
3
+ /**
4
+ * User by add-ons as base-class.
5
+ */
6
+ class NewsletterAddon {
7
+
8
+ var $logger;
9
+ var $admin_logger;
10
+ var $name;
11
+ var $options;
12
+ var $version;
13
+ var $labels;
14
+
15
+ public function __construct($name, $version = '0.0.0') {
16
+ $this->name = $name;
17
+ $this->version = $version;
18
+ if (is_admin()) {
19
+ $old_version = get_option('newsletter_' . $name . '_version');
20
+ if ($version !== $old_version) {
21
+ $this->upgrade($old_version === false);
22
+ update_option('newsletter_' . $name . '_version', $version, false);
23
+ }
24
+ }
25
+ add_action('newsletter_init', array($this, 'init'));
26
+ //Load translations from specific addon /languages/ directory
27
+ load_plugin_textdomain('newsletter-' . $this->name, false, 'newsletter-' . $this->name . '/languages/');
28
+ }
29
+
30
+ /**
31
+ * Method to be overridden and invoked on version change or on first install.
32
+ *
33
+ * @param bool $first_install
34
+ */
35
+ function upgrade($first_install = false) {
36
+
37
+ }
38
+
39
+ /**
40
+ * Method to be overridden to initialize the add-on. It is invoked when Newsletter
41
+ * fires the <code>newsletter_init</code> event.
42
+ */
43
+ function init() {
44
+
45
+ }
46
+
47
+ function get_current_language() {
48
+ return Newsletter::instance()->get_current_language();
49
+ }
50
+
51
+ function is_all_languages() {
52
+ return Newsletter::instance()->is_all_languages();
53
+ }
54
+
55
+ function is_allowed() {
56
+ return Newsletter::instance()->is_allowed();
57
+ }
58
+
59
+ function get_languages() {
60
+ return Newsletter::instance()->get_languages();
61
+ }
62
+
63
+ function is_multilanguage() {
64
+ return Newsletter::instance()->is_multilanguage();
65
+ }
66
+
67
+ /**
68
+ * General logger for this add-on.
69
+ *
70
+ * @return NewsletterLogger
71
+ */
72
+ function get_logger() {
73
+ if (!$this->logger) {
74
+ $this->logger = new NewsletterLogger($this->name);
75
+ }
76
+ return $this->logger;
77
+ }
78
+
79
+ /**
80
+ * Specific logger for administrator actions.
81
+ *
82
+ * @return NewsletterLogger
83
+ */
84
+ function get_admin_logger() {
85
+ if (!$this->admin_logger) {
86
+ $this->admin_logger = new NewsletterLogger($this->name . '-admin');
87
+ }
88
+ return $this->admin_logger;
89
+ }
90
+
91
+ /**
92
+ * Loads and prepares the options. It can be used to late initialize the options to save some resources on
93
+ * add-ons which do not need to do something on each page load.
94
+ */
95
+ function setup_options() {
96
+ if ($this->options) {
97
+ return;
98
+ }
99
+ $this->options = get_option('newsletter_' . $this->name, []);
100
+ }
101
+
102
+ /**
103
+ * Retrieve the stored options, merged with the specified language set.
104
+ *
105
+ * @param string $language
106
+ * @return array
107
+ */
108
+ function get_options($language = '') {
109
+ if ($language) {
110
+ return array_merge(get_option('newsletter_' . $this->name, []), get_option('newsletter_' . $this->name . '_' . $language, []));
111
+ } else {
112
+ return get_option('newsletter_' . $this->name, []);
113
+ }
114
+ }
115
+
116
+ /**
117
+ * Saved the options under the correct keys and update the internal $options
118
+ * property.
119
+ * @param array $options
120
+ */
121
+ function save_options($options, $language = '') {
122
+ if ($language) {
123
+ update_option('newsletter_' . $this->name . '_' . $language, $options);
124
+ } else {
125
+ update_option('newsletter_' . $this->name, $options);
126
+ $this->options = $options;
127
+ }
128
+ }
129
+
130
+ function merge_defaults($defaults) {
131
+ $options = get_option('newsletter_' . $this->name, []);
132
+ $options = array_merge($defaults, $options);
133
+ $this->save_options($options);
134
+ }
135
+
136
+ /**
137
+ *
138
+ */
139
+ function setup_labels() {
140
+ if (!$this->labels) {
141
+ $labels = [];
142
+ }
143
+ }
144
+
145
+ function get_label($key) {
146
+ if (!$this->options)
147
+ $this->setup_options();
148
+
149
+ if (!empty($this->options[$key])) {
150
+ return $this->options[$key];
151
+ }
152
+
153
+ if (!$this->labels)
154
+ $this->setup_labels();
155
+
156
+ // We assume the required key is defined. If not there is an error elsewhere.
157
+ return $this->labels[$key];
158
+ }
159
+
160
+ /**
161
+ * Equivalent to $wpdb->query() but logs the event in case of error.
162
+ *
163
+ * @global wpdb $wpdb
164
+ * @param string $query
165
+ */
166
+ function query($query) {
167
+ global $wpdb;
168
+
169
+ $r = $wpdb->query($query);
170
+ if ($r === false) {
171
+ $logger = $this->get_logger();
172
+ $logger->fatal($query);
173
+ $logger->fatal($wpdb->last_error);
174
+ }
175
+ return $r;
176
+ }
177
+
178
+ }
179
+
180
+ /**
181
+ * Used by mailer add-ons as base-class. Some specific options collected by the mailer
182
+ * are interpreted automatically.
183
+ *
184
+ * They are:
185
+ *
186
+ * `enabled` if not empty it means the mailer is active and should be registered
187
+ *
188
+ * The options are set up in the constructor, there is no need to setup them later.
189
+ */
190
+ class NewsletterMailerAddon extends NewsletterAddon {
191
+
192
+ var $enabled = false;
193
+ var $menu_title = null;
194
+ var $menu_description = null;
195
+ var $dir = '';
196
+
197
+ function __construct($name, $version = '0.0.0', $dir = '') {
198
+ parent::__construct($name, $version);
199
+ $this->dir = $dir;
200
+ $this->setup_options();
201
+ $this->enabled = !empty($this->options['enabled']);
202
+ }
203
+
204
+ /**
205
+ * This method must be called as `parent::init()` is overridden.
206
+ */
207
+ function init() {
208
+ parent::init();
209
+ add_action('newsletter_register_mailer', function () {
210
+ if ($this->enabled) {
211
+ Newsletter::instance()->register_mailer($this->get_mailer());
212
+ }
213
+ });
214
+
215
+ if (is_admin() && !empty($this->menu_title) && !empty($this->dir) && current_user_can('administrator')) {
216
+ add_action('admin_menu', [$this, 'hook_admin_menu'], 101);
217
+ add_filter('newsletter_menu_settings', [$this, 'hook_newsletter_menu_settings']);
218
+ }
219
+ }
220
+
221
+ function hook_newsletter_menu_settings($entries) {
222
+ $entries[] = array('label' => '<i class="fas fa-envelope"></i> ' . $this->menu_title, 'url' => '?page=newsletter_' . $this->name . '_index', 'description' => $this->menu_description);
223
+ return $entries;
224
+ }
225
+
226
+ function hook_admin_menu() {
227
+ add_submenu_page('newsletter_main_index', $this->menu_title, '<span class="tnp-side-menu">' . $this->menu_title . '</span>', 'manage_options', 'newsletter_' . $this->name . '_index',
228
+ function () {
229
+ require $this->dir . '/index.php';
230
+ }
231
+ );
232
+ }
233
+
234
+ /**
235
+ * Must return an implementation of NewsletterMailer.
236
+ * @return NewsletterMailer
237
+ */
238
+ function get_mailer() {
239
+ return null;
240
+ }
241
+
242
+ function get_last_run() {
243
+ return get_option('newsletter_' . $this->name . '_last_run', 0);
244
+ }
245
+
246
+ function save_last_run($time) {
247
+ update_option('newsletter_' . $this->name . '_last_run', $time);
248
+ }
249
+
250
+ function save_options($options, $language = '') {
251
+ parent::save_options($options, $language);
252
+ $this->enabled = !empty($options['enabled']);
253
+ }
254
+
255
+ /**
256
+ * Returns a TNP_Mailer_Message built to send a test message to the <code>$to</code>
257
+ * email address.
258
+ *
259
+ * @param string $to
260
+ * @param string $subject
261
+ * @return TNP_Mailer_Message
262
+ */
263
+ static function get_test_message($to, $subject = '', $type = '') {
264
+ $message = new TNP_Mailer_Message();
265
+ $message->to = $to;
266
+ $message->to_name = '';
267
+ if (empty($type) || $type == 'html') {
268
+ $message->body = file_get_contents(NEWSLETTER_DIR . '/includes/test-message.html');
269
+ $message->body = str_replace('{plugin_url}', NEWSLETTER_URL, $message->body);
270
+ }
271
+
272
+ if (empty($type) || $type == 'text') {
273
+ $message->body_text = 'This is the TEXT version of a test message. You should see this message only if you email client does not support the rich text (HTML) version.';
274
+ }
275
+
276
+ $message->headers['X-Newsletter-Email-Id'] = '0';
277
+
278
+ if (empty($subject)) {
279
+ $message->subject = '[' . get_option('blogname') . '] Test message from Newsletter (' . date(DATE_ISO8601) . ')';
280
+ } else {
281
+ $message->subject = $subject;
282
+ }
283
+
284
+ if ($type) {
285
+ $message->subject .= ' - ' . $type . ' only';
286
+ }
287
+
288
+ $message->from = Newsletter::instance()->options['sender_email'];
289
+ $message->from_name = Newsletter::instance()->options['sender_name'];
290
+ return $message;
291
+ }
292
+
293
+ /**
294
+ * Returns a set of test messages to be sent to the specified email address. Used for
295
+ * turbo mode tests. Each message has a different generated subject.
296
+ *
297
+ * @param string $to The destination mailbox
298
+ * @param int $count Number of message objects to create
299
+ * @return TNP_Mailer_Message[]
300
+ */
301
+ function get_test_messages($to, $count, $type = '') {
302
+ $messages = array();
303
+ for ($i = 0; $i < $count; $i++) {
304
+ $messages[] = self::get_test_message($to, '[' . get_option('blogname') . '] Test message ' . ($i + 1) . ' from Newsletter (' . date(DATE_ISO8601) . ')', $type);
305
+ }
306
+ return $messages;
307
+ }
308
+
309
+ }
310
+
311
+ class NewsletterFormManagerAddon extends NewsletterAddon {
312
+
313
+ var $menu_title = null;
314
+ var $menu_description = null;
315
+ var $dir = '';
316
+
317
+ function __construct($name, $version, $dir) {
318
+ parent::__construct($name, $version);
319
+ $this->dir = $dir;
320
+ $this->setup_options();
321
+ }
322
+
323
+ function init() {
324
+ parent::init();
325
+
326
+ if (is_admin() && !empty($this->menu_title) && !empty($this->dir) && Newsletter::instance()->is_allowed()) {
327
+ add_action('admin_menu', [$this, 'hook_admin_menu'], 101);
328
+ add_filter('newsletter_menu_subscription', [$this, 'hook_newsletter_menu_subscription']);
329
+ }
330
+ }
331
+
332
+ function hook_newsletter_menu_subscription($entries) {
333
+ $entries[] = array('label' => '<i class="fas fa-envelope"></i> ' . $this->menu_title, 'url' => '?page=newsletter_' . $this->name . '_index', 'description' => $this->menu_description);
334
+ return $entries;
335
+ }
336
+
337
+ function hook_admin_menu() {
338
+ add_submenu_page('newsletter_main_index', $this->menu_title, '<span class="tnp-side-menu">' . $this->menu_title . '</span>', 'exist', 'newsletter_' . $this->name . '_index',
339
+ function () {
340
+ require $this->dir . '/admin/index.php';
341
+ }
342
+ );
343
+ }
344
+
345
+ /**
346
+ * Returns a lists of representations of forms available in the plugin subject of integration.
347
+ * Usually the $fields is not set up on returned objects.
348
+ * Must be implemented.
349
+ *
350
+ * @return TNP_FormManager_Form[] List of forms by 3rd party plugin
351
+ */
352
+ function get_forms() {
353
+ return [];
354
+ }
355
+
356
+ /**
357
+ * Build a form general representation of a real form from a form manager plugin extracting
358
+ * only the data required to integrate. The form id is domain of the form manager plugin, so it can be
359
+ * anything.
360
+ * Must be implemented.
361
+ *
362
+ * @param mixed $form_id
363
+ * @return TNP_FormManager_Form
364
+ */
365
+ function get_form($form_id) {
366
+ return null;
367
+ }
368
+
369
+ /**
370
+ * Saves the form mapping and integration settings.
371
+ * @param mixed $form_id
372
+ * @param array $data
373
+ */
374
+ public function save_form_options($form_id, $data) {
375
+ update_option('newsletter_' . $this->name . '_' . $form_id, $data, false);
376
+ }
377
+
378
+ /**
379
+ * Gets the form mapping and integration settings. Returns an empty array if the dataset is missing.
380
+ * @param mixed $form_id
381
+ * @return array
382
+ */
383
+ public function get_form_options($form_id) {
384
+ return get_option('newsletter_' . $this->name . '_' . $form_id, []);
385
+ }
386
+
387
+ }
388
+
389
+ class TNP_FormManager_Form {
390
+
391
+ var $id = null;
392
+ var $title = '';
393
+ var $fields = [];
394
+ var $connected = false;
395
+
396
+ }
includes/controls.php CHANGED
@@ -1860,7 +1860,7 @@ tnp_controls_init();
1860
  }
1861
 
1862
  function language($name = 'language', $empty_label = 'All') {
1863
- if (!class_exists('SitePress') && !function_exists('pll_default_language') && !class_exists('TRP_Translate_Press')) {
1864
  echo __('Install a multilanguage plugin.', 'newsletter');
1865
  echo ' <a href="https://www.thenewsletterplugin.com/documentation/multilanguage" target="_blank">', __('Read more', 'newsletter'), '</a>';
1866
  return;
1860
  }
1861
 
1862
  function language($name = 'language', $empty_label = 'All') {
1863
+ if (!$this->is_multilanguage()) {
1864
  echo __('Install a multilanguage plugin.', 'newsletter');
1865
  echo ' <a href="https://www.thenewsletterplugin.com/documentation/multilanguage" target="_blank">', __('Read more', 'newsletter'), '</a>';
1866
  return;
includes/module.php CHANGED
@@ -2513,7 +2513,7 @@ class NewsletterModule {
2513
  }
2514
 
2515
  function is_multilanguage() {
2516
- return class_exists('SitePress') || function_exists('pll_default_language') || class_exists('TRP_Translate_Press');
2517
  }
2518
 
2519
  function get_posts($filters = array(), $language = '') {
2513
  }
2514
 
2515
  function is_multilanguage() {
2516
+ return apply_filters('newsletter_is_multilanguage', class_exists('SitePress') || function_exists('pll_default_language') || class_exists('TRP_Translate_Press'));
2517
  }
2518
 
2519
  function get_posts($filters = array(), $language = '') {
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.5.5
8
  Author: Stefano Lissa & The Newsletter Team
9
  Author URI: https://www.thenewsletterplugin.com
10
  Disclaimer: Use at your own risk. No warranty expressed or implied is provided.
@@ -37,7 +37,7 @@ if (version_compare(phpversion(), '5.6', '<')) {
37
  return;
38
  }
39
 
40
- define('NEWSLETTER_VERSION', '7.5.5');
41
 
42
  global $newsletter, $wpdb;
43
 
4
  Plugin Name: Newsletter
5
  Plugin URI: https://www.thenewsletterplugin.com/plugins/newsletter
6
  Description: Newsletter is a cool plugin to create your own subscriber list, to send newsletters, to build your business. <strong>Before update give a look to <a href="https://www.thenewsletterplugin.com/category/release">this page</a> to know what's changed.</strong>
7
+ Version: 7.5.6
8
  Author: Stefano Lissa & The Newsletter Team
9
  Author URI: https://www.thenewsletterplugin.com
10
  Disclaimer: Use at your own risk. No warranty expressed or implied is provided.
37
  return;
38
  }
39
 
40
+ define('NEWSLETTER_VERSION', '7.5.6');
41
 
42
  global $newsletter, $wpdb;
43
 
readme.txt CHANGED
@@ -1,7 +1,7 @@
1
  === Newsletter - Send awesome emails from WordPress ===
2
  Tags: newsletter, email marketing, welcome email, signup forms, lead generation, marketing automation
3
  Tested up to: 6.1
4
- Stable tag: 7.5.5
5
  Contributors: satollo,webagile,michael-travan
6
  License: GPLv2 or later
7
  License URI: https://www.gnu.org/licenses/gpl-2.0.html
@@ -126,6 +126,11 @@ Thank you, The Newsletter Team
126
 
127
  == Changelog ==
128
 
 
 
 
 
 
129
  = 7.5.5 =
130
 
131
  * Fixed access control on form manager base class
1
  === Newsletter - Send awesome emails from WordPress ===
2
  Tags: newsletter, email marketing, welcome email, signup forms, lead generation, marketing automation
3
  Tested up to: 6.1
4
+ Stable tag: 7.5.6
5
  Contributors: satollo,webagile,michael-travan
6
  License: GPLv2 or later
7
  License URI: https://www.gnu.org/licenses/gpl-2.0.html
126
 
127
  == Changelog ==
128
 
129
+ = 7.5.6 =
130
+
131
+ * Added new filter for multilanguage support for GTranslate (experimental)
132
+ * Added link to the backup/recovery documentation
133
+
134
  = 7.5.5 =
135
 
136
  * Fixed access control on form manager base class
tnp-header.php CHANGED
@@ -1,345 +1,349 @@
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
- $current_user_email = ''; //$current_user->user_email;
34
- //if (strpos($current_user_email, 'admin@') === 0) {
35
- // $current_user_email = '';
36
- //}
37
- ?>
38
-
39
- <div class="tnp-drowpdown" id="tnp-header">
40
- <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>
41
- <ul>
42
- <li><a href="#"><i class="fas fa-users"></i> <?php _e('Subscribers', 'newsletter') ?> <i class="fas fa-chevron-down"></i></a>
43
- <ul>
44
- <li>
45
- <a href="?page=newsletter_users_index"><i class="fas fa-search"></i> <?php _e('Search And Edit', 'newsletter') ?>
46
- <small><?php _e('Add, edit, search', 'newsletter') ?></small></a>
47
- </li>
48
-
49
- <li>
50
- <a href="?page=newsletter_profile_index"><i class="fas fa-user-circle"></i> <?php _e('Profile page', 'newsletter') ?>
51
- <small><?php _e('The subscriber personal profile editing panel', 'newsletter') ?></small></a>
52
- </li>
53
-
54
- <?php if (!class_exists('NewsletterImport')) { ?>
55
- <li>
56
- <a href="?page=newsletter_users_import"><i class="fas fa-upload"></i> <?php _e('Import', 'newsletter') ?>
57
- <small><?php _e('Import from external sources', 'newsletter') ?></small></a>
58
- </li>
59
- <?php } ?>
60
-
61
- <li>
62
- <a href="?page=newsletter_users_export"><i class="fas fa-download"></i> <?php _e('Export', 'newsletter') ?>
63
- <small><?php _e('Export your subscribers list', 'newsletter') ?></small></a>
64
- </li>
65
-
66
- <li>
67
- <a href="?page=newsletter_users_massive"><i class="fas fa-wrench"></i> <?php _e('Maintenance', 'newsletter') ?>
68
- <small><?php _e('Massive actions: change list, clean up, ...', 'newsletter') ?></small></a>
69
- </li>
70
-
71
- <li>
72
- <a href="?page=newsletter_users_statistics"><i class="fas fa-chart-bar"></i> <?php _e('Statistics', 'newsletter') ?>
73
- <small><?php _e('All about your subscribers', 'newsletter') ?></small></a>
74
- </li>
75
-
76
- <?php newsletter_print_entries('subscribers') ?>
77
- </ul>
78
- </li>
79
- <li><a href="#"><i class="fas fa-list"></i> <?php _e('List Building', 'newsletter') ?> <i class="fas fa-chevron-down"></i></a>
80
- <ul>
81
- <li>
82
- <a href="?page=newsletter_subscription_profile"><i class="fas fa-check-square"></i> <?php _e('Subscription Form Fields, Buttons, Labels', 'newsletter') ?>
83
- <small><?php _e('When and what data to collect', 'newsletter') ?></small></a>
84
- </li>
85
-
86
- <li>
87
- <a href="?page=newsletter_subscription_options"><i class="fas fa-sign-in-alt"></i> <?php _e('Subscription', 'newsletter') ?>
88
- <small><?php _e('The subscription process in detail', 'newsletter') ?></small></a>
89
- </li>
90
-
91
- <li>
92
- <a href="?page=newsletter_subscription_lists"><i class="fas fa-th-list"></i> <?php _e('Lists', 'newsletter') ?>
93
- <small><?php _e('Profile the subscribers for a better targeting', 'newsletter') ?></small></a>
94
- </li>
95
-
96
- <li>
97
- <a href="?page=newsletter_subscription_antibot"><i class="fas fa-lock"></i> <?php _e('Security', 'newsletter') ?>
98
- <small><?php _e('Spam subscriptions control', 'newsletter') ?></small></a>
99
- </li>
100
-
101
- <li>
102
- <a href="?page=newsletter_unsubscription_index"><i class="fas fa-sign-out-alt"></i> <?php _e('Unsubscription', 'newsletter') ?>
103
- <small><?php _e('How to give the last goodbye (or avoid it!)', 'newsletter') ?></small></a>
104
- </li>
105
-
106
- <?php
107
- newsletter_print_entries('subscription');
108
- ?>
109
- </ul>
110
- </li>
111
-
112
- <li>
113
- <a href="#"><i class="fas fa-newspaper"></i> <?php _e('Newsletters', 'newsletter') ?> <i class="fas fa-chevron-down"></i></a>
114
- <ul>
115
- <li>
116
- <a href="?page=newsletter_emails_composer"><i class="fas fa-plus"></i> <?php _e('Create newsletter', 'newsletter') ?>
117
- <small><?php _e('Start your new campaign', 'newsletter') ?></small></a>
118
- </li>
119
-
120
- <li>
121
- <a href="?page=newsletter_emails_index"><i class="fas fa-newspaper"></i> <?php _e('Newsletters', 'newsletter') ?>
122
- <small><?php _e('The classic "write & send" newsletters', 'newsletter') ?></small></a>
123
- </li>
124
-
125
- <li>
126
- <a href="<?php echo NewsletterStatistics::instance()->get_index_url() ?>"><i class="fas fa-chart-bar"></i> <?php _e('Statistics', 'newsletter') ?>
127
- <small><?php _e('Tracking configuration and basic data', 'newsletter') ?></small></a>
128
- </li>
129
- <?php
130
- newsletter_print_entries('newsletters');
131
- ?>
132
- </ul>
133
- </li>
134
-
135
- <li>
136
- <a href="#"><i class="fas fa-cog"></i> <?php _e('Settings', 'newsletter') ?> <i class="fas fa-chevron-down"></i></a>
137
- <ul>
138
- <?php if ($is_administrator) { ?>
139
- <li>
140
- <a href="?page=newsletter_main_main"><i class="fas fa-cogs"></i> <?php _e('General Settings', 'newsletter') ?>
141
- <small><?php _e('Delivery speed, sender details, ...', 'newsletter') ?></small></a>
142
- </li>
143
- <?php if (!class_exists('NewsletterSmtp')) { ?>
144
- <li>
145
- <a href="?page=newsletter_main_smtp"><i class="fas fa-server"></i> <?php _e('SMTP', 'newsletter') ?>
146
- <small><?php _e('External mail server', 'newsletter') ?></small>
147
- </a>
148
- </li>
149
- <?php } ?>
150
- <?php } ?>
151
-
152
- <li>
153
- <a href="?page=newsletter_main_info"><i class="fas fa-info"></i> <?php _e('Company Info', 'newsletter') ?>
154
- <small><?php _e('Social, address, logo and general info', 'newsletter') ?></small></a>
155
- </li>
156
-
157
- <li>
158
- <a href="?page=newsletter_subscription_template"><i class="fas fa-file-alt"></i> <?php _e('Messages Template', 'newsletter') ?>
159
- <small><?php _e('Change the look of your service emails', 'newsletter') ?></small></a>
160
- </li>
161
-
162
- <?php
163
- newsletter_print_entries('settings');
164
- ?>
165
- </ul>
166
- </li>
167
-
168
- <?php if ($is_administrator) { ?>
169
- <li>
170
- <a href="#"><i class="fas fa-thermometer"></i> <?php _e('System', 'newsletter') ?>
171
- <?php if ($warning) { ?>
172
- <i class="fas fa-exclamation-triangle" style="color: red;"></i>
173
- <?php } ?>
174
- </a>
175
- <ul>
176
- <li>
177
- <a href="<?php echo admin_url('site-health.php') ?> "><i class="fas fa-file"></i> <?php _e('Site health') ?>
178
- <small><?php _e('WP native site health checks', 'newsletter') ?></small></a>
179
- </li>
180
- <li>
181
- <a href="?page=newsletter_system_delivery"><i class="fas fa-file"></i> <?php _e('Delivery Diagnostic', 'newsletter') ?>
182
- <small><?php _e('Delivery analysis and test', 'newsletter') ?></small></a>
183
- </li>
184
- <li>
185
- <a href="?page=newsletter_system_scheduler"><i class="fas fa-robot"></i> <?php _e('Scheduler', 'newsletter') ?>
186
- <small><?php _e('WordPress background jobs', 'newsletter') ?></small></a>
187
- </li>
188
- <li>
189
- <a href="?page=newsletter_system_status"><i class="fas fa-file"></i> <?php _e('Status', 'newsletter') ?>
190
- <small><?php _e('Checks and parameters', 'newsletter') ?></small></a>
191
- </li>
192
-
193
- <li>
194
- <a href="?page=newsletter_system_logs"><i class="fas fa-file"></i> <?php _e('Logs', 'newsletter') ?>
195
- <small><?php _e('Plugin and addons logs', 'newsletter') ?></small></a>
196
- </li>
197
- </ul>
198
- </li>
199
- <?php } ?>
200
-
201
- <?php
202
- $license_data = Newsletter::instance()->get_license_data();
203
- $premium_url = 'https://www.thenewsletterplugin.com/premium?utm_source=header&utm_medium=link&utm_campaign=plugin&utm_content=' . urlencode($_GET['page']);
204
- ?>
205
-
206
- <?php if (empty($license_data)) { ?>
207
- <?php if (time() < 1638226799) { ?>
208
- <li class="tnp-professional-extensions-button" style="background-color: #000; color: #fff; border-color: #FF5F65!important; border-width: 2px !important;"><a href="https://www.thenewsletterplugin.com/premium?utm_source=header&utm_medium=link&utm_campaign=black-friday" target="_blank">
209
- <i class="fas fa-trophy"></i> BLACK FRIDAY IS LIVE</a>
210
- </li>
211
- <?php } else { ?>
212
- <li class="tnp-professional-extensions-button"><a href="<?php echo $premium_url ?>" target="_blank">
213
- <i class="fas fa-trophy"></i> <?php _e('Get Professional Addons', 'newsletter') ?></a>
214
- </li>
215
- <?php } ?>
216
- <?php } elseif (is_wp_error($license_data)) { ?>
217
- <li class="tnp-professional-extensions-button-red">
218
- <a href="?page=newsletter_main_main"><i class="fas fa-hand-paper" style="color: white"></i> <?php _e('Unable to check', 'newsletter') ?></a>
219
- </li>
220
-
221
- <?php } elseif ($license_data->expire == 0) { ?>
222
- <?php if (time() < 1638226799) { ?>
223
- <li class="tnp-professional-extensions-button" style="background-color: #000; color: #fff; border-color: #FF5F65!important; border-width: 2px !important;"><a href="https://www.thenewsletterplugin.com/premium?utm_source=header&utm_medium=link&utm_campaign=black-friday" target="_blank">
224
- <i class="fas fa-trophy"></i> BLACK FRIDAY IS LIVE</a>
225
- </li>
226
- <?php } else { ?>
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
- <?php } ?>
231
-
232
- <?php } elseif ($license_data->expire < time()) { ?>
233
-
234
- <li class="tnp-professional-extensions-button-red">
235
- <a href="?page=newsletter_main_main"><i class="fas fa-hand-paper" style="color: white"></i> <?php _e('License expired', 'newsletter') ?></a>
236
- </li>
237
-
238
- <?php } elseif ($license_data->expire >= time()) { ?>
239
-
240
- <?php $p = class_exists('NewsletterExtensions') ? 'newsletter_extensions_index' : 'newsletter_main_extensions'; ?>
241
- <li class="tnp-professional-extensions-button">
242
- <a href="?page=<?php echo $p ?>"><i class="fas fa-check-square"></i> <?php _e('License active', 'newsletter') ?></a>
243
- </li>
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 esc_attr($_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 esc_attr($_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 esc_attr($_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 esc_attr($_SERVER['REQUEST_URI']) . '&noheader=1&dismiss=newsletter-subscribe' ?>" class="tnp-dismiss">&times;</a>
294
- Subscribe to our news, promotions and getting started lessons!
295
- Proceeding you agree to the <a href="https://www.thenewsletterplugin.com/privacy" target="_blank">privacy policy</a>.
296
- <br>
297
- <form action="https://www.thenewsletterplugin.com/?na=s" target="_blank" method="post">
298
- <input type="hidden" value="plugin-header" name="nr">
299
- <input type="hidden" value="3" name="nl[]">
300
- <input type="hidden" value="1" name="nl[]">
301
- <input type="hidden" value="double" name="optin">
302
- <input type="email" name="ne" value="<?php echo esc_attr($current_user_email) ?>">
303
- <input type="submit" value="<?php esc_attr_e('Subscribe', 'newsletter') ?>">
304
- </form>
305
- </div>
306
- <?php } ?>
307
-
308
- <?php
309
- if (!defined('NEWSLETTER_CRON_WARNINGS') || NEWSLETTER_CRON_WARNINGS) {
310
- $x = NewsletterSystem::instance()->get_job_status();
311
- if ($x !== NewsletterSystem::JOB_OK) {
312
- echo '<div class="tnpc-warning">There are issues with the delivery engine. Please <a href="?page=newsletter_system_scheduler">check them here</a>.</div>';
313
- }
314
- }
315
- ?>
316
-
317
- <?php
318
- if ($_GET['page'] !== 'newsletter_emails_edit') {
319
-
320
- $last_failed_newsletters = Newsletter::instance()->get_emails_by_status(TNP_Email::STATUS_ERROR);
321
- if ($last_failed_newsletters) {
322
- $c = new NewsletterControls();
323
- foreach ($last_failed_newsletters as $n) {
324
- echo '<div class="tnpc-error">';
325
- printf(__('Newsletter "%s" stopped by fatal error.', 'newsletter'), esc_html($n->subject));
326
- echo '&nbsp;';
327
-
328
- $c->btn_link('?page=newsletter_emails_edit&id=' . $n->id, __('Check', 'newsletter'));
329
- echo '</div>';
330
- }
331
- }
332
- }
333
- ?>
334
-
335
- <div id="tnp-notification">
336
- <?php
337
- if (isset($controls)) {
338
- $controls->show();
339
- $controls->messages = '';
340
- $controls->errors = '';
341
- }
342
- ?>
343
- </div>
344
-
345
-
 
 
 
 
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
+ $current_user_email = ''; //$current_user->user_email;
34
+ //if (strpos($current_user_email, 'admin@') === 0) {
35
+ // $current_user_email = '';
36
+ //}
37
+ ?>
38
+
39
+ <div class="tnp-drowpdown" id="tnp-header">
40
+ <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>
41
+ <ul>
42
+ <li><a href="#"><i class="fas fa-users"></i> <?php _e('Subscribers', 'newsletter') ?> <i class="fas fa-chevron-down"></i></a>
43
+ <ul>
44
+ <li>
45
+ <a href="?page=newsletter_users_index"><i class="fas fa-search"></i> <?php _e('Search And Edit', 'newsletter') ?>
46
+ <small><?php _e('Add, edit, search', 'newsletter') ?></small></a>
47
+ </li>
48
+
49
+ <li>
50
+ <a href="?page=newsletter_profile_index"><i class="fas fa-user-circle"></i> <?php _e('Profile page', 'newsletter') ?>
51
+ <small><?php _e('The subscriber personal profile editing panel', 'newsletter') ?></small></a>
52
+ </li>
53
+
54
+ <?php if (!class_exists('NewsletterImport')) { ?>
55
+ <li>
56
+ <a href="?page=newsletter_users_import"><i class="fas fa-upload"></i> <?php _e('Import', 'newsletter') ?>
57
+ <small><?php _e('Import from external sources', 'newsletter') ?></small></a>
58
+ </li>
59
+ <?php } ?>
60
+
61
+ <li>
62
+ <a href="?page=newsletter_users_export"><i class="fas fa-download"></i> <?php _e('Export', 'newsletter') ?>
63
+ <small><?php _e('Export your subscribers list', 'newsletter') ?></small></a>
64
+ </li>
65
+
66
+ <li>
67
+ <a href="?page=newsletter_users_massive"><i class="fas fa-wrench"></i> <?php _e('Maintenance', 'newsletter') ?>
68
+ <small><?php _e('Massive actions: change list, clean up, ...', 'newsletter') ?></small></a>
69
+ </li>
70
+
71
+ <li>
72
+ <a href="?page=newsletter_users_statistics"><i class="fas fa-chart-bar"></i> <?php _e('Statistics', 'newsletter') ?>
73
+ <small><?php _e('All about your subscribers', 'newsletter') ?></small></a>
74
+ </li>
75
+
76
+ <?php newsletter_print_entries('subscribers') ?>
77
+ </ul>
78
+ </li>
79
+ <li><a href="#"><i class="fas fa-list"></i> <?php _e('List Building', 'newsletter') ?> <i class="fas fa-chevron-down"></i></a>
80
+ <ul>
81
+ <li>
82
+ <a href="?page=newsletter_subscription_profile"><i class="fas fa-check-square"></i> <?php _e('Subscription Form Fields, Buttons, Labels', 'newsletter') ?>
83
+ <small><?php _e('When and what data to collect', 'newsletter') ?></small></a>
84
+ </li>
85
+
86
+ <li>
87
+ <a href="?page=newsletter_subscription_options"><i class="fas fa-sign-in-alt"></i> <?php _e('Subscription', 'newsletter') ?>
88
+ <small><?php _e('The subscription process in detail', 'newsletter') ?></small></a>
89
+ </li>
90
+
91
+ <li>
92
+ <a href="?page=newsletter_subscription_lists"><i class="fas fa-th-list"></i> <?php _e('Lists', 'newsletter') ?>
93
+ <small><?php _e('Profile the subscribers for a better targeting', 'newsletter') ?></small></a>
94
+ </li>
95
+
96
+ <li>
97
+ <a href="?page=newsletter_subscription_antibot"><i class="fas fa-lock"></i> <?php _e('Security', 'newsletter') ?>
98
+ <small><?php _e('Spam subscriptions control', 'newsletter') ?></small></a>
99
+ </li>
100
+
101
+ <li>
102
+ <a href="?page=newsletter_unsubscription_index"><i class="fas fa-sign-out-alt"></i> <?php _e('Unsubscription', 'newsletter') ?>
103
+ <small><?php _e('How to give the last goodbye (or avoid it!)', 'newsletter') ?></small></a>
104
+ </li>
105
+
106
+ <?php
107
+ newsletter_print_entries('subscription');
108
+ ?>
109
+ </ul>
110
+ </li>
111
+
112
+ <li>
113
+ <a href="#"><i class="fas fa-newspaper"></i> <?php _e('Newsletters', 'newsletter') ?> <i class="fas fa-chevron-down"></i></a>
114
+ <ul>
115
+ <li>
116
+ <a href="?page=newsletter_emails_composer"><i class="fas fa-plus"></i> <?php _e('Create newsletter', 'newsletter') ?>
117
+ <small><?php _e('Start your new campaign', 'newsletter') ?></small></a>
118
+ </li>
119
+
120
+ <li>
121
+ <a href="?page=newsletter_emails_index"><i class="fas fa-newspaper"></i> <?php _e('Newsletters', 'newsletter') ?>
122
+ <small><?php _e('The classic "write & send" newsletters', 'newsletter') ?></small></a>
123
+ </li>
124
+
125
+ <li>
126
+ <a href="<?php echo NewsletterStatistics::instance()->get_index_url() ?>"><i class="fas fa-chart-bar"></i> <?php _e('Statistics', 'newsletter') ?>
127
+ <small><?php _e('Tracking configuration and basic data', 'newsletter') ?></small></a>
128
+ </li>
129
+ <?php
130
+ newsletter_print_entries('newsletters');
131
+ ?>
132
+ </ul>
133
+ </li>
134
+
135
+ <li>
136
+ <a href="#"><i class="fas fa-cog"></i> <?php _e('Settings', 'newsletter') ?> <i class="fas fa-chevron-down"></i></a>
137
+ <ul>
138
+ <?php if ($is_administrator) { ?>
139
+ <li>
140
+ <a href="?page=newsletter_main_main"><i class="fas fa-cogs"></i> <?php _e('General Settings', 'newsletter') ?>
141
+ <small><?php _e('Delivery speed, sender details, ...', 'newsletter') ?></small></a>
142
+ </li>
143
+ <?php if (!class_exists('NewsletterSmtp')) { ?>
144
+ <li>
145
+ <a href="?page=newsletter_main_smtp"><i class="fas fa-server"></i> <?php _e('SMTP', 'newsletter') ?>
146
+ <small><?php _e('External mail server', 'newsletter') ?></small>
147
+ </a>
148
+ </li>
149
+ <?php } ?>
150
+ <?php } ?>
151
+
152
+ <li>
153
+ <a href="?page=newsletter_main_info"><i class="fas fa-info"></i> <?php _e('Company Info', 'newsletter') ?>
154
+ <small><?php _e('Social, address, logo and general info', 'newsletter') ?></small></a>
155
+ </li>
156
+
157
+ <li>
158
+ <a href="?page=newsletter_subscription_template"><i class="fas fa-file-alt"></i> <?php _e('Messages Template', 'newsletter') ?>
159
+ <small><?php _e('Change the look of your service emails', 'newsletter') ?></small></a>
160
+ </li>
161
+
162
+ <?php
163
+ newsletter_print_entries('settings');
164
+ ?>
165
+ </ul>
166
+ </li>
167
+
168
+ <?php if ($is_administrator) { ?>
169
+ <li>
170
+ <a href="#"><i class="fas fa-thermometer"></i> <?php _e('System', 'newsletter') ?>
171
+ <?php if ($warning) { ?>
172
+ <i class="fas fa-exclamation-triangle" style="color: red;"></i>
173
+ <?php } ?>
174
+ </a>
175
+ <ul>
176
+ <li>
177
+ <a href="<?php echo admin_url('site-health.php') ?> "><i class="fas fa-file"></i> <?php _e('Site health') ?>
178
+ <small><?php _e('WP native site health checks', 'newsletter') ?></small></a>
179
+ </li>
180
+ <li>
181
+ <a href="?page=newsletter_system_delivery"><i class="fas fa-file"></i> <?php _e('Delivery Diagnostic', 'newsletter') ?>
182
+ <small><?php _e('Delivery analysis and test', 'newsletter') ?></small></a>
183
+ </li>
184
+ <li>
185
+ <a href="?page=newsletter_system_scheduler"><i class="fas fa-robot"></i> <?php _e('Scheduler', 'newsletter') ?>
186
+ <small><?php _e('WordPress background jobs', 'newsletter') ?></small></a>
187
+ </li>
188
+ <li>
189
+ <a href="?page=newsletter_system_status"><i class="fas fa-file"></i> <?php _e('Status', 'newsletter') ?>
190
+ <small><?php _e('Checks and parameters', 'newsletter') ?></small></a>
191
+ </li>
192
+
193
+ <li>
194
+ <a href="?page=newsletter_system_logs"><i class="fas fa-file"></i> <?php _e('Logs', 'newsletter') ?>
195
+ <small><?php _e('Plugin and addons logs', 'newsletter') ?></small></a>
196
+ </li>
197
+ <li>
198
+ <a href="https://www.thenewsletterplugin.com/documentation/developers/backup-recovery/" target="_blank"><i class="fas fa-file"></i> <?php _e('Backup', 'newsletter') ?>
199
+ <small><?php _e('How to backup, recovery, delete', 'newsletter') ?></small></a>
200
+ </li>
201
+ </ul>
202
+ </li>
203
+ <?php } ?>
204
+
205
+ <?php
206
+ $license_data = Newsletter::instance()->get_license_data();
207
+ $premium_url = 'https://www.thenewsletterplugin.com/premium?utm_source=header&utm_medium=link&utm_campaign=plugin&utm_content=' . urlencode($_GET['page']);
208
+ ?>
209
+
210
+ <?php if (empty($license_data)) { ?>
211
+ <?php if (time() < 1638226799) { ?>
212
+ <li class="tnp-professional-extensions-button" style="background-color: #000; color: #fff; border-color: #FF5F65!important; border-width: 2px !important;"><a href="https://www.thenewsletterplugin.com/premium?utm_source=header&utm_medium=link&utm_campaign=black-friday" target="_blank">
213
+ <i class="fas fa-trophy"></i> BLACK FRIDAY IS LIVE</a>
214
+ </li>
215
+ <?php } else { ?>
216
+ <li class="tnp-professional-extensions-button"><a href="<?php echo $premium_url ?>" target="_blank">
217
+ <i class="fas fa-trophy"></i> <?php _e('Get Professional Addons', 'newsletter') ?></a>
218
+ </li>
219
+ <?php } ?>
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
+ <?php if (time() < 1638226799) { ?>
227
+ <li class="tnp-professional-extensions-button" style="background-color: #000; color: #fff; border-color: #FF5F65!important; border-width: 2px !important;"><a href="https://www.thenewsletterplugin.com/premium?utm_source=header&utm_medium=link&utm_campaign=black-friday" target="_blank">
228
+ <i class="fas fa-trophy"></i> BLACK FRIDAY IS LIVE</a>
229
+ </li>
230
+ <?php } else { ?>
231
+ <li class="tnp-professional-extensions-button"><a href="<?php echo $premium_url ?>" target="_blank">
232
+ <i class="fas fa-trophy"></i> <?php _e('Get Professional Addons', 'newsletter') ?></a>
233
+ </li>
234
+ <?php } ?>
235
+
236
+ <?php } elseif ($license_data->expire < time()) { ?>
237
+
238
+ <li class="tnp-professional-extensions-button-red">
239
+ <a href="?page=newsletter_main_main"><i class="fas fa-hand-paper" style="color: white"></i> <?php _e('License expired', 'newsletter') ?></a>
240
+ </li>
241
+
242
+ <?php } elseif ($license_data->expire >= time()) { ?>
243
+
244
+ <?php $p = class_exists('NewsletterExtensions') ? 'newsletter_extensions_index' : 'newsletter_main_extensions'; ?>
245
+ <li class="tnp-professional-extensions-button">
246
+ <a href="?page=<?php echo $p ?>"><i class="fas fa-check-square"></i> <?php _e('License active', 'newsletter') ?></a>
247
+ </li>
248
+
249
+ <?php } ?>
250
+ </ul>
251
+ </div>
252
+
253
+ <?php if (isset($_GET['debug']) || !isset($dismissed['newsletter-shortcode'])) { ?>
254
+ <?php
255
+ // Check of Newsletter dedicated page
256
+ if (!empty(Newsletter::instance()->options['page'])) {
257
+ if (get_post_status(Newsletter::instance()->options['page']) === 'publish') {
258
+ $content = get_post_field('post_content', Newsletter::instance()->options['page']);
259
+ // With and without attributes
260
+ if (strpos($content, '[newsletter]') === false && strpos($content, '[newsletter ') === false) {
261
+ ?>
262
+ <div class="tnp-notice">
263
+ <a href="<?php echo esc_attr($_SERVER['REQUEST_URI']) . '&noheader=1&dismiss=newsletter-shortcode' ?>" class="tnp-dismiss">&times;</a>
264
+ The Newsletter dedicated page does not contain the [newsletter] shortcode. If you're using a visual composer it could be ok.
265
+ <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>.
266
+
267
+ </div>
268
+ <?php
269
+ }
270
+ }
271
+ }
272
+ ?>
273
+ <?php } ?>
274
+
275
+ <?php if (isset($_GET['debug']) || !isset($dismissed['rate']) && $user_count > 300) { ?>
276
+ <div class="tnp-notice">
277
+ <a href="<?php echo esc_attr($_SERVER['REQUEST_URI']) . '&noheader=1&dismiss=rate' ?>" class="tnp-dismiss">&times;</a>
278
+
279
+ We never asked before and we're curious: <a href="http://wordpress.org/plugins/newsletter/" target="_blank">would you rate this plugin</a>?
280
+ (few seconds required - account on WordPress.org required, every blog owner should have one...). <strong>Really appreciated, The Newsletter Team</strong>.
281
+
282
+ </div>
283
+ <?php } ?>
284
+
285
+ <?php if (isset($_GET['debug']) || !isset($dismissed['newsletter-page']) && empty(Newsletter::instance()->options['page'])) { ?>
286
+ <div class="tnp-notice">
287
+ <a href="<?php echo esc_attr($_SERVER['REQUEST_URI']) . '&noheader=1&dismiss=newsletter-page' ?>" class="tnp-dismiss">&times;</a>
288
+
289
+ You should create a blog page to show the subscription form and the subscription messages. Go to the
290
+ <a href="?page=newsletter_main_main">general settings panel</a> to configure it.
291
+
292
+ </div>
293
+ <?php } ?>
294
+
295
+ <?php if (isset($_GET['debug']) || !isset($dismissed['newsletter-subscribe']) && get_option('newsletter_install_time') && get_option('newsletter_install_time') < time() - 86400 * 15) { ?>
296
+ <div class="tnp-notice">
297
+ <a href="<?php echo esc_attr($_SERVER['REQUEST_URI']) . '&noheader=1&dismiss=newsletter-subscribe' ?>" class="tnp-dismiss">&times;</a>
298
+ Subscribe to our news, promotions and getting started lessons!
299
+ Proceeding you agree to the <a href="https://www.thenewsletterplugin.com/privacy" target="_blank">privacy policy</a>.
300
+ <br>
301
+ <form action="https://www.thenewsletterplugin.com/?na=s" target="_blank" method="post">
302
+ <input type="hidden" value="plugin-header" name="nr">
303
+ <input type="hidden" value="3" name="nl[]">
304
+ <input type="hidden" value="1" name="nl[]">
305
+ <input type="hidden" value="double" name="optin">
306
+ <input type="email" name="ne" value="<?php echo esc_attr($current_user_email) ?>">
307
+ <input type="submit" value="<?php esc_attr_e('Subscribe', 'newsletter') ?>">
308
+ </form>
309
+ </div>
310
+ <?php } ?>
311
+
312
+ <?php
313
+ if (!defined('NEWSLETTER_CRON_WARNINGS') || NEWSLETTER_CRON_WARNINGS) {
314
+ $x = NewsletterSystem::instance()->get_job_status();
315
+ if ($x !== NewsletterSystem::JOB_OK) {
316
+ echo '<div class="tnpc-warning">There are issues with the delivery engine. Please <a href="?page=newsletter_system_scheduler">check them here</a>.</div>';
317
+ }
318
+ }
319
+ ?>
320
+
321
+ <?php
322
+ if ($_GET['page'] !== 'newsletter_emails_edit') {
323
+
324
+ $last_failed_newsletters = Newsletter::instance()->get_emails_by_status(TNP_Email::STATUS_ERROR);
325
+ if ($last_failed_newsletters) {
326
+ $c = new NewsletterControls();
327
+ foreach ($last_failed_newsletters as $n) {
328
+ echo '<div class="tnpc-error">';
329
+ printf(__('Newsletter "%s" stopped by fatal error.', 'newsletter'), esc_html($n->subject));
330
+ echo '&nbsp;';
331
+
332
+ $c->btn_link('?page=newsletter_emails_edit&id=' . $n->id, __('Check', 'newsletter'));
333
+ echo '</div>';
334
+ }
335
+ }
336
+ }
337
+ ?>
338
+
339
+ <div id="tnp-notification">
340
+ <?php
341
+ if (isset($controls)) {
342
+ $controls->show();
343
+ $controls->messages = '';
344
+ $controls->errors = '';
345
+ }
346
+ ?>
347
+ </div>
348
+
349
+