Newsletter - Version 7.4.7

Version Description

  • Changed removal order of the emojis admin script breaking newsletters' body
  • Disabled DOM warnings
Download this release

Release Info

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

Code changes from version 7.4.6 to 7.4.7

Files changed (10) hide show
  1. admin/admin.css +1448 -1448
  2. includes/addon.php +396 -396
  3. includes/composer.php +977 -972
  4. main/info.php +200 -200
  5. main/main.php +388 -388
  6. plugin.php +19 -17
  7. profile/index.php +163 -163
  8. readme.txt +6 -1
  9. subscription/template.php +121 -121
  10. unsubscription/unsubscription.php +217 -217
admin/admin.css CHANGED
@@ -1,1448 +1,1448 @@
1
- /* WordPress admin main wrapper */
2
- :root {
3
- --tnp-color-green: #27AE60;
4
- }
5
-
6
- #wpwrap {
7
- background-color: #222B36 !important;
8
- }
9
-
10
- #tnp-wrap * {
11
- -webkit-box-sizing: border-box;
12
- -moz-box-sizing: border-box;
13
- box-sizing: border-box;
14
- }
15
-
16
- #tnp-wrap *:before,
17
- #tnp-wrap *:after {
18
- -webkit-box-sizing: border-box;
19
- -moz-box-sizing: border-box;
20
- box-sizing: border-box;
21
- }
22
-
23
- #tnp-wrap,
24
- #tnp-wrap td,
25
- #tnp-wrap h1,
26
- #tnp-wrap h2,
27
- #tnp-wrap h3,
28
- #tnp-wrap h4,
29
- #tnp-wrap input,
30
- #tnp-wrap select,
31
- #tnp-wrap textarea,
32
- #tnp-wrap button,
33
- #tnp-wrap li,
34
- #tnp-wrap a
35
- {
36
- font-family: soleil, sans-serif;
37
- -webkit-font-smoothing: antialiased; /* Chrome, Safari */
38
- -moz-osx-font-smoothing: grayscale; /* Firefox */
39
- }
40
-
41
- .container {
42
- width: 100%;
43
- padding-right: 1rem;
44
- padding-left: 1rem;
45
- margin-right: auto;
46
- margin-left: auto;
47
- }
48
-
49
- @media (min-width: 576px) {
50
- .container {
51
- max-width: 540px;
52
- }
53
- }
54
-
55
- @media (min-width: 768px) {
56
- .container {
57
- max-width: 720px;
58
- }
59
- }
60
-
61
- @media (min-width: 992px) {
62
- .container {
63
- max-width: 960px;
64
- }
65
- }
66
-
67
- @media (min-width: 1200px) {
68
- .container {
69
- max-width: 1140px;
70
- }
71
- }
72
-
73
- .row:before,
74
- .row:after {
75
- display: table;
76
- content: " ";
77
-
78
- }
79
- .row:after {
80
- clear: both;
81
- }
82
- .col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12 {
83
- position: relative;
84
- min-height: 1px;
85
- padding-right: 15px;
86
- padding-left: 15px;
87
- }
88
- .col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12 {
89
- float: left;
90
- }
91
- .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12 {
92
- float: left;
93
- }
94
- .col-md-12 {width: 100%;}
95
- .col-md-11 {width: 91.66666667%;}
96
- .col-md-10 {width: 83.33333333%;}
97
- .col-md-9 {width: 75%;}
98
- .col-md-8 {width: 66.66666667%;}
99
- .col-md-7 {width: 58.33333333%;}
100
- .col-md-6 {width: 50%;}
101
- .col-md-5 {width: 41.66666667%;}
102
- .col-md-4 {width: 33.33333333%;}
103
- .col-md-3 {width: 25%;}
104
- .col-md-2 {width: 16.66666667%;}
105
- .col-md-1 {width: 8.33333333%;}
106
-
107
- @media all and (max-width: 1100px) {
108
- .col-md-12 {width: 100%;}
109
- .col-md-11 {width: 100%;}
110
- .col-md-10 {width: 100%;}
111
- .col-md-9 {width: 100%;}
112
- .col-md-8 {width: 100%;}
113
- .col-md-7 {width: 100%;}
114
- .col-md-6 {width: 100%;}
115
- .col-md-5 {width: 100%;}
116
- .col-md-4 {width: 100%;}
117
- .col-md-3 {width: 100%;}
118
- .col-md-2 {width: 100%;}
119
- .col-md-1 {width: 100%;}
120
- }
121
-
122
- .tnp-row-padded {
123
- width: 90%;
124
- margin: 0 auto;
125
- display: flex;
126
- justify-content: space-between;
127
- }
128
-
129
- .tnp-col-3-boxed {
130
- width: 30%;
131
- border: 1px solid #3c414c;
132
- padding: 0px 0px 30px 0px;
133
- border-radius: 10px;
134
- }
135
-
136
- .tnp-margin-top {
137
- margin-top: 80px;
138
- }
139
-
140
- #tnp-promotion-bar {
141
- background-color: #FF5F65;
142
- color: ddd;
143
- padding: 10px 0;
144
- text-align: center;
145
- font-size: 16px;
146
- }
147
-
148
- #tnp-promotion-bar a {
149
- color: #fff;
150
- font-weight: normal;
151
- text-decoration: none;
152
- }
153
-
154
- /*******************************************************************************
155
- * Header
156
- */
157
-
158
- #tnp-header {
159
- text-align: left;
160
- font-size: 12px;
161
- color: #fff;
162
- font-family: soleil, sans-serif;
163
- min-width: 1200px;
164
- }
165
-
166
- #tnp-header input {
167
- font-size: 12px;
168
- }
169
-
170
- #tnp-header a {
171
- text-decoration: none;
172
- color: white;
173
- letter-spacing: 0.1em;
174
- }
175
-
176
- #tnp-header a:hover {
177
- color: #fff;
178
- }
179
-
180
- .error a, .error a:hover {
181
- color: #000!important;
182
- }
183
-
184
- .updated a, .updated a:hover {
185
- color: #000!important;
186
- }
187
-
188
- /******************************************************************************
189
- * BODY
190
- ******************************************************************************/
191
-
192
- #tnp-body {
193
- padding: 0 10px 10px 10px;
194
- /*background-color: #28313C;*/
195
- }
196
-
197
- #tnp-body h1,
198
- #tnp-body h2,
199
- #tnp-body h3,
200
- #tnp-body h4,
201
- #tnp-body p {
202
- color: #fff;
203
- font-weight: normal;
204
- }
205
-
206
- #tnp-body h3 {
207
- margin-top: 25px;
208
- clear: both;
209
- margin-bottom: 10px;
210
- }
211
-
212
- #tnp-body hr {
213
- height: 0px;
214
- border: 0;
215
- border-top: 1px solid #666;
216
- }
217
-
218
- #tnp-body ul {
219
- list-style-type: circle;
220
- list-style-position: inside;
221
- }
222
-
223
- #tnp-body a, #tnp-body a:active {
224
- color: #2980B9; /* Blue */
225
- }
226
-
227
- #tnp-body a:hover {
228
- color: #3498DB;
229
- }
230
-
231
- /* Action button container */
232
- #tnp-body .tnp-submit {
233
- margin-bottom: 10px;
234
- }
235
-
236
- /* Primary button correction */
237
- #tnp-body .button,
238
- #tnp-body .button:visited,
239
- #tnp-body .button:hover,
240
- #tnp-body .button:active,
241
- #tnp-body .button:focus,
242
- #tnp-body .button-primary,
243
- #tnp-body .button-primary:visited,
244
- #tnp-body .button-primary:hover {
245
- color: #fff;
246
- text-shadow: none;
247
- width: auto;
248
- vertical-align: bottom;
249
- }
250
-
251
- #tnp-body .button-secondary,
252
- #tnp-body .button-secondary:visited,
253
- #tnp-body .button-secondary:hover {
254
- color: #fff;
255
- background-color: #999;
256
- text-shadow: none;
257
- width: auto;
258
- vertical-align: bottom;
259
- }
260
-
261
- /* Icon in button media selector */
262
- #tnp-body span.wp-media-buttons-icon:before {
263
- color: #fff;
264
- }
265
-
266
- /* Standard button */
267
- #tnp-body .tnp-button {
268
- color: #fff;
269
- background-color: #3498db;
270
- text-shadow: none;
271
- }
272
-
273
- /* White button variant */
274
- #tnp-body .button-primary.tnp-button-white, #tnp-body .tnp-button.tnp-button-white {
275
- color: #444!important;
276
- background-color: #fff!important;
277
- box-shadow: none !important;
278
- -webkit-box-shadow: none !important;
279
- width: auto;
280
- }
281
-
282
- #tnp-body tbody th,
283
- #tnp-body td,
284
- #tnp-body td p,
285
- #tnp-body td .button,
286
- #tnp-body td .button:visited,
287
- #tnp-body td .button:hover,
288
- #tnp-body td .button:active,
289
- #tnp-body td .button:focus {
290
- color: #444;
291
- }
292
-
293
- #tnp-body td a,
294
- #tnp-body td a:visited {
295
- color: #27AE60; /* Green */
296
- }
297
-
298
- /*******************************************************************************
299
- * Wide fat tables
300
- */
301
-
302
- #tnp-body .widefat {
303
- width: 90%;
304
- color: #444;
305
- }
306
-
307
- #tnp-body .widefat th {
308
- text-align: left;
309
- }
310
-
311
- #tnp-body .widefat thead {
312
- background-color: #3498DB;
313
- font-family: soleil, sans-serif;
314
- color: #fff !important;
315
- }
316
-
317
- #tnp-body .widefat thead tr th {
318
- color: #fff !important;
319
- }
320
-
321
- #tnp-body .widefat td, .widefat th {
322
- vertical-align: middle;
323
- }
324
-
325
- #tnp-body .widefat tr:nth-child(even) {
326
- background-color: #f4faff;
327
- }
328
-
329
-
330
-
331
- table.clicks td {
332
- border: 1px solid #666;
333
- padding: 2px;
334
- font-size: 10px;
335
- }
336
-
337
- table.clicks {
338
- border-collapse: collapse;
339
- }
340
-
341
- .grid {
342
- border-collapse: collapse;
343
- }
344
- .grid td, .grid th {
345
- padding: 10px;
346
- border: 1px solid #ddd;
347
- margin: 0;
348
- }
349
- .grid th {
350
- background-color: #aaa;
351
- }
352
-
353
- .tnp-buttons {
354
- /*background-color: #0073aa;*/
355
- padding: 10px 0;
356
- }
357
-
358
-
359
-
360
-
361
-
362
-
363
- .tnp-notice {
364
- padding: 15px;
365
- margin: 10px 0;
366
- padding-right: 70px;
367
- position: relative;
368
- border: 1px solid #eee;
369
- background-color: #fff;
370
- color: #444;
371
- font-size: 13px;
372
- border-left: 5px solid #27AE60;
373
- }
374
-
375
- .tnp-notice a,
376
- .tnp-warning a {
377
- color: #0073aa;
378
- text-decoration: none;
379
- font-weight: bold;
380
- }
381
-
382
- .tnp-notice a.tnp-dismiss,
383
- .tnp-warning a.tnp-dismiss{
384
- display: block;
385
- position: absolute;
386
- right: 10px;
387
- top: 13px;
388
- font-size: 25px;
389
- text-decoration: none;
390
- color: #666;
391
- }
392
-
393
- .tnp-notice input[type=email] {
394
- margin: 10px 5px 5px;
395
- width: 250px;
396
- border: none;
397
- box-shadow: none;
398
- background-color: #ECF0F1;
399
- padding: 8px;
400
- }
401
-
402
- .tnp-notice input[type=submit] {
403
- border: none;
404
- box-shadow: none;
405
- background-color: #27AE60;
406
- padding: 8px;
407
- font-family: soleil, sans-serif;
408
- font-size: 13px;
409
- color: #fff;
410
- cursor: pointer;
411
- }
412
-
413
- .tnp-paginator {
414
- margin-top: 10px;
415
- margin-bottom: 5px;
416
- }
417
-
418
- .newsletter-box {
419
- border: 1px solid #ddd;
420
- padding: 10px;
421
- background-color: #fafafa;
422
- margin-bottom: 15px;
423
- }
424
-
425
- .newsletter-box h3 {
426
- margin-top: 0;
427
- }
428
-
429
- .newsletter-textarea-preview {
430
- border: 1px solid #ddd;
431
- }
432
-
433
- .tnp-tab-notice {
434
- background-color: #fff;
435
- border: 1px solid #eee;
436
- border-left: 3px solid gray;
437
- padding: 10px;
438
- margin: 10px 0;
439
- color: #444;
440
- }
441
-
442
- .tnp-tab-warning {
443
- background-color: #fff;
444
- border: 1px solid #eee;
445
- border-left: 3px solid orange;
446
- padding: 10px;
447
- margin: 10px 0;
448
- color: #444;
449
- }
450
-
451
- .tnp-tab-success {
452
- background-color: #fff;
453
- border: 1px solid #eee;
454
- border-left: 3px solid green;
455
- padding: 10px;
456
- margin: 10px 0;
457
- color: #444;
458
- }
459
-
460
- .tnp-tab-error {
461
- background-color: #fff;
462
- border: 1px solid #eee;
463
- border-left: 3px solid red;
464
- padding: 10px;
465
- margin: 10px 0;
466
- color: #444;
467
- }
468
-
469
- /* .tnp-wrap a[target=_blank]:after {
470
- content: "»";
471
- }*/
472
-
473
-
474
- /* CSS The Newsletter Team */
475
-
476
- /* CSS Tips */
477
-
478
- .tnp-tip {
479
- margin-top: 5px;
480
- }
481
-
482
- .tip-button {
483
- padding: 0px 5px;
484
- color: #FD5F65;
485
- text-transform: uppercase;
486
- letter-spacing: 0.2em;
487
- /*font-family: soleil;*/
488
- font-size: 10px;
489
- border: 1px red solid;
490
- }
491
-
492
- .tip-content {
493
- /*font-family: soleil;*/
494
- font-weight: 500;
495
- font-size: 11px;
496
- color: #999999;
497
- }
498
-
499
- /* CSS General Font Styles */
500
-
501
-
502
-
503
-
504
- /* CSS Themes Preview */
505
-
506
- .tnp-theme-preview {
507
- display: inline-block;
508
- text-align: center;
509
- }
510
-
511
- .tnp-theme-preview p {
512
- font-family: soleil;
513
- font-size: 13px;
514
- letter-spacing: 0.2em;
515
- color: #fff;
516
- }
517
-
518
- .tnp-theme-preview img:hover {
519
- box-shadow: 3px 3px 8px 2px #293848;
520
- }
521
-
522
- .tnp-theme-preview img {
523
- border-radius: 10px;
524
- height: 190px;
525
- width: auto;
526
- }
527
-
528
- .tnp-theme-preview .tnp-theme-composer {
529
- height: 250px;
530
- width: auto;
531
- }
532
-
533
- .tnp-theme-preview .tnp-theme-html {
534
- width: auto;
535
- }
536
-
537
-
538
- .tnp-header-logo {
539
- margin-left: 10px;
540
- }
541
-
542
-
543
- /* Altrimenti si crea una striscia bianca in mezzo alla pagina! */
544
-
545
-
546
- .wp-core-ui .button-primary {
547
- background-color: #2b2f3a;
548
- color: #fff;
549
- width: auto;
550
- }
551
-
552
-
553
-
554
-
555
-
556
- .tnp-body-lite {
557
- background-color: #F1F1F1 !important;
558
- }
559
-
560
-
561
- /* Header & Sub-header Pannelli */
562
-
563
- #tnp-heading {
564
- padding: 10px;
565
- /*background-color: #28313C;*/
566
- margin-bottom: 10px;
567
- border-radius: 5px;
568
- }
569
-
570
- #tnp-heading a {
571
- color: #fff;
572
- border-bottom: 1px solid #fff;
573
- text-decoration: none;
574
- }
575
-
576
- #tnp-heading a:hover {
577
- color: #27AE60;
578
- border-bottom: 1px solid #27AE60;
579
- }
580
-
581
- #tnp-heading div p {
582
- color: #565656;
583
- }
584
-
585
- #tnp-heading h1 {
586
- color: #fff;
587
- font-family: soleil, sans-serif;
588
- font-weight: 900;
589
- }
590
-
591
- #tnp-heading h2 {
592
- color: #fff;
593
- font-family: soleil, sans-serif;
594
- letter-spacing: 0.1rem;
595
- font-size: 1.1rem;
596
- line-height: normal;
597
- text-transform: uppercase;
598
- vertical-align: middle;
599
- font-weight: 700;
600
- padding: 0;
601
- margin: 0px;
602
- margin-bottom: 15px;
603
- }
604
-
605
- #tnp-heading h3 {
606
- color: #27AE60;
607
- font-family: soleil, sans-serif;
608
- letter-spacing: 0.1rem;
609
- font-size: .8rem;
610
- line-height: 1.8rem;
611
- text-transform: uppercase;
612
- vertical-align: middle;
613
- font-weight: 700;
614
- padding: 0;
615
- margin: 0px;
616
- }
617
-
618
- #tnp-heading p, #tnp-heading ul {
619
- margin: 0px;
620
- color: #ccc;
621
- }
622
-
623
- #tnp-heading ul {
624
- list-style-type: circle;
625
- list-style-position: inside;
626
- margin-left: 2em;
627
- margin-top: 1em;
628
- }
629
-
630
- /* Style for WP global notices */
631
- #tnp-heading .notice p {
632
- margin: 0.5em 0;
633
- padding: 2px;
634
- }
635
-
636
- #tnp-heading .tnp-btn-h1 {
637
- color: #fff;
638
- background-color: #3498db;
639
- border-radius: 3px;
640
- padding: 6px 11px;
641
- text-decoration: none;
642
- text-transform: capitalize;
643
- font-family: soleil, sans-serif;
644
- margin-left: 10px;
645
- font-size: 0.75rem;
646
- font-weight: 300;
647
- border: none;
648
- }
649
-
650
- #tnp-heading .tnp-btn-h1:hover {
651
- color: #fff;
652
- background-color: #5DADE2;
653
- -webkit-transition: background-color .25s linear;
654
- transition: background-color .25s linear;
655
- -webkit-font-smoothing: subpixel-antialiased;
656
- border: none;
657
- color: #fff;
658
- }
659
-
660
- /* Dashboard Box */
661
-
662
- .metabox-holder {
663
- width: 100%;
664
- }
665
-
666
- .postbox {
667
- border: none;
668
- }
669
-
670
- .postbox h3 a {
671
- float: right;
672
-
673
- }
674
-
675
-
676
- #dashboard-widgets .postbox-container {
677
- width: 33.333%
678
- }
679
-
680
- #tnp-body .postbox p {
681
- color: #000;
682
- }
683
-
684
- #dashboard-widgets .postbox-container .postbox h3 {
685
- font-family: soleil, sans-serif;
686
- letter-spacing: 0.05rem;
687
- background-color: #415b76;
688
- color: #fff;
689
- margin: 0;
690
- padding: 9px;
691
- }
692
-
693
- #dashboard-widgets .postbox-container h3 a {
694
- color: white;
695
- text-decoration: none;
696
- margin-left: 5px;
697
- padding: 2px 8px;
698
- background-color: #26C281;
699
- border-radius: 2px;
700
- font-weight: 300;
701
- text-transform: capitalize;
702
- font-size: 0.8rem;
703
- }
704
-
705
- #dashboard-widgets .postbox-container h3 a:hover {
706
- color: white;
707
- text-decoration: none;
708
- margin-left: 5px;
709
- background-color: #2ECC71;
710
- }
711
-
712
- .postbox-container i {
713
- margin-right: 3px;
714
- }
715
- #tnp-dash-newsletters tr td:last-of-type {
716
- width: 80px;
717
- text-align: right;
718
- }
719
-
720
- #tnp-dash-subscribers tr td:last-of-type {
721
- width: 80px;
722
- text-align: right;
723
- }
724
-
725
- #tnp-dash-subscribers tr td:first-of-type {
726
- width: 250px;
727
- overflow: hidden;
728
- }
729
-
730
- #tnp-dash-subscribers table {
731
- table-layout: fixed;
732
- }
733
-
734
- #tnp-dash-documentation .inside div {
735
- margin-top: 10px;
736
- }
737
-
738
- #tnp-dash-documentation .inside a {
739
- text-decoration: none;
740
- color: #fff;
741
- display: block;
742
- font-family: soleil, sans-serif;
743
- padding: 5px 10px;
744
- }
745
-
746
-
747
- /* Footer */
748
-
749
- #tnp-footer {
750
- display: flex;
751
- justify-content: space-between;
752
- align-items: flex-start;
753
- margin-top: 10px;
754
- padding: 10px 30px;
755
- background-color: #28313C;
756
- font-family: soleil, sans-serif;
757
- }
758
-
759
- #tnp-footer div {
760
- /*width: 33%;*/
761
- /*display: inline-block;*/
762
- }
763
-
764
- #tnp-footer a {
765
- color: #fff;
766
- text-decoration: none;
767
- }
768
-
769
- #tnp-footer a:hover {
770
- color: #BDC3C7;
771
- }
772
-
773
- #tnp-footer input[type="submit"] {
774
- background-color: #2ECC71;
775
- border: none;
776
- padding: 5px 10px;
777
- color: #fff;
778
- }
779
-
780
- #tnp-footer form {
781
- white-space: nowrap;
782
- }
783
-
784
- #tnp-footer li {
785
- display: inline;
786
- margin-left: 15px;
787
- padding: 2px 5px;
788
- border-left: 3px solid #2ECC71;
789
- }
790
-
791
- /* Global buttons styles */
792
-
793
- #dashboard-widgets .button {
794
- border: none;
795
- background: none;
796
- box-shadow: none;
797
- color: #322C39;
798
- }
799
-
800
- #dashboard-widgets .button:hover {
801
- background-color: #ECF0F1;
802
- }
803
-
804
- .wp-core-ui .button-secondary, .wp-core-ui .button-primary {
805
- background-color: #3498db;
806
- border: none;
807
- box-shadow: none;
808
- color: #fff;
809
- font-family: soleil,sans-serif;
810
- margin: 0px 2px;
811
- width: auto;
812
- }
813
-
814
- .wp-core-ui .button-secondary:hover, .wp-core-ui .button-primary:hover {
815
- background-color: #5DADE2;
816
- color: #fff;
817
- width: auto;
818
- }
819
-
820
- span.wp-media-buttons-icon:before {
821
- color: #fff;
822
- }
823
-
824
- .tnp-paginator [value="Go"] {
825
- background-color: #27AE60;
826
- }
827
-
828
- .tnp-paginator [value="Go"]:hover {
829
- background-color: #2ECC71;
830
- }
831
-
832
- .notice-dismiss {
833
- padding: 3px;
834
- }
835
-
836
- /*.widefat .button-secondary {
837
- background: none;
838
- color: #3498db;
839
- }*/
840
-
841
- /* Paginator */
842
-
843
- .tnp-paginator {
844
- color: #fff;
845
- font-family: soleil,sans-serif;
846
- margin: 10px 0px;
847
- }
848
-
849
- .tnp-paginator .button-secondary {
850
- padding: 5px;
851
- line-height: normal;
852
- height: auto;
853
- font-size: 12px;
854
- height: 25px;
855
- border: none;
856
- border-radius: 3px;
857
- vertical-align: baseline;
858
- }
859
-
860
- .tnp-paginator [value="Go"] {
861
- background-color: #27AE60 !important;
862
- }
863
-
864
- .tnp-paginator [value="Go"]:hover {
865
- background-color: #2ECC71 !important;
866
- }
867
-
868
- .tnp-paginator input {
869
- background-color: #2C3E50;
870
- border: none;
871
- border-radius: 3px;
872
- color: #fff;
873
- padding: 5px;
874
- line-height: normal;
875
- font-size: 12px;
876
- height: 25px;
877
- }
878
-
879
- /* Subscribers Search Box */
880
-
881
- .tnp-subscribers-search {
882
- color: #fff;
883
- font-family: soleil, sans-serif;
884
- background-color: #2C3E50;
885
- padding: 20px;
886
- border-radius: 5px;
887
- margin-bottom: 20px;
888
- display: inline-block;
889
- }
890
-
891
- .tnp-subscribers-search select {
892
- margin-left: 5px;
893
- padding: 0;
894
- line-height: inherit;
895
- }
896
-
897
-
898
- /* Responsive Video Embeds */
899
-
900
- .tnp-video-container {
901
- position: relative;
902
- padding-bottom: 56.25%;
903
- padding-top: 30px; height: 0; overflow: hidden;
904
- }
905
-
906
- .tnp-video-container iframe,
907
- .tnp-video-container object,
908
- .tnp-video-container embed {
909
- position: absolute;
910
- top: 0;
911
- left: 0;
912
- width: 100%;
913
- height: 100%;
914
- }
915
-
916
-
917
- /* Colors Palette */
918
-
919
- .bg-white {
920
- background-color: #FFF;
921
- }
922
-
923
- .orange {
924
- background-color: #F39C12; /*Orange #F39C12 */
925
- }
926
-
927
- .blue {
928
- background-color: #2980B9; /* Blue #2980B9 */
929
- }
930
-
931
- .purple {
932
- background-color: #8E44AD; /* Purple #8E44AD */
933
- }
934
-
935
- .notice a {
936
- color: #27AE60 !important;
937
- text-decoration: underline!important;
938
- }
939
-
940
- .tnp-chart {
941
- border: 1px solid #eee;
942
- width: 100%;
943
- }
944
-
945
- /* Suggerimenti Oggetto + Inserimento Emoticons */
946
-
947
- .tnp-emails-edit #options-subject {
948
- font-size: 16px;
949
- display: inline-block;
950
- margin: 20px 0px;
951
- width: auto;
952
- border-radius: 4px;
953
- padding: 5px 10px;
954
- }
955
-
956
- .tnp-suggest-button {
957
- font-family: soleil, sans-serif;
958
- margin-left: 8px;
959
- border-radius: 3px;
960
- background-color: #2980B9;
961
- padding: 10px 15px 8px;
962
- font-size: 14px;
963
- color: #fff !important;
964
- text-decoration: none;
965
- }
966
-
967
- .tnp-suggest-button:hover {
968
- background-color: #3f8dbf;
969
- }
970
-
971
- .tnp-popup-overlay {
972
- display: none;
973
- position: fixed;
974
- top: 0;
975
- left: 0;
976
- width: 100%;
977
- height: 100%;
978
- background-color: rgba(0, 0, 0, .8);
979
- z-index: 10000;
980
- }
981
-
982
- .tnp-popup {
983
- width: 40vw;
984
- height: 66vh;
985
- overflow: auto;
986
- margin: 100px auto 0 auto;
987
- background-color: #181818;
988
- padding: 20px;
989
- position: relative;
990
- }
991
- .tnp-popup-close {
992
- display: block;
993
- position: absolute;
994
- top: 5px;
995
- right: 5px;
996
- background-color: #181818;
997
- color: #fff;
998
- font-size: 40px;
999
- padding: 10px;
1000
- text-align: right;
1001
- cursor: pointer;
1002
- }
1003
-
1004
- .tnp-subjects-header {
1005
- font-size: 16px;
1006
- color: #fff;
1007
- padding: 0px 70px 20px 20px;
1008
- font-family: soleil, sans-serif;
1009
- border-bottom: 1px solid #282828;
1010
- }
1011
-
1012
- #tnp-edit-subjects-list {
1013
- padding: 0px 70px 20px 20px;
1014
- }
1015
-
1016
- #tnp-edit-subjects-list a {
1017
- padding: 5px;
1018
- }
1019
-
1020
- #tnp-edit-subjects-list svg {
1021
- margin: 0px 10px 0px 0px;
1022
- vertical-align: middle;
1023
- }
1024
-
1025
- .tnp-subject-category {
1026
- color: #565656;
1027
- margin: 25px 0px 10px 0px;
1028
- /* font-family: soleil; */
1029
- font-size: 12px;
1030
- text-transform: uppercase;
1031
- letter-spacing: 0.1em;
1032
- }
1033
-
1034
-
1035
- /* Stile selettore liste - Schermata di invio newsletter */
1036
-
1037
- .tnp-list-conditions p {
1038
- margin: 0px 10px;
1039
- }
1040
-
1041
- /* Lists panel */
1042
- .tnp-lists .tnp-notes {
1043
- margin: 0;
1044
- font-size: .9em;
1045
- }
1046
-
1047
- /* Codemirror editor with preview */
1048
- iframe.tnp-editor-preview-mobile {
1049
- box-sizing: border-box;
1050
- background-color: #fff;
1051
- border: 1px solid #bbb;
1052
- box-shadow: 1px 1px 10px #777;
1053
- border-radius: 10px;
1054
- padding: 5px;
1055
- width: 320px;
1056
- height: 500px;
1057
- float: left;
1058
- }
1059
-
1060
- iframe.tnp-editor-preview-desktop {
1061
- box-sizing: border-box;
1062
- background-color: #fff;
1063
- border: 1px solid #bbb;
1064
- border-radius: 10px;
1065
- box-shadow: 1px 1px 10px #777;
1066
- padding: 15px;
1067
- width: 650px;
1068
- margin-right: 20px;
1069
- height: 500px;
1070
- float: left;
1071
- }
1072
-
1073
-
1074
- /* Form inserimento licenza in Addons Manager */
1075
-
1076
- #tnp-license-control {
1077
- border-left: 5px solid #27ae60;
1078
- display: inline-block;
1079
- padding: 15px 20px;
1080
- margin-left: -10px;
1081
- margin-top: 15px;
1082
- border-radius: 2px;
1083
- background-color: #fff;
1084
- }
1085
-
1086
- #tnp-license-control form {
1087
- margin-bottom: 10px;
1088
- margin-top: 10px;
1089
- }
1090
-
1091
- #tnp-license-control form input {
1092
- padding-left: 10px;
1093
- }
1094
-
1095
- #tnp-license-control a {
1096
- border-bottom: none;
1097
- color: #27AE60;
1098
- }
1099
-
1100
-
1101
- /* Status Box Style */
1102
-
1103
- #tnp-nl-status {
1104
- width: 100%;
1105
- background: #fffafa;
1106
- padding: 15px 25px 15px 25px;
1107
- border-left: 10px solid #27AE60;
1108
- border-radius: 0px 5px 5px 0px;
1109
- }
1110
-
1111
- #tnp-nl-status p {
1112
- font-size: 17px;
1113
- }
1114
-
1115
- .tnp-nl-status-row {
1116
- margin: 10px 0;
1117
- }
1118
-
1119
- .tnp-nl-status-title {
1120
- font-size: 26px;
1121
- line-height: 32px;
1122
- margin: 5px 0px 0px 0px;
1123
- color: #3498DB;
1124
- font-weight: 900;
1125
- vertical-align: middle;
1126
- }
1127
-
1128
- .tnp-nl-status-title-value {
1129
- font-size: 13px;
1130
- line-height: 32px;
1131
- margin: 0px 0px 0px 5px;
1132
- color: #fff;
1133
- background-color: #95A5A6;
1134
- border-radius: 4px;
1135
- text-transform: uppercase;
1136
- letter-spacing: 1px;
1137
- vertical-align: sub;
1138
- }
1139
-
1140
- .tnp-nl-status-schedule-targeting {
1141
- font-size: 15px;
1142
- color: #34495E;
1143
- }
1144
-
1145
- .tnp-nl-status-schedule-value {
1146
- font-size: 15px;
1147
- color: #34495E;
1148
- }
1149
-
1150
- .tnp-status-header #options-subject {
1151
- width: calc(100% - 150px);
1152
- }
1153
-
1154
- /* Grid Helpers */
1155
-
1156
- .tnp-one-third {
1157
- width: 40%;
1158
- display: inline-block;
1159
- vertical-align: top;
1160
- }
1161
-
1162
- .tnp-two-thirds {
1163
- width: 59%;
1164
- display: inline-block;
1165
- vertical-align: top;
1166
- }
1167
-
1168
- /* Progress bar */
1169
- .tnp-progress {
1170
- display: flex;
1171
- height: 1.5rem;
1172
- overflow: hidden;
1173
- font-size: .75rem;
1174
- background-color: #c9cccf;
1175
- border-radius: .25rem;
1176
- margin: 0px 0px 0px;
1177
- min-width: 100px;
1178
- }
1179
-
1180
- .tnp-progress-bar {
1181
- display: -ms-flexbox;
1182
- display: flex;
1183
- -ms-flex-direction: column;
1184
- flex-direction: column;
1185
- -ms-flex-pack: center;
1186
- justify-content: center;
1187
- color: #fff;
1188
- text-align: center;
1189
- white-space: nowrap;
1190
- background-color: #007bff;
1191
- transition: width .6s ease;
1192
- }
1193
-
1194
- .tnp-progress--sent .tnp-progress-bar {
1195
- background-color: green;
1196
- }
1197
-
1198
- .tnp-progress--error .tnp-progress-bar {
1199
- background-color: #E74C3C;
1200
- }
1201
-
1202
- .tnp-progress-numbers {
1203
- text-align: center;
1204
- color: #666;
1205
- }
1206
-
1207
- .tnp-progress-date {
1208
- color: #666;
1209
- font-style: italic;
1210
- }
1211
-
1212
- span.tnp-email-status {
1213
- background-color: #95A5A6;
1214
- padding: 2px 10px;
1215
- border-radius: 4px;
1216
- color: #fff;
1217
- white-space: nowrap;
1218
- }
1219
-
1220
- /* Email status label */
1221
- span.tnp-email-status.tnp-email-status--new {
1222
- background-color: #8E44AD;
1223
- }
1224
-
1225
- span.tnp-email-status.tnp-email-status--paused {
1226
- background-color: #95A5A6;
1227
- }
1228
-
1229
- span.tnp-email-status.tnp-email-status--sending {
1230
- background-color: #27AE60;
1231
- }
1232
-
1233
- span.tnp-email-status.tnp-email-status--scheduled {
1234
- background-color: #E67E22;
1235
- }
1236
-
1237
- span.tnp-email-status.tnp-email-status--sent {
1238
- background-color: #95A5A6;
1239
- }
1240
-
1241
- span.tnp-email-status.tnp-email-status--error {
1242
- background-color: #E74C3C;
1243
- }
1244
-
1245
- /* Schedule buttons styles */
1246
-
1247
- #tnp-schedule-button {
1248
- background-color: #E67E22 !important;
1249
- }
1250
-
1251
- #tnp-schedule-button:hover {
1252
- background-color: #ec913f !important;
1253
- }
1254
-
1255
- .tnp-button-cancel {
1256
- background-color: #E74C3C !important;
1257
- }
1258
-
1259
- /* Newsletter preview on targeting panel */
1260
- .tnpc-preview {
1261
- margin-top: 10px;
1262
- }
1263
-
1264
- .tnpc-preview .fake-browser-ui iframe {
1265
- width: 700px;
1266
- }
1267
-
1268
- .tnpc-preview .fake-mobile-browser-ui iframe {
1269
- width: 320px;
1270
- }
1271
-
1272
- .fake-browser-ui {
1273
- padding: 30px 0 0;
1274
- border-radius: 3px;
1275
- border-bottom: 10px solid #ccc;
1276
- background: #ddd;
1277
- display: inline-block;
1278
- position: relative;
1279
- line-height: 0;
1280
- vertical-align: top;
1281
- margin-left: 20px;
1282
- }
1283
-
1284
- .fake-mobile-browser-ui {
1285
- padding: 30px 10px 37px;
1286
- border-radius: 10px;
1287
- border-bottom: 10px solid #ccc;
1288
- background: #ddd;
1289
- display: inline-block;
1290
- position: relative;
1291
- line-height: 0;
1292
- margin-left: 30px;
1293
- }
1294
-
1295
- .fake-browser-ui .frame {
1296
- display: block;
1297
- height: 25px;
1298
- position: absolute;
1299
- top: 12px;
1300
- left: 8px;
1301
- }
1302
-
1303
- .fake-mobile-browser-ui .frame {
1304
- display: block;
1305
- height: 25px;
1306
- margin-top: 10px;
1307
- }
1308
-
1309
- .fake-browser-ui span {
1310
- height: 12px;
1311
- width: 12px;
1312
- border-radius: 8px;
1313
- background-color: #eee;
1314
- border: 1px solid #dadada;
1315
- float: left;
1316
- margin: 0 0 0 4px;
1317
- }
1318
-
1319
- .fake-mobile-browser-ui span {
1320
- height: 50px;
1321
- width: 50px;
1322
- border-radius: 60px;
1323
- background-color: #eee;
1324
- border: 2px solid #ccc;
1325
- display: block;
1326
- margin: auto;
1327
- }
1328
-
1329
- .fake-browser-ui .bt-1 {
1330
- background-color: #ED594A;
1331
- }
1332
-
1333
- .fake-browser-ui .bt-2 {
1334
- background-color: #FDD800;
1335
- }
1336
-
1337
- .fake-browser-ui .bt-3 {
1338
- background-color: #5AC05A;
1339
- }
1340
-
1341
- /* Addons page */
1342
-
1343
- #tnp-promo {
1344
- text-align: left;
1345
- background-color: #222b36;
1346
- margin: 20px;
1347
- border-radius: 5px;
1348
- padding: 20px 40px;
1349
- }
1350
- #tnp-promo .tnp-promo-how-to {
1351
- width: 50%;
1352
- padding: 5px 20px;
1353
- margin-top: 30px;
1354
- margin-bottom: 30px;
1355
- border-left: 2px solid #F1C40F;
1356
- }
1357
-
1358
- #tnp-promo .tnp-promo-how-to h3 {
1359
- color: #ECF0F1;
1360
- margin: 0px;
1361
- line-height: 36px;
1362
- }
1363
-
1364
- #tnp-promo .tnp-promo-how-to p {
1365
- color: #ECF0F1;
1366
- margin: 0px;
1367
- font-size: 16px;
1368
- line-height: 26px;
1369
- }
1370
-
1371
- #tnp-promo .tnp-promo-buttons {
1372
- margin: 50px 0px;
1373
- }
1374
-
1375
- #tnp-promo .tnp-promo-button {
1376
- background: #27AE60;
1377
- text-decoration: none;
1378
- color: white;
1379
- padding: 15px 20px;
1380
- font-size: 15px;
1381
- border-radius: 2px;
1382
- }
1383
-
1384
- #tnp-promo .tnp-promo-button:hover {
1385
- background: #2ECC71;
1386
- color: white;
1387
- }
1388
-
1389
- #tnp-promo .tnp-promo-button i {
1390
- margin-right: 3px;
1391
- }
1392
-
1393
- #tnp-body td a.tnp-table-link,
1394
- #tnp-body td a.tnp-table-link:visited {
1395
- color: #444;
1396
- }
1397
-
1398
- #tnp-body td a.tnp-table-link:hover {
1399
- color: #3498DB;
1400
- }
1401
-
1402
- .text-left {
1403
- text-align: left;
1404
- }
1405
-
1406
- .tab-min-height {
1407
- min-height: 500px;
1408
- }
1409
-
1410
- .tnp-control-all-languages-notice {
1411
- padding: 15px;
1412
- border: 1px dashed #999;
1413
- }
1414
-
1415
- /* Spectru, color picker */
1416
-
1417
- /* Down arrow */
1418
- .sp-dd {
1419
- display: none;
1420
- }
1421
-
1422
- .sp-replacer {
1423
- width: 30px!important;
1424
- height: 30px!important;
1425
- }
1426
-
1427
-
1428
- /* Subscriber status labels */
1429
-
1430
- #tnp-body .unsubscribed {
1431
- color: gray;
1432
- }
1433
-
1434
- #tnp-body .confirmed {
1435
- color: darkgreen;
1436
- }
1437
-
1438
- #tnp-body .not-confirmed {
1439
- color: darkgray;
1440
- }
1441
-
1442
- #tnp-body .complained {
1443
- color: red;
1444
- }
1445
-
1446
- #tnp-body .bounced {
1447
- color: darkorange;
1448
- }
1
+ /* WordPress admin main wrapper */
2
+ :root {
3
+ --tnp-color-green: #27AE60;
4
+ }
5
+
6
+ #wpwrap {
7
+ background-color: #222B36 !important;
8
+ }
9
+
10
+ #tnp-wrap * {
11
+ -webkit-box-sizing: border-box;
12
+ -moz-box-sizing: border-box;
13
+ box-sizing: border-box;
14
+ }
15
+
16
+ #tnp-wrap *:before,
17
+ #tnp-wrap *:after {
18
+ -webkit-box-sizing: border-box;
19
+ -moz-box-sizing: border-box;
20
+ box-sizing: border-box;
21
+ }
22
+
23
+ #tnp-wrap,
24
+ #tnp-wrap td,
25
+ #tnp-wrap h1,
26
+ #tnp-wrap h2,
27
+ #tnp-wrap h3,
28
+ #tnp-wrap h4,
29
+ #tnp-wrap input,
30
+ #tnp-wrap select,
31
+ #tnp-wrap textarea,
32
+ #tnp-wrap button,
33
+ #tnp-wrap li,
34
+ #tnp-wrap a
35
+ {
36
+ font-family: soleil, sans-serif;
37
+ -webkit-font-smoothing: antialiased; /* Chrome, Safari */
38
+ -moz-osx-font-smoothing: grayscale; /* Firefox */
39
+ }
40
+
41
+ .container {
42
+ width: 100%;
43
+ padding-right: 1rem;
44
+ padding-left: 1rem;
45
+ margin-right: auto;
46
+ margin-left: auto;
47
+ }
48
+
49
+ @media (min-width: 576px) {
50
+ .container {
51
+ max-width: 540px;
52
+ }
53
+ }
54
+
55
+ @media (min-width: 768px) {
56
+ .container {
57
+ max-width: 720px;
58
+ }
59
+ }
60
+
61
+ @media (min-width: 992px) {
62
+ .container {
63
+ max-width: 960px;
64
+ }
65
+ }
66
+
67
+ @media (min-width: 1200px) {
68
+ .container {
69
+ max-width: 1140px;
70
+ }
71
+ }
72
+
73
+ .row:before,
74
+ .row:after {
75
+ display: table;
76
+ content: " ";
77
+
78
+ }
79
+ .row:after {
80
+ clear: both;
81
+ }
82
+ .col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12 {
83
+ position: relative;
84
+ min-height: 1px;
85
+ padding-right: 15px;
86
+ padding-left: 15px;
87
+ }
88
+ .col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12 {
89
+ float: left;
90
+ }
91
+ .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12 {
92
+ float: left;
93
+ }
94
+ .col-md-12 {width: 100%;}
95
+ .col-md-11 {width: 91.66666667%;}
96
+ .col-md-10 {width: 83.33333333%;}
97
+ .col-md-9 {width: 75%;}
98
+ .col-md-8 {width: 66.66666667%;}
99
+ .col-md-7 {width: 58.33333333%;}
100
+ .col-md-6 {width: 50%;}
101
+ .col-md-5 {width: 41.66666667%;}
102
+ .col-md-4 {width: 33.33333333%;}
103
+ .col-md-3 {width: 25%;}
104
+ .col-md-2 {width: 16.66666667%;}
105
+ .col-md-1 {width: 8.33333333%;}
106
+
107
+ @media all and (max-width: 1100px) {
108
+ .col-md-12 {width: 100%;}
109
+ .col-md-11 {width: 100%;}
110
+ .col-md-10 {width: 100%;}
111
+ .col-md-9 {width: 100%;}
112
+ .col-md-8 {width: 100%;}
113
+ .col-md-7 {width: 100%;}
114
+ .col-md-6 {width: 100%;}
115
+ .col-md-5 {width: 100%;}
116
+ .col-md-4 {width: 100%;}
117
+ .col-md-3 {width: 100%;}
118
+ .col-md-2 {width: 100%;}
119
+ .col-md-1 {width: 100%;}
120
+ }
121
+
122
+ .tnp-row-padded {
123
+ width: 90%;
124
+ margin: 0 auto;
125
+ display: flex;
126
+ justify-content: space-between;
127
+ }
128
+
129
+ .tnp-col-3-boxed {
130
+ width: 30%;
131
+ border: 1px solid #3c414c;
132
+ padding: 0px 0px 30px 0px;
133
+ border-radius: 10px;
134
+ }
135
+
136
+ .tnp-margin-top {
137
+ margin-top: 80px;
138
+ }
139
+
140
+ #tnp-promotion-bar {
141
+ background-color: #FF5F65;
142
+ color: ddd;
143
+ padding: 10px 0;
144
+ text-align: center;
145
+ font-size: 16px;
146
+ }
147
+
148
+ #tnp-promotion-bar a {
149
+ color: #fff;
150
+ font-weight: normal;
151
+ text-decoration: none;
152
+ }
153
+
154
+ /*******************************************************************************
155
+ * Header
156
+ */
157
+
158
+ #tnp-header {
159
+ text-align: left;
160
+ font-size: 12px;
161
+ color: #fff;
162
+ font-family: soleil, sans-serif;
163
+ min-width: 1200px;
164
+ }
165
+
166
+ #tnp-header input {
167
+ font-size: 12px;
168
+ }
169
+
170
+ #tnp-header a {
171
+ text-decoration: none;
172
+ color: white;
173
+ letter-spacing: 0.1em;
174
+ }
175
+
176
+ #tnp-header a:hover {
177
+ color: #fff;
178
+ }
179
+
180
+ .error a, .error a:hover {
181
+ color: #000!important;
182
+ }
183
+
184
+ .updated a, .updated a:hover {
185
+ color: #000!important;
186
+ }
187
+
188
+ /******************************************************************************
189
+ * BODY
190
+ ******************************************************************************/
191
+
192
+ #tnp-body {
193
+ padding: 0 10px 10px 10px;
194
+ /*background-color: #28313C;*/
195
+ }
196
+
197
+ #tnp-body h1,
198
+ #tnp-body h2,
199
+ #tnp-body h3,
200
+ #tnp-body h4,
201
+ #tnp-body p {
202
+ color: #fff;
203
+ font-weight: normal;
204
+ }
205
+
206
+ #tnp-body h3 {
207
+ margin-top: 25px;
208
+ clear: both;
209
+ margin-bottom: 10px;
210
+ }
211
+
212
+ #tnp-body hr {
213
+ height: 0px;
214
+ border: 0;
215
+ border-top: 1px solid #666;
216
+ }
217
+
218
+ #tnp-body ul {
219
+ list-style-type: circle;
220
+ list-style-position: inside;
221
+ }
222
+
223
+ #tnp-body a, #tnp-body a:active {
224
+ color: #2980B9; /* Blue */
225
+ }
226
+
227
+ #tnp-body a:hover {
228
+ color: #3498DB;
229
+ }
230
+
231
+ /* Action button container */
232
+ #tnp-body .tnp-submit {
233
+ margin-bottom: 10px;
234
+ }
235
+
236
+ /* Primary button correction */
237
+ #tnp-body .button,
238
+ #tnp-body .button:visited,
239
+ #tnp-body .button:hover,
240
+ #tnp-body .button:active,
241
+ #tnp-body .button:focus,
242
+ #tnp-body .button-primary,
243
+ #tnp-body .button-primary:visited,
244
+ #tnp-body .button-primary:hover {
245
+ color: #fff;
246
+ text-shadow: none;
247
+ width: auto;
248
+ vertical-align: bottom;
249
+ }
250
+
251
+ #tnp-body .button-secondary,
252
+ #tnp-body .button-secondary:visited,
253
+ #tnp-body .button-secondary:hover {
254
+ color: #fff;
255
+ background-color: #999;
256
+ text-shadow: none;
257
+ width: auto;
258
+ vertical-align: bottom;
259
+ }
260
+
261
+ /* Icon in button media selector */
262
+ #tnp-body span.wp-media-buttons-icon:before {
263
+ color: #fff;
264
+ }
265
+
266
+ /* Standard button */
267
+ #tnp-body .tnp-button {
268
+ color: #fff;
269
+ background-color: #3498db;
270
+ text-shadow: none;
271
+ }
272
+
273
+ /* White button variant */
274
+ #tnp-body .button-primary.tnp-button-white, #tnp-body .tnp-button.tnp-button-white {
275
+ color: #444!important;
276
+ background-color: #fff!important;
277
+ box-shadow: none !important;
278
+ -webkit-box-shadow: none !important;
279
+ width: auto;
280
+ }
281
+
282
+ #tnp-body tbody th,
283
+ #tnp-body td,
284
+ #tnp-body td p,
285
+ #tnp-body td .button,
286
+ #tnp-body td .button:visited,
287
+ #tnp-body td .button:hover,
288
+ #tnp-body td .button:active,
289
+ #tnp-body td .button:focus {
290
+ color: #444;
291
+ }
292
+
293
+ #tnp-body td a,
294
+ #tnp-body td a:visited {
295
+ color: #27AE60; /* Green */
296
+ }
297
+
298
+ /*******************************************************************************
299
+ * Wide fat tables
300
+ */
301
+
302
+ #tnp-body .widefat {
303
+ width: 90%;
304
+ color: #444;
305
+ }
306
+
307
+ #tnp-body .widefat th {
308
+ text-align: left;
309
+ }
310
+
311
+ #tnp-body .widefat thead {
312
+ background-color: #3498DB;
313
+ font-family: soleil, sans-serif;
314
+ color: #fff !important;
315
+ }
316
+
317
+ #tnp-body .widefat thead tr th {
318
+ color: #fff !important;
319
+ }
320
+
321
+ #tnp-body .widefat td, .widefat th {
322
+ vertical-align: middle;
323
+ }
324
+
325
+ #tnp-body .widefat tr:nth-child(even) {
326
+ background-color: #f4faff;
327
+ }
328
+
329
+
330
+
331
+ table.clicks td {
332
+ border: 1px solid #666;
333
+ padding: 2px;
334
+ font-size: 10px;
335
+ }
336
+
337
+ table.clicks {
338
+ border-collapse: collapse;
339
+ }
340
+
341
+ .grid {
342
+ border-collapse: collapse;
343
+ }
344
+ .grid td, .grid th {
345
+ padding: 10px;
346
+ border: 1px solid #ddd;
347
+ margin: 0;
348
+ }
349
+ .grid th {
350
+ background-color: #aaa;
351
+ }
352
+
353
+ .tnp-buttons {
354
+ /*background-color: #0073aa;*/
355
+ padding: 10px 0;
356
+ }
357
+
358
+
359
+
360
+
361
+
362
+
363
+ .tnp-notice {
364
+ padding: 15px;
365
+ margin: 10px 0;
366
+ padding-right: 70px;
367
+ position: relative;
368
+ border: 1px solid #eee;
369
+ background-color: #fff;
370
+ color: #444;
371
+ font-size: 13px;
372
+ border-left: 5px solid #27AE60;
373
+ }
374
+
375
+ .tnp-notice a,
376
+ .tnp-warning a {
377
+ color: #0073aa;
378
+ text-decoration: none;
379
+ font-weight: bold;
380
+ }
381
+
382
+ .tnp-notice a.tnp-dismiss,
383
+ .tnp-warning a.tnp-dismiss{
384
+ display: block;
385
+ position: absolute;
386
+ right: 10px;
387
+ top: 13px;
388
+ font-size: 25px;
389
+ text-decoration: none;
390
+ color: #666;
391
+ }
392
+
393
+ .tnp-notice input[type=email] {
394
+ margin: 10px 5px 5px;
395
+ width: 250px;
396
+ border: none;
397
+ box-shadow: none;
398
+ background-color: #ECF0F1;
399
+ padding: 8px;
400
+ }
401
+
402
+ .tnp-notice input[type=submit] {
403
+ border: none;
404
+ box-shadow: none;
405
+ background-color: #27AE60;
406
+ padding: 8px;
407
+ font-family: soleil, sans-serif;
408
+ font-size: 13px;
409
+ color: #fff;
410
+ cursor: pointer;
411
+ }
412
+
413
+ .tnp-paginator {
414
+ margin-top: 10px;
415
+ margin-bottom: 5px;
416
+ }
417
+
418
+ .newsletter-box {
419
+ border: 1px solid #ddd;
420
+ padding: 10px;
421
+ background-color: #fafafa;
422
+ margin-bottom: 15px;
423
+ }
424
+
425
+ .newsletter-box h3 {
426
+ margin-top: 0;
427
+ }
428
+
429
+ .newsletter-textarea-preview {
430
+ border: 1px solid #ddd;
431
+ }
432
+
433
+ .tnp-tab-notice {
434
+ background-color: #fff;
435
+ border: 1px solid #eee;
436
+ border-left: 3px solid gray;
437
+ padding: 10px;
438
+ margin: 10px 0;
439
+ color: #444;
440
+ }
441
+
442
+ .tnp-tab-warning {
443
+ background-color: #fff;
444
+ border: 1px solid #eee;
445
+ border-left: 3px solid orange;
446
+ padding: 10px;
447
+ margin: 10px 0;
448
+ color: #444;
449
+ }
450
+
451
+ .tnp-tab-success {
452
+ background-color: #fff;
453
+ border: 1px solid #eee;
454
+ border-left: 3px solid green;
455
+ padding: 10px;
456
+ margin: 10px 0;
457
+ color: #444;
458
+ }
459
+
460
+ .tnp-tab-error {
461
+ background-color: #fff;
462
+ border: 1px solid #eee;
463
+ border-left: 3px solid red;
464
+ padding: 10px;
465
+ margin: 10px 0;
466
+ color: #444;
467
+ }
468
+
469
+ /* .tnp-wrap a[target=_blank]:after {
470
+ content: "»";
471
+ }*/
472
+
473
+
474
+ /* CSS The Newsletter Team */
475
+
476
+ /* CSS Tips */
477
+
478
+ .tnp-tip {
479
+ margin-top: 5px;
480
+ }
481
+
482
+ .tip-button {
483
+ padding: 0px 5px;
484
+ color: #FD5F65;
485
+ text-transform: uppercase;
486
+ letter-spacing: 0.2em;
487
+ /*font-family: soleil;*/
488
+ font-size: 10px;
489
+ border: 1px red solid;
490
+ }
491
+
492
+ .tip-content {
493
+ /*font-family: soleil;*/
494
+ font-weight: 500;
495
+ font-size: 11px;
496
+ color: #999999;
497
+ }
498
+
499
+ /* CSS General Font Styles */
500
+
501
+
502
+
503
+
504
+ /* CSS Themes Preview */
505
+
506
+ .tnp-theme-preview {
507
+ display: inline-block;
508
+ text-align: center;
509
+ }
510
+
511
+ .tnp-theme-preview p {
512
+ font-family: soleil;
513
+ font-size: 13px;
514
+ letter-spacing: 0.2em;
515
+ color: #fff;
516
+ }
517
+
518
+ .tnp-theme-preview img:hover {
519
+ box-shadow: 3px 3px 8px 2px #293848;
520
+ }
521
+
522
+ .tnp-theme-preview img {
523
+ border-radius: 10px;
524
+ height: 190px;
525
+ width: auto;
526
+ }
527
+
528
+ .tnp-theme-preview .tnp-theme-composer {
529
+ height: 250px;
530
+ width: auto;
531
+ }
532
+
533
+ .tnp-theme-preview .tnp-theme-html {
534
+ width: auto;
535
+ }
536
+
537
+
538
+ .tnp-header-logo {
539
+ margin-left: 10px;
540
+ }
541
+
542
+
543
+ /* Altrimenti si crea una striscia bianca in mezzo alla pagina! */
544
+
545
+
546
+ .wp-core-ui .button-primary {
547
+ background-color: #2b2f3a;
548
+ color: #fff;
549
+ width: auto;
550
+ }
551
+
552
+
553
+
554
+
555
+
556
+ .tnp-body-lite {
557
+ background-color: #F1F1F1 !important;
558
+ }
559
+
560
+
561
+ /* Header & Sub-header Pannelli */
562
+
563
+ #tnp-heading {
564
+ padding: 10px;
565
+ /*background-color: #28313C;*/
566
+ margin-bottom: 10px;
567
+ border-radius: 5px;
568
+ }
569
+
570
+ #tnp-heading a {
571
+ color: #fff;
572
+ border-bottom: 1px solid #fff;
573
+ text-decoration: none;
574
+ }
575
+
576
+ #tnp-heading a:hover {
577
+ color: #27AE60;
578
+ border-bottom: 1px solid #27AE60;
579
+ }
580
+
581
+ #tnp-heading div p {
582
+ color: #565656;
583
+ }
584
+
585
+ #tnp-heading h1 {
586
+ color: #fff;
587
+ font-family: soleil, sans-serif;
588
+ font-weight: 900;
589
+ }
590
+
591
+ #tnp-heading h2 {
592
+ color: #fff;
593
+ font-family: soleil, sans-serif;
594
+ letter-spacing: 0.1rem;
595
+ font-size: 1.1rem;
596
+ line-height: normal;
597
+ text-transform: uppercase;
598
+ vertical-align: middle;
599
+ font-weight: 700;
600
+ padding: 0;
601
+ margin: 0px;
602
+ margin-bottom: 15px;
603
+ }
604
+
605
+ #tnp-heading h3 {
606
+ color: #27AE60;
607
+ font-family: soleil, sans-serif;
608
+ letter-spacing: 0.1rem;
609
+ font-size: .8rem;
610
+ line-height: 1.8rem;
611
+ text-transform: uppercase;
612
+ vertical-align: middle;
613
+ font-weight: 700;
614
+ padding: 0;
615
+ margin: 0px;
616
+ }
617
+
618
+ #tnp-heading p, #tnp-heading ul {
619
+ margin: 0px;
620
+ color: #ccc;
621
+ }
622
+
623
+ #tnp-heading ul {
624
+ list-style-type: circle;
625
+ list-style-position: inside;
626
+ margin-left: 2em;
627
+ margin-top: 1em;
628
+ }
629
+
630
+ /* Style for WP global notices */
631
+ #tnp-heading .notice p {
632
+ margin: 0.5em 0;
633
+ padding: 2px;
634
+ }
635
+
636
+ #tnp-heading .tnp-btn-h1 {
637
+ color: #fff;
638
+ background-color: #3498db;
639
+ border-radius: 3px;
640
+ padding: 6px 11px;
641
+ text-decoration: none;
642
+ text-transform: capitalize;
643
+ font-family: soleil, sans-serif;
644
+ margin-left: 10px;
645
+ font-size: 0.75rem;
646
+ font-weight: 300;
647
+ border: none;
648
+ }
649
+
650
+ #tnp-heading .tnp-btn-h1:hover {
651
+ color: #fff;
652
+ background-color: #5DADE2;
653
+ -webkit-transition: background-color .25s linear;
654
+ transition: background-color .25s linear;
655
+ -webkit-font-smoothing: subpixel-antialiased;
656
+ border: none;
657
+ color: #fff;
658
+ }
659
+
660
+ /* Dashboard Box */
661
+
662
+ .metabox-holder {
663
+ width: 100%;
664
+ }
665
+
666
+ .postbox {
667
+ border: none;
668
+ }
669
+
670
+ .postbox h3 a {
671
+ float: right;
672
+
673
+ }
674
+
675
+
676
+ #dashboard-widgets .postbox-container {
677
+ width: 33.333%
678
+ }
679
+
680
+ #tnp-body .postbox p {
681
+ color: #000;
682
+ }
683
+
684
+ #dashboard-widgets .postbox-container .postbox h3 {
685
+ font-family: soleil, sans-serif;
686
+ letter-spacing: 0.05rem;
687
+ background-color: #415b76;
688
+ color: #fff;
689
+ margin: 0;
690
+ padding: 9px;
691
+ }
692
+
693
+ #dashboard-widgets .postbox-container h3 a {
694
+ color: white;
695
+ text-decoration: none;
696
+ margin-left: 5px;
697
+ padding: 2px 8px;
698
+ background-color: #26C281;
699
+ border-radius: 2px;
700
+ font-weight: 300;
701
+ text-transform: capitalize;
702
+ font-size: 0.8rem;
703
+ }
704
+
705
+ #dashboard-widgets .postbox-container h3 a:hover {
706
+ color: white;
707
+ text-decoration: none;
708
+ margin-left: 5px;
709
+ background-color: #2ECC71;
710
+ }
711
+
712
+ .postbox-container i {
713
+ margin-right: 3px;
714
+ }
715
+ #tnp-dash-newsletters tr td:last-of-type {
716
+ width: 80px;
717
+ text-align: right;
718
+ }
719
+
720
+ #tnp-dash-subscribers tr td:last-of-type {
721
+ width: 80px;
722
+ text-align: right;
723
+ }
724
+
725
+ #tnp-dash-subscribers tr td:first-of-type {
726
+ width: 250px;
727
+ overflow: hidden;
728
+ }
729
+
730
+ #tnp-dash-subscribers table {
731
+ table-layout: fixed;
732
+ }
733
+
734
+ #tnp-dash-documentation .inside div {
735
+ margin-top: 10px;
736
+ }
737
+
738
+ #tnp-dash-documentation .inside a {
739
+ text-decoration: none;
740
+ color: #fff;
741
+ display: block;
742
+ font-family: soleil, sans-serif;
743
+ padding: 5px 10px;
744
+ }
745
+
746
+
747
+ /* Footer */
748
+
749
+ #tnp-footer {
750
+ display: flex;
751
+ justify-content: space-between;
752
+ align-items: flex-start;
753
+ margin-top: 10px;
754
+ padding: 10px 30px;
755
+ background-color: #28313C;
756
+ font-family: soleil, sans-serif;
757
+ }
758
+
759
+ #tnp-footer div {
760
+ /*width: 33%;*/
761
+ /*display: inline-block;*/
762
+ }
763
+
764
+ #tnp-footer a {
765
+ color: #fff;
766
+ text-decoration: none;
767
+ }
768
+
769
+ #tnp-footer a:hover {
770
+ color: #BDC3C7;
771
+ }
772
+
773
+ #tnp-footer input[type="submit"] {
774
+ background-color: #2ECC71;
775
+ border: none;
776
+ padding: 5px 10px;
777
+ color: #fff;
778
+ }
779
+
780
+ #tnp-footer form {
781
+ white-space: nowrap;
782
+ }
783
+
784
+ #tnp-footer li {
785
+ display: inline;
786
+ margin-left: 15px;
787
+ padding: 2px 5px;
788
+ border-left: 3px solid #2ECC71;
789
+ }
790
+
791
+ /* Global buttons styles */
792
+
793
+ #dashboard-widgets .button {
794
+ border: none;
795
+ background: none;
796
+ box-shadow: none;
797
+ color: #322C39;
798
+ }
799
+
800
+ #dashboard-widgets .button:hover {
801
+ background-color: #ECF0F1;
802
+ }
803
+
804
+ .wp-core-ui .button-secondary, .wp-core-ui .button-primary {
805
+ background-color: #3498db;
806
+ border: none;
807
+ box-shadow: none;
808
+ color: #fff;
809
+ font-family: soleil,sans-serif;
810
+ margin: 0px 2px;
811
+ width: auto;
812
+ }
813
+
814
+ .wp-core-ui .button-secondary:hover, .wp-core-ui .button-primary:hover {
815
+ background-color: #5DADE2;
816
+ color: #fff;
817
+ width: auto;
818
+ }
819
+
820
+ span.wp-media-buttons-icon:before {
821
+ color: #fff;
822
+ }
823
+
824
+ .tnp-paginator [value="Go"] {
825
+ background-color: #27AE60;
826
+ }
827
+
828
+ .tnp-paginator [value="Go"]:hover {
829
+ background-color: #2ECC71;
830
+ }
831
+
832
+ .notice-dismiss {
833
+ padding: 3px;
834
+ }
835
+
836
+ /*.widefat .button-secondary {
837
+ background: none;
838
+ color: #3498db;
839
+ }*/
840
+
841
+ /* Paginator */
842
+
843
+ .tnp-paginator {
844
+ color: #fff;
845
+ font-family: soleil,sans-serif;
846
+ margin: 10px 0px;
847
+ }
848
+
849
+ .tnp-paginator .button-secondary {
850
+ padding: 5px;
851
+ line-height: normal;
852
+ height: auto;
853
+ font-size: 12px;
854
+ height: 25px;
855
+ border: none;
856
+ border-radius: 3px;
857
+ vertical-align: baseline;
858
+ }
859
+
860
+ .tnp-paginator [value="Go"] {
861
+ background-color: #27AE60 !important;
862
+ }
863
+
864
+ .tnp-paginator [value="Go"]:hover {
865
+ background-color: #2ECC71 !important;
866
+ }
867
+
868
+ .tnp-paginator input {
869
+ background-color: #2C3E50;
870
+ border: none;
871
+ border-radius: 3px;
872
+ color: #fff;
873
+ padding: 5px;
874
+ line-height: normal;
875
+ font-size: 12px;
876
+ height: 25px;
877
+ }
878
+
879
+ /* Subscribers Search Box */
880
+
881
+ .tnp-subscribers-search {
882
+ color: #fff;
883
+ font-family: soleil, sans-serif;
884
+ background-color: #2C3E50;
885
+ padding: 20px;
886
+ border-radius: 5px;
887
+ margin-bottom: 20px;
888
+ display: inline-block;
889
+ }
890
+
891
+ .tnp-subscribers-search select {
892
+ margin-left: 5px;
893
+ padding: 0;
894
+ line-height: inherit;
895
+ }
896
+
897
+
898
+ /* Responsive Video Embeds */
899
+
900
+ .tnp-video-container {
901
+ position: relative;
902
+ padding-bottom: 56.25%;
903
+ padding-top: 30px; height: 0; overflow: hidden;
904
+ }
905
+
906
+ .tnp-video-container iframe,
907
+ .tnp-video-container object,
908
+ .tnp-video-container embed {
909
+ position: absolute;
910
+ top: 0;
911
+ left: 0;
912
+ width: 100%;
913
+ height: 100%;
914
+ }
915
+
916
+
917
+ /* Colors Palette */
918
+
919
+ .bg-white {
920
+ background-color: #FFF;
921
+ }
922
+
923
+ .orange {
924
+ background-color: #F39C12; /*Orange #F39C12 */
925
+ }
926
+
927
+ .blue {
928
+ background-color: #2980B9; /* Blue #2980B9 */
929
+ }
930
+
931
+ .purple {
932
+ background-color: #8E44AD; /* Purple #8E44AD */
933
+ }
934
+
935
+ .notice a {
936
+ color: #27AE60 !important;
937
+ text-decoration: underline!important;
938
+ }
939
+
940
+ .tnp-chart {
941
+ border: 1px solid #eee;
942
+ width: 100%;
943
+ }
944
+
945
+ /* Suggerimenti Oggetto + Inserimento Emoticons */
946
+
947
+ .tnp-emails-edit #options-subject {
948
+ font-size: 16px;
949
+ display: inline-block;
950
+ margin: 20px 0px;
951
+ width: auto;
952
+ border-radius: 4px;
953
+ padding: 5px 10px;
954
+ }
955
+
956
+ .tnp-suggest-button {
957
+ font-family: soleil, sans-serif;
958
+ margin-left: 8px;
959
+ border-radius: 3px;
960
+ background-color: #2980B9;
961
+ padding: 10px 15px 8px;
962
+ font-size: 14px;
963
+ color: #fff !important;
964
+ text-decoration: none;
965
+ }
966
+
967
+ .tnp-suggest-button:hover {
968
+ background-color: #3f8dbf;
969
+ }
970
+
971
+ .tnp-popup-overlay {
972
+ display: none;
973
+ position: fixed;
974
+ top: 0;
975
+ left: 0;
976
+ width: 100%;
977
+ height: 100%;
978
+ background-color: rgba(0, 0, 0, .8);
979
+ z-index: 10000;
980
+ }
981
+
982
+ .tnp-popup {
983
+ width: 40vw;
984
+ height: 66vh;
985
+ overflow: auto;
986
+ margin: 100px auto 0 auto;
987
+ background-color: #181818;
988
+ padding: 20px;
989
+ position: relative;
990
+ }
991
+ .tnp-popup-close {
992
+ display: block;
993
+ position: absolute;
994
+ top: 5px;
995
+ right: 5px;
996
+ background-color: #181818;
997
+ color: #fff;
998
+ font-size: 40px;
999
+ padding: 10px;
1000
+ text-align: right;
1001
+ cursor: pointer;
1002
+ }
1003
+
1004
+ .tnp-subjects-header {
1005
+ font-size: 16px;
1006
+ color: #fff;
1007
+ padding: 0px 70px 20px 20px;
1008
+ font-family: soleil, sans-serif;
1009
+ border-bottom: 1px solid #282828;
1010
+ }
1011
+
1012
+ #tnp-edit-subjects-list {
1013
+ padding: 0px 70px 20px 20px;
1014
+ }
1015
+
1016
+ #tnp-edit-subjects-list a {
1017
+ padding: 5px;
1018
+ }
1019
+
1020
+ #tnp-edit-subjects-list svg {
1021
+ margin: 0px 10px 0px 0px;
1022
+ vertical-align: middle;
1023
+ }
1024
+
1025
+ .tnp-subject-category {
1026
+ color: #565656;
1027
+ margin: 25px 0px 10px 0px;
1028
+ /* font-family: soleil; */
1029
+ font-size: 12px;
1030
+ text-transform: uppercase;
1031
+ letter-spacing: 0.1em;
1032
+ }
1033
+
1034
+
1035
+ /* Stile selettore liste - Schermata di invio newsletter */
1036
+
1037
+ .tnp-list-conditions p {
1038
+ margin: 0px 10px;
1039
+ }
1040
+
1041
+ /* Lists panel */
1042
+ .tnp-lists .tnp-notes {
1043
+ margin: 0;
1044
+ font-size: .9em;
1045
+ }
1046
+
1047
+ /* Codemirror editor with preview */
1048
+ iframe.tnp-editor-preview-mobile {
1049
+ box-sizing: border-box;
1050
+ background-color: #fff;
1051
+ border: 1px solid #bbb;
1052
+ box-shadow: 1px 1px 10px #777;
1053
+ border-radius: 10px;
1054
+ padding: 5px;
1055
+ width: 320px;
1056
+ height: 500px;
1057
+ float: left;
1058
+ }
1059
+
1060
+ iframe.tnp-editor-preview-desktop {
1061
+ box-sizing: border-box;
1062
+ background-color: #fff;
1063
+ border: 1px solid #bbb;
1064
+ border-radius: 10px;
1065
+ box-shadow: 1px 1px 10px #777;
1066
+ padding: 15px;
1067
+ width: 650px;
1068
+ margin-right: 20px;
1069
+ height: 500px;
1070
+ float: left;
1071
+ }
1072
+
1073
+
1074
+ /* Form inserimento licenza in Addons Manager */
1075
+
1076
+ #tnp-license-control {
1077
+ border-left: 5px solid #27ae60;
1078
+ display: inline-block;
1079
+ padding: 15px 20px;
1080
+ margin-left: -10px;
1081
+ margin-top: 15px;
1082
+ border-radius: 2px;
1083
+ background-color: #fff;
1084
+ }
1085
+
1086
+ #tnp-license-control form {
1087
+ margin-bottom: 10px;
1088
+ margin-top: 10px;
1089
+ }
1090
+
1091
+ #tnp-license-control form input {
1092
+ padding-left: 10px;
1093
+ }
1094
+
1095
+ #tnp-license-control a {
1096
+ border-bottom: none;
1097
+ color: #27AE60;
1098
+ }
1099
+
1100
+
1101
+ /* Status Box Style */
1102
+
1103
+ #tnp-nl-status {
1104
+ width: 100%;
1105
+ background: #fffafa;
1106
+ padding: 15px 25px 15px 25px;
1107
+ border-left: 10px solid #27AE60;
1108
+ border-radius: 0px 5px 5px 0px;
1109
+ }
1110
+
1111
+ #tnp-nl-status p {
1112
+ font-size: 17px;
1113
+ }
1114
+
1115
+ .tnp-nl-status-row {
1116
+ margin: 10px 0;
1117
+ }
1118
+
1119
+ .tnp-nl-status-title {
1120
+ font-size: 26px;
1121
+ line-height: 32px;
1122
+ margin: 5px 0px 0px 0px;
1123
+ color: #3498DB;
1124
+ font-weight: 900;
1125
+ vertical-align: middle;
1126
+ }
1127
+
1128
+ .tnp-nl-status-title-value {
1129
+ font-size: 13px;
1130
+ line-height: 32px;
1131
+ margin: 0px 0px 0px 5px;
1132
+ color: #fff;
1133
+ background-color: #95A5A6;
1134
+ border-radius: 4px;
1135
+ text-transform: uppercase;
1136
+ letter-spacing: 1px;
1137
+ vertical-align: sub;
1138
+ }
1139
+
1140
+ .tnp-nl-status-schedule-targeting {
1141
+ font-size: 15px;
1142
+ color: #34495E;
1143
+ }
1144
+
1145
+ .tnp-nl-status-schedule-value {
1146
+ font-size: 15px;
1147
+ color: #34495E;
1148
+ }
1149
+
1150
+ .tnp-status-header #options-subject {
1151
+ width: calc(100% - 150px);
1152
+ }
1153
+
1154
+ /* Grid Helpers */
1155
+
1156
+ .tnp-one-third {
1157
+ width: 40%;
1158
+ display: inline-block;
1159
+ vertical-align: top;
1160
+ }
1161
+
1162
+ .tnp-two-thirds {
1163
+ width: 59%;
1164
+ display: inline-block;
1165
+ vertical-align: top;
1166
+ }
1167
+
1168
+ /* Progress bar */
1169
+ .tnp-progress {
1170
+ display: flex;
1171
+ height: 1.5rem;
1172
+ overflow: hidden;
1173
+ font-size: .75rem;
1174
+ background-color: #c9cccf;
1175
+ border-radius: .25rem;
1176
+ margin: 0px 0px 0px;
1177
+ min-width: 100px;
1178
+ }
1179
+
1180
+ .tnp-progress-bar {
1181
+ display: -ms-flexbox;
1182
+ display: flex;
1183
+ -ms-flex-direction: column;
1184
+ flex-direction: column;
1185
+ -ms-flex-pack: center;
1186
+ justify-content: center;
1187
+ color: #fff;
1188
+ text-align: center;
1189
+ white-space: nowrap;
1190
+ background-color: #007bff;
1191
+ transition: width .6s ease;
1192
+ }
1193
+
1194
+ .tnp-progress--sent .tnp-progress-bar {
1195
+ background-color: green;
1196
+ }
1197
+
1198
+ .tnp-progress--error .tnp-progress-bar {
1199
+ background-color: #E74C3C;
1200
+ }
1201
+
1202
+ .tnp-progress-numbers {
1203
+ text-align: center;
1204
+ color: #666;
1205
+ }
1206
+
1207
+ .tnp-progress-date {
1208
+ color: #666;
1209
+ font-style: italic;
1210
+ }
1211
+
1212
+ span.tnp-email-status {
1213
+ background-color: #95A5A6;
1214
+ padding: 2px 10px;
1215
+ border-radius: 4px;
1216
+ color: #fff;
1217
+ white-space: nowrap;
1218
+ }
1219
+
1220
+ /* Email status label */
1221
+ span.tnp-email-status.tnp-email-status--new {
1222
+ background-color: #8E44AD;
1223
+ }
1224
+
1225
+ span.tnp-email-status.tnp-email-status--paused {
1226
+ background-color: #95A5A6;
1227
+ }
1228
+
1229
+ span.tnp-email-status.tnp-email-status--sending {
1230
+ background-color: #27AE60;
1231
+ }
1232
+
1233
+ span.tnp-email-status.tnp-email-status--scheduled {
1234
+ background-color: #E67E22;
1235
+ }
1236
+
1237
+ span.tnp-email-status.tnp-email-status--sent {
1238
+ background-color: #95A5A6;
1239
+ }
1240
+
1241
+ span.tnp-email-status.tnp-email-status--error {
1242
+ background-color: #E74C3C;
1243
+ }
1244
+
1245
+ /* Schedule buttons styles */
1246
+
1247
+ #tnp-schedule-button {
1248
+ background-color: #E67E22 !important;
1249
+ }
1250
+
1251
+ #tnp-schedule-button:hover {
1252
+ background-color: #ec913f !important;
1253
+ }
1254
+
1255
+ .tnp-button-cancel {
1256
+ background-color: #E74C3C !important;
1257
+ }
1258
+
1259
+ /* Newsletter preview on targeting panel */
1260
+ .tnpc-preview {
1261
+ margin-top: 10px;
1262
+ }
1263
+
1264
+ .tnpc-preview .fake-browser-ui iframe {
1265
+ width: 700px;
1266
+ }
1267
+
1268
+ .tnpc-preview .fake-mobile-browser-ui iframe {
1269
+ width: 320px;
1270
+ }
1271
+
1272
+ .fake-browser-ui {
1273
+ padding: 30px 0 0;
1274
+ border-radius: 3px;
1275
+ border-bottom: 10px solid #ccc;
1276
+ background: #ddd;
1277
+ display: inline-block;
1278
+ position: relative;
1279
+ line-height: 0;
1280
+ vertical-align: top;
1281
+ margin-left: 20px;
1282
+ }
1283
+
1284
+ .fake-mobile-browser-ui {
1285
+ padding: 30px 10px 37px;
1286
+ border-radius: 10px;
1287
+ border-bottom: 10px solid #ccc;
1288
+ background: #ddd;
1289
+ display: inline-block;
1290
+ position: relative;
1291
+ line-height: 0;
1292
+ margin-left: 30px;
1293
+ }
1294
+
1295
+ .fake-browser-ui .frame {
1296
+ display: block;
1297
+ height: 25px;
1298
+ position: absolute;
1299
+ top: 12px;
1300
+ left: 8px;
1301
+ }
1302
+
1303
+ .fake-mobile-browser-ui .frame {
1304
+ display: block;
1305
+ height: 25px;
1306
+ margin-top: 10px;
1307
+ }
1308
+
1309
+ .fake-browser-ui span {
1310
+ height: 12px;
1311
+ width: 12px;
1312
+ border-radius: 8px;
1313
+ background-color: #eee;
1314
+ border: 1px solid #dadada;
1315
+ float: left;
1316
+ margin: 0 0 0 4px;
1317
+ }
1318
+
1319
+ .fake-mobile-browser-ui span {
1320
+ height: 50px;
1321
+ width: 50px;
1322
+ border-radius: 60px;
1323
+ background-color: #eee;
1324
+ border: 2px solid #ccc;
1325
+ display: block;
1326
+ margin: auto;
1327
+ }
1328
+
1329
+ .fake-browser-ui .bt-1 {
1330
+ background-color: #ED594A;
1331
+ }
1332
+
1333
+ .fake-browser-ui .bt-2 {
1334
+ background-color: #FDD800;
1335
+ }
1336
+
1337
+ .fake-browser-ui .bt-3 {
1338
+ background-color: #5AC05A;
1339
+ }
1340
+
1341
+ /* Addons page */
1342
+
1343
+ #tnp-promo {
1344
+ text-align: left;
1345
+ background-color: #222b36;
1346
+ margin: 20px;
1347
+ border-radius: 5px;
1348
+ padding: 20px 40px;
1349
+ }
1350
+ #tnp-promo .tnp-promo-how-to {
1351
+ width: 50%;
1352
+ padding: 5px 20px;
1353
+ margin-top: 30px;
1354
+ margin-bottom: 30px;
1355
+ border-left: 2px solid #F1C40F;
1356
+ }
1357
+
1358
+ #tnp-promo .tnp-promo-how-to h3 {
1359
+ color: #ECF0F1;
1360
+ margin: 0px;
1361
+ line-height: 36px;
1362
+ }
1363
+
1364
+ #tnp-promo .tnp-promo-how-to p {
1365
+ color: #ECF0F1;
1366
+ margin: 0px;
1367
+ font-size: 16px;
1368
+ line-height: 26px;
1369
+ }
1370
+
1371
+ #tnp-promo .tnp-promo-buttons {
1372
+ margin: 50px 0px;
1373
+ }
1374
+
1375
+ #tnp-promo .tnp-promo-button {
1376
+ background: #27AE60;
1377
+ text-decoration: none;
1378
+ color: white;
1379
+ padding: 15px 20px;
1380
+ font-size: 15px;
1381
+ border-radius: 2px;
1382
+ }
1383
+
1384
+ #tnp-promo .tnp-promo-button:hover {
1385
+ background: #2ECC71;
1386
+ color: white;
1387
+ }
1388
+
1389
+ #tnp-promo .tnp-promo-button i {
1390
+ margin-right: 3px;
1391
+ }
1392
+
1393
+ #tnp-body td a.tnp-table-link,
1394
+ #tnp-body td a.tnp-table-link:visited {
1395
+ color: #444;
1396
+ }
1397
+
1398
+ #tnp-body td a.tnp-table-link:hover {
1399
+ color: #3498DB;
1400
+ }
1401
+
1402
+ .text-left {
1403
+ text-align: left;
1404
+ }
1405
+
1406
+ .tab-min-height {
1407
+ min-height: 500px;
1408
+ }
1409
+
1410
+ .tnp-control-all-languages-notice {
1411
+ padding: 15px;
1412
+ border: 1px dashed #999;
1413
+ }
1414
+
1415
+ /* Spectru, color picker */
1416
+
1417
+ /* Down arrow */
1418
+ .sp-dd {
1419
+ display: none;
1420
+ }
1421
+
1422
+ .sp-replacer {
1423
+ width: 30px!important;
1424
+ height: 30px!important;
1425
+ }
1426
+
1427
+
1428
+ /* Subscriber status labels */
1429
+
1430
+ #tnp-body .unsubscribed {
1431
+ color: gray;
1432
+ }
1433
+
1434
+ #tnp-body .confirmed {
1435
+ color: darkgreen;
1436
+ }
1437
+
1438
+ #tnp-body .not-confirmed {
1439
+ color: darkgray;
1440
+ }
1441
+
1442
+ #tnp-body .complained {
1443
+ color: red;
1444
+ }
1445
+
1446
+ #tnp-body .bounced {
1447
+ color: darkorange;
1448
+ }
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) && current_user_can('administrator')) {
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>', 'manage_options', '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>', 'manage_options', '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/composer.php CHANGED
@@ -1,972 +1,977 @@
1
- <?php
2
-
3
- /** For old style coders */
4
- function tnp_register_block($dir) {
5
- return TNP_Composer::register_block($dir);
6
- }
7
-
8
- /**
9
- * Generates and HTML button for email using the values found on $options and
10
- * prefixed by $prefix, with the standard syntax of NewsletterFields::button().
11
- *
12
- * @param array $options
13
- * @param string $prefix
14
- * @return string
15
- */
16
- function tnpc_button($options, $prefix = 'button') {
17
- return TNP_Composer::button($options, $prefix);
18
- }
19
-
20
- class TNP_Composer {
21
-
22
- static $block_dirs = array();
23
-
24
- static function register_block($dir) {
25
- // Checks
26
- $dir = realpath($dir);
27
- if (!$dir) {
28
- $error = new WP_Error('1', 'Seems not a valid path: ' . $dir);
29
- NewsletterEmails::instance()->logger->error($error);
30
- return $error;
31
- }
32
-
33
- $dir = wp_normalize_path($dir);
34
-
35
- if (!file_exists($dir . '/block.php')) {
36
- $error = new WP_Error('1', 'block.php missing on folder ' . $dir);
37
- NewsletterEmails::instance()->logger->error($error);
38
- return $error;
39
- }
40
-
41
- self::$block_dirs[] = $dir;
42
- return true;
43
- }
44
-
45
- /**
46
- * @param string $open
47
- * @param string $inner
48
- * @param string $close
49
- * @param string[] $markers
50
- *
51
- * @return string
52
- */
53
- static function wrap_html_element($open, $inner, $close, $markers = array('<!-- tnp -->', '<!-- /tnp -->')) {
54
-
55
- return $open . $markers[0] . $inner . $markers[1] . $close;
56
- }
57
-
58
- /**
59
- * @param string $block
60
- * @param string[] $markers
61
- *
62
- * @return string
63
- */
64
- static function unwrap_html_element($block, $markers = array('<!-- tnp -->', '<!-- /tnp -->')) {
65
- if (self::_has_markers($block, $markers)) {
66
- self::_escape_markers($markers);
67
- $pattern = sprintf('/%s(.*?)%s/s', $markers[0], $markers[1]);
68
-
69
- $matches = array();
70
- preg_match($pattern, $block, $matches);
71
-
72
- return $matches[1];
73
- }
74
-
75
- return $block;
76
- }
77
-
78
- /**
79
- * @param string $block
80
- * @param string[] $markers
81
- *
82
- * @return bool
83
- */
84
- private static function _has_markers($block, $markers = array('<!-- tnp -->', '<!-- /tnp -->')) {
85
-
86
- self::_escape_markers($markers);
87
-
88
- $pattern = sprintf('/%s(.*?)%s/s', $markers[0], $markers[1]);
89
-
90
- return preg_match($pattern, $block);
91
- }
92
-
93
- /**
94
- * Sources:
95
- * - https://webdesign.tutsplus.com/tutorials/creating-a-future-proof-responsive-email-without-media-queries--cms-23919
96
- *
97
- * @param type $email
98
- * @return type
99
- */
100
- static function get_html_open($email) {
101
- $open = "<!DOCTYPE html>\n";
102
- $open .= "<html xmlns=\"https://www.w3.org/1999/xhtml\" xmlns:o=\"urn:schemas-microsoft-com:office:office\">\n<head>\n<title>{email_subject}</title>\n";
103
- $open .= "<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n";
104
-
105
- $open .= '<!--[if !mso]><!-->' . "\n";
106
- $open .= '<meta http-equiv="X-UA-Compatible" content="IE=edge" />' . "\n";
107
- $open .= '<!--<![endif]-->' . "\n";
108
-
109
- $open .= '<!--[if mso]>' . "\n";
110
- ;
111
- $open .= '<style type="text/css">';
112
- $open .= 'table {border-collapse:collapse;border-spacing:0;margin:0;}';
113
- $open .= 'div, td {padding:0;}';
114
- $open .= 'div {margin:0 !important;}';
115
- $open .= '</style>';
116
- $open .= "\n";
117
- $open .= '<noscript>';
118
- $open .= '<xml>';
119
- $open .= '<o:OfficeDocumentSettings>';
120
- $open .= '<o:PixelsPerInch>96</o:PixelsPerInch>';
121
- $open .= '</o:OfficeDocumentSettings>';
122
- $open .= '</xml>';
123
- $open .= '</noscript>';
124
- $open .= "\n";
125
- $open .= '<![endif]-->';
126
- $open .= "\n";
127
- $open .= "<style type=\"text/css\">\n";
128
- $open .= NewsletterEmails::instance()->get_composer_css();
129
- $open .= "\n</style>\n";
130
- $open .= "</head>\n";
131
- $open .= '<body style="margin: 0; padding: 0; line-height: normal; word-spacing: normal;" dir="' . (is_rtl() ? 'rtl' : 'ltr') . '">';
132
- $open .= "\n";
133
- $open .= self::get_html_preheader($email);
134
-
135
- return $open;
136
- }
137
-
138
- static private function get_html_preheader($email) {
139
-
140
- if (empty($email->options['preheader'])) {
141
- return "";
142
- }
143
-
144
- $preheader_text = esc_html($email->options['preheader']);
145
- $html = "<div style=\"display:none;font-size:1px;color:#ffffff;line-height:1px;max-height:0px;max-width:0px;opacity:0;overflow:hidden;\">$preheader_text</div>";
146
- $html .= "\n";
147
-
148
- return $html;
149
- }
150
-
151
- static function get_html_close($email) {
152
- return "</body>\n</html>";
153
- }
154
-
155
- /**
156
- *
157
- * @param TNP_Email $email
158
- * @return string
159
- */
160
- static function get_main_wrapper_open($email) {
161
- if (!isset($email->options['composer_background']) || $email->options['composer_background'] == 'inherit') {
162
- $bgcolor = '';
163
- } else {
164
- $bgcolor = $email->options['composer_background'];
165
- }
166
-
167
- return "\n<table cellpadding='0' cellspacing='0' border='0' width='100%'>\n" .
168
- "<tr>\n" .
169
- "<td bgcolor='$bgcolor' valign='top'><!-- tnp -->";
170
- }
171
-
172
- /**
173
- *
174
- * @param TNP_Email $email
175
- * @return string
176
- */
177
- static function get_main_wrapper_close($email) {
178
- return "\n<!-- /tnp -->\n" .
179
- "</td>\n" .
180
- "</tr>\n" .
181
- "</table>\n\n";
182
- }
183
-
184
- /**
185
- * Remove <doctype>, <body> and unnecessary envelopes for editing with composer
186
- *
187
- * @param string $html_email
188
- *
189
- * @return string
190
- */
191
- static function unwrap_email($html_email) {
192
-
193
- if (self::_has_markers($html_email)) {
194
- $html_email = self::unwrap_html_element($html_email);
195
- } else {
196
- //KEEP FOR OLD EMAIL COMPATIBILITY
197
- // Extracts only the body part
198
- $x = strpos($html_email, '<body');
199
- if ($x) {
200
- $x = strpos($html_email, '>', $x);
201
- $y = strpos($html_email, '</body>');
202
- $html_email = substr($html_email, $x + 1, $y - $x - 1);
203
- }
204
-
205
- /* Cleans up uncorrectly stored newsletter bodies */
206
- $html_email = preg_replace('/<style\s+.*?>.*?<\\/style>/is', '', $html_email);
207
- $html_email = preg_replace('/<meta.*?>/', '', $html_email);
208
- $html_email = preg_replace('/<title\s+.*?>.*?<\\/title>/i', '', $html_email);
209
- $html_email = trim($html_email);
210
- }
211
-
212
- // Required since esc_html DOES NOT escape the HTML entities (apparently)
213
- $html_email = str_replace('&', '&amp;', $html_email);
214
- $html_email = str_replace('"', '&quot;', $html_email);
215
- $html_email = str_replace('<', '&lt;', $html_email);
216
- $html_email = str_replace('>', '&gt;', $html_email);
217
-
218
- return $html_email;
219
- }
220
-
221
- private static function _escape_markers(&$markers) {
222
- $markers[0] = str_replace('/', '\/', $markers[0]);
223
- $markers[1] = str_replace('/', '\/', $markers[1]);
224
- }
225
-
226
- /**
227
- * Using the data collected inside $controls (and submitted by a form containing the
228
- * composer fields), updates the email. The message body is completed with doctype,
229
- * head, style and the main wrapper.
230
- *
231
- * @param TNP_Email $email
232
- * @param NewsletterControls $controls
233
- */
234
- static function update_email($email, $controls) {
235
- if (isset($controls->data['subject'])) {
236
- $email->subject = $controls->data['subject'];
237
- }
238
-
239
- // They should be only composer options
240
- foreach ($controls->data as $name => $value) {
241
- if (strpos($name, 'options_') === 0) {
242
- $email->options[substr($name, 8)] = $value;
243
- }
244
- }
245
-
246
- //if (isset($controls->data['preheader'])) {
247
- // $email->options['preheader'] = $controls->data['preheader'];
248
- //}
249
-
250
- $email->editor = NewsletterEmails::EDITOR_COMPOSER;
251
-
252
- $email->message = self::get_html_open($email) . self::get_main_wrapper_open($email) .
253
- $controls->data['message'] . self::get_main_wrapper_close($email) . self::get_html_close($email);
254
- }
255
-
256
- /**
257
- * Prepares a controls object injecting the relevant fields from an email
258
- * which cannot be directly used by controls. If $email is null or missing,
259
- * $controls is prepared with default values.
260
- *
261
- * @param NewsletterControls $controls
262
- * @param TNP_Email $email
263
- */
264
- static function prepare_controls($controls, $email = null) {
265
-
266
- // Controls for a new email (which actually does not exist yet
267
- if (!empty($email)) {
268
-
269
- foreach ($email->options as $name => $value) {
270
- $controls->data['options_' . $name] = $value;
271
- }
272
-
273
- $controls->data['message'] = TNP_Composer::unwrap_email($email->message);
274
- $controls->data['subject'] = $email->subject;
275
- $controls->data['updated'] = $email->updated;
276
- }
277
-
278
- if (!empty($email->options['sender_email'])) {
279
- $controls->data['sender_email'] = $email->options['sender_email'];
280
- } else {
281
- $controls->data['sender_email'] = Newsletter::instance()->options['sender_email'];
282
- }
283
-
284
- if (!empty($email->options['sender_name'])) {
285
- $controls->data['sender_name'] = $email->options['sender_name'];
286
- } else {
287
- $controls->data['sender_name'] = Newsletter::instance()->options['sender_name'];
288
- }
289
-
290
- $controls->data = array_merge(TNP_Composer::get_global_style_defaults(), $controls->data);
291
- }
292
-
293
- /**
294
- * Extract inline edited post field from inline_edit_list[]
295
- *
296
- * @param array $inline_edit_list
297
- * @param string $field_type
298
- * @param int $post_id
299
- *
300
- * @return string
301
- */
302
- static function get_edited_inline_post_field($inline_edit_list, $field_type, $post_id) {
303
-
304
- foreach ($inline_edit_list as $edit) {
305
- if ($edit['type'] == $field_type && $edit['post_id'] == $post_id) {
306
- return $edit['content'];
307
- }
308
- }
309
-
310
- return '';
311
- }
312
-
313
- /**
314
- * Check if inline_edit_list[] have inline edit field for specific post
315
- *
316
- * @param array $inline_edit_list
317
- * @param string $field_type
318
- * @param int $post_id
319
- *
320
- * @return bool
321
- */
322
- static function is_post_field_edited_inline($inline_edit_list, $field_type, $post_id) {
323
- if (empty($inline_edit_list) || !is_array($inline_edit_list)) {
324
- return false;
325
- }
326
- foreach ($inline_edit_list as $edit) {
327
- if ($edit['type'] == $field_type && $edit['post_id'] == $post_id) {
328
- return true;
329
- }
330
- }
331
-
332
- return false;
333
- }
334
-
335
- /**
336
- * Creates the HTML for a button extrating from the options, with the provided prefix, the button attributes:
337
- *
338
- * - [prefix]_url The button URL
339
- * - [prefix]_font_family
340
- * - [prefix]_font_size
341
- * - [prefix]_font_weight
342
- * - [prefix]_label
343
- * - [prefix]_font_color The label color
344
- * - [prefix]_background The button color
345
- *
346
- * TODO: Add radius and possiblt the alignment
347
- *
348
- * @param array $options
349
- * @param string $prefix
350
- * @return string
351
- */
352
- static function button($options, $prefix = 'button') {
353
-
354
- if (empty($options[$prefix . '_label'])) {
355
- return;
356
- }
357
- $defaults = [
358
- $prefix . '_url' => '#',
359
- $prefix . '_font_family' => 'Helvetica, Arial, sans-serif',
360
- $prefix . '_font_color' => '#ffffff',
361
- $prefix . '_font_weight' => 'bold',
362
- $prefix . '_font_size' => 20,
363
- $prefix . '_background' => '#256F9C',
364
- $prefix . '_align' => 'center'
365
- ];
366
-
367
- $options = array_merge($defaults, array_filter($options));
368
-
369
- $b = '<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="margin: 0 auto"';
370
- if (!empty($options[$prefix . '_align'])) {
371
- $b .= ' align="' . esc_attr($options[$prefix . '_align']) . '"';
372
- }
373
- if (!empty($options[$prefix . '_width'])) {
374
- $b .= ' width="' . esc_attr($options[$prefix . '_width']) . '"';
375
- }
376
- $b .= '>';
377
- $b .= '<tr>';
378
- $b .= '<td align="center" bgcolor="' . $options[$prefix . '_background'] . '" role="presentation" style="border:none;border-radius:3px;cursor:auto;mso-padding-alt:10px 25px;background:' . $options[$prefix . '_background'] . '" valign="middle">';
379
- $b .= '<a href="' . $options[$prefix . '_url'] . '"';
380
- $b .= ' style="display:inline-block;background:' . $options[$prefix . '_background'] . ';color:' . $options[$prefix . '_font_color'] . ';font-family:' . $options[$prefix . '_font_family'] . ';font-size:' . $options[$prefix . '_font_size'] . 'px;font-weight:' . $options[$prefix . '_font_weight'] . ';line-height:120%;margin:0;text-decoration:none;text-transform:none;padding:10px 25px;mso-padding-alt:0px;border-radius:3px;"';
381
- $b .= ' target="_blank">';
382
- $b .= $options[$prefix . '_label'];
383
- $b .= '</a>';
384
- $b .= '</td></tr></table>';
385
- return $b;
386
- }
387
-
388
- /**
389
- * Generates an IMG tag, linked if the media has an URL.
390
- *
391
- * @param TNP_Media $media
392
- * @param string $style
393
- * @return string
394
- */
395
- static function image($media, $attr = []) {
396
-
397
- $default_attrs = [
398
- 'style' => 'max-width: 100%; height: auto; display: inline-block',
399
- 'class' => '',
400
- 'link-style' => 'text-decoration: none; display: inline-block',
401
- 'link-class' => null,
402
- ];
403
-
404
- $attr = array_merge($default_attrs, $attr);
405
-
406
- //Class and style attribute are mutually exclusive.
407
- //Class take priority to style because classes will transform to inline style inside block rendering operation
408
- if (!empty($attr['inline-class'])) {
409
- $styling = ' inline-class="' . esc_attr($attr['inline-class']) . '" ';
410
- } else {
411
- $styling = ' style="' . esc_attr($attr['style']) . '" ';
412
- }
413
-
414
- if (!empty($attr['class'])) {
415
- $styling .= ' class="' . esc_attr($attr['class']) . '" ';
416
- }
417
-
418
- //Class and style attribute are mutually exclusive.
419
- //Class take priority to style because classes will transform to inline style inside block rendering operation
420
- if (!empty($attr['link-class'])) {
421
- $link_styling = ' inline-class="' . esc_attr($attr['link-class']) . '" ';
422
- } else {
423
- $link_styling = ' style="' . esc_attr($attr['link-style']) . '" ';
424
- }
425
-
426
- $b = '';
427
- if ($media->link) {
428
- $b .= '<a href="' . esc_attr($media->link) . '" target="_blank" rel="noopener nofollow" style="display: inline-block; font-size: 0; text-decoration: none; line-height: normal!important">';
429
- } else {
430
- // The span grants images are not upscaled when fluid (two columns posts block)
431
- $b .= '<span style="display: inline-block; font-size: 0; text-decoration: none; line-height: normal!important">';
432
- }
433
- if ($media) {
434
- $b .= '<img src="' . esc_attr($media->url) . '" width="' . esc_attr($media->width) . '"';
435
- if ($media->height) {
436
- $b .= ' height="' . esc_attr($media->height) . '"';
437
- }
438
- $b .= ' alt="' . esc_attr($media->alt) . '"'
439
- . ' border="0"'
440
- . ' style="display: inline-block; max-width: 100%!important; padding: 0; border: 0; font-size: 12px"'
441
- . ' class="' . esc_attr($attr['class']) . '" '
442
- . '>';
443
- }
444
-
445
- if ($media->link) {
446
- $b .= '</a>';
447
- } else {
448
- $b .= '</span>';
449
- }
450
-
451
- return $b;
452
- }
453
-
454
- /**
455
- * Returns a WP media ID for the specified post (or false if nothing can be found)
456
- * looking for the featured image or, if missing, taking the first media in the gallery and
457
- * if again missing, searching the first reference to a media in the post content.
458
- *
459
- * The media ID is not checked for real existance of the associated attachment.
460
- *
461
- * @param int $post_id
462
- * @return int
463
- */
464
- static function get_post_thumbnail_id($post_id) {
465
- if (is_object($post_id)) {
466
- $post_id = $post_id->ID;
467
- }
468
-
469
- // Find a media id to be used as featured image
470
- $media_id = get_post_thumbnail_id($post_id);
471
- if (!empty($media_id)) {
472
- return $media_id;
473
- }
474
-
475
- $attachments = get_children(array('numberpost' => 1, 'post_parent' => $post_id, 'post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => 'image', 'order' => 'ASC', 'orderby' => 'menu_order'));
476
- if (!empty($attachments)) {
477
- foreach ($attachments as $id => &$attachment) {
478
- return $id;
479
- }
480
- }
481
-
482
- $post = get_post($post_id);
483
-
484
- $r = preg_match('/wp-image-(\d+)/', $post->post_content, $matches);
485
- if ($matches) {
486
- return (int) $matches[1];
487
- }
488
-
489
- return false;
490
- }
491
-
492
- /**
493
- * Builds a TNP_Media object to be used in newsletters from a WP media/attachement ID. The returned
494
- * media has a size which best match the one requested (this is the standard WP behavior, plugins
495
- * could change it).
496
- *
497
- * @param int $media_id
498
- * @param array $size
499
- * @return \TNP_Media
500
- */
501
- function get_media($media_id, $size) {
502
- $src = wp_get_attachment_image_src($media_id, $size);
503
- if (!$src) {
504
- return null;
505
- }
506
- $media = new TNP_Media();
507
- $media->id = $media_id;
508
- $media->url = $src[0];
509
- $media->width = $src[1];
510
- $media->height = $src[2];
511
- return $media;
512
- }
513
-
514
- static function post_content($post) {
515
- $content = $post->post_content;
516
- $content = wpautop($content);
517
- if (true || $options['enable shortcodes']) {
518
- remove_shortcode('gallery');
519
- add_shortcode('gallery', 'tnp_gallery_shortcode');
520
- $content = do_shortcode($content);
521
- }
522
- $content = str_replace('<p>', '<p class="paragraph">', $content);
523
-
524
- $selected_images = array();
525
- if (preg_match_all('/<img [^>]+>/', $content, $matches)) {
526
- foreach ($matches[0] as $image) {
527
- if (preg_match('/wp-image-([0-9]+)/i', $image, $class_id) && ( $attachment_id = absint($class_id[1]) )) {
528
- $selected_images[$image] = $attachment_id;
529
- }
530
- }
531
- }
532
-
533
- foreach ($selected_images as $image => $attachment_id) {
534
- $src = tnp_media_resize($attachment_id, array(600, 0));
535
- if (is_wp_error($src)) {
536
- continue;
537
- }
538
- $content = str_replace($image, '<img src="' . $src . '" width="600" style="max-width: 100%">', $content);
539
- }
540
-
541
- return $content;
542
- }
543
-
544
- static function get_global_style_defaults() {
545
- return [
546
- 'options_composer_title_font_family' => 'Verdana, Geneva, sans-serif',
547
- 'options_composer_title_font_size' => 32,
548
- 'options_composer_title_font_weight' => 'normal',
549
- 'options_composer_title_font_color' => '#222222',
550
- 'options_composer_text_font_family' => 'Verdana, Geneva, sans-serif',
551
- 'options_composer_text_font_size' => 16,
552
- 'options_composer_text_font_weight' => 'normal',
553
- 'options_composer_text_font_color' => '#222222',
554
- 'options_composer_button_font_family' => 'Verdana, Geneva, sans-serif',
555
- 'options_composer_button_font_size' => 16,
556
- 'options_composer_button_font_weight' => 'normal',
557
- 'options_composer_button_font_color' => '#FFFFFF',
558
- 'options_composer_button_background_color' => '#256F9C',
559
- 'options_composer_background' => '#FFFFFF',
560
- 'options_composer_block_background' => '#FFFFFF',
561
- ];
562
- }
563
-
564
- /**
565
- * Inspired by: https://webdesign.tutsplus.com/tutorials/creating-a-future-proof-responsive-email-without-media-queries--cms-23919
566
- *
567
- * Attributes:
568
- * - columns: number of columns [2]
569
- * - padding: cells padding [10]
570
- * - responsive: il on mobile the cell should stack up [true]
571
- * - width: the whole row width, it should reduced by the external row padding [600]
572
- *
573
- * @param string[] $items
574
- * @param array $attrs
575
- * @return string
576
- */
577
- static function grid($items = [], $attrs = []) {
578
- $attrs = wp_parse_args($attrs, ['width' => 600, 'columns' => 2, 'padding' => 10, 'responsive' => true]);
579
- $width = (int) $attrs['width'];
580
- $columns = (int) $attrs['columns'];
581
- $padding = (int) $attrs['padding'];
582
- $column_width = $width / $columns;
583
- $td_width = 100 / $columns;
584
- $chunks = array_chunk($items, $columns);
585
-
586
- if ($attrs['responsive']) {
587
-
588
- $e = '';
589
- foreach ($chunks as &$chunk) {
590
- $e .= '<div style="text-align:center;font-size:0;">';
591
- $e .= '<!--[if mso]><table role="presentation" width="100%"><tr><![endif]-->';
592
- foreach ($chunk as &$item) {
593
- $e .= '<!--[if mso]><td width="' . $td_width . '%" style="width:' . $td_width . '%;padding:' . $padding . 'px" valign="top"><![endif]-->';
594
-
595
- $e .= '<div class="max-width-100" style="width:100%;max-width:' . $column_width . 'px;display:inline-block;vertical-align: top;box-sizing: border-box;">';
596
-
597
- // This element to add padding without deal with border-box not well supported
598
- $e .= '<div style="padding:' . $padding . 'px;">';
599
- $e .= $item;
600
- $e .= '</div>';
601
- $e .= '</div>';
602
-
603
- $e .= '<!--[if mso]></td><![endif]-->';
604
- }
605
- $e .= '<!--[if mso]></tr></table><![endif]-->';
606
- $e .= '</div>';
607
- }
608
-
609
- return $e;
610
- } else {
611
- $e = '<table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0" style="width: 100%; max-width: 100%!important">';
612
- foreach ($chunks as &$chunk) {
613
- $e .= '<tr>';
614
- foreach ($chunk as &$item) {
615
- $e .= '<td width="' . $td_width . '%" style="width:' . $td_width . '%; padding:' . $padding . 'px" valign="top">';
616
- $e .= $item;
617
- $e .= '</td>';
618
- }
619
- $e .= '</tr>';
620
- }
621
- $e .= '</table>';
622
- return $e;
623
- }
624
- }
625
-
626
- static function get_text_style($options, $prefix, $composer, $attrs = []) {
627
- return self::get_style($options, $prefix, $composer, 'text', $attrs);
628
- }
629
-
630
- static function get_title_style($options, $prefix, $composer, $attrs = []) {
631
- return self::get_style($options, $prefix, $composer, 'title', $attrs);
632
- }
633
-
634
- static function get_style($options, $prefix, $composer, $type = 'text', $attrs = []) {
635
- $style = new TNP_Style();
636
- $scale = 1.0;
637
- if (!empty($attrs['scale'])) {
638
- $scale = (float) $attrs['scale'];
639
- }
640
- if (!empty($prefix))
641
- $prefix .= '_';
642
-
643
- $style->font_family = empty($options[$prefix . 'font_family']) ? $composer[$type . '_font_family'] : $options[$prefix . 'font_family'];
644
- $style->font_size = empty($options[$prefix . 'font_size']) ? round($composer[$type . '_font_size'] * $scale) : $options[$prefix . 'font_size'];
645
- $style->font_color = empty($options[$prefix . 'font_color']) ? $composer[$type . '_font_color'] : $options[$prefix . 'font_color'];
646
- $style->font_weight = empty($options[$prefix . 'font_weight']) ? $composer[$type . '_font_weight'] : $options[$prefix . 'font_weight'];
647
- if ($type === 'button') {
648
- $style->background = empty($options[$prefix . 'background']) ? $composer[$type . '_background_color'] : $options[$prefix . 'background'];
649
- }
650
- return $style;
651
- }
652
-
653
- static function get_button_options($options, $prefix, $composer) {
654
- $button_options = [];
655
- $scale = 1;
656
- $button_options['button_font_family'] = empty($options[$prefix . '_font_family']) ? $composer['button_font_family'] : $options[$prefix . '_font_family'];
657
- $button_options['button_font_size'] = empty($options[$prefix . '_font_size']) ? round($composer['button_font_size'] * $scale) : $options[$prefix . '_font_size'];
658
- $button_options['button_font_color'] = empty($options[$prefix . '_font_color']) ? $composer['button_font_color'] : $options[$prefix . '_font_color'];
659
- $button_options['button_font_weight'] = empty($options[$prefix . '_font_weight']) ? $composer['button_font_weight'] : $options[$prefix . '_font_weight'];
660
- $button_options['button_background'] = empty($options[$prefix . '_background']) ? $composer['button_background_color'] : $options[$prefix . '_background'];
661
- $button_options['button_align'] = empty($options[$prefix . '_align']) ? 'center' : $options[$prefix . '_align'];
662
- $button_options['button_width'] = empty($options[$prefix . '_width']) ? 'center' : $options[$prefix . '_width'];
663
- $button_options['button_url'] = empty($options[$prefix . '_url']) ? '#' : $options[$prefix . '_url'];
664
- $button_options['button_label'] = empty($options[$prefix . '_label']) ? '' : $options[$prefix . '_label'];
665
-
666
- return $button_options;
667
- }
668
-
669
- static function convert_to_text($html) {
670
- if (!class_exists('DOMDocument')) {
671
- return '';
672
- }
673
- // Replace '&' with '&amp;' in URLs to avoid warnings about inavlid entities from loadHTML()
674
- // Todo: make this more general using a regular expression
675
- //$logger = PlaintextNewsletterAddon::$instance->get_logger();
676
- //$logger->debug('html="' . $html . '"');
677
- $html = str_replace(
678
- array('&nk=', '&nek=', '&id='),
679
- array('&amp;nk=', '&amp;nek=', '&amp;id='),
680
- $html);
681
- //$logger->debug('new html="' . $html . '"');
682
- //
683
- $output = '';
684
- $dom = new DOMDocument();
685
- $r = $dom->loadHTML('<?xml encoding="utf-8" ?>' . $html);
686
- if (!$r) {
687
- return '';
688
- }
689
- $bodylist = $dom->getElementsByTagName('body');
690
- // Of course it should be a single element
691
- foreach ($bodylist as $body) {
692
- self::process_dom_element($body, $output);
693
- }
694
- return $output;
695
- }
696
-
697
- static function process_dom_element(DOMElement $parent, &$output) {
698
- foreach ($parent->childNodes as $node) {
699
- if (is_a($node, 'DOMElement') && ($node->tagName != 'style')) {
700
- if ($node->tagName== 'br') {
701
- $output .= "\n";
702
- continue;
703
- }
704
- self::process_dom_element($node, $output);
705
-
706
- // If the containing tag was a block level tag, we add a couple of line ending
707
- if ($node->tagName == 'p' || $node->tagName == 'div' || $node->tagName == 'td') {
708
- // Avoid more than one blank line between elements
709
- if ((strlen($output) >= 2) && (substr($output, -2) != "\n\n")) {
710
- $output .= "\n\n";
711
- }
712
- }
713
-
714
- if ($node->tagName == 'a') {
715
- $output .= ' (' . $node->getAttribute('href') . ') ';
716
- continue;
717
- }
718
- elseif ($node->tagName == 'img') {
719
- $output .= $node->getAttribute('alt');
720
- }
721
- }
722
- elseif (is_a($node, 'DOMText')) {
723
- $decoded = utf8_decode($node->wholeText);
724
- if (ctype_space($decoded)) {
725
- // Append blank only if last character output is not blank.
726
- if ((strlen($output) > 0) && !ctype_space(substr($output, -1))) {
727
- $output .= ' ';
728
- }
729
- } else {
730
- $output .= trim($node->wholeText);
731
- }
732
- }
733
- }
734
- }
735
- }
736
-
737
-
738
-
739
- class TNP_Style {
740
-
741
- var $font_family;
742
- var $font_size;
743
- var $font_weight;
744
- var $font_color;
745
- var $background;
746
-
747
- function echo_css($scale = 1.0) {
748
- echo 'font-size: ', round($this->font_size * $scale), 'px;';
749
- echo 'font-family: ', $this->font_family, ';';
750
- echo 'font-weight: ', $this->font_weight, ';';
751
- echo 'color: ', $this->font_color, ';';
752
- }
753
-
754
- }
755
-
756
- /**
757
- * Generate multicolumn and responsive html template for email.
758
- * Initialize class with max columns per row and start to add cells.
759
- */
760
- class TNP_Composer_Grid_System {
761
-
762
- /**
763
- * @var TNP_Composer_Grid_Row[]
764
- */
765
- private $rows;
766
-
767
- /**
768
- * @var int
769
- */
770
- private $cells_per_row;
771
-
772
- /**
773
- * @var int
774
- */
775
- private $cells_counter;
776
-
777
- /**
778
- * TNP_Composer_Grid_System constructor.
779
- *
780
- * @param int $columns_per_row Max columns per row
781
- */
782
- public function __construct($columns_per_row) {
783
- $this->cells_per_row = $columns_per_row;
784
- $this->cells_counter = 0;
785
- $this->rows = [];
786
- }
787
-
788
- public function __toString() {
789
- return $this->render();
790
- }
791
-
792
- /**
793
- * Add cell to grid
794
- *
795
- * @param TNP_Composer_Grid_Cell $cell
796
- */
797
- public function add_cell($cell) {
798
-
799
- if ($this->cells_counter % $this->cells_per_row === 0) {
800
- $this->add_row(new TNP_Composer_Grid_Row());
801
- }
802
-
803
- $row_idx = (int) floor($this->cells_counter / $this->cells_per_row);
804
- $this->rows[$row_idx]->add_cell($cell);
805
- $this->cells_counter++;
806
- }
807
-
808
- private function add_row($row) {
809
- $this->rows[] = $row;
810
- }
811
-
812
- public function render() {
813
-
814
- $str = '';
815
- foreach ($this->rows as $row) {
816
- $str .= $row->render();
817
- }
818
-
819
- return $str;
820
- }
821
-
822
- }
823
-
824
- /**
825
- * Class TNP_Composer_Grid_Row
826
- */
827
- class TNP_Composer_Grid_Row {
828
-
829
- /**
830
- * @var TNP_Composer_Grid_Cell[]
831
- */
832
- private $cells;
833
-
834
- public function __construct(...$cells) {
835
- if (!empty($cells)) {
836
- foreach ($cells as $cell) {
837
- $this->add_cell($cell);
838
- }
839
- }
840
- }
841
-
842
- /**
843
- * @param TNP_Composer_Grid_Cell $cell
844
- */
845
- public function add_cell($cell) {
846
- $this->cells[] = $cell;
847
- }
848
-
849
- public function render() {
850
- $rendered_cells = '';
851
- $column_percentage_width = round(100 / $this->cells_count(), 0, PHP_ROUND_HALF_DOWN) . '%';
852
- foreach ($this->cells as $cell) {
853
- $rendered_cells .= $cell->render(['width' => $column_percentage_width]);
854
- }
855
-
856
- $row_template = $this->get_template();
857
-
858
- return str_replace('TNP_ROW_CONTENT_PH', $rendered_cells, $row_template);
859
- }
860
-
861
- private function cells_count() {
862
- return count($this->cells);
863
- }
864
-
865
- private function get_template() {
866
- return "<table border='0' cellpadding='0' cellspacing='0' width='100%'><tbody><tr><td>TNP_ROW_CONTENT_PH</td></tr></tbody></table>";
867
- }
868
-
869
- }
870
-
871
- /**
872
- * Class TNP_Composer_Grid_Cell
873
- */
874
- class TNP_Composer_Grid_Cell {
875
-
876
- /**
877
- * @var string
878
- */
879
- private $content;
880
-
881
- /**
882
- * @var array
883
- */
884
- public $args;
885
-
886
- public function __construct($content = null, $args = []) {
887
- $default_args = [
888
- 'width' => '100%',
889
- 'class' => '',
890
- 'align' => 'left',
891
- 'valign' => 'top'
892
- ];
893
-
894
- $this->args = array_merge($default_args, $args);
895
-
896
- $this->content = $content ? $content : '';
897
- }
898
-
899
- public function add_content($content) {
900
- $this->content .= $content;
901
- }
902
-
903
- public function render($args) {
904
- $this->args = array_merge($this->args, $args);
905
-
906
- $column_template = $this->get_template();
907
- $column = str_replace(
908
- [
909
- 'TNP_ALIGN_PH',
910
- 'TNP_VALIGN_PH',
911
- 'TNP_WIDTH_PH',
912
- 'TNP_CLASS_PH',
913
- 'TNP_COLUMN_CONTENT_PH'
914
- ], [
915
- $this->args['align'],
916
- $this->args['valign'],
917
- $this->args['width'],
918
- $this->args['class'],
919
- $this->content
920
- ], $column_template);
921
-
922
- return $column;
923
- }
924
-
925
- private function get_template() {
926
- return "<table border='0' cellpadding='0' cellspacing='0' width='TNP_WIDTH_PH' align='left' style='table-layout: fixed;' class='responsive'>
927
- <tbody>
928
- <tr>
929
- <td border='0' style='padding: 20px 10px 40px;' align='TNP_ALIGN_PH' valign='TNP_VALIGN_PH' class='TNP_CLASS_PH'>
930
- TNP_COLUMN_CONTENT_PH
931
- </td>
932
- </tr>
933
- </tbody>
934
- </table>";
935
- }
936
-
937
- }
938
-
939
- class TNP_Composer_Component_Factory {
940
-
941
- private $options;
942
-
943
- /**
944
- * TNP_Composer_Component_Factory constructor.
945
- *
946
- * @param Controller$controller
947
- */
948
- public function __construct($controller) {
949
-
950
- }
951
-
952
- function heading() {
953
-
954
- }
955
-
956
- function paragraph() {
957
-
958
- }
959
-
960
- function link() {
961
-
962
- }
963
-
964
- function button() {
965
-
966
- }
967
-
968
- function image() {
969
-
970
- }
971
-
972
- }
 
 
 
 
 
1
+ <?php
2
+
3
+ /** For old style coders */
4
+ function tnp_register_block($dir) {
5
+ return TNP_Composer::register_block($dir);
6
+ }
7
+
8
+ /**
9
+ * Generates and HTML button for email using the values found on $options and
10
+ * prefixed by $prefix, with the standard syntax of NewsletterFields::button().
11
+ *
12
+ * @param array $options
13
+ * @param string $prefix
14
+ * @return string
15
+ */
16
+ function tnpc_button($options, $prefix = 'button') {
17
+ return TNP_Composer::button($options, $prefix);
18
+ }
19
+
20
+ class TNP_Composer {
21
+
22
+ static $block_dirs = array();
23
+
24
+ static function register_block($dir) {
25
+ // Checks
26
+ $dir = realpath($dir);
27
+ if (!$dir) {
28
+ $error = new WP_Error('1', 'Seems not a valid path: ' . $dir);
29
+ NewsletterEmails::instance()->logger->error($error);
30
+ return $error;
31
+ }
32
+
33
+ $dir = wp_normalize_path($dir);
34
+
35
+ if (!file_exists($dir . '/block.php')) {
36
+ $error = new WP_Error('1', 'block.php missing on folder ' . $dir);
37
+ NewsletterEmails::instance()->logger->error($error);
38
+ return $error;
39
+ }
40
+
41
+ self::$block_dirs[] = $dir;
42
+ return true;
43
+ }
44
+
45
+ /**
46
+ * @param string $open
47
+ * @param string $inner
48
+ * @param string $close
49
+ * @param string[] $markers
50
+ *
51
+ * @return string
52
+ */
53
+ static function wrap_html_element($open, $inner, $close, $markers = array('<!-- tnp -->', '<!-- /tnp -->')) {
54
+
55
+ return $open . $markers[0] . $inner . $markers[1] . $close;
56
+ }
57
+
58
+ /**
59
+ * @param string $block
60
+ * @param string[] $markers
61
+ *
62
+ * @return string
63
+ */
64
+ static function unwrap_html_element($block, $markers = array('<!-- tnp -->', '<!-- /tnp -->')) {
65
+ if (self::_has_markers($block, $markers)) {
66
+ self::_escape_markers($markers);
67
+ $pattern = sprintf('/%s(.*?)%s/s', $markers[0], $markers[1]);
68
+
69
+ $matches = array();
70
+ preg_match($pattern, $block, $matches);
71
+
72
+ return $matches[1];
73
+ }
74
+
75
+ return $block;
76
+ }
77
+
78
+ /**
79
+ * @param string $block
80
+ * @param string[] $markers
81
+ *
82
+ * @return bool
83
+ */
84
+ private static function _has_markers($block, $markers = array('<!-- tnp -->', '<!-- /tnp -->')) {
85
+
86
+ self::_escape_markers($markers);
87
+
88
+ $pattern = sprintf('/%s(.*?)%s/s', $markers[0], $markers[1]);
89
+
90
+ return preg_match($pattern, $block);
91
+ }
92
+
93
+ /**
94
+ * Sources:
95
+ * - https://webdesign.tutsplus.com/tutorials/creating-a-future-proof-responsive-email-without-media-queries--cms-23919
96
+ *
97
+ * @param type $email
98
+ * @return type
99
+ */
100
+ static function get_html_open($email) {
101
+ $open = "<!DOCTYPE html>\n";
102
+ $open .= "<html xmlns=\"https://www.w3.org/1999/xhtml\" xmlns:o=\"urn:schemas-microsoft-com:office:office\">\n<head>\n<title>{email_subject}</title>\n";
103
+ $open .= "<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n";
104
+
105
+ $open .= '<!--[if !mso]><!-->' . "\n";
106
+ $open .= '<meta http-equiv="X-UA-Compatible" content="IE=edge" />' . "\n";
107
+ $open .= '<!--<![endif]-->' . "\n";
108
+
109
+ $open .= '<!--[if mso]>' . "\n";
110
+ ;
111
+ $open .= '<style type="text/css">';
112
+ $open .= 'table {border-collapse:collapse;border-spacing:0;margin:0;}';
113
+ $open .= 'div, td {padding:0;}';
114
+ $open .= 'div {margin:0 !important;}';
115
+ $open .= '</style>';
116
+ $open .= "\n";
117
+ $open .= '<noscript>';
118
+ $open .= '<xml>';
119
+ $open .= '<o:OfficeDocumentSettings>';
120
+ $open .= '<o:PixelsPerInch>96</o:PixelsPerInch>';
121
+ $open .= '</o:OfficeDocumentSettings>';
122
+ $open .= '</xml>';
123
+ $open .= '</noscript>';
124
+ $open .= "\n";
125
+ $open .= '<![endif]-->';
126
+ $open .= "\n";
127
+ $open .= "<style type=\"text/css\">\n";
128
+ $open .= NewsletterEmails::instance()->get_composer_css();
129
+ $open .= "\n</style>\n";
130
+ $open .= "</head>\n";
131
+ $open .= '<body style="margin: 0; padding: 0; line-height: normal; word-spacing: normal;" dir="' . (is_rtl() ? 'rtl' : 'ltr') . '">';
132
+ $open .= "\n";
133
+ $open .= self::get_html_preheader($email);
134
+
135
+ return $open;
136
+ }
137
+
138
+ static private function get_html_preheader($email) {
139
+
140
+ if (empty($email->options['preheader'])) {
141
+ return "";
142
+ }
143
+
144
+ $preheader_text = esc_html($email->options['preheader']);
145
+ $html = "<div style=\"display:none;font-size:1px;color:#ffffff;line-height:1px;max-height:0px;max-width:0px;opacity:0;overflow:hidden;\">$preheader_text</div>";
146
+ $html .= "\n";
147
+
148
+ return $html;
149
+ }
150
+
151
+ static function get_html_close($email) {
152
+ return "</body>\n</html>";
153
+ }
154
+
155
+ /**
156
+ *
157
+ * @param TNP_Email $email
158
+ * @return string
159
+ */
160
+ static function get_main_wrapper_open($email) {
161
+ if (!isset($email->options['composer_background']) || $email->options['composer_background'] == 'inherit') {
162
+ $bgcolor = '';
163
+ } else {
164
+ $bgcolor = $email->options['composer_background'];
165
+ }
166
+
167
+ return "\n<table cellpadding='0' cellspacing='0' border='0' width='100%'>\n" .
168
+ "<tr>\n" .
169
+ "<td bgcolor='$bgcolor' valign='top'><!-- tnp -->";
170
+ }
171
+
172
+ /**
173
+ *
174
+ * @param TNP_Email $email
175
+ * @return string
176
+ */
177
+ static function get_main_wrapper_close($email) {
178
+ return "\n<!-- /tnp -->\n" .
179
+ "</td>\n" .
180
+ "</tr>\n" .
181
+ "</table>\n\n";
182
+ }
183
+
184
+ /**
185
+ * Remove <doctype>, <body> and unnecessary envelopes for editing with composer
186
+ *
187
+ * @param string $html_email
188
+ *
189
+ * @return string
190
+ */
191
+ static function unwrap_email($html_email) {
192
+
193
+ if (self::_has_markers($html_email)) {
194
+ $html_email = self::unwrap_html_element($html_email);
195
+ } else {
196
+ //KEEP FOR OLD EMAIL COMPATIBILITY
197
+ // Extracts only the body part
198
+ $x = strpos($html_email, '<body');
199
+ if ($x) {
200
+ $x = strpos($html_email, '>', $x);
201
+ $y = strpos($html_email, '</body>');
202
+ $html_email = substr($html_email, $x + 1, $y - $x - 1);
203
+ }
204
+
205
+ /* Cleans up uncorrectly stored newsletter bodies */
206
+ $html_email = preg_replace('/<style\s+.*?>.*?<\\/style>/is', '', $html_email);
207
+ $html_email = preg_replace('/<meta.*?>/', '', $html_email);
208
+ $html_email = preg_replace('/<title\s+.*?>.*?<\\/title>/i', '', $html_email);
209
+ $html_email = trim($html_email);
210
+ }
211
+
212
+ // Required since esc_html DOES NOT escape the HTML entities (apparently)
213
+ $html_email = str_replace('&', '&amp;', $html_email);
214
+ $html_email = str_replace('"', '&quot;', $html_email);
215
+ $html_email = str_replace('<', '&lt;', $html_email);
216
+ $html_email = str_replace('>', '&gt;', $html_email);
217
+
218
+ return $html_email;
219
+ }
220
+
221
+ private static function _escape_markers(&$markers) {
222
+ $markers[0] = str_replace('/', '\/', $markers[0]);
223
+ $markers[1] = str_replace('/', '\/', $markers[1]);
224
+ }
225
+
226
+ /**
227
+ * Using the data collected inside $controls (and submitted by a form containing the
228
+ * composer fields), updates the email. The message body is completed with doctype,
229
+ * head, style and the main wrapper.
230
+ *
231
+ * @param TNP_Email $email
232
+ * @param NewsletterControls $controls
233
+ */
234
+ static function update_email($email, $controls) {
235
+ if (isset($controls->data['subject'])) {
236
+ $email->subject = $controls->data['subject'];
237
+ }
238
+
239
+ // They should be only composer options
240
+ foreach ($controls->data as $name => $value) {
241
+ if (strpos($name, 'options_') === 0) {
242
+ $email->options[substr($name, 8)] = $value;
243
+ }
244
+ }
245
+
246
+ //if (isset($controls->data['preheader'])) {
247
+ // $email->options['preheader'] = $controls->data['preheader'];
248
+ //}
249
+
250
+ $email->editor = NewsletterEmails::EDITOR_COMPOSER;
251
+
252
+ $email->message = self::get_html_open($email) . self::get_main_wrapper_open($email) .
253
+ $controls->data['message'] . self::get_main_wrapper_close($email) . self::get_html_close($email);
254
+ }
255
+
256
+ /**
257
+ * Prepares a controls object injecting the relevant fields from an email
258
+ * which cannot be directly used by controls. If $email is null or missing,
259
+ * $controls is prepared with default values.
260
+ *
261
+ * @param NewsletterControls $controls
262
+ * @param TNP_Email $email
263
+ */
264
+ static function prepare_controls($controls, $email = null) {
265
+
266
+ // Controls for a new email (which actually does not exist yet
267
+ if (!empty($email)) {
268
+
269
+ foreach ($email->options as $name => $value) {
270
+ $controls->data['options_' . $name] = $value;
271
+ }
272
+
273
+ $controls->data['message'] = TNP_Composer::unwrap_email($email->message);
274
+ $controls->data['subject'] = $email->subject;
275
+ $controls->data['updated'] = $email->updated;
276
+ }
277
+
278
+ if (!empty($email->options['sender_email'])) {
279
+ $controls->data['sender_email'] = $email->options['sender_email'];
280
+ } else {
281
+ $controls->data['sender_email'] = Newsletter::instance()->options['sender_email'];
282
+ }
283
+
284
+ if (!empty($email->options['sender_name'])) {
285
+ $controls->data['sender_name'] = $email->options['sender_name'];
286
+ } else {
287
+ $controls->data['sender_name'] = Newsletter::instance()->options['sender_name'];
288
+ }
289
+
290
+ $controls->data = array_merge(TNP_Composer::get_global_style_defaults(), $controls->data);
291
+ }
292
+
293
+ /**
294
+ * Extract inline edited post field from inline_edit_list[]
295
+ *
296
+ * @param array $inline_edit_list
297
+ * @param string $field_type
298
+ * @param int $post_id
299
+ *
300
+ * @return string
301
+ */
302
+ static function get_edited_inline_post_field($inline_edit_list, $field_type, $post_id) {
303
+
304
+ foreach ($inline_edit_list as $edit) {
305
+ if ($edit['type'] == $field_type && $edit['post_id'] == $post_id) {
306
+ return $edit['content'];
307
+ }
308
+ }
309
+
310
+ return '';
311
+ }
312
+
313
+ /**
314
+ * Check if inline_edit_list[] have inline edit field for specific post
315
+ *
316
+ * @param array $inline_edit_list
317
+ * @param string $field_type
318
+ * @param int $post_id
319
+ *
320
+ * @return bool
321
+ */
322
+ static function is_post_field_edited_inline($inline_edit_list, $field_type, $post_id) {
323
+ if (empty($inline_edit_list) || !is_array($inline_edit_list)) {
324
+ return false;
325
+ }
326
+ foreach ($inline_edit_list as $edit) {
327
+ if ($edit['type'] == $field_type && $edit['post_id'] == $post_id) {
328
+ return true;
329
+ }
330
+ }
331
+
332
+ return false;
333
+ }
334
+
335
+ /**
336
+ * Creates the HTML for a button extrating from the options, with the provided prefix, the button attributes:
337
+ *
338
+ * - [prefix]_url The button URL
339
+ * - [prefix]_font_family
340
+ * - [prefix]_font_size
341
+ * - [prefix]_font_weight
342
+ * - [prefix]_label
343
+ * - [prefix]_font_color The label color
344
+ * - [prefix]_background The button color
345
+ *
346
+ * TODO: Add radius and possiblt the alignment
347
+ *
348
+ * @param array $options
349
+ * @param string $prefix
350
+ * @return string
351
+ */
352
+ static function button($options, $prefix = 'button') {
353
+
354
+ if (empty($options[$prefix . '_label'])) {
355
+ return;
356
+ }
357
+ $defaults = [
358
+ $prefix . '_url' => '#',
359
+ $prefix . '_font_family' => 'Helvetica, Arial, sans-serif',
360
+ $prefix . '_font_color' => '#ffffff',
361
+ $prefix . '_font_weight' => 'bold',
362
+ $prefix . '_font_size' => 20,
363
+ $prefix . '_background' => '#256F9C',
364
+ $prefix . '_align' => 'center'
365
+ ];
366
+
367
+ $options = array_merge($defaults, array_filter($options));
368
+
369
+ $b = '<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="margin: 0 auto"';
370
+ if (!empty($options[$prefix . '_align'])) {
371
+ $b .= ' align="' . esc_attr($options[$prefix . '_align']) . '"';
372
+ }
373
+ if (!empty($options[$prefix . '_width'])) {
374
+ $b .= ' width="' . esc_attr($options[$prefix . '_width']) . '"';
375
+ }
376
+ $b .= '>';
377
+ $b .= '<tr>';
378
+ $b .= '<td align="center" bgcolor="' . $options[$prefix . '_background'] . '" role="presentation" style="border:none;border-radius:3px;cursor:auto;mso-padding-alt:10px 25px;background:' . $options[$prefix . '_background'] . '" valign="middle">';
379
+ $b .= '<a href="' . $options[$prefix . '_url'] . '"';
380
+ $b .= ' style="display:inline-block;background:' . $options[$prefix . '_background'] . ';color:' . $options[$prefix . '_font_color'] . ';font-family:' . $options[$prefix . '_font_family'] . ';font-size:' . $options[$prefix . '_font_size'] . 'px;font-weight:' . $options[$prefix . '_font_weight'] . ';line-height:120%;margin:0;text-decoration:none;text-transform:none;padding:10px 25px;mso-padding-alt:0px;border-radius:3px;"';
381
+ $b .= ' target="_blank">';
382
+ $b .= $options[$prefix . '_label'];
383
+ $b .= '</a>';
384
+ $b .= '</td></tr></table>';
385
+ return $b;
386
+ }
387
+
388
+ /**
389
+ * Generates an IMG tag, linked if the media has an URL.
390
+ *
391
+ * @param TNP_Media $media
392
+ * @param string $style
393
+ * @return string
394
+ */
395
+ static function image($media, $attr = []) {
396
+
397
+ $default_attrs = [
398
+ 'style' => 'max-width: 100%; height: auto; display: inline-block',
399
+ 'class' => '',
400
+ 'link-style' => 'text-decoration: none; display: inline-block',
401
+ 'link-class' => null,
402
+ ];
403
+
404
+ $attr = array_merge($default_attrs, $attr);
405
+
406
+ //Class and style attribute are mutually exclusive.
407
+ //Class take priority to style because classes will transform to inline style inside block rendering operation
408
+ if (!empty($attr['inline-class'])) {
409
+ $styling = ' inline-class="' . esc_attr($attr['inline-class']) . '" ';
410
+ } else {
411
+ $styling = ' style="' . esc_attr($attr['style']) . '" ';
412
+ }
413
+
414
+ if (!empty($attr['class'])) {
415
+ $styling .= ' class="' . esc_attr($attr['class']) . '" ';
416
+ }
417
+
418
+ //Class and style attribute are mutually exclusive.
419
+ //Class take priority to style because classes will transform to inline style inside block rendering operation
420
+ if (!empty($attr['link-class'])) {
421
+ $link_styling = ' inline-class="' . esc_attr($attr['link-class']) . '" ';
422
+ } else {
423
+ $link_styling = ' style="' . esc_attr($attr['link-style']) . '" ';
424
+ }
425
+
426
+ $b = '';
427
+ if ($media->link) {
428
+ $b .= '<a href="' . esc_attr($media->link) . '" target="_blank" rel="noopener nofollow" style="display: inline-block; font-size: 0; text-decoration: none; line-height: normal!important">';
429
+ } else {
430
+ // The span grants images are not upscaled when fluid (two columns posts block)
431
+ $b .= '<span style="display: inline-block; font-size: 0; text-decoration: none; line-height: normal!important">';
432
+ }
433
+ if ($media) {
434
+ $b .= '<img src="' . esc_attr($media->url) . '" width="' . esc_attr($media->width) . '"';
435
+ if ($media->height) {
436
+ $b .= ' height="' . esc_attr($media->height) . '"';
437
+ }
438
+ $b .= ' alt="' . esc_attr($media->alt) . '"'
439
+ . ' border="0"'
440
+ . ' style="display: inline-block; max-width: 100%!important; padding: 0; border: 0; font-size: 12px"'
441
+ . ' class="' . esc_attr($attr['class']) . '" '
442
+ . '>';
443
+ }
444
+
445
+ if ($media->link) {
446
+ $b .= '</a>';
447
+ } else {
448
+ $b .= '</span>';
449
+ }
450
+
451
+ return $b;
452
+ }
453
+
454
+ /**
455
+ * Returns a WP media ID for the specified post (or false if nothing can be found)
456
+ * looking for the featured image or, if missing, taking the first media in the gallery and
457
+ * if again missing, searching the first reference to a media in the post content.
458
+ *
459
+ * The media ID is not checked for real existance of the associated attachment.
460
+ *
461
+ * @param int $post_id
462
+ * @return int
463
+ */
464
+ static function get_post_thumbnail_id($post_id) {
465
+ if (is_object($post_id)) {
466
+ $post_id = $post_id->ID;
467
+ }
468
+
469
+ // Find a media id to be used as featured image
470
+ $media_id = get_post_thumbnail_id($post_id);
471
+ if (!empty($media_id)) {
472
+ return $media_id;
473
+ }
474
+
475
+ $attachments = get_children(array('numberpost' => 1, 'post_parent' => $post_id, 'post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => 'image', 'order' => 'ASC', 'orderby' => 'menu_order'));
476
+ if (!empty($attachments)) {
477
+ foreach ($attachments as $id => &$attachment) {
478
+ return $id;
479
+ }
480
+ }
481
+
482
+ $post = get_post($post_id);
483
+
484
+ $r = preg_match('/wp-image-(\d+)/', $post->post_content, $matches);
485
+ if ($matches) {
486
+ return (int) $matches[1];
487
+ }
488
+
489
+ return false;
490
+ }
491
+
492
+ /**
493
+ * Builds a TNP_Media object to be used in newsletters from a WP media/attachement ID. The returned
494
+ * media has a size which best match the one requested (this is the standard WP behavior, plugins
495
+ * could change it).
496
+ *
497
+ * @param int $media_id
498
+ * @param array $size
499
+ * @return \TNP_Media
500
+ */
501
+ function get_media($media_id, $size) {
502
+ $src = wp_get_attachment_image_src($media_id, $size);
503
+ if (!$src) {
504
+ return null;
505
+ }
506
+ $media = new TNP_Media();
507
+ $media->id = $media_id;
508
+ $media->url = $src[0];
509
+ $media->width = $src[1];
510
+ $media->height = $src[2];
511
+ return $media;
512
+ }
513
+
514
+ static function post_content($post) {
515
+ $content = $post->post_content;
516
+ $content = wpautop($content);
517
+ if (true || $options['enable shortcodes']) {
518
+ remove_shortcode('gallery');
519
+ add_shortcode('gallery', 'tnp_gallery_shortcode');
520
+ $content = do_shortcode($content);
521
+ }
522
+ $content = str_replace('<p>', '<p class="paragraph">', $content);
523
+
524
+ $selected_images = array();
525
+ if (preg_match_all('/<img [^>]+>/', $content, $matches)) {
526
+ foreach ($matches[0] as $image) {
527
+ if (preg_match('/wp-image-([0-9]+)/i', $image, $class_id) && ( $attachment_id = absint($class_id[1]) )) {
528
+ $selected_images[$image] = $attachment_id;
529
+ }
530
+ }
531
+ }
532
+
533
+ foreach ($selected_images as $image => $attachment_id) {
534
+ $src = tnp_media_resize($attachment_id, array(600, 0));
535
+ if (is_wp_error($src)) {
536
+ continue;
537
+ }
538
+ $content = str_replace($image, '<img src="' . $src . '" width="600" style="max-width: 100%">', $content);
539
+ }
540
+
541
+ return $content;
542
+ }
543
+
544
+ static function get_global_style_defaults() {
545
+ return [
546
+ 'options_composer_title_font_family' => 'Verdana, Geneva, sans-serif',
547
+ 'options_composer_title_font_size' => 32,
548
+ 'options_composer_title_font_weight' => 'normal',
549
+ 'options_composer_title_font_color' => '#222222',
550
+ 'options_composer_text_font_family' => 'Verdana, Geneva, sans-serif',
551
+ 'options_composer_text_font_size' => 16,
552
+ 'options_composer_text_font_weight' => 'normal',
553
+ 'options_composer_text_font_color' => '#222222',
554
+ 'options_composer_button_font_family' => 'Verdana, Geneva, sans-serif',
555
+ 'options_composer_button_font_size' => 16,
556
+ 'options_composer_button_font_weight' => 'normal',
557
+ 'options_composer_button_font_color' => '#FFFFFF',
558
+ 'options_composer_button_background_color' => '#256F9C',
559
+ 'options_composer_background' => '#FFFFFF',
560
+ 'options_composer_block_background' => '#FFFFFF',
561
+ ];
562
+ }
563
+
564
+ /**
565
+ * Inspired by: https://webdesign.tutsplus.com/tutorials/creating-a-future-proof-responsive-email-without-media-queries--cms-23919
566
+ *
567
+ * Attributes:
568
+ * - columns: number of columns [2]
569
+ * - padding: cells padding [10]
570
+ * - responsive: il on mobile the cell should stack up [true]
571
+ * - width: the whole row width, it should reduced by the external row padding [600]
572
+ *
573
+ * @param string[] $items
574
+ * @param array $attrs
575
+ * @return string
576
+ */
577
+ static function grid($items = [], $attrs = []) {
578
+ $attrs = wp_parse_args($attrs, ['width' => 600, 'columns' => 2, 'padding' => 10, 'responsive' => true]);
579
+ $width = (int) $attrs['width'];
580
+ $columns = (int) $attrs['columns'];
581
+ $padding = (int) $attrs['padding'];
582
+ $column_width = $width / $columns;
583
+ $td_width = 100 / $columns;
584
+ $chunks = array_chunk($items, $columns);
585
+
586
+ if ($attrs['responsive']) {
587
+
588
+ $e = '';
589
+ foreach ($chunks as &$chunk) {
590
+ $e .= '<div style="text-align:center;font-size:0;">';
591
+ $e .= '<!--[if mso]><table role="presentation" width="100%"><tr><![endif]-->';
592
+ foreach ($chunk as &$item) {
593
+ $e .= '<!--[if mso]><td width="' . $td_width . '%" style="width:' . $td_width . '%;padding:' . $padding . 'px" valign="top"><![endif]-->';
594
+
595
+ $e .= '<div class="max-width-100" style="width:100%;max-width:' . $column_width . 'px;display:inline-block;vertical-align: top;box-sizing: border-box;">';
596
+
597
+ // This element to add padding without deal with border-box not well supported
598
+ $e .= '<div style="padding:' . $padding . 'px;">';
599
+ $e .= $item;
600
+ $e .= '</div>';
601
+ $e .= '</div>';
602
+
603
+ $e .= '<!--[if mso]></td><![endif]-->';
604
+ }
605
+ $e .= '<!--[if mso]></tr></table><![endif]-->';
606
+ $e .= '</div>';
607
+ }
608
+
609
+ return $e;
610
+ } else {
611
+ $e = '<table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0" style="width: 100%; max-width: 100%!important">';
612
+ foreach ($chunks as &$chunk) {
613
+ $e .= '<tr>';
614
+ foreach ($chunk as &$item) {
615
+ $e .= '<td width="' . $td_width . '%" style="width:' . $td_width . '%; padding:' . $padding . 'px" valign="top">';
616
+ $e .= $item;
617
+ $e .= '</td>';
618
+ }
619
+ $e .= '</tr>';
620
+ }
621
+ $e .= '</table>';
622
+ return $e;
623
+ }
624
+ }
625
+
626
+ static function get_text_style($options, $prefix, $composer, $attrs = []) {
627
+ return self::get_style($options, $prefix, $composer, 'text', $attrs);
628
+ }
629
+
630
+ static function get_title_style($options, $prefix, $composer, $attrs = []) {
631
+ return self::get_style($options, $prefix, $composer, 'title', $attrs);
632
+ }
633
+
634
+ static function get_style($options, $prefix, $composer, $type = 'text', $attrs = []) {
635
+ $style = new TNP_Style();
636
+ $scale = 1.0;
637
+ if (!empty($attrs['scale'])) {
638
+ $scale = (float) $attrs['scale'];
639
+ }
640
+ if (!empty($prefix))
641
+ $prefix .= '_';
642
+
643
+ $style->font_family = empty($options[$prefix . 'font_family']) ? $composer[$type . '_font_family'] : $options[$prefix . 'font_family'];
644
+ $style->font_size = empty($options[$prefix . 'font_size']) ? round($composer[$type . '_font_size'] * $scale) : $options[$prefix . 'font_size'];
645
+ $style->font_color = empty($options[$prefix . 'font_color']) ? $composer[$type . '_font_color'] : $options[$prefix . 'font_color'];
646
+ $style->font_weight = empty($options[$prefix . 'font_weight']) ? $composer[$type . '_font_weight'] : $options[$prefix . 'font_weight'];
647
+ if ($type === 'button') {
648
+ $style->background = empty($options[$prefix . 'background']) ? $composer[$type . '_background_color'] : $options[$prefix . 'background'];
649
+ }
650
+ return $style;
651
+ }
652
+
653
+ static function get_button_options($options, $prefix, $composer) {
654
+ $button_options = [];
655
+ $scale = 1;
656
+ $button_options['button_font_family'] = empty($options[$prefix . '_font_family']) ? $composer['button_font_family'] : $options[$prefix . '_font_family'];
657
+ $button_options['button_font_size'] = empty($options[$prefix . '_font_size']) ? round($composer['button_font_size'] * $scale) : $options[$prefix . '_font_size'];
658
+ $button_options['button_font_color'] = empty($options[$prefix . '_font_color']) ? $composer['button_font_color'] : $options[$prefix . '_font_color'];
659
+ $button_options['button_font_weight'] = empty($options[$prefix . '_font_weight']) ? $composer['button_font_weight'] : $options[$prefix . '_font_weight'];
660
+ $button_options['button_background'] = empty($options[$prefix . '_background']) ? $composer['button_background_color'] : $options[$prefix . '_background'];
661
+ $button_options['button_align'] = empty($options[$prefix . '_align']) ? 'center' : $options[$prefix . '_align'];
662
+ $button_options['button_width'] = empty($options[$prefix . '_width']) ? 'center' : $options[$prefix . '_width'];
663
+ $button_options['button_url'] = empty($options[$prefix . '_url']) ? '#' : $options[$prefix . '_url'];
664
+ $button_options['button_label'] = empty($options[$prefix . '_label']) ? '' : $options[$prefix . '_label'];
665
+
666
+ return $button_options;
667
+ }
668
+
669
+ static function convert_to_text($html) {
670
+ if (!class_exists('DOMDocument')) {
671
+ return '';
672
+ }
673
+ // Replace '&' with '&amp;' in URLs to avoid warnings about inavlid entities from loadHTML()
674
+ // Todo: make this more general using a regular expression
675
+ //$logger = PlaintextNewsletterAddon::$instance->get_logger();
676
+ //$logger->debug('html="' . $html . '"');
677
+ $html = str_replace(
678
+ array('&nk=', '&nek=', '&id='),
679
+ array('&amp;nk=', '&amp;nek=', '&amp;id='),
680
+ $html);
681
+ //$logger->debug('new html="' . $html . '"');
682
+ //
683
+ $output = '';
684
+
685
+ // Prevents warnings for problems with the HTML
686
+ if (function_exists('libxml_use_internal_errors')) {
687
+ libxml_use_internal_errors(true);
688
+ }
689
+ $dom = new DOMDocument();
690
+ $r = $dom->loadHTML('<?xml encoding="utf-8" ?>' . $html);
691
+ if (!$r) {
692
+ return '';
693
+ }
694
+ $bodylist = $dom->getElementsByTagName('body');
695
+ // Of course it should be a single element
696
+ foreach ($bodylist as $body) {
697
+ self::process_dom_element($body, $output);
698
+ }
699
+ return $output;
700
+ }
701
+
702
+ static function process_dom_element(DOMElement $parent, &$output) {
703
+ foreach ($parent->childNodes as $node) {
704
+ if (is_a($node, 'DOMElement') && ($node->tagName != 'style')) {
705
+ if ($node->tagName== 'br') {
706
+ $output .= "\n";
707
+ continue;
708
+ }
709
+ self::process_dom_element($node, $output);
710
+
711
+ // If the containing tag was a block level tag, we add a couple of line ending
712
+ if ($node->tagName == 'p' || $node->tagName == 'div' || $node->tagName == 'td') {
713
+ // Avoid more than one blank line between elements
714
+ if ((strlen($output) >= 2) && (substr($output, -2) != "\n\n")) {
715
+ $output .= "\n\n";
716
+ }
717
+ }
718
+
719
+ if ($node->tagName == 'a') {
720
+ $output .= ' (' . $node->getAttribute('href') . ') ';
721
+ continue;
722
+ }
723
+ elseif ($node->tagName == 'img') {
724
+ $output .= $node->getAttribute('alt');
725
+ }
726
+ }
727
+ elseif (is_a($node, 'DOMText')) {
728
+ $decoded = utf8_decode($node->wholeText);
729
+ if (ctype_space($decoded)) {
730
+ // Append blank only if last character output is not blank.
731
+ if ((strlen($output) > 0) && !ctype_space(substr($output, -1))) {
732
+ $output .= ' ';
733
+ }
734
+ } else {
735
+ $output .= trim($node->wholeText);
736
+ }
737
+ }
738
+ }
739
+ }
740
+ }
741
+
742
+
743
+
744
+ class TNP_Style {
745
+
746
+ var $font_family;
747
+ var $font_size;
748
+ var $font_weight;
749
+ var $font_color;
750
+ var $background;
751
+
752
+ function echo_css($scale = 1.0) {
753
+ echo 'font-size: ', round($this->font_size * $scale), 'px;';
754
+ echo 'font-family: ', $this->font_family, ';';
755
+ echo 'font-weight: ', $this->font_weight, ';';
756
+ echo 'color: ', $this->font_color, ';';
757
+ }
758
+
759
+ }
760
+
761
+ /**
762
+ * Generate multicolumn and responsive html template for email.
763
+ * Initialize class with max columns per row and start to add cells.
764
+ */
765
+ class TNP_Composer_Grid_System {
766
+
767
+ /**
768
+ * @var TNP_Composer_Grid_Row[]
769
+ */
770
+ private $rows;
771
+
772
+ /**
773
+ * @var int
774
+ */
775
+ private $cells_per_row;
776
+
777
+ /**
778
+ * @var int
779
+ */
780
+ private $cells_counter;
781
+
782
+ /**
783
+ * TNP_Composer_Grid_System constructor.
784
+ *
785
+ * @param int $columns_per_row Max columns per row
786
+ */
787
+ public function __construct($columns_per_row) {
788
+ $this->cells_per_row = $columns_per_row;
789
+ $this->cells_counter = 0;
790
+ $this->rows = [];
791
+ }
792
+
793
+ public function __toString() {
794
+ return $this->render();
795
+ }
796
+
797
+ /**
798
+ * Add cell to grid
799
+ *
800
+ * @param TNP_Composer_Grid_Cell $cell
801
+ */
802
+ public function add_cell($cell) {
803
+
804
+ if ($this->cells_counter % $this->cells_per_row === 0) {
805
+ $this->add_row(new TNP_Composer_Grid_Row());
806
+ }
807
+
808
+ $row_idx = (int) floor($this->cells_counter / $this->cells_per_row);
809
+ $this->rows[$row_idx]->add_cell($cell);
810
+ $this->cells_counter++;
811
+ }
812
+
813
+ private function add_row($row) {
814
+ $this->rows[] = $row;
815
+ }
816
+
817
+ public function render() {
818
+
819
+ $str = '';
820
+ foreach ($this->rows as $row) {
821
+ $str .= $row->render();
822
+ }
823
+
824
+ return $str;
825
+ }
826
+
827
+ }
828
+
829
+ /**
830
+ * Class TNP_Composer_Grid_Row
831
+ */
832
+ class TNP_Composer_Grid_Row {
833
+
834
+ /**
835
+ * @var TNP_Composer_Grid_Cell[]
836
+ */
837
+ private $cells;
838
+
839
+ public function __construct(...$cells) {
840
+ if (!empty($cells)) {
841
+ foreach ($cells as $cell) {
842
+ $this->add_cell($cell);
843
+ }
844
+ }
845
+ }
846
+
847
+ /**
848
+ * @param TNP_Composer_Grid_Cell $cell
849
+ */
850
+ public function add_cell($cell) {
851
+ $this->cells[] = $cell;
852
+ }
853
+
854
+ public function render() {
855
+ $rendered_cells = '';
856
+ $column_percentage_width = round(100 / $this->cells_count(), 0, PHP_ROUND_HALF_DOWN) . '%';
857
+ foreach ($this->cells as $cell) {
858
+ $rendered_cells .= $cell->render(['width' => $column_percentage_width]);
859
+ }
860
+
861
+ $row_template = $this->get_template();
862
+
863
+ return str_replace('TNP_ROW_CONTENT_PH', $rendered_cells, $row_template);
864
+ }
865
+
866
+ private function cells_count() {
867
+ return count($this->cells);
868
+ }
869
+
870
+ private function get_template() {
871
+ return "<table border='0' cellpadding='0' cellspacing='0' width='100%'><tbody><tr><td>TNP_ROW_CONTENT_PH</td></tr></tbody></table>";
872
+ }
873
+
874
+ }
875
+
876
+ /**
877
+ * Class TNP_Composer_Grid_Cell
878
+ */
879
+ class TNP_Composer_Grid_Cell {
880
+
881
+ /**
882
+ * @var string
883
+ */
884
+ private $content;
885
+
886
+ /**
887
+ * @var array
888
+ */
889
+ public $args;
890
+
891
+ public function __construct($content = null, $args = []) {
892
+ $default_args = [
893
+ 'width' => '100%',
894
+ 'class' => '',
895
+ 'align' => 'left',
896
+ 'valign' => 'top'
897
+ ];
898
+
899
+ $this->args = array_merge($default_args, $args);
900
+
901
+ $this->content = $content ? $content : '';
902
+ }
903
+
904
+ public function add_content($content) {
905
+ $this->content .= $content;
906
+ }
907
+
908
+ public function render($args) {
909
+ $this->args = array_merge($this->args, $args);
910
+
911
+ $column_template = $this->get_template();
912
+ $column = str_replace(
913
+ [
914
+ 'TNP_ALIGN_PH',
915
+ 'TNP_VALIGN_PH',
916
+ 'TNP_WIDTH_PH',
917
+ 'TNP_CLASS_PH',
918
+ 'TNP_COLUMN_CONTENT_PH'
919
+ ], [
920
+ $this->args['align'],
921
+ $this->args['valign'],
922
+ $this->args['width'],
923
+ $this->args['class'],
924
+ $this->content
925
+ ], $column_template);
926
+
927
+ return $column;
928
+ }
929
+
930
+ private function get_template() {
931
+ return "<table border='0' cellpadding='0' cellspacing='0' width='TNP_WIDTH_PH' align='left' style='table-layout: fixed;' class='responsive'>
932
+ <tbody>
933
+ <tr>
934
+ <td border='0' style='padding: 20px 10px 40px;' align='TNP_ALIGN_PH' valign='TNP_VALIGN_PH' class='TNP_CLASS_PH'>
935
+ TNP_COLUMN_CONTENT_PH
936
+ </td>
937
+ </tr>
938
+ </tbody>
939
+ </table>";
940
+ }
941
+
942
+ }
943
+
944
+ class TNP_Composer_Component_Factory {
945
+
946
+ private $options;
947
+
948
+ /**
949
+ * TNP_Composer_Component_Factory constructor.
950
+ *
951
+ * @param Controller$controller
952
+ */
953
+ public function __construct($controller) {
954
+
955
+ }
956
+
957
+ function heading() {
958
+
959
+ }
960
+
961
+ function paragraph() {
962
+
963
+ }
964
+
965
+ function link() {
966
+
967
+ }
968
+
969
+ function button() {
970
+
971
+ }
972
+
973
+ function image() {
974
+
975
+ }
976
+
977
+ }
main/info.php CHANGED
@@ -1,200 +1,200 @@
1
- <?php
2
- /* @var $this Newsletter */
3
- defined('ABSPATH') || exit;
4
-
5
- include_once NEWSLETTER_INCLUDES_DIR . '/controls.php';
6
- $controls = new NewsletterControls();
7
-
8
- $current_language = $this->get_current_language();
9
-
10
- $is_all_languages = $this->is_all_languages();
11
-
12
- //if (!$is_all_languages) {
13
- // $controls->warnings[] = 'You are configuring the language "<strong>' . $current_language . '</strong>". Switch to "all languages" to see every options.';
14
- //}
15
-
16
- if (!$controls->is_action()) {
17
- $controls->data = get_option('newsletter_main');
18
- } else {
19
-
20
- if ($controls->is_action('save')) {
21
- $this->merge_options($controls->data);
22
- $this->save_options($controls->data, 'info');
23
- $controls->add_message_saved();
24
- }
25
- }
26
- ?>
27
-
28
- <div class="wrap" id="tnp-wrap">
29
-
30
- <?php include NEWSLETTER_DIR . '/tnp-header.php'; ?>
31
-
32
- <div id="tnp-heading">
33
-
34
- <h2><?php _e('Company Info', 'newsletter') ?></h2>
35
-
36
- </div>
37
- <div id="tnp-body">
38
-
39
- <form method="post" action="">
40
- <?php $controls->init(); ?>
41
-
42
- <div id="tabs">
43
-
44
- <ul>
45
- <li><a href="#tabs-general"><?php _e('General', 'newsletter') ?></a></li>
46
- <li><a href="#tabs-social"><?php _e('Social', 'newsletter') ?></a></li>
47
- </ul>
48
-
49
- <div id="tabs-general">
50
- <h3><?php _e('Header Settings', 'newsletter') ?></h3>
51
-
52
- <table class="form-table">
53
- <tr>
54
- <th>
55
- <?php _e('Logo', 'newsletter') ?><br>
56
- <?php $controls->help('https://www.thenewsletterplugin.com/documentation/newsletter-configuration#company-logo') ?>
57
- </th>
58
- <td style="cursor: pointer">
59
- <?php $controls->media('header_logo', 'medium'); ?>
60
- </td>
61
- </tr>
62
- <tr>
63
- <th><?php _e('Title', 'newsletter') ?></th>
64
- <td>
65
- <?php $controls->text('header_title', 40); ?>
66
- </td>
67
- </tr>
68
- <tr>
69
- <th><?php _e('Motto', 'newsletter') ?></th>
70
- <td>
71
- <?php $controls->text('header_sub', 40); ?>
72
- </td>
73
- </tr>
74
- </table>
75
-
76
- <h3><?php _e('Footer Settings', 'newsletter') ?></h3>
77
-
78
- <table class="form-table">
79
- <tr>
80
- <th><?php _e('Company name', 'newsletter') ?></th>
81
- <td>
82
- <?php $controls->text('footer_title', 40); ?>
83
- </td>
84
- </tr>
85
- <tr>
86
- <th><?php _e('Address', 'newsletter') ?></th>
87
- <td>
88
- <?php $controls->text('footer_contact', 40); ?>
89
- </td>
90
- </tr>
91
- <tr>
92
- <th><?php _e('Copyright or legal text', 'newsletter') ?></th>
93
- <td>
94
- <?php $controls->text('footer_legal', 40); ?>
95
- </td>
96
- </tr>
97
- </table>
98
- </div>
99
-
100
- <div id="tabs-social">
101
-
102
- <table class="form-table">
103
- <tr>
104
- <th>Facebook URL</th>
105
- <td>
106
- <?php $controls->text('facebook_url', 40); ?>
107
- </td>
108
- </tr>
109
- <tr>
110
- <th>Twitter URL</th>
111
- <td>
112
- <?php $controls->text('twitter_url', 40); ?>
113
- </td>
114
- </tr>
115
- <tr>
116
- <th>Instagram URL</th>
117
- <td>
118
- <?php $controls->text('instagram_url', 40); ?>
119
- </td>
120
- </tr>
121
- <tr>
122
- <th>Pinterest URL</th>
123
- <td>
124
- <?php $controls->text('pinterest_url', 40); ?>
125
- </td>
126
- </tr>
127
- <tr>
128
- <th>Linkedin URL</th>
129
- <td>
130
- <?php $controls->text('linkedin_url', 40); ?>
131
- </td>
132
- </tr>
133
- <tr>
134
- <th>Tumblr URL</th>
135
- <td>
136
- <?php $controls->text('tumblr_url', 40); ?>
137
- </td>
138
- </tr>
139
- <tr>
140
- <th>YouTube URL</th>
141
- <td>
142
- <?php $controls->text('youtube_url', 40); ?>
143
- </td>
144
- </tr>
145
- <tr>
146
- <th>Vimeo URL</th>
147
- <td>
148
- <?php $controls->text('vimeo_url', 40); ?>
149
- </td>
150
- </tr>
151
- <tr>
152
- <th>Soundcloud URL</th>
153
- <td>
154
- <?php $controls->text('soundcloud_url', 40); ?>
155
- </td>
156
- </tr>
157
- <tr>
158
- <th>Telegram URL</th>
159
- <td>
160
- <?php $controls->text('telegram_url', 40); ?>
161
- </td>
162
- </tr>
163
- <tr>
164
- <th>VK URL</th>
165
- <td>
166
- <?php $controls->text('vk_url', 40); ?>
167
- </td>
168
- </tr>
169
- <tr>
170
- <th>Twitch</th>
171
- <td>
172
- <?php $controls->text('twitch_url', 40); ?>
173
- </td>
174
- </tr>
175
- <tr>
176
- <th>Discord</th>
177
- <td>
178
- <?php $controls->text('discord_url', 40); ?>
179
- </td>
180
- </tr>
181
- <tr>
182
- <th>TikTok</th>
183
- <td>
184
- <?php $controls->text('tiktok_url', 40); ?>
185
- </td>
186
- </tr>
187
- </table>
188
- </div>
189
- </div>
190
-
191
- <div class="tnp-buttons">
192
- <?php $controls->button_save(); ?>
193
- </div>
194
-
195
- </form>
196
- </div>
197
-
198
- <?php include NEWSLETTER_DIR . '/tnp-footer.php'; ?>
199
-
200
- </div>
1
+ <?php
2
+ /* @var $this Newsletter */
3
+ defined('ABSPATH') || exit;
4
+
5
+ include_once NEWSLETTER_INCLUDES_DIR . '/controls.php';
6
+ $controls = new NewsletterControls();
7
+
8
+ $current_language = $this->get_current_language();
9
+
10
+ $is_all_languages = $this->is_all_languages();
11
+
12
+ //if (!$is_all_languages) {
13
+ // $controls->warnings[] = 'You are configuring the language "<strong>' . $current_language . '</strong>". Switch to "all languages" to see every options.';
14
+ //}
15
+
16
+ if (!$controls->is_action()) {
17
+ $controls->data = get_option('newsletter_main');
18
+ } else {
19
+
20
+ if ($controls->is_action('save')) {
21
+ $this->merge_options($controls->data);
22
+ $this->save_options($controls->data, 'info');
23
+ $controls->add_message_saved();
24
+ }
25
+ }
26
+ ?>
27
+
28
+ <div class="wrap" id="tnp-wrap">
29
+
30
+ <?php include NEWSLETTER_DIR . '/tnp-header.php'; ?>
31
+
32
+ <div id="tnp-heading">
33
+
34
+ <h2><?php _e('Company Info', 'newsletter') ?></h2>
35
+
36
+ </div>
37
+ <div id="tnp-body">
38
+
39
+ <form method="post" action="">
40
+ <?php $controls->init(); ?>
41
+
42
+ <div id="tabs">
43
+
44
+ <ul>
45
+ <li><a href="#tabs-general"><?php _e('General', 'newsletter') ?></a></li>
46
+ <li><a href="#tabs-social"><?php _e('Social', 'newsletter') ?></a></li>
47
+ </ul>
48
+
49
+ <div id="tabs-general">
50
+ <h3><?php _e('Header Settings', 'newsletter') ?></h3>
51
+
52
+ <table class="form-table">
53
+ <tr>
54
+ <th>
55
+ <?php _e('Logo', 'newsletter') ?><br>
56
+ <?php $controls->help('https://www.thenewsletterplugin.com/documentation/newsletter-configuration#company-logo') ?>
57
+ </th>
58
+ <td style="cursor: pointer">
59
+ <?php $controls->media('header_logo', 'medium'); ?>
60
+ </td>
61
+ </tr>
62
+ <tr>
63
+ <th><?php _e('Title', 'newsletter') ?></th>
64
+ <td>
65
+ <?php $controls->text('header_title', 40); ?>
66
+ </td>
67
+ </tr>
68
+ <tr>
69
+ <th><?php _e('Motto', 'newsletter') ?></th>
70
+ <td>
71
+ <?php $controls->text('header_sub', 40); ?>
72
+ </td>
73
+ </tr>
74
+ </table>
75
+
76
+ <h3><?php _e('Footer Settings', 'newsletter') ?></h3>
77
+
78
+ <table class="form-table">
79
+ <tr>
80
+ <th><?php _e('Company name', 'newsletter') ?></th>
81
+ <td>
82
+ <?php $controls->text('footer_title', 40); ?>
83
+ </td>
84
+ </tr>
85
+ <tr>
86
+ <th><?php _e('Address', 'newsletter') ?></th>
87
+ <td>
88
+ <?php $controls->text('footer_contact', 40); ?>
89
+ </td>
90
+ </tr>
91
+ <tr>
92
+ <th><?php _e('Copyright or legal text', 'newsletter') ?></th>
93
+ <td>
94
+ <?php $controls->text('footer_legal', 40); ?>
95
+ </td>
96
+ </tr>
97
+ </table>
98
+ </div>
99
+
100
+ <div id="tabs-social">
101
+
102
+ <table class="form-table">
103
+ <tr>
104
+ <th>Facebook URL</th>
105
+ <td>
106
+ <?php $controls->text('facebook_url', 40); ?>
107
+ </td>
108
+ </tr>
109
+ <tr>
110
+ <th>Twitter URL</th>
111
+ <td>
112
+ <?php $controls->text('twitter_url', 40); ?>
113
+ </td>
114
+ </tr>
115
+ <tr>
116
+ <th>Instagram URL</th>
117
+ <td>
118
+ <?php $controls->text('instagram_url', 40); ?>
119
+ </td>
120
+ </tr>
121
+ <tr>
122
+ <th>Pinterest URL</th>
123
+ <td>
124
+ <?php $controls->text('pinterest_url', 40); ?>
125
+ </td>
126
+ </tr>
127
+ <tr>
128
+ <th>Linkedin URL</th>
129
+ <td>
130
+ <?php $controls->text('linkedin_url', 40); ?>
131
+ </td>
132
+ </tr>
133
+ <tr>
134
+ <th>Tumblr URL</th>
135
+ <td>
136
+ <?php $controls->text('tumblr_url', 40); ?>
137
+ </td>
138
+ </tr>
139
+ <tr>
140
+ <th>YouTube URL</th>
141
+ <td>
142
+ <?php $controls->text('youtube_url', 40); ?>
143
+ </td>
144
+ </tr>
145
+ <tr>
146
+ <th>Vimeo URL</th>
147
+ <td>
148
+ <?php $controls->text('vimeo_url', 40); ?>
149
+ </td>
150
+ </tr>
151
+ <tr>
152
+ <th>Soundcloud URL</th>
153
+ <td>
154
+ <?php $controls->text('soundcloud_url', 40); ?>
155
+ </td>
156
+ </tr>
157
+ <tr>
158
+ <th>Telegram URL</th>
159
+ <td>
160
+ <?php $controls->text('telegram_url', 40); ?>
161
+ </td>
162
+ </tr>
163
+ <tr>
164
+ <th>VK URL</th>
165
+ <td>
166
+ <?php $controls->text('vk_url', 40); ?>
167
+ </td>
168
+ </tr>
169
+ <tr>
170
+ <th>Twitch</th>
171
+ <td>
172
+ <?php $controls->text('twitch_url', 40); ?>
173
+ </td>
174
+ </tr>
175
+ <tr>
176
+ <th>Discord</th>
177
+ <td>
178
+ <?php $controls->text('discord_url', 40); ?>
179
+ </td>
180
+ </tr>
181
+ <tr>
182
+ <th>TikTok</th>
183
+ <td>
184
+ <?php $controls->text('tiktok_url', 40); ?>
185
+ </td>
186
+ </tr>
187
+ </table>
188
+ </div>
189
+ </div>
190
+
191
+ <div class="tnp-buttons">
192
+ <?php $controls->button_save(); ?>
193
+ </div>
194
+
195
+ </form>
196
+ </div>
197
+
198
+ <?php include NEWSLETTER_DIR . '/tnp-footer.php'; ?>
199
+
200
+ </div>
main/main.php CHANGED
@@ -1,388 +1,388 @@
1
- <?php
2
- /* @var $this Newsletter */
3
- defined('ABSPATH') || exit;
4
-
5
- include_once NEWSLETTER_INCLUDES_DIR . '/controls.php';
6
- $controls = new NewsletterControls();
7
-
8
- if (!$controls->is_action()) {
9
- $controls->data = get_option('newsletter_main');
10
- if (!isset($controls->data['roles'])) {
11
- $controls->data['roles'] = array();
12
- if (!empty($controls->data['editor']))
13
- $controls->data['roles'] = 'editor';
14
- }
15
- } else {
16
-
17
- if ($controls->is_action('save')) {
18
- $errors = null;
19
-
20
- if (!isset($controls->data['roles']))
21
- $controls->data['roles'] = array();
22
-
23
- // Validation
24
- $controls->data['sender_email'] = $this->normalize_email($controls->data['sender_email']);
25
- if (!$this->is_email($controls->data['sender_email'])) {
26
- $controls->errors .= __('The sender email address is not correct.', 'newsletter') . '<br>';
27
- } else {
28
- $controls->data['sender_email'] = $this->normalize_email($controls->data['sender_email']);
29
- }
30
-
31
- if (!$this->is_email($controls->data['return_path'], true)) {
32
- $controls->errors .= __('Return path email is not correct.', 'newsletter') . '<br>';
33
- } else {
34
- $controls->data['return_path'] = $this->normalize_email($controls->data['return_path']);
35
- }
36
-
37
- $controls->data['scheduler_max'] = (int) $controls->data['scheduler_max'];
38
- if ($controls->data['scheduler_max'] < 12)
39
- $controls->data['scheduler_max'] = 12;
40
-
41
-
42
- if (!$this->is_email($controls->data['reply_to'], true)) {
43
- $controls->errors .= __('Reply to email is not correct.', 'newsletter') . '<br>';
44
- } else {
45
- $controls->data['reply_to'] = $this->normalize_email($controls->data['reply_to']);
46
- }
47
-
48
- if (!empty($controls->data['contract_key'])) {
49
- $controls->data['contract_key'] = trim($controls->data['contract_key']);
50
- }
51
-
52
- if (empty($controls->errors)) {
53
- $this->merge_options($controls->data);
54
- $controls->add_message_saved();
55
- $this->logger->debug('Main options saved');
56
- }
57
-
58
- update_option('newsletter_log_level', $controls->data['log_level']);
59
-
60
- //$this->hook_newsletter_extension_versions(true);
61
- delete_transient("tnp_extensions_json");
62
- delete_transient('newsletter_license_data');
63
- }
64
-
65
- if ($controls->is_action('create')) {
66
- $page = array();
67
- $page['post_title'] = 'Newsletter';
68
- $page['post_content'] = '[newsletter]';
69
- $page['post_status'] = 'publish';
70
- $page['post_type'] = 'page';
71
- $page['comment_status'] = 'closed';
72
- $page['ping_status'] = 'closed';
73
- $page['post_category'] = array(1);
74
-
75
- $current_language = $this->get_current_language();
76
- $this->switch_language('');
77
- // Insert the post into the database
78
- $page_id = wp_insert_post($page);
79
- $this->switch_language($current_language);
80
-
81
- $controls->data['page'] = $page_id;
82
- $this->merge_options($controls->data);
83
-
84
- $controls->messages = 'A new page has been created';
85
- }
86
- }
87
-
88
- $license_data = $this->get_license_data(true);
89
-
90
- if (is_wp_error($license_data)) {
91
- $controls->errors .= esc_html('[' . $license_data->get_error_code()) . '] - ' . esc_html($license_data->get_error_message());
92
- } else {
93
- if ($license_data !== false) {
94
- if ($license_data->expire == 0) {
95
- $controls->messages = 'Your FREE license is valid';
96
- } elseif ($license_data->expire >= time()) {
97
- $controls->messages = 'Your license is valid and expires on ' . esc_html(date('Y-m-d', $license_data->expire));
98
- } else {
99
- $controls->errors = 'Your license is expired on ' . esc_html(date('Y-m-d', $license_data->expire));
100
- }
101
- }
102
- }
103
-
104
- $return_path = $this->options['return_path'];
105
-
106
- if (!empty($return_path)) {
107
- list($return_path_local, $return_path_domain) = explode('@', $return_path);
108
-
109
- $sender = $this->options['sender_email'];
110
- list($sender_local, $sender_domain) = explode('@', $sender);
111
-
112
- if ($sender_domain != $return_path_domain) {
113
- $controls->warnings[] = __('Your Return Path domain is different from your Sender domain. Providers may require them to match.', 'newsletter');
114
- }
115
- }
116
- ?>
117
-
118
- <?php include NEWSLETTER_INCLUDES_DIR . '/codemirror.php'; ?>
119
- <style>
120
- .CodeMirror {
121
- border: 1px solid #ddd;
122
- }
123
- </style>
124
-
125
- <script>
126
- jQuery(function () {
127
- var editor = CodeMirror.fromTextArea(document.getElementById("options-css"), {
128
- lineNumbers: true,
129
- mode: 'css',
130
- extraKeys: {"Ctrl-Space": "autocomplete"}
131
- });
132
- });
133
- </script>
134
-
135
- <div class="wrap" id="tnp-wrap">
136
-
137
- <?php include NEWSLETTER_DIR . '/tnp-header.php'; ?>
138
-
139
- <div id="tnp-heading">
140
-
141
- <h2><?php _e('General Settings', 'newsletter') ?></h2>
142
-
143
- </div>
144
- <div id="tnp-body" class="tnp-main-main">
145
-
146
-
147
- <form method="post" action="">
148
- <?php $controls->init(); ?>
149
-
150
- <div id="tabs">
151
-
152
- <ul>
153
- <li><a href="#tabs-basic"><?php _e('Basic Settings', 'newsletter') ?></a></li>
154
- <li><a href="#tabs-speed"><?php _e('Delivery Speed', 'newsletter') ?></a></li>
155
- <li><a href="#tabs-advanced"><?php _e('Advanced Settings', 'newsletter') ?></a></li>
156
- </ul>
157
-
158
- <div id="tabs-basic">
159
-
160
- <p>
161
- <?php $controls->panel_help('https://www.thenewsletterplugin.com/plugins/newsletter/newsletter-configuration') ?>
162
- </p>
163
-
164
-
165
- <table class="form-table">
166
-
167
- <tr>
168
- <th>
169
- <?php _e('Sender email address', 'newsletter') ?>
170
- <?php $controls->field_help('https://www.thenewsletterplugin.com/documentation/installation/newsletter-configuration/#sender') ?>
171
- </th>
172
- <td>
173
- <?php $controls->text_email('sender_email', 40); ?>
174
- <div class="description">
175
- <a href="https://www.thenewsletterplugin.com/documentation/installation/newsletter-configuration/#sender" target="_blank">Emails delivered with a different address?</a>
176
- </div>
177
- </td>
178
- </tr>
179
- <tr>
180
- <th>
181
- <?php _e('Sender name', 'newsletter') ?>
182
- </th>
183
- <td>
184
- <?php $controls->text('sender_name', 40); ?>
185
- </td>
186
- </tr>
187
-
188
- <tr>
189
- <th>
190
- <?php _e('Return path', 'newsletter') ?>
191
- <?php $controls->field_help('https://www.thenewsletterplugin.com/documentation/installation/newsletter-configuration/#return-path') ?>
192
- </th>
193
- <td>
194
- <?php $controls->text_email('return_path', 40); ?>
195
-
196
- </td>
197
- </tr>
198
- <tr>
199
- <th>
200
- <?php _e('Reply to', 'newsletter') ?>
201
- <?php $controls->field_help('https://www.thenewsletterplugin.com/documentation/installation/newsletter-configuration/#reply-to') ?>
202
- </th>
203
- <td>
204
- <?php $controls->text_email('reply_to', 40); ?>
205
-
206
- </td>
207
- </tr>
208
- <tr>
209
- <th>
210
- <?php _e('Dedicated page', 'newsletter') ?>
211
- <?php $controls->field_help('https://www.thenewsletterplugin.com/documentation/installation/newsletter-configuration/#dedicated-page') ?>
212
- </th>
213
- <td>
214
- <?php $controls->page('page', __('Unstyled page', 'newsletter'), '', true); ?>
215
- <?php
216
- if (empty($controls->data['page'])) {
217
- $controls->button('create', __('Create the page', 'newsletter'));
218
- }
219
- ?>
220
- <?php if ($this->is_multilanguage()) { ?>
221
- <p class="description">
222
- With multilanguage plugins be sure the selected page is translated in every language (usually only the title needs to be translated, the content
223
- should just be the <code>[newsletter]</code> shortcode).
224
- </p>
225
- <?php } ?>
226
- </td>
227
- </tr>
228
-
229
- <tr>
230
- <th><?php _e('License key', 'newsletter') ?></th>
231
- <td>
232
- <?php if (defined('NEWSLETTER_LICENSE_KEY')) { ?>
233
- <?php _e('A license key is set', 'newsletter') ?>
234
- <?php } else { ?>
235
- <?php $controls->text('contract_key', 40); ?>
236
- <p class="description">
237
- <?php printf(__('Find it in <a href="%s" target="_blank">your account</a> page', 'newsletter'), "https://www.thenewsletterplugin.com/account") ?>
238
- </p>
239
- <?php } ?>
240
- </td>
241
- </tr>
242
-
243
- </table>
244
- </div>
245
-
246
- <div id="tabs-speed">
247
-
248
- <table class="form-table">
249
- <tr>
250
- <th>
251
- <?php _e('Max emails per hour', 'newsletter') ?>
252
- <?php $controls->field_help('https://www.thenewsletterplugin.com/documentation/installation/newsletter-configuration/#speed') ?>
253
- </th>
254
- <td>
255
- <?php $controls->text('scheduler_max', 5); ?> (min. 10)
256
- <p class="description">
257
- <a href="?page=newsletter_system_status#tnp-speed">See the collected statistics</a>
258
- </td>
259
- </tr>
260
- </table>
261
-
262
- <?php do_action('newsletter_panel_main_speed', $controls) ?>
263
- </div>
264
-
265
-
266
- <div id="tabs-advanced">
267
-
268
- <p>
269
- <?php $controls->panel_help('https://www.thenewsletterplugin.com/plugins/newsletter/newsletter-configuration#advanced') ?>
270
- </p>
271
-
272
- <table class="form-table">
273
- <tr>
274
- <th><?php _e('Allowed roles', 'newsletter') ?></th>
275
- <td>
276
- <?php
277
- $wp_roles = get_editable_roles();
278
- $roles = array();
279
- foreach ($wp_roles as $key => $wp_role) {
280
- if ($key == 'administrator')
281
- continue;
282
- if ($key == 'subscriber')
283
- continue;
284
- $roles[$key] = $wp_role['name'];
285
- }
286
- $controls->checkboxes('roles', $roles);
287
- ?>
288
-
289
- </td>
290
- </tr>
291
-
292
- <tr>
293
- <th>
294
- <?php _e('Tracking default', 'newsletter') ?>
295
- <?php $controls->field_help('https://www.thenewsletterplugin.com/plugins/newsletter/newsletter-configuration#tracking') ?>
296
- </th>
297
- <td>
298
- <?php $controls->yesno('track'); ?>
299
- </td>
300
- </tr>
301
-
302
- <tr>
303
- <th>
304
- <?php _e('Execute shortcodes on newsletters', 'newsletter') ?>
305
- <?php $controls->field_help("https://www.thenewsletterplugin.com/documentation/newsletter-configuration#shortcodes") ?>
306
- </th>
307
- <td>
308
- <?php $controls->yesno('do_shortcodes', 40); ?>
309
- </td>
310
- </tr>
311
-
312
- <tr>
313
- <th>
314
- <?php _e('Log level', 'newsletter') ?>
315
- <?php $controls->field_help('https://www.thenewsletterplugin.com/plugins/newsletter/newsletter-configuration#log') ?>
316
- </th>
317
- <td>
318
- <?php $controls->log_level('log_level'); ?>
319
- </td>
320
- </tr>
321
-
322
- <tr>
323
- <th><?php _e('Standard styles', 'newsletter') ?></th>
324
- <td>
325
- <?php $controls->disabled('css_disabled'); ?>
326
- </td>
327
- </tr>
328
-
329
- <tr>
330
- <th><?php _e('Custom styles', 'newsletter') ?></th>
331
- <td>
332
- <?php if (apply_filters('newsletter_enqueue_style', true) === false) { ?>
333
- <p><strong>Warning: Newsletter styles and custom styles are disable by your theme or a plugin.</strong></p>
334
- <?php } ?>
335
- <?php $controls->textarea('css'); ?>
336
- </td>
337
- </tr>
338
-
339
-
340
- <tr>
341
- <th><?php _e('IP addresses', 'newsletter') ?></th>
342
- <td>
343
- <?php $controls->select('ip', array('' => __('Store', 'newsletter'), 'anonymize' => __('Anonymize', 'newsletter'), 'skip' => __('Do not store', 'newsletter'))); ?>
344
- </td>
345
- </tr>
346
-
347
-
348
-
349
- <tr>
350
- <th>
351
- <?php _e('Debug mode', 'newsletter') ?>
352
- <?php $controls->field_help('https://www.thenewsletterplugin.com/plugins/newsletter/newsletter-configuration#debug') ?>
353
- </th>
354
- <td>
355
- <?php $controls->yesno('debug', 40); ?>
356
- </td>
357
- </tr>
358
-
359
- <tr>
360
- <th>
361
- <?php _e('Email encoding', 'newsletter') ?>
362
- <?php $controls->field_help('https://www.thenewsletterplugin.com/plugins/newsletter/newsletter-configuration#encoding') ?>
363
- </th>
364
- <td>
365
- <?php $controls->select('content_transfer_encoding', array('' => 'Default', '8bit' => '8 bit', 'base64' => 'Base 64', 'binary' => 'Binary', 'quoted-printable' => 'Quoted printable', '7bit' => '7 bit')); ?>
366
- </td>
367
- </tr>
368
-
369
-
370
- </table>
371
-
372
- </div>
373
-
374
-
375
- </div> <!-- tabs -->
376
-
377
- <div class="tnp-buttons">
378
- <?php $controls->button_save(); ?>
379
- </div>
380
-
381
- </form>
382
-
383
- </div>
384
-
385
- <?php include NEWSLETTER_DIR . '/tnp-footer.php'; ?>
386
-
387
- </div>
388
-
1
+ <?php
2
+ /* @var $this Newsletter */
3
+ defined('ABSPATH') || exit;
4
+
5
+ include_once NEWSLETTER_INCLUDES_DIR . '/controls.php';
6
+ $controls = new NewsletterControls();
7
+
8
+ if (!$controls->is_action()) {
9
+ $controls->data = get_option('newsletter_main');
10
+ if (!isset($controls->data['roles'])) {
11
+ $controls->data['roles'] = array();
12
+ if (!empty($controls->data['editor']))
13
+ $controls->data['roles'] = 'editor';
14
+ }
15
+ } else {
16
+
17
+ if ($controls->is_action('save')) {
18
+ $errors = null;
19
+
20
+ if (!isset($controls->data['roles']))
21
+ $controls->data['roles'] = array();
22
+
23
+ // Validation
24
+ $controls->data['sender_email'] = $this->normalize_email($controls->data['sender_email']);
25
+ if (!$this->is_email($controls->data['sender_email'])) {
26
+ $controls->errors .= __('The sender email address is not correct.', 'newsletter') . '<br>';
27
+ } else {
28
+ $controls->data['sender_email'] = $this->normalize_email($controls->data['sender_email']);
29
+ }
30
+
31
+ if (!$this->is_email($controls->data['return_path'], true)) {
32
+ $controls->errors .= __('Return path email is not correct.', 'newsletter') . '<br>';
33
+ } else {
34
+ $controls->data['return_path'] = $this->normalize_email($controls->data['return_path']);
35
+ }
36
+
37
+ $controls->data['scheduler_max'] = (int) $controls->data['scheduler_max'];
38
+ if ($controls->data['scheduler_max'] < 12)
39
+ $controls->data['scheduler_max'] = 12;
40
+
41
+
42
+ if (!$this->is_email($controls->data['reply_to'], true)) {
43
+ $controls->errors .= __('Reply to email is not correct.', 'newsletter') . '<br>';
44
+ } else {
45
+ $controls->data['reply_to'] = $this->normalize_email($controls->data['reply_to']);
46
+ }
47
+
48
+ if (!empty($controls->data['contract_key'])) {
49
+ $controls->data['contract_key'] = trim($controls->data['contract_key']);
50
+ }
51
+
52
+ if (empty($controls->errors)) {
53
+ $this->merge_options($controls->data);
54
+ $controls->add_message_saved();
55
+ $this->logger->debug('Main options saved');
56
+ }
57
+
58
+ update_option('newsletter_log_level', $controls->data['log_level']);
59
+
60
+ //$this->hook_newsletter_extension_versions(true);
61
+ delete_transient("tnp_extensions_json");
62
+ delete_transient('newsletter_license_data');
63
+ }
64
+
65
+ if ($controls->is_action('create')) {
66
+ $page = array();
67
+ $page['post_title'] = 'Newsletter';
68
+ $page['post_content'] = '[newsletter]';
69
+ $page['post_status'] = 'publish';
70
+ $page['post_type'] = 'page';
71
+ $page['comment_status'] = 'closed';
72
+ $page['ping_status'] = 'closed';
73
+ $page['post_category'] = array(1);
74
+
75
+ $current_language = $this->get_current_language();
76
+ $this->switch_language('');
77
+ // Insert the post into the database
78
+ $page_id = wp_insert_post($page);
79
+ $this->switch_language($current_language);
80
+
81
+ $controls->data['page'] = $page_id;
82
+ $this->merge_options($controls->data);
83
+
84
+ $controls->messages = 'A new page has been created';
85
+ }
86
+ }
87
+
88
+ $license_data = $this->get_license_data(true);
89
+
90
+ if (is_wp_error($license_data)) {
91
+ $controls->errors .= esc_html('[' . $license_data->get_error_code()) . '] - ' . esc_html($license_data->get_error_message());
92
+ } else {
93
+ if ($license_data !== false) {
94
+ if ($license_data->expire == 0) {
95
+ $controls->messages = 'Your FREE license is valid';
96
+ } elseif ($license_data->expire >= time()) {
97
+ $controls->messages = 'Your license is valid and expires on ' . esc_html(date('Y-m-d', $license_data->expire));
98
+ } else {
99
+ $controls->errors = 'Your license is expired on ' . esc_html(date('Y-m-d', $license_data->expire));
100
+ }
101
+ }
102
+ }
103
+
104
+ $return_path = $this->options['return_path'];
105
+
106
+ if (!empty($return_path)) {
107
+ list($return_path_local, $return_path_domain) = explode('@', $return_path);
108
+
109
+ $sender = $this->options['sender_email'];
110
+ list($sender_local, $sender_domain) = explode('@', $sender);
111
+
112
+ if ($sender_domain != $return_path_domain) {
113
+ $controls->warnings[] = __('Your Return Path domain is different from your Sender domain. Providers may require them to match.', 'newsletter');
114
+ }
115
+ }
116
+ ?>
117
+
118
+ <?php include NEWSLETTER_INCLUDES_DIR . '/codemirror.php'; ?>
119
+ <style>
120
+ .CodeMirror {
121
+ border: 1px solid #ddd;
122
+ }
123
+ </style>
124
+
125
+ <script>
126
+ jQuery(function () {
127
+ var editor = CodeMirror.fromTextArea(document.getElementById("options-css"), {
128
+ lineNumbers: true,
129
+ mode: 'css',
130
+ extraKeys: {"Ctrl-Space": "autocomplete"}
131
+ });
132
+ });
133
+ </script>
134
+
135
+ <div class="wrap" id="tnp-wrap">
136
+
137
+ <?php include NEWSLETTER_DIR . '/tnp-header.php'; ?>
138
+
139
+ <div id="tnp-heading">
140
+
141
+ <h2><?php _e('General Settings', 'newsletter') ?></h2>
142
+
143
+ </div>
144
+ <div id="tnp-body" class="tnp-main-main">
145
+
146
+
147
+ <form method="post" action="">
148
+ <?php $controls->init(); ?>
149
+
150
+ <div id="tabs">
151
+
152
+ <ul>
153
+ <li><a href="#tabs-basic"><?php _e('Basic Settings', 'newsletter') ?></a></li>
154
+ <li><a href="#tabs-speed"><?php _e('Delivery Speed', 'newsletter') ?></a></li>
155
+ <li><a href="#tabs-advanced"><?php _e('Advanced Settings', 'newsletter') ?></a></li>
156
+ </ul>
157
+
158
+ <div id="tabs-basic">
159
+
160
+ <p>
161
+ <?php $controls->panel_help('https://www.thenewsletterplugin.com/plugins/newsletter/newsletter-configuration') ?>
162
+ </p>
163
+
164
+
165
+ <table class="form-table">
166
+
167
+ <tr>
168
+ <th>
169
+ <?php _e('Sender email address', 'newsletter') ?>
170
+ <?php $controls->field_help('https://www.thenewsletterplugin.com/documentation/installation/newsletter-configuration/#sender') ?>
171
+ </th>
172
+ <td>
173
+ <?php $controls->text_email('sender_email', 40); ?>
174
+ <div class="description">
175
+ <a href="https://www.thenewsletterplugin.com/documentation/installation/newsletter-configuration/#sender" target="_blank">Emails delivered with a different address?</a>
176
+ </div>
177
+ </td>
178
+ </tr>
179
+ <tr>
180
+ <th>
181
+ <?php _e('Sender name', 'newsletter') ?>
182
+ </th>
183
+ <td>
184
+ <?php $controls->text('sender_name', 40); ?>
185
+ </td>
186
+ </tr>
187
+
188
+ <tr>
189
+ <th>
190
+ <?php _e('Return path', 'newsletter') ?>
191
+ <?php $controls->field_help('https://www.thenewsletterplugin.com/documentation/installation/newsletter-configuration/#return-path') ?>
192
+ </th>
193
+ <td>
194
+ <?php $controls->text_email('return_path', 40); ?>
195
+
196
+ </td>
197
+ </tr>
198
+ <tr>
199
+ <th>
200
+ <?php _e('Reply to', 'newsletter') ?>
201
+ <?php $controls->field_help('https://www.thenewsletterplugin.com/documentation/installation/newsletter-configuration/#reply-to') ?>
202
+ </th>
203
+ <td>
204
+ <?php $controls->text_email('reply_to', 40); ?>
205
+
206
+ </td>
207
+ </tr>
208
+ <tr>
209
+ <th>
210
+ <?php _e('Dedicated page', 'newsletter') ?>
211
+ <?php $controls->field_help('https://www.thenewsletterplugin.com/documentation/installation/newsletter-configuration/#dedicated-page') ?>
212
+ </th>
213
+ <td>
214
+ <?php $controls->page('page', __('Unstyled page', 'newsletter'), '', true); ?>
215
+ <?php
216
+ if (empty($controls->data['page'])) {
217
+ $controls->button('create', __('Create the page', 'newsletter'));
218
+ }
219
+ ?>
220
+ <?php if ($this->is_multilanguage()) { ?>
221
+ <p class="description">
222
+ With multilanguage plugins be sure the selected page is translated in every language (usually only the title needs to be translated, the content
223
+ should just be the <code>[newsletter]</code> shortcode).
224
+ </p>
225
+ <?php } ?>
226
+ </td>
227
+ </tr>
228
+
229
+ <tr>
230
+ <th><?php _e('License key', 'newsletter') ?></th>
231
+ <td>
232
+ <?php if (defined('NEWSLETTER_LICENSE_KEY')) { ?>
233
+ <?php _e('A license key is set', 'newsletter') ?>
234
+ <?php } else { ?>
235
+ <?php $controls->text('contract_key', 40); ?>
236
+ <p class="description">
237
+ <?php printf(__('Find it in <a href="%s" target="_blank">your account</a> page', 'newsletter'), "https://www.thenewsletterplugin.com/account") ?>
238
+ </p>
239
+ <?php } ?>
240
+ </td>
241
+ </tr>
242
+
243
+ </table>
244
+ </div>
245
+
246
+ <div id="tabs-speed">
247
+
248
+ <table class="form-table">
249
+ <tr>
250
+ <th>
251
+ <?php _e('Max emails per hour', 'newsletter') ?>
252
+ <?php $controls->field_help('https://www.thenewsletterplugin.com/documentation/installation/newsletter-configuration/#speed') ?>
253
+ </th>
254
+ <td>
255
+ <?php $controls->text('scheduler_max', 5); ?> (min. 10)
256
+ <p class="description">
257
+ <a href="?page=newsletter_system_status#tnp-speed">See the collected statistics</a>
258
+ </td>
259
+ </tr>
260
+ </table>
261
+
262
+ <?php do_action('newsletter_panel_main_speed', $controls) ?>
263
+ </div>
264
+
265
+
266
+ <div id="tabs-advanced">
267
+
268
+ <p>
269
+ <?php $controls->panel_help('https://www.thenewsletterplugin.com/plugins/newsletter/newsletter-configuration#advanced') ?>
270
+ </p>
271
+
272
+ <table class="form-table">
273
+ <tr>
274
+ <th><?php _e('Allowed roles', 'newsletter') ?></th>
275
+ <td>
276
+ <?php
277
+ $wp_roles = get_editable_roles();
278
+ $roles = array();
279
+ foreach ($wp_roles as $key => $wp_role) {
280
+ if ($key == 'administrator')
281
+ continue;
282
+ if ($key == 'subscriber')
283
+ continue;
284
+ $roles[$key] = $wp_role['name'];
285
+ }
286
+ $controls->checkboxes('roles', $roles);
287
+ ?>
288
+
289
+ </td>
290
+ </tr>
291
+
292
+ <tr>
293
+ <th>
294
+ <?php _e('Tracking default', 'newsletter') ?>
295
+ <?php $controls->field_help('https://www.thenewsletterplugin.com/plugins/newsletter/newsletter-configuration#tracking') ?>
296
+ </th>
297
+ <td>
298
+ <?php $controls->yesno('track'); ?>
299
+ </td>
300
+ </tr>
301
+
302
+ <tr>
303
+ <th>
304
+ <?php _e('Execute shortcodes on newsletters', 'newsletter') ?>
305
+ <?php $controls->field_help("https://www.thenewsletterplugin.com/documentation/newsletter-configuration#shortcodes") ?>
306
+ </th>
307
+ <td>
308
+ <?php $controls->yesno('do_shortcodes', 40); ?>
309
+ </td>
310
+ </tr>
311
+
312
+ <tr>
313
+ <th>
314
+ <?php _e('Log level', 'newsletter') ?>
315
+ <?php $controls->field_help('https://www.thenewsletterplugin.com/plugins/newsletter/newsletter-configuration#log') ?>
316
+ </th>
317
+ <td>
318
+ <?php $controls->log_level('log_level'); ?>
319
+ </td>
320
+ </tr>
321
+
322
+ <tr>
323
+ <th><?php _e('Standard styles', 'newsletter') ?></th>
324
+ <td>
325
+ <?php $controls->disabled('css_disabled'); ?>
326
+ </td>
327
+ </tr>
328
+
329
+ <tr>
330
+ <th><?php _e('Custom styles', 'newsletter') ?></th>
331
+ <td>
332
+ <?php if (apply_filters('newsletter_enqueue_style', true) === false) { ?>
333
+ <p><strong>Warning: Newsletter styles and custom styles are disable by your theme or a plugin.</strong></p>
334
+ <?php } ?>
335
+ <?php $controls->textarea('css'); ?>
336
+ </td>
337
+ </tr>
338
+
339
+
340
+ <tr>
341
+ <th><?php _e('IP addresses', 'newsletter') ?></th>
342
+ <td>
343
+ <?php $controls->select('ip', array('' => __('Store', 'newsletter'), 'anonymize' => __('Anonymize', 'newsletter'), 'skip' => __('Do not store', 'newsletter'))); ?>
344
+ </td>
345
+ </tr>
346
+
347
+
348
+
349
+ <tr>
350
+ <th>
351
+ <?php _e('Debug mode', 'newsletter') ?>
352
+ <?php $controls->field_help('https://www.thenewsletterplugin.com/plugins/newsletter/newsletter-configuration#debug') ?>
353
+ </th>
354
+ <td>
355
+ <?php $controls->yesno('debug', 40); ?>
356
+ </td>
357
+ </tr>
358
+
359
+ <tr>
360
+ <th>
361
+ <?php _e('Email encoding', 'newsletter') ?>
362
+ <?php $controls->field_help('https://www.thenewsletterplugin.com/plugins/newsletter/newsletter-configuration#encoding') ?>
363
+ </th>
364
+ <td>
365
+ <?php $controls->select('content_transfer_encoding', array('' => 'Default', '8bit' => '8 bit', 'base64' => 'Base 64', 'binary' => 'Binary', 'quoted-printable' => 'Quoted printable', '7bit' => '7 bit')); ?>
366
+ </td>
367
+ </tr>
368
+
369
+
370
+ </table>
371
+
372
+ </div>
373
+
374
+
375
+ </div> <!-- tabs -->
376
+
377
+ <div class="tnp-buttons">
378
+ <?php $controls->button_save(); ?>
379
+ </div>
380
+
381
+ </form>
382
+
383
+ </div>
384
+
385
+ <?php include NEWSLETTER_DIR . '/tnp-footer.php'; ?>
386
+
387
+ </div>
388
+
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.4.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,7 +37,7 @@ if (version_compare(phpversion(), '5.6', '<')) {
37
  return;
38
  }
39
 
40
- define('NEWSLETTER_VERSION', '7.4.6');
41
 
42
  global $newsletter, $wpdb;
43
 
@@ -90,9 +90,7 @@ class Newsletter extends NewsletterModule {
90
  var $time_start;
91
  var $time_limit = 0;
92
  var $max_emails = null;
93
-
94
  var $mailer = null;
95
-
96
  var $action = '';
97
 
98
  /** @var Newsletter */
@@ -149,11 +147,8 @@ class Newsletter extends NewsletterModule {
149
  add_filter('display_post_states', [$this, 'add_notice_to_chosen_profile_page_hook'], 10, 2);
150
 
151
  if ($this->is_admin_page()) {
152
- // Remove the emoji replacer to save to database the original emoji characters (see even woocommerce for the same problem)
153
- remove_action('admin_print_scripts', 'print_emoji_detection_script');
154
  add_action('admin_enqueue_scripts', [$this, 'hook_admin_enqueue_scripts']);
155
  }
156
-
157
  }
158
  }
159
 
@@ -204,7 +199,7 @@ class Newsletter extends NewsletterModule {
204
 
205
  if ($this->is_admin_page()) {
206
 
207
- $dismissed = get_option('newsletter_dismissed', []);
208
 
209
  if (isset($_GET['dismiss'])) {
210
  $dismissed[$_GET['dismiss']] = 1;
@@ -502,6 +497,11 @@ class Newsletter extends NewsletterModule {
502
  delete_option('newsletter_show_welcome');
503
  wp_redirect(admin_url('admin.php?page=newsletter_main_welcome'));
504
  }
 
 
 
 
 
505
  }
506
 
507
  function hook_admin_head() {
@@ -582,7 +582,7 @@ class Newsletter extends NewsletterModule {
582
 
583
  function get_emails_per_run() {
584
  $speed = $this->get_send_speed();
585
- $max = (int)($speed / $this->get_runs_per_hour());
586
 
587
  return $max;
588
  }
@@ -644,15 +644,15 @@ class Newsletter extends NewsletterModule {
644
  */
645
  function send($email, $users = null, $test = false) {
646
  global $wpdb;
647
-
648
  if (is_array($email)) {
649
  $email = (object) $email;
650
- }
651
 
652
  $this->logger->info(__METHOD__ . '> Send email ' . $email->id);
653
 
654
  $this->send_setup();
655
-
656
  if ($this->max_emails <= 0) {
657
  $this->logger->info(__METHOD__ . '> No more capacity');
658
  return false;
@@ -744,7 +744,7 @@ class Newsletter extends NewsletterModule {
744
  return $r;
745
  }
746
  }
747
-
748
  if (!$supplied_users && !$test && $this->time_exceeded()) {
749
  $result = false;
750
  break;
@@ -794,7 +794,7 @@ class Newsletter extends NewsletterModule {
794
  return $r;
795
  }
796
  }
797
-
798
  if (!$supplied_users && !$test && $this->time_exceeded()) {
799
  $result = false;
800
  break;
@@ -818,7 +818,7 @@ class Newsletter extends NewsletterModule {
818
 
819
  return $result;
820
  }
821
-
822
  function update_send_stats($start_time, $end_time, $count, $result) {
823
  $send_calls = get_option('newsletter_diagnostic_send_calls', []);
824
  $send_calls[] = [$start_time, $end_time, $count, $result];
@@ -934,12 +934,13 @@ class Newsletter extends NewsletterModule {
934
  * @deprecated since version 6.0.0
935
  */
936
  function register_mail_method($callable) {
 
937
  }
938
 
939
  function register_mailer($mailer) {
940
  if ($mailer instanceof NewsletterMailer) {
941
  $this->mailer = $mailer;
942
- }
943
  }
944
 
945
  /**
@@ -1239,7 +1240,8 @@ class Newsletter extends NewsletterModule {
1239
  */
1240
  function get_newsletter_page() {
1241
  $page_id = $this->get_newsletter_page_id();
1242
- if (!$page_id) return false;
 
1243
  return get_post($this->get_newsletter_page_id());
1244
  }
1245
 
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.4.7
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.4.7');
41
 
42
  global $newsletter, $wpdb;
43
 
90
  var $time_start;
91
  var $time_limit = 0;
92
  var $max_emails = null;
 
93
  var $mailer = null;
 
94
  var $action = '';
95
 
96
  /** @var Newsletter */
147
  add_filter('display_post_states', [$this, 'add_notice_to_chosen_profile_page_hook'], 10, 2);
148
 
149
  if ($this->is_admin_page()) {
 
 
150
  add_action('admin_enqueue_scripts', [$this, 'hook_admin_enqueue_scripts']);
151
  }
 
152
  }
153
  }
154
 
199
 
200
  if ($this->is_admin_page()) {
201
 
202
+ $dismissed = get_option('newsletter_dismissed', []);
203
 
204
  if (isset($_GET['dismiss'])) {
205
  $dismissed[$_GET['dismiss']] = 1;
497
  delete_option('newsletter_show_welcome');
498
  wp_redirect(admin_url('admin.php?page=newsletter_main_welcome'));
499
  }
500
+
501
+ if ($this->is_admin_page()) {
502
+ // Remove the emoji replacer to save to database the original emoji characters (see even woocommerce for the same problem)
503
+ remove_action('admin_print_scripts', 'print_emoji_detection_script');
504
+ }
505
  }
506
 
507
  function hook_admin_head() {
582
 
583
  function get_emails_per_run() {
584
  $speed = $this->get_send_speed();
585
+ $max = (int) ($speed / $this->get_runs_per_hour());
586
 
587
  return $max;
588
  }
644
  */
645
  function send($email, $users = null, $test = false) {
646
  global $wpdb;
647
+
648
  if (is_array($email)) {
649
  $email = (object) $email;
650
+ }
651
 
652
  $this->logger->info(__METHOD__ . '> Send email ' . $email->id);
653
 
654
  $this->send_setup();
655
+
656
  if ($this->max_emails <= 0) {
657
  $this->logger->info(__METHOD__ . '> No more capacity');
658
  return false;
744
  return $r;
745
  }
746
  }
747
+
748
  if (!$supplied_users && !$test && $this->time_exceeded()) {
749
  $result = false;
750
  break;
794
  return $r;
795
  }
796
  }
797
+
798
  if (!$supplied_users && !$test && $this->time_exceeded()) {
799
  $result = false;
800
  break;
818
 
819
  return $result;
820
  }
821
+
822
  function update_send_stats($start_time, $end_time, $count, $result) {
823
  $send_calls = get_option('newsletter_diagnostic_send_calls', []);
824
  $send_calls[] = [$start_time, $end_time, $count, $result];
934
  * @deprecated since version 6.0.0
935
  */
936
  function register_mail_method($callable) {
937
+
938
  }
939
 
940
  function register_mailer($mailer) {
941
  if ($mailer instanceof NewsletterMailer) {
942
  $this->mailer = $mailer;
943
+ }
944
  }
945
 
946
  /**
1240
  */
1241
  function get_newsletter_page() {
1242
  $page_id = $this->get_newsletter_page_id();
1243
+ if (!$page_id)
1244
+ return false;
1245
  return get_post($this->get_newsletter_page_id());
1246
  }
1247
 
profile/index.php CHANGED
@@ -1,163 +1,163 @@
1
- <?php
2
- defined('ABSPATH') || exit;
3
-
4
- /* @var $this NewsletterProfile */
5
-
6
- require_once NEWSLETTER_INCLUDES_DIR . '/controls.php';
7
-
8
- $controls = new NewsletterControls();
9
-
10
- $current_language = $this->get_current_language();
11
-
12
- $is_all_languages = $this->is_all_languages();
13
-
14
- if (!$is_all_languages) {
15
- $controls->warnings[] = 'You are configuring the language "<strong>' . $current_language . '</strong>". Switch to "all languages" to see every options.';
16
- }
17
-
18
- // Profile options are still inside the main options
19
- if ($controls->is_action()) {
20
- if ($controls->is_action('save')) {
21
- $this->save_options($controls->data, '', null, $current_language);
22
- $controls->add_message_saved();
23
- }
24
- if ($controls->is_action('reset')) {
25
- $this->reset_options();
26
- $controls->data = $this->get_options('', $current_language);
27
- $controls->add_message_reset();
28
- }
29
- } else {
30
- $controls->data = $this->get_options('', $current_language);
31
- }
32
- ?>
33
-
34
- <div class="wrap tnp-profile tnp-profile-index" id="tnp-wrap">
35
-
36
- <?php include NEWSLETTER_DIR . '/tnp-header.php'; ?>
37
-
38
- <div id="tnp-heading">
39
-
40
- <h2><?php _e('The subscriber profile page', 'newsletter') ?></h2>
41
- <?php $controls->page_help('https://www.thenewsletterplugin.com/documentation/profile-page') ?>
42
- </div>
43
-
44
- <div id="tnp-body">
45
-
46
-
47
- <form id="channel" method="post" action="">
48
- <?php $controls->init(); ?>
49
- <div id="tabs">
50
- <ul>
51
- <li><a href="#tabs-general"><?php _e('General', 'newsletter') ?></a></li>
52
- <li><a href="#tabs-export"><?php _e('Subscriber data export', 'newsletter') ?></a></li>
53
-
54
- </ul>
55
-
56
- <div id="tabs-general">
57
-
58
-
59
- <table class="form-table">
60
-
61
- <tr>
62
- <th><?php _e('Profile page', 'newsletter') ?>
63
- <br><?php $controls->help('https://www.thenewsletterplugin.com/documentation/subscription#profile') ?>
64
- </th>
65
- <td>
66
- <?php $controls->wp_editor('text'); ?>
67
- </td>
68
- </tr>
69
-
70
- <tr>
71
- <th><?php _e('Alternative profile page URL', 'newsletter') ?></th>
72
- <td>
73
- <?php $controls->text('url', 70); ?>
74
- </td>
75
- </tr>
76
-
77
- </table>
78
-
79
- <h3><?php _e('Messages', 'newsletter') ?></h3>
80
- <table class="form-table">
81
- <tr>
82
- <th><?php _e('Profile saved', 'newsletter') ?></th>
83
- <td>
84
- <?php $controls->text('saved', 80); ?>
85
- </td>
86
- </tr>
87
-
88
- <tr>
89
- <tr>
90
- <th><?php _e('Email changed alert', 'newsletter') ?></th>
91
- <td>
92
- <?php $controls->text('email_changed', 80); ?>
93
- </td>
94
- </tr>
95
-
96
- <tr>
97
-
98
- <tr>
99
- <tr>
100
- <th><?php _e('General error', 'newsletter') ?></th>
101
- <td>
102
- <?php $controls->text('error', 80); ?>
103
- </td>
104
- </tr>
105
-
106
- </table>
107
-
108
- <h3><?php _e('Labels', 'newsletter') ?></h3>
109
- <table class="form-table">
110
- <tr>
111
- <th><?php _e('"Save" label', 'newsletter') ?></th>
112
- <td>
113
- <?php $controls->text('save_label'); ?>
114
- </td>
115
- </tr>
116
-
117
- <tr>
118
- <th><?php _e('Privacy link text', 'newsletter') ?></th>
119
- <td>
120
- <?php $controls->text('privacy_label', 80); ?>
121
- <p class="description">
122
-
123
- </p>
124
- </td>
125
- </tr>
126
-
127
- </table>
128
- </div>
129
-
130
- <div id="tabs-export">
131
- <?php if ($is_all_languages) { ?>
132
-
133
- <table class="form-table">
134
-
135
- <tr>
136
- <th>
137
- <?php _e('Log of sent newsletters', 'newsletter') ?>
138
- </th>
139
- <td>
140
- <?php $controls->yesno('export_newsletters'); ?>
141
- </td>
142
- </tr>
143
- </table>
144
- <?php } else { ?>
145
-
146
- <?php $controls->switch_to_all_languages_notice(); ?>
147
-
148
- <?php } ?>
149
- </div>
150
-
151
- </div>
152
-
153
- <p>
154
- <?php $controls->button_save() ?>
155
- <?php $controls->button_reset() ?>
156
- </p>
157
-
158
- </form>
159
- </div>
160
-
161
- <?php include NEWSLETTER_DIR . '/tnp-footer.php'; ?>
162
-
163
- </div>
1
+ <?php
2
+ defined('ABSPATH') || exit;
3
+
4
+ /* @var $this NewsletterProfile */
5
+
6
+ require_once NEWSLETTER_INCLUDES_DIR . '/controls.php';
7
+
8
+ $controls = new NewsletterControls();
9
+
10
+ $current_language = $this->get_current_language();
11
+
12
+ $is_all_languages = $this->is_all_languages();
13
+
14
+ if (!$is_all_languages) {
15
+ $controls->warnings[] = 'You are configuring the language "<strong>' . $current_language . '</strong>". Switch to "all languages" to see every options.';
16
+ }
17
+
18
+ // Profile options are still inside the main options
19
+ if ($controls->is_action()) {
20
+ if ($controls->is_action('save')) {
21
+ $this->save_options($controls->data, '', null, $current_language);
22
+ $controls->add_message_saved();
23
+ }
24
+ if ($controls->is_action('reset')) {
25
+ $this->reset_options();
26
+ $controls->data = $this->get_options('', $current_language);
27
+ $controls->add_message_reset();
28
+ }
29
+ } else {
30
+ $controls->data = $this->get_options('', $current_language);
31
+ }
32
+ ?>
33
+
34
+ <div class="wrap tnp-profile tnp-profile-index" id="tnp-wrap">
35
+
36
+ <?php include NEWSLETTER_DIR . '/tnp-header.php'; ?>
37
+
38
+ <div id="tnp-heading">
39
+
40
+ <h2><?php _e('The subscriber profile page', 'newsletter') ?></h2>
41
+ <?php $controls->page_help('https://www.thenewsletterplugin.com/documentation/profile-page') ?>
42
+ </div>
43
+
44
+ <div id="tnp-body">
45
+
46
+
47
+ <form id="channel" method="post" action="">
48
+ <?php $controls->init(); ?>
49
+ <div id="tabs">
50
+ <ul>
51
+ <li><a href="#tabs-general"><?php _e('General', 'newsletter') ?></a></li>
52
+ <li><a href="#tabs-export"><?php _e('Subscriber data export', 'newsletter') ?></a></li>
53
+
54
+ </ul>
55
+
56
+ <div id="tabs-general">
57
+
58
+
59
+ <table class="form-table">
60
+
61
+ <tr>
62
+ <th><?php _e('Profile page', 'newsletter') ?>
63
+ <br><?php $controls->help('https://www.thenewsletterplugin.com/documentation/subscription#profile') ?>
64
+ </th>
65
+ <td>
66
+ <?php $controls->wp_editor('text'); ?>
67
+ </td>
68
+ </tr>
69
+
70
+ <tr>
71
+ <th><?php _e('Alternative profile page URL', 'newsletter') ?></th>
72
+ <td>
73
+ <?php $controls->text('url', 70); ?>
74
+ </td>
75
+ </tr>
76
+
77
+ </table>
78
+
79
+ <h3><?php _e('Messages', 'newsletter') ?></h3>
80
+ <table class="form-table">
81
+ <tr>
82
+ <th><?php _e('Profile saved', 'newsletter') ?></th>
83
+ <td>
84
+ <?php $controls->text('saved', 80); ?>
85
+ </td>
86
+ </tr>
87
+
88
+ <tr>
89
+ <tr>
90
+ <th><?php _e('Email changed alert', 'newsletter') ?></th>
91
+ <td>
92
+ <?php $controls->text('email_changed', 80); ?>
93
+ </td>
94
+ </tr>
95
+
96
+ <tr>
97
+
98
+ <tr>
99
+ <tr>
100
+ <th><?php _e('General error', 'newsletter') ?></th>
101
+ <td>
102
+ <?php $controls->text('error', 80); ?>
103
+ </td>
104
+ </tr>
105
+
106
+ </table>
107
+
108
+ <h3><?php _e('Labels', 'newsletter') ?></h3>
109
+ <table class="form-table">
110
+ <tr>
111
+ <th><?php _e('"Save" label', 'newsletter') ?></th>
112
+ <td>
113
+ <?php $controls->text('save_label'); ?>
114
+ </td>
115
+ </tr>
116
+
117
+ <tr>
118
+ <th><?php _e('Privacy link text', 'newsletter') ?></th>
119
+ <td>
120
+ <?php $controls->text('privacy_label', 80); ?>
121
+ <p class="description">
122
+
123
+ </p>
124
+ </td>
125
+ </tr>
126
+
127
+ </table>
128
+ </div>
129
+
130
+ <div id="tabs-export">
131
+ <?php if ($is_all_languages) { ?>
132
+
133
+ <table class="form-table">
134
+
135
+ <tr>
136
+ <th>
137
+ <?php _e('Log of sent newsletters', 'newsletter') ?>
138
+ </th>
139
+ <td>
140
+ <?php $controls->yesno('export_newsletters'); ?>
141
+ </td>
142
+ </tr>
143
+ </table>
144
+ <?php } else { ?>
145
+
146
+ <?php $controls->switch_to_all_languages_notice(); ?>
147
+
148
+ <?php } ?>
149
+ </div>
150
+
151
+ </div>
152
+
153
+ <p>
154
+ <?php $controls->button_save() ?>
155
+ <?php $controls->button_reset() ?>
156
+ </p>
157
+
158
+ </form>
159
+ </div>
160
+
161
+ <?php include NEWSLETTER_DIR . '/tnp-footer.php'; ?>
162
+
163
+ </div>
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.0
4
- Stable tag: 7.4.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,6 +126,11 @@ Thank you, The Newsletter Team
126
 
127
  == Changelog ==
128
 
 
 
 
 
 
129
  = 7.4.6 =
130
 
131
  * XSS security fix
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.0
4
+ Stable tag: 7.4.7
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.4.7 =
130
+
131
+ * Changed removal order of the emojis admin script breaking newsletters' body
132
+ * Disabled DOM warnings
133
+
134
  = 7.4.6 =
135
 
136
  * XSS security fix
subscription/template.php CHANGED
@@ -1,122 +1,122 @@
1
- <?php
2
- defined('ABSPATH') || exit;
3
-
4
- @include_once NEWSLETTER_INCLUDES_DIR . '/controls.php';
5
- $controls = new NewsletterControls();
6
- $module = NewsletterSubscription::instance();
7
-
8
- $current_language = $module->get_current_language();
9
-
10
- $is_all_languages = $module->is_all_languages();
11
-
12
- if (!$is_all_languages) {
13
- $controls->warnings[] = 'You are configuring the language <strong>' . $current_language . '</strong>.';
14
- }
15
-
16
- if (!$controls->is_action()) {
17
- $controls->data = $module->get_options('template', $current_language);
18
- } else {
19
- if ($controls->is_action('save')) {
20
- $module->save_options($controls->data, 'template', null, $current_language);
21
-
22
- $controls->add_message_saved();
23
- }
24
-
25
- if ($controls->is_action('reset')) {
26
- // TODO: Reset by language?
27
- $module->reset_options('template');
28
- $controls->data = $module->get_options('template', $current_language);
29
- $controls->add_message_done();
30
- }
31
-
32
- if ($controls->is_action('test')) {
33
-
34
- $users = $module->get_test_users();
35
- if (count($users) == 0) {
36
- $controls->errors = __('No test subscribers found.', 'newsletter') . ' <a href="https://www.thenewsletterplugin.com/plugins/newsletter/subscribers-module#test" target="_blank"><i class="fas fa-info-circle"></i></a>';
37
- } else {
38
- $template = $controls->data['template'];
39
-
40
- $message = '<p>This is a generic example of message embedded inside the template.</p>';
41
- $message .= '<p>Subscriber data can be referenced using tags. See the <a href="https://www.thenewsletterplugin.com/documentation">plugin documentation</a>.</p>';
42
- $message .= '<p>First name: {name}</p>';
43
- $message .= '<p>Last name: {surname}</p>';
44
- $message .= '<p>Email: {email}</p>';
45
- $message .= '<p>Here an image as well. Make them styled with the CSS rule "max-width: 100%"</p>';
46
- $message .= '<p><img src="' . plugins_url('newsletter') . '/images/test.jpg" style="max-width: 100%"></p>';
47
-
48
- $message = str_replace('{message}', $message, $template);
49
- $addresses = array();
50
- foreach ($users as $user) {
51
- $addresses[] = $user->email;
52
- Newsletter::instance()->mail($user->email, 'Newsletter Messages Template Test', $module->replace($message, $user));
53
- }
54
- $controls->messages .= 'Test emails sent to ' . count($users) . ' test subscribers: ' .
55
- implode(', ', $addresses) . '.' . ' <a href="https://www.thenewsletterplugin.com/plugins/newsletter/subscribers-module#test" target="_blank"><i class="fas fa-info-circle"></i></a>';
56
- }
57
- }
58
- }
59
-
60
- if (strpos($controls->data['template'], '{message}') === false) {
61
- $controls->errors = __('The tag {message} is missing in your template', 'newsletter');
62
- }
63
- ?>
64
-
65
- <?php include NEWSLETTER_INCLUDES_DIR . '/codemirror.php'; ?>
66
- <style>
67
- .CodeMirror {
68
- height: 100%;
69
- }
70
- </style>
71
- <script>
72
- jQuery(function () {
73
- templateEditor = CodeMirror.fromTextArea(document.getElementById("options-template"), {
74
- lineNumbers: true,
75
- mode: 'htmlmixed',
76
- extraKeys: {"Ctrl-Space": "autocomplete"}
77
- });
78
- });
79
- </script>
80
-
81
- <div class="wrap" id="tnp-wrap">
82
-
83
- <?php include NEWSLETTER_DIR . '/tnp-header.php'; ?>
84
-
85
- <div id="tnp-heading">
86
-
87
- <h2><?php _e('Messages template', 'newsletter') ?></h2>
88
- <p>
89
- Edit the default template of confirmation, welcome and cancellation emails. Add the {message} tag where you
90
- want the specific message text to be included.
91
- </p>
92
-
93
- </div>
94
-
95
- <div id="tnp-body">
96
-
97
- <form method="post" action="">
98
- <?php $controls->init(); ?>
99
- <p>
100
- <?php $controls->button_save(); ?>
101
- <?php $controls->button_reset(); ?>
102
- <?php $controls->button('test', 'Send a test'); ?>
103
- </p>
104
-
105
-
106
- <h3><?php _e('Template', 'newsletter') ?></h3>
107
-
108
- <?php $controls->textarea_preview('template', '100%', '700px'); ?>
109
- <br><br>
110
-
111
-
112
- <p>
113
- <?php $controls->button_save(); ?>
114
- <?php $controls->button_reset(); ?>
115
- <?php $controls->button('test', 'Send a test'); ?>
116
- </p>
117
- </form>
118
- </div>
119
-
120
- <?php include NEWSLETTER_DIR . '/tnp-footer.php'; ?>
121
-
122
  </div>
1
+ <?php
2
+ defined('ABSPATH') || exit;
3
+
4
+ @include_once NEWSLETTER_INCLUDES_DIR . '/controls.php';
5
+ $controls = new NewsletterControls();
6
+ $module = NewsletterSubscription::instance();
7
+
8
+ $current_language = $module->get_current_language();
9
+
10
+ $is_all_languages = $module->is_all_languages();
11
+
12
+ if (!$is_all_languages) {
13
+ $controls->warnings[] = 'You are configuring the language <strong>' . $current_language . '</strong>.';
14
+ }
15
+
16
+ if (!$controls->is_action()) {
17
+ $controls->data = $module->get_options('template', $current_language);
18
+ } else {
19
+ if ($controls->is_action('save')) {
20
+ $module->save_options($controls->data, 'template', null, $current_language);
21
+
22
+ $controls->add_message_saved();
23
+ }
24
+
25
+ if ($controls->is_action('reset')) {
26
+ // TODO: Reset by language?
27
+ $module->reset_options('template');
28
+ $controls->data = $module->get_options('template', $current_language);
29
+ $controls->add_message_done();
30
+ }
31
+
32
+ if ($controls->is_action('test')) {
33
+
34
+ $users = $module->get_test_users();
35
+ if (count($users) == 0) {
36
+ $controls->errors = __('No test subscribers found.', 'newsletter') . ' <a href="https://www.thenewsletterplugin.com/plugins/newsletter/subscribers-module#test" target="_blank"><i class="fas fa-info-circle"></i></a>';
37
+ } else {
38
+ $template = $controls->data['template'];
39
+
40
+ $message = '<p>This is a generic example of message embedded inside the template.</p>';
41
+ $message .= '<p>Subscriber data can be referenced using tags. See the <a href="https://www.thenewsletterplugin.com/documentation">plugin documentation</a>.</p>';
42
+ $message .= '<p>First name: {name}</p>';
43
+ $message .= '<p>Last name: {surname}</p>';
44
+ $message .= '<p>Email: {email}</p>';
45
+ $message .= '<p>Here an image as well. Make them styled with the CSS rule "max-width: 100%"</p>';
46
+ $message .= '<p><img src="' . plugins_url('newsletter') . '/images/test.jpg" style="max-width: 100%"></p>';
47
+
48
+ $message = str_replace('{message}', $message, $template);
49
+ $addresses = array();
50
+ foreach ($users as $user) {
51
+ $addresses[] = $user->email;
52
+ Newsletter::instance()->mail($user->email, 'Newsletter Messages Template Test', $module->replace($message, $user));
53
+ }
54
+ $controls->messages .= 'Test emails sent to ' . count($users) . ' test subscribers: ' .
55
+ implode(', ', $addresses) . '.' . ' <a href="https://www.thenewsletterplugin.com/plugins/newsletter/subscribers-module#test" target="_blank"><i class="fas fa-info-circle"></i></a>';
56
+ }
57
+ }
58
+ }
59
+
60
+ if (strpos($controls->data['template'], '{message}') === false) {
61
+ $controls->errors = __('The tag {message} is missing in your template', 'newsletter');
62
+ }
63
+ ?>
64
+
65
+ <?php include NEWSLETTER_INCLUDES_DIR . '/codemirror.php'; ?>
66
+ <style>
67
+ .CodeMirror {
68
+ height: 100%;
69
+ }
70
+ </style>
71
+ <script>
72
+ jQuery(function () {
73
+ templateEditor = CodeMirror.fromTextArea(document.getElementById("options-template"), {
74
+ lineNumbers: true,
75
+ mode: 'htmlmixed',
76
+ extraKeys: {"Ctrl-Space": "autocomplete"}
77
+ });
78
+ });
79
+ </script>
80
+
81
+ <div class="wrap" id="tnp-wrap">
82
+
83
+ <?php include NEWSLETTER_DIR . '/tnp-header.php'; ?>
84
+
85
+ <div id="tnp-heading">
86
+
87
+ <h2><?php _e('Messages template', 'newsletter') ?></h2>
88
+ <p>
89
+ Edit the default template of confirmation, welcome and cancellation emails. Add the {message} tag where you
90
+ want the specific message text to be included.
91
+ </p>
92
+
93
+ </div>
94
+
95
+ <div id="tnp-body">
96
+
97
+ <form method="post" action="">
98
+ <?php $controls->init(); ?>
99
+ <p>
100
+ <?php $controls->button_save(); ?>
101
+ <?php $controls->button_reset(); ?>
102
+ <?php $controls->button('test', 'Send a test'); ?>
103
+ </p>
104
+
105
+
106
+ <h3><?php _e('Template', 'newsletter') ?></h3>
107
+
108
+ <?php $controls->textarea_preview('template', '100%', '700px'); ?>
109
+ <br><br>
110
+
111
+
112
+ <p>
113
+ <?php $controls->button_save(); ?>
114
+ <?php $controls->button_reset(); ?>
115
+ <?php $controls->button('test', 'Send a test'); ?>
116
+ </p>
117
+ </form>
118
+ </div>
119
+
120
+ <?php include NEWSLETTER_DIR . '/tnp-footer.php'; ?>
121
+
122
  </div>
unsubscription/unsubscription.php CHANGED
@@ -1,217 +1,217 @@
1
- <?php
2
-
3
- defined('ABSPATH') || exit;
4
-
5
- class NewsletterUnsubscription extends NewsletterModule {
6
-
7
- static $instance;
8
-
9
- /**
10
- * @return NewsletterUnsubscription
11
- */
12
- static function instance() {
13
- if (self::$instance == null) {
14
- self::$instance = new NewsletterUnsubscription();
15
- }
16
- return self::$instance;
17
- }
18
-
19
- function __construct() {
20
- parent::__construct('unsubscription', '1.0.3');
21
-
22
- add_filter('newsletter_replace', array($this, 'hook_newsletter_replace'), 10, 4);
23
- add_filter('newsletter_page_text', array($this, 'hook_newsletter_page_text'), 10, 3);
24
- add_filter('newsletter_message_headers', array($this, 'hook_add_unsubscribe_headers_to_email'), 10, 3);
25
-
26
- add_action('newsletter_action', array($this, 'hook_newsletter_action'), 11, 3);
27
- }
28
-
29
- function hook_newsletter_action($action, $user, $email) {
30
-
31
- if (in_array($action, ['u', 'uc', 'lu', 'reactivate'])) {
32
- if (!$user) {
33
- $this->dienow(__('Subscriber not found', 'newsletter'), 'Already deleted or using the wrong subscriber key in the URL', 404);
34
- }
35
- }
36
-
37
- switch ($action) {
38
- case 'u':
39
- $url = $this->build_message_url(null, 'unsubscribe', $user, $email);
40
- wp_redirect($url);
41
- die();
42
- break;
43
-
44
- case 'lu': //Left for backwards compatibility, could be removed after some time
45
- case 'uc':
46
- if (isset($_POST['List-Unsubscribe']) && 'One-Click' === $_POST['List-Unsubscribe']) {
47
- $this->unsubscribe($user, $email);
48
- } else if ($this->antibot_form_check()) {
49
- $this->unsubscribe($user, $email);
50
- $url = $this->build_message_url(null, 'unsubscribed', $user, $email);
51
- wp_redirect($url);
52
- } else {
53
- $this->request_to_antibot_form('Unsubscribe');
54
- }
55
- die();
56
- break;
57
-
58
- case 'reactivate':
59
- if ($this->antibot_form_check()) {
60
- $this->reactivate($user);
61
- $url = $this->build_message_url(null, 'reactivated', $user);
62
- wp_redirect($url);
63
- } else {
64
- $this->request_to_antibot_form('Reactivate');
65
- }
66
- die();
67
- break;
68
- }
69
- }
70
-
71
- /**
72
- * Unsubscribes the subscriber from the request. Die on subscriber extraction failure.
73
- *
74
- * @return TNP_User
75
- */
76
- function unsubscribe($user, $email = null) {
77
- global $wpdb;
78
-
79
- if ($user->status == TNP_User::STATUS_UNSUBSCRIBED) {
80
- return $user;
81
- }
82
-
83
- $this->refresh_user_token($user);
84
- $this->set_user_status($user, TNP_User::STATUS_UNSUBSCRIBED);
85
-
86
- $this->add_user_log($user, 'unsubscribe');
87
-
88
- do_action('newsletter_user_unsubscribed', $user);
89
-
90
- if ($email) {
91
- $wpdb->update(NEWSLETTER_USERS_TABLE, array('unsub_email_id' => (int) $email->id, 'unsub_time' => time()), array('id' => $user->id));
92
- }
93
-
94
- $this->send_unsubscribed_email($user);
95
-
96
- $this->notify_admin_on_unsubscription($user);
97
-
98
- return $user;
99
- }
100
-
101
- function send_unsubscribed_email($user, $force = false) {
102
- $options = $this->get_options('', $this->get_user_language($user));
103
- if (!$force && !empty($options['unsubscribed_disabled'])) {
104
- return true;
105
- }
106
-
107
- $message = do_shortcode( $options['unsubscribed_message'] );
108
- $subject = $options['unsubscribed_subject'];
109
-
110
- return NewsletterSubscription::instance()->mail($user, $subject, $message);
111
- }
112
-
113
- function notify_admin_on_unsubscription($user) {
114
-
115
- if (empty($this->options['notify_admin_on_unsubscription'])) {
116
- return;
117
- }
118
-
119
- $message = $this->generate_admin_notification_message($user);
120
- $email = trim(get_option('admin_email'));
121
- $subject = $this->generate_admin_notification_subject('New cancellation');
122
-
123
- Newsletter::instance()->mail($email, $subject, array('html' => $message));
124
- }
125
-
126
- /**
127
- * Reactivate the subscriber extracted from the request setting his status
128
- * to confirmed and logging. No email are sent. Dies on subscriber extraction failure.
129
- *
130
- * @return TNP_User
131
- */
132
- function reactivate($user = null) {
133
- // For compatibility, to be removed
134
- if (!$user) {
135
- $user = $this->get_user_from_request(true);
136
- }
137
- $this->set_user_status($user, TNP_User::STATUS_CONFIRMED);
138
- $this->add_user_log($user, 'reactivate');
139
- do_action('newsletter_user_reactivated', $user);
140
- }
141
-
142
- function hook_newsletter_replace($text, $user, $email, $html = true) {
143
-
144
- if ($user) {
145
- $text = $this->replace_url($text, 'unsubscription_confirm_url', $this->build_action_url('uc', $user, $email));
146
- $text = $this->replace_url($text, 'unsubscription_url', $this->build_action_url('u', $user, $email));
147
- $text = $this->replace_url($text, 'reactivate_url', $this->build_action_url('reactivate', $user, $email));
148
- } else {
149
- $text = $this->replace_url($text, 'unsubscription_confirm_url', $this->build_action_url('nul'));
150
- $text = $this->replace_url($text, 'unsubscription_url', $this->build_action_url('nul'));
151
- }
152
-
153
- return $text;
154
- }
155
-
156
- function hook_newsletter_page_text($text, $key, $user = null) {
157
-
158
- $options = $this->get_options('', $this->get_current_language($user));
159
- if ($key == 'unsubscribe') {
160
- if (!$user) {
161
- return 'Subscriber not found.';
162
- }
163
- return $options['unsubscribe_text'];
164
- }
165
- if ($key == 'unsubscribed') {
166
- if (!$user) {
167
- return $options['error_text'];
168
- }
169
- return $options['unsubscribed_text'];
170
- }
171
- if ($key == 'reactivated') {
172
- if (!$user) {
173
- return $options['error_text'];
174
- }
175
- return $options['reactivated_text'];
176
- }
177
- if ($key == 'unsubscription_error') {
178
- return $options['error_text'];
179
- }
180
- return $text;
181
- }
182
-
183
- function admin_menu() {
184
- $this->add_admin_page('index', 'Unsubscribe');
185
- }
186
-
187
- /**
188
- * @param array $headers
189
- * @param TNP_Email $email
190
- * @param TNP_User $user
191
- *
192
- * @return array
193
- */
194
- function hook_add_unsubscribe_headers_to_email($headers, $email, $user) {
195
-
196
- if (!empty($this->options['disable_unsubscribe_headers'])) {
197
- return $headers;
198
- }
199
-
200
- $list_unsubscribe_values = [];
201
- if (!empty($this->options['list_unsubscribe_mailto_header'])) {
202
- $unsubscribe_address = $this->options['list_unsubscribe_mailto_header'];
203
- $list_unsubscribe_values[] = "<mailto:$unsubscribe_address?subject=unsubscribe>";
204
- }
205
-
206
- $unsubscribe_action_url = $this->build_action_url('uc', $user, $email);
207
- $list_unsubscribe_values[] = "<$unsubscribe_action_url>";
208
-
209
- $headers['List-Unsubscribe'] = implode(', ', $list_unsubscribe_values);
210
- $headers['List-Unsubscribe-Post'] = 'List-Unsubscribe=One-Click';
211
-
212
- return $headers;
213
- }
214
-
215
- }
216
-
217
- NewsletterUnsubscription::instance();
1
+ <?php
2
+
3
+ defined('ABSPATH') || exit;
4
+
5
+ class NewsletterUnsubscription extends NewsletterModule {
6
+
7
+ static $instance;
8
+
9
+ /**
10
+ * @return NewsletterUnsubscription
11
+ */
12
+ static function instance() {
13
+ if (self::$instance == null) {
14
+ self::$instance = new NewsletterUnsubscription();
15
+ }
16
+ return self::$instance;
17
+ }
18
+
19
+ function __construct() {
20
+ parent::__construct('unsubscription', '1.0.3');
21
+
22
+ add_filter('newsletter_replace', array($this, 'hook_newsletter_replace'), 10, 4);
23
+ add_filter('newsletter_page_text', array($this, 'hook_newsletter_page_text'), 10, 3);
24
+ add_filter('newsletter_message_headers', array($this, 'hook_add_unsubscribe_headers_to_email'), 10, 3);
25
+
26
+ add_action('newsletter_action', array($this, 'hook_newsletter_action'), 11, 3);
27
+ }
28
+
29
+ function hook_newsletter_action($action, $user, $email) {
30
+
31
+ if (in_array($action, ['u', 'uc', 'lu', 'reactivate'])) {
32
+ if (!$user) {
33
+ $this->dienow(__('Subscriber not found', 'newsletter'), 'Already deleted or using the wrong subscriber key in the URL', 404);
34
+ }
35
+ }
36
+
37
+ switch ($action) {
38
+ case 'u':
39
+ $url = $this->build_message_url(null, 'unsubscribe', $user, $email);
40
+ wp_redirect($url);
41
+ die();
42
+ break;
43
+
44
+ case 'lu': //Left for backwards compatibility, could be removed after some time
45
+ case 'uc':
46
+ if (isset($_POST['List-Unsubscribe']) && 'One-Click' === $_POST['List-Unsubscribe']) {
47
+ $this->unsubscribe($user, $email);
48
+ } else if ($this->antibot_form_check()) {
49
+ $this->unsubscribe($user, $email);
50
+ $url = $this->build_message_url(null, 'unsubscribed', $user, $email);
51
+ wp_redirect($url);
52
+ } else {
53
+ $this->request_to_antibot_form('Unsubscribe');
54
+ }
55
+ die();
56
+ break;
57
+
58
+ case 'reactivate':
59
+ if ($this->antibot_form_check()) {
60
+ $this->reactivate($user);
61
+ $url = $this->build_message_url(null, 'reactivated', $user);
62
+ wp_redirect($url);
63
+ } else {
64
+ $this->request_to_antibot_form('Reactivate');
65
+ }
66
+ die();
67
+ break;
68
+ }
69
+ }
70
+
71
+ /**
72
+ * Unsubscribes the subscriber from the request. Die on subscriber extraction failure.
73
+ *
74
+ * @return TNP_User
75
+ */
76
+ function unsubscribe($user, $email = null) {
77
+ global $wpdb;
78
+
79
+ if ($user->status == TNP_User::STATUS_UNSUBSCRIBED) {
80
+ return $user;
81
+ }
82
+
83
+ $this->refresh_user_token($user);
84
+ $this->set_user_status($user, TNP_User::STATUS_UNSUBSCRIBED);
85
+
86
+ $this->add_user_log($user, 'unsubscribe');
87
+
88
+ do_action('newsletter_user_unsubscribed', $user);
89
+
90
+ if ($email) {
91
+ $wpdb->update(NEWSLETTER_USERS_TABLE, array('unsub_email_id' => (int) $email->id, 'unsub_time' => time()), array('id' => $user->id));
92
+ }
93
+
94
+ $this->send_unsubscribed_email($user);
95
+
96
+ $this->notify_admin_on_unsubscription($user);
97
+
98
+ return $user;
99
+ }
100
+
101
+ function send_unsubscribed_email($user, $force = false) {
102
+ $options = $this->get_options('', $this->get_user_language($user));
103
+ if (!$force && !empty($options['unsubscribed_disabled'])) {
104
+ return true;
105
+ }
106
+
107
+ $message = do_shortcode( $options['unsubscribed_message'] );
108
+ $subject = $options['unsubscribed_subject'];
109
+
110
+ return NewsletterSubscription::instance()->mail($user, $subject, $message);
111
+ }
112
+
113
+ function notify_admin_on_unsubscription($user) {
114
+
115
+ if (empty($this->options['notify_admin_on_unsubscription'])) {
116
+ return;
117
+ }
118
+
119
+ $message = $this->generate_admin_notification_message($user);
120
+ $email = trim(get_option('admin_email'));
121
+ $subject = $this->generate_admin_notification_subject('New cancellation');
122
+
123
+ Newsletter::instance()->mail($email, $subject, array('html' => $message));
124
+ }
125
+
126
+ /**
127
+ * Reactivate the subscriber extracted from the request setting his status
128
+ * to confirmed and logging. No email are sent. Dies on subscriber extraction failure.
129
+ *
130
+ * @return TNP_User
131
+ */
132
+ function reactivate($user = null) {
133
+ // For compatibility, to be removed
134
+ if (!$user) {
135
+ $user = $this->get_user_from_request(true);
136
+ }
137
+ $this->set_user_status($user, TNP_User::STATUS_CONFIRMED);
138
+ $this->add_user_log($user, 'reactivate');
139
+ do_action('newsletter_user_reactivated', $user);
140
+ }
141
+
142
+ function hook_newsletter_replace($text, $user, $email, $html = true) {
143
+
144
+ if ($user) {
145
+ $text = $this->replace_url($text, 'unsubscription_confirm_url', $this->build_action_url('uc', $user, $email));
146
+ $text = $this->replace_url($text, 'unsubscription_url', $this->build_action_url('u', $user, $email));
147
+ $text = $this->replace_url($text, 'reactivate_url', $this->build_action_url('reactivate', $user, $email));
148
+ } else {
149
+ $text = $this->replace_url($text, 'unsubscription_confirm_url', $this->build_action_url('nul'));
150
+ $text = $this->replace_url($text, 'unsubscription_url', $this->build_action_url('nul'));
151
+ }
152
+
153
+ return $text;
154
+ }
155
+
156
+ function hook_newsletter_page_text($text, $key, $user = null) {
157
+
158
+ $options = $this->get_options('', $this->get_current_language($user));
159
+ if ($key == 'unsubscribe') {
160
+ if (!$user) {
161
+ return 'Subscriber not found.';
162
+ }
163
+ return $options['unsubscribe_text'];
164
+ }
165
+ if ($key == 'unsubscribed') {
166
+ if (!$user) {
167
+ return $options['error_text'];
168
+ }
169
+ return $options['unsubscribed_text'];
170
+ }
171
+ if ($key == 'reactivated') {
172
+ if (!$user) {
173
+ return $options['error_text'];
174
+ }
175
+ return $options['reactivated_text'];
176
+ }
177
+ if ($key == 'unsubscription_error') {
178
+ return $options['error_text'];
179
+ }
180
+ return $text;
181
+ }
182
+
183
+ function admin_menu() {
184
+ $this->add_admin_page('index', 'Unsubscribe');
185
+ }
186
+
187
+ /**
188
+ * @param array $headers
189
+ * @param TNP_Email $email
190
+ * @param TNP_User $user
191
+ *
192
+ * @return array
193
+ */
194
+ function hook_add_unsubscribe_headers_to_email($headers, $email, $user) {
195
+
196
+ if (!empty($this->options['disable_unsubscribe_headers'])) {
197
+ return $headers;
198
+ }
199
+
200
+ $list_unsubscribe_values = [];
201
+ if (!empty($this->options['list_unsubscribe_mailto_header'])) {
202
+ $unsubscribe_address = $this->options['list_unsubscribe_mailto_header'];
203
+ $list_unsubscribe_values[] = "<mailto:$unsubscribe_address?subject=unsubscribe>";
204
+ }
205
+
206
+ $unsubscribe_action_url = $this->build_action_url('uc', $user, $email);
207
+ $list_unsubscribe_values[] = "<$unsubscribe_action_url>";
208
+
209
+ $headers['List-Unsubscribe'] = implode(', ', $list_unsubscribe_values);
210
+ $headers['List-Unsubscribe-Post'] = 'List-Unsubscribe=One-Click';
211
+
212
+ return $headers;
213
+ }
214
+
215
+ }
216
+
217
+ NewsletterUnsubscription::instance();