Newsletter - Version 7.4.8

Version Description

  • Changed the image block for Gmail
  • Removed the WP emojis staticizer on emails sent by Newsletter
Download this release

Release Info

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

Code changes from version 7.4.5 to 7.4.8

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
+ }
admin/fields.css CHANGED
@@ -1,203 +1,203 @@
1
- html {
2
- --tnpf-col-padding: 0 10px;
3
- }
4
-
5
- .tnpf-form {
6
- padding: 15px;
7
- margin-top: 0;
8
- color: #444;
9
- background-color: #ECF0F1 !important;
10
- }
11
-
12
- .tnp-field-row, .tnpf-row {
13
- clear: both;
14
- margin-left: -10px;
15
- margin-right: -10px;
16
- }
17
-
18
- .tnp-field-col-2, .tnpf-col-2, .tnpf-col-50 {
19
- width: 50%;
20
- box-sizing: border-box;
21
- padding: 0 10px;
22
- float: left;
23
- }
24
-
25
- .tnp-field-col-3, .tnp-field-col-33, .tnpf-col-33 {
26
- width: 33%;
27
- box-sizing: border-box;
28
- padding: 0 10px;
29
- float: left;
30
- }
31
-
32
- .tnp-field-col-20, .tnpf-col-20 {
33
- width: 20%;
34
- box-sizing: border-box;
35
- padding: 0 10px;
36
- float: left;
37
- }
38
-
39
- .tnp-field-col-66, .tnpf-col-66 {
40
- width: 66%;
41
- box-sizing: border-box;
42
- padding: 0 10px;
43
- float: left;
44
- }
45
-
46
- .tnp-field-col-80, .tnpf-col-80 {
47
- width: 80%;
48
- box-sizing: border-box;
49
- padding: 0 10px;
50
- float: left;
51
- }
52
-
53
- .tnpf-field-box {
54
- padding: 10px;
55
- background-color: #eee !important;
56
- border: 1px solid #bbb;
57
- }
58
-
59
- .tnpf-section {
60
- font-size: 1.0rem;
61
- font-weight: bold;
62
- color: #333;
63
- margin-top: 0;
64
- margin-bottom: 10px;
65
- }
66
-
67
- .tnpf-separator {
68
- border-top: 1px solid #ddd;
69
- line-height: 0;
70
- display: block;
71
- width: 100%;
72
- margin-bottom: 10px;
73
- }
74
-
75
- /* Single field container */
76
- .tnpf-field, .tnpf-field {
77
- display: block;
78
- width: 100%;
79
- margin-bottom: 10px;
80
- }
81
-
82
- .tnpf-field select.tnpf-small,
83
- .tnpf-field select.tnp-small {
84
- font-size: .9em;
85
- }
86
-
87
- .tnpf-field label.tnpf-label,
88
- .tnpf-field label.tnp-label {
89
- display: block;
90
- font-size: 12px;
91
- font-weight: 300;
92
- border: 0;
93
- margin: 0px 0px 0px 0px;
94
- font-family: soleil, sans-serif;
95
- font-weight: 300;
96
- padding-bottom: 5px;
97
- color: #666;
98
- }
99
-
100
- .tnpf-row label.tnpf-row-label,
101
- .tnp-field-row label.tnp-row-label {
102
- display: block;
103
- font-size: 12px;
104
- font-weight: 300;
105
- border: 0;
106
- margin: 0px 0px 0px 10px;
107
- font-family: soleil, sans-serif;
108
- font-weight: 300;
109
- padding-bottom: 5px;
110
- color: #666;
111
- }
112
-
113
- /* Compesate the negative row margin */
114
- .tnp-field-row label.tnp-row-label {
115
- margin-left: 10px;
116
- }
117
-
118
- .tnpf-row .tnpf-field .tnpf-label,
119
- .tnpf-row .tnpf-field .tnp-label
120
- {
121
- margin-top: 0;
122
- }
123
-
124
- .tnpf-field.tnpf-checkbox label {
125
- display: inline;
126
- }
127
-
128
- /* Set of inlined field for font, size, color... */
129
-
130
- .tnpf-field:not(.tnp-colorpicker) input {
131
- width: 100%;
132
- }
133
-
134
- .tnpf-field input[type=number] {
135
- width: 100px;
136
- }
137
-
138
- .tnpf-field.tnpf-size input {
139
- width: 60px;
140
- display: inline;
141
- }
142
-
143
- .tnpf-field .tnp-padding-fields {
144
- display: inline;
145
- }
146
-
147
- /* Four field for block padding selection */
148
- .tnpf-field .tnp-padding-fields input {
149
- width: 40px;
150
- display: inline;
151
- }
152
-
153
- .tnpf-field select {
154
- width: auto!important;
155
- color: #34495E;
156
- }
157
-
158
- .tnpf-field input[type=url] {
159
- font-family: monospace;
160
- }
161
-
162
- .tnpf-field input[type=color] {
163
- width: 40px;
164
- min-height: 30px;
165
- vertical-align: middle;
166
- }
167
-
168
- .tnpf-field input[type=submit] {
169
- width: auto;
170
- }
171
-
172
- .tnpf-field input[type=checkbox] {
173
- width: auto;
174
- }
175
-
176
- .tnpf-field.tnpf-button .tnpf-font-family {
177
- font-size: 13px;
178
- }
179
-
180
- .tnpf-field.tnpf-button .tnpf-font-size {
181
- font-size: 13px;
182
- }
183
-
184
- .tnpf-field.tnpf-button .tnpf-font-weight {
185
- font-size: 13px;
186
- }
187
-
188
- .tnpf-field.tnp-categories {
189
-
190
- }
191
-
192
- /* The label for each category checkbox (should override the generic label appearance) */
193
- .tnpf-field.tnp-categories label {
194
-
195
- }
196
-
197
- .tnpf-description {
198
- margin-top: 3px;
199
- font-style: italic;
200
- font-weight: normal;
201
- color: #999999;
202
- font-size: 12px;
203
- }
1
+ html {
2
+ --tnpf-col-padding: 0 10px;
3
+ }
4
+
5
+ .tnpf-form {
6
+ padding: 15px;
7
+ margin-top: 0;
8
+ color: #444;
9
+ background-color: #ECF0F1 !important;
10
+ }
11
+
12
+ .tnp-field-row, .tnpf-row {
13
+ clear: both;
14
+ margin-left: -10px;
15
+ margin-right: -10px;
16
+ }
17
+
18
+ .tnp-field-col-2, .tnpf-col-2, .tnpf-col-50 {
19
+ width: 50%;
20
+ box-sizing: border-box;
21
+ padding: 0 10px;
22
+ float: left;
23
+ }
24
+
25
+ .tnp-field-col-3, .tnp-field-col-33, .tnpf-col-33 {
26
+ width: 33%;
27
+ box-sizing: border-box;
28
+ padding: 0 10px;
29
+ float: left;
30
+ }
31
+
32
+ .tnp-field-col-20, .tnpf-col-20 {
33
+ width: 20%;
34
+ box-sizing: border-box;
35
+ padding: 0 10px;
36
+ float: left;
37
+ }
38
+
39
+ .tnp-field-col-66, .tnpf-col-66 {
40
+ width: 66%;
41
+ box-sizing: border-box;
42
+ padding: 0 10px;
43
+ float: left;
44
+ }
45
+
46
+ .tnp-field-col-80, .tnpf-col-80 {
47
+ width: 80%;
48
+ box-sizing: border-box;
49
+ padding: 0 10px;
50
+ float: left;
51
+ }
52
+
53
+ .tnpf-field-box {
54
+ padding: 10px;
55
+ background-color: #eee !important;
56
+ border: 1px solid #bbb;
57
+ }
58
+
59
+ .tnpf-section {
60
+ font-size: 1.0rem;
61
+ font-weight: bold;
62
+ color: #333;
63
+ margin-top: 0;
64
+ margin-bottom: 10px;
65
+ }
66
+
67
+ .tnpf-separator {
68
+ border-top: 1px solid #ddd;
69
+ line-height: 0;
70
+ display: block;
71
+ width: 100%;
72
+ margin-bottom: 10px;
73
+ }
74
+
75
+ /* Single field container */
76
+ .tnpf-field, .tnpf-field {
77
+ display: block;
78
+ width: 100%;
79
+ margin-bottom: 10px;
80
+ }
81
+
82
+ .tnpf-field select.tnpf-small,
83
+ .tnpf-field select.tnp-small {
84
+ font-size: .9em;
85
+ }
86
+
87
+ .tnpf-field label.tnpf-label,
88
+ .tnpf-field label.tnp-label {
89
+ display: block;
90
+ font-size: 12px;
91
+ font-weight: 300;
92
+ border: 0;
93
+ margin: 0px 0px 0px 0px;
94
+ font-family: soleil, sans-serif;
95
+ font-weight: 300;
96
+ padding-bottom: 5px;
97
+ color: #666;
98
+ }
99
+
100
+ .tnpf-row label.tnpf-row-label,
101
+ .tnp-field-row label.tnp-row-label {
102
+ display: block;
103
+ font-size: 12px;
104
+ font-weight: 300;
105
+ border: 0;
106
+ margin: 0px 0px 0px 10px;
107
+ font-family: soleil, sans-serif;
108
+ font-weight: 300;
109
+ padding-bottom: 5px;
110
+ color: #666;
111
+ }
112
+
113
+ /* Compesate the negative row margin */
114
+ .tnp-field-row label.tnp-row-label {
115
+ margin-left: 10px;
116
+ }
117
+
118
+ .tnpf-row .tnpf-field .tnpf-label,
119
+ .tnpf-row .tnpf-field .tnp-label
120
+ {
121
+ margin-top: 0;
122
+ }
123
+
124
+ .tnpf-field.tnpf-checkbox label {
125
+ display: inline;
126
+ }
127
+
128
+ /* Set of inlined field for font, size, color... */
129
+
130
+ .tnpf-field:not(.tnp-colorpicker) input {
131
+ width: 100%;
132
+ }
133
+
134
+ .tnpf-field input[type=number] {
135
+ width: 100px;
136
+ }
137
+
138
+ .tnpf-field.tnpf-size input {
139
+ width: 60px;
140
+ display: inline;
141
+ }
142
+
143
+ .tnpf-field .tnp-padding-fields {
144
+ display: inline;
145
+ }
146
+
147
+ /* Four field for block padding selection */
148
+ .tnpf-field .tnp-padding-fields input {
149
+ width: 40px;
150
+ display: inline;
151
+ }
152
+
153
+ .tnpf-field select {
154
+ width: auto!important;
155
+ color: #34495E;
156
+ }
157
+
158
+ .tnpf-field input[type=url] {
159
+ font-family: monospace;
160
+ }
161
+
162
+ .tnpf-field input[type=color] {
163
+ width: 40px;
164
+ min-height: 30px;
165
+ vertical-align: middle;
166
+ }
167
+
168
+ .tnpf-field input[type=submit] {
169
+ width: auto;
170
+ }
171
+
172
+ .tnpf-field input[type=checkbox] {
173
+ width: auto;
174
+ }
175
+
176
+ .tnpf-field.tnpf-button .tnpf-font-family {
177
+ font-size: 13px;
178
+ }
179
+
180
+ .tnpf-field.tnpf-button .tnpf-font-size {
181
+ font-size: 13px;
182
+ }
183
+
184
+ .tnpf-field.tnpf-button .tnpf-font-weight {
185
+ font-size: 13px;
186
+ }
187
+
188
+ .tnpf-field.tnp-categories {
189
+
190
+ }
191
+
192
+ /* The label for each category checkbox (should override the generic label appearance) */
193
+ .tnpf-field.tnp-categories label {
194
+
195
+ }
196
+
197
+ .tnpf-description {
198
+ margin-top: 3px;
199
+ font-style: italic;
200
+ font-weight: normal;
201
+ color: #999999;
202
+ font-size: 12px;
203
+ }
admin/flows.css DELETED
@@ -1,71 +0,0 @@
1
-
2
- .flow {
3
- display: flex;
4
- align-items: center;
5
- }
6
- .item {
7
- display: block;
8
- border: 1px solid #fff;
9
- width: 150px;
10
- min-height: 100px;
11
- color: #444 !important;
12
- text-decoration: none;
13
- text-align: center;
14
- font-size: 16px;
15
- padding: 15px;
16
- margin: 10px;
17
- cursor: pointer;
18
- background-color: #fff;
19
- box-shadow: 0 0 3px #fff;
20
- }
21
- .arr {
22
- color: #ddd !important;
23
- font-size: 20px;
24
- }
25
- .item.source {
26
- min-height: 50px;
27
- }
28
- .fieldset {
29
- display: none;
30
- justify-content: center;
31
- position: fixed;
32
- xdisplay: none;
33
- max-width: 100%;
34
- height: 100%;
35
- padding-bottom: 50px;
36
- top: 0;
37
- left: 0;
38
- width: 100%;
39
- height: 100%;
40
- background-color: #000000aa;
41
- z-index: 1000;
42
- }
43
-
44
- .fieldset>div {
45
- position: relative;
46
- max-width: 100%;
47
- width: 1024px;
48
- overflow-y: auto;
49
- height: fit-content;
50
- max-height: 80%;
51
- background-color: #fff;
52
- margin-top: 60px;
53
- padding: 15px;
54
- border-radius: 5px;
55
- box-shadow: 0 0 5px #999;
56
-
57
- }
58
-
59
- .fieldset i.fas.fa-times {
60
- display: block;
61
- position: absolute;
62
- right: 10px;
63
- top: 10px;
64
- font-size: 30px;
65
- }
66
-
67
-
68
-
69
- .fieldset>div>p {
70
- color: #000 !important;
71
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
admin/toast.css CHANGED
@@ -1,131 +1,131 @@
1
- .tnp-toast-main-wrapper {
2
- position: fixed;
3
- top: 0;
4
- left: 0;
5
- box-sizing: border-box;
6
- height: 100%;
7
- width: 100%;
8
- z-index: 9991;
9
- pointer-events: none;
10
-
11
- display: flex;
12
- flex-direction: column;
13
-
14
- }
15
-
16
- .tnp-toast-main-wrapper .notification {
17
- display: block;
18
- overflow: hidden;
19
- pointer-events: auto;
20
- box-shadow: 0 3px 7px 0 rgba(0, 0, 0, .25);
21
- position: relative;
22
- padding: 15px;
23
- padding-left: 20px;
24
- border-radius: 2px;
25
- max-width: 300px;
26
- transform: translateY(25%);
27
- box-sizing: border-box;
28
- flex-shrink: 0;
29
- font-weight: bold;
30
- animation: .4s ease-in forwards;
31
- background-color: #FFF;
32
- }
33
-
34
- .tnp-toast-main-wrapper .notification:after {
35
- content: ' ';
36
- position: absolute;
37
- left: 0;
38
- top: 0;
39
- width: 5px;
40
- height: 100%;
41
- }
42
-
43
- .tnp-toast-main-wrapper .notification.push-up {
44
- animation-name: notification-fadeinup;
45
- }
46
-
47
- .tnp-toast-main-wrapper .notification.push-down {
48
- animation-name: notification-fadeindown;
49
- }
50
-
51
- .tnp-toast-main-wrapper .notification.pop-down {
52
- transform: translateY(0);
53
- animation: notification-fadeoutdown .4s forwards;
54
- animation-delay: .25s;
55
- }
56
-
57
- .tnp-toast-main-wrapper .notification.pop-up {
58
- transform: translateY(0);
59
- animation: notification-fadeoutup .4s forwards;
60
- animation-delay: .25s;
61
- }
62
-
63
- .tnp-toast-main-wrapper .notification.top-to-bottom {
64
- margin-bottom: 20px;
65
- }
66
-
67
- .tnp-toast-main-wrapper .notification.bottom-to-top {
68
- margin-top: 20px;
69
- }
70
-
71
- .tnp-toast-main-wrapper .notification.notification-success:after {
72
- background-color: #46b450;
73
- }
74
-
75
- .tnp-toast-main-wrapper .notification.notification-error:after {
76
- background-color: #dd0000;
77
- }
78
-
79
- .tnp-toast-main-wrapper .notification.notification-info:after {
80
- background-color: #0073aa;
81
- }
82
-
83
- .tnp-toast-main-wrapper .notification.notification-warning:after {
84
- background-color: #ffb900;
85
- }
86
-
87
- @keyframes notification-fadeinup {
88
- 0% {
89
- opacity: 0;
90
- transform: translateY(25%);
91
- }
92
-
93
- 100% {
94
- opacity: 1;
95
- transform: translateY(0);
96
- }
97
- }
98
-
99
- @keyframes notification-fadeoutdown {
100
- 100% {
101
- opacity: 1;
102
- transform: translateY(0);
103
- }
104
- 100% {
105
- opacity: 0;
106
- transform: translateY(25%);
107
- }
108
- }
109
-
110
- @keyframes notification-fadeindown {
111
- 0% {
112
- opacity: 0;
113
- transform: translateY(-25%);
114
- }
115
-
116
- 100% {
117
- opacity: 1;
118
- transform: translateY(0);
119
- }
120
- }
121
-
122
- @keyframes notification-fadeoutup {
123
- 100% {
124
- opacity: 1;
125
- transform: translateY(0);
126
- }
127
- 100% {
128
- opacity: 0;
129
- transform: translateY(-25%);
130
- }
131
- }
1
+ .tnp-toast-main-wrapper {
2
+ position: fixed;
3
+ top: 0;
4
+ left: 0;
5
+ box-sizing: border-box;
6
+ height: 100%;
7
+ width: 100%;
8
+ z-index: 9991;
9
+ pointer-events: none;
10
+
11
+ display: flex;
12
+ flex-direction: column;
13
+
14
+ }
15
+
16
+ .tnp-toast-main-wrapper .notification {
17
+ display: block;
18
+ overflow: hidden;
19
+ pointer-events: auto;
20
+ box-shadow: 0 3px 7px 0 rgba(0, 0, 0, .25);
21
+ position: relative;
22
+ padding: 15px;
23
+ padding-left: 20px;
24
+ border-radius: 2px;
25
+ max-width: 300px;
26
+ transform: translateY(25%);
27
+ box-sizing: border-box;
28
+ flex-shrink: 0;
29
+ font-weight: bold;
30
+ animation: .4s ease-in forwards;
31
+ background-color: #FFF;
32
+ }
33
+
34
+ .tnp-toast-main-wrapper .notification:after {
35
+ content: ' ';
36
+ position: absolute;
37
+ left: 0;
38
+ top: 0;
39
+ width: 5px;
40
+ height: 100%;
41
+ }
42
+
43
+ .tnp-toast-main-wrapper .notification.push-up {
44
+ animation-name: notification-fadeinup;
45
+ }
46
+
47
+ .tnp-toast-main-wrapper .notification.push-down {
48
+ animation-name: notification-fadeindown;
49
+ }
50
+
51
+ .tnp-toast-main-wrapper .notification.pop-down {
52
+ transform: translateY(0);
53
+ animation: notification-fadeoutdown .4s forwards;
54
+ animation-delay: .25s;
55
+ }
56
+
57
+ .tnp-toast-main-wrapper .notification.pop-up {
58
+ transform: translateY(0);
59
+ animation: notification-fadeoutup .4s forwards;
60
+ animation-delay: .25s;
61
+ }
62
+
63
+ .tnp-toast-main-wrapper .notification.top-to-bottom {
64
+ margin-bottom: 20px;
65
+ }
66
+
67
+ .tnp-toast-main-wrapper .notification.bottom-to-top {
68
+ margin-top: 20px;
69
+ }
70
+
71
+ .tnp-toast-main-wrapper .notification.notification-success:after {
72
+ background-color: #46b450;
73
+ }
74
+
75
+ .tnp-toast-main-wrapper .notification.notification-error:after {
76
+ background-color: #dd0000;
77
+ }
78
+
79
+ .tnp-toast-main-wrapper .notification.notification-info:after {
80
+ background-color: #0073aa;
81
+ }
82
+
83
+ .tnp-toast-main-wrapper .notification.notification-warning:after {
84
+ background-color: #ffb900;
85
+ }
86
+
87
+ @keyframes notification-fadeinup {
88
+ 0% {
89
+ opacity: 0;
90
+ transform: translateY(25%);
91
+ }
92
+
93
+ 100% {
94
+ opacity: 1;
95
+ transform: translateY(0);
96
+ }
97
+ }
98
+
99
+ @keyframes notification-fadeoutdown {
100
+ 100% {
101
+ opacity: 1;
102
+ transform: translateY(0);
103
+ }
104
+ 100% {
105
+ opacity: 0;
106
+ transform: translateY(25%);
107
+ }
108
+ }
109
+
110
+ @keyframes notification-fadeindown {
111
+ 0% {
112
+ opacity: 0;
113
+ transform: translateY(-25%);
114
+ }
115
+
116
+ 100% {
117
+ opacity: 1;
118
+ transform: translateY(0);
119
+ }
120
+ }
121
+
122
+ @keyframes notification-fadeoutup {
123
+ 100% {
124
+ opacity: 1;
125
+ transform: translateY(0);
126
+ }
127
+ 100% {
128
+ opacity: 0;
129
+ transform: translateY(-25%);
130
+ }
131
+ }
emails/blocks/footer/block.php CHANGED
@@ -1,63 +1,63 @@
1
- <?php
2
- /*
3
- * Name: Footer
4
- * Section: footer
5
- * Description: View online ad profile links
6
- */
7
-
8
- $default_options = array(
9
- 'view' => __('View online', 'newsletter'),
10
- 'view_enabled' => 1,
11
- 'profile' => __('Manage your subscription', 'newsletter'),
12
- 'profile_enabled' => 1,
13
- 'unsubscribe' => __('Unsubscribe', 'newsletter'),
14
- 'unsubscribe_enabled' => 1,
15
- 'font_family' => '',
16
- 'font_size' => '',
17
- 'font_color' => '',
18
- 'font_weight' => '',
19
- 'block_padding_left' => 15,
20
- 'block_padding_right' => 15,
21
- 'block_padding_bottom' => 15,
22
- 'block_padding_top' => 15,
23
- 'block_background' => '',
24
- 'url' => 'profile'
25
- );
26
-
27
- // Migration code
28
- if (!isset($options['profile_enabled']) && isset($options['url'])) {
29
- if ($options['url'] === 'profile') {
30
- $options['profile_enabled'] = 1;
31
- $options['unsubscribe_enabled'] = 0;
32
- } else {
33
- $options['profile_enabled'] = 1;
34
- $options['unsubscribe_enabled'] = 0;
35
- }
36
- }
37
-
38
- $options = array_merge($default_options, $options);
39
-
40
- $text_style = TNP_Composer::get_text_style($options, '', $composer, ['scale'=>0.8]);
41
-
42
- $links = [];
43
- if ($options['unsubscribe_enabled']) {
44
- $links[] = '<a inline-class="text" href="{unsubscription_url}" target="_blank">' . esc_html($options['unsubscribe']) . '</a>';
45
- }
46
- if ($options['profile_enabled']) {
47
- $links[] = '<a inline-class="text" href="{profile_url}" target="_blank">' . esc_html($options['profile']) . '</a>';
48
- }
49
- if ($options['view_enabled']) {
50
- $links[] = '<a inline-class="text" href="{email_url}" target="_blank">' . esc_html($options['view']) . '</a>';
51
- }
52
-
53
- ?>
54
- <style>
55
- .text {
56
- <?php $text_style->echo_css()?>
57
- text-decoration: none;
58
- line-height: normal;
59
- }
60
- </style>
61
-
62
- <?php echo implode('<span inline-class="text">&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;</span>', $links) ?>
63
-
1
+ <?php
2
+ /*
3
+ * Name: Footer
4
+ * Section: footer
5
+ * Description: View online ad profile links
6
+ */
7
+
8
+ $default_options = array(
9
+ 'view' => __('View online', 'newsletter'),
10
+ 'view_enabled' => 1,
11
+ 'profile' => __('Manage your subscription', 'newsletter'),
12
+ 'profile_enabled' => 1,
13
+ 'unsubscribe' => __('Unsubscribe', 'newsletter'),
14
+ 'unsubscribe_enabled' => 1,
15
+ 'font_family' => '',
16
+ 'font_size' => '',
17
+ 'font_color' => '',
18
+ 'font_weight' => '',
19
+ 'block_padding_left' => 15,
20
+ 'block_padding_right' => 15,
21
+ 'block_padding_bottom' => 15,
22
+ 'block_padding_top' => 15,
23
+ 'block_background' => '',
24
+ 'url' => 'profile'
25
+ );
26
+
27
+ // Migration code
28
+ if (!isset($options['profile_enabled']) && isset($options['url'])) {
29
+ if ($options['url'] === 'profile') {
30
+ $options['profile_enabled'] = 1;
31
+ $options['unsubscribe_enabled'] = 0;
32
+ } else {
33
+ $options['profile_enabled'] = 1;
34
+ $options['unsubscribe_enabled'] = 0;
35
+ }
36
+ }
37
+
38
+ $options = array_merge($default_options, $options);
39
+
40
+ $text_style = TNP_Composer::get_text_style($options, '', $composer, ['scale'=>0.8]);
41
+
42
+ $links = [];
43
+ if ($options['unsubscribe_enabled']) {
44
+ $links[] = '<a inline-class="text" href="{unsubscription_url}" target="_blank">' . esc_html($options['unsubscribe']) . '</a>';
45
+ }
46
+ if ($options['profile_enabled']) {
47
+ $links[] = '<a inline-class="text" href="{profile_url}" target="_blank">' . esc_html($options['profile']) . '</a>';
48
+ }
49
+ if ($options['view_enabled']) {
50
+ $links[] = '<a inline-class="text" href="{email_url}" target="_blank">' . esc_html($options['view']) . '</a>';
51
+ }
52
+
53
+ ?>
54
+ <style>
55
+ .text {
56
+ <?php $text_style->echo_css()?>
57
+ text-decoration: none;
58
+ line-height: normal;
59
+ }
60
+ </style>
61
+
62
+ <?php echo implode('<span inline-class="text">&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;</span>', $links) ?>
63
+
emails/blocks/header/block.php CHANGED
@@ -1,49 +1,49 @@
1
- <?php
2
-
3
- /*
4
- * Name: Header
5
- * Section: header
6
- * Description: Default header with company info
7
- */
8
-
9
- $default_options = array(
10
- 'font_family' => '',
11
- 'font_size' => '',
12
- 'font_color' => '',
13
- 'font_weight' => '',
14
- 'logo_width' => 120,
15
- 'block_padding_top' => 15,
16
- 'block_padding_bottom' => 15,
17
- 'block_padding_left' => 15,
18
- 'block_padding_right' => 15,
19
- 'block_background' => '',
20
- 'layout' => ''
21
- );
22
- $options = array_merge($default_options, $options);
23
-
24
- if (empty($info['header_logo']['id'])) {
25
- $media = false;
26
- } else {
27
- $media = tnp_get_media($info['header_logo']['id'], 'large');
28
- if ($media) {
29
- $media->alt = $info['header_title'];
30
- $media->link = home_url();
31
- }
32
- }
33
-
34
- $empty = !$media && empty($info['header_sub']) && empty($info['header_title']);
35
-
36
- if ($empty) {
37
- echo '<p>Please, set your company info.</p>';
38
- } elseif ($options['layout'] === 'logo') {
39
- include __DIR__ . '/layout-logo.php';
40
- return;
41
- } elseif ($options['layout'] === 'titlemotto') {
42
- include __DIR__ . '/layout-titlemotto.php';
43
- return;
44
- } else {
45
- include __DIR__ . '/layout-default.php';
46
- return;
47
- }
48
- ?>
49
-
1
+ <?php
2
+
3
+ /*
4
+ * Name: Header
5
+ * Section: header
6
+ * Description: Default header with company info
7
+ */
8
+
9
+ $default_options = array(
10
+ 'font_family' => '',
11
+ 'font_size' => '',
12
+ 'font_color' => '',
13
+ 'font_weight' => '',
14
+ 'logo_width' => 120,
15
+ 'block_padding_top' => 15,
16
+ 'block_padding_bottom' => 15,
17
+ 'block_padding_left' => 15,
18
+ 'block_padding_right' => 15,
19
+ 'block_background' => '',
20
+ 'layout' => ''
21
+ );
22
+ $options = array_merge($default_options, $options);
23
+
24
+ if (empty($info['header_logo']['id'])) {
25
+ $media = false;
26
+ } else {
27
+ $media = tnp_get_media($info['header_logo']['id'], 'large');
28
+ if ($media) {
29
+ $media->alt = $info['header_title'];
30
+ $media->link = home_url();
31
+ }
32
+ }
33
+
34
+ $empty = !$media && empty($info['header_sub']) && empty($info['header_title']);
35
+
36
+ if ($empty) {
37
+ echo '<p>Please, set your company info.</p>';
38
+ } elseif ($options['layout'] === 'logo') {
39
+ include __DIR__ . '/layout-logo.php';
40
+ return;
41
+ } elseif ($options['layout'] === 'titlemotto') {
42
+ include __DIR__ . '/layout-titlemotto.php';
43
+ return;
44
+ } else {
45
+ include __DIR__ . '/layout-default.php';
46
+ return;
47
+ }
48
+ ?>
49
+
emails/blocks/header/layout-logo.php CHANGED
@@ -1,12 +1,12 @@
1
- <?php
2
- if (!$media) {
3
- echo '<p>Set your logo on company info page, thank you.</p>';
4
- return;
5
- }
6
- $image_width = 600-$options['block_padding_left']-$options['block_padding_right'];
7
- if ($options['logo_width']) {
8
- $image_width = min($options['logo_width'], $image_width);
9
- }
10
- $media->set_width($image_width);
11
- ?>
12
- <a href="<?php echo esc_url($media->link) ?>" target="_blank"><img src="<?php echo $media->url ?>" width="<?php echo $media->width ?>" height="<?php echo $media->height ?>" border="0" alt="<?php echo esc_attr($media->alt) ?>"></a>
1
+ <?php
2
+ if (!$media) {
3
+ echo '<p>Set your logo on company info page, thank you.</p>';
4
+ return;
5
+ }
6
+ $image_width = 600-$options['block_padding_left']-$options['block_padding_right'];
7
+ if ($options['logo_width']) {
8
+ $image_width = min($options['logo_width'], $image_width);
9
+ }
10
+ $media->set_width($image_width);
11
+ ?>
12
+ <a href="<?php echo esc_url($media->link) ?>" target="_blank"><img src="<?php echo $media->url ?>" width="<?php echo $media->width ?>" height="<?php echo $media->height ?>" border="0" alt="<?php echo esc_attr($media->alt) ?>"></a>
emails/blocks/image/block.php CHANGED
@@ -55,12 +55,24 @@ if (!empty($options['width'])) {
55
  $media->link = $options['url'];
56
  $media->alt = $options['image-alt'];
57
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58
  ?>
59
 
60
- <table border="0" cellspacing="0" cellpadding="0" width="100%" class="responsive" style="margin: 0;">
61
- <tr>
62
- <td align="<?php echo esc_attr($options['align']) ?>" valign="middle" width="100%">
63
- <?php echo TNP_Composer::image($media); ?>
64
- </td>
65
- </tr>
66
- </table>
55
  $media->link = $options['url'];
56
  $media->alt = $options['image-alt'];
57
 
58
+ if ($media->link) {
59
+ echo '<a href="', esc_attr($media->link), '" target="_blank" rel="noopener nofollow" style="display: block; font-size: 0; text-decoration: none; line-height: normal!important">';
60
+ } else {
61
+ }
62
+
63
+
64
+ echo '<img src="', esc_attr($media->url), '" width="', esc_attr($media->width), '"';
65
+ if ($media->height) {
66
+ echo ' height="', esc_attr($media->height), '"';
67
+ }
68
+ echo ' alt="', esc_attr($media->alt), '"';
69
+ // The font size is important for the alt text
70
+ echo ' border="0" style="display: block; max-width: ', esc_attr($media->width), 'px; width: 100%; padding: 0; border: 0; font-size: 12px"';
71
+ echo '>';
72
+
73
+ if ($media->link) {
74
+ echo '</a>';
75
+ } else {
76
+ }
77
  ?>
78
 
 
 
 
 
 
 
 
emails/blocks/posts/layout-two.php CHANGED
@@ -1,136 +1,136 @@
1
- <?php
2
- $size = array('width' => 600, 'height' => 400, "crop" => true);
3
- $total_width = 600 - $options['block_padding_left'] - $options['block_padding_right'];
4
- $column_width = $total_width / 2 - 20;
5
-
6
- $title_style = TNP_Composer::get_style($options, 'title', $composer, 'title', ['scale' => .8]);
7
- $text_style = TNP_Composer::get_style($options, '', $composer, 'text');
8
-
9
- $items = [];
10
- ?>
11
- <style>
12
- .title {
13
- font-family: <?php echo $title_style->font_family ?>;
14
- font-size: <?php echo $title_style->font_size ?>px;
15
- font-weight: <?php echo $title_style->font_weight ?>;
16
- color: <?php echo $title_style->font_color ?>;
17
- line-height: 1.3em;
18
- padding: 15px 0 0 0;
19
- }
20
-
21
- .excerpt {
22
- font-family: <?php echo $text_style->font_family ?>;
23
- font-size: <?php echo $text_style->font_size ?>px;
24
- font-weight: <?php echo $text_style->font_weight ?>;
25
- color: <?php echo $text_style->font_color ?>;
26
- line-height: 1.4em;
27
- padding: 5px 0 0 0;
28
- }
29
-
30
- .meta {
31
- font-family: <?php echo $text_style->font_family ?>;
32
- color: <?php echo $text_style->font_color ?>;
33
- font-size: <?php echo round($text_style->font_size * 0.9) ?>px;
34
- font-weight: <?php echo $text_style->font_weight ?>;
35
- padding: 10px 0 0 0;
36
- font-style: italic;
37
- line-height: normal !important;
38
- }
39
- .button {
40
- padding: 15px 0;
41
- }
42
- .column-left {
43
- padding-right: 10px;
44
- padding-bottom: 20px;
45
- }
46
- .column-right {
47
- padding-left: 10px;
48
- padding-bottom: 20px;
49
- }
50
-
51
- </style>
52
-
53
-
54
- <?php foreach ($posts AS $p) { ?>
55
- <?php
56
- $media = null;
57
- if ($show_image) {
58
- $media = tnp_composer_block_posts_get_media($p, $size, $image_placeholder_url);
59
- if ($media) {
60
- $media->link = tnp_post_permalink($p);
61
- $media->set_width($column_width);
62
- }
63
- }
64
-
65
- $meta = [];
66
-
67
- if ($show_date) {
68
- $meta[] = tnp_post_date($p);
69
- }
70
-
71
- if ($show_author) {
72
- $author_object = get_user_by('id', $p->post_author);
73
- if ($author_object) {
74
- $meta[] = $author_object->display_name;
75
- }
76
- }
77
-
78
- $button_options['button_url'] = tnp_post_permalink($p);
79
- ob_start();
80
- ?>
81
- <table cellpadding="0" cellspacing="0" border="0" width="100%">
82
- <?php if ($media) { ?>
83
- <tr>
84
- <td align="center" valign="middle">
85
- <?php echo TNP_Composer::image($media, ['class' => 'fluid']) ?>
86
- </td>
87
- </tr>
88
- <?php } ?>
89
- <tr>
90
- <td align="center" inline-class="title" class="title tnpc-row-edit tnpc-inline-editable" data-type="title" data-id="<?php echo $p->ID ?>">
91
- <?php
92
- echo TNP_Composer::is_post_field_edited_inline($options['inline_edits'], 'title', $p->ID) ?
93
- TNP_Composer::get_edited_inline_post_field($options['inline_edits'], 'title', $p->ID) :
94
- tnp_post_title($p)
95
- ?>
96
- </td>
97
- </tr>
98
- <?php if ($meta) { ?>
99
- <tr>
100
- <td align="center" inline-class="meta" class="meta">
101
- <?php echo esc_html(implode(' - ', $meta)) ?>
102
- </td>
103
- </tr>
104
- <?php } ?>
105
-
106
-
107
- <?php if ($excerpt_length) { ?>
108
- <tr>
109
- <td align="center" inline-class="excerpt" class="title tnpc-row-edit tnpc-inline-editable" data-type="text" data-id="<?php echo $p->ID ?>">
110
- <?php
111
- echo TNP_Composer::is_post_field_edited_inline($options['inline_edits'], 'text', $p->ID) ?
112
- TNP_Composer::get_edited_inline_post_field($options['inline_edits'], 'text', $p->ID) :
113
- tnp_post_excerpt($p, $excerpt_length)
114
- ?>
115
- </td>
116
- </tr>
117
- <?php } ?>
118
-
119
- <?php if ($show_read_more_button) { ?>
120
- <tr>
121
- <td align="center" inline-class="button">
122
- <?php echo TNP_Composer::button($button_options) ?>
123
- </td>
124
- </tr>
125
- <?php } ?>
126
- </table>
127
- <?php
128
- $items[] = ob_get_clean();
129
- }
130
- ?>
131
-
132
-
133
- <?php echo TNP_Composer::grid($items, ['width' => $total_width, 'responsive' => true, 'padding' => 5]) ?>
134
-
135
-
136
-
1
+ <?php
2
+ $size = array('width' => 600, 'height' => 400, "crop" => true);
3
+ $total_width = 600 - $options['block_padding_left'] - $options['block_padding_right'];
4
+ $column_width = $total_width / 2 - 20;
5
+
6
+ $title_style = TNP_Composer::get_style($options, 'title', $composer, 'title', ['scale' => .8]);
7
+ $text_style = TNP_Composer::get_style($options, '', $composer, 'text');
8
+
9
+ $items = [];
10
+ ?>
11
+ <style>
12
+ .title {
13
+ font-family: <?php echo $title_style->font_family ?>;
14
+ font-size: <?php echo $title_style->font_size ?>px;
15
+ font-weight: <?php echo $title_style->font_weight ?>;
16
+ color: <?php echo $title_style->font_color ?>;
17
+ line-height: 1.3em;
18
+ padding: 15px 0 0 0;
19
+ }
20
+
21
+ .excerpt {
22
+ font-family: <?php echo $text_style->font_family ?>;
23
+ font-size: <?php echo $text_style->font_size ?>px;
24
+ font-weight: <?php echo $text_style->font_weight ?>;
25
+ color: <?php echo $text_style->font_color ?>;
26
+ line-height: 1.4em;
27
+ padding: 5px 0 0 0;
28
+ }
29
+
30
+ .meta {
31
+ font-family: <?php echo $text_style->font_family ?>;
32
+ color: <?php echo $text_style->font_color ?>;
33
+ font-size: <?php echo round($text_style->font_size * 0.9) ?>px;
34
+ font-weight: <?php echo $text_style->font_weight ?>;
35
+ padding: 10px 0 0 0;
36
+ font-style: italic;
37
+ line-height: normal !important;
38
+ }
39
+ .button {
40
+ padding: 15px 0;
41
+ }
42
+ .column-left {
43
+ padding-right: 10px;
44
+ padding-bottom: 20px;
45
+ }
46
+ .column-right {
47
+ padding-left: 10px;
48
+ padding-bottom: 20px;
49
+ }
50
+
51
+ </style>
52
+
53
+
54
+ <?php foreach ($posts AS $p) { ?>
55
+ <?php
56
+ $media = null;
57
+ if ($show_image) {
58
+ $media = tnp_composer_block_posts_get_media($p, $size, $image_placeholder_url);
59
+ if ($media) {
60
+ $media->link = tnp_post_permalink($p);
61
+ $media->set_width($column_width);
62
+ }
63
+ }
64
+
65
+ $meta = [];
66
+
67
+ if ($show_date) {
68
+ $meta[] = tnp_post_date($p);
69
+ }
70
+
71
+ if ($show_author) {
72
+ $author_object = get_user_by('id', $p->post_author);
73
+ if ($author_object) {
74
+ $meta[] = $author_object->display_name;
75
+ }
76
+ }
77
+
78
+ $button_options['button_url'] = tnp_post_permalink($p);
79
+ ob_start();
80
+ ?>
81
+ <table cellpadding="0" cellspacing="0" border="0" width="100%">
82
+ <?php if ($media) { ?>
83
+ <tr>
84
+ <td align="center" valign="middle">
85
+ <?php echo TNP_Composer::image($media, ['class' => 'fluid']) ?>
86
+ </td>
87
+ </tr>
88
+ <?php } ?>
89
+ <tr>
90
+ <td align="center" inline-class="title" class="title tnpc-row-edit tnpc-inline-editable" data-type="title" data-id="<?php echo $p->ID ?>">
91
+ <?php
92
+ echo TNP_Composer::is_post_field_edited_inline($options['inline_edits'], 'title', $p->ID) ?
93
+ TNP_Composer::get_edited_inline_post_field($options['inline_edits'], 'title', $p->ID) :
94
+ tnp_post_title($p)
95
+ ?>
96
+ </td>
97
+ </tr>
98
+ <?php if ($meta) { ?>
99
+ <tr>
100
+ <td align="center" inline-class="meta" class="meta">
101
+ <?php echo esc_html(implode(' - ', $meta)) ?>
102
+ </td>
103
+ </tr>
104
+ <?php } ?>
105
+
106
+
107
+ <?php if ($excerpt_length) { ?>
108
+ <tr>
109
+ <td align="center" inline-class="excerpt" class="title tnpc-row-edit tnpc-inline-editable" data-type="text" data-id="<?php echo $p->ID ?>">
110
+ <?php
111
+ echo TNP_Composer::is_post_field_edited_inline($options['inline_edits'], 'text', $p->ID) ?
112
+ TNP_Composer::get_edited_inline_post_field($options['inline_edits'], 'text', $p->ID) :
113
+ tnp_post_excerpt($p, $excerpt_length)
114
+ ?>
115
+ </td>
116
+ </tr>
117
+ <?php } ?>
118
+
119
+ <?php if ($show_read_more_button) { ?>
120
+ <tr>
121
+ <td align="center" inline-class="button">
122
+ <?php echo TNP_Composer::button($button_options) ?>
123
+ </td>
124
+ </tr>
125
+ <?php } ?>
126
+ </table>
127
+ <?php
128
+ $items[] = ob_get_clean();
129
+ }
130
+ ?>
131
+
132
+
133
+ <?php echo TNP_Composer::grid($items, ['width' => $total_width, 'responsive' => true, 'padding' => 5]) ?>
134
+
135
+
136
+
emails/composer.php CHANGED
@@ -1,175 +1,175 @@
1
- <?php
2
- /* @var $this NewsletterEmails */
3
- defined('ABSPATH') || exit;
4
-
5
- require_once NEWSLETTER_INCLUDES_DIR . '/controls.php';
6
-
7
- $controls = new NewsletterControls();
8
- $module = NewsletterEmails::instance();
9
-
10
- wp_enqueue_style('tnpc-newsletter-style', home_url('/') . '?na=emails-composer-css');
11
-
12
- include NEWSLETTER_INCLUDES_DIR . '/codemirror.php';
13
-
14
- $email = null;
15
-
16
- if ($controls->is_action()) {
17
-
18
- if ($controls->is_action('save_preset')) {
19
- $this->admin_logger->info('Saving new preset: ' . $controls->data['subject']);
20
- // Create new preset email
21
- $email = new stdClass();
22
- TNP_Composer::update_email($email, $controls);
23
- $email->type = NewsletterEmails::PRESET_EMAIL_TYPE;
24
- $email->editor = NewsletterEmails::EDITOR_COMPOSER;
25
- $email->subject = $controls->data['subject'];
26
- $email->message = $controls->data['message'];
27
-
28
- $email = Newsletter::instance()->save_email($email);
29
-
30
- $redirect = $module->get_admin_page_url('composer');
31
- $controls->js_redirect($redirect);
32
-
33
- return;
34
- }
35
-
36
- if ($controls->is_action('update_preset')) {
37
- $this->admin_logger->info('Updating preset ' . $_POST['preset_id']);
38
- $email = Newsletter::instance()->get_email($_POST['preset_id']);
39
- TNP_Composer::update_email($email, $controls);
40
-
41
- $email->subject = $controls->data['subject'];
42
-
43
- // We store only the blocks, after the TNP_Composer::update_email(...) call we have the full HTML
44
- $email->message = $controls->data['message'];
45
-
46
- $email = Newsletter::instance()->save_email($email);
47
-
48
- $redirect = $module->get_admin_page_url('composer');
49
- $controls->js_redirect($redirect);
50
- }
51
-
52
-
53
- if (empty($_GET['id'])) {
54
-
55
- $this->admin_logger->info('Saving new newsletter from composer');
56
-
57
- // Create a new email
58
- $email = new stdClass();
59
- $email->status = 'new';
60
- $email->track = Newsletter::instance()->options['track'];
61
- $email->token = $module->get_token();
62
- $email->message_text = "This email requires a modern e-mail reader but you can view the email online here:\n{email_url}.\nThank you, " . wp_specialchars_decode(get_option('blogname'), ENT_QUOTES) .
63
- "\nTo change your subscription follow: {profile_url}.";
64
- $email->editor = NewsletterEmails::EDITOR_COMPOSER;
65
- $email->type = 'message';
66
- $email->send_on = time();
67
- $email->query = "select * from " . NEWSLETTER_USERS_TABLE . " where status='C'";
68
-
69
- TNP_Composer::update_email($email, $controls);
70
-
71
- $email = Newsletter::instance()->save_email($email);
72
- if ($controls->is_action('preview')) {
73
- $redirect = $module->get_admin_page_url('edit');
74
- } else {
75
- $redirect = $module->get_admin_page_url('composer');
76
- }
77
-
78
- $controls->js_redirect($redirect . '&id=' . $email->id);
79
- } else {
80
- $this->admin_logger->info('Saving newsletter ' . $_GET['id'] . ' from composer');
81
- $email = Newsletter::instance()->get_email($_GET['id']);
82
- if ($email->updated != $controls->data['updated']) {
83
- $controls->errors = 'This newsletter has been modified by someone else. Cannot save.';
84
- if (!empty($email->options['sender_email'])) {
85
- $controls->data['sender_email'] = $email->options['sender_email'];
86
- } else {
87
- $controls->data['sender_email'] = Newsletter::instance()->options['sender_email'];
88
- }
89
-
90
- if (!empty($email->options['sender_name'])) {
91
- $controls->data['sender_name'] = $email->options['sender_name'];
92
- } else {
93
- $controls->data['sender_name'] = Newsletter::instance()->options['sender_name'];
94
- }
95
- } else {
96
- TNP_Composer::update_email($email, $controls);
97
-
98
- if (empty($email->options['text_message_mode'])) {
99
- $text = TNP_Composer::convert_to_text($email->message);
100
- if ($text) {
101
- $email->message_text = TNP_Composer::convert_to_text($email->message);
102
- }
103
- }
104
-
105
- $email->updated = time();
106
- $email = Newsletter::instance()->save_email($email);
107
- TNP_Composer::prepare_controls($controls, $email);
108
- if ($controls->is_action('save')) {
109
- $controls->add_message_saved();
110
- }
111
- }
112
- }
113
-
114
- if ($controls->is_action('preview')) {
115
- $redirect = $module->get_admin_page_url('edit');
116
- $controls->js_redirect($redirect . '&id=' . $email->id);
117
- }
118
-
119
- if ($controls->is_action('test')) {
120
- $module->send_test_email($module->get_email($email->id), $controls);
121
- }
122
-
123
- if ($controls->is_action('send-test-to-email-address')) {
124
- $custom_email = sanitize_email($_POST['test_address_email']);
125
- if (!empty($custom_email)) {
126
- try {
127
- $message = $module->send_test_newsletter_to_email_address($module->get_email($email->id), $custom_email);
128
- $controls->messages .= $message;
129
- } catch (Exception $e) {
130
- $controls->errors = __('Newsletter should be saved before send a test', 'newsletter');
131
- }
132
- } else {
133
- $controls->errors = __('Empty email address', 'newsletter');
134
- }
135
- }
136
- } else {
137
-
138
- if (!empty($_GET['id'])) {
139
- $email = Newsletter::instance()->get_email((int) $_GET['id']);
140
- }
141
- TNP_Composer::prepare_controls($controls, $email);
142
- }
143
- ?>
144
-
145
- <div id="tnp-notification">
146
- <?php
147
- $controls->show();
148
- $controls->messages = '';
149
- $controls->errors = '';
150
- ?>
151
- </div>
152
-
153
- <div class="wrap tnp-emails-composer" id="tnp-wrap">
154
-
155
- <?php $controls->composer_load_v2(true); ?>
156
-
157
- <div id="tnp-heading" class="tnp-composer-heading">
158
- <div class="tnpc-logo">
159
- <p>The Newsletter Plugin <strong>Composer</strong></p>
160
- </div>
161
- <div class="tnpc-controls">
162
- <form method="post" action="" id="tnpc-form">
163
- <?php $controls->init(); ?>
164
-
165
- <?php $controls->composer_fields_v2(); ?>
166
-
167
- <?php $controls->button('update_preset', __('Update preset', 'newsletter'), 'tnpc_update_preset(this.form)', 'update-preset-button'); ?>
168
- <?php $controls->button('save_preset', __('Save as preset', 'newsletter'), 'tnpc_save_preset(this.form)', 'save-preset-button'); ?>
169
- <?php $controls->button_confirm('reset', __('Back to last save', 'newsletter'), 'Are you sure?'); ?>
170
- <?php $controls->button('save', __('Save', 'newsletter'), 'tnpc_save(this.form); this.form.submit();'); ?>
171
- <?php $controls->button('preview', __('Next', 'newsletter') . ' &raquo;', 'tnpc_save(this.form); this.form.submit();'); ?>
172
- </form>
173
- </div>
174
- </div>
175
- </div>
1
+ <?php
2
+ /* @var $this NewsletterEmails */
3
+ defined('ABSPATH') || exit;
4
+
5
+ require_once NEWSLETTER_INCLUDES_DIR . '/controls.php';
6
+
7
+ $controls = new NewsletterControls();
8
+ $module = NewsletterEmails::instance();
9
+
10
+ wp_enqueue_style('tnpc-newsletter-style', home_url('/') . '?na=emails-composer-css');
11
+
12
+ include NEWSLETTER_INCLUDES_DIR . '/codemirror.php';
13
+
14
+ $email = null;
15
+
16
+ if ($controls->is_action()) {
17
+
18
+ if ($controls->is_action('save_preset')) {
19
+ $this->admin_logger->info('Saving new preset: ' . $controls->data['subject']);
20
+ // Create new preset email
21
+ $email = new stdClass();
22
+ TNP_Composer::update_email($email, $controls);
23
+ $email->type = NewsletterEmails::PRESET_EMAIL_TYPE;
24
+ $email->editor = NewsletterEmails::EDITOR_COMPOSER;
25
+ $email->subject = $controls->data['subject'];
26
+ $email->message = $controls->data['message'];
27
+
28
+ $email = Newsletter::instance()->save_email($email);
29
+
30
+ $redirect = $module->get_admin_page_url('composer');
31
+ $controls->js_redirect($redirect);
32
+
33
+ return;
34
+ }
35
+
36
+ if ($controls->is_action('update_preset')) {
37
+ $this->admin_logger->info('Updating preset ' . $_POST['preset_id']);
38
+ $email = Newsletter::instance()->get_email($_POST['preset_id']);
39
+ TNP_Composer::update_email($email, $controls);
40
+
41
+ $email->subject = $controls->data['subject'];
42
+
43
+ // We store only the blocks, after the TNP_Composer::update_email(...) call we have the full HTML
44
+ $email->message = $controls->data['message'];
45
+
46
+ $email = Newsletter::instance()->save_email($email);
47
+
48
+ $redirect = $module->get_admin_page_url('composer');
49
+ $controls->js_redirect($redirect);
50
+ }
51
+
52
+
53
+ if (empty($_GET['id'])) {
54
+
55
+ $this->admin_logger->info('Saving new newsletter from composer');
56
+
57
+ // Create a new email
58
+ $email = new stdClass();
59
+ $email->status = 'new';
60
+ $email->track = Newsletter::instance()->options['track'];
61
+ $email->token = $module->get_token();
62
+ $email->message_text = "This email requires a modern e-mail reader but you can view the email online here:\n{email_url}.\nThank you, " . wp_specialchars_decode(get_option('blogname'), ENT_QUOTES) .
63
+ "\nTo change your subscription follow: {profile_url}.";
64
+ $email->editor = NewsletterEmails::EDITOR_COMPOSER;
65
+ $email->type = 'message';
66
+ $email->send_on = time();
67
+ $email->query = "select * from " . NEWSLETTER_USERS_TABLE . " where status='C'";
68
+
69
+ TNP_Composer::update_email($email, $controls);
70
+
71
+ $email = Newsletter::instance()->save_email($email);
72
+ if ($controls->is_action('preview')) {
73
+ $redirect = $module->get_admin_page_url('edit');
74
+ } else {
75
+ $redirect = $module->get_admin_page_url('composer');
76
+ }
77
+
78
+ $controls->js_redirect($redirect . '&id=' . $email->id);
79
+ } else {
80
+ $this->admin_logger->info('Saving newsletter ' . $_GET['id'] . ' from composer');
81
+ $email = Newsletter::instance()->get_email($_GET['id']);
82
+ if ($email->updated != $controls->data['updated']) {
83
+ $controls->errors = 'This newsletter has been modified by someone else. Cannot save.';
84
+ if (!empty($email->options['sender_email'])) {
85
+ $controls->data['sender_email'] = $email->options['sender_email'];
86
+ } else {
87
+ $controls->data['sender_email'] = Newsletter::instance()->options['sender_email'];
88
+ }
89
+
90
+ if (!empty($email->options['sender_name'])) {
91
+ $controls->data['sender_name'] = $email->options['sender_name'];
92
+ } else {
93
+ $controls->data['sender_name'] = Newsletter::instance()->options['sender_name'];
94
+ }
95
+ } else {
96
+ TNP_Composer::update_email($email, $controls);
97
+
98
+ if (empty($email->options['text_message_mode'])) {
99
+ $text = TNP_Composer::convert_to_text($email->message);
100
+ if ($text) {
101
+ $email->message_text = TNP_Composer::convert_to_text($email->message);
102
+ }
103
+ }
104
+
105
+ $email->updated = time();
106
+ $email = Newsletter::instance()->save_email($email);
107
+ TNP_Composer::prepare_controls($controls, $email);
108
+ if ($controls->is_action('save')) {
109
+ $controls->add_message_saved();
110
+ }
111
+ }
112
+ }
113
+
114
+ if ($controls->is_action('preview')) {
115
+ $redirect = $module->get_admin_page_url('edit');
116
+ $controls->js_redirect($redirect . '&id=' . $email->id);
117
+ }
118
+
119
+ if ($controls->is_action('test')) {
120
+ $module->send_test_email($module->get_email($email->id), $controls);
121
+ }
122
+
123
+ if ($controls->is_action('send-test-to-email-address')) {
124
+ $custom_email = sanitize_email($_POST['test_address_email']);
125
+ if (!empty($custom_email)) {
126
+ try {
127
+ $message = $module->send_test_newsletter_to_email_address($module->get_email($email->id), $custom_email);
128
+ $controls->messages .= $message;
129
+ } catch (Exception $e) {
130
+ $controls->errors = __('Newsletter should be saved before send a test', 'newsletter');
131
+ }
132
+ } else {
133
+ $controls->errors = __('Empty email address', 'newsletter');
134
+ }
135
+ }
136
+ } else {
137
+
138
+ if (!empty($_GET['id'])) {
139
+ $email = Newsletter::instance()->get_email((int) $_GET['id']);
140
+ }
141
+ TNP_Composer::prepare_controls($controls, $email);
142
+ }
143
+ ?>
144
+
145
+ <div id="tnp-notification">
146
+ <?php
147
+ $controls->show();
148
+ $controls->messages = '';
149
+ $controls->errors = '';
150
+ ?>
151
+ </div>
152
+
153
+ <div class="wrap tnp-emails-composer" id="tnp-wrap">
154
+
155
+ <?php $controls->composer_load_v2(true); ?>
156
+
157
+ <div id="tnp-heading" class="tnp-composer-heading">
158
+ <div class="tnpc-logo">
159
+ <p>The Newsletter Plugin <strong>Composer</strong></p>
160
+ </div>
161
+ <div class="tnpc-controls">
162
+ <form method="post" action="" id="tnpc-form">
163
+ <?php $controls->init(); ?>
164
+
165
+ <?php $controls->composer_fields_v2(); ?>
166
+
167
+ <?php $controls->button('update_preset', __('Update preset', 'newsletter'), 'tnpc_update_preset(this.form)', 'update-preset-button'); ?>
168
+ <?php $controls->button('save_preset', __('Save as preset', 'newsletter'), 'tnpc_save_preset(this.form)', 'save-preset-button'); ?>
169
+ <?php $controls->button_confirm('reset', __('Back to last save', 'newsletter'), 'Are you sure?'); ?>
170
+ <?php $controls->button('save', __('Save', 'newsletter'), 'tnpc_save(this.form); this.form.submit();'); ?>
171
+ <?php $controls->button('preview', __('Next', 'newsletter') . ' &raquo;', 'tnpc_save(this.form); this.form.submit();'); ?>
172
+ </form>
173
+ </div>
174
+ </div>
175
+ </div>
emails/edit.php CHANGED
@@ -1,607 +1,607 @@
1
- <?php
2
- /* @var $this NewsletterEmails */
3
- defined('ABSPATH') || exit;
4
-
5
- /* @var $wpdb wpdb */
6
- require_once NEWSLETTER_INCLUDES_DIR . '/controls.php';
7
- $controls = new NewsletterControls();
8
- $module = NewsletterEmails::instance();
9
-
10
- function tnp_prepare_controls($email, $controls) {
11
- $controls->data = $email;
12
-
13
- foreach ($email['options'] as $name => $value) {
14
- $controls->data['options_' . $name] = $value;
15
- }
16
- }
17
-
18
- // Always required
19
- $email = $this->get_email($_GET['id'], ARRAY_A);
20
-
21
- if (empty($email)) {
22
- echo 'Wrong email identifier';
23
- return;
24
- }
25
-
26
- $email_id = $email['id'];
27
-
28
- /* Satus changes which require a reload */
29
- if ($controls->is_action('pause')) {
30
- $this->admin_logger->info('Newsletter ' . $email_id . ' paused');
31
- $wpdb->update(NEWSLETTER_EMAILS_TABLE, array('status' => 'paused'), array('id' => $email_id));
32
- $email = $this->get_email($_GET['id'], ARRAY_A);
33
- tnp_prepare_controls($email, $controls);
34
- }
35
-
36
- if ($controls->is_action('continue')) {
37
- $this->admin_logger->info('Newsletter ' . $email_id . ' restarted');
38
- $wpdb->update(NEWSLETTER_EMAILS_TABLE, array('status' => 'sending'), array('id' => $email_id));
39
- $email = $this->get_email($_GET['id'], ARRAY_A);
40
- tnp_prepare_controls($email, $controls);
41
- }
42
-
43
- if ($controls->is_action('abort')) {
44
- $this->admin_logger->info('Newsletter ' . $email_id . ' aborted');
45
- $wpdb->query("update " . NEWSLETTER_EMAILS_TABLE . " set last_id=0, sent=0, status='new' where id=" . $email_id);
46
- $email = $this->get_email($_GET['id'], ARRAY_A);
47
- tnp_prepare_controls($email, $controls);
48
- $controls->messages = __('Delivery definitively cancelled', 'newsletter');
49
- }
50
-
51
- if ($controls->is_action('change-private')) {
52
- $data = [];
53
- $data['private'] = $controls->data['private'];
54
- $data['id'] = $email['id'];
55
- $email = $this->save_email($data, ARRAY_A);
56
- $controls->add_message_saved();
57
-
58
- tnp_prepare_controls($email, $controls);
59
- }
60
-
61
-
62
- $editor_type = $this->get_editor_type($email);
63
-
64
- // Backward compatibility: preferences conversion
65
- if (!$controls->is_action()) {
66
- if (!isset($email['options']['lists'])) {
67
-
68
- $options_profile = get_option('newsletter_profile');
69
-
70
- if (empty($controls->data['preferences_status_operator'])) {
71
- $email['options']['lists_operator'] = 'or';
72
- } else {
73
- $email['options']['lists_operator'] = 'and';
74
- }
75
- $controls->data['options_lists'] = array();
76
- $controls->data['options_lists_exclude'] = array();
77
-
78
- if (!empty($email['preferences'])) {
79
- $preferences = explode(',', $email['preferences']);
80
- $value = empty($email['options']['preferences_status']) ? 'on' : 'off';
81
-
82
- foreach ($preferences as $x) {
83
- if ($value == 'on') {
84
- $controls->data['options_lists'][] = $x;
85
- } else {
86
- $controls->data['options_lists_exclude'][] = $x;
87
- }
88
- }
89
- }
90
- }
91
- }
92
- // End backward compatibility
93
-
94
- if (!$controls->is_action()) {
95
- tnp_prepare_controls($email, $controls);
96
- }
97
-
98
- if ($controls->is_action('html')) {
99
-
100
- $this->admin_logger->info('Newsletter ' . $email_id . ' converted to HTML');
101
-
102
- $data = [];
103
- $data['editor'] = NewsletterEmails::EDITOR_HTML;
104
- $data['id'] = $email_id;
105
-
106
- // Backward compatibility: clean up the composer flag
107
- $data['options'] = $email['options'];
108
- unset($data['options']['composer']);
109
- // End backward compatibility
110
-
111
- $email = $this->save_email($data, ARRAY_A);
112
- $controls->messages = 'You can now edit the newsletter as pure HTML';
113
-
114
- tnp_prepare_controls($email, $controls);
115
-
116
- $editor_type = NewsletterEmails::EDITOR_HTML;
117
- }
118
-
119
-
120
-
121
- if ($controls->is_action('test') || $controls->is_action('save') || $controls->is_action('send') || $controls->is_action('schedule')) {
122
-
123
- if ($email['updated'] != $controls->data['updated']) {
124
- $controls->errors = 'This newsletter has been modified by someone else. Cannot save.';
125
- } else {
126
- $email['updated'] = time();
127
- if ($controls->is_action('save')) {
128
- $this->admin_logger->info('Saving newsletter: ' . $email_id);
129
- } else if ($controls->is_action('send')) {
130
- $this->admin_logger->info('Sending newsletter: ' . $email_id);
131
- } else if ($controls->is_action('schedule')) {
132
- $this->admin_logger->info('Scheduling newsletter: ' . $email_id);
133
- }
134
-
135
- $email['subject'] = $controls->data['subject'];
136
- $email['track'] = $controls->data['track'];
137
- $email['editor'] = $editor_type;
138
- $email['private'] = $controls->data['private'];
139
- $email['message_text'] = $controls->data['message_text'];
140
- if ($controls->is_action('send')) {
141
- $email['send_on'] = time();
142
- } else {
143
- // Patch, empty on continuation
144
- if (!empty($controls->data['send_on'])) {
145
- $email['send_on'] = $controls->data['send_on'];
146
- }
147
- }
148
-
149
- // Reset and refill the options
150
- // Try without the reset and let's see where the problems are
151
- //$email['options'] = array();
152
- // Reset only specific keys
153
- unset($email['options']['lists']);
154
- unset($email['options']['lists_operator']);
155
- unset($email['options']['lists_exclude']);
156
- unset($email['options']['sex']);
157
- for ($i = 1; $i <= 20; $i++) {
158
- unset($email['options']["profile_$i"]);
159
- }
160
-
161
- // Patch for Geo addon to be solved with a filter
162
- unset($email['options']['countries']);
163
- unset($email['options']['regions']);
164
- unset($email['options']['cities']);
165
-
166
- foreach ($controls->data as $name => $value) {
167
- if (strpos($name, 'options_') === 0) {
168
- $email['options'][substr($name, 8)] = $value;
169
- }
170
- }
171
-
172
- // Before send, we build the query to extract subscriber, so the delivery engine does not
173
- // have to worry about the email parameters
174
- if ($email['options']['status'] == 'S') {
175
- $query = "select * from " . NEWSLETTER_USERS_TABLE . " where status='S'";
176
- } else {
177
- $query = "select * from " . NEWSLETTER_USERS_TABLE . " where status='C'";
178
- }
179
-
180
- if ($email['options']['wp_users'] == '1') {
181
- $query .= " and wp_user_id<>0";
182
- }
183
-
184
- if (!empty($email['options']['language'])) {
185
- $query .= " and language='" . esc_sql((string) $email['options']['language']) . "'";
186
- }
187
-
188
-
189
- $list_where = array();
190
- if (isset($email['options']['lists']) && count($email['options']['lists'])) {
191
- foreach ($email['options']['lists'] as $list) {
192
- $list = (int) $list;
193
- $list_where[] = 'list_' . $list . '=1';
194
- }
195
- }
196
-
197
- if (!empty($list_where)) {
198
- if (isset($email['options']['lists_operator']) && $email['options']['lists_operator'] == 'and') {
199
- $query .= ' and (' . implode(' and ', $list_where) . ')';
200
- } else {
201
- $query .= ' and (' . implode(' or ', $list_where) . ')';
202
- }
203
- }
204
-
205
- // Excluded lists
206
- $list_where = array();
207
- if (isset($email['options']['lists_exclude']) && count($email['options']['lists_exclude'])) {
208
- foreach ($email['options']['lists_exclude'] as $list) {
209
- $list = (int) $list;
210
- $list_where[] = 'list_' . $list . '=0';
211
- }
212
- }
213
- if (!empty($list_where)) {
214
- // Must not be in one of the excluded lists
215
- $query .= ' and (' . implode(' and ', $list_where) . ')';
216
- }
217
-
218
- // Gender
219
- if (isset($email['options']['sex'])) {
220
- $sex = $email['options']['sex'];
221
- if (is_array($sex) && count($sex)) {
222
- $query .= " and sex in (";
223
- foreach ($sex as $x) {
224
- $query .= "'" . esc_sql((string) $x) . "', ";
225
- }
226
- $query = substr($query, 0, -2);
227
- $query .= ")";
228
- }
229
- }
230
-
231
- // Profile fields filter
232
- $profile_clause = array();
233
- for ($i = 1; $i <= 20; $i++) {
234
- if (isset($email["options"]["profile_$i"]) && count($email["options"]["profile_$i"])) {
235
- $profile_clause[] = 'profile_' . $i . " IN ('" . implode("','", esc_sql($email["options"]["profile_$i"])) . "') ";
236
- }
237
- }
238
-
239
- if (!empty($profile_clause)) {
240
- $query .= ' and (' . implode(' and ', $profile_clause) . ')';
241
- }
242
-
243
- // Temporary save to have an object and call the query filter
244
- $e = Newsletter::instance()->save_email($email);
245
- $query = apply_filters('newsletter_emails_email_query', $query, $e);
246
-
247
- $email['query'] = $query;
248
- if ($email['status'] == 'sent') {
249
- $email['total'] = $email['sent'];
250
- } else {
251
- $email['total'] = $wpdb->get_var(str_replace('*', 'count(*)', $query));
252
- }
253
-
254
- if ($controls->is_action('send') && $controls->data['send_on'] < time()) {
255
- $controls->data['send_on'] = time();
256
- }
257
-
258
- $email = Newsletter::instance()->save_email($email, ARRAY_A);
259
-
260
- tnp_prepare_controls($email, $controls);
261
-
262
- if ($email === false) {
263
- $controls->errors = 'Unable to save. Try to deactivate and reactivate the plugin may be the database is out of sync.';
264
- }
265
-
266
- $controls->add_message_saved();
267
- }
268
- }
269
-
270
- if (empty($controls->errors) && ($controls->is_action('send') || $controls->is_action('schedule'))) {
271
-
272
- NewsletterStatistics::instance()->reset_stats($email);
273
-
274
- if ($email['subject'] == '') {
275
- $controls->errors = __('A subject is required to send', 'newsletter');
276
- } else {
277
- $wpdb->update(NEWSLETTER_EMAILS_TABLE, array('status' => TNP_Email::STATUS_SENDING), array('id' => $email_id));
278
- $email['status'] = TNP_Email::STATUS_SENDING;
279
- if ($controls->is_action('send')) {
280
- $controls->messages = __('Now sending.', 'newsletter');
281
- } else {
282
- $controls->messages = __('Scheduled.', 'newsletter');
283
- }
284
- }
285
- }
286
-
287
- if (isset($email['options']['status']) && $email['options']['status'] == 'S') {
288
- $controls->warnings[] = __('This newsletter will be sent to not confirmed subscribers.', 'newsletter');
289
- }
290
-
291
- if (strpos($email['message'], '{profile_url}') === false && strpos($email['message'], '{unsubscription_url}') === false && strpos($email['message'], '{unsubscription_confirm_url}') === false) {
292
- $controls->warnings[] = __('The message is missing the subscriber profile or cancellation link.', 'newsletter');
293
- }
294
-
295
- if (TNP_Email::STATUS_ERROR === $email['status'] && isset($email['options']['error_message'])) {
296
- $controls->errors .= sprintf(__('Stopped by fatal error: %s', 'newsletter'), esc_html($email['options']['error_message']));
297
- }
298
-
299
-
300
- if ($email['status'] != 'sent') {
301
- $subscriber_count = $wpdb->get_var(str_replace('*', 'count(*)', $email['query']));
302
- } else {
303
- $subscriber_count = $email['sent'];
304
- }
305
- ?>
306
- <style>
307
- .select2-container {
308
- max-width: 500px;
309
- display: block;
310
- margin: 1px;
311
- margin-top: 5px;
312
- }
313
- </style>
314
-
315
- <div class="wrap tnp-emails tnp-emails-edit" id="tnp-wrap">
316
-
317
- <?php include NEWSLETTER_DIR . '/tnp-header.php'; ?>
318
-
319
- <div id="tnp-heading">
320
-
321
- <h2><?php _e('Edit Newsletter', 'newsletter') ?></h2>
322
-
323
- </div>
324
-
325
- <div id="tnp-body">
326
- <form method="post" action="" id="newsletter-form">
327
- <?php $controls->init(array('cookie_name' => 'newsletter_emails_edit_tab')); ?>
328
- <?php $controls->hidden('updated') ?>
329
- <div class="tnp-status-header">
330
-
331
- <div class="tnp-two-thirds">
332
-
333
- <div class="tnp-submit">
334
-
335
- <?php if ($email['status'] == 'sending' || $email['status'] == 'sent') { ?>
336
-
337
- <?php $controls->button_back('?page=newsletter_emails_index') ?>
338
-
339
- <?php } else { ?>
340
-
341
- <a class="button-primary" href="<?php echo $module->get_editor_url($email_id, $editor_type) ?>">
342
- <i class="fas fa-edit"></i> <?php _e('Edit', 'newsletter') ?>
343
- </a>
344
-
345
- <?php } ?>
346
-
347
- <?php if ($email['status'] != 'sending' && $email['status'] != 'sent') $controls->button_save(); ?>
348
- <?php if ($email['status'] == 'new') $controls->button_confirm('send', __('Send now', 'newsletter'), __('Start real delivery?', 'newsletter')); ?>
349
- <?php if ($email['status'] == 'sending') $controls->button_confirm('pause', __('Pause', 'newsletter'), __('Pause the delivery?', 'newsletter')); ?>
350
- <?php if ($email['status'] == 'paused' || $email['status'] == 'error') $controls->button_confirm('continue', __('Continue', 'newsletter'), 'Continue the delivery?'); ?>
351
- <?php if ($email['status'] == 'paused') $controls->button_confirm('abort', __('Stop', 'newsletter'), __('This totally stop the delivery, ok?', 'newsletter')); ?>
352
- <?php if ($email['status'] == 'new' || ( $email['status'] == 'paused' && $email['send_on'] > time() )) { ?>
353
- <a id="tnp-schedule-button" class="button-secondary" href="javascript:tnp_toggle_schedule()"><i class="far fa-clock"></i> <?php _e("Schedule") ?></a>
354
- <span id="tnp-schedule" style="display: none;">
355
- <?php $controls->datetime('send_on') ?>
356
- <?php $controls->button_confirm('schedule', __('Schedule', 'newsletter'), __('Schedule delivery?', 'newsletter')); ?>
357
- <a class="button-secondary tnp-button-cancel" href="javascript:tnp_toggle_schedule()"><?php _e("Cancel") ?></a>
358
- </span>
359
- <?php } ?>
360
- </div>
361
-
362
- <?php $controls->text('subject', null, 'Subject'); ?>
363
- &nbsp;&nbsp;&nbsp;
364
- <i class="far fa-lightbulb" data-tnp-modal-target="#subject-ideas-modal" style="color: #fff; font-size: 24px"></i>
365
- </div>
366
-
367
- <div class="tnp-one-third">
368
-
369
- <div id="tnp-nl-status">
370
- <span class="tnp-nl-status-title"><?php _e("Status:") ?></span>
371
- <span class="tnp-nl-status-title-value"><?php _e("") ?> <?php $module->show_email_status_label($email) ?></span>
372
-
373
- <?php $module->show_email_progress_bar($email, array('numbers' => $email['status'] == 'sent' ? false : true)) ?>
374
-
375
- <?php if ($email['status'] == 'sent' || $email['status'] == 'sending') { ?>
376
- <div class="tnp-nl-status-row">
377
- <span class="tnp-nl-status-schedule-value"><?php
378
- if ($email['status'] == 'sent') {
379
- echo __('Sent on'), ' ', $module->format_date($email['send_on']);
380
- } else if ($email['status'] == 'sending' && $email['send_on'] > time()) {
381
- echo __('Scheduled on'), ' ', $module->format_date($email['send_on']);
382
- }
383
- ?></span>
384
- </div>
385
- <?php } ?>
386
- <div class="tnp-nl-status-row">
387
- <span class="tnp-nl-status-schedule-targeting"><?php _e('Targeted subscribers', 'newsletter') ?>:</span>
388
- <span class="tnp-nl-status-schedule-value"><?php echo $subscriber_count ?></span>
389
- </div>
390
-
391
- </div>
392
- </div>
393
-
394
- </div>
395
-
396
- <div id="tabs">
397
-
398
- <ul>
399
- <li><a href="#tabs-options"><?php _e('Sending Options', 'newsletter') ?></a></li>
400
- <li><a href="#tabs-advanced"><?php _e('Advanced', 'newsletter') ?></a></li>
401
- <li><a href="#tabs-preview"><?php _e('Preview', 'newsletter') ?></a></li>
402
- </ul>
403
-
404
-
405
- <div id="tabs-options" class="tnp-list-conditions">
406
- <p>
407
- <?php $controls->panel_help('https://www.thenewsletterplugin.com/documentation/newsletter-targeting') ?>
408
- </p>
409
-
410
- <p>
411
- <?php _e('Leaving all multichoice options unselected is like to select all them', 'newsletter'); ?>
412
- </p>
413
- <table class="form-table">
414
- <tr>
415
- <th><?php _e('Lists', 'newsletter') ?></th>
416
- <td>
417
- <?php
418
- $lists = $controls->get_list_options();
419
- ?>
420
- <?php $controls->select('options_lists_operator', array('or' => __('Match at least one of', 'newsletter'), 'and' => __('Match all of', 'newsletter'))); ?>
421
-
422
- <?php $controls->select2('options_lists', $lists, null, true, null, __('All', 'newsletter')); ?>
423
-
424
- <br>
425
- <?php _e('must not in one of', 'newsletter') ?>
426
-
427
- <?php $controls->select2('options_lists_exclude', $lists, null, true, null, __('None', 'newsletter')); ?>
428
- </td>
429
- </tr>
430
-
431
- <tr>
432
- <th><?php _e('Language', 'newsletter') ?></th>
433
- <td>
434
- <?php $controls->language('options_language'); ?>
435
- </td>
436
- </tr>
437
-
438
- <tr>
439
- <th><?php _e('Gender', 'newsletter') ?></th>
440
- <td>
441
- <?php $controls->checkboxes_group('options_sex', array('f' => 'Women', 'm' => 'Men', 'n' => 'Not specified')); ?>
442
- </td>
443
- </tr>
444
- <tr>
445
- <th><?php _e('Status', 'newsletter') ?></th>
446
- <td>
447
- <?php $controls->select('options_status', array('C' => __('Confirmed', 'newsletter'), 'S' => __('Not confirmed', 'newsletter'))); ?>
448
-
449
- </td>
450
- </tr>
451
- <tr>
452
- <th><?php _e('Only to subscribers linked to WP users', 'newsletter') ?></th>
453
- <td>
454
- <?php $controls->yesno('options_wp_users'); ?>
455
- </td>
456
- </tr>
457
- <?php
458
- $fields = TNP_Profile_Service::get_profiles('', TNP_Profile::TYPE_SELECT);
459
- ?>
460
- <?php if (!empty($fields)) { ?>
461
- <tr>
462
- <th><?php _e('Profile fields', 'newsletter') ?></th>
463
- <td>
464
- <?php foreach ($fields as $profile) { ?>
465
- <?php echo esc_html($profile->name), ' ', __('is one of:', 'newsletter') ?>
466
- <?php $controls->select2("options_profile_$profile->id", $profile->options, null, true, null, __('Do not filter by this field', 'newsletter')); ?>
467
- <br>
468
- <?php } ?>
469
- <p class="description">
470
-
471
- </p>
472
- </td>
473
- </tr>
474
- <?php } ?>
475
- </table>
476
-
477
- <?php do_action('newsletter_emails_edit_target', $module->get_email($email_id), $controls) ?>
478
-
479
- </div>
480
-
481
-
482
- <div id="tabs-advanced">
483
-
484
- <table class="form-table">
485
- <tr>
486
- <th><?php _e('Keep private', 'newsletter') ?></th>
487
- <td>
488
- <?php $controls->yesno('private'); ?>
489
- <?php if ($email['status'] == 'sent') { ?>
490
- <?php $controls->button('change-private', __('Save')) ?>
491
- <?php } ?>
492
- <p class="description">
493
- <?php _e('Hide/show from public sent newsletter list.', 'newsletter') ?>
494
- <?php _e('Required', 'newsletter') ?>: <a href="" target="_blank">Newsletter Archive Extension</a>
495
- </p>
496
- </td>
497
- </tr>
498
- <tr>
499
- <th><?php _e('Track clicks and message opening', 'newsletter') ?></th>
500
- <td>
501
- <?php $controls->yesno('track'); ?>
502
- </td>
503
- </tr>
504
- <tr>
505
- <th><?php _e('Sender email address', 'newsletter') ?></th>
506
- <td>
507
- <?php $controls->text_email('options_sender_email', 40); ?>
508
- <div class="tnpc-hint">
509
- Original: <?php echo esc_html(Newsletter::instance()->get_sender_email()) ?>.<br>
510
- If you use a delivery service, be sure to use a validated email address.
511
- </div>
512
- </td>
513
- </tr>
514
- <tr>
515
- <th>
516
- <?php _e('Sender name', 'newsletter') ?>
517
- </th>
518
- <td>
519
- <?php $controls->text('options_sender_name', 40); ?>
520
- <div class="tnpc-hint">
521
- Original: <?php echo esc_html(Newsletter::instance()->get_sender_name()) ?>
522
- </div>
523
- </td>
524
- </tr>
525
- </table>
526
-
527
- <?php do_action('newsletter_emails_edit_other', $module->get_email($email_id), $controls) ?>
528
-
529
- <table class="form-table">
530
- <tr>
531
- <th>Query (tech)</th>
532
- <td><?php echo esc_html($email['query']); ?></td>
533
- </tr>
534
- <tr>
535
- <th>Token (tech)</th>
536
- <td><?php echo esc_html($email['token']); ?></td>
537
- </tr>
538
- <tr>
539
- <th style="vertical-align: top">
540
- This is the textual version of your newsletter.
541
- If you empty it, only an HTML version will be sent but is an anti-spam best practice to include a text only version.
542
- </th>
543
- <td>
544
- <?php if ($editor_type == NewsletterEmails::EDITOR_COMPOSER) { ?>
545
- <?php $controls->select('options_text_message_mode', [''=>__('Autogenerate', 'newsletter'), '1'=>__('Hand edited', 'newsletter')]) ?>
546
- <p class="description"></p>
547
- <?php } ?>
548
-
549
- <?php $controls->textarea_fixed('message_text', '100%', '500'); ?>
550
- <!--
551
- <p class="tnp-tab-warning">
552
- See <a href="https://wordpress.org/plugins/plaintext-newsletter/" target="_blank">this plugin</a> for automatic plaintext generation.
553
- </p>
554
- -->
555
- </td>
556
- </tr>
557
- </table>
558
- </div>
559
-
560
-
561
- <div id="tabs-preview">
562
-
563
- <div class="tnpc-preview">
564
- <!-- Flat Laptop Browser -->
565
- <div class="fake-browser-ui">
566
- <div class="frame">
567
- <span class="bt-1"></span>
568
- <span class="bt-2"></span>
569
- <span class="bt-3"></span>
570
- </div>
571
- <iframe id="tnpc-preview-desktop" src="" width="700" height="520" alt="" frameborder="0"></iframe>
572
- </div>
573
-
574
- <!-- Flat Mobile Browser -->
575
- <div class="fake-mobile-browser-ui">
576
- <iframe id="tnpc-preview-mobile" src="" width="320" height="445" alt="" frameborder="0"></iframe>
577
- <div class="frame">
578
- <span class="bt-4"></span>
579
- </div>
580
- </div>
581
- </div>
582
-
583
- <script type="text/javascript">
584
- preview_url = ajaxurl + "?action=tnpc_preview&id=<?php echo $email_id ?>";
585
- jQuery('#tnpc-preview-desktop, #tnpc-preview-mobile').attr("src", preview_url);
586
- setTimeout(function () {
587
- jQuery('#tnpc-preview-desktop, #tnpc-preview-mobile').contents().find("a").click(function (e) {
588
- e.preventDefault();
589
- })
590
- }, 500);
591
- </script>
592
-
593
- <p>
594
- <?php if ($editor_type != NewsletterEmails::EDITOR_HTML && $email['status'] != 'sending' && $email['status'] != 'sent') $controls->button_confirm('html', __('Convert to HTML newsletter', 'newsletter'), 'Attention: no way back!'); ?>
595
- </p>
596
- </div>
597
-
598
- </div>
599
-
600
- </form>
601
- </div>
602
-
603
- <?php include NEWSLETTER_DIR . '/emails/subjects.php'; ?>
604
-
605
- <?php include NEWSLETTER_DIR . '/tnp-footer.php'; ?>
606
-
607
- </div>
1
+ <?php
2
+ /* @var $this NewsletterEmails */
3
+ defined('ABSPATH') || exit;
4
+
5
+ /* @var $wpdb wpdb */
6
+ require_once NEWSLETTER_INCLUDES_DIR . '/controls.php';
7
+ $controls = new NewsletterControls();
8
+ $module = NewsletterEmails::instance();
9
+
10
+ function tnp_prepare_controls($email, $controls) {
11
+ $controls->data = $email;
12
+
13
+ foreach ($email['options'] as $name => $value) {
14
+ $controls->data['options_' . $name] = $value;
15
+ }
16
+ }
17
+
18
+ // Always required
19
+ $email = $this->get_email($_GET['id'], ARRAY_A);
20
+
21
+ if (empty($email)) {
22
+ echo 'Wrong email identifier';
23
+ return;
24
+ }
25
+
26
+ $email_id = $email['id'];
27
+
28
+ /* Satus changes which require a reload */
29
+ if ($controls->is_action('pause')) {
30
+ $this->admin_logger->info('Newsletter ' . $email_id . ' paused');
31
+ $wpdb->update(NEWSLETTER_EMAILS_TABLE, array('status' => 'paused'), array('id' => $email_id));
32
+ $email = $this->get_email($_GET['id'], ARRAY_A);
33
+ tnp_prepare_controls($email, $controls);
34
+ }
35
+
36
+ if ($controls->is_action('continue')) {
37
+ $this->admin_logger->info('Newsletter ' . $email_id . ' restarted');
38
+ $wpdb->update(NEWSLETTER_EMAILS_TABLE, array('status' => 'sending'), array('id' => $email_id));
39
+ $email = $this->get_email($_GET['id'], ARRAY_A);
40
+ tnp_prepare_controls($email, $controls);
41
+ }
42
+
43
+ if ($controls->is_action('abort')) {
44
+ $this->admin_logger->info('Newsletter ' . $email_id . ' aborted');
45
+ $wpdb->query("update " . NEWSLETTER_EMAILS_TABLE . " set last_id=0, sent=0, status='new' where id=" . $email_id);
46
+ $email = $this->get_email($_GET['id'], ARRAY_A);
47
+ tnp_prepare_controls($email, $controls);
48
+ $controls->messages = __('Delivery definitively cancelled', 'newsletter');
49
+ }
50
+
51
+ if ($controls->is_action('change-private')) {
52
+ $data = [];
53
+ $data['private'] = $controls->data['private'];
54
+ $data['id'] = $email['id'];
55
+ $email = $this->save_email($data, ARRAY_A);
56
+ $controls->add_message_saved();
57
+
58
+ tnp_prepare_controls($email, $controls);
59
+ }
60
+
61
+
62
+ $editor_type = $this->get_editor_type($email);
63
+
64
+ // Backward compatibility: preferences conversion
65
+ if (!$controls->is_action()) {
66
+ if (!isset($email['options']['lists'])) {
67
+
68
+ $options_profile = get_option('newsletter_profile');
69
+
70
+ if (empty($controls->data['preferences_status_operator'])) {
71
+ $email['options']['lists_operator'] = 'or';
72
+ } else {
73
+ $email['options']['lists_operator'] = 'and';
74
+ }
75
+ $controls->data['options_lists'] = array();
76
+ $controls->data['options_lists_exclude'] = array();
77
+
78
+ if (!empty($email['preferences'])) {
79
+ $preferences = explode(',', $email['preferences']);
80
+ $value = empty($email['options']['preferences_status']) ? 'on' : 'off';
81
+
82
+ foreach ($preferences as $x) {
83
+ if ($value == 'on') {
84
+ $controls->data['options_lists'][] = $x;
85
+ } else {
86
+ $controls->data['options_lists_exclude'][] = $x;
87
+ }
88
+ }
89
+ }
90
+ }
91
+ }
92
+ // End backward compatibility
93
+
94
+ if (!$controls->is_action()) {
95
+ tnp_prepare_controls($email, $controls);
96
+ }
97
+
98
+ if ($controls->is_action('html')) {
99
+
100
+ $this->admin_logger->info('Newsletter ' . $email_id . ' converted to HTML');
101
+
102
+ $data = [];
103
+ $data['editor'] = NewsletterEmails::EDITOR_HTML;
104
+ $data['id'] = $email_id;
105
+
106
+ // Backward compatibility: clean up the composer flag
107
+ $data['options'] = $email['options'];
108
+ unset($data['options']['composer']);
109
+ // End backward compatibility
110
+
111
+ $email = $this->save_email($data, ARRAY_A);
112
+ $controls->messages = 'You can now edit the newsletter as pure HTML';
113
+
114
+ tnp_prepare_controls($email, $controls);
115
+
116
+ $editor_type = NewsletterEmails::EDITOR_HTML;
117
+ }
118
+
119
+
120
+
121
+ if ($controls->is_action('test') || $controls->is_action('save') || $controls->is_action('send') || $controls->is_action('schedule')) {
122
+
123
+ if ($email['updated'] != $controls->data['updated']) {
124
+ $controls->errors = 'This newsletter has been modified by someone else. Cannot save.';
125
+ } else {
126
+ $email['updated'] = time();
127
+ if ($controls->is_action('save')) {
128
+ $this->admin_logger->info('Saving newsletter: ' . $email_id);
129
+ } else if ($controls->is_action('send')) {
130
+ $this->admin_logger->info('Sending newsletter: ' . $email_id);
131
+ } else if ($controls->is_action('schedule')) {
132
+ $this->admin_logger->info('Scheduling newsletter: ' . $email_id);
133
+ }
134
+
135
+ $email['subject'] = $controls->data['subject'];
136
+ $email['track'] = $controls->data['track'];
137
+ $email['editor'] = $editor_type;
138
+ $email['private'] = $controls->data['private'];
139
+ $email['message_text'] = $controls->data['message_text'];
140
+ if ($controls->is_action('send')) {
141
+ $email['send_on'] = time();
142
+ } else {
143
+ // Patch, empty on continuation
144
+ if (!empty($controls->data['send_on'])) {
145
+ $email['send_on'] = $controls->data['send_on'];
146
+ }
147
+ }
148
+
149
+ // Reset and refill the options
150
+ // Try without the reset and let's see where the problems are
151
+ //$email['options'] = array();
152
+ // Reset only specific keys
153
+ unset($email['options']['lists']);
154
+ unset($email['options']['lists_operator']);
155
+ unset($email['options']['lists_exclude']);
156
+ unset($email['options']['sex']);
157
+ for ($i = 1; $i <= 20; $i++) {
158
+ unset($email['options']["profile_$i"]);
159
+ }
160
+
161
+ // Patch for Geo addon to be solved with a filter
162
+ unset($email['options']['countries']);
163
+ unset($email['options']['regions']);
164
+ unset($email['options']['cities']);
165
+
166
+ foreach ($controls->data as $name => $value) {
167
+ if (strpos($name, 'options_') === 0) {
168
+ $email['options'][substr($name, 8)] = $value;
169
+ }
170
+ }
171
+
172
+ // Before send, we build the query to extract subscriber, so the delivery engine does not
173
+ // have to worry about the email parameters
174
+ if ($email['options']['status'] == 'S') {
175
+ $query = "select * from " . NEWSLETTER_USERS_TABLE . " where status='S'";
176
+ } else {
177
+ $query = "select * from " . NEWSLETTER_USERS_TABLE . " where status='C'";
178
+ }
179
+
180
+ if ($email['options']['wp_users'] == '1') {
181
+ $query .= " and wp_user_id<>0";
182
+ }
183
+
184
+ if (!empty($email['options']['language'])) {
185
+ $query .= " and language='" . esc_sql((string) $email['options']['language']) . "'";
186
+ }
187
+
188
+
189
+ $list_where = array();
190
+ if (isset($email['options']['lists']) && count($email['options']['lists'])) {
191
+ foreach ($email['options']['lists'] as $list) {
192
+ $list = (int) $list;
193
+ $list_where[] = 'list_' . $list . '=1';
194
+ }
195
+ }
196
+
197
+ if (!empty($list_where)) {
198
+ if (isset($email['options']['lists_operator']) && $email['options']['lists_operator'] == 'and') {
199
+ $query .= ' and (' . implode(' and ', $list_where) . ')';
200
+ } else {
201
+ $query .= ' and (' . implode(' or ', $list_where) . ')';
202
+ }
203
+ }
204
+
205
+ // Excluded lists
206
+ $list_where = array();
207
+ if (isset($email['options']['lists_exclude']) && count($email['options']['lists_exclude'])) {
208
+ foreach ($email['options']['lists_exclude'] as $list) {
209
+ $list = (int) $list;
210
+ $list_where[] = 'list_' . $list . '=0';
211
+ }
212
+ }
213
+ if (!empty($list_where)) {
214
+ // Must not be in one of the excluded lists
215
+ $query .= ' and (' . implode(' and ', $list_where) . ')';
216
+ }
217
+
218
+ // Gender
219
+ if (isset($email['options']['sex'])) {
220
+ $sex = $email['options']['sex'];
221
+ if (is_array($sex) && count($sex)) {
222
+ $query .= " and sex in (";
223
+ foreach ($sex as $x) {
224
+ $query .= "'" . esc_sql((string) $x) . "', ";
225
+ }
226
+ $query = substr($query, 0, -2);
227
+ $query .= ")";
228
+ }
229
+ }
230
+
231
+ // Profile fields filter
232
+ $profile_clause = array();
233
+ for ($i = 1; $i <= 20; $i++) {
234
+ if (isset($email["options"]["profile_$i"]) && count($email["options"]["profile_$i"])) {
235
+ $profile_clause[] = 'profile_' . $i . " IN ('" . implode("','", esc_sql($email["options"]["profile_$i"])) . "') ";
236
+ }
237
+ }
238
+
239
+ if (!empty($profile_clause)) {
240
+ $query .= ' and (' . implode(' and ', $profile_clause) . ')';
241
+ }
242
+
243
+ // Temporary save to have an object and call the query filter
244
+ $e = Newsletter::instance()->save_email($email);
245
+ $query = apply_filters('newsletter_emails_email_query', $query, $e);
246
+
247
+ $email['query'] = $query;
248
+ if ($email['status'] == 'sent') {
249
+ $email['total'] = $email['sent'];
250
+ } else {
251
+ $email['total'] = $wpdb->get_var(str_replace('*', 'count(*)', $query));
252
+ }
253
+
254
+ if ($controls->is_action('send') && $controls->data['send_on'] < time()) {
255
+ $controls->data['send_on'] = time();
256
+ }
257
+
258
+ $email = Newsletter::instance()->save_email($email, ARRAY_A);
259
+
260
+ tnp_prepare_controls($email, $controls);
261
+
262
+ if ($email === false) {
263
+ $controls->errors = 'Unable to save. Try to deactivate and reactivate the plugin may be the database is out of sync.';
264
+ }
265
+
266
+ $controls->add_message_saved();
267
+ }
268
+ }
269
+
270
+ if (empty($controls->errors) && ($controls->is_action('send') || $controls->is_action('schedule'))) {
271
+
272
+ NewsletterStatistics::instance()->reset_stats($email);
273
+
274
+ if ($email['subject'] == '') {
275
+ $controls->errors = __('A subject is required to send', 'newsletter');
276
+ } else {
277
+ $wpdb->update(NEWSLETTER_EMAILS_TABLE, array('status' => TNP_Email::STATUS_SENDING), array('id' => $email_id));
278
+ $email['status'] = TNP_Email::STATUS_SENDING;
279
+ if ($controls->is_action('send')) {
280
+ $controls->messages = __('Now sending.', 'newsletter');
281
+ } else {
282
+ $controls->messages = __('Scheduled.', 'newsletter');
283
+ }
284
+ }
285
+ }
286
+
287
+ if (isset($email['options']['status']) && $email['options']['status'] == 'S') {
288
+ $controls->warnings[] = __('This newsletter will be sent to not confirmed subscribers.', 'newsletter');
289
+ }
290
+
291
+ if (strpos($email['message'], '{profile_url}') === false && strpos($email['message'], '{unsubscription_url}') === false && strpos($email['message'], '{unsubscription_confirm_url}') === false) {
292
+ $controls->warnings[] = __('The message is missing the subscriber profile or cancellation link.', 'newsletter');
293
+ }
294
+
295
+ if (TNP_Email::STATUS_ERROR === $email['status'] && isset($email['options']['error_message'])) {
296
+ $controls->errors .= sprintf(__('Stopped by fatal error: %s', 'newsletter'), esc_html($email['options']['error_message']));
297
+ }
298
+
299
+
300
+ if ($email['status'] != 'sent') {
301
+ $subscriber_count = $wpdb->get_var(str_replace('*', 'count(*)', $email['query']));
302
+ } else {
303
+ $subscriber_count = $email['sent'];
304
+ }
305
+ ?>
306
+ <style>
307
+ .select2-container {
308
+ max-width: 500px;
309
+ display: block;
310
+ margin: 1px;
311
+ margin-top: 5px;
312
+ }
313
+ </style>
314
+
315
+ <div class="wrap tnp-emails tnp-emails-edit" id="tnp-wrap">
316
+
317
+ <?php include NEWSLETTER_DIR . '/tnp-header.php'; ?>
318
+
319
+ <div id="tnp-heading">
320
+
321
+ <h2><?php _e('Edit Newsletter', 'newsletter') ?></h2>
322
+
323
+ </div>
324
+
325
+ <div id="tnp-body">
326
+ <form method="post" action="" id="newsletter-form">
327
+ <?php $controls->init(array('cookie_name' => 'newsletter_emails_edit_tab')); ?>
328
+ <?php $controls->hidden('updated') ?>
329
+ <div class="tnp-status-header">
330
+
331
+ <div class="tnp-two-thirds">
332
+
333
+ <div class="tnp-submit">
334
+
335
+ <?php if ($email['status'] == 'sending' || $email['status'] == 'sent') { ?>
336
+
337
+ <?php $controls->button_back('?page=newsletter_emails_index') ?>
338
+
339
+ <?php } else { ?>
340
+
341
+ <a class="button-primary" href="<?php echo $module->get_editor_url($email_id, $editor_type) ?>">
342
+ <i class="fas fa-edit"></i> <?php _e('Edit', 'newsletter') ?>
343
+ </a>
344
+
345
+ <?php } ?>
346
+
347
+ <?php if ($email['status'] != 'sending' && $email['status'] != 'sent') $controls->button_save(); ?>
348
+ <?php if ($email['status'] == 'new') $controls->button_confirm('send', __('Send now', 'newsletter'), __('Start real delivery?', 'newsletter')); ?>
349
+ <?php if ($email['status'] == 'sending') $controls->button_confirm('pause', __('Pause', 'newsletter'), __('Pause the delivery?', 'newsletter')); ?>
350
+ <?php if ($email['status'] == 'paused' || $email['status'] == 'error') $controls->button_confirm('continue', __('Continue', 'newsletter'), 'Continue the delivery?'); ?>
351
+ <?php if ($email['status'] == 'paused') $controls->button_confirm('abort', __('Stop', 'newsletter'), __('This totally stop the delivery, ok?', 'newsletter')); ?>
352
+ <?php if ($email['status'] == 'new' || ( $email['status'] == 'paused' && $email['send_on'] > time() )) { ?>
353
+ <a id="tnp-schedule-button" class="button-secondary" href="javascript:tnp_toggle_schedule()"><i class="far fa-clock"></i> <?php _e("Schedule") ?></a>
354
+ <span id="tnp-schedule" style="display: none;">
355
+ <?php $controls->datetime('send_on') ?>
356
+ <?php $controls->button_confirm('schedule', __('Schedule', 'newsletter'), __('Schedule delivery?', 'newsletter')); ?>
357
+ <a class="button-secondary tnp-button-cancel" href="javascript:tnp_toggle_schedule()"><?php _e("Cancel") ?></a>
358
+ </span>
359
+ <?php } ?>
360
+ </div>
361
+
362
+ <?php $controls->text('subject', null, 'Subject'); ?>
363
+ &nbsp;&nbsp;&nbsp;
364
+ <i class="far fa-lightbulb" data-tnp-modal-target="#subject-ideas-modal" style="color: #fff; font-size: 24px"></i>
365
+ </div>
366
+
367
+ <div class="tnp-one-third">
368
+
369
+ <div id="tnp-nl-status">
370
+ <span class="tnp-nl-status-title"><?php _e("Status:") ?></span>
371
+ <span class="tnp-nl-status-title-value"><?php _e("") ?> <?php $module->show_email_status_label($email) ?></span>
372
+
373
+ <?php $module->show_email_progress_bar($email, array('numbers' => $email['status'] == 'sent' ? false : true)) ?>
374
+
375
+ <?php if ($email['status'] == 'sent' || $email['status'] == 'sending') { ?>
376
+ <div class="tnp-nl-status-row">
377
+ <span class="tnp-nl-status-schedule-value"><?php
378
+ if ($email['status'] == 'sent') {
379
+ echo __('Sent on'), ' ', $module->format_date($email['send_on']);
380
+ } else if ($email['status'] == 'sending' && $email['send_on'] > time()) {
381
+ echo __('Scheduled on'), ' ', $module->format_date($email['send_on']);
382
+ }
383
+ ?></span>
384
+ </div>
385
+ <?php } ?>
386
+ <div class="tnp-nl-status-row">
387
+ <span class="tnp-nl-status-schedule-targeting"><?php _e('Targeted subscribers', 'newsletter') ?>:</span>
388
+ <span class="tnp-nl-status-schedule-value"><?php echo $subscriber_count ?></span>
389
+ </div>
390
+
391
+ </div>
392
+ </div>
393
+
394
+ </div>
395
+
396
+ <div id="tabs">
397
+
398
+ <ul>
399
+ <li><a href="#tabs-options"><?php _e('Sending Options', 'newsletter') ?></a></li>
400
+ <li><a href="#tabs-advanced"><?php _e('Advanced', 'newsletter') ?></a></li>
401
+ <li><a href="#tabs-preview"><?php _e('Preview', 'newsletter') ?></a></li>
402
+ </ul>
403
+
404
+
405
+ <div id="tabs-options" class="tnp-list-conditions">
406
+ <p>
407
+ <?php $controls->panel_help('https://www.thenewsletterplugin.com/documentation/newsletter-targeting') ?>
408
+ </p>
409
+
410
+ <p>
411
+ <?php _e('Leaving all multichoice options unselected is like to select all them', 'newsletter'); ?>
412
+ </p>
413
+ <table class="form-table">
414
+ <tr>
415
+ <th><?php _e('Lists', 'newsletter') ?></th>
416
+ <td>
417
+ <?php
418
+ $lists = $controls->get_list_options();
419
+ ?>
420
+ <?php $controls->select('options_lists_operator', array('or' => __('Match at least one of', 'newsletter'), 'and' => __('Match all of', 'newsletter'))); ?>
421
+
422
+ <?php $controls->select2('options_lists', $lists, null, true, null, __('All', 'newsletter')); ?>
423
+
424
+ <br>
425
+ <?php _e('must not in one of', 'newsletter') ?>
426
+
427
+ <?php $controls->select2('options_lists_exclude', $lists, null, true, null, __('None', 'newsletter')); ?>
428
+ </td>
429
+ </tr>
430
+
431
+ <tr>
432
+ <th><?php _e('Language', 'newsletter') ?></th>
433
+ <td>
434
+ <?php $controls->language('options_language'); ?>
435
+ </td>
436
+ </tr>
437
+
438
+ <tr>
439
+ <th><?php _e('Gender', 'newsletter') ?></th>
440
+ <td>
441
+ <?php $controls->checkboxes_group('options_sex', array('f' => 'Women', 'm' => 'Men', 'n' => 'Not specified')); ?>
442
+ </td>
443
+ </tr>
444
+ <tr>
445
+ <th><?php _e('Status', 'newsletter') ?></th>
446
+ <td>
447
+ <?php $controls->select('options_status', array('C' => __('Confirmed', 'newsletter'), 'S' => __('Not confirmed', 'newsletter'))); ?>
448
+
449
+ </td>
450
+ </tr>
451
+ <tr>
452
+ <th><?php _e('Only to subscribers linked to WP users', 'newsletter') ?></th>
453
+ <td>
454
+ <?php $controls->yesno('options_wp_users'); ?>
455
+ </td>
456
+ </tr>
457
+ <?php
458
+ $fields = TNP_Profile_Service::get_profiles('', TNP_Profile::TYPE_SELECT);
459
+ ?>
460
+ <?php if (!empty($fields)) { ?>
461
+ <tr>
462
+ <th><?php _e('Profile fields', 'newsletter') ?></th>
463
+ <td>
464
+ <?php foreach ($fields as $profile) { ?>
465
+ <?php echo esc_html($profile->name), ' ', __('is one of:', 'newsletter') ?>
466
+ <?php $controls->select2("options_profile_$profile->id", $profile->options, null, true, null, __('Do not filter by this field', 'newsletter')); ?>
467
+ <br>
468
+ <?php } ?>
469
+ <p class="description">
470
+
471
+ </p>
472
+ </td>
473
+ </tr>
474
+ <?php } ?>
475
+ </table>
476
+
477
+ <?php do_action('newsletter_emails_edit_target', $module->get_email($email_id), $controls) ?>
478
+
479
+ </div>
480
+
481
+
482
+ <div id="tabs-advanced">
483
+
484
+ <table class="form-table">
485
+ <tr>
486
+ <th><?php _e('Keep private', 'newsletter') ?></th>
487
+ <td>
488
+ <?php $controls->yesno('private'); ?>
489
+ <?php if ($email['status'] == 'sent') { ?>
490
+ <?php $controls->button('change-private', __('Save')) ?>
491
+ <?php } ?>
492
+ <p class="description">
493
+ <?php _e('Hide/show from public sent newsletter list.', 'newsletter') ?>
494
+ <?php _e('Required', 'newsletter') ?>: <a href="" target="_blank">Newsletter Archive Extension</a>
495
+ </p>
496
+ </td>
497
+ </tr>
498
+ <tr>
499
+ <th><?php _e('Track clicks and message opening', 'newsletter') ?></th>
500
+ <td>
501
+ <?php $controls->yesno('track'); ?>
502
+ </td>
503
+ </tr>
504
+ <tr>
505
+ <th><?php _e('Sender email address', 'newsletter') ?></th>
506
+ <td>
507
+ <?php $controls->text_email('options_sender_email', 40); ?>
508
+ <div class="tnpc-hint">
509
+ Original: <?php echo esc_html(Newsletter::instance()->get_sender_email()) ?>.<br>
510
+ If you use a delivery service, be sure to use a validated email address.
511
+ </div>
512
+ </td>
513
+ </tr>
514
+ <tr>
515
+ <th>
516
+ <?php _e('Sender name', 'newsletter') ?>
517
+ </th>
518
+ <td>
519
+ <?php $controls->text('options_sender_name', 40); ?>
520
+ <div class="tnpc-hint">
521
+ Original: <?php echo esc_html(Newsletter::instance()->get_sender_name()) ?>
522
+ </div>
523
+ </td>
524
+ </tr>
525
+ </table>
526
+
527
+ <?php do_action('newsletter_emails_edit_other', $module->get_email($email_id), $controls) ?>
528
+
529
+ <table class="form-table">
530
+ <tr>
531
+ <th>Query (tech)</th>
532
+ <td><?php echo esc_html($email['query']); ?></td>
533
+ </tr>
534
+ <tr>
535
+ <th>Token (tech)</th>
536
+ <td><?php echo esc_html($email['token']); ?></td>
537
+ </tr>
538
+ <tr>
539
+ <th style="vertical-align: top">
540
+ This is the textual version of your newsletter.
541
+ If you empty it, only an HTML version will be sent but is an anti-spam best practice to include a text only version.
542
+ </th>
543
+ <td>
544
+ <?php if ($editor_type == NewsletterEmails::EDITOR_COMPOSER) { ?>
545
+ <?php $controls->select('options_text_message_mode', [''=>__('Autogenerate', 'newsletter'), '1'=>__('Hand edited', 'newsletter')]) ?>
546
+ <p class="description"></p>
547
+ <?php } ?>
548
+
549
+ <?php $controls->textarea_fixed('message_text', '100%', '500'); ?>
550
+ <!--
551
+ <p class="tnp-tab-warning">
552
+ See <a href="https://wordpress.org/plugins/plaintext-newsletter/" target="_blank">this plugin</a> for automatic plaintext generation.
553
+ </p>
554
+ -->
555
+ </td>
556
+ </tr>
557
+ </table>
558
+ </div>
559
+
560
+
561
+ <div id="tabs-preview">
562
+
563
+ <div class="tnpc-preview">
564
+ <!-- Flat Laptop Browser -->
565
+ <div class="fake-browser-ui">
566
+ <div class="frame">
567
+ <span class="bt-1"></span>
568
+ <span class="bt-2"></span>
569
+ <span class="bt-3"></span>
570
+ </div>
571
+ <iframe id="tnpc-preview-desktop" src="" width="700" height="520" alt="" frameborder="0"></iframe>
572
+ </div>
573
+
574
+ <!-- Flat Mobile Browser -->
575
+ <div class="fake-mobile-browser-ui">
576
+ <iframe id="tnpc-preview-mobile" src="" width="320" height="445" alt="" frameborder="0"></iframe>
577
+ <div class="frame">
578
+ <span class="bt-4"></span>
579
+ </div>
580
+ </div>
581
+ </div>
582
+
583
+ <script type="text/javascript">
584
+ preview_url = ajaxurl + "?action=tnpc_preview&id=<?php echo $email_id ?>";
585
+ jQuery('#tnpc-preview-desktop, #tnpc-preview-mobile').attr("src", preview_url);
586
+ setTimeout(function () {
587
+ jQuery('#tnpc-preview-desktop, #tnpc-preview-mobile').contents().find("a").click(function (e) {
588
+ e.preventDefault();
589
+ })
590
+ }, 500);
591
+ </script>
592
+
593
+ <p>
594
+ <?php if ($editor_type != NewsletterEmails::EDITOR_HTML && $email['status'] != 'sending' && $email['status'] != 'sent') $controls->button_confirm('html', __('Convert to HTML newsletter', 'newsletter'), 'Attention: no way back!'); ?>
595
+ </p>
596
+ </div>
597
+
598
+ </div>
599
+
600
+ </form>
601
+ </div>
602
+
603
+ <?php include NEWSLETTER_DIR . '/emails/subjects.php'; ?>
604
+
605
+ <?php include NEWSLETTER_DIR . '/tnp-footer.php'; ?>
606
+
607
+ </div>
emails/emails.php CHANGED
@@ -547,7 +547,7 @@ class NewsletterEmails extends NewsletterModule {
547
  if ($wrapper) {
548
  echo '<table border="0" cellpadding="0" cellspacing="0" align="center" width="100%" style="border-collapse: collapse; width: 100%;" class="tnpc-row tnpc-row-block" data-id="', esc_attr($block_id), '">', "\n";
549
  echo "<tr>";
550
- echo '<td align="center" style="padding: 0;" class="edit-block">', "\n";
551
  }
552
 
553
  // Container that fixes the width and makes the block responsive
@@ -555,7 +555,7 @@ class NewsletterEmails extends NewsletterModule {
555
 
556
  echo '<table type="options" data-json="', esc_attr($data), '" class="tnpc-block-content" border="0" cellpadding="0" align="center" cellspacing="0" width="100%" style="width: 100%!important; max-width: ', $options['block_width'], 'px!important">', "\n";
557
  echo "<tr>";
558
- echo '<td align="', esc_attr($options['block_align']), '" style="', $style, '" bgcolor="', $block_background, '" width="100%">';
559
 
560
  //echo "<!-- block generated content -->\n";
561
  echo trim($content);
547
  if ($wrapper) {
548
  echo '<table border="0" cellpadding="0" cellspacing="0" align="center" width="100%" style="border-collapse: collapse; width: 100%;" class="tnpc-row tnpc-row-block" data-id="', esc_attr($block_id), '">', "\n";
549
  echo "<tr>";
550
+ echo '<td align="center" style="padding: 0;">', "\n";
551
  }
552
 
553
  // Container that fixes the width and makes the block responsive
555
 
556
  echo '<table type="options" data-json="', esc_attr($data), '" class="tnpc-block-content" border="0" cellpadding="0" align="center" cellspacing="0" width="100%" style="width: 100%!important; max-width: ', $options['block_width'], 'px!important">', "\n";
557
  echo "<tr>";
558
+ echo '<td align="', esc_attr($options['block_align']), '" style="', esc_attr($style), '" bgcolor="', esc_attr($block_background), '" width="100%">';
559
 
560
  //echo "<!-- block generated content -->\n";
561
  echo trim($content);
emails/tnp-composer/css/newsletter.css CHANGED
@@ -2,11 +2,10 @@
2
  .ReadMsgBody{width:100%;} .ExternalClass{width:100%;}
3
  .ExternalClass, .ExternalClass p, .ExternalClass span, .ExternalClass font, .ExternalClass td, .ExternalClass div {line-height: 100%;}
4
 
5
- body { margin: 0; padding: 0; height: 100%!important; width: 100%!important; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%;}
6
  table,td { border-collapse: collapse !important; mso-table-lspace: 0pt; mso-table-rspace: 0pt;}
7
  img { border: 0; height: auto; line-height: 100%; outline: none; text-decoration: none; max-width: 100%!important; -ms-interpolation-mode: bicubic;}
8
  img.aligncenter { display: block; margin: 0 auto;}
9
-
10
  @media screen and (max-width: 525px) {
11
  .pt-1, .padding-top-15 { padding-top: 15px!important; }
12
  .pb-1, .padding-bottom-15 { padding-bottom: 15px!important; }
2
  .ReadMsgBody{width:100%;} .ExternalClass{width:100%;}
3
  .ExternalClass, .ExternalClass p, .ExternalClass span, .ExternalClass font, .ExternalClass td, .ExternalClass div {line-height: 100%;}
4
 
5
+ body { margin: 0; padding: 0; height: 100%!important; width: 100%!important; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; mso-line-height-rule: exactly;}
6
  table,td { border-collapse: collapse !important; mso-table-lspace: 0pt; mso-table-rspace: 0pt;}
7
  img { border: 0; height: auto; line-height: 100%; outline: none; text-decoration: none; max-width: 100%!important; -ms-interpolation-mode: bicubic;}
8
  img.aligncenter { display: block; margin: 0 auto;}
 
9
  @media screen and (max-width: 525px) {
10
  .pt-1, .padding-top-15 { padding-top: 15px!important; }
11
  .pb-1, .padding-bottom-15 { padding-bottom: 15px!important; }
emails/tnp-composer/index-v2.php CHANGED
@@ -1,206 +1,206 @@
1
- <?php
2
- /**
3
- * This file is included by NewsletterControls to create the composer.
4
- */
5
- /* @var $this NewsletterControls */
6
-
7
- defined('ABSPATH') || exit;
8
-
9
- $list = NewsletterEmails::instance()->get_blocks();
10
-
11
- $blocks = array();
12
- foreach ($list as $key => $data) {
13
- if (!isset($blocks[$data['section']])) {
14
- $blocks[$data['section']] = array();
15
- }
16
- $blocks[$data['section']][$key]['name'] = $data['name'];
17
- $blocks[$data['section']][$key]['filename'] = $key;
18
- $blocks[$data['section']][$key]['icon'] = $data['icon'];
19
- }
20
-
21
- // order the sections
22
- $blocks = array_merge(array_flip(array('header', 'content', 'footer')), $blocks);
23
-
24
- // prepare the options for the default blocks
25
- $block_options = get_option('newsletter_main');
26
-
27
- $fields = new NewsletterFields($controls);
28
-
29
- $dir = is_rtl() ? 'rtl' : 'ltr';
30
- $rev_dir = is_rtl() ? 'ltr' : 'rlt';
31
- ?>
32
- <script type="text/javascript">
33
- if (window.innerWidth < 1550) {
34
- document.body.classList.add('folded');
35
- }
36
-
37
- function tnp_view(type) {
38
- if (type === 'mobile') {
39
- jQuery('#newsletter-builder-area-center-frame-content').addClass('tnp-view-mobile');
40
- } else {
41
- jQuery('#newsletter-builder-area-center-frame-content').removeClass('tnp-view-mobile');
42
- }
43
- }
44
- </script>
45
-
46
-
47
- <style>
48
- <?php echo NewsletterEmails::instance()->get_composer_backend_css(); ?>
49
- </style>
50
- <div id="newsletter-builder" dir="ltr">
51
-
52
- <div id="newsletter-builder-area" class="tnp-builder-column">
53
-
54
- <?php if ($tnpc_show_subject) { ?>
55
- <div id="tnpc-subject-wrap" dir="<?php echo $dir ?>">
56
- <table role="presentation" style="width: 100%">
57
- <?php if (!empty($controls->data['sender_email'])) { ?>
58
- <tr>
59
- <th dir="<?php echo $dir ?>"><?php _e('From', 'newsletter') ?></th>
60
- <td dir="<?php echo $dir ?>"><?php echo esc_html($controls->data['sender_email']) ?></td>
61
- </tr>
62
- <?php } ?>
63
- <tr>
64
- <th dir="<?php echo $dir ?>">
65
- <?php _e('Subject', 'newsletter') ?>
66
- <?php if ($context_type === 'automated') { ?>
67
- <?php $this->field_help('https://www.thenewsletterplugin.com/documentation/addons/extended-features/automated-extension/#subject') ?>
68
- <?php } ?>
69
- </th>
70
- <td dir="<?php echo $dir ?>">
71
- <div id="tnpc-subject">
72
- <?php $this->subject('subject'); ?>
73
- </div>
74
- </td>
75
- </tr>
76
- <tr>
77
- <th dir="<?php echo $dir ?>"><span title="<?php esc_attr_e('Shown by some email clients as excerpt', 'newsletter') ?>"><?php _e('Snippet', 'newsletter') ?></span>
78
- <?php $this->field_help('https://www.thenewsletterplugin.com/documentation/newsletters/composer/#subject') ?>
79
- </th>
80
- <td dir="<?php echo $dir ?>"><?php $this->text('preheader') ?></td>
81
- </tr>
82
- </table>
83
-
84
- <div style="text-align: left; margin-left: 1em;">
85
- <a href="https://www.thenewsletterplugin.com/documentation/newsletters/newsletter-tags/"
86
- target="_blank">You can use tags to inject subscriber fields</a>. Even on subject.
87
-
88
-
89
- </div>
90
-
91
- <div class="composer-actions">
92
-
93
- <div id="attachment-newsletter-button" class="button-primary" data-tnp-modal-target="#attachment-modal">
94
- <i class="fas fa-paperclip"></i>
95
- </div>
96
- <?php if ($show_test) { ?>
97
- <div id="test-newsletter-button" class="button-primary" data-tnp-modal-target="#test-newsletter-modal">
98
- <i class="fas fa-paper-plane"></i> <?php _e('Test', 'newsletter') ?>
99
- </div>
100
- <?php } ?>
101
-
102
- <div class="composer-view-mode">
103
- <span class="composer-view-mode__item" data-view-mode="desktop">
104
- <i class="fas fa-desktop"></i>
105
- </span>
106
-
107
- <span class="composer-view-mode__item" data-view-mode="mobile">
108
- <i class="fas fa-mobile"></i>
109
- <span>
110
- </div>
111
-
112
- </div>
113
-
114
- <?php include NEWSLETTER_DIR . '/emails/tnp-composer/modal/test-newsletter.php' ?>
115
- <?php include NEWSLETTER_DIR . '/emails/tnp-composer/modal/attachment.php' ?>
116
-
117
- </div>
118
- <?php } ?>
119
-
120
-
121
- <div id="newsletter-builder-area-center-frame-content" dir="<?php echo $dir ?>">
122
-
123
- <!-- Composer content -->
124
-
125
- </div>
126
- </div>
127
-
128
- <div id="newsletter-builder-sidebar" dir="<?php echo is_rtl() ? 'rtl' : 'ltr' ?>">
129
-
130
- <div class="tnpc-tabs">
131
- <button class="tablinks" onclick="openTab(event, 'tnpc-blocks')" data-tab-id='tnpc-blocks' id="defaultOpen"><?php _e('Blocks', 'newsletter') ?></button>
132
- <button class="tablinks" onclick="openTab(event, 'tnpc-global-styles')" data-tab-id='tnpc-global-styles'><?php _e('Settings', 'newsletter') ?></button>
133
- </div>
134
-
135
- <div id="tnpc-blocks" class="tabcontent">
136
- <?php foreach ($blocks as $k => $section) { ?>
137
- <div class="newsletter-sidebar-add-buttons" id="sidebar-add-<?php echo $k ?>">
138
- <!--<h4><span><?php echo ucfirst($k) ?></span></h4>-->
139
- <?php foreach ($section AS $key => $block) { ?>
140
- <div class="newsletter-sidebar-buttons-content-tab" data-id="<?php echo $key ?>" data-name="<?php echo esc_attr($block['name']) ?>">
141
- <img src="<?php echo $block['icon'] ?>" title="<?php echo esc_attr($block['name']) ?>">
142
- </div>
143
- <?php } ?>
144
- </div>
145
- <?php } ?>
146
- </div>
147
-
148
- <div id="tnpc-global-styles" class="tabcontent">
149
-
150
- <form id="tnpc-global-styles-form">
151
-
152
- <div class="tnp-field-row">
153
- <div class="tnp-field-col-2">
154
- <?php $fields->color('options_composer_background', __('Main background', 'newsletter')) ?>
155
- </div>
156
- <div class="tnp-field-col-2">
157
- <?php $fields->color('options_composer_block_background', 'Blocks background') ?>
158
- </div>
159
- </div>
160
-
161
- <?php $fields->font('options_composer_title_font', __('Titles font', 'newsletter')) ?>
162
- <?php $fields->font('options_composer_text_font', __('Text font', 'newsletter')) ?>
163
- <?php $fields->button_style('options_composer_button', __('Button style', 'newsletter')); ?>
164
-
165
- <button class="button-secondary" name="apply"><?php _e("Apply", 'newsletter') ?></button>
166
-
167
- </form>
168
-
169
- </div>
170
-
171
- <!-- Block options container (dynamically loaded -->
172
- <div id="tnpc-block-options">
173
- <div id="tnpc-block-options-buttons">
174
- <span id="tnpc-block-options-cancel" class="button-secondary"><?php _e("Cancel", "newsletter") ?></span>
175
- <span id="tnpc-block-options-save" class="button-primary"><?php _e("Apply", "newsletter") ?></span>
176
- </div>
177
- <form id="tnpc-block-options-form" onsubmit="return false;"></form>
178
- </div>
179
-
180
- </div>
181
-
182
- <div style="clear: both"></div>
183
-
184
- </div>
185
-
186
- <div style="display: none">
187
- <div id="newsletter-preloaded-export"></div>
188
- <!-- Block placeholder used by jQuery UI -->
189
- <div id="draggable-helper"></div>
190
- <div id="sortable-helper"></div>
191
- </div>
192
-
193
- <script type="text/javascript">
194
- TNP_PLUGIN_URL = "<?php echo esc_js(NEWSLETTER_URL) ?>";
195
- TNP_HOME_URL = "<?php echo esc_js(home_url('/', is_ssl() ? 'https' : 'http')) ?>";
196
- tnp_context_type = "<?php echo esc_js($context_type) ?>";
197
- tnp_nonce = '<?php echo esc_js(wp_create_nonce('save')) ?>';
198
- tnp_preset_nonce = '<?php echo esc_js(wp_create_nonce('preset')) ?>';
199
- </script>
200
- <?php
201
- wp_enqueue_script('tnp-composer', plugins_url('newsletter') . '/emails/tnp-composer/_scripts/newsletter-builder-v2.js', ['tnp-modal', 'tnp-toast'], NEWSLETTER_VERSION);
202
- ?>
203
-
204
- <?php include NEWSLETTER_DIR . '/emails/subjects.php'; ?>
205
-
206
- <?php if (function_exists('wp_enqueue_editor')) wp_enqueue_editor(); ?>
1
+ <?php
2
+ /**
3
+ * This file is included by NewsletterControls to create the composer.
4
+ */
5
+ /* @var $this NewsletterControls */
6
+
7
+ defined('ABSPATH') || exit;
8
+
9
+ $list = NewsletterEmails::instance()->get_blocks();
10
+
11
+ $blocks = array();
12
+ foreach ($list as $key => $data) {
13
+ if (!isset($blocks[$data['section']])) {
14
+ $blocks[$data['section']] = array();
15
+ }
16
+ $blocks[$data['section']][$key]['name'] = $data['name'];
17
+ $blocks[$data['section']][$key]['filename'] = $key;
18
+ $blocks[$data['section']][$key]['icon'] = $data['icon'];
19
+ }
20
+
21
+ // order the sections
22
+ $blocks = array_merge(array_flip(array('header', 'content', 'footer')), $blocks);
23
+
24
+ // prepare the options for the default blocks
25
+ $block_options = get_option('newsletter_main');
26
+
27
+ $fields = new NewsletterFields($controls);
28
+
29
+ $dir = is_rtl() ? 'rtl' : 'ltr';
30
+ $rev_dir = is_rtl() ? 'ltr' : 'rlt';
31
+ ?>
32
+ <script type="text/javascript">
33
+ if (window.innerWidth < 1550) {
34
+ document.body.classList.add('folded');
35
+ }
36
+
37
+ function tnp_view(type) {
38
+ if (type === 'mobile') {
39
+ jQuery('#newsletter-builder-area-center-frame-content').addClass('tnp-view-mobile');
40
+ } else {
41
+ jQuery('#newsletter-builder-area-center-frame-content').removeClass('tnp-view-mobile');
42
+ }
43
+ }
44
+ </script>
45
+
46
+
47
+ <style>
48
+ <?php echo NewsletterEmails::instance()->get_composer_backend_css(); ?>
49
+ </style>
50
+ <div id="newsletter-builder" dir="ltr">
51
+
52
+ <div id="newsletter-builder-area" class="tnp-builder-column">
53
+
54
+ <?php if ($tnpc_show_subject) { ?>
55
+ <div id="tnpc-subject-wrap" dir="<?php echo $dir ?>">
56
+ <table role="presentation" style="width: 100%">
57
+ <?php if (!empty($controls->data['sender_email'])) { ?>
58
+ <tr>
59
+ <th dir="<?php echo $dir ?>"><?php _e('From', 'newsletter') ?></th>
60
+ <td dir="<?php echo $dir ?>"><?php echo esc_html($controls->data['sender_email']) ?></td>
61
+ </tr>
62
+ <?php } ?>
63
+ <tr>
64
+ <th dir="<?php echo $dir ?>">
65
+ <?php _e('Subject', 'newsletter') ?>
66
+ <?php if ($context_type === 'automated') { ?>
67
+ <?php $this->field_help('https://www.thenewsletterplugin.com/documentation/addons/extended-features/automated-extension/#subject') ?>
68
+ <?php } ?>
69
+ </th>
70
+ <td dir="<?php echo $dir ?>">
71
+ <div id="tnpc-subject">
72
+ <?php $this->subject('subject'); ?>
73
+ </div>
74
+ </td>
75
+ </tr>
76
+ <tr>
77
+ <th dir="<?php echo $dir ?>"><span title="<?php esc_attr_e('Shown by some email clients as excerpt', 'newsletter') ?>"><?php _e('Snippet', 'newsletter') ?></span>
78
+ <?php $this->field_help('https://www.thenewsletterplugin.com/documentation/newsletters/composer/#subject') ?>
79
+ </th>
80
+ <td dir="<?php echo $dir ?>"><?php $this->text('preheader') ?></td>
81
+ </tr>
82
+ </table>
83
+
84
+ <div style="text-align: left; margin-left: 1em;">
85
+ <a href="https://www.thenewsletterplugin.com/documentation/newsletters/newsletter-tags/"
86
+ target="_blank">You can use tags to inject subscriber fields</a>. Even on subject.
87
+
88
+
89
+ </div>
90
+
91
+ <div class="composer-actions">
92
+
93
+ <div id="attachment-newsletter-button" class="button-primary" data-tnp-modal-target="#attachment-modal">
94
+ <i class="fas fa-paperclip"></i>
95
+ </div>
96
+ <?php if ($show_test) { ?>
97
+ <div id="test-newsletter-button" class="button-primary" data-tnp-modal-target="#test-newsletter-modal">
98
+ <i class="fas fa-paper-plane"></i> <?php _e('Test', 'newsletter') ?>
99
+ </div>
100
+ <?php } ?>
101
+
102
+ <div class="composer-view-mode">
103
+ <span class="composer-view-mode__item" data-view-mode="desktop">
104
+ <i class="fas fa-desktop"></i>
105
+ </span>
106
+
107
+ <span class="composer-view-mode__item" data-view-mode="mobile">
108
+ <i class="fas fa-mobile"></i>
109
+ <span>
110
+ </div>
111
+
112
+ </div>
113
+
114
+ <?php include NEWSLETTER_DIR . '/emails/tnp-composer/modal/test-newsletter.php' ?>
115
+ <?php include NEWSLETTER_DIR . '/emails/tnp-composer/modal/attachment.php' ?>
116
+
117
+ </div>
118
+ <?php } ?>
119
+
120
+
121
+ <div id="newsletter-builder-area-center-frame-content" dir="<?php echo $dir ?>">
122
+
123
+ <!-- Composer content -->
124
+
125
+ </div>
126
+ </div>
127
+
128
+ <div id="newsletter-builder-sidebar" dir="<?php echo is_rtl() ? 'rtl' : 'ltr' ?>">
129
+
130
+ <div class="tnpc-tabs">
131
+ <button class="tablinks" onclick="openTab(event, 'tnpc-blocks')" data-tab-id='tnpc-blocks' id="defaultOpen"><?php _e('Blocks', 'newsletter') ?></button>
132
+ <button class="tablinks" onclick="openTab(event, 'tnpc-global-styles')" data-tab-id='tnpc-global-styles'><?php _e('Settings', 'newsletter') ?></button>
133
+ </div>
134
+
135
+ <div id="tnpc-blocks" class="tabcontent">
136
+ <?php foreach ($blocks as $k => $section) { ?>
137
+ <div class="newsletter-sidebar-add-buttons" id="sidebar-add-<?php echo $k ?>">
138
+ <!--<h4><span><?php echo ucfirst($k) ?></span></h4>-->
139
+ <?php foreach ($section AS $key => $block) { ?>
140
+ <div class="newsletter-sidebar-buttons-content-tab" data-id="<?php echo $key ?>" data-name="<?php echo esc_attr($block['name']) ?>">
141
+ <img src="<?php echo $block['icon'] ?>" title="<?php echo esc_attr($block['name']) ?>">
142
+ </div>
143
+ <?php } ?>
144
+ </div>
145
+ <?php } ?>
146
+ </div>
147
+
148
+ <div id="tnpc-global-styles" class="tabcontent">
149
+
150
+ <form id="tnpc-global-styles-form">
151
+
152
+ <div class="tnp-field-row">
153
+ <div class="tnp-field-col-2">
154
+ <?php $fields->color('options_composer_background', __('Main background', 'newsletter')) ?>
155
+ </div>
156
+ <div class="tnp-field-col-2">
157
+ <?php $fields->color('options_composer_block_background', 'Blocks background') ?>
158
+ </div>
159
+ </div>
160
+
161
+ <?php $fields->font('options_composer_title_font', __('Titles font', 'newsletter')) ?>
162
+ <?php $fields->font('options_composer_text_font', __('Text font', 'newsletter')) ?>
163
+ <?php $fields->button_style('options_composer_button', __('Button style', 'newsletter')); ?>
164
+
165
+ <button class="button-secondary" name="apply"><?php _e("Apply", 'newsletter') ?></button>
166
+
167
+ </form>
168
+
169
+ </div>
170
+
171
+ <!-- Block options container (dynamically loaded -->
172
+ <div id="tnpc-block-options">
173
+ <div id="tnpc-block-options-buttons">
174
+ <span id="tnpc-block-options-cancel" class="button-secondary"><?php _e("Cancel", "newsletter") ?></span>
175
+ <span id="tnpc-block-options-save" class="button-primary"><?php _e("Apply", "newsletter") ?></span>
176
+ </div>
177
+ <form id="tnpc-block-options-form" onsubmit="return false;"></form>
178
+ </div>
179
+
180
+ </div>
181
+
182
+ <div style="clear: both"></div>
183
+
184
+ </div>
185
+
186
+ <div style="display: none">
187
+ <div id="newsletter-preloaded-export"></div>
188
+ <!-- Block placeholder used by jQuery UI -->
189
+ <div id="draggable-helper"></div>
190
+ <div id="sortable-helper"></div>
191
+ </div>
192
+
193
+ <script type="text/javascript">
194
+ TNP_PLUGIN_URL = "<?php echo esc_js(NEWSLETTER_URL) ?>";
195
+ TNP_HOME_URL = "<?php echo esc_js(home_url('/', is_ssl() ? 'https' : 'http')) ?>";
196
+ tnp_context_type = "<?php echo esc_js($context_type) ?>";
197
+ tnp_nonce = '<?php echo esc_js(wp_create_nonce('save')) ?>';
198
+ tnp_preset_nonce = '<?php echo esc_js(wp_create_nonce('preset')) ?>';
199
+ </script>
200
+ <?php
201
+ wp_enqueue_script('tnp-composer', plugins_url('newsletter') . '/emails/tnp-composer/_scripts/newsletter-builder-v2.js', ['tnp-modal', 'tnp-toast'], NEWSLETTER_VERSION);
202
+ ?>
203
+
204
+ <?php include NEWSLETTER_DIR . '/emails/subjects.php'; ?>
205
+
206
+ <?php if (function_exists('wp_enqueue_editor')) wp_enqueue_editor(); ?>
includes/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
@@ -98,33 +98,43 @@ class TNP_Composer {
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";
@@ -141,7 +151,7 @@ class TNP_Composer {
141
  return "";
142
  }
143
 
144
- $preheader_text = $email->options['preheader'];
145
  $html = "<div style=\"display:none;font-size:1px;color:#ffffff;line-height:1px;max-height:0px;max-width:0px;opacity:0;overflow:hidden;\">$preheader_text</div>";
146
  $html .= "\n";
147
 
@@ -681,6 +691,11 @@ class TNP_Composer {
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) {
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";
103
+ $open .= '<head>' . "\n";
104
+ $open .= '<title>{email_subject}</title>' . "\n";
105
+ $open .= '<meta charset="utf-8">' . "\n";
106
+ $open .= '<meta name="viewport" content="width=device-width, initial-scale=1">' . "\n";
107
+ $open .= '<meta http-equiv="X-UA-Compatible" content="IE=edge">' . "\n";
108
+ $open .= '<meta name="format-detection" content="address=no">' . "\n";
109
+ $open .= '<meta name="format-detection" content="telephone=no">' . "\n";
110
+ $open .= '<meta name="format-detection" content="email=no">' . "\n";
111
+ $open .= '<meta name="x-apple-disable-message-reformatting">' . "\n";
112
+
113
+ // $open .= '<!--[if !mso]><!-->' . "\n";
114
+ // $open .= '<meta http-equiv="X-UA-Compatible" content="IE=edge" />' . "\n";
115
+ // $open .= '<!--<![endif]-->' . "\n";
116
+
117
+ // $open .= '<!--[if mso]>' . "\n";
118
+
119
+ $open .= '<!--[if gte mso 9]><xml><o:OfficeDocumentSettings><o:AllowPNG/><o:PixelsPerInch>96</o:PixelsPerInch></o:OfficeDocumentSettings></xml><![endif]-->' . "\n";
120
+
121
+ // $open .= '<style type="text/css">';
122
+ // $open .= 'table {border-collapse:collapse;border-spacing:0;margin:0;}';
123
+ // $open .= 'div, td {padding:0;}';
124
+ // $open .= 'div {margin:0 !important;}';
125
+ // $open .= '</style>';
126
+ // $open .= "\n";
127
+ // $open .= '<noscript>';
128
+ // $open .= '<xml>';
129
+ // $open .= '<o:OfficeDocumentSettings>';
130
+ // $open .= '<o:PixelsPerInch>96</o:PixelsPerInch>';
131
+ // $open .= '</o:OfficeDocumentSettings>';
132
+ // $open .= '</xml>';
133
+ // $open .= '</noscript>';
134
+ // $open .= "\n";
135
+ // $open .= '<![endif]-->';
136
+ // $open .= "\n";
137
+ $open .= '<style type="text/css">' . "\n";
138
  $open .= NewsletterEmails::instance()->get_composer_css();
139
  $open .= "\n</style>\n";
140
  $open .= "</head>\n";
151
  return "";
152
  }
153
 
154
+ $preheader_text = esc_html($email->options['preheader']);
155
  $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>";
156
  $html .= "\n";
157
 
691
  //$logger->debug('new html="' . $html . '"');
692
  //
693
  $output = '';
694
+
695
+ // Prevents warnings for problems with the HTML
696
+ if (function_exists('libxml_use_internal_errors')) {
697
+ libxml_use_internal_errors(true);
698
+ }
699
  $dom = new DOMDocument();
700
  $r = $dom->loadHTML('<?xml encoding="utf-8" ?>' . $html);
701
  if (!$r) {
includes/mailer.php CHANGED
@@ -227,6 +227,7 @@ class NewsletterDefaultMailer extends NewsletterMailer {
227
  function __construct() {
228
  parent::__construct('default', Newsletter::instance()->get_options('smtp'));
229
  add_action('wp_mail_failed', [$this, 'hook_wp_mail_failed']);
 
230
  }
231
 
232
  function hook_wp_mail_failed($error) {
227
  function __construct() {
228
  parent::__construct('default', Newsletter::instance()->get_options('smtp'));
229
  add_action('wp_mail_failed', [$this, 'hook_wp_mail_failed']);
230
+ remove_filter('wp_mail', 'wp_staticize_emoji_for_email');
231
  }
232
 
233
  function hook_wp_mail_failed($error) {
includes/store.php CHANGED
@@ -1,252 +1,252 @@
1
- <?php
2
- defined('ABSPATH') || exit;
3
-
4
- @require_once NEWSLETTER_INCLUDES_DIR . '/logger.php';
5
-
6
- class NewsletterStore {
7
-
8
- static $instance = null;
9
-
10
- /**
11
- * @var NewsletterLogger
12
- */
13
- var $logger;
14
-
15
- /**
16
- *
17
- * @return NewsletterStore
18
- */
19
- static function instance() {
20
- if (self::$instance == null) {
21
- self::$instance = new NewsletterStore();
22
- }
23
- return self::$instance;
24
- }
25
-
26
- static function singleton() {
27
- return self::instance();
28
- }
29
-
30
- function __construct() {
31
- $this->logger = new NewsletterLogger('store');
32
- }
33
-
34
- function get_field($table, $id, $field_name) {
35
- global $wpdb;
36
- $field_name = (string)$field_name;
37
- if (preg_match('/^[a-zA-Z0-9_]+$/', $field_name) == 0) {
38
- $this->logger->fatal('Invalis field name: ' . $field_name);
39
- return false;
40
- }
41
- $id = (int)$id;
42
- $r = $wpdb->get_var($wpdb->prepare("select $field_name from $table where id=%d limit 1", $id));
43
- if ($wpdb->last_error) {
44
- $this->logger->error($wpdb->last_error);
45
- return false;
46
- }
47
- return $r;
48
- }
49
-
50
- function get_single($table, $id, $format = OBJECT) {
51
- global $wpdb;
52
- $id = (int)$id;
53
- return $this->get_single_by_query($wpdb->prepare("select * from $table where id=%d limit 1", $id), $format);
54
- }
55
-
56
- function get_single_by_field($table, $field_name, $field_value, $format = OBJECT) {
57
- global $wpdb;
58
- $field_name = (string)$field_name;
59
- $field_value = (string)$field_value;
60
- if (preg_match('/^[a-zA-Z0-9_]+$/', $field_name) == 0) {
61
- $this->logger->error('Invalis field name: ' . $field_name);
62
- return false;
63
- }
64
- return $this->get_single_by_query($wpdb->prepare("select * from $table where $field_name=%s limit 1", $field_value), $format);
65
- }
66
-
67
- function get_count($table, $where = null) {
68
- global $wpdb;
69
- $r = $wpdb->get_var("select count(*) from $table " . ($where != null ? $where : ''));
70
- if ($wpdb->last_error) {
71
- $this->logger->error($wpdb->last_error);
72
- return false;
73
- }
74
- return $r;
75
- }
76
-
77
- /**
78
- * Returns a single record executing the given query or null if no row can be found. If more rows are matching
79
- * the query, only the first one is returned. Returns "false" on error (use the strict type checking ===) and a log
80
- * is written.
81
- *
82
- * @global wpdb $wpdb
83
- * @param string $query
84
- * @param type $format
85
- * @return boolean|mixed
86
- */
87
- function get_single_by_query($query, $format = OBJECT) {
88
- global $wpdb;
89
- $r = $wpdb->get_row($query, $format);
90
- if ($wpdb->last_error) {
91
- $this->logger->error($wpdb->last_error);
92
- return false;
93
- }
94
- return $r;
95
- }
96
-
97
- function sanitize($data) {
98
- global $wpdb;
99
- //if (strpos($wpdb->charset, 'utf8mb4') === 0) return $data;
100
- if (strpos($wpdb->charset, 'utf8mb4') === 0) return $data;
101
- foreach ($data as $key => $value) {
102
- $data[$key] = preg_replace('%(?:\xF0[\x90-\xBF][\x80-\xBF]{2}|[\xF1-\xF3][\x80-\xBF]{3}|\xF4[\x80-\x8F][\x80-\xBF]{2})%xs', '', $value);
103
- }
104
- return $data;
105
- }
106
-
107
- /**
108
- * Save a record on given table, updating it id the "id" value is set (as key or object property) or inserting it
109
- * if "id" is not set. Accepts objects or associative arrays as data.
110
- *
111
- * Returns "false" is an error occurred or the saved data re-read from the database in given format.
112
- *
113
- * @global wpdb $wpdb
114
- * @param type $table
115
- * @param type $data
116
- */
117
- function save($table, $data, $return_format = OBJECT) {
118
- global $wpdb;
119
- if (is_object($data)) {
120
- $data = (array) $data;
121
- }
122
-
123
- // Remove transient fields
124
- foreach (array_keys($data) as $key) {
125
- if (substr($key, 0, 1) == '_') unset($data[$key]);
126
- }
127
-
128
- //$this->logger->debug($data);
129
-
130
- if (isset($data['id'])) {
131
- $id = (int)$data['id'];
132
- unset($data['id']);
133
- if (!empty($data)) {
134
- $r = $wpdb->update($table, $this->sanitize($data), array('id' => $id));
135
- if ($r === false) {
136
- $this->logger->fatal($wpdb->last_error);
137
- $this->logger->fatal($wpdb->last_query);
138
- $this->logger->debug($data);
139
- die('Database error. If you were saving a newsletter try a table upgrade from the status panel.');
140
- }
141
- }
142
- //$this->logger->debug('save: ' . $wpdb->last_query);
143
- } else {
144
- $r = $wpdb->insert($table, $this->sanitize($data));
145
- if ($r === false) {
146
- $this->logger->fatal($wpdb->last_error);
147
- $this->logger->fatal($wpdb->last_query);
148
- $this->logger->debug($data);
149
- die('Database error. If you were saving a newsletter try a table upgrade from the status panel.');
150
- }
151
- $id = $wpdb->insert_id;
152
- }
153
- // if ($wpdb->last_error) {
154
- // $this->logger->error('save: ' . $wpdb->last_error);
155
- // return false;
156
- // }
157
-
158
- return $this->get_single($table, $id, $return_format);
159
- }
160
-
161
- function increment($table, $id, $field) {
162
- global $wpdb;
163
- $id = (int)$id;
164
- $field = (string)$field;
165
-
166
- if (preg_match('/^[a-zA-Z0-9_]+$/', $field) == 0) {
167
- $this->logger->error('Invalis field name: ' . $field);
168
- return false;
169
- }
170
- $result = $wpdb->query($wpdb->prepare("update $table set $field=$field+1 where id=%d", $id));
171
-
172
- if ($wpdb->last_error) {
173
- $this->logger->error($wpdb->last_error);
174
- return false;
175
- }
176
-
177
- return $result;
178
- }
179
-
180
- /**
181
- * Deletes one or more rows by id (or an array of id)
182
- *
183
- * @param int|array $id
184
- * @return int Number of rows deleted
185
- */
186
- function delete($table, $id) {
187
- global $wpdb;
188
- if (empty($id)) return false;
189
- if (is_array($id)) {
190
- for ($i=0; $i<count($id); $i++) {
191
- $id[$i] = (int)$id[$i];
192
- }
193
- $wpdb->query("delete from " . $table . " where id in (" . implode(',', $id) . ")");
194
- } else {
195
- $wpdb->delete($table, ['id' => (int)$id]);
196
- }
197
- if ($wpdb->last_error) {
198
- $this->logger->error($wpdb->last_error);
199
- return false;
200
- }
201
- return $wpdb->rows_affected;
202
- }
203
-
204
- /**
205
- *
206
- * @global wpdb $wpdb
207
- * @param type $table
208
- * @param type $order_by
209
- * @param type $format
210
- * @return type
211
- */
212
- function get_all($table, $where = null, $format = OBJECT) {
213
- global $wpdb;
214
- if ($where == null) {
215
- $result = $wpdb->get_results("select * from $table", $format);
216
- } else {
217
- $result = $wpdb->get_results("select * from $table $where", $format);
218
- }
219
- return $result;
220
- }
221
-
222
- function set_field($table, $id, $field, $value) {
223
- global $wpdb;
224
- $field = (string)$field;
225
- $id = (int)$id;
226
- $value = (string)$value;
227
-
228
- if (preg_match('/^[a-zA-Z0-9_]+$/', $field) == 0) {
229
- $this->logger->error('Invalis field name: ' . $field_name);
230
- return false;
231
- }
232
- $result = $wpdb->query($wpdb->prepare("update $table set $field=%s where id=%d limit 1", $value, $id));
233
-
234
- if ($wpdb->last_error) {
235
- $this->logger->error($wpdb->last_error);
236
- return false;
237
- }
238
-
239
- return $result;
240
- }
241
-
242
- function query($query) {
243
- global $wpdb;
244
- $result = $wpdb->query($query);
245
- if ($wpdb->last_error) {
246
- $this->logger->error($wpdb->last_error);
247
- return false;
248
- }
249
- return $result;
250
- }
251
-
252
- }
1
+ <?php
2
+ defined('ABSPATH') || exit;
3
+
4
+ @require_once NEWSLETTER_INCLUDES_DIR . '/logger.php';
5
+
6
+ class NewsletterStore {
7
+
8
+ static $instance = null;
9
+
10
+ /**
11
+ * @var NewsletterLogger
12
+ */
13
+ var $logger;
14
+
15
+ /**
16
+ *
17
+ * @return NewsletterStore
18
+ */
19
+ static function instance() {
20
+ if (self::$instance == null) {
21
+ self::$instance = new NewsletterStore();
22
+ }
23
+ return self::$instance;
24
+ }
25
+
26
+ static function singleton() {
27
+ return self::instance();
28
+ }
29
+
30
+ function __construct() {
31
+ $this->logger = new NewsletterLogger('store');
32
+ }
33
+
34
+ function get_field($table, $id, $field_name) {
35
+ global $wpdb;
36
+ $field_name = (string)$field_name;
37
+ if (preg_match('/^[a-zA-Z0-9_]+$/', $field_name) == 0) {
38
+ $this->logger->fatal('Invalis field name: ' . $field_name);
39
+ return false;
40
+ }
41
+ $id = (int)$id;
42
+ $r = $wpdb->get_var($wpdb->prepare("select $field_name from $table where id=%d limit 1", $id));
43
+ if ($wpdb->last_error) {
44
+ $this->logger->error($wpdb->last_error);
45
+ return false;
46
+ }
47
+ return $r;
48
+ }
49
+
50
+ function get_single($table, $id, $format = OBJECT) {
51
+ global $wpdb;
52
+ $id = (int)$id;
53
+ return $this->get_single_by_query($wpdb->prepare("select * from $table where id=%d limit 1", $id), $format);
54
+ }
55
+
56
+ function get_single_by_field($table, $field_name, $field_value, $format = OBJECT) {
57
+ global $wpdb;
58
+ $field_name = (string)$field_name;
59
+ $field_value = (string)$field_value;
60
+ if (preg_match('/^[a-zA-Z0-9_]+$/', $field_name) == 0) {
61
+ $this->logger->error('Invalis field name: ' . $field_name);
62
+ return false;
63
+ }
64
+ return $this->get_single_by_query($wpdb->prepare("select * from $table where $field_name=%s limit 1", $field_value), $format);
65
+ }
66
+
67
+ function get_count($table, $where = null) {
68
+ global $wpdb;
69
+ $r = $wpdb->get_var("select count(*) from $table " . ($where != null ? $where : ''));
70
+ if ($wpdb->last_error) {
71
+ $this->logger->error($wpdb->last_error);
72
+ return false;
73
+ }
74
+ return $r;
75
+ }
76
+
77
+ /**
78
+ * Returns a single record executing the given query or null if no row can be found. If more rows are matching
79
+ * the query, only the first one is returned. Returns "false" on error (use the strict type checking ===) and a log
80
+ * is written.
81
+ *
82
+ * @global wpdb $wpdb
83
+ * @param string $query
84
+ * @param type $format
85
+ * @return boolean|mixed
86
+ */
87
+ function get_single_by_query($query, $format = OBJECT) {
88
+ global $wpdb;
89
+ $r = $wpdb->get_row($query, $format);
90
+ if ($wpdb->last_error) {
91
+ $this->logger->error($wpdb->last_error);
92
+ return false;
93
+ }
94
+ return $r;
95
+ }
96
+
97
+ function sanitize($data) {
98
+ global $wpdb;
99
+ //if (strpos($wpdb->charset, 'utf8mb4') === 0) return $data;
100
+ if (strpos($wpdb->charset, 'utf8mb4') === 0) return $data;
101
+ foreach ($data as $key => $value) {
102
+ $data[$key] = preg_replace('%(?:\xF0[\x90-\xBF][\x80-\xBF]{2}|[\xF1-\xF3][\x80-\xBF]{3}|\xF4[\x80-\x8F][\x80-\xBF]{2})%xs', '', $value);
103
+ }
104
+ return $data;
105
+ }
106
+
107
+ /**
108
+ * Save a record on given table, updating it id the "id" value is set (as key or object property) or inserting it
109
+ * if "id" is not set. Accepts objects or associative arrays as data.
110
+ *
111
+ * Returns "false" is an error occurred or the saved data re-read from the database in given format.
112
+ *
113
+ * @global wpdb $wpdb
114
+ * @param type $table
115
+ * @param type $data
116
+ */
117
+ function save($table, $data, $return_format = OBJECT) {
118
+ global $wpdb;
119
+ if (is_object($data)) {
120
+ $data = (array) $data;
121
+ }
122
+
123
+ // Remove transient fields
124
+ foreach (array_keys($data) as $key) {
125
+ if (substr($key, 0, 1) == '_') unset($data[$key]);
126
+ }
127
+
128
+ //$this->logger->debug($data);
129
+
130
+ if (isset($data['id'])) {
131
+ $id = (int)$data['id'];
132
+ unset($data['id']);
133
+ if (!empty($data)) {
134
+ $r = $wpdb->update($table, $this->sanitize($data), array('id' => $id));
135
+ if ($r === false) {
136
+ $this->logger->fatal($wpdb->last_error);
137
+ $this->logger->fatal($wpdb->last_query);
138
+ $this->logger->debug($data);
139
+ die('Database error. If you were saving a newsletter try a table upgrade from the status panel.');
140
+ }
141
+ }
142
+ //$this->logger->debug('save: ' . $wpdb->last_query);
143
+ } else {
144
+ $r = $wpdb->insert($table, $this->sanitize($data));
145
+ if ($r === false) {
146
+ $this->logger->fatal($wpdb->last_error);
147
+ $this->logger->fatal($wpdb->last_query);
148
+ $this->logger->debug($data);
149
+ die('Database error. If you were saving a newsletter try a table upgrade from the status panel.');
150
+ }
151
+ $id = $wpdb->insert_id;
152
+ }
153
+ // if ($wpdb->last_error) {
154
+ // $this->logger->error('save: ' . $wpdb->last_error);
155
+ // return false;
156
+ // }
157
+
158
+ return $this->get_single($table, $id, $return_format);
159
+ }
160
+
161
+ function increment($table, $id, $field) {
162
+ global $wpdb;
163
+ $id = (int)$id;
164
+ $field = (string)$field;
165
+
166
+ if (preg_match('/^[a-zA-Z0-9_]+$/', $field) == 0) {
167
+ $this->logger->error('Invalis field name: ' . $field);
168
+ return false;
169
+ }
170
+ $result = $wpdb->query($wpdb->prepare("update $table set $field=$field+1 where id=%d", $id));
171
+
172
+ if ($wpdb->last_error) {
173
+ $this->logger->error($wpdb->last_error);
174
+ return false;
175
+ }
176
+
177
+ return $result;
178
+ }
179
+
180
+ /**
181
+ * Deletes one or more rows by id (or an array of id)
182
+ *
183
+ * @param int|array $id
184
+ * @return int Number of rows deleted
185
+ */
186
+ function delete($table, $id) {
187
+ global $wpdb;
188
+ if (empty($id)) return false;
189
+ if (is_array($id)) {
190
+ for ($i=0; $i<count($id); $i++) {
191
+ $id[$i] = (int)$id[$i];
192
+ }
193
+ $wpdb->query("delete from " . $table . " where id in (" . implode(',', $id) . ")");
194
+ } else {
195
+ $wpdb->delete($table, ['id' => (int)$id]);
196
+ }
197
+ if ($wpdb->last_error) {
198
+ $this->logger->error($wpdb->last_error);
199
+ return false;
200
+ }
201
+ return $wpdb->rows_affected;
202
+ }
203
+
204
+ /**
205
+ *
206
+ * @global wpdb $wpdb
207
+ * @param type $table
208
+ * @param type $order_by
209
+ * @param type $format
210
+ * @return type
211
+ */
212
+ function get_all($table, $where = null, $format = OBJECT) {
213
+ global $wpdb;
214
+ if ($where == null) {
215
+ $result = $wpdb->get_results("select * from $table", $format);
216
+ } else {
217
+ $result = $wpdb->get_results("select * from $table $where", $format);
218
+ }
219
+ return $result;
220
+ }
221
+
222
+ function set_field($table, $id, $field, $value) {
223
+ global $wpdb;
224
+ $field = (string)$field;
225
+ $id = (int)$id;
226
+ $value = (string)$value;
227
+
228
+ if (preg_match('/^[a-zA-Z0-9_]+$/', $field) == 0) {
229
+ $this->logger->error('Invalis field name: ' . $field_name);
230
+ return false;
231
+ }
232
+ $result = $wpdb->query($wpdb->prepare("update $table set $field=%s where id=%d limit 1", $value, $id));
233
+
234
+ if ($wpdb->last_error) {
235
+ $this->logger->error($wpdb->last_error);
236
+ return false;
237
+ }
238
+
239
+ return $result;
240
+ }
241
+
242
+ function query($query) {
243
+ global $wpdb;
244
+ $result = $wpdb->query($query);
245
+ if ($wpdb->last_error) {
246
+ $this->logger->error($wpdb->last_error);
247
+ return false;
248
+ }
249
+ return $result;
250
+ }
251
+
252
+ }
includes/test-message.html CHANGED
@@ -1,87 +1,87 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <title>Test Message by The Newsletter Plugin</title>
5
- <!--[if !mso]><!-- -->
6
- <meta http-equiv="X-UA-Compatible" content="IE=edge">
7
- <!--<![endif]-->
8
- <meta charset="UTF-8">
9
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
10
- <style>
11
- body {
12
- margin: 0;
13
- padding: 0;
14
- -webkit-text-size-adjust: 100%;
15
- -ms-text-size-adjust: 100%;
16
- }
17
- img {
18
- max-width: 100%;
19
- }
20
- </style>
21
- </head>
22
- <body style="margin: 0; padding: 0">
23
-
24
- <table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width: 100%;">
25
- <tbody>
26
- <tr>
27
- <td style="direction: ltr; font-size: 16px; padding: 20px 0; text-align: center;">
28
-
29
- <p>
30
- That is an HTML email with some <strong>bold text</strong>,
31
- a <a href="https://www.thenewsletterplugin.com">cool link</a> and an image just below.
32
- </p>
33
-
34
- <img src="{plugin_url}/images/test.jpg" width="600" alt="Desk">
35
-
36
- </td>
37
- </tr>
38
-
39
- <tr>
40
- <td style="direction: ltr; font-size: 16px; padding: 20px 10px; text-align: center;">
41
- <p><strong>Emoji</strong></p>
42
- <p>
43
- 😋 💡 👍 👩 😲 😍
44
- </p>
45
- <p>Emoji are actually special characters, every device renders them in a different way (or not render at all!).</p>
46
- </td>
47
- </tr>
48
-
49
- <tr>
50
- <td style="direction: ltr; font-size: 16px; padding: 20px 10px; text-align: center;">
51
- <p><strong>Cyrillic</strong></p>
52
- <p>
53
- Лорем ипсум долор сит амет, нонумы инвидунт детерруиссет сит еа. Меис лаудем цонцлудатуряуе яуи ин.
54
- Ат цум веро иусто инвенире. Те вим лудус граецис. Диссентиас реформиданс ин меи. Сеа ад хабео
55
- алияуип, популо тинцидунт цонсецтетуер цу еум, еа хас дицта рецусабо.
56
- </p>
57
- </td>
58
- </tr>
59
-
60
- <tr>
61
- <td style="direction: ltr; font-size: 16px; padding: 20px 10px; text-align: center;">
62
- <p><strong>Greek</strong></p>
63
- <p>
64
- Λορεμ ιπσθμ δολορ σιτ αμετ, vιξ ωισι προμπτα αccθσαμθσ νο, εοσ σιντ λεγερε ινστρθcτιορ τε,
65
- προ cθ ταμqθαμ cομπλεcτιτθρ. Ρεφορμιδανσ ρεφερρεντθρ ηισ ιν, vιξ διαμ ιμπεδιτ αν. Εστ
66
- ηαβεο μεδιοcρεμ νο. Εθ ιθδιcο qθαεστιο vολθπτατθμ νεc, ιθσ θτ αλια ιθδιcαβιτ εξπλιcαρι,
67
- προ αππετερε cονσετετθρ ιδ. Ηισ νεμορε εξπετενδισ ρεφερρεντθρ ατ.
68
- </p>
69
- </td>
70
- </tr>
71
-
72
- <tr>
73
- <td style="direction: ltr; font-size: 16px; padding: 20px 10px; text-align: center;">
74
- <p><strong>Hindi</strong></p>
75
- <p>
76
- विवरन साधन दर्शाता सम्पर्क स्वतंत्रता अन्तरराष्ट्रीयकरन देखने सुना नयेलिए स्थापित
77
- कम्प्युटर रचना बनाना गटको कराना लक्ष्य स्थापित विभाजनक्षमता वास्तविक यन्त्रालय खण्ड
78
- मुख्य ७हल लचकनहि अमितकुमार लक्षण सारांश प्राथमिक देकर प्राण आंतरजाल मार्गदर्शन
79
- दिये मजबुत करता। भारतीय वैश्विक वहहर
80
- </p>
81
- </td>
82
- </tr>
83
- </tbody>
84
- </table>
85
-
86
- </body>
87
- </html>
1
+ <!doctype html>
2
+ <html>
3
+ <head>
4
+ <title>Test Message by The Newsletter Plugin</title>
5
+ <!--[if !mso]><!-- -->
6
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
7
+ <!--<![endif]-->
8
+ <meta charset="UTF-8">
9
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
10
+ <style>
11
+ body {
12
+ margin: 0;
13
+ padding: 0;
14
+ -webkit-text-size-adjust: 100%;
15
+ -ms-text-size-adjust: 100%;
16
+ }
17
+ img {
18
+ max-width: 100%;
19
+ }
20
+ </style>
21
+ </head>
22
+ <body style="margin: 0; padding: 0">
23
+
24
+ <table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width: 100%;">
25
+ <tbody>
26
+ <tr>
27
+ <td style="direction: ltr; font-size: 16px; padding: 20px 0; text-align: center;">
28
+
29
+ <p>
30
+ That is an HTML email with some <strong>bold text</strong>,
31
+ a <a href="https://www.thenewsletterplugin.com">cool link</a> and an image just below.
32
+ </p>
33
+
34
+ <img src="https://www.satollo.net/wp-content/plugins/newsletter/images/test.jpg" width="600" alt="Desk" style="max-width: 600px; width: 100%">
35
+
36
+ </td>
37
+ </tr>
38
+
39
+ <tr>
40
+ <td style="direction: ltr; font-size: 16px; padding: 20px 10px; text-align: center;">
41
+ <p><strong>Emoji</strong></p>
42
+ <p style="font-size: 16px;">
43
+ 😋 💡 👍 👩 😲 😍
44
+ </p>
45
+ <p>Emoji are actually special characters, every device renders them in a different way (or not render at all!).</p>
46
+ </td>
47
+ </tr>
48
+
49
+ <tr>
50
+ <td style="direction: ltr; font-size: 16px; padding: 20px 10px; text-align: center;">
51
+ <p><strong>Cyrillic</strong></p>
52
+ <p>
53
+ Лорем ипсум долор сит амет, нонумы инвидунт детерруиссет сит еа. Меис лаудем цонцлудатуряуе яуи ин.
54
+ Ат цум веро иусто инвенире. Те вим лудус граецис. Диссентиас реформиданс ин меи. Сеа ад хабео
55
+ алияуип, популо тинцидунт цонсецтетуер цу еум, еа хас дицта рецусабо.
56
+ </p>
57
+ </td>
58
+ </tr>
59
+
60
+ <tr>
61
+ <td style="direction: ltr; font-size: 16px; padding: 20px 10px; text-align: center;">
62
+ <p><strong>Greek</strong></p>
63
+ <p>
64
+ Λορεμ ιπσθμ δολορ σιτ αμετ, vιξ ωισι προμπτα αccθσαμθσ νο, εοσ σιντ λεγερε ινστρθcτιορ τε,
65
+ προ cθ ταμqθαμ cομπλεcτιτθρ. Ρεφορμιδανσ ρεφερρεντθρ ηισ ιν, vιξ διαμ ιμπεδιτ αν. Εστ
66
+ ηαβεο μεδιοcρεμ νο. Εθ ιθδιcο qθαεστιο vολθπτατθμ νεc, ιθσ θτ αλια ιθδιcαβιτ εξπλιcαρι,
67
+ προ αππετερε cονσετετθρ ιδ. Ηισ νεμορε εξπετενδισ ρεφερρεντθρ ατ.
68
+ </p>
69
+ </td>
70
+ </tr>
71
+
72
+ <tr>
73
+ <td style="direction: ltr; font-size: 16px; padding: 20px 10px; text-align: center;">
74
+ <p><strong>Hindi</strong></p>
75
+ <p>
76
+ विवरन साधन दर्शाता सम्पर्क स्वतंत्रता अन्तरराष्ट्रीयकरन देखने सुना नयेलिए स्थापित
77
+ कम्प्युटर रचना बनाना गटको कराना लक्ष्य स्थापित विभाजनक्षमता वास्तविक यन्त्रालय खण्ड
78
+ मुख्य ७हल लचकनहि अमितकुमार लक्षण सारांश प्राथमिक देकर प्राण आंतरजाल मार्गदर्शन
79
+ दिये मजबुत करता। भारतीय वैश्विक वहहर
80
+ </p>
81
+ </td>
82
+ </tr>
83
+ </tbody>
84
+ </table>
85
+
86
+ </body>
87
+ </html>
includes/tnp-blocks.js CHANGED
@@ -1,253 +1,253 @@
1
- (function (blocks, editor, element, components) {
2
-
3
-
4
- const el = element.createElement;
5
- const {registerBlockType} = blocks;
6
- const { RichText, InspectorControls, withColors, PanelColorSettings, getColorClassName, AlignmentToolbar, BlockControls } = editor;
7
- const { Fragment } = element;
8
- const { TextControl, RadioControl, Panel, PanelBody, PanelRow, SelectControl, RangeControl } = components;
9
- const colorSamples = [
10
- {
11
- name: 'GREEN SEA',
12
- slug: 'GREENSEA',
13
- color: '#16A085'
14
- },
15
- {
16
- name: 'NEPHRITIS',
17
- slug: 'NEPHRITIS',
18
- color: '#27AE60'
19
- },
20
- {
21
- name: 'BELIZE HOLE',
22
- slug: 'BELIZEHOLE',
23
- color: '#2980B9'
24
- },
25
- {
26
- name: 'WISTERIA',
27
- slug: 'WISTERIA',
28
- color: '#8E44AD'
29
- },
30
- {
31
- name: 'MIDNIGHT BLUE',
32
- slug: 'MIDNIGHTBLUE',
33
- color: '#2C3E50'
34
- },
35
- {
36
- name: 'ORANGE',
37
- slug: 'ORANGE',
38
- color: '#F39C12'
39
- },
40
- {
41
- name: 'ALIZARIN',
42
- slug: 'ALIZARIN',
43
- color: '#E74C3C'
44
- },
45
- {
46
- name: 'WHITE',
47
- slug: 'WHITE',
48
- color: '#FFFFFF'
49
- },
50
- {
51
- name: 'CLOUDS',
52
- slug: 'CLOUDS',
53
- color: '#ECF0F1'
54
- },
55
- {
56
- name: 'ASBESTOS',
57
- slug: 'ASBESTOS',
58
- color: '#7F8C8D'
59
- }
60
- ];
61
-
62
- registerBlockType('tnp/minimal', {
63
- title: 'Newsletter subscription form',
64
- icon: 'email',
65
- category: 'common',
66
- keywords: ['newsletter', 'subscription', 'form'],
67
- attributes: {
68
- formtype: {type: 'string', default: 'minimal'},
69
- content: { type: 'array', source: 'children', selector: 'p', default: 'Subscribe to our newsletter!'},
70
- list_ids: { type: 'string' },
71
- rowColor: { type: 'string'},
72
- customRowColor: { type: 'string'},
73
- textColor: { type: 'string'},
74
- customTextColor: { type: 'string'},
75
- buttonColor: { type: 'string'},
76
- customButtonColor: { type: 'string'},
77
- padding: {type: 'integer', default: 20},
78
- alignment: { type: 'string'}
79
- },
80
-
81
- edit: withColors('rowColor', 'textColor', 'buttonColor')(function (props) {
82
-
83
- function onChangeContent( newContent ) {
84
- props.setAttributes( { content: newContent } );
85
- }
86
-
87
- function onChangeAlignment( newAlignment ) {
88
- props.setAttributes( { alignment: newAlignment } );
89
- }
90
-
91
- return el( Fragment, {},
92
- el( InspectorControls, {},
93
-
94
- // 1st Panel - Form Settings
95
- el( PanelBody, { title: 'Form Settings', initialOpen: true },
96
-
97
- /* Form type */
98
- el( RadioControl,
99
- {
100
- label: 'Form type',
101
- options : [
102
- { label: 'Minimal', value: 'minimal' },
103
- { label: 'Full', value: 'full' },
104
- ],
105
- onChange: ( value ) => {
106
- props.setAttributes( { formtype: value } );
107
- },
108
- selected: props.attributes.formtype
109
- }
110
- ),
111
-
112
- /* Lists field */
113
- el( PanelRow, {},
114
- el( TextControl,
115
- {
116
- label: 'Lists IDs (comma separated)',
117
- onChange: ( value ) => {
118
- props.setAttributes( { list_ids: value } );
119
- },
120
- value: props.attributes.list_ids
121
- }
122
- )
123
- )
124
- ),
125
-
126
- /* Style */
127
- el( PanelColorSettings, {
128
- title: 'Style',
129
- colorSettings: [
130
- {
131
- colors: colorSamples, // here you can pass custom colors
132
- value: props.rowColor.color,
133
- label: 'Background color',
134
- onChange: props.setRowColor,
135
- },
136
- {
137
- colors: colorSamples, // here you can pass custom colors
138
- value: props.textColor.color,
139
- label: 'Text color',
140
- onChange: props.setTextColor,
141
- },
142
- {
143
- colors: colorSamples, // here you can pass custom colors
144
- value: props.buttonColor.color,
145
- label: 'Button color',
146
- onChange: props.setButtonColor,
147
- }
148
- ]
149
- }),
150
-
151
- el( RangeControl,
152
- {
153
- label: 'Padding',
154
- min: 0,
155
- max: 100,
156
- onChange: ( value ) => {
157
- props.setAttributes( { padding: value } );
158
- },
159
- value: props.attributes.padding
160
- }
161
- )
162
-
163
- ),
164
-
165
- el(
166
- "div",
167
- {style: {backgroundColor: props.rowColor.color, color: props.textColor.color, padding: props.attributes.padding, textAlign: props.attributes.alignment}},
168
- el(
169
- BlockControls,
170
- { key: 'controls' },
171
- el(
172
- AlignmentToolbar,
173
- {
174
- value: props.attributes.alignment,
175
- onChange: onChangeAlignment
176
- }
177
- )
178
- ),
179
- el(RichText,
180
- {
181
- tagName: 'p',
182
- format: 'string',
183
- onChange: onChangeContent,
184
- value: props.attributes.content,
185
- // formattingControls: [ 'bold' ]
186
- }),
187
- el('div',
188
- {style: {backgroundColor: 'lightGrey', margin: '20px', padding: '5px',
189
- fontFamily: '-apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen-Sans, Ubuntu, Cantarell, Helvetica Neue, sans-serif'}},
190
- el('svg',
191
- {
192
- width: 20,
193
- height: 20
194
- },
195
- wp.element.createElement( 'path',
196
- {
197
- d: "M6 14H4V6h2V4H2v12h4M7.1 17h2.1l3.7-14h-2.1M14 4v2h2v8h-2v2h4V4"
198
- }
199
- )
200
- ),
201
- ' Newsletter Form'
202
- ),
203
- ))
204
- }),
205
- save: function (props) {
206
-
207
- var rowClass = getColorClassName( 'row-color', props.attributes.rowColor );
208
- var textClass = getColorClassName( 'text-color', props.attributes.textColor );
209
- var buttonClass = getColorClassName( 'button-color', props.attributes.buttonColor );
210
-
211
- formtype_attr = "";
212
- if (props.attributes.formtype != "full") {
213
- formtype_attr = " type=\"minimal\"";
214
- }
215
-
216
- lists_attr = "";
217
- if (props.attributes.list_ids) {
218
- lists_attr = " lists=\"" + props.attributes.list_ids + "\"";
219
- }
220
-
221
- button_color_attr = "";
222
- button_color = buttonClass ? undefined : props.attributes.customButtonColor;
223
- if (button_color) {
224
- button_color_attr = " button_color=\"" + button_color + "\"";
225
- }
226
-
227
- var formStyles = {
228
- backgroundColor: rowClass ? undefined : props.attributes.customRowColor,
229
- color: textClass ? undefined : props.attributes.customTextColor,
230
- padding: props.attributes.padding,
231
- textAlign: props.attributes.alignment
232
- };
233
-
234
- return (
235
- el('div', {style: formStyles},
236
- el( RichText.Content, {
237
- tagName: 'p',
238
- value: props.attributes.content
239
- }),
240
- el(
241
- "div",
242
- {},
243
- "[newsletter_form" + formtype_attr + lists_attr + button_color_attr + "]"
244
- )));
245
- }
246
- });
247
-
248
- })(
249
- window.wp.blocks,
250
- window.wp.blockEditor,
251
- window.wp.element,
252
- window.wp.components,
253
- );
1
+ (function (blocks, editor, element, components) {
2
+
3
+
4
+ const el = element.createElement;
5
+ const {registerBlockType} = blocks;
6
+ const { RichText, InspectorControls, withColors, PanelColorSettings, getColorClassName, AlignmentToolbar, BlockControls } = editor;
7
+ const { Fragment } = element;
8
+ const { TextControl, RadioControl, Panel, PanelBody, PanelRow, SelectControl, RangeControl } = components;
9
+ const colorSamples = [
10
+ {
11
+ name: 'GREEN SEA',
12
+ slug: 'GREENSEA',
13
+ color: '#16A085'
14
+ },
15
+ {
16
+ name: 'NEPHRITIS',
17
+ slug: 'NEPHRITIS',
18
+ color: '#27AE60'
19
+ },
20
+ {
21
+ name: 'BELIZE HOLE',
22
+ slug: 'BELIZEHOLE',
23
+ color: '#2980B9'
24
+ },
25
+ {
26
+ name: 'WISTERIA',
27
+ slug: 'WISTERIA',
28
+ color: '#8E44AD'
29
+ },
30
+ {
31
+ name: 'MIDNIGHT BLUE',
32
+ slug: 'MIDNIGHTBLUE',
33
+ color: '#2C3E50'
34
+ },
35
+ {
36
+ name: 'ORANGE',
37
+ slug: 'ORANGE',
38
+ color: '#F39C12'
39
+ },
40
+ {
41
+ name: 'ALIZARIN',
42
+ slug: 'ALIZARIN',
43
+ color: '#E74C3C'
44
+ },
45
+ {
46
+ name: 'WHITE',
47
+ slug: 'WHITE',
48
+ color: '#FFFFFF'
49
+ },
50
+ {
51
+ name: 'CLOUDS',
52
+ slug: 'CLOUDS',
53
+ color: '#ECF0F1'
54
+ },
55
+ {
56
+ name: 'ASBESTOS',
57
+ slug: 'ASBESTOS',
58
+ color: '#7F8C8D'
59
+ }
60
+ ];
61
+
62
+ registerBlockType('tnp/minimal', {
63
+ title: 'Newsletter subscription form',
64
+ icon: 'email',
65
+ category: 'common',
66
+ keywords: ['newsletter', 'subscription', 'form'],
67
+ attributes: {
68
+ formtype: {type: 'string', default: 'minimal'},
69
+ content: { type: 'array', source: 'children', selector: 'p', default: 'Subscribe to our newsletter!'},
70
+ list_ids: { type: 'string' },
71
+ rowColor: { type: 'string'},
72
+ customRowColor: { type: 'string'},
73
+ textColor: { type: 'string'},
74
+ customTextColor: { type: 'string'},
75
+ buttonColor: { type: 'string'},
76
+ customButtonColor: { type: 'string'},
77
+ padding: {type: 'integer', default: 20},
78
+ alignment: { type: 'string'}
79
+ },
80
+
81
+ edit: withColors('rowColor', 'textColor', 'buttonColor')(function (props) {
82
+
83
+ function onChangeContent( newContent ) {
84
+ props.setAttributes( { content: newContent } );
85
+ }
86
+
87
+ function onChangeAlignment( newAlignment ) {
88
+ props.setAttributes( { alignment: newAlignment } );
89
+ }
90
+
91
+ return el( Fragment, {},
92
+ el( InspectorControls, {},
93
+
94
+ // 1st Panel - Form Settings
95
+ el( PanelBody, { title: 'Form Settings', initialOpen: true },
96
+
97
+ /* Form type */
98
+ el( RadioControl,
99
+ {
100
+ label: 'Form type',
101
+ options : [
102
+ { label: 'Minimal', value: 'minimal' },
103
+ { label: 'Full', value: 'full' },
104
+ ],
105
+ onChange: ( value ) => {
106
+ props.setAttributes( { formtype: value } );
107
+ },
108
+ selected: props.attributes.formtype
109
+ }
110
+ ),
111
+
112
+ /* Lists field */
113
+ el( PanelRow, {},
114
+ el( TextControl,
115
+ {
116
+ label: 'Lists IDs (comma separated)',
117
+ onChange: ( value ) => {
118
+ props.setAttributes( { list_ids: value } );
119
+ },
120
+ value: props.attributes.list_ids
121
+ }
122
+ )
123
+ )
124
+ ),
125
+
126
+ /* Style */
127
+ el( PanelColorSettings, {
128
+ title: 'Style',
129
+ colorSettings: [
130
+ {
131
+ colors: colorSamples, // here you can pass custom colors
132
+ value: props.rowColor.color,
133
+ label: 'Background color',
134
+ onChange: props.setRowColor,
135
+ },
136
+ {
137
+ colors: colorSamples, // here you can pass custom colors
138
+ value: props.textColor.color,
139
+ label: 'Text color',
140
+ onChange: props.setTextColor,
141
+ },
142
+ {
143
+ colors: colorSamples, // here you can pass custom colors
144
+ value: props.buttonColor.color,
145
+ label: 'Button color',
146
+ onChange: props.setButtonColor,
147
+ }
148
+ ]
149
+ }),
150
+
151
+ el( RangeControl,
152
+ {
153
+ label: 'Padding',
154
+ min: 0,
155
+ max: 100,
156
+ onChange: ( value ) => {
157
+ props.setAttributes( { padding: value } );
158
+ },
159
+ value: props.attributes.padding
160
+ }
161
+ )
162
+
163
+ ),
164
+
165
+ el(
166
+ "div",
167
+ {style: {backgroundColor: props.rowColor.color, color: props.textColor.color, padding: props.attributes.padding, textAlign: props.attributes.alignment}},
168
+ el(
169
+ BlockControls,
170
+ { key: 'controls' },
171
+ el(
172
+ AlignmentToolbar,
173
+ {
174
+ value: props.attributes.alignment,
175
+ onChange: onChangeAlignment
176
+ }
177
+ )
178
+ ),
179
+ el(RichText,
180
+ {
181
+ tagName: 'p',
182
+ format: 'string',
183
+ onChange: onChangeContent,
184
+ value: props.attributes.content,
185
+ // formattingControls: [ 'bold' ]
186
+ }),
187
+ el('div',
188
+ {style: {backgroundColor: 'lightGrey', margin: '20px', padding: '5px',
189
+ fontFamily: '-apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen-Sans, Ubuntu, Cantarell, Helvetica Neue, sans-serif'}},
190
+ el('svg',
191
+ {
192
+ width: 20,
193
+ height: 20
194
+ },
195
+ wp.element.createElement( 'path',
196
+ {
197
+ d: "M6 14H4V6h2V4H2v12h4M7.1 17h2.1l3.7-14h-2.1M14 4v2h2v8h-2v2h4V4"
198
+ }
199
+ )
200
+ ),
201
+ ' Newsletter Form'
202
+ ),
203
+ ))
204
+ }),
205
+ save: function (props) {
206
+
207
+ var rowClass = getColorClassName( 'row-color', props.attributes.rowColor );
208
+ var textClass = getColorClassName( 'text-color', props.attributes.textColor );
209
+ var buttonClass = getColorClassName( 'button-color', props.attributes.buttonColor );
210
+
211
+ formtype_attr = "";
212
+ if (props.attributes.formtype != "full") {
213
+ formtype_attr = " type=\"minimal\"";
214
+ }
215
+
216
+ lists_attr = "";
217
+ if (props.attributes.list_ids) {
218
+ lists_attr = " lists=\"" + props.attributes.list_ids + "\"";
219
+ }
220
+
221
+ button_color_attr = "";
222
+ button_color = buttonClass ? undefined : props.attributes.customButtonColor;
223
+ if (button_color) {
224
+ button_color_attr = " button_color=\"" + button_color + "\"";
225
+ }
226
+
227
+ var formStyles = {
228
+ backgroundColor: rowClass ? undefined : props.attributes.customRowColor,
229
+ color: textClass ? undefined : props.attributes.customTextColor,
230
+ padding: props.attributes.padding,
231
+ textAlign: props.attributes.alignment
232
+ };
233
+
234
+ return (
235
+ el('div', {style: formStyles},
236
+ el( RichText.Content, {
237
+ tagName: 'p',
238
+ value: props.attributes.content
239
+ }),
240
+ el(
241
+ "div",
242
+ {},
243
+ "[newsletter_form" + formtype_attr + lists_attr + button_color_attr + "]"
244
+ )));
245
+ }
246
+ });
247
+
248
+ })(
249
+ window.wp.blocks,
250
+ window.wp.blockEditor,
251
+ window.wp.element,
252
+ window.wp.components,
253
+ );
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
@@ -1,1420 +1,1422 @@
1
- <?php
2
-
3
- /*
4
- Plugin Name: Newsletter
5
- Plugin URI: https://www.thenewsletterplugin.com/plugins/newsletter
6
- Description: Newsletter is a cool plugin to create your own subscriber list, to send newsletters, to build your business. <strong>Before update give a look to <a href="https://www.thenewsletterplugin.com/category/release">this page</a> to know what's changed.</strong>
7
- Version: 7.4.5
8
- Author: Stefano Lissa & The Newsletter Team
9
- Author URI: https://www.thenewsletterplugin.com
10
- Disclaimer: Use at your own risk. No warranty expressed or implied is provided.
11
- Text Domain: newsletter
12
- License: GPLv2 or later
13
- Requires at least: 4.6
14
- Requires PHP: 5.6
15
-
16
- Copyright 2009-2022 The Newsletter Team (email: info@thenewsletterplugin.com, web: https://www.thenewsletterplugin.com)
17
-
18
- Newsletter is free software: you can redistribute it and/or modify
19
- it under the terms of the GNU General Public License as published by
20
- the Free Software Foundation, either version 2 of the License, or
21
- any later version.
22
-
23
- Newsletter is distributed in the hope that it will be useful,
24
- but WITHOUT ANY WARRANTY; without even the implied warranty of
25
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26
- GNU General Public License for more details.
27
-
28
- You should have received a copy of the GNU General Public License
29
- along with Newsletter. If not, see https://www.gnu.org/licenses/gpl-2.0.html.
30
-
31
- */
32
-
33
- if (version_compare(phpversion(), '5.6', '<')) {
34
- add_action('admin_notices', function () {
35
- echo '<div class="notice notice-error"><p>PHP version 5.6 or greater is required for Newsletter. Ask your provider to upgrade. <a href="https://www.php.net/supported-versions.php" target="_blank">Read more on PHP versions</a></p></div>';
36
- });
37
- return;
38
- }
39
-
40
- define('NEWSLETTER_VERSION', '7.4.5');
41
-
42
- global $newsletter, $wpdb;
43
-
44
- // For acceptance tests, DO NOT CHANGE
45
- if (!defined('NEWSLETTER_DEBUG'))
46
- define('NEWSLETTER_DEBUG', false);
47
-
48
- if (!defined('NEWSLETTER_EXTENSION_UPDATE'))
49
- define('NEWSLETTER_EXTENSION_UPDATE', true);
50
-
51
- if (!defined('NEWSLETTER_EMAILS_TABLE'))
52
- define('NEWSLETTER_EMAILS_TABLE', $wpdb->prefix . 'newsletter_emails');
53
-
54
- if (!defined('NEWSLETTER_USERS_TABLE'))
55
- define('NEWSLETTER_USERS_TABLE', $wpdb->prefix . 'newsletter');
56
-
57
- if (!defined('NEWSLETTER_STATS_TABLE'))
58
- define('NEWSLETTER_STATS_TABLE', $wpdb->prefix . 'newsletter_stats');
59
-
60
- if (!defined('NEWSLETTER_SENT_TABLE'))
61
- define('NEWSLETTER_SENT_TABLE', $wpdb->prefix . 'newsletter_sent');
62
-
63
- define('NEWSLETTER_SLUG', 'newsletter');
64
-
65
- define('NEWSLETTER_DIR', __DIR__);
66
- define('NEWSLETTER_INCLUDES_DIR', __DIR__ . '/includes');
67
-
68
- // Deperacted since plugin can change the plugins_url()
69
- define('NEWSLETTER_URL', WP_PLUGIN_URL . '/newsletter');
70
-
71
- if (!defined('NEWSLETTER_LIST_MAX'))
72
- define('NEWSLETTER_LIST_MAX', 40);
73
-
74
- if (!defined('NEWSLETTER_PROFILE_MAX'))
75
- define('NEWSLETTER_PROFILE_MAX', 20);
76
-
77
- if (!defined('NEWSLETTER_FORMS_MAX'))
78
- define('NEWSLETTER_FORMS_MAX', 10);
79
-
80
- if (!defined('NEWSLETTER_HEADER'))
81
- define('NEWSLETTER_HEADER', true);
82
-
83
- require_once NEWSLETTER_INCLUDES_DIR . '/module.php';
84
- require_once NEWSLETTER_INCLUDES_DIR . '/TNP.php';
85
- require_once NEWSLETTER_INCLUDES_DIR . '/cron.php';
86
-
87
- class Newsletter extends NewsletterModule {
88
-
89
- // Limits to respect to avoid memory, time or provider limits
90
- var $time_start;
91
- var $time_limit = 0;
92
- var $max_emails = null;
93
-
94
- var $mailer = null;
95
-
96
- var $action = '';
97
-
98
- /** @var Newsletter */
99
- static $instance;
100
-
101
- const STATUS_NOT_CONFIRMED = 'S';
102
- const STATUS_CONFIRMED = 'C';
103
-
104
- /**
105
- * @return Newsletter
106
- */
107
- static function instance() {
108
- if (self::$instance == null) {
109
- self::$instance = new Newsletter();
110
- }
111
- return self::$instance;
112
- }
113
-
114
- function __construct() {
115
-
116
- // Grab it before a plugin decides to remove it.
117
- if (isset($_GET['na'])) {
118
- $this->action = $_GET['na'];
119
- }
120
- if (isset($_POST['na'])) {
121
- $this->action = $_POST['na'];
122
- }
123
-
124
- $this->time_start = time();
125
-
126
- parent::__construct('main', '1.6.7', null, ['info', 'smtp']);
127
-
128
- add_action('plugins_loaded', [$this, 'hook_plugins_loaded']);
129
- add_action('init', [$this, 'hook_init'], 1);
130
- add_action('wp_loaded', [$this, 'hook_wp_loaded'], 1);
131
-
132
- add_action('newsletter', [$this, 'hook_newsletter'], 1);
133
-
134
- register_activation_hook(__FILE__, [$this, 'hook_activate']);
135
- register_deactivation_hook(__FILE__, [$this, 'hook_deactivate']);
136
-
137
- add_action('admin_init', [$this, 'hook_admin_init']);
138
-
139
- if (is_admin()) {
140
- add_action('admin_head', [$this, 'hook_admin_head']);
141
-
142
- // Protection against strange schedule removal on some installations
143
- if (!wp_next_scheduled('newsletter') && (!defined('WP_INSTALLING') || !WP_INSTALLING)) {
144
- wp_schedule_event(time() + 30, 'newsletter', 'newsletter');
145
- }
146
-
147
- add_action('admin_menu', [$this, 'add_extensions_menu'], 90);
148
-
149
- add_filter('display_post_states', [$this, 'add_notice_to_chosen_profile_page_hook'], 10, 2);
150
-
151
- if ($this->is_admin_page()) {
152
- // Remove the emoji replacer to save to database the original emoji characters (see even woocommerce for the same problem)
153
- remove_action('admin_print_scripts', 'print_emoji_detection_script');
154
- add_action('admin_enqueue_scripts', [$this, 'hook_admin_enqueue_scripts']);
155
- }
156
-
157
- }
158
- }
159
-
160
- function hook_plugins_loaded() {
161
- // Used to load dependant modules
162
- do_action('newsletter_loaded', NEWSLETTER_VERSION);
163
-
164
- if (function_exists('load_plugin_textdomain')) {
165
- load_plugin_textdomain('newsletter', false, plugin_basename(__DIR__) . '/languages');
166
- }
167
- }
168
-
169
- function hook_init() {
170
- global $wpdb;
171
-
172
- if (isset($this->options['debug']) && $this->options['debug'] == 1) {
173
- ini_set('log_errors', 1);
174
- ini_set('error_log', WP_CONTENT_DIR . '/logs/newsletter/php-' . date('Y-m') . '-' . get_option('newsletter_logger_secret') . '.txt');
175
- }
176
-
177
- add_shortcode('newsletter_replace', [$this, 'shortcode_newsletter_replace']);
178
-
179
- add_filter('site_transient_update_plugins', [$this, 'hook_site_transient_update_plugins']);
180
-
181
- if (is_admin()) {
182
- if (!class_exists('NewsletterExtensions')) {
183
-
184
- add_filter('plugin_row_meta', function ($plugin_meta, $plugin_file) {
185
-
186
- static $slugs = array();
187
- if (empty($slugs)) {
188
- $addons = $this->getTnpExtensions();
189
- if ($addons) {
190
- foreach ($addons as $addon) {
191
- $slugs[] = $addon->wp_slug;
192
- }
193
- }
194
- }
195
- if (array_search($plugin_file, $slugs) !== false) {
196
-
197
- $plugin_meta[] = '<a href="admin.php?page=newsletter_main_extensions" style="font-weight: bold">Newsletter Addons Manager required</a>';
198
- }
199
- return $plugin_meta;
200
- }, 10, 2);
201
- }
202
-
203
- add_action('in_admin_header', array($this, 'hook_in_admin_header'), 1000);
204
-
205
- if ($this->is_admin_page()) {
206
-
207
- $dismissed = get_option('newsletter_dismissed', []);
208
-
209
- if (isset($_GET['dismiss'])) {
210
- $dismissed[$_GET['dismiss']] = 1;
211
- update_option('newsletter_dismissed', $dismissed);
212
- wp_redirect($_SERVER['HTTP_REFERER']);
213
- exit();
214
- }
215
- }
216
- } else {
217
- add_action('wp_enqueue_scripts', [$this, 'hook_wp_enqueue_scripts']);
218
- }
219
-
220
- do_action('newsletter_init');
221
- }
222
-
223
- function hook_wp_loaded() {
224
- if (empty($this->action)) {
225
- return;
226
- }
227
-
228
- if ($this->action == 'test') {
229
- // This response is tested, do not change it!
230
- echo 'ok';
231
- die();
232
- }
233
-
234
- if ($this->action === 'nul') {
235
- $this->dienow('This link is not active on newsletter preview', 'You can send a test message to test subscriber to have the real working link.');
236
- }
237
-
238
- $user = $this->get_user_from_request();
239
- $email = $this->get_email_from_request();
240
- do_action('newsletter_action', $this->action, $user, $email);
241
- }
242
-
243
- function hook_activate() {
244
- // Ok, why? When the plugin is not active WordPress may remove the scheduled "newsletter" action because
245
- // the every-five-minutes schedule named "newsletter" is not present.
246
- // Since the activation does not forces an upgrade, that schedule must be reactivated here. It is activated on
247
- // the upgrade method as well for the user which upgrade the plugin without deactivte it (many).
248
- if (!wp_next_scheduled('newsletter')) {
249
- wp_schedule_event(time() + 30, 'newsletter', 'newsletter');
250
- }
251
-
252
- $install_time = get_option('newsletter_install_time');
253
- if (!$install_time) {
254
- update_option('newsletter_install_time', time(), false);
255
- }
256
-
257
- touch(NEWSLETTER_LOG_DIR . '/index.html');
258
-
259
- Newsletter::instance()->upgrade();
260
- NewsletterUsers::instance()->upgrade();
261
- NewsletterEmails::instance()->upgrade();
262
- NewsletterSubscription::instance()->upgrade();
263
- NewsletterStatistics::instance()->upgrade();
264
- NewsletterProfile::instance()->upgrade();
265
- }
266
-
267
- function first_install() {
268
- parent::first_install();
269
- update_option('newsletter_show_welcome', '1', false);
270
- }
271
-
272
- function upgrade() {
273
- global $wpdb, $charset_collate;
274
-
275
- require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
276
-
277
- parent::upgrade();
278
-
279
- $sql = "CREATE TABLE `" . $wpdb->prefix . "newsletter_emails` (
280
- `id` int(11) NOT NULL AUTO_INCREMENT,
281
- `language` varchar(10) NOT NULL DEFAULT '',
282
- `subject` varchar(255) NOT NULL DEFAULT '',
283
- `message` longtext,
284
- `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
285
- `status` enum('new','sending','sent','paused','error') NOT NULL DEFAULT 'new',
286
- `total` int(11) NOT NULL DEFAULT '0',
287
- `last_id` int(11) NOT NULL DEFAULT '0',
288
- `sent` int(11) NOT NULL DEFAULT '0',
289
- `track` int(11) NOT NULL DEFAULT '1',
290
- `list` int(11) NOT NULL DEFAULT '0',
291
- `type` varchar(50) NOT NULL DEFAULT '',
292
- `query` longtext,
293
- `editor` tinyint(4) NOT NULL DEFAULT '0',
294
- `sex` varchar(20) NOT NULL DEFAULT '',
295
- `theme` varchar(50) NOT NULL DEFAULT '',
296
- `message_text` longtext,
297
- `preferences` longtext,
298
- `send_on` int(11) NOT NULL DEFAULT '0',
299
- `token` varchar(10) NOT NULL DEFAULT '',
300
- `options` longtext,
301
- `private` tinyint(1) NOT NULL DEFAULT '0',
302
- `click_count` int(10) unsigned NOT NULL DEFAULT '0',
303
- `version` varchar(10) NOT NULL DEFAULT '',
304
- `open_count` int(10) unsigned NOT NULL DEFAULT '0',
305
- `unsub_count` int(10) unsigned NOT NULL DEFAULT '0',
306
- `error_count` int(10) unsigned NOT NULL DEFAULT '0',
307
- `stats_time` int(10) unsigned NOT NULL DEFAULT '0',
308
- `updated` int(10) unsigned NOT NULL DEFAULT '0',
309
- PRIMARY KEY (`id`)
310
- ) $charset_collate;";
311
-
312
- dbDelta($sql);
313
-
314
- // WP does not manage composite primary key when it tries to upgrade a table...
315
- $suppress_errors = $wpdb->suppress_errors(true);
316
-
317
- dbDelta("CREATE TABLE " . $wpdb->prefix . "newsletter_sent (
318
- email_id int(10) unsigned NOT NULL DEFAULT '0',
319
- user_id int(10) unsigned NOT NULL DEFAULT '0',
320
- status tinyint(1) unsigned NOT NULL DEFAULT '0',
321
- open tinyint(1) unsigned NOT NULL DEFAULT '0',
322
- time int(10) unsigned NOT NULL DEFAULT '0',
323
- error varchar(255) NOT NULL DEFAULT '',
324
- ip varchar(100) NOT NULL DEFAULT '',
325
- PRIMARY KEY (email_id,user_id),
326
- KEY user_id (user_id),
327
- KEY email_id (email_id)
328
- ) $charset_collate;");
329
- $wpdb->suppress_errors($suppress_errors);
330
-
331
- // Some setting check to avoid the common support request for mis-configurations
332
- $options = $this->get_options();
333
-
334
- if (empty($options['scheduler_max']) || !is_numeric($options['scheduler_max'])) {
335
- $options['scheduler_max'] = 100;
336
- $this->save_options($options);
337
- }
338
-
339
- wp_clear_scheduled_hook('newsletter');
340
- wp_schedule_event(time() + 30, 'newsletter', 'newsletter');
341
-
342
- if (!empty($this->options['editor'])) {
343
- if (empty($this->options['roles'])) {
344
- $this->options['roles'] = array('editor');
345
- unset($this->options['editor']);
346
- }
347
- $this->save_options($this->options);
348
- }
349
-
350
- // Clear the addons and license caches
351
- delete_transient('newsletter_license_data');
352
- $this->clear_extensions_cache();
353
-
354
- touch(NEWSLETTER_LOG_DIR . '/index.html');
355
-
356
- return true;
357
- }
358
-
359
- function is_allowed() {
360
- if (current_user_can('administrator')) {
361
- return true;
362
- }
363
- if (!empty($this->options['roles'])) {
364
- foreach ($this->options['roles'] as $role) {
365
- if (current_user_can($role)) {
366
- return true;
367
- }
368
- }
369
- }
370
- return false;
371
- }
372
-
373
- function admin_menu() {
374
- if (!$this->is_allowed()) {
375
- return;
376
- }
377
-
378
- add_menu_page('Newsletter', 'Newsletter', 'exist', 'newsletter_main_index', '', plugins_url('newsletter') . '/admin/images/menu-icon.png', '30.333');
379
-
380
- $this->add_menu_page('index', __('Dashboard', 'newsletter'));
381
- $this->add_admin_page('info', __('Company info', 'newsletter'));
382
-
383
- if (current_user_can('administrator')) {
384
- $this->add_menu_page('welcome', __('Welcome', 'newsletter'));
385
- $this->add_menu_page('main', __('Settings', 'newsletter'));
386
-
387
- // Pages not on menu
388
- $this->add_admin_page('smtp', 'SMTP');
389
- $this->add_admin_page('diagnostic', __('Diagnostic', 'newsletter'));
390
- $this->add_admin_page('test', __('Test', 'newsletter'));
391
- }
392
- }
393
-
394
- function add_extensions_menu() {
395
- if (!class_exists('NewsletterExtensions')) {
396
- $this->add_menu_page('extensions', '<span style="color:#27AE60; font-weight: bold;">' . __('Addons', 'newsletter') . '</span>');
397
- }
398
- }
399
-
400
- function hook_in_admin_header() {
401
- if (!$this->is_admin_page()) {
402
- add_action('admin_notices', array($this, 'hook_admin_notices'));
403
- return;
404
- }
405
- remove_all_actions('admin_notices');
406
- remove_all_actions('all_admin_notices');
407
- add_action('admin_notices', array($this, 'hook_admin_notices'));
408
- }
409
-
410
- function hook_admin_notices() {
411
- // Check of Newsletter dedicated page
412
- if (!empty($this->options['page'])) {
413
- if (get_post_status($this->options['page']) !== 'publish') {
414
- echo '<div class="notice notice-error"><p>The Newsletter dedicated page is not published. <a href="', site_url('/wp-admin/post.php') . '?post=', $this->options['page'], '&action=edit"><strong>Edit the page</strong></a> or <a href="admin.php?page=newsletter_main_main"><strong>review the main settings</strong></a>.</p></div>';
415
- }
416
- }
417
-
418
- if (isset($this->options['debug']) && $this->options['debug'] == 1) {
419
- echo '<div class="notice notice-warning"><p>The Newsletter plugin is in <strong>debug mode</strong>. When done change it on Newsletter <a href="admin.php?page=newsletter_main_main"><strong>main settings</strong></a>. Do not keep the debug mode active on production sites.</p></div>';
420
- }
421
- }
422
-
423
- function hook_wp_enqueue_scripts() {
424
- if (empty($this->options['css_disabled']) && apply_filters('newsletter_enqueue_style', true)) {
425
- wp_enqueue_style('newsletter', plugins_url('newsletter') . '/style.css', [], NEWSLETTER_VERSION);
426
- if (!empty($this->options['css'])) {
427
- wp_add_inline_style('newsletter', $this->options['css']);
428
- }
429
- } else {
430
- if (!empty($this->options['css'])) {
431
- add_action('wp_head', function () {
432
- echo '<style>', $this->options['css'], '</style>';
433
- });
434
- }
435
- }
436
- }
437
-
438
- function hook_admin_enqueue_scripts() {
439
-
440
- $newsletter_url = plugins_url('newsletter');
441
- wp_enqueue_script('jquery-ui-tabs');
442
- wp_enqueue_script('jquery-ui-tooltip');
443
- wp_enqueue_script('jquery-ui-draggable');
444
- wp_enqueue_media();
445
-
446
- wp_enqueue_script('tnp-admin', $newsletter_url . '/admin/admin.js', ['jquery'], NEWSLETTER_VERSION);
447
-
448
- wp_enqueue_style('tnp-select2', $newsletter_url . '/vendor/select2/css/select2.min.css', [], NEWSLETTER_VERSION);
449
- wp_enqueue_script('tnp-select2', $newsletter_url . '/vendor/select2/js/select2.min.js', ['jquery'], NEWSLETTER_VERSION);
450
-
451
- wp_enqueue_style('tnp-modal', $newsletter_url . '/admin/modal.css', [], NEWSLETTER_VERSION);
452
- wp_enqueue_script('tnp-modal', $newsletter_url . '/admin/modal.js', ['jquery'], NEWSLETTER_VERSION, true);
453
-
454
- wp_enqueue_style('tnp-toast', $newsletter_url . '/admin/toast.css', [], NEWSLETTER_VERSION);
455
- wp_enqueue_script('tnp-toast', $newsletter_url . '/admin/toast.js', ['jquery'], NEWSLETTER_VERSION);
456
-
457
- wp_enqueue_style('tnp-admin-font', 'https://use.typekit.net/jlj2wjy.css');
458
- wp_enqueue_style('tnp-admin-fontawesome', $newsletter_url . '/vendor/fa/css/all.min.css', [], NEWSLETTER_VERSION);
459
- wp_enqueue_style('tnp-admin-jquery-ui', $newsletter_url . '/vendor/jquery-ui/jquery-ui.min.css', [], NEWSLETTER_VERSION);
460
- wp_enqueue_style('tnp-admin', $newsletter_url . '/admin/admin.css', [], NEWSLETTER_VERSION);
461
- wp_enqueue_style('tnp-admin-dropdown', $newsletter_url . '/admin/dropdown.css', [], NEWSLETTER_VERSION);
462
- wp_enqueue_style('tnp-admin-tabs', $newsletter_url . '/admin/tabs.css', [], NEWSLETTER_VERSION);
463
- wp_enqueue_style('tnp-admin-controls', $newsletter_url . '/admin/controls.css', [], NEWSLETTER_VERSION);
464
- wp_enqueue_style('tnp-admin-fields', $newsletter_url . '/admin/fields.css', [], NEWSLETTER_VERSION);
465
- wp_enqueue_style('tnp-admin-widgets', $newsletter_url . '/admin/widgets.css', [], NEWSLETTER_VERSION);
466
- wp_enqueue_style('tnp-admin-extensions', $newsletter_url . '/admin/extensions.css', [], NEWSLETTER_VERSION);
467
-
468
- $translations_array = array(
469
- 'save_to_update_counter' => __('Save the newsletter to update the counter!', 'newsletter')
470
- );
471
- wp_localize_script('tnp-admin', 'tnp_translations', $translations_array);
472
-
473
- wp_enqueue_script('tnp-jquery-vmap', $newsletter_url . '/vendor/jqvmap/jquery.vmap.min.js', ['jquery'], NEWSLETTER_VERSION);
474
- wp_enqueue_script('tnp-jquery-vmap-world', $newsletter_url . '/vendor/jqvmap/jquery.vmap.world.js', ['tnp-jquery-vmap'], NEWSLETTER_VERSION);
475
- wp_enqueue_style('tnp-jquery-vmap', $newsletter_url . '/vendor/jqvmap/jqvmap.min.css', [], NEWSLETTER_VERSION);
476
-
477
- wp_register_script('tnp-chart', $newsletter_url . '/vendor/chartjs/Chart.min.js', ['jquery'], NEWSLETTER_VERSION);
478
-
479
- wp_enqueue_script('tnp-color-picker', $newsletter_url . '/vendor/spectrum/spectrum.min.js', ['jquery']);
480
- wp_enqueue_style('tnp-color-picker', $newsletter_url . '/vendor/spectrum/spectrum.min.css', [], NEWSLETTER_VERSION);
481
- }
482
-
483
- function shortcode_newsletter_replace($attrs, $content) {
484
- $content = do_shortcode($content);
485
- $content = $this->replace($content, $this->get_user_from_request(), $this->get_email_from_request());
486
- return $content;
487
- }
488
-
489
- function is_admin_page() {
490
- if (!isset($_GET['page'])) {
491
- return false;
492
- }
493
- $page = $_GET['page'];
494
- return strpos($page, 'newsletter_') === 0;
495
- }
496
-
497
- function hook_admin_init() {
498
- // Verificare il contesto
499
- if (isset($_GET['page']) && $_GET['page'] === 'newsletter_main_welcome')
500
- return;
501
- if (get_option('newsletter_show_welcome')) {
502
- delete_option('newsletter_show_welcome');
503
- wp_redirect(admin_url('admin.php?page=newsletter_main_welcome'));
504
- }
505
- }
506
-
507
- function hook_admin_head() {
508
- // Small global rule for sidebar menu entries
509
- echo '<style>';
510
- echo '.tnp-side-menu { color: #E67E22!important; }';
511
- echo '</style>';
512
- }
513
-
514
- function relink($text, $email_id, $user_id, $email_token = '') {
515
- return NewsletterStatistics::instance()->relink($text, $email_id, $user_id, $email_token);
516
- }
517
-
518
- /**
519
- * Runs every 5 minutes and look for emails that need to be processed.
520
- */
521
- function hook_newsletter() {
522
-
523
- $this->logger->debug(__METHOD__ . '> Start');
524
-
525
- if (!$this->check_transient('engine', NEWSLETTER_CRON_INTERVAL)) {
526
- $this->logger->debug(__METHOD__ . '> Engine already active, exit');
527
- return;
528
- }
529
-
530
- $emails = $this->get_results("select * from " . NEWSLETTER_EMAILS_TABLE . " where status='sending' and send_on<" . time() . " order by id asc");
531
- $this->logger->debug(__METHOD__ . '> Emails found in sending status: ' . count($emails));
532
-
533
- foreach ($emails as $email) {
534
- $this->logger->debug(__METHOD__ . '> Start newsletter ' . $email->id);
535
- $email->options = maybe_unserialize($email->options);
536
- $r = $this->send($email);
537
- $this->logger->debug(__METHOD__ . '> End newsletter ' . $email->id);
538
- if (!$r) {
539
- $this->logger->debug(__METHOD__ . '> Engine returned false, there is no more capacity');
540
- break;
541
- }
542
- }
543
- // Remove the semaphore so the delivery engine can be activated again
544
- $this->delete_transient('engine');
545
-
546
- $this->logger->debug(__METHOD__ . '> End');
547
- }
548
-
549
- function get_send_speed($email = null) {
550
- $this->logger->debug(__METHOD__ . '> Computing delivery speed');
551
- $mailer = $this->get_mailer();
552
- $speed = (int) $mailer->get_speed();
553
- if (!$speed) {
554
- $this->logger->debug(__METHOD__ . '> Speed not set by mailer, use the default');
555
- $speed = (int) $this->options['scheduler_max'];
556
- } else {
557
- $this->logger->debug(__METHOD__ . '> Speed set by mailer');
558
- }
559
-
560
- //$speed = (int) apply_filters('newsletter_send_speed', $speed, $email);
561
- // Fail safe setting
562
- $runs_per_hour = $this->get_runs_per_hour();
563
- if (!$speed || $speed < $runs_per_hour) {
564
- return $runs_per_hour;
565
- }
566
-
567
- $this->logger->debug(__METHOD__ . '> Speed: ' . $speed);
568
- return $speed;
569
- }
570
-
571
- function get_send_delay() {
572
- return 0;
573
- }
574
-
575
- function skip_this_run($email = null) {
576
- return (boolean) apply_filters('newsletter_send_skip', false, $email);
577
- }
578
-
579
- function get_runs_per_hour() {
580
- return (int) (3600 / NEWSLETTER_CRON_INTERVAL);
581
- }
582
-
583
- function get_emails_per_run() {
584
- $speed = $this->get_send_speed();
585
- $max = (int)($speed / $this->get_runs_per_hour());
586
-
587
- return $max;
588
- }
589
-
590
- function get_max_emails($email) {
591
- // Obsolete, here from Speed Control Addon
592
- $max = (int) apply_filters('newsletter_send_max_emails', $this->max_emails, $email);
593
-
594
- return min($max, $this->max_emails);
595
- }
596
-
597
- function fix_email($email) {
598
- if (empty($email->query)) {
599
- $email->query = "select * from " . NEWSLETTER_USERS_TABLE . " where status='C'";
600
- }
601
- if (empty($email->id)) {
602
- $email->id = 0;
603
- }
604
- }
605
-
606
- function send_setup() {
607
- $this->logger->debug(__METHOD__ . '> Setup delivery engine');
608
- if (is_null($this->max_emails)) {
609
- $this->max_emails = $this->get_emails_per_run();
610
- $this->logger->debug(__METHOD__ . '> Max emails: ' . $this->max_emails);
611
- ignore_user_abort(true);
612
-
613
- @set_time_limit(NEWSLETTER_CRON_INTERVAL + 30);
614
-
615
- $max_time = (int) (@ini_get('max_execution_time') * 0.95);
616
- if ($max_time == 0 || $max_time > NEWSLETTER_CRON_INTERVAL) {
617
- $max_time = (int) (NEWSLETTER_CRON_INTERVAL * 0.95);
618
- }
619
-
620
- $this->time_limit = $this->time_start + $max_time;
621
-
622
- $this->logger->debug(__METHOD__ . '> Max time set to ' . $max_time);
623
- } else {
624
- $this->logger->debug(__METHOD__ . '> Already setup');
625
- }
626
- }
627
-
628
- function time_exceeded() {
629
- if ($this->time_limit && time() > $this->time_limit) {
630
- $this->logger->info(__METHOD__ . '> Max execution time limit reached');
631
- return true;
632
- }
633
- }
634
-
635
- /**
636
- * Sends an email to targeted users or to given users. If a list of users is given (usually a list of test users)
637
- * the query inside the email to retrieve users is not used.
638
- *
639
- * @global wpdb $wpdb
640
- * @global type $newsletter_feed
641
- * @param TNP_Email $email
642
- * @param array $users
643
- * @return boolean|WP_Error True if the process completed, false if limits was reached. On false the caller should no continue to call it with other emails.
644
- */
645
- function send($email, $users = null, $test = false) {
646
- global $wpdb;
647
-
648
- if (is_array($email)) {
649
- $email = (object) $email;
650
- }
651
-
652
- $this->logger->info(__METHOD__ . '> Send email ' . $email->id);
653
-
654
- $this->send_setup();
655
-
656
- if ($this->max_emails <= 0) {
657
- $this->logger->info(__METHOD__ . '> No more capacity');
658
- return false;
659
- }
660
-
661
- $this->fix_email($email);
662
-
663
- // This stops the update of last_id and sent fields since
664
- // it's not a scheduled delivery but a test or something else (like an autoresponder)
665
- $supplied_users = $users != null;
666
-
667
- if (!$supplied_users) {
668
-
669
- if ($this->skip_this_run($email)) {
670
- return true;
671
- }
672
-
673
- // Speed change for specific email by Speed Control Addon
674
- $max_emails = $this->get_max_emails($email);
675
- if ($max_emails <= 0) {
676
- return true;
677
- }
678
-
679
- $query = $email->query;
680
- $query .= " and id>" . $email->last_id . " order by id limit " . $max_emails;
681
-
682
- $this->logger->debug(__METHOD__ . '> Query: ' . $query);
683
-
684
- //Retrieve subscribers
685
- $users = $this->get_results($query);
686
-
687
- $this->logger->debug(__METHOD__ . '> Loaded subscribers: ' . count($users));
688
-
689
- // If there was a database error, return error
690
- if ($users === false) {
691
- return new WP_Error('1', 'Unable to query subscribers, check the logs');
692
- }
693
-
694
- if (empty($users)) {
695
- $this->logger->info(__METHOD__ . '> No more users, set as sent');
696
- $wpdb->query("update " . NEWSLETTER_EMAILS_TABLE . " set status='sent', total=sent where id=" . $email->id . " limit 1");
697
- do_action('newsletter_ended_sending_newsletter', $email);
698
- return true;
699
- }
700
- } else {
701
- $this->logger->info(__METHOD__ . '> Subscribers supplied');
702
- }
703
-
704
- $start_time = microtime(true);
705
- $count = 0;
706
- $result = true;
707
-
708
- $mailer = $this->get_mailer();
709
-
710
- $batch_size = $mailer->get_batch_size();
711
-
712
- $this->logger->debug(__METHOD__ . '> Batch size: ' . $batch_size);
713
-
714
- // For batch size == 1 (normal condition) we optimize
715
- if ($batch_size == 1) {
716
-
717
- foreach ($users as $user) {
718
-
719
- $this->logger->debug(__METHOD__ . '> Processing user ID: ' . $user->id);
720
- $user = apply_filters('newsletter_send_user', $user);
721
- $message = $this->build_message($email, $user);
722
-
723
- // Save even test emails since people wants to see some stats even for test emails. Stats are reset upon the real "send" of a newsletter
724
- $this->save_sent_message($message);
725
-
726
- //Se non è un test incremento il contatore delle email spedite. Perchè incremento prima di spedire??
727
- if (!$test) {
728
- $wpdb->query("update " . NEWSLETTER_EMAILS_TABLE . " set sent=sent+1, last_id=" . $user->id . " where id=" . $email->id . " limit 1");
729
- }
730
-
731
- $r = $mailer->send($message);
732
-
733
- if (!empty($message->error)) {
734
- $this->logger->error($message);
735
- $this->save_sent_message($message);
736
- }
737
-
738
- if (is_wp_error($r)) {
739
- $this->logger->error($r);
740
-
741
- // For fatal error, the newsletter status i changed to error (and the delivery stopped)
742
- if (!$test && $r->get_error_code() == NewsletterMailer::ERROR_FATAL) {
743
- $this->set_error_state_of_email($email, $r->get_error_message());
744
- return $r;
745
- }
746
- }
747
-
748
- if (!$supplied_users && !$test && $this->time_exceeded()) {
749
- $result = false;
750
- break;
751
- }
752
- }
753
-
754
- $this->max_emails--;
755
- $count++;
756
- } else {
757
-
758
- $chunks = array_chunk($users, $batch_size);
759
-
760
- foreach ($chunks as $chunk) {
761
-
762
- $messages = [];
763
-
764
- // Peeparing a batch of messages
765
- foreach ($chunk as $user) {
766
- $this->logger->debug(__METHOD__ . '> Processing user ID: ' . $user->id);
767
- $user = apply_filters('newsletter_send_user', $user);
768
- $message = $this->build_message($email, $user);
769
- $this->save_sent_message($message);
770
- $messages[] = $message;
771
-
772
- if (!$test) {
773
- $wpdb->query("update " . NEWSLETTER_EMAILS_TABLE . " set sent=sent+1, last_id=" . $user->id . " where id=" . $email->id . " limit 1");
774
- }
775
- $this->max_emails--;
776
- $count++;
777
- }
778
-
779
- $r = $mailer->send_batch($messages);
780
-
781
- // Updating the status of the sent messages
782
- foreach ($messages as $message) {
783
- if (!empty($message->error)) {
784
- $this->save_sent_message($message);
785
- }
786
- }
787
-
788
- // The batch went in error
789
- if (is_wp_error($r)) {
790
- $this->logger->error($r);
791
-
792
- if (!$test && $r->get_error_code() == NewsletterMailer::ERROR_FATAL) {
793
- $this->set_error_state_of_email($email, $r->get_error_message());
794
- return $r;
795
- }
796
- }
797
-
798
- if (!$supplied_users && !$test && $this->time_exceeded()) {
799
- $result = false;
800
- break;
801
- }
802
- }
803
- }
804
-
805
- $end_time = microtime(true);
806
-
807
- // Stats only for newsletter with enough emails in a batch (we exclude the Autoresponder since it send one email per call)
808
- if (!$test && !$supplied_users && $count > 5) {
809
- $this->update_send_stats($start_time, $end_time, $count, $result);
810
- }
811
-
812
- // Cached general statistics are reset
813
- if (!$test) {
814
- NewsletterStatistics::instance()->reset_stats_time($email->id);
815
- }
816
-
817
- $this->logger->info(__METHOD__ . '> End run for email ' . $email->id);
818
-
819
- return $result;
820
- }
821
-
822
- function update_send_stats($start_time, $end_time, $count, $result) {
823
- $send_calls = get_option('newsletter_diagnostic_send_calls', []);
824
- $send_calls[] = [$start_time, $end_time, $count, $result];
825
-
826
- if (count($send_calls) > 100) {
827
- array_shift($send_calls);
828
- }
829
-
830
- update_option('newsletter_diagnostic_send_calls', $send_calls, false);
831
- }
832
-
833
- /**
834
- * @param TNP_Email $email
835
- */
836
- private function set_error_state_of_email($email, $message = '') {
837
- // Handle only message type at the moment
838
- if ($email->type !== 'message') {
839
- return;
840
- }
841
-
842
- do_action('newsletter_error_on_sending', $email, $message);
843
-
844
- $edited_email = new TNP_Email();
845
- $edited_email->id = $email->id;
846
- $edited_email->status = TNP_Email::STATUS_ERROR;
847
- $edited_email->options = $email->options;
848
- $edited_email->options['error_message'] = $message;
849
-
850
- $this->save_email($edited_email);
851
- }
852
-
853
- /**
854
- *
855
- * @param TNP_Email $email
856
- * @param TNP_User $user
857
- * @return \TNP_Mailer_Message
858
- */
859
- function build_message($email, $user) {
860
-
861
- $message = new TNP_Mailer_Message();
862
-
863
- $message->to = $user->email;
864
-
865
- $message->headers = [];
866
- $message->headers['Precedence'] = 'bulk';
867
- $message->headers['X-Newsletter-Email-Id'] = $email->id;
868
- $message->headers['X-Auto-Response-Suppress'] = 'OOF, AutoReply';
869
- $message->headers = apply_filters('newsletter_message_headers', $message->headers, $email, $user);
870
-
871
- $message->body = preg_replace('/data-json=".*?"/is', '', $email->message);
872
- $message->body = preg_replace('/ +/s', ' ', $message->body);
873
- $message->body = $this->replace($message->body, $user, $email);
874
- if ($this->options['do_shortcodes']) {
875
- $message->body = do_shortcode($message->body);
876
- }
877
- $message->body = apply_filters('newsletter_message_html', $message->body, $email, $user);
878
-
879
- $message->body_text = $this->replace($email->message_text, $user, $email);
880
- $message->body_text = apply_filters('newsletter_message_text', $message->body_text, $email, $user);
881
-
882
- if ($email->track == 1) {
883
- $message->body = $this->relink($message->body, $email->id, $user->id, $email->token);
884
- }
885
-
886
- $message->subject = $this->replace($email->subject, $user);
887
- $message->subject = apply_filters('newsletter_message_subject', $message->subject, $email, $user);
888
-
889
- if (!empty($email->options['sender_email'])) {
890
- $message->from = $email->options['sender_email'];
891
- } else {
892
- $message->from = $this->options['sender_email'];
893
- }
894
-
895
- if (!empty($email->options['sender_name'])) {
896
- $message->from_name = $email->options['sender_name'];
897
- } else {
898
- $message->from_name = $this->options['sender_name'];
899
- }
900
-
901
- $message->email_id = $email->id;
902
- $message->user_id = $user->id;
903
-
904
- return apply_filters('newsletter_message', $message, $email, $user);
905
- }
906
-
907
- /**
908
- *
909
- * @param TNP_Mailer_Message $message
910
- * @param int $status
911
- * @param string $error
912
- */
913
- function save_sent_message($message) {
914
- global $wpdb;
915
-
916
- if (!$message->user_id || !$message->email_id) {
917
- return;
918
- }
919
- $status = empty($message->error) ? 0 : 1;
920
-
921
- $error = mb_substr($message->error, 0, 250);
922
-
923
- $this->query($wpdb->prepare("insert into " . $wpdb->prefix . 'newsletter_sent (user_id, email_id, time, status, error) values (%d, %d, %d, %d, %s) on duplicate key update time=%d, status=%d, error=%s', $message->user_id, $message->email_id, time(), $status, $error, time(), $status, $error));
924
- }
925
-
926
- /**
927
- * @deprecated since version 7.3.0
928
- */
929
- function limits_exceeded() {
930
- return false;
931
- }
932
-
933
- /**
934
- * @deprecated since version 6.0.0
935
- */
936
- function register_mail_method($callable) {
937
- }
938
-
939
- function register_mailer($mailer) {
940
- if ($mailer instanceof NewsletterMailer) {
941
- $this->mailer = $mailer;
942
- }
943
- }
944
-
945
- /**
946
- * Returns the current registered mailer which must be used to send emails.
947
- *
948
- * @return NewsletterMailer
949
- */
950
- function get_mailer() {
951
- if ($this->mailer) {
952
- return $this->mailer;
953
- }
954
-
955
- do_action('newsletter_register_mailer');
956
-
957
- if (!$this->mailer) {
958
- // Compatibility
959
- $smtp = $this->get_options('smtp');
960
- if (!empty($smtp['enabled'])) {
961
- $this->mailer = new NewsletterDefaultSMTPMailer($smtp);
962
- } else {
963
- $this->mailer = new NewsletterDefaultMailer();
964
- }
965
- }
966
- return $this->mailer;
967
- }
968
-
969
- /**
970
- *
971
- * @param TNP_Mailer_Message $message
972
- * @return type
973
- */
974
- function deliver($message) {
975
- $mailer = $this->get_mailer();
976
- if (empty($message->from))
977
- $message->from = $this->options['sender_email'];
978
- if (empty($message->from_name))
979
- $mailer->from_name = $this->options['sender_name'];
980
- return $mailer->send($message);
981
- }
982
-
983
- /**
984
- *
985
- * @param type $to
986
- * @param type $subject
987
- * @param string|array $message If string is considered HTML, is array should contains the keys "html" and "text"
988
- * @param type $headers
989
- * @param type $enqueue
990
- * @param type $from
991
- * @return boolean
992
- */
993
- function mail($to, $subject, $message, $headers = array(), $enqueue = false, $from = false) {
994
-
995
- if (empty($subject)) {
996
- $this->logger->error('mail> Subject empty, skipped');
997
- return true;
998
- }
999
-
1000
- $mailer_message = new TNP_Mailer_Message();
1001
- $mailer_message->to = $to;
1002
- $mailer_message->subject = $subject;
1003
- $mailer_message->from = $this->options['sender_email'];
1004
- $mailer_message->from_name = $this->options['sender_name'];
1005
-
1006
- if (!empty($headers)) {
1007
- $mailer_message->headers = $headers;
1008
- }
1009
- $mailer_message->headers['X-Auto-Response-Suppress'] = 'OOF, AutoReply';
1010
-
1011
- // Message carrige returns and line feeds clean up
1012
- if (!is_array($message)) {
1013
- $mailer_message->body = $this->clean_eol($message);
1014
- } else {
1015
- if (!empty($message['text'])) {
1016
- $mailer_message->body_text = $this->clean_eol($message['text']);
1017
- }
1018
-
1019
- if (!empty($message['html'])) {
1020
- $mailer_message->body = $this->clean_eol($message['html']);
1021
- }
1022
- }
1023
-
1024
- $this->logger->debug($mailer_message);
1025
-
1026
- $mailer = $this->get_mailer();
1027
-
1028
- $r = $mailer->send($mailer_message);
1029
-
1030
- return !is_wp_error($r);
1031
- }
1032
-
1033
- function hook_deactivate() {
1034
- wp_clear_scheduled_hook('newsletter');
1035
- }
1036
-
1037
- function find_file($file1, $file2) {
1038
- if (is_file($file1))
1039
- return $file1;
1040
- return $file2;
1041
- }
1042
-
1043
- function hook_site_transient_update_plugins($value) {
1044
- static $extra_response = array();
1045
-
1046
- //$this->logger->debug('Update plugins transient called');
1047
-
1048
- if (!$value || !is_object($value)) {
1049
- //$this->logger->info('Empty object');
1050
- return $value;
1051
- }
1052
-
1053
- if (!isset($value->response) || !is_array($value->response)) {
1054
- $value->response = array();
1055
- }
1056
-
1057
- // Already computed? Use it! (this filter is called many times in a single request)
1058
- if ($extra_response) {
1059
- //$this->logger->debug('Already updated');
1060
- $value->response = array_merge($value->response, $extra_response);
1061
- return $value;
1062
- }
1063
-
1064
- $extensions = $this->getTnpExtensions();
1065
-
1066
- // Ops...
1067
- if (!$extensions) {
1068
- return $value;
1069
- }
1070
-
1071
- foreach ($extensions as $extension) {
1072
- unset($value->response[$extension->wp_slug]);
1073
- unset($value->no_update[$extension->wp_slug]);
1074
- }
1075
-
1076
- // Someone doesn't want our addons updated, let respect it (this constant should be defined in wp-config.php)
1077
- if (!NEWSLETTER_EXTENSION_UPDATE) {
1078
- //$this->logger->info('Updates disabled');
1079
- return $value;
1080
- }
1081
-
1082
- include_once(ABSPATH . 'wp-admin/includes/plugin.php');
1083
-
1084
- // Ok, that is really bad (should we remove it? is there a minimum WP version?)
1085
- if (!function_exists('get_plugin_data')) {
1086
- //$this->logger->error('No get_plugin_data function available!');
1087
- return $value;
1088
- }
1089
-
1090
- $license_key = $this->get_license_key();
1091
-
1092
- // Here we prepare the update information BUT do not add the link to the package which is privided
1093
- // by our Addons Manager (due to WP policies)
1094
- foreach ($extensions as $extension) {
1095
-
1096
- // Patch for names convention
1097
- $extension->plugin = $extension->wp_slug;
1098
-
1099
- //$this->logger->debug('Processing ' . $extension->plugin);
1100
- //$this->logger->debug($extension);
1101
-
1102
- $plugin_data = false;
1103
- if (file_exists(WP_PLUGIN_DIR . '/' . $extension->plugin)) {
1104
- $plugin_data = get_plugin_data(WP_PLUGIN_DIR . '/' . $extension->plugin, false, false);
1105
- } else if (file_exists(WPMU_PLUGIN_DIR . '/' . $extension->plugin)) {
1106
- $plugin_data = get_plugin_data(WPMU_PLUGIN_DIR . '/' . $extension->plugin, false, false);
1107
- }
1108
-
1109
- if (!$plugin_data) {
1110
- //$this->logger->debug('Seems not installed');
1111
- continue;
1112
- }
1113
-
1114
- $plugin = new stdClass();
1115
- $plugin->id = $extension->id;
1116
- $plugin->slug = $extension->slug;
1117
- $plugin->plugin = $extension->plugin;
1118
- $plugin->new_version = $extension->version;
1119
- $plugin->url = $extension->url;
1120
- if (class_exists('NewsletterExtensions')) {
1121
- // NO filters here!
1122
- $plugin->package = NewsletterExtensions::$instance->get_package($extension->id, $license_key);
1123
- } else {
1124
- $plugin->package = '';
1125
- }
1126
- // [banners] => Array
1127
- // (
1128
- // [2x] => https://ps.w.org/wp-rss-aggregator/assets/banner-1544x500.png?rev=2040548
1129
- // [1x] => https://ps.w.org/wp-rss-aggregator/assets/banner-772x250.png?rev=2040548
1130
- // )
1131
- // [icons] => Array
1132
- // (
1133
- // [2x] => https://ps.w.org/advanced-custom-fields/assets/icon-256x256.png?rev=1082746
1134
- // [1x] => https://ps.w.org/advanced-custom-fields/assets/icon-128x128.png?rev=1082746
1135
- // )
1136
- if (version_compare($extension->version, $plugin_data['Version']) > 0) {
1137
- //$this->logger->debug('There is a new version');
1138
- $extra_response[$extension->plugin] = $plugin;
1139
- } else {
1140
- // Maybe useless...
1141
- //$this->logger->debug('There is NOT a new version');
1142
- $value->no_update[$extension->plugin] = $plugin;
1143
- }
1144
- //$this->logger->debug('Added');
1145
- }
1146
-
1147
- $value->response = array_merge($value->response, $extra_response);
1148
-
1149
- return $value;
1150
- }
1151
-
1152
- /**
1153
- * @deprecated since version 6.1.9
1154
- */
1155
- function get_extension_version($extension_id) {
1156
- return null;
1157
- }
1158
-
1159
- /**
1160
- * @deprecated since version 6.1.9
1161
- */
1162
- function set_extension_update_data($value, $extension) {
1163
- return $value;
1164
- }
1165
-
1166
- /**
1167
- * Retrieve the extensions form the tnp site
1168
- * @return array
1169
- */
1170
- function getTnpExtensions() {
1171
-
1172
- $extensions_json = get_transient('tnp_extensions_json');
1173
-
1174
- if (empty($extensions_json)) {
1175
- $url = "http://www.thenewsletterplugin.com/wp-content/extensions.json?ver=" . NEWSLETTER_VERSION;
1176
- $extensions_response = wp_remote_get($url);
1177
-
1178
- if (is_wp_error($extensions_response)) {
1179
- // Cache anyway for blogs which cannot connect outside
1180
- $extensions_json = '[]';
1181
- set_transient('tnp_extensions_json', $extensions_json, 72 * 60 * 60);
1182
- $this->logger->error($extensions_response);
1183
- } else {
1184
-
1185
- $extensions_json = wp_remote_retrieve_body($extensions_response);
1186
-
1187
- // Not clear cases
1188
- if (empty($extensions_json) || !json_decode($extensions_json)) {
1189
- $this->logger->error('Invalid json from thenewsletterplugin.com: retrying in 72 hours');
1190
- $this->logger->error('JSON: ' . $extensions_json);
1191
- $extensions_json = '[]';
1192
- }
1193
- set_transient('tnp_extensions_json', $extensions_json, 72 * 60 * 60);
1194
- }
1195
- }
1196
-
1197
- $extensions = json_decode($extensions_json);
1198
-
1199
- return $extensions;
1200
- }
1201
-
1202
- function clear_extensions_cache() {
1203
- delete_transient('tnp_extensions_json');
1204
- }
1205
-
1206
- var $panels = array();
1207
-
1208
- function add_panel($key, $panel) {
1209
- if (!isset($this->panels[$key]))
1210
- $this->panels[$key] = array();
1211
- if (!isset($panel['id']))
1212
- $panel['id'] = sanitize_key($panel['label']);
1213
- $this->panels[$key][] = $panel;
1214
- }
1215
-
1216
- function has_license() {
1217
- return !empty($this->options['contract_key']);
1218
- }
1219
-
1220
- function get_sender_name() {
1221
- return $this->options['sender_name'];
1222
- }
1223
-
1224
- function get_sender_email() {
1225
- return $this->options['sender_email'];
1226
- }
1227
-
1228
- /**
1229
- *
1230
- * @return int
1231
- */
1232
- function get_newsletter_page_id() {
1233
- return (int) $this->options['page'];
1234
- }
1235
-
1236
- /**
1237
- *
1238
- * @return WP_Post
1239
- */
1240
- function get_newsletter_page() {
1241
- $page_id = $this->get_newsletter_page_id();
1242
- if (!$page_id) return false;
1243
- return get_post($this->get_newsletter_page_id());
1244
- }
1245
-
1246
- /**
1247
- * Returns the Newsletter dedicated page URL or an alternative URL if that page if not
1248
- * configured or not available.
1249
- *
1250
- * @staticvar string $url
1251
- * @return string
1252
- */
1253
- function get_newsletter_page_url($language = '') {
1254
-
1255
- $page = $this->get_newsletter_page();
1256
-
1257
- if (!$page || $page->post_status !== 'publish') {
1258
- return $this->build_action_url('m');
1259
- }
1260
-
1261
- $newsletter_page_url = get_permalink($page->ID);
1262
- if ($language && $newsletter_page_url) {
1263
- if (class_exists('SitePress')) {
1264
- $newsletter_page_url = apply_filters('wpml_permalink', $newsletter_page_url, $language, true);
1265
- }
1266
- if (function_exists('pll_get_post')) {
1267
- $translated_page = get_permalink(pll_get_post($page->ID, $language));
1268
- if ($translated_page) {
1269
- $newsletter_page_url = $translated_page;
1270
- }
1271
- }
1272
- }
1273
-
1274
- return $newsletter_page_url;
1275
- }
1276
-
1277
- function get_license_key() {
1278
- if (defined('NEWSLETTER_LICENSE_KEY')) {
1279
- return NEWSLETTER_LICENSE_KEY;
1280
- } else {
1281
- if (!empty($this->options['contract_key'])) {
1282
- return trim($this->options['contract_key']);
1283
- }
1284
- }
1285
- return false;
1286
- }
1287
-
1288
- /**
1289
- * Get the data connected to the specified license code on man settings.
1290
- *
1291
- * - false if no license is present
1292
- * - WP_Error if something went wrong if getting the license data
1293
- * - object with expiration and addons list
1294
- *
1295
- * @param boolean $refresh
1296
- * @return \WP_Error|boolean|object
1297
- */
1298
- function get_license_data($refresh = false) {
1299
-
1300
- $this->logger->debug('Getting license data');
1301
-
1302
- $license_key = $this->get_license_key();
1303
- if (empty($license_key)) {
1304
- $this->logger->debug('License was empty');
1305
- delete_transient('newsletter_license_data');
1306
- return false;
1307
- }
1308
-
1309
- if (!$refresh) {
1310
- $license_data = get_transient('newsletter_license_data');
1311
- if ($license_data !== false && is_object($license_data)) {
1312
- $this->logger->debug('License data found on cache');
1313
- return $license_data;
1314
- }
1315
- }
1316
-
1317
- $this->logger->debug('Refreshing the license data');
1318
-
1319
- $license_data_url = 'https://www.thenewsletterplugin.com/wp-content/plugins/file-commerce-pro/get-license-data.php';
1320
-
1321
- $response = wp_remote_post($license_data_url, array(
1322
- 'body' => array('k' => $license_key)
1323
- ));
1324
-
1325
- // Fall back to http...
1326
- if (is_wp_error($response)) {
1327
- $this->logger->error($response);
1328
- $this->logger->error('Falling back to http');
1329
- $license_data_url = str_replace('https', 'http', $license_data_url);
1330
- $response = wp_remote_post($license_data_url, array(
1331
- 'body' => array('k' => $license_key)
1332
- ));
1333
- if (is_wp_error($response)) {
1334
- $this->logger->error($response);
1335
- set_transient('newsletter_license_data', $response, DAY_IN_SECONDS);
1336
- return $response;
1337
- }
1338
- }
1339
-
1340
- $download_message = 'You can download all addons from www.thenewsletterplugin.com if your license is valid.';
1341
-
1342
- if (wp_remote_retrieve_response_code($response) != '200') {
1343
- $this->logger->error('license data error: ' . wp_remote_retrieve_response_code($response));
1344
- $data = new WP_Error(wp_remote_retrieve_response_code($response), 'License validation service error. <br>' . $download_message);
1345
- set_transient('newsletter_license_data', $data, DAY_IN_SECONDS);
1346
- return $data;
1347
- }
1348
-
1349
- $json = wp_remote_retrieve_body($response);
1350
- $data = json_decode($json);
1351
-
1352
- if (!is_object($data)) {
1353
- $this->logger->error($json);
1354
- $data = new WP_Error(1, 'License validation service error. <br>' . $download_message);
1355
- set_transient('newsletter_license_data', $data, DAY_IN_SECONDS);
1356
- return $data;
1357
- }
1358
-
1359
- if (isset($data->message)) {
1360
- $data = new WP_Error(1, $data->message . ' (check the license on Newsletter main settings)');
1361
- set_transient('newsletter_license_data', $data, DAY_IN_SECONDS);
1362
- return $data;
1363
- }
1364
-
1365
- $expiration = WEEK_IN_SECONDS;
1366
- // If the license expires in few days, make the transient live only few days, so it will be refreshed
1367
- if ($data->expire > time() && $data->expire - time() < WEEK_IN_SECONDS) {
1368
- $expiration = $data->expire - time();
1369
- }
1370
- set_transient('newsletter_license_data', $data, $expiration);
1371
-
1372
- return $data;
1373
- }
1374
-
1375
- /**
1376
- * @deprecated
1377
- * @param type $license_key
1378
- * @return \WP_Error
1379
- */
1380
- public static function check_license($license_key) {
1381
- $response = wp_remote_get('http://www.thenewsletterplugin.com/wp-content/plugins/file-commerce-pro/check.php?k=' . urlencode($license_key), array('sslverify' => false));
1382
- if (is_wp_error($response)) {
1383
- /* @var $response WP_Error */
1384
- return new WP_Error(-1, 'It seems that your blog cannot contact the license validator. Ask your provider to unlock the HTTP/HTTPS connections to www.thenewsletterplugin.com<br>'
1385
- . esc_html($response->get_error_code()) . ' - ' . esc_html($response->get_error_message()));
1386
- } else if ($response['response']['code'] != 200) {
1387
- return new WP_Error(-1, '[' . $response['response']['code'] . '] The license seems expired or not valid, please check your <a href="https://www.thenewsletterplugin.com/account">license code and status</a>, thank you.'
1388
- . '<br>You can anyway download the professional extension from https://www.thenewsletterplugin.com.');
1389
- } elseif ($expires = json_decode(wp_remote_retrieve_body($response))) {
1390
- return array('expires' => $expires->expire, 'message' => 'Your license is valid and expires on ' . esc_html(date('Y-m-d', $expires->expire)));
1391
- } else {
1392
- return new WP_Error(-1, 'Unable to detect the license expiration. Debug data to report to the support: <code>' . esc_html(wp_remote_retrieve_body($response)) . '</code>');
1393
- }
1394
- }
1395
-
1396
- function add_notice_to_chosen_profile_page_hook($post_states, $post) {
1397
-
1398
- if ($post->ID == $this->options['page']) {
1399
- $post_states[] = __('Newsletter plugin page, do not delete', 'newsletter');
1400
- }
1401
-
1402
- return $post_states;
1403
- }
1404
-
1405
- }
1406
-
1407
- $newsletter = Newsletter::instance();
1408
-
1409
- if (is_admin()) {
1410
- require_once NEWSLETTER_DIR . '/system/system.php';
1411
- }
1412
-
1413
- require_once NEWSLETTER_DIR . '/subscription/subscription.php';
1414
- require_once NEWSLETTER_DIR . '/unsubscription/unsubscription.php';
1415
- require_once NEWSLETTER_DIR . '/profile/profile.php';
1416
- require_once NEWSLETTER_DIR . '/emails/emails.php';
1417
- require_once NEWSLETTER_DIR . '/users/users.php';
1418
- require_once NEWSLETTER_DIR . '/statistics/statistics.php';
1419
- require_once NEWSLETTER_DIR . '/widget/standard.php';
1420
- require_once NEWSLETTER_DIR . '/widget/minimal.php';
 
 
1
+ <?php
2
+
3
+ /*
4
+ Plugin Name: Newsletter
5
+ Plugin URI: https://www.thenewsletterplugin.com/plugins/newsletter
6
+ Description: Newsletter is a cool plugin to create your own subscriber list, to send newsletters, to build your business. <strong>Before update give a look to <a href="https://www.thenewsletterplugin.com/category/release">this page</a> to know what's changed.</strong>
7
+ Version: 7.4.8
8
+ Author: Stefano Lissa & The Newsletter Team
9
+ Author URI: https://www.thenewsletterplugin.com
10
+ Disclaimer: Use at your own risk. No warranty expressed or implied is provided.
11
+ Text Domain: newsletter
12
+ License: GPLv2 or later
13
+ Requires at least: 4.6
14
+ Requires PHP: 5.6
15
+
16
+ Copyright 2009-2022 The Newsletter Team (email: info@thenewsletterplugin.com, web: https://www.thenewsletterplugin.com)
17
+
18
+ Newsletter is free software: you can redistribute it and/or modify
19
+ it under the terms of the GNU General Public License as published by
20
+ the Free Software Foundation, either version 2 of the License, or
21
+ any later version.
22
+
23
+ Newsletter is distributed in the hope that it will be useful,
24
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
25
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26
+ GNU General Public License for more details.
27
+
28
+ You should have received a copy of the GNU General Public License
29
+ along with Newsletter. If not, see https://www.gnu.org/licenses/gpl-2.0.html.
30
+
31
+ */
32
+
33
+ if (version_compare(phpversion(), '5.6', '<')) {
34
+ add_action('admin_notices', function () {
35
+ echo '<div class="notice notice-error"><p>PHP version 5.6 or greater is required for Newsletter. Ask your provider to upgrade. <a href="https://www.php.net/supported-versions.php" target="_blank">Read more on PHP versions</a></p></div>';
36
+ });
37
+ return;
38
+ }
39
+
40
+ define('NEWSLETTER_VERSION', '7.4.8');
41
+
42
+ global $newsletter, $wpdb;
43
+
44
+ // For acceptance tests, DO NOT CHANGE
45
+ if (!defined('NEWSLETTER_DEBUG'))
46
+ define('NEWSLETTER_DEBUG', false);
47
+
48
+ if (!defined('NEWSLETTER_EXTENSION_UPDATE'))
49
+ define('NEWSLETTER_EXTENSION_UPDATE', true);
50
+
51
+ if (!defined('NEWSLETTER_EMAILS_TABLE'))
52
+ define('NEWSLETTER_EMAILS_TABLE', $wpdb->prefix . 'newsletter_emails');
53
+
54
+ if (!defined('NEWSLETTER_USERS_TABLE'))
55
+ define('NEWSLETTER_USERS_TABLE', $wpdb->prefix . 'newsletter');
56
+
57
+ if (!defined('NEWSLETTER_STATS_TABLE'))
58
+ define('NEWSLETTER_STATS_TABLE', $wpdb->prefix . 'newsletter_stats');
59
+
60
+ if (!defined('NEWSLETTER_SENT_TABLE'))
61
+ define('NEWSLETTER_SENT_TABLE', $wpdb->prefix . 'newsletter_sent');
62
+
63
+ define('NEWSLETTER_SLUG', 'newsletter');
64
+
65
+ define('NEWSLETTER_DIR', __DIR__);
66
+ define('NEWSLETTER_INCLUDES_DIR', __DIR__ . '/includes');
67
+
68
+ // Deperacted since plugin can change the plugins_url()
69
+ define('NEWSLETTER_URL', WP_PLUGIN_URL . '/newsletter');
70
+
71
+ if (!defined('NEWSLETTER_LIST_MAX'))
72
+ define('NEWSLETTER_LIST_MAX', 40);
73
+
74
+ if (!defined('NEWSLETTER_PROFILE_MAX'))
75
+ define('NEWSLETTER_PROFILE_MAX', 20);
76
+
77
+ if (!defined('NEWSLETTER_FORMS_MAX'))
78
+ define('NEWSLETTER_FORMS_MAX', 10);
79
+
80
+ if (!defined('NEWSLETTER_HEADER'))
81
+ define('NEWSLETTER_HEADER', true);
82
+
83
+ require_once NEWSLETTER_INCLUDES_DIR . '/module.php';
84
+ require_once NEWSLETTER_INCLUDES_DIR . '/TNP.php';
85
+ require_once NEWSLETTER_INCLUDES_DIR . '/cron.php';
86
+
87
+ class Newsletter extends NewsletterModule {
88
+
89
+ // Limits to respect to avoid memory, time or provider limits
90
+ var $time_start;
91
+ var $time_limit = 0;
92
+ var $max_emails = null;
93
+ var $mailer = null;
94
+ var $action = '';
95
+
96
+ /** @var Newsletter */
97
+ static $instance;
98
+
99
+ const STATUS_NOT_CONFIRMED = 'S';
100
+ const STATUS_CONFIRMED = 'C';
101
+
102
+ /**
103
+ * @return Newsletter
104
+ */
105
+ static function instance() {
106
+ if (self::$instance == null) {
107
+ self::$instance = new Newsletter();
108
+ }
109
+ return self::$instance;
110
+ }
111
+
112
+ function __construct() {
113
+
114
+ // Grab it before a plugin decides to remove it.
115
+ if (isset($_GET['na'])) {
116
+ $this->action = $_GET['na'];
117
+ }
118
+ if (isset($_POST['na'])) {
119
+ $this->action = $_POST['na'];
120
+ }
121
+
122
+ $this->time_start = time();
123
+
124
+ parent::__construct('main', '1.6.7', null, ['info', 'smtp']);
125
+
126
+ add_action('plugins_loaded', [$this, 'hook_plugins_loaded']);
127
+ add_action('init', [$this, 'hook_init'], 1);
128
+ add_action('wp_loaded', [$this, 'hook_wp_loaded'], 1);
129
+
130
+ add_action('newsletter', [$this, 'hook_newsletter'], 1);
131
+
132
+ register_activation_hook(__FILE__, [$this, 'hook_activate']);
133
+ register_deactivation_hook(__FILE__, [$this, 'hook_deactivate']);
134
+
135
+ add_action('admin_init', [$this, 'hook_admin_init']);
136
+
137
+ if (is_admin()) {
138
+ add_action('admin_head', [$this, 'hook_admin_head']);
139
+
140
+ // Protection against strange schedule removal on some installations
141
+ if (!wp_next_scheduled('newsletter') && (!defined('WP_INSTALLING') || !WP_INSTALLING)) {
142
+ wp_schedule_event(time() + 30, 'newsletter', 'newsletter');
143
+ }
144
+
145
+ add_action('admin_menu', [$this, 'add_extensions_menu'], 90);
146
+
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
+
155
+ function hook_plugins_loaded() {
156
+ // Used to load dependant modules
157
+ do_action('newsletter_loaded', NEWSLETTER_VERSION);
158
+
159
+ if (function_exists('load_plugin_textdomain')) {
160
+ load_plugin_textdomain('newsletter', false, plugin_basename(__DIR__) . '/languages');
161
+ }
162
+ }
163
+
164
+ function hook_init() {
165
+ global $wpdb;
166
+
167
+ if (isset($this->options['debug']) && $this->options['debug'] == 1) {
168
+ ini_set('log_errors', 1);
169
+ ini_set('error_log', WP_CONTENT_DIR . '/logs/newsletter/php-' . date('Y-m') . '-' . get_option('newsletter_logger_secret') . '.txt');
170
+ }
171
+
172
+ add_shortcode('newsletter_replace', [$this, 'shortcode_newsletter_replace']);
173
+
174
+ add_filter('site_transient_update_plugins', [$this, 'hook_site_transient_update_plugins']);
175
+
176
+ if (is_admin()) {
177
+ if (!class_exists('NewsletterExtensions')) {
178
+
179
+ add_filter('plugin_row_meta', function ($plugin_meta, $plugin_file) {
180
+
181
+ static $slugs = array();
182
+ if (empty($slugs)) {
183
+ $addons = $this->getTnpExtensions();
184
+ if ($addons) {
185
+ foreach ($addons as $addon) {
186
+ $slugs[] = $addon->wp_slug;
187
+ }
188
+ }
189
+ }
190
+ if (array_search($plugin_file, $slugs) !== false) {
191
+
192
+ $plugin_meta[] = '<a href="admin.php?page=newsletter_main_extensions" style="font-weight: bold">Newsletter Addons Manager required</a>';
193
+ }
194
+ return $plugin_meta;
195
+ }, 10, 2);
196
+ }
197
+
198
+ add_action('in_admin_header', array($this, 'hook_in_admin_header'), 1000);
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;
206
+ update_option('newsletter_dismissed', $dismissed);
207
+ wp_redirect($_SERVER['HTTP_REFERER']);
208
+ exit();
209
+ }
210
+ }
211
+ } else {
212
+ add_action('wp_enqueue_scripts', [$this, 'hook_wp_enqueue_scripts']);
213
+ }
214
+
215
+ do_action('newsletter_init');
216
+ }
217
+
218
+ function hook_wp_loaded() {
219
+ if (empty($this->action)) {
220
+ return;
221
+ }
222
+
223
+ if ($this->action == 'test') {
224
+ // This response is tested, do not change it!
225
+ echo 'ok';
226
+ die();
227
+ }
228
+
229
+ if ($this->action === 'nul') {
230
+ $this->dienow('This link is not active on newsletter preview', 'You can send a test message to test subscriber to have the real working link.');
231
+ }
232
+
233
+ $user = $this->get_user_from_request();
234
+ $email = $this->get_email_from_request();
235
+ do_action('newsletter_action', $this->action, $user, $email);
236
+ }
237
+
238
+ function hook_activate() {
239
+ // Ok, why? When the plugin is not active WordPress may remove the scheduled "newsletter" action because
240
+ // the every-five-minutes schedule named "newsletter" is not present.
241
+ // Since the activation does not forces an upgrade, that schedule must be reactivated here. It is activated on
242
+ // the upgrade method as well for the user which upgrade the plugin without deactivte it (many).
243
+ if (!wp_next_scheduled('newsletter')) {
244
+ wp_schedule_event(time() + 30, 'newsletter', 'newsletter');
245
+ }
246
+
247
+ $install_time = get_option('newsletter_install_time');
248
+ if (!$install_time) {
249
+ update_option('newsletter_install_time', time(), false);
250
+ }
251
+
252
+ touch(NEWSLETTER_LOG_DIR . '/index.html');
253
+
254
+ Newsletter::instance()->upgrade();
255
+ NewsletterUsers::instance()->upgrade();
256
+ NewsletterEmails::instance()->upgrade();
257
+ NewsletterSubscription::instance()->upgrade();
258
+ NewsletterStatistics::instance()->upgrade();
259
+ NewsletterProfile::instance()->upgrade();
260
+ }
261
+
262
+ function first_install() {
263
+ parent::first_install();
264
+ update_option('newsletter_show_welcome', '1', false);
265
+ }
266
+
267
+ function upgrade() {
268
+ global $wpdb, $charset_collate;
269
+
270
+ require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
271
+
272
+ parent::upgrade();
273
+
274
+ $sql = "CREATE TABLE `" . $wpdb->prefix . "newsletter_emails` (
275
+ `id` int(11) NOT NULL AUTO_INCREMENT,
276
+ `language` varchar(10) NOT NULL DEFAULT '',
277
+ `subject` varchar(255) NOT NULL DEFAULT '',
278
+ `message` longtext,
279
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
280
+ `status` enum('new','sending','sent','paused','error') NOT NULL DEFAULT 'new',
281
+ `total` int(11) NOT NULL DEFAULT '0',
282
+ `last_id` int(11) NOT NULL DEFAULT '0',
283
+ `sent` int(11) NOT NULL DEFAULT '0',
284
+ `track` int(11) NOT NULL DEFAULT '1',
285
+ `list` int(11) NOT NULL DEFAULT '0',
286
+ `type` varchar(50) NOT NULL DEFAULT '',
287
+ `query` longtext,
288
+ `editor` tinyint(4) NOT NULL DEFAULT '0',
289
+ `sex` varchar(20) NOT NULL DEFAULT '',
290
+ `theme` varchar(50) NOT NULL DEFAULT '',
291
+ `message_text` longtext,
292
+ `preferences` longtext,
293
+ `send_on` int(11) NOT NULL DEFAULT '0',
294
+ `token` varchar(10) NOT NULL DEFAULT '',
295
+ `options` longtext,
296
+ `private` tinyint(1) NOT NULL DEFAULT '0',
297
+ `click_count` int(10) unsigned NOT NULL DEFAULT '0',
298
+ `version` varchar(10) NOT NULL DEFAULT '',
299
+ `open_count` int(10) unsigned NOT NULL DEFAULT '0',
300
+ `unsub_count` int(10) unsigned NOT NULL DEFAULT '0',
301
+ `error_count` int(10) unsigned NOT NULL DEFAULT '0',
302
+ `stats_time` int(10) unsigned NOT NULL DEFAULT '0',
303
+ `updated` int(10) unsigned NOT NULL DEFAULT '0',
304
+ PRIMARY KEY (`id`)
305
+ ) $charset_collate;";
306
+
307
+ dbDelta($sql);
308
+
309
+ // WP does not manage composite primary key when it tries to upgrade a table...
310
+ $suppress_errors = $wpdb->suppress_errors(true);
311
+
312
+ dbDelta("CREATE TABLE " . $wpdb->prefix . "newsletter_sent (
313
+ email_id int(10) unsigned NOT NULL DEFAULT '0',
314
+ user_id int(10) unsigned NOT NULL DEFAULT '0',
315
+ status tinyint(1) unsigned NOT NULL DEFAULT '0',
316
+ open tinyint(1) unsigned NOT NULL DEFAULT '0',
317
+ time int(10) unsigned NOT NULL DEFAULT '0',
318
+ error varchar(255) NOT NULL DEFAULT '',
319
+ ip varchar(100) NOT NULL DEFAULT '',
320
+ PRIMARY KEY (email_id,user_id),
321
+ KEY user_id (user_id),
322
+ KEY email_id (email_id)
323
+ ) $charset_collate;");
324
+ $wpdb->suppress_errors($suppress_errors);
325
+
326
+ // Some setting check to avoid the common support request for mis-configurations
327
+ $options = $this->get_options();
328
+
329
+ if (empty($options['scheduler_max']) || !is_numeric($options['scheduler_max'])) {
330
+ $options['scheduler_max'] = 100;
331
+ $this->save_options($options);
332
+ }
333
+
334
+ wp_clear_scheduled_hook('newsletter');
335
+ wp_schedule_event(time() + 30, 'newsletter', 'newsletter');
336
+
337
+ if (!empty($this->options['editor'])) {
338
+ if (empty($this->options['roles'])) {
339
+ $this->options['roles'] = array('editor');
340
+ unset($this->options['editor']);
341
+ }
342
+ $this->save_options($this->options);
343
+ }
344
+
345
+ // Clear the addons and license caches
346
+ delete_transient('newsletter_license_data');
347
+ $this->clear_extensions_cache();
348
+
349
+ touch(NEWSLETTER_LOG_DIR . '/index.html');
350
+
351
+ return true;
352
+ }
353
+
354
+ function is_allowed() {
355
+ if (current_user_can('administrator')) {
356
+ return true;
357
+ }
358
+ if (!empty($this->options['roles'])) {
359
+ foreach ($this->options['roles'] as $role) {
360
+ if (current_user_can($role)) {
361
+ return true;
362
+ }
363
+ }
364
+ }
365
+ return false;
366
+ }
367
+
368
+ function admin_menu() {
369
+ if (!$this->is_allowed()) {
370
+ return;
371
+ }
372
+
373
+ add_menu_page('Newsletter', 'Newsletter', 'exist', 'newsletter_main_index', '', plugins_url('newsletter') . '/admin/images/menu-icon.png', '30.333');
374
+
375
+ $this->add_menu_page('index', __('Dashboard', 'newsletter'));
376
+ $this->add_admin_page('info', __('Company info', 'newsletter'));
377
+
378
+ if (current_user_can('administrator')) {
379
+ $this->add_menu_page('welcome', __('Welcome', 'newsletter'));
380
+ $this->add_menu_page('main', __('Settings', 'newsletter'));
381
+
382
+ // Pages not on menu
383
+ $this->add_admin_page('smtp', 'SMTP');
384
+ $this->add_admin_page('diagnostic', __('Diagnostic', 'newsletter'));
385
+ $this->add_admin_page('test', __('Test', 'newsletter'));
386
+ }
387
+ }
388
+
389
+ function add_extensions_menu() {
390
+ if (!class_exists('NewsletterExtensions')) {
391
+ $this->add_menu_page('extensions', '<span style="color:#27AE60; font-weight: bold;">' . __('Addons', 'newsletter') . '</span>');
392
+ }
393
+ }
394
+
395
+ function hook_in_admin_header() {
396
+ if (!$this->is_admin_page()) {
397
+ add_action('admin_notices', array($this, 'hook_admin_notices'));
398
+ return;
399
+ }
400
+ remove_all_actions('admin_notices');
401
+ remove_all_actions('all_admin_notices');
402
+ add_action('admin_notices', array($this, 'hook_admin_notices'));
403
+ }
404
+
405
+ function hook_admin_notices() {
406
+ // Check of Newsletter dedicated page
407
+ if (!empty($this->options['page'])) {
408
+ if (get_post_status($this->options['page']) !== 'publish') {
409
+ echo '<div class="notice notice-error"><p>The Newsletter dedicated page is not published. <a href="', site_url('/wp-admin/post.php') . '?post=', $this->options['page'], '&action=edit"><strong>Edit the page</strong></a> or <a href="admin.php?page=newsletter_main_main"><strong>review the main settings</strong></a>.</p></div>';
410
+ }
411
+ }
412
+
413
+ if (isset($this->options['debug']) && $this->options['debug'] == 1) {
414
+ echo '<div class="notice notice-warning"><p>The Newsletter plugin is in <strong>debug mode</strong>. When done change it on Newsletter <a href="admin.php?page=newsletter_main_main"><strong>main settings</strong></a>. Do not keep the debug mode active on production sites.</p></div>';
415
+ }
416
+ }
417
+
418
+ function hook_wp_enqueue_scripts() {
419
+ if (empty($this->options['css_disabled']) && apply_filters('newsletter_enqueue_style', true)) {
420
+ wp_enqueue_style('newsletter', plugins_url('newsletter') . '/style.css', [], NEWSLETTER_VERSION);
421
+ if (!empty($this->options['css'])) {
422
+ wp_add_inline_style('newsletter', $this->options['css']);
423
+ }
424
+ } else {
425
+ if (!empty($this->options['css'])) {
426
+ add_action('wp_head', function () {
427
+ echo '<style>', $this->options['css'], '</style>';
428
+ });
429
+ }
430
+ }
431
+ }
432
+
433
+ function hook_admin_enqueue_scripts() {
434
+
435
+ $newsletter_url = plugins_url('newsletter');
436
+ wp_enqueue_script('jquery-ui-tabs');
437
+ wp_enqueue_script('jquery-ui-tooltip');
438
+ wp_enqueue_script('jquery-ui-draggable');
439
+ wp_enqueue_media();
440
+
441
+ wp_enqueue_script('tnp-admin', $newsletter_url . '/admin/admin.js', ['jquery'], NEWSLETTER_VERSION);
442
+
443
+ wp_enqueue_style('tnp-select2', $newsletter_url . '/vendor/select2/css/select2.min.css', [], NEWSLETTER_VERSION);
444
+ wp_enqueue_script('tnp-select2', $newsletter_url . '/vendor/select2/js/select2.min.js', ['jquery'], NEWSLETTER_VERSION);
445
+
446
+ wp_enqueue_style('tnp-modal', $newsletter_url . '/admin/modal.css', [], NEWSLETTER_VERSION);
447
+ wp_enqueue_script('tnp-modal', $newsletter_url . '/admin/modal.js', ['jquery'], NEWSLETTER_VERSION, true);
448
+
449
+ wp_enqueue_style('tnp-toast', $newsletter_url . '/admin/toast.css', [], NEWSLETTER_VERSION);
450
+ wp_enqueue_script('tnp-toast', $newsletter_url . '/admin/toast.js', ['jquery'], NEWSLETTER_VERSION);
451
+
452
+ wp_enqueue_style('tnp-admin-font', 'https://use.typekit.net/jlj2wjy.css');
453
+ wp_enqueue_style('tnp-admin-fontawesome', $newsletter_url . '/vendor/fa/css/all.min.css', [], NEWSLETTER_VERSION);
454
+ wp_enqueue_style('tnp-admin-jquery-ui', $newsletter_url . '/vendor/jquery-ui/jquery-ui.min.css', [], NEWSLETTER_VERSION);
455
+ wp_enqueue_style('tnp-admin', $newsletter_url . '/admin/admin.css', [], NEWSLETTER_VERSION);
456
+ wp_enqueue_style('tnp-admin-dropdown', $newsletter_url . '/admin/dropdown.css', [], NEWSLETTER_VERSION);
457
+ wp_enqueue_style('tnp-admin-tabs', $newsletter_url . '/admin/tabs.css', [], NEWSLETTER_VERSION);
458
+ wp_enqueue_style('tnp-admin-controls', $newsletter_url . '/admin/controls.css', [], NEWSLETTER_VERSION);
459
+ wp_enqueue_style('tnp-admin-fields', $newsletter_url . '/admin/fields.css', [], NEWSLETTER_VERSION);
460
+ wp_enqueue_style('tnp-admin-widgets', $newsletter_url . '/admin/widgets.css', [], NEWSLETTER_VERSION);
461
+ wp_enqueue_style('tnp-admin-extensions', $newsletter_url . '/admin/extensions.css', [], NEWSLETTER_VERSION);
462
+
463
+ $translations_array = array(
464
+ 'save_to_update_counter' => __('Save the newsletter to update the counter!', 'newsletter')
465
+ );
466
+ wp_localize_script('tnp-admin', 'tnp_translations', $translations_array);
467
+
468
+ wp_enqueue_script('tnp-jquery-vmap', $newsletter_url . '/vendor/jqvmap/jquery.vmap.min.js', ['jquery'], NEWSLETTER_VERSION);
469
+ wp_enqueue_script('tnp-jquery-vmap-world', $newsletter_url . '/vendor/jqvmap/jquery.vmap.world.js', ['tnp-jquery-vmap'], NEWSLETTER_VERSION);
470
+ wp_enqueue_style('tnp-jquery-vmap', $newsletter_url . '/vendor/jqvmap/jqvmap.min.css', [], NEWSLETTER_VERSION);
471
+
472
+ wp_register_script('tnp-chart', $newsletter_url . '/vendor/chartjs/Chart.min.js', ['jquery'], NEWSLETTER_VERSION);
473
+
474
+ wp_enqueue_script('tnp-color-picker', $newsletter_url . '/vendor/spectrum/spectrum.min.js', ['jquery']);
475
+ wp_enqueue_style('tnp-color-picker', $newsletter_url . '/vendor/spectrum/spectrum.min.css', [], NEWSLETTER_VERSION);
476
+ }
477
+
478
+ function shortcode_newsletter_replace($attrs, $content) {
479
+ $content = do_shortcode($content);
480
+ $content = $this->replace($content, $this->get_user_from_request(), $this->get_email_from_request());
481
+ return $content;
482
+ }
483
+
484
+ function is_admin_page() {
485
+ if (!isset($_GET['page'])) {
486
+ return false;
487
+ }
488
+ $page = $_GET['page'];
489
+ return strpos($page, 'newsletter_') === 0;
490
+ }
491
+
492
+ function hook_admin_init() {
493
+ // Verificare il contesto
494
+ if (isset($_GET['page']) && $_GET['page'] === 'newsletter_main_welcome')
495
+ return;
496
+ if (get_option('newsletter_show_welcome')) {
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() {
508
+ // Small global rule for sidebar menu entries
509
+ echo '<style>';
510
+ echo '.tnp-side-menu { color: #E67E22!important; }';
511
+ echo '</style>';
512
+ }
513
+
514
+ function relink($text, $email_id, $user_id, $email_token = '') {
515
+ return NewsletterStatistics::instance()->relink($text, $email_id, $user_id, $email_token);
516
+ }
517
+
518
+ /**
519
+ * Runs every 5 minutes and look for emails that need to be processed.
520
+ */
521
+ function hook_newsletter() {
522
+
523
+ $this->logger->debug(__METHOD__ . '> Start');
524
+
525
+ if (!$this->check_transient('engine', NEWSLETTER_CRON_INTERVAL)) {
526
+ $this->logger->debug(__METHOD__ . '> Engine already active, exit');
527
+ return;
528
+ }
529
+
530
+ $emails = $this->get_results("select * from " . NEWSLETTER_EMAILS_TABLE . " where status='sending' and send_on<" . time() . " order by id asc");
531
+ $this->logger->debug(__METHOD__ . '> Emails found in sending status: ' . count($emails));
532
+
533
+ foreach ($emails as $email) {
534
+ $this->logger->debug(__METHOD__ . '> Start newsletter ' . $email->id);
535
+ $email->options = maybe_unserialize($email->options);
536
+ $r = $this->send($email);
537
+ $this->logger->debug(__METHOD__ . '> End newsletter ' . $email->id);
538
+ if (!$r) {
539
+ $this->logger->debug(__METHOD__ . '> Engine returned false, there is no more capacity');
540
+ break;
541
+ }
542
+ }
543
+ // Remove the semaphore so the delivery engine can be activated again
544
+ $this->delete_transient('engine');
545
+
546
+ $this->logger->debug(__METHOD__ . '> End');
547
+ }
548
+
549
+ function get_send_speed($email = null) {
550
+ $this->logger->debug(__METHOD__ . '> Computing delivery speed');
551
+ $mailer = $this->get_mailer();
552
+ $speed = (int) $mailer->get_speed();
553
+ if (!$speed) {
554
+ $this->logger->debug(__METHOD__ . '> Speed not set by mailer, use the default');
555
+ $speed = (int) $this->options['scheduler_max'];
556
+ } else {
557
+ $this->logger->debug(__METHOD__ . '> Speed set by mailer');
558
+ }
559
+
560
+ //$speed = (int) apply_filters('newsletter_send_speed', $speed, $email);
561
+ // Fail safe setting
562
+ $runs_per_hour = $this->get_runs_per_hour();
563
+ if (!$speed || $speed < $runs_per_hour) {
564
+ return $runs_per_hour;
565
+ }
566
+
567
+ $this->logger->debug(__METHOD__ . '> Speed: ' . $speed);
568
+ return $speed;
569
+ }
570
+
571
+ function get_send_delay() {
572
+ return 0;
573
+ }
574
+
575
+ function skip_this_run($email = null) {
576
+ return (boolean) apply_filters('newsletter_send_skip', false, $email);
577
+ }
578
+
579
+ function get_runs_per_hour() {
580
+ return (int) (3600 / NEWSLETTER_CRON_INTERVAL);
581
+ }
582
+
583
+ function get_emails_per_run() {
584
+ $speed = $this->get_send_speed();
585
+ $max = (int) ($speed / $this->get_runs_per_hour());
586
+
587
+ return $max;
588
+ }
589
+
590
+ function get_max_emails($email) {
591
+ // Obsolete, here from Speed Control Addon
592
+ $max = (int) apply_filters('newsletter_send_max_emails', $this->max_emails, $email);
593
+
594
+ return min($max, $this->max_emails);
595
+ }
596
+
597
+ function fix_email($email) {
598
+ if (empty($email->query)) {
599
+ $email->query = "select * from " . NEWSLETTER_USERS_TABLE . " where status='C'";
600
+ }
601
+ if (empty($email->id)) {
602
+ $email->id = 0;
603
+ }
604
+ }
605
+
606
+ function send_setup() {
607
+ $this->logger->debug(__METHOD__ . '> Setup delivery engine');
608
+ if (is_null($this->max_emails)) {
609
+ $this->max_emails = $this->get_emails_per_run();
610
+ $this->logger->debug(__METHOD__ . '> Max emails: ' . $this->max_emails);
611
+ ignore_user_abort(true);
612
+
613
+ @set_time_limit(NEWSLETTER_CRON_INTERVAL + 30);
614
+
615
+ $max_time = (int) (@ini_get('max_execution_time') * 0.95);
616
+ if ($max_time == 0 || $max_time > NEWSLETTER_CRON_INTERVAL) {
617
+ $max_time = (int) (NEWSLETTER_CRON_INTERVAL * 0.95);
618
+ }
619
+
620
+ $this->time_limit = $this->time_start + $max_time;
621
+
622
+ $this->logger->debug(__METHOD__ . '> Max time set to ' . $max_time);
623
+ } else {
624
+ $this->logger->debug(__METHOD__ . '> Already setup');
625
+ }
626
+ }
627
+
628
+ function time_exceeded() {
629
+ if ($this->time_limit && time() > $this->time_limit) {
630
+ $this->logger->info(__METHOD__ . '> Max execution time limit reached');
631
+ return true;
632
+ }
633
+ }
634
+
635
+ /**
636
+ * Sends an email to targeted users or to given users. If a list of users is given (usually a list of test users)
637
+ * the query inside the email to retrieve users is not used.
638
+ *
639
+ * @global wpdb $wpdb
640
+ * @global type $newsletter_feed
641
+ * @param TNP_Email $email
642
+ * @param array $users
643
+ * @return boolean|WP_Error True if the process completed, false if limits was reached. On false the caller should no continue to call it with other emails.
644
+ */
645
+ function send($email, $users = null, $test = false) {
646
+ global $wpdb;
647
+
648
+ if (is_array($email)) {
649
+ $email = (object) $email;
650
+ }
651
+
652
+ $this->logger->info(__METHOD__ . '> Send email ' . $email->id);
653
+
654
+ $this->send_setup();
655
+
656
+ if ($this->max_emails <= 0) {
657
+ $this->logger->info(__METHOD__ . '> No more capacity');
658
+ return false;
659
+ }
660
+
661
+ $this->fix_email($email);
662
+
663
+ // This stops the update of last_id and sent fields since
664
+ // it's not a scheduled delivery but a test or something else (like an autoresponder)
665
+ $supplied_users = $users != null;
666
+
667
+ if (!$supplied_users) {
668
+
669
+ if ($this->skip_this_run($email)) {
670
+ return true;
671
+ }
672
+
673
+ // Speed change for specific email by Speed Control Addon
674
+ $max_emails = $this->get_max_emails($email);
675
+ if ($max_emails <= 0) {
676
+ return true;
677
+ }
678
+
679
+ $query = $email->query;
680
+ $query .= " and id>" . $email->last_id . " order by id limit " . $max_emails;
681
+
682
+ $this->logger->debug(__METHOD__ . '> Query: ' . $query);
683
+
684
+ //Retrieve subscribers
685
+ $users = $this->get_results($query);
686
+
687
+ $this->logger->debug(__METHOD__ . '> Loaded subscribers: ' . count($users));
688
+
689
+ // If there was a database error, return error
690
+ if ($users === false) {
691
+ return new WP_Error('1', 'Unable to query subscribers, check the logs');
692
+ }
693
+
694
+ if (empty($users)) {
695
+ $this->logger->info(__METHOD__ . '> No more users, set as sent');
696
+ $wpdb->query("update " . NEWSLETTER_EMAILS_TABLE . " set status='sent', total=sent where id=" . $email->id . " limit 1");
697
+ do_action('newsletter_ended_sending_newsletter', $email);
698
+ return true;
699
+ }
700
+ } else {
701
+ $this->logger->info(__METHOD__ . '> Subscribers supplied');
702
+ }
703
+
704
+ $start_time = microtime(true);
705
+ $count = 0;
706
+ $result = true;
707
+
708
+ $mailer = $this->get_mailer();
709
+
710
+ $batch_size = $mailer->get_batch_size();
711
+
712
+ $this->logger->debug(__METHOD__ . '> Batch size: ' . $batch_size);
713
+
714
+ // For batch size == 1 (normal condition) we optimize
715
+ if ($batch_size == 1) {
716
+
717
+ foreach ($users as $user) {
718
+
719
+ $this->logger->debug(__METHOD__ . '> Processing user ID: ' . $user->id);
720
+ $user = apply_filters('newsletter_send_user', $user);
721
+ $message = $this->build_message($email, $user);
722
+
723
+ // Save even test emails since people wants to see some stats even for test emails. Stats are reset upon the real "send" of a newsletter
724
+ $this->save_sent_message($message);
725
+
726
+ //Se non è un test incremento il contatore delle email spedite. Perchè incremento prima di spedire??
727
+ if (!$test) {
728
+ $wpdb->query("update " . NEWSLETTER_EMAILS_TABLE . " set sent=sent+1, last_id=" . $user->id . " where id=" . $email->id . " limit 1");
729
+ }
730
+
731
+ $r = $mailer->send($message);
732
+
733
+ if (!empty($message->error)) {
734
+ $this->logger->error($message);
735
+ $this->save_sent_message($message);
736
+ }
737
+
738
+ if (is_wp_error($r)) {
739
+ $this->logger->error($r);
740
+
741
+ // For fatal error, the newsletter status i changed to error (and the delivery stopped)
742
+ if (!$test && $r->get_error_code() == NewsletterMailer::ERROR_FATAL) {
743
+ $this->set_error_state_of_email($email, $r->get_error_message());
744
+ return $r;
745
+ }
746
+ }
747
+
748
+ if (!$supplied_users && !$test && $this->time_exceeded()) {
749
+ $result = false;
750
+ break;
751
+ }
752
+ }
753
+
754
+ $this->max_emails--;
755
+ $count++;
756
+ } else {
757
+
758
+ $chunks = array_chunk($users, $batch_size);
759
+
760
+ foreach ($chunks as $chunk) {
761
+
762
+ $messages = [];
763
+
764
+ // Peeparing a batch of messages
765
+ foreach ($chunk as $user) {
766
+ $this->logger->debug(__METHOD__ . '> Processing user ID: ' . $user->id);
767
+ $user = apply_filters('newsletter_send_user', $user);
768
+ $message = $this->build_message($email, $user);
769
+ $this->save_sent_message($message);
770
+ $messages[] = $message;
771
+
772
+ if (!$test) {
773
+ $wpdb->query("update " . NEWSLETTER_EMAILS_TABLE . " set sent=sent+1, last_id=" . $user->id . " where id=" . $email->id . " limit 1");
774
+ }
775
+ $this->max_emails--;
776
+ $count++;
777
+ }
778
+
779
+ $r = $mailer->send_batch($messages);
780
+
781
+ // Updating the status of the sent messages
782
+ foreach ($messages as $message) {
783
+ if (!empty($message->error)) {
784
+ $this->save_sent_message($message);
785
+ }
786
+ }
787
+
788
+ // The batch went in error
789
+ if (is_wp_error($r)) {
790
+ $this->logger->error($r);
791
+
792
+ if (!$test && $r->get_error_code() == NewsletterMailer::ERROR_FATAL) {
793
+ $this->set_error_state_of_email($email, $r->get_error_message());
794
+ return $r;
795
+ }
796
+ }
797
+
798
+ if (!$supplied_users && !$test && $this->time_exceeded()) {
799
+ $result = false;
800
+ break;
801
+ }
802
+ }
803
+ }
804
+
805
+ $end_time = microtime(true);
806
+
807
+ // Stats only for newsletter with enough emails in a batch (we exclude the Autoresponder since it send one email per call)
808
+ if (!$test && !$supplied_users && $count > 5) {
809
+ $this->update_send_stats($start_time, $end_time, $count, $result);
810
+ }
811
+
812
+ // Cached general statistics are reset
813
+ if (!$test) {
814
+ NewsletterStatistics::instance()->reset_stats_time($email->id);
815
+ }
816
+
817
+ $this->logger->info(__METHOD__ . '> End run for email ' . $email->id);
818
+
819
+ return $result;
820
+ }
821
+
822
+ function update_send_stats($start_time, $end_time, $count, $result) {
823
+ $send_calls = get_option('newsletter_diagnostic_send_calls', []);
824
+ $send_calls[] = [$start_time, $end_time, $count, $result];
825
+
826
+ if (count($send_calls) > 100) {
827
+ array_shift($send_calls);
828
+ }
829
+
830
+ update_option('newsletter_diagnostic_send_calls', $send_calls, false);
831
+ }
832
+
833
+ /**
834
+ * @param TNP_Email $email
835
+ */
836
+ private function set_error_state_of_email($email, $message = '') {
837
+ // Handle only message type at the moment
838
+ if ($email->type !== 'message') {
839
+ return;
840
+ }
841
+
842
+ do_action('newsletter_error_on_sending', $email, $message);
843
+
844
+ $edited_email = new TNP_Email();
845
+ $edited_email->id = $email->id;
846
+ $edited_email->status = TNP_Email::STATUS_ERROR;
847
+ $edited_email->options = $email->options;
848
+ $edited_email->options['error_message'] = $message;
849
+
850
+ $this->save_email($edited_email);
851
+ }
852
+
853
+ /**
854
+ *
855
+ * @param TNP_Email $email
856
+ * @param TNP_User $user
857
+ * @return \TNP_Mailer_Message
858
+ */
859
+ function build_message($email, $user) {
860
+
861
+ $message = new TNP_Mailer_Message();
862
+
863
+ $message->to = $user->email;
864
+
865
+ $message->headers = [];
866
+ $message->headers['Precedence'] = 'bulk';
867
+ $message->headers['X-Newsletter-Email-Id'] = $email->id;
868
+ $message->headers['X-Auto-Response-Suppress'] = 'OOF, AutoReply';
869
+ $message->headers = apply_filters('newsletter_message_headers', $message->headers, $email, $user);
870
+
871
+ $message->body = preg_replace('/data-json=".*?"/is', '', $email->message);
872
+ $message->body = preg_replace('/ +/s', ' ', $message->body);
873
+ $message->body = $this->replace($message->body, $user, $email);
874
+ if ($this->options['do_shortcodes']) {
875
+ $message->body = do_shortcode($message->body);
876
+ }
877
+ $message->body = apply_filters('newsletter_message_html', $message->body, $email, $user);
878
+
879
+ $message->body_text = $this->replace($email->message_text, $user, $email);
880
+ $message->body_text = apply_filters('newsletter_message_text', $message->body_text, $email, $user);
881
+
882
+ if ($email->track == 1) {
883
+ $message->body = $this->relink($message->body, $email->id, $user->id, $email->token);
884
+ }
885
+
886
+ $message->subject = $this->replace($email->subject, $user);
887
+ $message->subject = apply_filters('newsletter_message_subject', $message->subject, $email, $user);
888
+
889
+ if (!empty($email->options['sender_email'])) {
890
+ $message->from = $email->options['sender_email'];
891
+ } else {
892
+ $message->from = $this->options['sender_email'];
893
+ }
894
+
895
+ if (!empty($email->options['sender_name'])) {
896
+ $message->from_name = $email->options['sender_name'];
897
+ } else {
898
+ $message->from_name = $this->options['sender_name'];
899
+ }
900
+
901
+ $message->email_id = $email->id;
902
+ $message->user_id = $user->id;
903
+
904
+ return apply_filters('newsletter_message', $message, $email, $user);
905
+ }
906
+
907
+ /**
908
+ *
909
+ * @param TNP_Mailer_Message $message
910
+ * @param int $status
911
+ * @param string $error
912
+ */
913
+ function save_sent_message($message) {
914
+ global $wpdb;
915
+
916
+ if (!$message->user_id || !$message->email_id) {
917
+ return;
918
+ }
919
+ $status = empty($message->error) ? 0 : 1;
920
+
921
+ $error = mb_substr($message->error, 0, 250);
922
+
923
+ $this->query($wpdb->prepare("insert into " . $wpdb->prefix . 'newsletter_sent (user_id, email_id, time, status, error) values (%d, %d, %d, %d, %s) on duplicate key update time=%d, status=%d, error=%s', $message->user_id, $message->email_id, time(), $status, $error, time(), $status, $error));
924
+ }
925
+
926
+ /**
927
+ * @deprecated since version 7.3.0
928
+ */
929
+ function limits_exceeded() {
930
+ return false;
931
+ }
932
+
933
+ /**
934
+ * @deprecated since version 6.0.0
935
+ */
936
+ function register_mail_method($callable) {
937
+
938
+ }
939
+
940
+ function register_mailer($mailer) {
941
+ if ($mailer instanceof NewsletterMailer) {
942
+ $this->mailer = $mailer;
943
+ }
944
+ }
945
+
946
+ /**
947
+ * Returns the current registered mailer which must be used to send emails.
948
+ *
949
+ * @return NewsletterMailer
950
+ */
951
+ function get_mailer() {
952
+ if ($this->mailer) {
953
+ return $this->mailer;
954
+ }
955
+
956
+ do_action('newsletter_register_mailer');
957
+
958
+ if (!$this->mailer) {
959
+ // Compatibility
960
+ $smtp = $this->get_options('smtp');
961
+ if (!empty($smtp['enabled'])) {
962
+ $this->mailer = new NewsletterDefaultSMTPMailer($smtp);
963
+ } else {
964
+ $this->mailer = new NewsletterDefaultMailer();
965
+ }
966
+ }
967
+ return $this->mailer;
968
+ }
969
+
970
+ /**
971
+ *
972
+ * @param TNP_Mailer_Message $message
973
+ * @return type
974
+ */
975
+ function deliver($message) {
976
+ $mailer = $this->get_mailer();
977
+ if (empty($message->from))
978
+ $message->from = $this->options['sender_email'];
979
+ if (empty($message->from_name))
980
+ $mailer->from_name = $this->options['sender_name'];
981
+ return $mailer->send($message);
982
+ }
983
+
984
+ /**
985
+ *
986
+ * @param type $to
987
+ * @param type $subject
988
+ * @param string|array $message If string is considered HTML, is array should contains the keys "html" and "text"
989
+ * @param type $headers
990
+ * @param type $enqueue
991
+ * @param type $from
992
+ * @return boolean
993
+ */
994
+ function mail($to, $subject, $message, $headers = array(), $enqueue = false, $from = false) {
995
+
996
+ if (empty($subject)) {
997
+ $this->logger->error('mail> Subject empty, skipped');
998
+ return true;
999
+ }
1000
+
1001
+ $mailer_message = new TNP_Mailer_Message();
1002
+ $mailer_message->to = $to;
1003
+ $mailer_message->subject = $subject;
1004
+ $mailer_message->from = $this->options['sender_email'];
1005
+ $mailer_message->from_name = $this->options['sender_name'];
1006
+
1007
+ if (!empty($headers)) {
1008
+ $mailer_message->headers = $headers;
1009
+ }
1010
+ $mailer_message->headers['X-Auto-Response-Suppress'] = 'OOF, AutoReply';
1011
+
1012
+ // Message carrige returns and line feeds clean up
1013
+ if (!is_array($message)) {
1014
+ $mailer_message->body = $this->clean_eol($message);
1015
+ } else {
1016
+ if (!empty($message['text'])) {
1017
+ $mailer_message->body_text = $this->clean_eol($message['text']);
1018
+ }
1019
+
1020
+ if (!empty($message['html'])) {
1021
+ $mailer_message->body = $this->clean_eol($message['html']);
1022
+ }
1023
+ }
1024
+
1025
+ $this->logger->debug($mailer_message);
1026
+
1027
+ $mailer = $this->get_mailer();
1028
+
1029
+ $r = $mailer->send($mailer_message);
1030
+
1031
+ return !is_wp_error($r);
1032
+ }
1033
+
1034
+ function hook_deactivate() {
1035
+ wp_clear_scheduled_hook('newsletter');
1036
+ }
1037
+
1038
+ function find_file($file1, $file2) {
1039
+ if (is_file($file1))
1040
+ return $file1;
1041
+ return $file2;
1042
+ }
1043
+
1044
+ function hook_site_transient_update_plugins($value) {
1045
+ static $extra_response = array();
1046
+
1047
+ //$this->logger->debug('Update plugins transient called');
1048
+
1049
+ if (!$value || !is_object($value)) {
1050
+ //$this->logger->info('Empty object');
1051
+ return $value;
1052
+ }
1053
+
1054
+ if (!isset($value->response) || !is_array($value->response)) {
1055
+ $value->response = array();
1056
+ }
1057
+
1058
+ // Already computed? Use it! (this filter is called many times in a single request)
1059
+ if ($extra_response) {
1060
+ //$this->logger->debug('Already updated');
1061
+ $value->response = array_merge($value->response, $extra_response);
1062
+ return $value;
1063
+ }
1064
+
1065
+ $extensions = $this->getTnpExtensions();
1066
+
1067
+ // Ops...
1068
+ if (!$extensions) {
1069
+ return $value;
1070
+ }
1071
+
1072
+ foreach ($extensions as $extension) {
1073
+ unset($value->response[$extension->wp_slug]);
1074
+ unset($value->no_update[$extension->wp_slug]);
1075
+ }
1076
+
1077
+ // Someone doesn't want our addons updated, let respect it (this constant should be defined in wp-config.php)
1078
+ if (!NEWSLETTER_EXTENSION_UPDATE) {
1079
+ //$this->logger->info('Updates disabled');
1080
+ return $value;
1081
+ }
1082
+
1083
+ include_once(ABSPATH . 'wp-admin/includes/plugin.php');
1084
+
1085
+ // Ok, that is really bad (should we remove it? is there a minimum WP version?)
1086
+ if (!function_exists('get_plugin_data')) {
1087
+ //$this->logger->error('No get_plugin_data function available!');
1088
+ return $value;
1089
+ }
1090
+
1091
+ $license_key = $this->get_license_key();
1092
+
1093
+ // Here we prepare the update information BUT do not add the link to the package which is privided
1094
+ // by our Addons Manager (due to WP policies)
1095
+ foreach ($extensions as $extension) {
1096
+
1097
+ // Patch for names convention
1098
+ $extension->plugin = $extension->wp_slug;
1099
+
1100
+ //$this->logger->debug('Processing ' . $extension->plugin);
1101
+ //$this->logger->debug($extension);
1102
+
1103
+ $plugin_data = false;
1104
+ if (file_exists(WP_PLUGIN_DIR . '/' . $extension->plugin)) {
1105
+ $plugin_data = get_plugin_data(WP_PLUGIN_DIR . '/' . $extension->plugin, false, false);
1106
+ } else if (file_exists(WPMU_PLUGIN_DIR . '/' . $extension->plugin)) {
1107
+ $plugin_data = get_plugin_data(WPMU_PLUGIN_DIR . '/' . $extension->plugin, false, false);
1108
+ }
1109
+
1110
+ if (!$plugin_data) {
1111
+ //$this->logger->debug('Seems not installed');
1112
+ continue;
1113
+ }
1114
+
1115
+ $plugin = new stdClass();
1116
+ $plugin->id = $extension->id;
1117
+ $plugin->slug = $extension->slug;
1118
+ $plugin->plugin = $extension->plugin;
1119
+ $plugin->new_version = $extension->version;
1120
+ $plugin->url = $extension->url;
1121
+ if (class_exists('NewsletterExtensions')) {
1122
+ // NO filters here!
1123
+ $plugin->package = NewsletterExtensions::$instance->get_package($extension->id, $license_key);
1124
+ } else {
1125
+ $plugin->package = '';
1126
+ }
1127
+ // [banners] => Array
1128
+ // (
1129
+ // [2x] => https://ps.w.org/wp-rss-aggregator/assets/banner-1544x500.png?rev=2040548
1130
+ // [1x] => https://ps.w.org/wp-rss-aggregator/assets/banner-772x250.png?rev=2040548
1131
+ // )
1132
+ // [icons] => Array
1133
+ // (
1134
+ // [2x] => https://ps.w.org/advanced-custom-fields/assets/icon-256x256.png?rev=1082746
1135
+ // [1x] => https://ps.w.org/advanced-custom-fields/assets/icon-128x128.png?rev=1082746
1136
+ // )
1137
+ if (version_compare($extension->version, $plugin_data['Version']) > 0) {
1138
+ //$this->logger->debug('There is a new version');
1139
+ $extra_response[$extension->plugin] = $plugin;
1140
+ } else {
1141
+ // Maybe useless...
1142
+ //$this->logger->debug('There is NOT a new version');
1143
+ $value->no_update[$extension->plugin] = $plugin;
1144
+ }
1145
+ //$this->logger->debug('Added');
1146
+ }
1147
+
1148
+ $value->response = array_merge($value->response, $extra_response);
1149
+
1150
+ return $value;
1151
+ }
1152
+
1153
+ /**
1154
+ * @deprecated since version 6.1.9
1155
+ */
1156
+ function get_extension_version($extension_id) {
1157
+ return null;
1158
+ }
1159
+
1160
+ /**
1161
+ * @deprecated since version 6.1.9
1162
+ */
1163
+ function set_extension_update_data($value, $extension) {
1164
+ return $value;
1165
+ }
1166
+
1167
+ /**
1168
+ * Retrieve the extensions form the tnp site
1169
+ * @return array
1170
+ */
1171
+ function getTnpExtensions() {
1172
+
1173
+ $extensions_json = get_transient('tnp_extensions_json');
1174
+
1175
+ if (empty($extensions_json)) {
1176
+ $url = "http://www.thenewsletterplugin.com/wp-content/extensions.json?ver=" . NEWSLETTER_VERSION;
1177
+ $extensions_response = wp_remote_get($url);
1178
+
1179
+ if (is_wp_error($extensions_response)) {
1180
+ // Cache anyway for blogs which cannot connect outside
1181
+ $extensions_json = '[]';
1182
+ set_transient('tnp_extensions_json', $extensions_json, 72 * 60 * 60);
1183
+ $this->logger->error($extensions_response);
1184
+ } else {
1185
+
1186
+ $extensions_json = wp_remote_retrieve_body($extensions_response);
1187
+
1188
+ // Not clear cases
1189
+ if (empty($extensions_json) || !json_decode($extensions_json)) {
1190
+ $this->logger->error('Invalid json from thenewsletterplugin.com: retrying in 72 hours');
1191
+ $this->logger->error('JSON: ' . $extensions_json);
1192
+ $extensions_json = '[]';
1193
+ }
1194
+ set_transient('tnp_extensions_json', $extensions_json, 72 * 60 * 60);
1195
+ }
1196
+ }
1197
+
1198
+ $extensions = json_decode($extensions_json);
1199
+
1200
+ return $extensions;
1201
+ }
1202
+
1203
+ function clear_extensions_cache() {
1204
+ delete_transient('tnp_extensions_json');
1205
+ }
1206
+
1207
+ var $panels = array();
1208
+
1209
+ function add_panel($key, $panel) {
1210
+ if (!isset($this->panels[$key]))
1211
+ $this->panels[$key] = array();
1212
+ if (!isset($panel['id']))
1213
+ $panel['id'] = sanitize_key($panel['label']);
1214
+ $this->panels[$key][] = $panel;
1215
+ }
1216
+
1217
+ function has_license() {
1218
+ return !empty($this->options['contract_key']);
1219
+ }
1220
+
1221
+ function get_sender_name() {
1222
+ return $this->options['sender_name'];
1223
+ }
1224
+
1225
+ function get_sender_email() {
1226
+ return $this->options['sender_email'];
1227
+ }
1228
+
1229
+ /**
1230
+ *
1231
+ * @return int
1232
+ */
1233
+ function get_newsletter_page_id() {
1234
+ return (int) $this->options['page'];
1235
+ }
1236
+
1237
+ /**
1238
+ *
1239
+ * @return WP_Post
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
+
1248
+ /**
1249
+ * Returns the Newsletter dedicated page URL or an alternative URL if that page if not
1250
+ * configured or not available.
1251
+ *
1252
+ * @staticvar string $url
1253
+ * @return string
1254
+ */
1255
+ function get_newsletter_page_url($language = '') {
1256
+
1257
+ $page = $this->get_newsletter_page();
1258
+
1259
+ if (!$page || $page->post_status !== 'publish') {
1260
+ return $this->build_action_url('m');
1261
+ }
1262
+
1263
+ $newsletter_page_url = get_permalink($page->ID);
1264
+ if ($language && $newsletter_page_url) {
1265
+ if (class_exists('SitePress')) {
1266
+ $newsletter_page_url = apply_filters('wpml_permalink', $newsletter_page_url, $language, true);
1267
+ }
1268
+ if (function_exists('pll_get_post')) {
1269
+ $translated_page = get_permalink(pll_get_post($page->ID, $language));
1270
+ if ($translated_page) {
1271
+ $newsletter_page_url = $translated_page;
1272
+ }
1273
+ }
1274
+ }
1275
+
1276
+ return $newsletter_page_url;
1277
+ }
1278
+
1279
+ function get_license_key() {
1280
+ if (defined('NEWSLETTER_LICENSE_KEY')) {
1281
+ return NEWSLETTER_LICENSE_KEY;
1282
+ } else {
1283
+ if (!empty($this->options['contract_key'])) {
1284
+ return trim($this->options['contract_key']);
1285
+ }
1286
+ }
1287
+ return false;
1288
+ }
1289
+
1290
+ /**
1291
+ * Get the data connected to the specified license code on man settings.
1292
+ *
1293
+ * - false if no license is present
1294
+ * - WP_Error if something went wrong if getting the license data
1295
+ * - object with expiration and addons list
1296
+ *
1297
+ * @param boolean $refresh
1298
+ * @return \WP_Error|boolean|object
1299
+ */
1300
+ function get_license_data($refresh = false) {
1301
+
1302
+ $this->logger->debug('Getting license data');
1303
+
1304
+ $license_key = $this->get_license_key();
1305
+ if (empty($license_key)) {
1306
+ $this->logger->debug('License was empty');
1307
+ delete_transient('newsletter_license_data');
1308
+ return false;
1309
+ }
1310
+
1311
+ if (!$refresh) {
1312
+ $license_data = get_transient('newsletter_license_data');
1313
+ if ($license_data !== false && is_object($license_data)) {
1314
+ $this->logger->debug('License data found on cache');
1315
+ return $license_data;
1316
+ }
1317
+ }
1318
+
1319
+ $this->logger->debug('Refreshing the license data');
1320
+
1321
+ $license_data_url = 'https://www.thenewsletterplugin.com/wp-content/plugins/file-commerce-pro/get-license-data.php';
1322
+
1323
+ $response = wp_remote_post($license_data_url, array(
1324
+ 'body' => array('k' => $license_key)
1325
+ ));
1326
+
1327
+ // Fall back to http...
1328
+ if (is_wp_error($response)) {
1329
+ $this->logger->error($response);
1330
+ $this->logger->error('Falling back to http');
1331
+ $license_data_url = str_replace('https', 'http', $license_data_url);
1332
+ $response = wp_remote_post($license_data_url, array(
1333
+ 'body' => array('k' => $license_key)
1334
+ ));
1335
+ if (is_wp_error($response)) {
1336
+ $this->logger->error($response);
1337
+ set_transient('newsletter_license_data', $response, DAY_IN_SECONDS);
1338
+ return $response;
1339
+ }
1340
+ }
1341
+
1342
+ $download_message = 'You can download all addons from www.thenewsletterplugin.com if your license is valid.';
1343
+
1344
+ if (wp_remote_retrieve_response_code($response) != '200') {
1345
+ $this->logger->error('license data error: ' . wp_remote_retrieve_response_code($response));
1346
+ $data = new WP_Error(wp_remote_retrieve_response_code($response), 'License validation service error. <br>' . $download_message);
1347
+ set_transient('newsletter_license_data', $data, DAY_IN_SECONDS);
1348
+ return $data;
1349
+ }
1350
+
1351
+ $json = wp_remote_retrieve_body($response);
1352
+ $data = json_decode($json);
1353
+
1354
+ if (!is_object($data)) {
1355
+ $this->logger->error($json);
1356
+ $data = new WP_Error(1, 'License validation service error. <br>' . $download_message);
1357
+ set_transient('newsletter_license_data', $data, DAY_IN_SECONDS);
1358
+ return $data;
1359
+ }
1360
+
1361
+ if (isset($data->message)) {
1362
+ $data = new WP_Error(1, $data->message . ' (check the license on Newsletter main settings)');
1363
+ set_transient('newsletter_license_data', $data, DAY_IN_SECONDS);
1364
+ return $data;
1365
+ }
1366
+
1367
+ $expiration = WEEK_IN_SECONDS;
1368
+ // If the license expires in few days, make the transient live only few days, so it will be refreshed
1369
+ if ($data->expire > time() && $data->expire - time() < WEEK_IN_SECONDS) {
1370
+ $expiration = $data->expire - time();
1371
+ }
1372
+ set_transient('newsletter_license_data', $data, $expiration);
1373
+
1374
+ return $data;
1375
+ }
1376
+
1377
+ /**
1378
+ * @deprecated
1379
+ * @param type $license_key
1380
+ * @return \WP_Error
1381
+ */
1382
+ public static function check_license($license_key) {
1383
+ $response = wp_remote_get('http://www.thenewsletterplugin.com/wp-content/plugins/file-commerce-pro/check.php?k=' . urlencode($license_key), array('sslverify' => false));
1384
+ if (is_wp_error($response)) {
1385
+ /* @var $response WP_Error */
1386
+ return new WP_Error(-1, 'It seems that your blog cannot contact the license validator. Ask your provider to unlock the HTTP/HTTPS connections to www.thenewsletterplugin.com<br>'
1387
+ . esc_html($response->get_error_code()) . ' - ' . esc_html($response->get_error_message()));
1388
+ } else if ($response['response']['code'] != 200) {
1389
+ return new WP_Error(-1, '[' . $response['response']['code'] . '] The license seems expired or not valid, please check your <a href="https://www.thenewsletterplugin.com/account">license code and status</a>, thank you.'
1390
+ . '<br>You can anyway download the professional extension from https://www.thenewsletterplugin.com.');
1391
+ } elseif ($expires = json_decode(wp_remote_retrieve_body($response))) {
1392
+ return array('expires' => $expires->expire, 'message' => 'Your license is valid and expires on ' . esc_html(date('Y-m-d', $expires->expire)));
1393
+ } else {
1394
+ return new WP_Error(-1, 'Unable to detect the license expiration. Debug data to report to the support: <code>' . esc_html(wp_remote_retrieve_body($response)) . '</code>');
1395
+ }
1396
+ }
1397
+
1398
+ function add_notice_to_chosen_profile_page_hook($post_states, $post) {
1399
+
1400
+ if ($post->ID == $this->options['page']) {
1401
+ $post_states[] = __('Newsletter plugin page, do not delete', 'newsletter');
1402
+ }
1403
+
1404
+ return $post_states;
1405
+ }
1406
+
1407
+ }
1408
+
1409
+ $newsletter = Newsletter::instance();
1410
+
1411
+ if (is_admin()) {
1412
+ require_once NEWSLETTER_DIR . '/system/system.php';
1413
+ }
1414
+
1415
+ require_once NEWSLETTER_DIR . '/subscription/subscription.php';
1416
+ require_once NEWSLETTER_DIR . '/unsubscription/unsubscription.php';
1417
+ require_once NEWSLETTER_DIR . '/profile/profile.php';
1418
+ require_once NEWSLETTER_DIR . '/emails/emails.php';
1419
+ require_once NEWSLETTER_DIR . '/users/users.php';
1420
+ require_once NEWSLETTER_DIR . '/statistics/statistics.php';
1421
+ require_once NEWSLETTER_DIR . '/widget/standard.php';
1422
+ require_once NEWSLETTER_DIR . '/widget/minimal.php';
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,456 +1,470 @@
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.5
5
- Contributors: satollo,webagile,michael-travan
6
- License: GPLv2 or later
7
- License URI: https://www.gnu.org/licenses/gpl-2.0.html
8
-
9
- An email marketing tool for your blog: subscription forms to create your lists with unlimited subscribers and newsletters.
10
-
11
- == Description ==
12
-
13
- Newsletter is a **real newsletter and email marketing system** for your WordPress blog: perfect for list building, you can easily create, send and track e-mails, headache-free. It just works out of box!
14
-
15
- = Discover a completely rewritten composer =
16
-
17
- We redesigned our drag and drop composer to make your campaign creation even easier. Try it!
18
-
19
- = Main Features =
20
-
21
- * **Easy-to-use Drag and drop composer** to build responsive newsletters
22
- * **Unlimited subscribers** with statistics
23
- * **Unlimited newsletters** with tracking
24
- * **Subscription spam check** with domain/ip black lists, Akismet, captcha
25
- * **Delivery speed** fine control (from 12 emails per hour to as much as your blog can manage)
26
- * [WPML ready](https://www.thenewsletterplugin.com/documentation/multilanguage), [Polylang ready](https://www.thenewsletterplugin.com/documentation/multilanguage), [Translatepress ready](https://www.thenewsletterplugin.com/documentation/multilanguage)
27
- * All messages are **fully translatable** from administration panels (no .po/.mo file to edit)
28
- * [GDPR ready](https://www.thenewsletterplugin.com/documentation/gdpr-compliancy)
29
- * **Advanced targeting** with lists combinations like all in, at least one, not in and so on
30
- * Customizable **subscription widget**, **page** or **custom form**
31
- * Wordpress Users registration **seamless integration**
32
- * **Single** And **Double Opt-In** plus privacy checkbox for EU laws compliance
33
- * **Subscribers lists** to fine-target your campaigns
34
- * PHP API and REST API for coders and integrations
35
- * SMTP-Ready (with free addon)
36
- * Customizable Themes
37
- * **Status panel** to check your blog mailing capability and configuration
38
- * **Compatible with every SMTP plugin**: Post SMTP (aka Postman), WP Mail SMTP, Easy WP SMTP, Easy SMTP Mail, WP Mail Bank, ...
39
- * **Subscribers import** from file
40
- * Newsletter with Html and Text message versions
41
-
42
- = Find Us =
43
-
44
- Newsletter is a continuously evolving plugin. Stay tuned following us on [Facebook](https://www.facebook.com/thenewsletterplugin/) or [our site](https://www.thenewsletterplugin.com/).
45
-
46
- = Free Addons =
47
-
48
- Improve The Newsletter Plugin with these free addons:
49
-
50
- * [WP Registration Addon](https://www.thenewsletterplugin.com/documentation/wpusers-extension) - connects the WordPress standard and custom registration with Newsletter subscription. Optionally imports all registered users as subscribers.
51
- * [Archive Addon](https://www.thenewsletterplugin.com/documentation/archive-extension) - creates a simple blog page which lists all your sent newsletters
52
- * [Locked Content Addon](https://www.thenewsletterplugin.com/documentation/locked-content-extension) - open up your premium content only after subscription
53
- * [Newsletter REST API Addon](https://www.thenewsletterplugin.com/documentation/developers/newsletter-api-2/) - adds a tier of REST api to integrate with the Newsletter core services
54
- * [Sendinblue Addon](https://www.thenewsletterplugin.com/documentation/addons/delivery-addons/sendinblue-extension/) - deliver your newsletters with Sendinblue
55
- * [SMTP Addon](https://www.thenewsletterplugin.com/documentation/addons/delivery-addons/smtp-extension/) - deliver your newsletters with external SMTP
56
- * [Advanced Import Addon](https://www.thenewsletterplugin.com/documentation/addons/extended-features/advanced-import/) - import contact from file or copy and paste data with full mapping
57
-
58
- (*easily add them from our [Addons panel](https://www.thenewsletterplugin.com/documentation/install-extensions)*)
59
-
60
- = Addons on WordPress.org =
61
-
62
- * [RSS Composer Block](https://wordpress.org/plugins/newsletter-rss-block/) - (3rd party) a composer block which builds its content from a RSS feed
63
- * [Popup Maker Integration](https://wordpress.org/plugins/newsletter-popupmaker/) - (3rd party) integration of Newsletter forms with Popup Maker plugin
64
- * [BuddyPress integration](https://wordpress.org/plugins/newsletter-buddypress/) - subscription opt-in inside BuddyPress signup form
65
- * [WP User Manager addon for Newsletter](https://wordpress.org/plugins/wpum-newsletter/) - adds the subscription option on registration forms
66
- * [Plaintext Generator](https://wordpress.org/plugins/plaintext-newsletter/) - generates the plaintext version from an HTML newsletter
67
-
68
- = Professional Addons =
69
-
70
- Need *more power*? Feel *something's missing*? The Newsletter Plugin features can be easily extended through our **premium, professional Addons**! Let us introduce just two of them : )
71
-
72
- * [Automated](https://www.thenewsletterplugin.com/automated) - generates and sends your newsletters using your blog last posts, even custom ones like events or products. Just sit and watch!
73
- * [Autoresponder](https://www.thenewsletterplugin.com/autoresponder) - creates email series to follow up your subscribers
74
- * [Extended Composer Blocks](https://www.thenewsletterplugin.com/composer) - adds new blocks to the drag & drop composer
75
- * [WooCommerce Integration](https://www.thenewsletterplugin.com/woocommerce) - subscribe customers to a mailing list and generate product newletters.
76
- * [Reports](https://www.thenewsletterplugin.com/reports) - improves the internal statistics collection system and provides better reports of data collected for each sent email. And retargeting. Neat.
77
- * [Leads](https://www.thenewsletterplugin.com/leads) adds a fancy subscription popup box or a fixed bar to your website that will boost your conversion rate
78
- * [Amazon SES and other mail providers integration](https://www.thenewsletterplugin.com/integrations) - seamlessly integrate Amazon SES and other email service providers with The Newsletter Plugin. Hassle-free.
79
- * [Contact Form 7 Integration](https://www.thenewsletterplugin.com/documentation/contact-form-7-extension) - integrate the subscription on Contact Form 7 forms
80
- * [Ninja Forms Integration](https://www.thenewsletterplugin.com/documentation/ninjaforms-extension) - integrate the subscription on Ninja Forms
81
- * [WP Forms Integration](https://www.thenewsletterplugin.com/documentation/wpforms-extension) - integrate the subscription on WP Forms
82
- * Events Manager and The Events Calendar (By Modern Tribe) integrations - easily add events to your newsletters
83
- * [Google Analytics](https://www.thenewsletterplugin.com/google-analytics) - track newsletter links with Google UTM tracking paramaters
84
- * [Subscribe on Comment](https://www.thenewsletterplugin.com/documentation/comments-extension) - adds the subscription option to your blog comment form
85
- * [Geolocation](https://www.thenewsletterplugin.com/documentation/geolocation-extension) - adds geolocation capability to target subscribers by location
86
-
87
- = GDPR =
88
-
89
- The Newsletter Plugin provides all the technical tools needed to achieve GDPR compliancy and we're continuously working to improve them and to give support even for specific use cases.
90
- The plugin does not collect users' own subscribers data, nor it has any access to those data: hence, we are not a data processor, so a data processing agreement is not needed.
91
- Anyway if you configure the plugin to use external services (usually an external mail delivery service) you should check with that service if some sort of agreement is required.
92
-
93
- = Support =
94
-
95
- We provide support for our plugin on [Wordpress.org forums](https://wordpress.org/support/plugin/newsletter) and through our [official forum](https://www.thenewsletterplugin.com/forums).
96
-
97
- Premium Users with an active license have access to one-to-one support via our [ticketing system](https://www.thenewsletterplugin.com/support-ticket).
98
-
99
- = Developers =
100
-
101
- We have a [documentation section](https://www.thenewsletterplugin.com/documentation/developers/) dedicated to who want to develop with Newsletter.
102
-
103
- You can find us on [GitHub](https://github.com/TheNewsletterPlugin) with some examples of addons.
104
-
105
- = Follow Us =
106
-
107
- * **Our Official Website** - [https://www.thenewsletterplugin.com/](https://www.thenewsletterplugin.com/)
108
- * **GitHub** - [https://github.com/TheNewsletterPlugin](https://github.com/TheNewsletterPlugin)
109
- * **LinkedIn** - [https://www.linkedin.com/company/the-newsletter-plugin](https://www.linkedin.com/company/the-newsletter-plugin)
110
- * **Our Facebook Page** - [https://www.facebook.com/thenewsletterplugin](https://www.facebook.com/thenewsletterplugin)
111
- * **Our Twitter Account** - [https://twitter.com/newsletterwp](https://twitter.com/newsletterwp)
112
-
113
- == Frequently Asked Questions ==
114
-
115
- See the [Newsletter Forum](https://www.thenewsletterplugin.com/forums) to ask for help.
116
-
117
- For documentation start from [Newsletter documentation](https://www.thenewsletterplugin.com/documentation).
118
-
119
- Thank you, The Newsletter Team
120
-
121
- == Screenshots ==
122
-
123
- 1. The responsive email Drag & Drop composer
124
- 2. The plugin dashboard
125
- 3. The Reports extension
126
-
127
- == Changelog ==
128
-
129
- = 7.4.5 =
130
-
131
- * Fixed image block font-size to show the alt text (not for Outlook, it never shows the alt text)
132
- * XSS security fix
133
-
134
- = 7.4.4 =
135
-
136
- * Changed the dedicated page retrieval to intercept misconfigurations
137
- * Added option to accept repeated subscription in single opt-in (you should check if it is compatible with your privacy regulation)
138
- * Posts field selector keep now track of the previous post selected even if no more in list
139
- * Improved the error management when delivery fails using the WP native mailing function
140
- * Added title filter on posts block for compatibility with WPGlobus
141
-
142
- = 7.4.3 =
143
-
144
- * Removed obsolete note about the newsletter textual part
145
- * Introduced text part generator right now only for the Composer (with permission of Frank M.)
146
- * Added check on System>Status for images with relative URL
147
- * Addec check for Freesoul Deactivate Plugins
148
- * Minimal for layout now consider the button_label attribute
149
-
150
- = 7.4.2 =
151
-
152
- * Fixed the post date (regression)
153
- * Added link to the automatic plaintext generator plugin by franciscus
154
- * Possible fix for our Gutenberg block (sometimes) not working
155
- * Added uoloads dir and url on System>Status panel
156
- * Fixed Status panel error when a newsletter is in "error" status
157
- * Added default width to the logo on header block
158
-
159
- = 7.4.1 =
160
-
161
- * Added check on invalid media on two columns post type
162
- * WP 5.9.2 compatibility check
163
-
164
- = 7.4.0 =
165
-
166
- * Added privacy links
167
- * Added filter "newsletter_subscription" (documentation is under writing)
168
-
169
- = 7.3.9 =
170
-
171
- * Fixed grid layout not showing correctly on gmail
172
- * Fixed notice on widget page about the wrong editor enqueued
173
- * Removed wrong top bar warning
174
- * Fixed block options on posts block
175
- * Fixed padding on two column posts block
176
-
177
- = 7.3.8 =
178
-
179
- * Fixed graph scale on System/Scheduler panel
180
- * Fixed untraslated labels on subscriber management panels
181
- * Fixed the "toggle" private/public status on sent newsletter
182
- * Removed the "action call test" from the status panel since it does not work with some providers but does not affect the correct working of Newsletter
183
- * Added developer information on this readme
184
-
185
- = 7.3.7 =
186
-
187
- * Fixed unwanted redirects on subscription errors
188
- * Fixed composer page HTML
189
- * Minor fixes on PHP, CSS
190
- * Fixed notice on image block
191
-
192
- = 7.3.6 =
193
-
194
- * Improved composer reusability in other contexts
195
- * Removed obsolete composer code
196
- * Fixed default tracking for old theme-based neewsletters
197
- * Forced enconding on export (attempt)
198
- * WP 5.9 check
199
-
200
- = 7.3.5 =
201
-
202
- * WP 5.8.3 compatibility check
203
- * Fixed 2021 max year in date picker
204
- * Typos
205
-
206
- = 7.3.4 =
207
-
208
- * Fixed delivery fatal error management
209
- * Fixed link to the schduler dianostica panel
210
-
211
- = 7.3.3 =
212
-
213
- * Added "complained" status to subscriber filters
214
- * Fixed some links bringing to a "not allowed" page
215
- * Fixed a notice on delivery diagnostic page
216
- * Fixed logo width notice on header block
217
-
218
- = 7.3.2 =
219
-
220
- * Fixed the remote ip retrieval and clean up
221
- * Fixed header link to status page
222
- * Fixed database error with too long IPs
223
- * Fixed the subscription of cancelled addresses
224
- * Fixed sender and name customization
225
-
226
- = 7.3.1 =
227
-
228
- * Dropped old mailers support
229
- * Improved sending process and limits checking
230
- * Removed obsolete notifications
231
- * Fixed the wrong report on single email delivery speed
232
- * Added support for custom sending speed by addons
233
- * Improved excerpt generation (but it still depends on plugins and themes...)
234
- * Support for building button option on composer blocks
235
-
236
- = 7.3.0 =
237
-
238
- * Fixed header block layout with (logo only layout)
239
- * Check for conflicts on newsletter saving
240
- * Added the subscriber complained status (actually not managed automatically)
241
-
242
- = 7.2.9 =
243
-
244
- * Fixed generic action button confirmation popup not showing the message
245
- * [SECURITY] Pre parsing of IP addresses on security panel
246
- * [SECURITY] Comments allowed on IP address list on security panel
247
- * [COMPOSER] Fixed preset name/subject
248
- * Improved generated forms for accessibility
249
-
250
- = 7.2.8 =
251
-
252
- * Fixed the print_date() when no time is provided
253
- * Fixed date alignment on posts block
254
- * Folders reorganization
255
- * Social block with a new set of icons
256
- * Boosted performances of the lists management page (for big databases)
257
- * Added a new scheduler diagnostic panel
258
- * Seriously improved the cron statistics and diagnostic panel (check it out!)
259
- * Removed obsolete migration code from ancient versions
260
-
261
- = 7.2.7 =
262
-
263
- * Fixed JS error on composer sometimes preventing the correct initialization
264
-
265
- = 7.2.6 =
266
-
267
- * Fixed links on test emails sent to a free email address
268
- * Added attachment explanation
269
- * Added link to explain the use of the snippet
270
-
271
- = 7.2.5 =
272
-
273
- * Fixed subject not saved under specific circumstance
274
-
275
- = 7.2.4 =
276
-
277
- * Fixed the composer not starting for blog with SSL plugin but still HTTP configured on main WP settings
278
- * Changed labels on subscriber maintenance panel
279
- * Updated requirements for WP version
280
-
281
- = 7.2.3 =
282
-
283
- * [COMPOSER] Added approx. indicators of the subsject visibile part in Apple and Android clients (experimental)
284
- * [COMPOSER] New mobile version view directly while composing (experimental)
285
- * [COMPOSER] New test email to test subscribers or to specific email address
286
- * [COMPOSER] Fixed missing background when creating a new message from a preset
287
- * [COMPOSER] Added media selector to the CTA block
288
- * [COMPOSER] Added reference to the tags on HTML and Text blocks
289
- * [COMPOSER] Move the snippet (preheader) field near the subject
290
- * [COMPOSER] Footer block with three link options: unsubscribe, manage and view online
291
- * [COMPOSER] Improve font coherence between blocks (by default)
292
- * [ANTISPAM] Improved the antispam checks on subscription
293
- * [GENERAL] Removed obsolete folders and code
294
- * [NEWSLETTERS] Refactored subject ideas selector
295
- * [SUBSCRIPTION] Inverted extra profile fields and lists on standard subscription form
296
- * [GENERAL] IP address extracted checking proxy variables
297
- * [GENERAL] Improved sending stats collection and display for the delivery engine (not related to click/open stats)
298
-
299
- = 7.2.2 =
300
-
301
- * [COMPOSER] Posts block excerpt removed when set to 0-length
302
- * [GENERAL]Added special characters on test message
303
- * [GENERAL]Added more specific error for action calls on System/Status panel
304
- * [SUBSCRIPTION] Check for the _wp_amp_action_xhr_converted parameter by AMP plugin
305
-
306
- = 7.2.1 =
307
-
308
- * [GENERAL] Added more detailed admin logging
309
- * [NEWSLETTERS] Fixed scheduled date sometimes reset to 1/1/1970
310
-
311
- = 7.2.0 =
312
-
313
- * [PROFILE] Fixed activation email on profile change
314
- * [NEWSLETTERS] Extended year selection on newsletter scheduling
315
- * [DELIVERY] Breaking change: old enqueue() and flush() methods have been removed
316
- * [GENERAL] Fixed alert message on some buttons
317
- * [SUBSCRIPTION] Fixed error message on multiple subscriptions (when not allowed)
318
- * [GENERAL] Fixed erratic error log line on main log
319
-
320
- = 7.1.9 =
321
-
322
- * [GENERAL] Removed the encodign defatlt to Base 64 when not specified since it seems incompatible with some SMTP plugins
323
- * [GENERAL] Review some controls layout and behavior
324
- * [DEBUG] Improved logging on database errors
325
- * [GENERAL] Added TikTok, Discord and Twitch socials
326
- * [GENERAL] Fixed odd error reported related to the cron call statistics collection
327
- * [DEBUG] Added a delivery diagnostic panel
328
-
329
- = 7.1.8 =
330
-
331
- * [COMPOSER] Fixed alignment of single big image on Outlook Android
332
-
333
- = 7.1.7 =
334
-
335
- * [GENERAL] Fix of permalink onm email with multilanguage plugins
336
-
337
- = 7.1.6 =
338
-
339
- * [COMPOSER] Fixed one column big image Read more links
340
-
341
- = 7.1.5 =
342
-
343
- * [COMPOSER] Improve buttons on posts layout
344
- * [COMPOSER] Improved header layout and logo
345
- * [COMPOSER] Generally improved block spacing
346
-
347
- = 7.1.4 =
348
-
349
- * [COMPOSER] Fixed image block for Outlook
350
- * [GENERAL] Fix undefined values in Gutenberg block
351
-
352
- = 7.1.3 =
353
-
354
- * [COMPOSER] Improvements on blocks layout compatibility
355
- * [COMPOSER] Fixed preset global options saving
356
- * [COMPOSER] Content regeneration on preset selection
357
- * [GENERAL] Added to System menu the Site Health link, a not well known native page of WordPress with system information
358
- * [GENERAL] Added sending statistics reset button on status panel
359
-
360
- = 7.1.2 =
361
-
362
- * [ADDONS] Fixed the addons list
363
-
364
- = 7.1.1 =
365
-
366
- * [GENERAL] Improved profile related functions
367
- * [GENERAL] Fixed check on field names (thanks Peter P.)
368
- * [COMPOSER] Fix on preset selection
369
- * [GENERAL] Fix ajax subscription on error
370
-
371
- = 7.1.0 =
372
-
373
- * [COMPOSER] Added link to tags documentation to inject subscriber's data
374
- * [COMPOSER] Fixed layout of posts block for Outlook 365
375
- * [GENERAL] Improved caching of addons json (even on error)
376
- * [GENERAL] Status menu changed to System/Status and System/Logs
377
- * [API] Fixed the subscriber status management (the API Addon should be updated as well)
378
- * [GENERAL] Fixed management of fatal errors on sending
379
- * [GENERAL] Limited error string per message to 250 chars
380
- * [GENERAL] Fixed check on field names (thanks Peter P.)
381
-
382
-
383
- = 7.0.9 =
384
-
385
- * [CAPTCHA] Fixed button label translation
386
- * [DELIVERY] Mailing fatal error management with newsletter stop and reporting
387
- * [SMTP] Marked obsolete the internal SMTP and made available the free SMTP addon
388
- * [DELIVERY] Better management of delivery fatal errors with a new "error" status for newsletters
389
- * [GENERAL] New log files panel
390
- * [IMPORT] Old import panel replaced by the new (really better) import addon (file, copy and paste, bounced addresses import)
391
- * [NEWSLETTERS] It's now possible to specify the sender name and email per newsletter (thanks to Matthew S.)
392
-
393
- = 7.0.8 =
394
-
395
- * [SUBSCRIBERS] Changed action buttons
396
- * [GENERAL] Reorganization of CSS and removal of unused files
397
- * [DASHBOARD] New window open for links and fix of invalid URLs
398
- * [NEWSLETTERS] New action buttons
399
- * [GENERAL] Minor fixes and optimizations
400
- * [COMPOSER] Fixed rare size error on gif images
401
-
402
- = 7.0.7 =
403
-
404
- * [COMPOSER] Fixed a warning in some inline editable blocks
405
- * [NEWSLETTERS] Fixed style which made list labels badly readable
406
- * [NEWSLETTERS] Fixed style which made bullet lists white (not on delivered newsletters)
407
- * [GENERAL] Added some references to the not working scheduler warning
408
-
409
- = 7.0.6 =
410
-
411
- * [COMPOSER] CTA block not grabbing settings from the "old" blocks
412
- * [COMPOSER] CSS issue on button settings group
413
- * [COMPOSER] Posts block two columns layout (author and padding)
414
- * [IMPORT] Removed the old low-featured import (the free import addon has everything needed!)
415
- * [GENERAL] Compatibility check with WP 5.7
416
-
417
- = 7.0.5 =
418
-
419
- * [COMPOSER] Hero CTA button not working
420
- * Fixed to the hero block button
421
- * Added support for the SMTP addon
422
-
423
- = 7.0.4 =
424
-
425
- * [COMPOSER] Redesigned drag and drop composer
426
- * [COMPOSER] NEW! Save drag and drop composed newsletters as templates to reuse easily
427
- * Redesigned dashboard
428
- * Several bug and fixes
429
-
430
- = 7.0.3 =
431
-
432
- * Option to choose between unsubscription and profile link in the footer block
433
- * Direct image src URL for image block
434
- * New media selector for blocks
435
- * Minor fixes
436
- * Updated codemirror
437
- * Updated default theme
438
- * Fixed tag filter on posts block (when tags have parathesis or like)
439
- * Fixed composer visualization of bullet points
440
-
441
- = 7.0.2 =
442
-
443
- * Fixed media 2x resize
444
-
445
- = 7.0.1 =
446
-
447
- * Fixed enforced lists by language with Polylang
448
-
449
- = 7.0.0 =
450
-
451
- * Added multiple newsletter selection for deletion
452
- * Added text part on welcome and activation email
453
- * Added the attribute "show_form" to "newsletter" shortcode
454
- * Added filter on subscriber saving (from external systems) with wrong list field values
455
- * Added index.html on log folder
456
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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.8
5
+ Contributors: satollo,webagile,michael-travan
6
+ License: GPLv2 or later
7
+ License URI: https://www.gnu.org/licenses/gpl-2.0.html
8
+
9
+ An email marketing tool for your blog: subscription forms to create your lists with unlimited subscribers and newsletters.
10
+
11
+ == Description ==
12
+
13
+ Newsletter is a **real newsletter and email marketing system** for your WordPress blog: perfect for list building, you can easily create, send and track e-mails, headache-free. It just works out of box!
14
+
15
+ = Discover a completely rewritten composer =
16
+
17
+ We redesigned our drag and drop composer to make your campaign creation even easier. Try it!
18
+
19
+ = Main Features =
20
+
21
+ * **Easy-to-use Drag and drop composer** to build responsive newsletters
22
+ * **Unlimited subscribers** with statistics
23
+ * **Unlimited newsletters** with tracking
24
+ * **Subscription spam check** with domain/ip black lists, Akismet, captcha
25
+ * **Delivery speed** fine control (from 12 emails per hour to as much as your blog can manage)
26
+ * [WPML ready](https://www.thenewsletterplugin.com/documentation/multilanguage), [Polylang ready](https://www.thenewsletterplugin.com/documentation/multilanguage), [Translatepress ready](https://www.thenewsletterplugin.com/documentation/multilanguage)
27
+ * All messages are **fully translatable** from administration panels (no .po/.mo file to edit)
28
+ * [GDPR ready](https://www.thenewsletterplugin.com/documentation/gdpr-compliancy)
29
+ * **Advanced targeting** with lists combinations like all in, at least one, not in and so on
30
+ * Customizable **subscription widget**, **page** or **custom form**
31
+ * Wordpress Users registration **seamless integration**
32
+ * **Single** And **Double Opt-In** plus privacy checkbox for EU laws compliance
33
+ * **Subscribers lists** to fine-target your campaigns
34
+ * PHP API and REST API for coders and integrations
35
+ * SMTP-Ready (with free addon)
36
+ * Customizable Themes
37
+ * **Status panel** to check your blog mailing capability and configuration
38
+ * **Compatible with every SMTP plugin**: Post SMTP (aka Postman), WP Mail SMTP, Easy WP SMTP, Easy SMTP Mail, WP Mail Bank, ...
39
+ * **Subscribers import** from file
40
+ * Newsletter with Html and Text message versions
41
+
42
+ = Find Us =
43
+
44
+ Newsletter is a continuously evolving plugin. Stay tuned following us on [Facebook](https://www.facebook.com/thenewsletterplugin/) or [our site](https://www.thenewsletterplugin.com/).
45
+
46
+ = Free Addons =
47
+
48
+ Improve The Newsletter Plugin with these free addons:
49
+
50
+ * [WP Registration Addon](https://www.thenewsletterplugin.com/documentation/wpusers-extension) - connects the WordPress standard and custom registration with Newsletter subscription. Optionally imports all registered users as subscribers.
51
+ * [Archive Addon](https://www.thenewsletterplugin.com/documentation/archive-extension) - creates a simple blog page which lists all your sent newsletters
52
+ * [Locked Content Addon](https://www.thenewsletterplugin.com/documentation/locked-content-extension) - open up your premium content only after subscription
53
+ * [Newsletter REST API Addon](https://www.thenewsletterplugin.com/documentation/developers/newsletter-api-2/) - adds a tier of REST api to integrate with the Newsletter core services
54
+ * [Sendinblue Addon](https://www.thenewsletterplugin.com/documentation/addons/delivery-addons/sendinblue-extension/) - deliver your newsletters with Sendinblue
55
+ * [SMTP Addon](https://www.thenewsletterplugin.com/documentation/addons/delivery-addons/smtp-extension/) - deliver your newsletters with external SMTP
56
+ * [Advanced Import Addon](https://www.thenewsletterplugin.com/documentation/addons/extended-features/advanced-import/) - import contact from file or copy and paste data with full mapping
57
+
58
+ (*easily add them from our [Addons panel](https://www.thenewsletterplugin.com/documentation/install-extensions)*)
59
+
60
+ = Addons on WordPress.org =
61
+
62
+ * [RSS Composer Block](https://wordpress.org/plugins/newsletter-rss-block/) - (3rd party) a composer block which builds its content from a RSS feed
63
+ * [Popup Maker Integration](https://wordpress.org/plugins/newsletter-popupmaker/) - (3rd party) integration of Newsletter forms with Popup Maker plugin
64
+ * [BuddyPress integration](https://wordpress.org/plugins/newsletter-buddypress/) - subscription opt-in inside BuddyPress signup form
65
+ * [WP User Manager addon for Newsletter](https://wordpress.org/plugins/wpum-newsletter/) - adds the subscription option on registration forms
66
+ * [Plaintext Generator](https://wordpress.org/plugins/plaintext-newsletter/) - generates the plaintext version from an HTML newsletter
67
+
68
+ = Professional Addons =
69
+
70
+ Need *more power*? Feel *something's missing*? The Newsletter Plugin features can be easily extended through our **premium, professional Addons**! Let us introduce just two of them : )
71
+
72
+ * [Automated](https://www.thenewsletterplugin.com/automated) - generates and sends your newsletters using your blog last posts, even custom ones like events or products. Just sit and watch!
73
+ * [Autoresponder](https://www.thenewsletterplugin.com/autoresponder) - creates email series to follow up your subscribers
74
+ * [Extended Composer Blocks](https://www.thenewsletterplugin.com/composer) - adds new blocks to the drag & drop composer
75
+ * [WooCommerce Integration](https://www.thenewsletterplugin.com/woocommerce) - subscribe customers to a mailing list and generate product newletters.
76
+ * [Reports](https://www.thenewsletterplugin.com/reports) - improves the internal statistics collection system and provides better reports of data collected for each sent email. And retargeting. Neat.
77
+ * [Leads](https://www.thenewsletterplugin.com/leads) adds a fancy subscription popup box or a fixed bar to your website that will boost your conversion rate
78
+ * [Amazon SES and other mail providers integration](https://www.thenewsletterplugin.com/integrations) - seamlessly integrate Amazon SES and other email service providers with The Newsletter Plugin. Hassle-free.
79
+ * [Contact Form 7 Integration](https://www.thenewsletterplugin.com/documentation/contact-form-7-extension) - integrate the subscription on Contact Form 7 forms
80
+ * [Ninja Forms Integration](https://www.thenewsletterplugin.com/documentation/ninjaforms-extension) - integrate the subscription on Ninja Forms
81
+ * [WP Forms Integration](https://www.thenewsletterplugin.com/documentation/wpforms-extension) - integrate the subscription on WP Forms
82
+ * Events Manager and The Events Calendar (By Modern Tribe) integrations - easily add events to your newsletters
83
+ * [Google Analytics](https://www.thenewsletterplugin.com/google-analytics) - track newsletter links with Google UTM tracking paramaters
84
+ * [Subscribe on Comment](https://www.thenewsletterplugin.com/documentation/comments-extension) - adds the subscription option to your blog comment form
85
+ * [Geolocation](https://www.thenewsletterplugin.com/documentation/geolocation-extension) - adds geolocation capability to target subscribers by location
86
+
87
+ = GDPR =
88
+
89
+ The Newsletter Plugin provides all the technical tools needed to achieve GDPR compliancy and we're continuously working to improve them and to give support even for specific use cases.
90
+ The plugin does not collect users' own subscribers data, nor it has any access to those data: hence, we are not a data processor, so a data processing agreement is not needed.
91
+ Anyway if you configure the plugin to use external services (usually an external mail delivery service) you should check with that service if some sort of agreement is required.
92
+
93
+ = Support =
94
+
95
+ We provide support for our plugin on [Wordpress.org forums](https://wordpress.org/support/plugin/newsletter) and through our [official forum](https://www.thenewsletterplugin.com/forums).
96
+
97
+ Premium Users with an active license have access to one-to-one support via our [ticketing system](https://www.thenewsletterplugin.com/support-ticket).
98
+
99
+ = Developers =
100
+
101
+ We have a [documentation section](https://www.thenewsletterplugin.com/documentation/developers/) dedicated to who want to develop with Newsletter.
102
+
103
+ You can find us on [GitHub](https://github.com/TheNewsletterPlugin) with some examples of addons.
104
+
105
+ = Follow Us =
106
+
107
+ * **Our Official Website** - [https://www.thenewsletterplugin.com/](https://www.thenewsletterplugin.com/)
108
+ * **GitHub** - [https://github.com/TheNewsletterPlugin](https://github.com/TheNewsletterPlugin)
109
+ * **LinkedIn** - [https://www.linkedin.com/company/the-newsletter-plugin](https://www.linkedin.com/company/the-newsletter-plugin)
110
+ * **Our Facebook Page** - [https://www.facebook.com/thenewsletterplugin](https://www.facebook.com/thenewsletterplugin)
111
+ * **Our Twitter Account** - [https://twitter.com/newsletterwp](https://twitter.com/newsletterwp)
112
+
113
+ == Frequently Asked Questions ==
114
+
115
+ See the [Newsletter Forum](https://www.thenewsletterplugin.com/forums) to ask for help.
116
+
117
+ For documentation start from [Newsletter documentation](https://www.thenewsletterplugin.com/documentation).
118
+
119
+ Thank you, The Newsletter Team
120
+
121
+ == Screenshots ==
122
+
123
+ 1. The responsive email Drag & Drop composer
124
+ 2. The plugin dashboard
125
+ 3. The Reports extension
126
+
127
+ == Changelog ==
128
+
129
+ = 7.4.8 =
130
+
131
+ * Changed the image block for Gmail
132
+ * Removed the WP emojis staticizer on emails sent by Newsletter
133
+
134
+ = 7.4.7 =
135
+
136
+ * Changed removal order of the emojis admin script breaking newsletters' body
137
+ * Disabled DOM warnings
138
+
139
+ = 7.4.6 =
140
+
141
+ * XSS security fix
142
+
143
+ = 7.4.5 =
144
+
145
+ * Fixed image block font-size to show the alt text (not for Outlook, it never shows the alt text)
146
+ * XSS security fix
147
+
148
+ = 7.4.4 =
149
+
150
+ * Changed the dedicated page retrieval to intercept misconfigurations
151
+ * Added option to accept repeated subscription in single opt-in (you should check if it is compatible with your privacy regulation)
152
+ * Posts field selector keep now track of the previous post selected even if no more in list
153
+ * Improved the error management when delivery fails using the WP native mailing function
154
+ * Added title filter on posts block for compatibility with WPGlobus
155
+
156
+ = 7.4.3 =
157
+
158
+ * Removed obsolete note about the newsletter textual part
159
+ * Introduced text part generator right now only for the Composer (with permission of Frank M.)
160
+ * Added check on System>Status for images with relative URL
161
+ * Addec check for Freesoul Deactivate Plugins
162
+ * Minimal for layout now consider the button_label attribute
163
+
164
+ = 7.4.2 =
165
+
166
+ * Fixed the post date (regression)
167
+ * Added link to the automatic plaintext generator plugin by franciscus
168
+ * Possible fix for our Gutenberg block (sometimes) not working
169
+ * Added uoloads dir and url on System>Status panel
170
+ * Fixed Status panel error when a newsletter is in "error" status
171
+ * Added default width to the logo on header block
172
+
173
+ = 7.4.1 =
174
+
175
+ * Added check on invalid media on two columns post type
176
+ * WP 5.9.2 compatibility check
177
+
178
+ = 7.4.0 =
179
+
180
+ * Added privacy links
181
+ * Added filter "newsletter_subscription" (documentation is under writing)
182
+
183
+ = 7.3.9 =
184
+
185
+ * Fixed grid layout not showing correctly on gmail
186
+ * Fixed notice on widget page about the wrong editor enqueued
187
+ * Removed wrong top bar warning
188
+ * Fixed block options on posts block
189
+ * Fixed padding on two column posts block
190
+
191
+ = 7.3.8 =
192
+
193
+ * Fixed graph scale on System/Scheduler panel
194
+ * Fixed untraslated labels on subscriber management panels
195
+ * Fixed the "toggle" private/public status on sent newsletter
196
+ * Removed the "action call test" from the status panel since it does not work with some providers but does not affect the correct working of Newsletter
197
+ * Added developer information on this readme
198
+
199
+ = 7.3.7 =
200
+
201
+ * Fixed unwanted redirects on subscription errors
202
+ * Fixed composer page HTML
203
+ * Minor fixes on PHP, CSS
204
+ * Fixed notice on image block
205
+
206
+ = 7.3.6 =
207
+
208
+ * Improved composer reusability in other contexts
209
+ * Removed obsolete composer code
210
+ * Fixed default tracking for old theme-based neewsletters
211
+ * Forced enconding on export (attempt)
212
+ * WP 5.9 check
213
+
214
+ = 7.3.5 =
215
+
216
+ * WP 5.8.3 compatibility check
217
+ * Fixed 2021 max year in date picker
218
+ * Typos
219
+
220
+ = 7.3.4 =
221
+
222
+ * Fixed delivery fatal error management
223
+ * Fixed link to the schduler dianostica panel
224
+
225
+ = 7.3.3 =
226
+
227
+ * Added "complained" status to subscriber filters
228
+ * Fixed some links bringing to a "not allowed" page
229
+ * Fixed a notice on delivery diagnostic page
230
+ * Fixed logo width notice on header block
231
+
232
+ = 7.3.2 =
233
+
234
+ * Fixed the remote ip retrieval and clean up
235
+ * Fixed header link to status page
236
+ * Fixed database error with too long IPs
237
+ * Fixed the subscription of cancelled addresses
238
+ * Fixed sender and name customization
239
+
240
+ = 7.3.1 =
241
+
242
+ * Dropped old mailers support
243
+ * Improved sending process and limits checking
244
+ * Removed obsolete notifications
245
+ * Fixed the wrong report on single email delivery speed
246
+ * Added support for custom sending speed by addons
247
+ * Improved excerpt generation (but it still depends on plugins and themes...)
248
+ * Support for building button option on composer blocks
249
+
250
+ = 7.3.0 =
251
+
252
+ * Fixed header block layout with (logo only layout)
253
+ * Check for conflicts on newsletter saving
254
+ * Added the subscriber complained status (actually not managed automatically)
255
+
256
+ = 7.2.9 =
257
+
258
+ * Fixed generic action button confirmation popup not showing the message
259
+ * [SECURITY] Pre parsing of IP addresses on security panel
260
+ * [SECURITY] Comments allowed on IP address list on security panel
261
+ * [COMPOSER] Fixed preset name/subject
262
+ * Improved generated forms for accessibility
263
+
264
+ = 7.2.8 =
265
+
266
+ * Fixed the print_date() when no time is provided
267
+ * Fixed date alignment on posts block
268
+ * Folders reorganization
269
+ * Social block with a new set of icons
270
+ * Boosted performances of the lists management page (for big databases)
271
+ * Added a new scheduler diagnostic panel
272
+ * Seriously improved the cron statistics and diagnostic panel (check it out!)
273
+ * Removed obsolete migration code from ancient versions
274
+
275
+ = 7.2.7 =
276
+
277
+ * Fixed JS error on composer sometimes preventing the correct initialization
278
+
279
+ = 7.2.6 =
280
+
281
+ * Fixed links on test emails sent to a free email address
282
+ * Added attachment explanation
283
+ * Added link to explain the use of the snippet
284
+
285
+ = 7.2.5 =
286
+
287
+ * Fixed subject not saved under specific circumstance
288
+
289
+ = 7.2.4 =
290
+
291
+ * Fixed the composer not starting for blog with SSL plugin but still HTTP configured on main WP settings
292
+ * Changed labels on subscriber maintenance panel
293
+ * Updated requirements for WP version
294
+
295
+ = 7.2.3 =
296
+
297
+ * [COMPOSER] Added approx. indicators of the subsject visibile part in Apple and Android clients (experimental)
298
+ * [COMPOSER] New mobile version view directly while composing (experimental)
299
+ * [COMPOSER] New test email to test subscribers or to specific email address
300
+ * [COMPOSER] Fixed missing background when creating a new message from a preset
301
+ * [COMPOSER] Added media selector to the CTA block
302
+ * [COMPOSER] Added reference to the tags on HTML and Text blocks
303
+ * [COMPOSER] Move the snippet (preheader) field near the subject
304
+ * [COMPOSER] Footer block with three link options: unsubscribe, manage and view online
305
+ * [COMPOSER] Improve font coherence between blocks (by default)
306
+ * [ANTISPAM] Improved the antispam checks on subscription
307
+ * [GENERAL] Removed obsolete folders and code
308
+ * [NEWSLETTERS] Refactored subject ideas selector
309
+ * [SUBSCRIPTION] Inverted extra profile fields and lists on standard subscription form
310
+ * [GENERAL] IP address extracted checking proxy variables
311
+ * [GENERAL] Improved sending stats collection and display for the delivery engine (not related to click/open stats)
312
+
313
+ = 7.2.2 =
314
+
315
+ * [COMPOSER] Posts block excerpt removed when set to 0-length
316
+ * [GENERAL]Added special characters on test message
317
+ * [GENERAL]Added more specific error for action calls on System/Status panel
318
+ * [SUBSCRIPTION] Check for the _wp_amp_action_xhr_converted parameter by AMP plugin
319
+
320
+ = 7.2.1 =
321
+
322
+ * [GENERAL] Added more detailed admin logging
323
+ * [NEWSLETTERS] Fixed scheduled date sometimes reset to 1/1/1970
324
+
325
+ = 7.2.0 =
326
+
327
+ * [PROFILE] Fixed activation email on profile change
328
+ * [NEWSLETTERS] Extended year selection on newsletter scheduling
329
+ * [DELIVERY] Breaking change: old enqueue() and flush() methods have been removed
330
+ * [GENERAL] Fixed alert message on some buttons
331
+ * [SUBSCRIPTION] Fixed error message on multiple subscriptions (when not allowed)
332
+ * [GENERAL] Fixed erratic error log line on main log
333
+
334
+ = 7.1.9 =
335
+
336
+ * [GENERAL] Removed the encodign defatlt to Base 64 when not specified since it seems incompatible with some SMTP plugins
337
+ * [GENERAL] Review some controls layout and behavior
338
+ * [DEBUG] Improved logging on database errors
339
+ * [GENERAL] Added TikTok, Discord and Twitch socials
340
+ * [GENERAL] Fixed odd error reported related to the cron call statistics collection
341
+ * [DEBUG] Added a delivery diagnostic panel
342
+
343
+ = 7.1.8 =
344
+
345
+ * [COMPOSER] Fixed alignment of single big image on Outlook Android
346
+
347
+ = 7.1.7 =
348
+
349
+ * [GENERAL] Fix of permalink onm email with multilanguage plugins
350
+
351
+ = 7.1.6 =
352
+
353
+ * [COMPOSER] Fixed one column big image Read more links
354
+
355
+ = 7.1.5 =
356
+
357
+ * [COMPOSER] Improve buttons on posts layout
358
+ * [COMPOSER] Improved header layout and logo
359
+ * [COMPOSER] Generally improved block spacing
360
+
361
+ = 7.1.4 =
362
+
363
+ * [COMPOSER] Fixed image block for Outlook
364
+ * [GENERAL] Fix undefined values in Gutenberg block
365
+
366
+ = 7.1.3 =
367
+
368
+ * [COMPOSER] Improvements on blocks layout compatibility
369
+ * [COMPOSER] Fixed preset global options saving
370
+ * [COMPOSER] Content regeneration on preset selection
371
+ * [GENERAL] Added to System menu the Site Health link, a not well known native page of WordPress with system information
372
+ * [GENERAL] Added sending statistics reset button on status panel
373
+
374
+ = 7.1.2 =
375
+
376
+ * [ADDONS] Fixed the addons list
377
+
378
+ = 7.1.1 =
379
+
380
+ * [GENERAL] Improved profile related functions
381
+ * [GENERAL] Fixed check on field names (thanks Peter P.)
382
+ * [COMPOSER] Fix on preset selection
383
+ * [GENERAL] Fix ajax subscription on error
384
+
385
+ = 7.1.0 =
386
+
387
+ * [COMPOSER] Added link to tags documentation to inject subscriber's data
388
+ * [COMPOSER] Fixed layout of posts block for Outlook 365
389
+ * [GENERAL] Improved caching of addons json (even on error)
390
+ * [GENERAL] Status menu changed to System/Status and System/Logs
391
+ * [API] Fixed the subscriber status management (the API Addon should be updated as well)
392
+ * [GENERAL] Fixed management of fatal errors on sending
393
+ * [GENERAL] Limited error string per message to 250 chars
394
+ * [GENERAL] Fixed check on field names (thanks Peter P.)
395
+
396
+
397
+ = 7.0.9 =
398
+
399
+ * [CAPTCHA] Fixed button label translation
400
+ * [DELIVERY] Mailing fatal error management with newsletter stop and reporting
401
+ * [SMTP] Marked obsolete the internal SMTP and made available the free SMTP addon
402
+ * [DELIVERY] Better management of delivery fatal errors with a new "error" status for newsletters
403
+ * [GENERAL] New log files panel
404
+ * [IMPORT] Old import panel replaced by the new (really better) import addon (file, copy and paste, bounced addresses import)
405
+ * [NEWSLETTERS] It's now possible to specify the sender name and email per newsletter (thanks to Matthew S.)
406
+
407
+ = 7.0.8 =
408
+
409
+ * [SUBSCRIBERS] Changed action buttons
410
+ * [GENERAL] Reorganization of CSS and removal of unused files
411
+ * [DASHBOARD] New window open for links and fix of invalid URLs
412
+ * [NEWSLETTERS] New action buttons
413
+ * [GENERAL] Minor fixes and optimizations
414
+ * [COMPOSER] Fixed rare size error on gif images
415
+
416
+ = 7.0.7 =
417
+
418
+ * [COMPOSER] Fixed a warning in some inline editable blocks
419
+ * [NEWSLETTERS] Fixed style which made list labels badly readable
420
+ * [NEWSLETTERS] Fixed style which made bullet lists white (not on delivered newsletters)
421
+ * [GENERAL] Added some references to the not working scheduler warning
422
+
423
+ = 7.0.6 =
424
+
425
+ * [COMPOSER] CTA block not grabbing settings from the "old" blocks
426
+ * [COMPOSER] CSS issue on button settings group
427
+ * [COMPOSER] Posts block two columns layout (author and padding)
428
+ * [IMPORT] Removed the old low-featured import (the free import addon has everything needed!)
429
+ * [GENERAL] Compatibility check with WP 5.7
430
+
431
+ = 7.0.5 =
432
+
433
+ * [COMPOSER] Hero CTA button not working
434
+ * Fixed to the hero block button
435
+ * Added support for the SMTP addon
436
+
437
+ = 7.0.4 =
438
+
439
+ * [COMPOSER] Redesigned drag and drop composer
440
+ * [COMPOSER] NEW! Save drag and drop composed newsletters as templates to reuse easily
441
+ * Redesigned dashboard
442
+ * Several bug and fixes
443
+
444
+ = 7.0.3 =
445
+
446
+ * Option to choose between unsubscription and profile link in the footer block
447
+ * Direct image src URL for image block
448
+ * New media selector for blocks
449
+ * Minor fixes
450
+ * Updated codemirror
451
+ * Updated default theme
452
+ * Fixed tag filter on posts block (when tags have parathesis or like)
453
+ * Fixed composer visualization of bullet points
454
+
455
+ = 7.0.2 =
456
+
457
+ * Fixed media 2x resize
458
+
459
+ = 7.0.1 =
460
+
461
+ * Fixed enforced lists by language with Polylang
462
+
463
+ = 7.0.0 =
464
+
465
+ * Added multiple newsletter selection for deletion
466
+ * Added text part on welcome and activation email
467
+ * Added the attribute "show_form" to "newsletter" shortcode
468
+ * Added filter on subscriber saving (from external systems) with wrong list field values
469
+ * Added index.html on log folder
470
+
subscription/subscription.php CHANGED
@@ -1,1941 +1,1941 @@
1
- <?php
2
-
3
- defined('ABSPATH') || exit;
4
-
5
- class NewsletterSubscription extends NewsletterModule {
6
-
7
- const MESSAGE_CONFIRMED = 'confirmed';
8
- const OPTIN_DOUBLE = 0;
9
- const OPTIN_SINGLE = 1;
10
-
11
- static $instance;
12
-
13
- /**
14
- * @var array
15
- */
16
- var $options_profile;
17
-
18
- /**
19
- * Contains the options for the current language to build a subscription form. Must be initialized with
20
- * setup_form_options() before use.
21
- *
22
- * @var array
23
- */
24
- var $form_options = null;
25
-
26
- /**
27
- * Contains the antibot/antispam options. Must be initialized with
28
- * setup_antibot_options() before use.
29
- *
30
- * @var array
31
- */
32
- var $antibot_options = null;
33
-
34
- /**
35
- * @var array
36
- */
37
- var $options_lists;
38
-
39
- /**
40
- * @return NewsletterSubscription
41
- */
42
- static function instance() {
43
- if (self::$instance == null) {
44
- self::$instance = new NewsletterSubscription();
45
- }
46
- return self::$instance;
47
- }
48
-
49
- function __construct() {
50
-
51
- parent::__construct('subscription', '2.2.7', null, array('lists', 'template', 'profile', 'antibot'));
52
- $this->options_profile = $this->get_options('profile');
53
- $this->options_lists = $this->get_options('lists');
54
-
55
- // Must be called after the Newsletter::hook_init, since some constants are defined
56
- // there.
57
- add_action('init', array($this, 'hook_init'), 90);
58
- }
59
-
60
- function hook_init() {
61
- add_action('newsletter_action', array($this, 'hook_newsletter_action'), 10, 3);
62
- if (is_admin()) {
63
- add_action('admin_init', array($this, 'hook_admin_init'));
64
- } else {
65
- // Shortcode for the Newsletter page
66
- add_shortcode('newsletter', array($this, 'shortcode_newsletter'));
67
- add_shortcode('newsletter_form', array($this, 'shortcode_newsletter_form'));
68
- add_shortcode('newsletter_field', array($this, 'shortcode_newsletter_field'));
69
- }
70
- }
71
-
72
- function hook_admin_init() {
73
- // So the user can add JS and other code
74
- if (isset($_GET['page']) && $_GET['page'] === 'newsletter_subscription_forms') {
75
- header('X-XSS-Protection: 0');
76
- }
77
-
78
- if (function_exists('register_block_type')) {
79
- // Add custom blocks to Gutenberg
80
- wp_register_script('tnp-blocks', plugins_url('newsletter') . '/includes/tnp-blocks.js', array('wp-block-editor', 'wp-blocks', 'wp-element', 'wp-components'), NEWSLETTER_VERSION);
81
- register_block_type('tnp/minimal', array('editor_script' => 'tnp-blocks'));
82
- }
83
- }
84
-
85
- /**
86
- *
87
- * @global wpdb $wpdb
88
- * @return mixed
89
- */
90
- function hook_newsletter_action($action, $user, $email) {
91
- global $wpdb;
92
-
93
- switch ($action) {
94
- case 'profile-change':
95
- if ($this->antibot_form_check()) {
96
-
97
- if (!$user || $user->status != TNP_user::STATUS_CONFIRMED) {
98
- $this->dienow('Subscriber not found or not confirmed.', 'Even the wrong subscriber token can lead to this error.', 404);
99
- }
100
-
101
- if (!$email) {
102
- $this->dienow('Newsletter not found', 'The newsletter containing the link has been deleted.', 404);
103
- }
104
-
105
- if (isset($_REQUEST['list'])) {
106
- $list_id = (int) $_REQUEST['list'];
107
-
108
- // Check if the list is public
109
- $list = $this->get_list($list_id);
110
- if (!$list || $list->status == TNP_List::STATUS_PRIVATE) {
111
- $this->dienow('List change not allowed.', 'Please check if the list is marked as "private".', 400);
112
- }
113
-
114
- if (empty($_REQUEST['redirect'])) {
115
- $url = home_url();
116
- } else {
117
- $url = esc_url_raw($_REQUEST['redirect']);
118
- }
119
- $this->set_user_list($user, $list_id, $_REQUEST['value']);
120
-
121
- $user = $this->get_user($user->id);
122
- $this->add_user_log($user, 'cta');
123
- NewsletterStatistics::instance()->add_click($url, $user->id, $email->id);
124
- wp_redirect($url);
125
- die();
126
- }
127
- } else {
128
- $this->request_to_antibot_form('Continue');
129
- }
130
-
131
- die();
132
-
133
- case 'm':
134
- case 'message':
135
- include dirname(__FILE__) . '/page.php';
136
- die();
137
-
138
- // normal subscription
139
- case 's':
140
- case 'subscribe':
141
-
142
- if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
143
- $this->dienow('Invalid request', 'The subscription request was not made with a HTTP POST', 400);
144
- }
145
-
146
- $options_antibot = $this->get_options('antibot');
147
-
148
- $captcha = !empty($options_antibot['captcha']);
149
-
150
- if (!empty($_GET['_wp_amp_action_xhr_converted']) || !empty($options_antibot['disabled']) || $this->antibot_form_check($captcha)) {
151
-
152
- $subscription = $this->build_subscription();
153
-
154
- $user = $this->subscribe2($subscription);
155
-
156
- if (is_wp_error($user)) {
157
- if ($user->get_error_code() === 'exists') {
158
- $language = isset($_REQUEST['nlang']) ? $_REQUEST['nlang'] : '';
159
- $options = $this->get_options('', $language);
160
- $this->dienow($options['error_text'], $user->get_error_message(), 200);
161
- }
162
- $this->dienow('Registration failed.', $user->get_error_message(), 400);
163
- }
164
-
165
- if ($user->status == TNP_User::STATUS_CONFIRMED) {
166
- $this->show_message('confirmed', $user);
167
- }
168
- if ($user->status == TNP_User::STATUS_NOT_CONFIRMED) {
169
- $this->show_message('confirmation', $user);
170
- }
171
- } else {
172
- $language = isset($_REQUEST['nlang']) ? $_REQUEST['nlang'] : '';
173
- $options = $this->get_form_options($language);
174
- $this->request_to_antibot_form($options['subscribe'], $captcha);
175
- }
176
- die();
177
-
178
- // AJAX subscription
179
- case 'ajaxsub':
180
-
181
- if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
182
- $this->dienow('Invalid request');
183
- }
184
-
185
- $subscription = $this->build_subscription();
186
-
187
- $user = $this->subscribe2($subscription);
188
-
189
- if (is_wp_error($user)) {
190
- if ($user->get_error_code() === 'exists') {
191
- echo $this->options['error_text'];
192
- die();
193
- } else {
194
- $this->dienow('Registration failed.', $user->get_error_message(), 400);
195
- }
196
- } else {
197
- if ($user->status == TNP_User::STATUS_CONFIRMED) {
198
- $key = 'confirmed';
199
- }
200
-
201
- if ($user->status == TNP_User::STATUS_NOT_CONFIRMED) {
202
- $key = 'confirmation';
203
- }
204
- }
205
-
206
- $message = $this->replace($this->options[$key . '_text'], $user);
207
- if (isset($this->options[$key . '_tracking'])) {
208
- $message .= $this->options[$key . '_tracking'];
209
- }
210
- echo $message;
211
- die();
212
-
213
- case 'c':
214
- case 'confirm':
215
- if (!$user) {
216
- $this->dienow(__('Subscriber not found.', 'newsletter'), 'Or it is not present or the secret key does not match.', 404);
217
- }
218
-
219
- if ($this->antibot_form_check()) {
220
- $user = $this->confirm($user);
221
- setcookie('newsletter', $user->id . '-' . $user->token, time() + 60 * 60 * 24 * 365, '/');
222
- $this->show_message('confirmed', $user);
223
- } else {
224
- $this->request_to_antibot_form('Confirm');
225
- }
226
- die();
227
- break;
228
-
229
- default:
230
- return;
231
- }
232
- }
233
-
234
- function upgrade() {
235
- global $wpdb, $charset_collate;
236
-
237
- parent::upgrade();
238
-
239
- $newsletter = Newsletter::instance();
240
- $lists_options = $this->get_options('lists');
241
- $profile_options = $this->get_options('profile');
242
-
243
- if (empty($lists_options)) {
244
- foreach ($profile_options as $key => $value) {
245
- if (strpos($key, 'list_') === 0) {
246
- $lists_options[$key] = $value;
247
- }
248
- }
249
- }
250
-
251
- for ($i = 1; $i <= NEWSLETTER_LIST_MAX; $i++) {
252
- // Options migration to the new set
253
- if (!empty($profile_options['list_' . $i]) && empty($lists_options['list_' . $i])) {
254
- $lists_options['list_' . $i] = $profile_options['list_' . $i];
255
- $lists_options['list_' . $i . '_checked'] = $profile_options['list_' . $i . '_checked'];
256
- $lists_options['list_' . $i . '_forced'] = $profile_options['list_' . $i . '_forced'];
257
- }
258
-
259
- if (!isset($profile_options['list_' . $i . '_forced'])) {
260
- $profile_options['list_' . $i . '_forced'] = empty($this->options['preferences_' . $i]) ? 0 : 1;
261
- $lists_options['list_' . $i . '_forced'] = empty($this->options['preferences_' . $i]) ? 0 : 1;
262
- }
263
- }
264
-
265
- $this->save_options($profile_options, 'profile');
266
- $this->save_options($lists_options, 'lists');
267
-
268
- $default_options = $this->get_default_options();
269
-
270
- if (empty($this->options['error_text'])) {
271
- $this->options['error_text'] = $default_options['error_text'];
272
- $this->save_options($this->options);
273
- }
274
-
275
- $this->init_options('template', false);
276
-
277
- global $wpdb, $charset_collate;
278
-
279
- require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
280
-
281
- $sql = "CREATE TABLE `" . $wpdb->prefix . "newsletter_user_logs` (
282
- `id` int(11) NOT NULL AUTO_INCREMENT,
283
- `user_id` int(11) NOT NULL DEFAULT 0,
284
- `ip` varchar(50) NOT NULL DEFAULT '',
285
- `source` varchar(50) NOT NULL DEFAULT '',
286
- `data` longtext,
287
- `created` int(11) NOT NULL DEFAULT 0,
288
- PRIMARY KEY (`id`)
289
- ) $charset_collate;";
290
-
291
- dbDelta($sql);
292
-
293
- return true;
294
- }
295
-
296
- function first_install() {
297
-
298
- }
299
-
300
- function admin_menu() {
301
- $this->add_menu_page('options', __('List building', 'newsletter'));
302
- $this->add_admin_page('lists', __('Lists', 'newsletter'));
303
- $this->add_admin_page('profile', __('Subscription Form', 'newsletter'));
304
- $this->add_menu_page('antibot', __('Security', 'newsletter'));
305
- $this->add_admin_page('forms', __('Forms', 'newsletter'));
306
- $this->add_admin_page('template', __('Template', 'newsletter'));
307
- }
308
-
309
- /**
310
- * This method has been redefined for compatibility with the old options naming. It would
311
- * be better to change them instead. The subscription options should be named
312
- * "newsletter_subscription" while the form field options, actually named
313
- * "newsletter_profile", should be renamed "newsletter_subscription_profile" (since
314
- * they are retrived with get_options('profile')) or "newsletter_subscription_fields" or
315
- * "newsletter_subscription_form".
316
- *
317
- * @param array $options
318
- * @param string $sub
319
- */
320
- function save_options($options, $sub = '', $autoload = null, $language = '') {
321
- if (empty($sub) && empty($language)) {
322
- // For compatibility the options are wrongly named
323
- return update_option('newsletter', $options, $autoload);
324
- }
325
-
326
- if (empty($sub) && !empty($language)) {
327
- return update_option('newsletter_' . $language, $options, $autoload);
328
- }
329
-
330
- if ($sub == 'profile') {
331
- if (empty($language)) {
332
- $this->options_profile = $options;
333
- return update_option('newsletter_profile', $options, $autoload);
334
- } else {
335
- return update_option('newsletter_profile_' . $language, $options, $autoload);
336
- }
337
- // For compatibility the options are wrongly named
338
- }
339
-
340
- if ($sub == 'forms') {
341
- // For compatibility the options are wrongly named
342
- return update_option('newsletter_forms', $options, $autoload);
343
- }
344
-
345
- if ($sub == 'lists') {
346
- $this->options_lists = $options;
347
- }
348
- return parent::save_options($options, $sub, $autoload, $language);
349
- }
350
-
351
- function get_options($sub = '', $language = '') {
352
- if ($sub == '') {
353
- // For compatibility the options are wrongly named
354
- if ($language) {
355
- $options = get_option('newsletter_' . $language, []);
356
- $options = array_merge(get_option('newsletter', []), $options);
357
- } else {
358
- $options = get_option('newsletter', []);
359
- }
360
- if (!is_array($options)) {
361
- $options = array();
362
- }
363
-
364
- return $options;
365
- }
366
- if ($sub == 'profile') {
367
- if ($language) {
368
- // All that because for unknown reasome, sometime the options are returned as string, maybe a WPML
369
- // interference...
370
- $i18n_options = get_option('newsletter_profile_' . $language, []);
371
- if (!is_array($i18n_options)) {
372
- $i18n_options = [];
373
- }
374
- $options = get_option('newsletter_profile', []);
375
- if (!is_array($options)) {
376
- $options = [];
377
- }
378
- $options = array_merge($options, array_filter($i18n_options));
379
- } else {
380
- $options = get_option('newsletter_profile', []);
381
- }
382
- // For compatibility the options are wrongly named
383
- return $options;
384
- }
385
- if ($sub == 'forms') {
386
- // For compatibility the options are wrongly named
387
- return get_option('newsletter_forms', []);
388
- }
389
- return parent::get_options($sub, $language);
390
- }
391
-
392
- /**
393
- * Prepares the options used to build a subscription form for the current language
394
- * in internal variable $form_options (for optimization). Can be called many times.
395
- */
396
- function setup_form_options() {
397
- if (empty($this->form_options)) {
398
- $this->form_options = $this->get_options('profile', $this->get_current_language());
399
- }
400
- }
401
-
402
- function get_form_options($language = '') {
403
- return $this->get_options('profile', $language);
404
- }
405
-
406
- function set_updated($user, $time = 0, $ip = '') {
407
- global $wpdb;
408
- if (!$time) {
409
- $time = time();
410
- }
411
-
412
- if (!$ip) {
413
- $ip = $this->get_remote_ip();
414
- }
415
- $ip = $this->process_ip($ip);
416
-
417
- if (is_object($user)) {
418
- $id = $user->id;
419
- } else if (is_array($user)) {
420
- $id = $user['id'];
421
- }
422
-
423
- $id = (int) $id;
424
-
425
- $wpdb->update(NEWSLETTER_USERS_TABLE, array('updated' => $time, 'ip' => $ip, 'geo' => 0), array('id' => $id));
426
- }
427
-
428
- /**
429
- * Sanitize the subscription data collected before process them. Cleanup the lists, the optin mode, email, first name,
430
- * last name, adds mandatory lists, get (if not provided) and process the IP.
431
- *
432
- * @param TNP_Subscription_Data $data
433
- */
434
- private function sanitize($data) {
435
- $data->email = $this->normalize_email($data->email);
436
- if (!empty($data->name)) {
437
- $data->name = $this->normalize_name($data->name);
438
- }
439
- if (!empty($data->surname)) {
440
- $data->surname = $this->normalize_name($data->surname);
441
- }
442
-
443
- if (empty($data->ip)) {
444
- $data->ip = $this->get_remote_ip();
445
- }
446
- $data->ip = $this->process_ip($data->ip);
447
-
448
- if (isset($data->http_referer)) {
449
- $data->http_referer = mb_substr(strip_tags($data->http_referer), 0, 200);
450
- }
451
-
452
- if (isset($data->sex)) {
453
- $data->sex = $this->normalize_sex($data->sex);
454
- }
455
-
456
- if (!isset($data->language)) {
457
- $data->language = $this->get_current_language();
458
- } else {
459
- $data->language = strtolower(strip_tags($data->language));
460
- }
461
-
462
- for ($i = 1; $i <= NEWSLETTER_PROFILE_MAX; $i++) {
463
- $key = 'profile_' . $i;
464
- if (isset($data->$key)) {
465
- $data->$key = trim($data->$key);
466
- }
467
- }
468
- }
469
-
470
- /**
471
- * Builds a default subscription object to be used to collect data and subscription options.
472
- *
473
- * @return TNP_Subscription
474
- */
475
- function get_default_subscription($language = null) {
476
- $subscription = new TNP_Subscription();
477
-
478
- $language = is_null($language) ? $this->get_current_language() : $language;
479
-
480
- $subscription->data->language = $language;
481
- $subscription->optin = $this->is_double_optin() ? 'double' : 'single';
482
-
483
- if (empty($this->options['multiple'])) {
484
- $subscription->if_exists = TNP_Subscription::EXISTING_ERROR;
485
- } else if ($this->options['multiple'] == 1) {
486
- $subscription->if_exists = TNP_Subscription::EXISTING_MERGE;
487
- } else {
488
- $subscription->if_exists = TNP_Subscription::EXISTING_SINGLE_OPTIN;
489
- }
490
-
491
- $lists = $this->get_lists();
492
- foreach ($lists as $list) {
493
- if ($list->forced) {
494
- $subscription->data->lists['' . $list->id] = 1;
495
- continue;
496
- }
497
- // Enforced by language
498
- if ($language && in_array($language, $list->languages)) {
499
- $subscription->data->lists['' . $list->id] = 1;
500
- }
501
- }
502
-
503
- return $subscription;
504
- }
505
-
506
- /**
507
- *
508
- * @param TNP_Subscription $subscription
509
- *
510
- * @return TNP_User|WP_Error
511
- */
512
- function subscribe2(TNP_Subscription $subscription) {
513
-
514
- $this->logger->debug($subscription);
515
-
516
- $this->sanitize($subscription->data);
517
-
518
- if (empty($subscription->data->email)) {
519
- return new WP_Error('email', 'Wrong email address');
520
- }
521
-
522
- if (!empty($subscription->data->country) && strlen($subscription->data->country) != 2) {
523
- return new WP_Error('country', 'Country code length error. ISO 3166-1 alpha-2 format (2 letters)');
524
- }
525
-
526
- // Here we should have a clean subscription data
527
- // Filter?
528
-
529
- if ($subscription->spamcheck) {
530
- // TODO: Use autoload
531
- require_once NEWSLETTER_INCLUDES_DIR . '/antispam.php';
532
- $antispam = NewsletterAntispam::instance();
533
- if ($antispam->is_spam($subscription)) {
534
- return new WP_Error('spam', 'This looks like a spam subscription');
535
- }
536
- }
537
-
538
- // Exists?
539
- $user = $this->get_user_by_email($subscription->data->email);
540
-
541
- $subscription = apply_filters('newsletter_subscription', $subscription, $user);
542
-
543
- // Do we accept repeated subscriptions?
544
- if ($user != null && $subscription->if_exists === TNP_Subscription::EXISTING_ERROR) {
545
- //$this->show_message('error', $user);
546
- return new WP_Error('exists', 'Email address already registered and Newsletter sets to block repeated registrations. You can change this behavior or the user message above on subscription configuration panel.');
547
- }
548
-
549
-
550
- if ($user != null) {
551
-
552
- $this->logger->info('Subscription of an address with status ' . $user->status);
553
-
554
- // We cannot communicate with bounced addresses, there is no reason to proceed
555
- // TODO: Evaluate if the bounce status is very old, possible reset it
556
- if ($user->status == TNP_User::STATUS_BOUNCED || $user->status == TNP_User::STATUS_COMPLAINED) {
557
- return new WP_Error('bounced', 'Subscriber present and blocked');
558
- }
559
-
560
- if ($user->status == TNP_User::STATUS_UNSUBSCRIBED) {
561
- // Special behavior?
562
- }
563
-
564
- if ($subscription->optin == 'single') {
565
- $user->status = TNP_User::STATUS_CONFIRMED;
566
- } else {
567
- if ($user->status == TNP_User::STATUS_CONFIRMED) {
568
-
569
- set_transient('newsletter_subscription_' . $user->id, $subscription->data, 3600 * 48);
570
-
571
- // This status is *not* stored it indicate a temporary status to show the correct messages
572
- $user->status = TNP_User::STATUS_NOT_CONFIRMED;
573
-
574
- $this->send_message('confirmation', $user);
575
-
576
- return $user;
577
- } else {
578
- $user->status = TNP_User::STATUS_NOT_CONFIRMED;
579
- }
580
- }
581
-
582
- // Can be updated on the fly?
583
- $subscription->data->merge_in($user);
584
- } else {
585
- $this->logger->info('New subscriber');
586
-
587
- $user = new TNP_User();
588
- $subscription->data->merge_in($user);
589
-
590
- $user->token = $this->get_token();
591
-
592
- $user->status = $subscription->optin == 'single' ? TNP_User::STATUS_CONFIRMED : TNP_User::STATUS_NOT_CONFIRMED;
593
- $user->updated = time();
594
- }
595
-
596
- $user->ip = $this->process_ip($user->ip);
597
-
598
- $user = apply_filters('newsletter_user_subscribe', $user);
599
-
600
- $user = $this->save_user($user);
601
-
602
- $this->add_user_log($user, 'subscribe');
603
-
604
- // Notification to admin (only for new confirmed subscriptions)
605
- if ($user->status == TNP_User::STATUS_CONFIRMED) {
606
- do_action('newsletter_user_confirmed', $user);
607
- $this->notify_admin_on_subscription($user);
608
- setcookie('newsletter', $user->id . '-' . $user->token, time() + 60 * 60 * 24 * 365, '/');
609
- }
610
-
611
- if ($subscription->send_emails) {
612
- $this->send_message(($user->status == TNP_User::STATUS_CONFIRMED) ? 'confirmed' : 'confirmation', $user);
613
- }
614
-
615
- // Used by Autoresponder (probably)
616
- do_action('newsletter_user_post_subscribe', $user);
617
-
618
- return $user;
619
- }
620
-
621
- /**
622
- * Create a subscription using the $_REQUEST data. Does security checks.
623
- *
624
- * @deprecated since version 6.9.0
625
- * @param string $status The status to use for this subscription (confirmed, not confirmed, ...)
626
- * @param bool $emails If the confirmation/welcome email should be sent or the subscription should be silent
627
- * @return TNP_User
628
- */
629
- function subscribe($status = null, $emails = true) {
630
-
631
- $this->logger->debug('Subscription start');
632
-
633
- // Validation
634
- $ip = $this->get_remote_ip();
635
- $email = $this->normalize_email(stripslashes($_REQUEST['ne']));
636
- $first_name = '';
637
- if (isset($_REQUEST['nn'])) {
638
- $first_name = $this->normalize_name(stripslashes($_REQUEST['nn']));
639
- }
640
-
641
- $last_name = '';
642
- if (isset($_REQUEST['ns'])) {
643
- $last_name = $this->normalize_name(stripslashes($_REQUEST['ns']));
644
- }
645
-
646
- $opt_in = (int) $this->options['noconfirmation']; // 0 - double, 1 - single
647
- if (!empty($this->options['optin_override']) && isset($_REQUEST['optin'])) {
648
- switch ($_REQUEST['optin']) {
649
- case 'single': $opt_in = self::OPTIN_SINGLE;
650
- break;
651
- case 'double': $opt_in = self::OPTIN_DOUBLE;
652
- break;
653
- }
654
- }
655
-
656
- if ($status != null) {
657
- // If a status is forced and it is requested to be "confirmed" is like a single opt in
658
- // $status here can only be confirmed or not confirmed
659
- // TODO: Add a check on status values
660
- if ($status == TNP_User::STATUS_CONFIRMED) {
661
- $opt_in = self::OPTIN_SINGLE;
662
- } else {
663
- $opt_in = self::OPTIN_DOUBLE;
664
- }
665
- }
666
-
667
- $user = $this->get_user($email);
668
-
669
- if ($user != null) {
670
- // Email already registered in our database
671
- $this->logger->info('Subscription of an address with status ' . $user->status);
672
-
673
- // Bounced
674
- // TODO: Manage other cases when added
675
- if ($user->status == 'B') {
676
- // Non persistent status to decide which message to show (error)
677
- $user->status = 'E';
678
- return $user;
679
- }
680
-
681
- // Is there any relevant data change? If so we can proceed otherwise if repeated subscriptions are disabled
682
- // show an already subscribed message
683
-
684
- if (empty($this->options['multiple'])) {
685
- $user->status = 'E';
686
- return $user;
687
- }
688
-
689
- // If the subscriber is confirmed, we cannot change his data in double opt in mode, we need to
690
- // temporary store and wait for activation
691
- if ($user->status == TNP_User::STATUS_CONFIRMED && $opt_in == self::OPTIN_DOUBLE) {
692
-
693
- set_transient($this->get_user_key($user), $_REQUEST, 3600 * 48);
694
-
695
- // This status is *not* stored it indicate a temporary status to show the correct messages
696
- $user->status = 'S';
697
-
698
- $this->send_message('confirmation', $user);
699
-
700
- return $user;
701
- }
702
- }
703
-
704
- // Here we have a new subscription or we can process the subscription even with a pre-existant user for example
705
- // because it is not confirmed
706
- if ($user != null) {
707
- $this->logger->info("Email address subscribed but not confirmed");
708
- $user = array('id' => $user->id);
709
- } else {
710
- $this->logger->info("New email address");
711
- $user = array('email' => $email);
712
- }
713
-
714
- $user = $this->update_user_from_request($user);
715
-
716
- $user['token'] = $this->get_token();
717
- $ip = $this->process_ip($ip);
718
- $user['ip'] = $ip;
719
- $user['geo'] = 0;
720
- $user['status'] = $opt_in == self::OPTIN_SINGLE ? TNP_User::STATUS_CONFIRMED : TNP_User::STATUS_NOT_CONFIRMED;
721
-
722
- $user['updated'] = time();
723
-
724
- $user = apply_filters('newsletter_user_subscribe', $user);
725
-
726
- $user = $this->save_user($user);
727
-
728
- $this->add_user_log($user, 'subscribe');
729
-
730
- // Notification to admin (only for new confirmed subscriptions)
731
- if ($user->status == TNP_User::STATUS_CONFIRMED) {
732
- do_action('newsletter_user_confirmed', $user);
733
- $this->notify_admin_on_subscription($user);
734
- setcookie('newsletter', $user->id . '-' . $user->token, time() + 60 * 60 * 24 * 365, '/');
735
- }
736
-
737
- if ($emails) {
738
- $this->send_message(($user->status == TNP_User::STATUS_CONFIRMED) ? 'confirmed' : 'confirmation', $user);
739
- }
740
-
741
- $user = apply_filters('newsletter_user_post_subscribe', $user);
742
-
743
- return $user;
744
- }
745
-
746
- function add_microdata($message) {
747
- return $message . '<span itemscope itemtype="http://schema.org/EmailMessage"><span itemprop="description" content="Email address confirmation"></span><span itemprop="action" itemscope itemtype="http://schema.org/ConfirmAction"><meta itemprop="name" content="Confirm Subscription"><span itemprop="handler" itemscope itemtype="http://schema.org/HttpActionHandler"><meta itemprop="url" content="{subscription_confirm_url}"><link itemprop="method" href="http://schema.org/HttpRequestMethod/POST"></span></span></span>';
748
- }
749
-
750
- /**
751
- * Builds a subscription object starting from values in the $_REQUEST
752
- * global variable. It DOES NOT sanitizie or formally check the values.
753
- * Usually data comes from a form submission.
754
- * https://www.thenewsletterplugin.com/documentation/subscription/newsletter-forms/
755
- *
756
- * @return TNP_Subscription
757
- */
758
- function build_subscription() {
759
-
760
- $language = '';
761
- if (!empty($_REQUEST['nlang'])) {
762
- $language = $_REQUEST['nlang'];
763
- } else {
764
- $language = $this->get_current_language();
765
- }
766
-
767
- $subscription = $this->get_default_subscription($language);
768
- $data = $subscription->data;
769
-
770
- $data->email = $_REQUEST['ne'];
771
-
772
- if (isset($_REQUEST['nn'])) {
773
- $data->name = stripslashes($_REQUEST['nn']);
774
- }
775
-
776
- if (isset($_REQUEST['ns'])) {
777
- $data->surname = stripslashes($_REQUEST['ns']);
778
- }
779
-
780
- if (!empty($_REQUEST['nx'])) {
781
- $data->sex = $_REQUEST['nx'][0];
782
- }
783
-
784
- if (isset($_REQUEST['nr'])) {
785
- $data->referrer = $_REQUEST['nr'];
786
- }
787
-
788
- // From the antibot form
789
- if (isset($_REQUEST['nhr'])) {
790
- $data->http_referer = stripslashes($_REQUEST['nhr']);
791
- } else if (isset($_SERVER['HTTP_REFERER'])) {
792
- $data->http_referer = $_SERVER['HTTP_REFERER'];
793
- }
794
-
795
- // New profiles
796
- for ($i = 1; $i <= NEWSLETTER_PROFILE_MAX; $i++) {
797
- // If the profile cannot be set by subscriber, skip it.
798
- if ($this->options_profile['profile_' . $i . '_status'] == 0) {
799
- continue;
800
- }
801
- if (isset($_REQUEST['np' . $i])) {
802
- $data->profiles['' . $i] = stripslashes($_REQUEST['np' . $i]);
803
- }
804
- }
805
-
806
- // Lists (field name is nl[] and values the list number so special forms with radio button can work)
807
- if (isset($_REQUEST['nl']) && is_array($_REQUEST['nl'])) {
808
- $this->logger->debug($_REQUEST['nl']);
809
- foreach ($_REQUEST['nl'] as $list_id) {
810
- $list = $this->get_list($list_id);
811
- if (!$list || $list->is_private()) {
812
- // To administrator show an error to make him aware of the wrong form configuration
813
- if (current_user_can('administrator')) {
814
- $this->dienow('Invalid list', 'List ' . $list_id . ' has been submitted but it is set as private. Please fix the subscription form.');
815
- }
816
- // Ignore this list
817
- continue;
818
- }
819
- $data->lists['' . $list_id] = 1;
820
- }
821
- } else {
822
- $this->logger->debug('No lists received');
823
- }
824
-
825
- // Opt-in mode
826
- if (!empty($this->options['optin_override']) && isset($_REQUEST['optin'])) {
827
- switch ($_REQUEST['optin']) {
828
- case 'single': $subscription->optin = 'single';
829
- break;
830
- case 'double': $subscription->optin = 'double';
831
- break;
832
- }
833
- }
834
-
835
- return $subscription;
836
- }
837
-
838
- /**
839
- * Processes the request and fill in the *array* representing a subscriber with submitted values
840
- * (filtering when necessary).
841
- *
842
- * @deprecated since version 6.9.0
843
- * @param array $user An array partially filled with subscriber data
844
- * @return array The filled array representing a subscriber
845
- */
846
- function update_user_from_request($user) {
847
-
848
- if (isset($_REQUEST['nn'])) {
849
- $user['name'] = $this->normalize_name(stripslashes($_REQUEST['nn']));
850
- }
851
- // TODO: required checking
852
-
853
- if (isset($_REQUEST['ns'])) {
854
- $user['surname'] = $this->normalize_name(stripslashes($_REQUEST['ns']));
855
- }
856
- // TODO: required checking
857
-
858
- if (!empty($_REQUEST['nx'])) {
859
- $user['sex'] = $this->normalize_sex($_REQUEST['nx'][0]);
860
- }
861
- // TODO: valid values check
862
-
863
- if (isset($_REQUEST['nr'])) {
864
- $user['referrer'] = strip_tags(trim($_REQUEST['nr']));
865
- }
866
-
867
- $language = '';
868
- if (!empty($_REQUEST['nlang'])) {
869
- $language = strtolower(strip_tags($_REQUEST['nlang']));
870
- // TODO: Check if it's an allowed language code
871
- $user['language'] = $language;
872
- } else {
873
- $language = $this->get_current_language();
874
- $user['language'] = $language;
875
- }
876
-
877
- // From the antibot form
878
- if (isset($_REQUEST['nhr'])) {
879
- $user['http_referer'] = strip_tags(trim($_REQUEST['nhr']));
880
- } else if (isset($_SERVER['HTTP_REFERER'])) {
881
- $user['http_referer'] = strip_tags(trim($_SERVER['HTTP_REFERER']));
882
- }
883
-
884
- if (strlen($user['http_referer']) > 200) {
885
- $user['http_referer'] = mb_substr($user['http_referer'], 0, 200);
886
- }
887
-
888
- // New profiles
889
- for ($i = 1; $i <= NEWSLETTER_PROFILE_MAX; $i++) {
890
- // If the profile cannot be set by subscriber, skip it.
891
- if ($this->options_profile['profile_' . $i . '_status'] == 0) {
892
- continue;
893
- }
894
- if (isset($_REQUEST['np' . $i])) {
895
- $user['profile_' . $i] = trim(stripslashes($_REQUEST['np' . $i]));
896
- }
897
- }
898
-
899
- // Extra validation to explain the administrator while the submitted data could
900
- // be interpreted only partially
901
- if (current_user_can('administrator')) {
902
- if (isset($_REQUEST['nl']) && is_array($_REQUEST['nl'])) {
903
- foreach ($_REQUEST['nl'] as $list_id) {
904
- $list = $this->get_list($list_id);
905
- if ($list && $list->status == TNP_List::STATUS_PRIVATE) {
906
- $this->dienow('Invalid list', '[old] List ' . $list_id . ' has been submitted but it is set as private. Please fix the subscription form.');
907
- }
908
- }
909
- }
910
- }
911
- // Preferences (field names are nl[] and values the list number so special forms with radio button can work)
912
- // Permetto l'aggiunta solo delle liste pubbliche
913
- if (isset($_REQUEST['nl']) && is_array($_REQUEST['nl'])) {
914
- $lists = $this->get_lists_public();
915
- //$this->logger->debug($_REQUEST['nl']);
916
- foreach ($lists as $list) {
917
- if (in_array('' . $list->id, $_REQUEST['nl'])) {
918
- $user['list_' . $list->id] = 1;
919
- }
920
- }
921
- } else {
922
- $this->logger->debug('No lists received');
923
- }
924
-
925
- // Forced lists (general or by language)
926
- // Forzo l'aggiunta delle liste forzate
927
- $lists = $this->get_lists();
928
- foreach ($lists as $list) {
929
- if ($list->forced) {
930
- $user['list_' . $list->id] = 1;
931
- }
932
- if (in_array($language, $list->languages)) {
933
- $user['list_' . $list->id] = 1;
934
- }
935
- }
936
-
937
- // TODO: should be removed!!!
938
- if (defined('NEWSLETTER_FEED_VERSION')) {
939
- $options_feed = get_option('newsletter_feed', array());
940
- if ($options_feed['add_new'] == 1) {
941
- $user['feed'] = 1;
942
- }
943
- }
944
- return $user;
945
- }
946
-
947
- /**
948
- * Sends a service message applying the template to the HTML part
949
- *
950
- * @param TNP_User $user
951
- * @param string $subject
952
- * @param string|array $message If string it is considered HTML, if array it should contains the key "html" and "text"
953
- * @return type
954
- */
955
- function mail($user, $subject, $message) {
956
- $language = $this->get_user_language($user);
957
-
958
- $options_template = $this->get_options('template', $language);
959
-
960
- $template = trim($options_template['template']);
961
- if (empty($template) || strpos($template, '{message}') === false) {
962
- $template = '{message}';
963
- }
964
-
965
- if (is_array($message)) {
966
- $message['html'] = str_replace('{message}', $message['html'], $template);
967
- $message['html'] = $this->replace($message['html'], $user);
968
- $message['text'] = $this->replace($message['text'], $user);
969
- } else {
970
- $message = str_replace('{message}', $message, $template);
971
- $message = $this->replace($message, $user);
972
- }
973
-
974
- $headers = [];
975
-
976
- // Replaces tags from the template
977
-
978
- $subject = $this->replace($subject, $user);
979
-
980
- return Newsletter::instance()->mail($user->email, $subject, $message, $headers);
981
- }
982
-
983
- /**
984
- * Confirms a subscription changing the user status and, possibly, merging the
985
- * temporary data if present.
986
- *
987
- * @param TNP_User $user Optionally it can be null (user search from requests paramaters, but deprecated, or a user id)
988
- * @return TNP_User
989
- */
990
- function confirm($user = null, $emails = true) {
991
-
992
- // Compatibility with WP Registration Addon
993
- if (!$user) {
994
- $user = $this->get_user_from_request(true);
995
- } else if (is_numeric($user)) {
996
- $user = $this->get_user($user);
997
- }
998
-
999
- if (!$user) {
1000
- $this->dienow('Subscriber not found', '', 404);
1001
- }
1002
- // End compatibility
1003
- // Should be merged?
1004
- $data = get_transient('newsletter_subscription_' . $user->id);
1005
- if ($data !== false) {
1006
- $data->merge_in($user);
1007
- //$this->merge($user, $data);
1008
- $user = $this->save_user($user);
1009
- $user->status = TNP_User::STATUS_NOT_CONFIRMED;
1010
- delete_transient('newsletter_subscription_' . $user->id);
1011
- } else {
1012
- $new_email = get_transient('newsletter_user_' . $user->id . '_email');
1013
- if ($new_email) {
1014
- $data = ['id' => $user->id, 'email' => $new_email];
1015
- $this->save_user($data);
1016
- delete_transient('newsletter_user_' . $user->id . '_email');
1017
- }
1018
- }
1019
-
1020
-
1021
- $this->update_user_last_activity($user);
1022
-
1023
- setcookie('newsletter', $user->id . '-' . $user->token, time() + 60 * 60 * 24 * 365, '/');
1024
-
1025
- if ($user->status == TNP_User::STATUS_CONFIRMED) {
1026
- $this->add_user_log($user, 'activate');
1027
- do_action('newsletter_user_confirmed', $user);
1028
- return $user;
1029
- }
1030
-
1031
- $this->set_user_status($user, TNP_User::STATUS_CONFIRMED);
1032
-
1033
- $user = $this->get_user($user);
1034
-
1035
- $this->add_user_log($user, 'activate');
1036
-
1037
- do_action('newsletter_user_confirmed', $user);
1038
- $this->notify_admin_on_subscription($user);
1039
-
1040
- if ($emails) {
1041
- $this->send_message('confirmed', $user);
1042
- }
1043
-
1044
- return $user;
1045
- }
1046
-
1047
- /**
1048
- * Sends a message (activation, welcome, cancellation, ...) with the correct template
1049
- * and checking if the message itself is disabled
1050
- *
1051
- * @param string $type
1052
- * @param TNP_User $user
1053
- */
1054
- function send_message($type, $user, $force = false) {
1055
- if (!$force && !empty($this->options[$type . '_disabled'])) {
1056
- return true;
1057
- }
1058
-
1059
- $language = $this->get_user_language($user);
1060
-
1061
- $options = $this->get_options('', $language);
1062
- $message = [];
1063
- $message['html'] = do_shortcode($options[$type . '_message']);
1064
- $message['text'] = $this->get_text_message($type);
1065
- if ($user->status == TNP_User::STATUS_NOT_CONFIRMED) {
1066
- $message['html'] = $this->add_microdata($message['html']);
1067
- }
1068
- $subject = $options[$type . '_subject'];
1069
-
1070
- return $this->mail($user, $subject, $message);
1071
- }
1072
-
1073
- function get_text_message($type) {
1074
- switch ($type) {
1075
- case 'confirmation':
1076
- return __('To confirm your subscription follow the link below.', 'newsletter') . "\n\n{subscription_confirm_url}";
1077
- case 'confirmed':
1078
- return __('Your subscription has been confirmed.', 'newsletter');
1079
- }
1080
- return '';
1081
- }
1082
-
1083
- function is_double_optin() {
1084
- return $this->options['noconfirmation'] == 0;
1085
- }
1086
-
1087
- /**
1088
- * Sends the activation email without conditions.
1089
- *
1090
- * @param stdClass $user
1091
- * @return bool
1092
- */
1093
- function send_activation_email($user) {
1094
- return $this->send_message('confirmation', $user, true);
1095
- }
1096
-
1097
- /**
1098
- * Finds the right way to show the message identified by $key (welcome, unsubscription, ...) redirecting the user to the
1099
- * WordPress page or loading the configured url or activating the standard page.
1100
- */
1101
- function show_message($key, $user, $alert = '', $email = null) {
1102
- $url = '';
1103
-
1104
- if (isset($_REQUEST['ncu'])) {
1105
- // Custom URL from the form
1106
- $url = $_REQUEST['ncu'];
1107
- } else {
1108
- // Per message custom URL from configuration (language variants could not be supported)
1109
- $options = $this->get_options('', $this->get_user_language($user));
1110
- if (!empty($options[$key . '_url'])) {
1111
- $url = $options[$key . '_url'];
1112
- }
1113
- }
1114
-
1115
- $url = Newsletter::instance()->build_message_url($url, $key, $user, $email, $alert);
1116
- wp_redirect($url);
1117
-
1118
- die();
1119
- }
1120
-
1121
- function get_message_key_from_request() {
1122
- if (empty($_GET['nm'])) {
1123
- return 'subscription';
1124
- }
1125
- $key = $_GET['nm'];
1126
- switch ($key) {
1127
- case 's': return 'confirmation';
1128
- case 'c': return 'confirmed';
1129
- case 'u': return 'unsubscription';
1130
- case 'uc': return 'unsubscribed';
1131
- case 'p':
1132
- case 'pe':
1133
- return 'profile';
1134
- default: return $key;
1135
- }
1136
- }
1137
-
1138
- var $privacy_url = false;
1139
-
1140
- /**
1141
- * Generates the privacy URL and cache it.
1142
- *
1143
- * @return string
1144
- */
1145
- function get_privacy_url() {
1146
- if ($this->privacy_url === false) {
1147
- $this->setup_form_options();
1148
- if (!empty($this->form_options['privacy_use_wp_url']) && function_exists('get_privacy_policy_url')) {
1149
- $this->privacy_url = get_privacy_policy_url();
1150
- } else {
1151
- $this->privacy_url = $this->form_options['privacy_url'];
1152
- }
1153
- }
1154
- return $this->privacy_url;
1155
- }
1156
-
1157
- function get_form_javascript() {
1158
-
1159
- }
1160
-
1161
- /**
1162
- * Manages the custom forms made with [newsletter_form] and internal [newsletter_field] shortcodes.
1163
- *
1164
- * @param array $attrs
1165
- * @param string $content
1166
- * @return string
1167
- */
1168
- function get_subscription_form_custom($attrs = [], $content = '') {
1169
- if (!is_array($attrs)) {
1170
- $attrs = [];
1171
- }
1172
-
1173
- $this->setup_form_options();
1174
-
1175
- $attrs = array_merge(['class' => 'tnp-subscription', 'style' => ''], $attrs);
1176
-
1177
- $action = esc_attr($this->build_action_url('s'));
1178
- $class = esc_attr($attrs['class']);
1179
- $style = esc_attr($attrs['style']);
1180
- $buffer = '<form method="post" action="' . $action . '" class="' . $class . '" style="' . $style . '">' . "\n";
1181
-
1182
- $language = $this->get_current_language();
1183
-
1184
- $buffer .= $this->get_form_hidden_fields($attrs);
1185
-
1186
- $buffer .= do_shortcode($content);
1187
-
1188
- if (isset($attrs['button_label'])) {
1189
- $label = $attrs['button_label'];
1190
- } else {
1191
- $label = $this->form_options['subscribe'];
1192
- }
1193
-
1194
- if (!empty($label)) {
1195
- $buffer .= '<div class="tnp-field tnp-field-button">';
1196
- if (strpos($label, 'http') === 0) {
1197
- $buffer .= '<input class="tnp-button-image" type="image" src="' . $label . '">';
1198
- } else {
1199
- $buffer .= '<input class="tnp-button" type="submit" value="' . $label . '">';
1200
- }
1201
- $buffer .= '</div>';
1202
- }
1203
-
1204
- $buffer .= '</form>';
1205
-
1206
- return $buffer;
1207
- }
1208
-
1209
- /** Generates the hidden field for lists which should be implicitely set with a subscription form.
1210
- *
1211
- * @param string $lists Comma separated directly from the shortcode "lists" attribute
1212
- * @param string $language ???
1213
- * @return string
1214
- */
1215
- function get_form_implicit_lists($lists, $language = '') {
1216
- $buffer = '';
1217
-
1218
- $arr = explode(',', $lists);
1219
-
1220
- foreach ($arr as $a) {
1221
- $a = trim($a);
1222
- if (empty($a))
1223
- continue;
1224
-
1225
- $list = $this->get_list($a);
1226
- if (!$list) {
1227
- $buffer .= $this->build_field_admin_notice('List "' . $a . '" added to the form is not configured, skipped.');
1228
- continue;
1229
- }
1230
-
1231
- if ($list->is_private()) {
1232
- $buffer .= $this->build_field_admin_notice('List ' . $a . ' is private cannot be used in a public form.');
1233
- continue;
1234
- }
1235
-
1236
- if ($list->forced) {
1237
- $buffer .= $this->build_field_admin_notice('List ' . $a . ' is already enforced on every subscription there is no need to specify it.');
1238
- continue;
1239
- }
1240
-
1241
- $buffer .= "<input type='hidden' name='nl[]' value='" . esc_attr($a) . "'>\n";
1242
- }
1243
- return $buffer;
1244
- }
1245
-
1246
- /**
1247
- * Builds all the hidden fields of a subscription form. Implicit lists, confirmation url,
1248
- * referrer, language, ...
1249
- *
1250
- * @param array $attrs Attributes of form shortcode
1251
- * @return string HTML with the hidden fields
1252
- */
1253
- function get_form_hidden_fields($attrs) {
1254
- $b = '';
1255
-
1256
- // Compatibility
1257
- if (isset($attrs['list'])) {
1258
- $attrs['lists'] = $attrs['list'];
1259
- }
1260
- if (isset($attrs['lists'])) {
1261
- $b .= $this->get_form_implicit_lists($attrs['lists']);
1262
- }
1263
-
1264
- if (isset($attrs['referrer'])) {
1265
- $b .= '<input type="hidden" name="nr" value="' . esc_attr($attrs['referrer']) . '">';
1266
- }
1267
-
1268
- if (isset($attrs['confirmation_url'])) {
1269
- if ($attrs['confirmation_url'] === '#') {
1270
- $attrs['confirmation_url'] = esc_url_raw($_SERVER['REQUEST_URI']);
1271
- }
1272
-
1273
- $b .= '<input type="hidden" name="ncu" value="' . esc_attr($attrs['confirmation_url']) . '">';
1274
- }
1275
-
1276
- if (isset($attrs['optin'])) {
1277
- $optin = trim(strtolower($attrs['optin']));
1278
- if ($optin !== 'double' && $optin !== 'single') {
1279
- $b .= $this->build_field_admin_notice('The optin is set to an invalid value.');
1280
- } else {
1281
- if ($optin !== 'double' && $this->is_double_optin() && empty($this->options['optin_override'])) {
1282
- $b .= $this->build_field_admin_notice('The optin is specified but cannot be overridden (see the subscription configiraton page).');
1283
- } else {
1284
- $b .= '<input type="hidden" name="optin" value="' . esc_attr($optin) . '">';
1285
- }
1286
- }
1287
- }
1288
-
1289
- $language = $this->get_current_language();
1290
- $b .= '<input type="hidden" name="nlang" value="' . esc_attr($language) . '">';
1291
-
1292
- return $b;
1293
- }
1294
-
1295
- /**
1296
- * Internal use only
1297
- *
1298
- * @param type $name
1299
- * @param type $attrs
1300
- * @param type $suffix
1301
- * @return string
1302
- */
1303
- private function _shortcode_label($name, $attrs, $suffix = null) {
1304
-
1305
- if (!$suffix) {
1306
- $suffix = $name;
1307
- }
1308
- $buffer = '<label for="' . esc_attr($attrs['id']) . '">';
1309
- if (isset($attrs['label'])) {
1310
- if (empty($attrs['label'])) {
1311
- return;
1312
- } else {
1313
- $buffer .= esc_html($attrs['label']);
1314
- }
1315
- } else {
1316
- if (isset($this->form_options[$name])) {
1317
- $buffer .= esc_html($this->form_options[$name]);
1318
- }
1319
- }
1320
- $buffer .= "</label>\n";
1321
- return $buffer;
1322
- }
1323
-
1324
- /**
1325
- * Creates a notices to be displayed near a subscription form field to inform of worng configurations.
1326
- * It is created only if the current user looking at the form is the administrator.
1327
- *
1328
- * @param string $message
1329
- * @return string
1330
- */
1331
- function build_field_admin_notice($message) {
1332
- if (!current_user_can('administrator')) {
1333
- return '';
1334
- }
1335
- return '<p style="background-color: #eee; color: #000; padding: 10px; margin: 10px 0">' . $message . ' <strong>This notice is shown only to administrators to help with configuration.</strong></p>';
1336
- }
1337
-
1338
- function shortcode_newsletter_field($attrs, $content = '') {
1339
- // Counter to create unique ID for checkbox and labels
1340
- static $idx = 0;
1341
-
1342
- $idx++;
1343
- $attrs['id'] = 'tnp-' . $idx;
1344
-
1345
- $this->setup_form_options();
1346
- $language = $this->get_current_language();
1347
-
1348
- $name = $attrs['name'];
1349
-
1350
- $buffer = '';
1351
-
1352
- if ($name == 'email') {
1353
- $buffer .= '<div class="tnp-field tnp-field-email">';
1354
-
1355
- $buffer .= $this->_shortcode_label('email', $attrs);
1356
-
1357
- $buffer .= '<input class="tnp-email" type="email" name="ne" id="' . esc_attr($attrs['id']) . '" value=""';
1358
- if (isset($attrs['placeholder'])) {
1359
- $buffer .= ' placeholder="' . esc_attr($attrs['placeholder']) . '"';
1360
- }
1361
- $buffer .= ' required>';
1362
- if (isset($attrs['button_label'])) {
1363
- $label = $attrs['button_label'];
1364
- if (strpos($label, 'http') === 0) {
1365
- $buffer .= ' <input class="tnp-submit-image" type="image" src="' . esc_attr(esc_url_raw($label)) . '">';
1366
- } else {
1367
- $buffer .= ' <input class="tnp-submit" type="submit" value="' . esc_attr($label) . '" style="width: 29%">';
1368
- }
1369
- }
1370
- $buffer .= "</div>\n";
1371
- return $buffer;
1372
- }
1373
-
1374
- if ($name == 'first_name' || $name == 'name') {
1375
- $buffer .= '<div class="tnp-field tnp-field-firstname">';
1376
- $buffer .= $this->_shortcode_label('name', $attrs);
1377
-
1378
- $buffer .= '<input class="tnp-name" type="text" name="nn" id="' . esc_attr($attrs['id']) . '" value=""';
1379
- if (isset($attrs['placeholder'])) {
1380
- $buffer .= ' placeholder="' . esc_attr($attrs['placeholder']) . '"';
1381
- }
1382
- if ($this->form_options['name_rules'] == 1) {
1383
- $buffer .= ' required';
1384
- }
1385
- $buffer .= '>';
1386
- $buffer .= "</div>\n";
1387
- return $buffer;
1388
- }
1389
-
1390
- if ($name == 'last_name' || $name == 'surname') {
1391
- $buffer .= '<div class="tnp-field tnp-field-surname">';
1392
- $buffer .= $this->_shortcode_label('surname', $attrs);
1393
-
1394
- $buffer .= '<input class="tnp-surname" type="text" name="ns" id="' . esc_attr($attrs['id']) . '" value=""';
1395
- if (isset($attrs['placeholder'])) {
1396
- $buffer .= ' placeholder="' . esc_attr($attrs['placeholder']) . '"';
1397
- }
1398
- if ($this->form_options['surname_rules'] == 1) {
1399
- $buffer .= ' required';
1400
- }
1401
- $buffer .= '>';
1402
- $buffer .= '</div>';
1403
- return $buffer;
1404
- }
1405
-
1406
- // Single list
1407
- if ($name == 'preference' || $name == 'list') {
1408
- if (!isset($attrs['number'])) {
1409
- return $this->build_field_admin_notice('List number not specified.');
1410
- }
1411
- $number = (int) $attrs['number'];
1412
- $list = $this->get_list($number, $language);
1413
- if (!$list) {
1414
- return $this->build_field_admin_notice('List ' . $number . ' is not configured, cannot be shown.');
1415
- }
1416
-
1417
- if ($list->status == 0 || $list->forced) {
1418
- return $this->build_field_admin_notice('List ' . $number . ' is private or enforced cannot be shown.');
1419
- }
1420
-
1421
- if (isset($attrs['hidden'])) {
1422
- return '<input type="hidden" name="nl[]" value="' . esc_attr($list->id) . '">';
1423
- }
1424
-
1425
- $idx++;
1426
- $buffer .= '<div class="tnp-field tnp-field-checkbox tnp-field-list"><label for="tnp-' . $idx . '">';
1427
- $buffer .= '<input type="checkbox" id="tnp-' . $idx . '" name="nl[]" value="' . esc_attr($list->id) . '"';
1428
- if (isset($attrs['checked'])) {
1429
- $buffer .= ' checked';
1430
- }
1431
- $buffer .= '>';
1432
- if (isset($attrs['label'])) {
1433
- if ($attrs['label'] != '') {
1434
- $buffer .= '&nbsp;' . esc_html($attrs['label']) . '</label>';
1435
- }
1436
- } else {
1437
- $buffer .= '&nbsp;' . esc_html($list->name) . '</label>';
1438
- }
1439
- $buffer .= "</div>\n";
1440
-
1441
- return $buffer;
1442
- }
1443
-
1444
- // All lists
1445
- if ($name == 'lists' || $name == 'preferences') {
1446
- $lists = $this->get_lists_for_subscription($language);
1447
- if (!empty($lists) && isset($attrs['layout']) && $attrs['layout'] === 'dropdown') {
1448
-
1449
- $buffer .= '<div class="tnp-field tnp-lists">';
1450
- // There is not a default "label" for the block of lists, so it can only be specified in the shortcode attrs as "label"
1451
- $buffer .= $this->_shortcode_label('lists', $attrs);
1452
- $buffer .= '<select class="tnp-lists" name="nl[]" required>';
1453
-
1454
- if (!empty($attrs['first_option_label'])) {
1455
- $buffer .= '<option value="" selected="true" disabled="disabled">' . esc_html($attrs['first_option_label']) . '</option>';
1456
- }
1457
-
1458
- foreach ($lists as $list) {
1459
- $buffer .= '<option value="' . $list->id . '">' . esc_html($list->name) . '</option>';
1460
- }
1461
- $buffer .= '</select>';
1462
- $buffer .= '</div>';
1463
- } else {
1464
-
1465
- foreach ($lists as $list) {
1466
- $idx++;
1467
- $buffer .= '<div class="tnp-field tnp-field-checkbox tnp-field-list"><label for="nl' . $idx . '">';
1468
- $buffer .= '<input type="checkbox" id="nl' . $idx . '" name="nl[]" value="' . $list->id . '"';
1469
- if ($list->checked) {
1470
- $buffer .= ' checked';
1471
- }
1472
- $buffer .= '>&nbsp;' . esc_html($list->name) . '</label>';
1473
- $buffer .= "</div>\n";
1474
- }
1475
- }
1476
- return $buffer;
1477
- }
1478
-
1479
- if ($name == 'sex' || $name == 'gender') {
1480
- $buffer .= '<div class="tnp-field tnp-field-gender">';
1481
- $buffer .= $this->_shortcode_label('sex', $attrs);
1482
-
1483
- $buffer .= '<select name="nx" class="tnp-gender" id="tnp-gender"';
1484
- if ($this->form_options['sex_rules']) {
1485
- $buffer .= ' required ';
1486
- }
1487
- $buffer .= '>';
1488
- if ($this->form_options['sex_rules']) {
1489
- $buffer .= '<option value=""></option>';
1490
- }
1491
- $buffer .= '<option value="n">' . esc_html($this->form_options['sex_none']) . '</option>';
1492
- $buffer .= '<option value="f">' . esc_html($this->form_options['sex_female']) . '</option>';
1493
- $buffer .= '<option value="m">' . esc_html($this->form_options['sex_male']) . '</option>';
1494
- $buffer .= '</select>';
1495
- $buffer .= "</div>\n";
1496
- return $buffer;
1497
- }
1498
-
1499
- if ($name == 'profile') {
1500
- if (!isset($attrs['number'])) {
1501
- return $this->build_field_admin_notice('Extra profile number not specified.');
1502
- }
1503
-
1504
- $number = (int) $attrs['number'];
1505
-
1506
- $profile = TNP_Profile_Service::get_profile_by_id($number, $language);
1507
-
1508
- if (!$profile) {
1509
- return $this->build_field_admin_notice('Extra profile ' . $number . ' is not configured, cannot be shown.');
1510
- }
1511
-
1512
- if ($profile->status == 0) {
1513
- return $this->build_field_admin_notice('Extra profile ' . $number . ' is private, cannot be shown.');
1514
- }
1515
-
1516
- $size = isset($attrs['size']) ? $attrs['size'] : '';
1517
- $buffer .= '<div class="tnp-field tnp-field-profile">';
1518
- $buffer .= $this->_shortcode_label('profile_' . $profile->id, $attrs);
1519
-
1520
- $placeholder = isset($attrs['placeholder']) ? $attrs['placeholder'] : $profile->placeholder;
1521
-
1522
- // Text field
1523
- if ($profile->type == TNP_Profile::TYPE_TEXT) {
1524
- $buffer .= '<input class="tnp-profile tnp-profile-' . $number . '" id="tnp-profile_' . $number . '" type="text" size="' . esc_attr($size) . '" name="np' . $number . '" placeholder="' . esc_attr($placeholder) . '"';
1525
- if ($profile->is_required()) {
1526
- $buffer .= ' required';
1527
- }
1528
- $buffer .= '>';
1529
- }
1530
-
1531
- // Select field
1532
- if ($profile->type == TNP_Profile::TYPE_SELECT) {
1533
- $buffer .= '<select class="tnp-profile tnp-profile-' . $number . '" id="tnp-profile_' . $number . '" name="np' . $number . '"';
1534
- if ($profile->is_required()) {
1535
- $buffer .= ' required';
1536
- }
1537
- $buffer .= '>';
1538
- if (!empty($placeholder)) {
1539
- $buffer .= '<option value="" selected disabled>' . esc_html($placeholder) . '</option>';
1540
- }
1541
- foreach ($profile->options as $option) {
1542
- $buffer .= '<option>' . esc_html(trim($option)) . '</option>';
1543
- }
1544
- $buffer .= "</select>\n";
1545
- }
1546
-
1547
- $buffer .= "</div>\n";
1548
-
1549
- return $buffer;
1550
- }
1551
-
1552
- if (strpos($name, 'privacy') === 0) {
1553
- if (!isset($attrs['url'])) {
1554
- $attrs['url'] = $this->get_privacy_url();
1555
- }
1556
-
1557
- if (!isset($attrs['label'])) {
1558
- $attrs['label'] = $this->form_options['privacy'];
1559
- }
1560
-
1561
- $buffer .= '<div class="tnp-field tnp-field-checkbox tnp-field-privacy">';
1562
-
1563
- $idx++;
1564
- $buffer .= '<input type="checkbox" name="ny" required class="tnp-privacy" id="tnp-' . $idx . '"> ';
1565
- $buffer .= '<label for="tnp-' . $idx . '">';
1566
- if (!empty($attrs['url'])) {
1567
- $buffer .= '<a target="_blank" href="' . esc_attr($attrs['url']) . '">';
1568
- }
1569
- $buffer .= $attrs['label'];
1570
- if (!empty($attrs['url'])) {
1571
- $buffer .= '</a>';
1572
- }
1573
- $buffer .= '</label>';
1574
- $buffer .= '</div>';
1575
-
1576
- return $buffer;
1577
- }
1578
- }
1579
-
1580
- /**
1581
- * Builds the privacy field only for completely generated forms.
1582
- *
1583
- * @return string Empty id the privacy filed is not configured
1584
- */
1585
- function get_privacy_field($pre_html = '', $post_html = '') {
1586
- $this->setup_form_options();
1587
- $privacy_status = (int) $this->form_options['privacy_status'];
1588
- if (empty($privacy_status)) {
1589
- return '';
1590
- }
1591
-
1592
- $buffer = '<label>';
1593
- if ($privacy_status === 1) {
1594
- $buffer .= '<input type="checkbox" name="ny" required class="tnp-privacy">&nbsp;';
1595
- }
1596
- $url = $this->get_privacy_url();
1597
- if (!empty($url)) {
1598
- $buffer .= '<a target="_blank" href="' . esc_attr($url) . '">';
1599
- $buffer .= esc_attr($this->form_options['privacy']) . '</a>';
1600
- } else {
1601
- $buffer .= esc_html($this->form_options['privacy']);
1602
- }
1603
-
1604
- $buffer .= "</label>";
1605
-
1606
- return $pre_html . $buffer . $post_html;
1607
- }
1608
-
1609
- /**
1610
- * The new standard form.
1611
- *
1612
- * @param string $referrer Deprecated since 6.9.1, use the "referrer" key on $attrs
1613
- * @param string $action
1614
- * @param string $attrs
1615
- * @return string The full HTML form
1616
- */
1617
- function get_subscription_form($referrer = '', $action = null, $attrs = []) {
1618
- $language = $this->get_current_language();
1619
- $options_profile = $this->get_options('profile', $language);
1620
-
1621
- if (!is_array($attrs)) {
1622
- $attrs = [];
1623
- }
1624
-
1625
- // Possible alternative form actions (used by...?)
1626
- if (isset($attrs['action'])) {
1627
- $action = $attrs['action'];
1628
- }
1629
-
1630
- // The referrer parameter is deprecated
1631
- if (!empty($referrer)) {
1632
- $attrs['referrer'] = $referrer;
1633
- }
1634
-
1635
- $buffer = '';
1636
-
1637
- if (empty($action)) {
1638
- $action = $this->build_action_url('s');
1639
- }
1640
-
1641
- if (isset($attrs['before'])) {
1642
- $buffer .= $attrs['before'];
1643
- } else {
1644
- if (isset($attrs['class'])) {
1645
- $buffer .= '<div class="tnp tnp-subscription ' . $attrs['class'] . '">' . "\n";
1646
- } else {
1647
- $buffer .= '<div class="tnp tnp-subscription">' . "\n";
1648
- }
1649
- }
1650
-
1651
- $buffer .= '<form method="post" action="' . esc_attr($action) . '">' . "\n\n";
1652
-
1653
- $buffer .= $this->get_form_hidden_fields($attrs);
1654
-
1655
- if ($options_profile['name_status'] == 2) {
1656
- $buffer .= $this->shortcode_newsletter_field(['name' => 'first_name']);
1657
- }
1658
-
1659
- if ($options_profile['surname_status'] == 2) {
1660
- $buffer .= $this->shortcode_newsletter_field(['name' => 'last_name']);
1661
- }
1662
-
1663
- $buffer .= $this->shortcode_newsletter_field(['name' => 'email']);
1664
-
1665
- if (isset($options_profile['sex_status']) && $options_profile['sex_status'] == 2) {
1666
- $buffer .= $this->shortcode_newsletter_field(['name' => 'gender']);
1667
- }
1668
-
1669
- // Extra profile fields
1670
- for ($i = 1; $i <= NEWSLETTER_PROFILE_MAX; $i++) {
1671
- // Not for subscription form
1672
- if ($options_profile['profile_' . $i . '_status'] != 2) {
1673
- continue;
1674
- }
1675
-
1676
- $buffer .= $this->shortcode_newsletter_field(['name' => 'profile', 'number' => $i]);
1677
- }
1678
-
1679
- $tmp = '';
1680
- $lists = $this->get_lists_for_subscription($language);
1681
- if (!empty($attrs['lists_field_layout']) && $attrs['lists_field_layout'] == 'dropdown') {
1682
- if (empty($attrs['lists_field_empty_label'])) {
1683
- $attrs['lists_field_empty_label'] = '';
1684
- }
1685
- if (empty($attrs['lists_field_label'])) {
1686
- $attrs['lists_field_label'] = '';
1687
- }
1688
- $buffer .= $this->shortcode_newsletter_field(['name' => 'lists', 'layout' => 'dropdown', 'first_option_label' => $attrs['lists_field_empty_label'], 'label' => $attrs['lists_field_label']]);
1689
- } else {
1690
- $buffer .= $this->shortcode_newsletter_field(['name' => 'lists']);
1691
- }
1692
-
1693
-
1694
-
1695
- // Deprecated
1696
- $extra = apply_filters('newsletter_subscription_extra', array());
1697
- foreach ($extra as $x) {
1698
- $label = $x['label'];
1699
- if (empty($label)) {
1700
- $label = '&nbsp;';
1701
- }
1702
- $name = '';
1703
- if (!empty($x['name'])) {
1704
- $name = $x['name'];
1705
- }
1706
- $buffer .= '<div class="tnp-field tnp-field-' . $name . '"><label>' . $label . "</label>";
1707
- $buffer .= $x['field'] . "</div>\n";
1708
- }
1709
-
1710
- $buffer .= $this->get_privacy_field('<div class="tnp-field tnp-privacy-field">', '</div>');
1711
-
1712
- $buffer .= '<div class="tnp-field tnp-field-button">';
1713
-
1714
- $button_style = '';
1715
- if (!empty($attrs['button_color'])) {
1716
- $button_style = 'style="background-color:' . esc_attr($attrs['button_color']) . '"';
1717
- }
1718
-
1719
- if (strpos($options_profile['subscribe'], 'http') === 0) {
1720
- $buffer .= '<input class="tnp-submit-image" type="image" src="' . esc_attr($options_profile['subscribe']) . '">' . "\n";
1721
- } else {
1722
- $buffer .= '<input class="tnp-submit" type="submit" value="' . esc_attr($options_profile['subscribe']) . '" ' . $button_style . '>' . "\n";
1723
- }
1724
-
1725
- $buffer .= "</div>\n</form>\n";
1726
-
1727
- if (isset($attrs['after'])) {
1728
- $buffer .= $attrs['after'];
1729
- } else {
1730
- $buffer .= "</div>\n";
1731
- }
1732
-
1733
- return $buffer;
1734
- }
1735
-
1736
- function get_form($number) {
1737
- $options = get_option('newsletter_forms');
1738
-
1739
- $form = $options['form_' . $number];
1740
-
1741
- $form = do_shortcode($form);
1742
-
1743
- $action = $this->build_action_url('s');
1744
-
1745
- if (stripos($form, '<form') === false) {
1746
- $form = '<form method="post" action="' . $action . '">' . $form . '</form>';
1747
- }
1748
-
1749
- // For compatibility
1750
- $form = str_replace('{newsletter_url}', $action, $form);
1751
-
1752
- $form = $this->replace_lists($form);
1753
-
1754
- return $form;
1755
- }
1756
-
1757
- /** Replaces on passed text the special tag {lists} that can be used to show the preferences as a list of checkbox.
1758
- * They are called lists but on configuration panel they are named preferences!
1759
- *
1760
- * @param string $buffer
1761
- * @return string
1762
- */
1763
- function replace_lists($buffer) {
1764
- $checkboxes = '';
1765
- $lists = $this->get_lists_for_subscription($this->get_current_language());
1766
- foreach ($lists as $list) {
1767
- $checkboxes .= '<input type="checkbox" name="nl[]" value="' . $list->id . '"/>&nbsp;' . $list->name . '<br />';
1768
- }
1769
- $buffer = str_replace('{lists}', $checkboxes, $buffer);
1770
- $buffer = str_replace('{preferences}', $checkboxes, $buffer);
1771
- return $buffer;
1772
- }
1773
-
1774
- function notify_admin_on_subscription($user) {
1775
-
1776
- if (empty($this->options['notify'])) {
1777
- return;
1778
- }
1779
-
1780
- $message = $this->generate_admin_notification_message($user);
1781
- $email = trim($this->options['notify_email']);
1782
- $subject = $this->generate_admin_notification_subject('New subscription');
1783
-
1784
- Newsletter::instance()->mail($email, $subject, array('html' => $message));
1785
- }
1786
-
1787
- /**
1788
- * Builds the minimal subscription form, with only the email field and inline
1789
- * submit button. If enabled the privacy checkbox is added.
1790
- *
1791
- * @param type $attrs
1792
- * @return string
1793
- */
1794
- function get_subscription_form_minimal($attrs) {
1795
-
1796
- $this->setup_form_options();
1797
-
1798
- if (!is_array($attrs)) {
1799
- $attrs = [];
1800
- }
1801
-
1802
- $attrs = array_merge(array('class' => '', 'referrer' => 'minimal',
1803
- 'button' => $this->form_options['subscribe'], 'button_color' => '',
1804
- 'button_radius' => '', 'placeholder' => $this->form_options['email']), $attrs);
1805
-
1806
- $form = '';
1807
-
1808
- $form .= '<div class="tnp tnp-subscription-minimal ' . $attrs['class'] . '">';
1809
- $form .= '<form action="' . esc_attr($this->build_action_url('s')) . '" method="post">';
1810
-
1811
- $form .= $this->get_form_hidden_fields($attrs);
1812
-
1813
- $form .= '<input class="tnp-email" type="email" required name="ne" value="" placeholder="' . esc_attr($attrs['placeholder']) . '">';
1814
-
1815
- if (isset($attrs['button_label'])) {
1816
- $label = $attrs['button_label'];
1817
- } else if (isset($attrs['button'])) { // Backward compatibility
1818
- $label = $attrs['button'];
1819
- } else {
1820
- $label = $this->form_options['subscribe'];
1821
- }
1822
-
1823
- $form .= '<input class="tnp-submit" type="submit" value="' . esc_attr($attrs['button']) . '"'
1824
- . ' style="background-color:' . esc_attr($attrs['button_color']) . '">';
1825
-
1826
- $form .= $this->get_privacy_field('<div class="tnp-field tnp-privacy-field">', '</div>');
1827
-
1828
- $form .= "</form></div>\n";
1829
-
1830
- return $form;
1831
- }
1832
-
1833
- function shortcode_newsletter_form($attrs, $content) {
1834
-
1835
- if (isset($attrs['type']) && $attrs['type'] === 'minimal') {
1836
- return $this->get_subscription_form_minimal($attrs);
1837
- }
1838
-
1839
- // Custom form using the [newsletter_field] shortcodes
1840
- if (!empty($content)) {
1841
- return $this->get_subscription_form_custom($attrs, $content);
1842
- }
1843
-
1844
- // Custom form hand coded and saved in the custom forms option
1845
- if (isset($attrs['form'])) {
1846
- return $this->get_form((int) $attrs['form']);
1847
- }
1848
-
1849
- // Custom hand coded form (as above, new syntax)
1850
- if (isset($attrs['number'])) {
1851
- return $this->get_form((int) $attrs['number']);
1852
- }
1853
-
1854
- return $this->get_subscription_form(null, null, $attrs);
1855
- }
1856
-
1857
- /**
1858
- *
1859
- * @global wpdb $wpdb
1860
- * @param array $attrs
1861
- * @param string $content
1862
- * @return string
1863
- */
1864
- function shortcode_newsletter($attrs, $content) {
1865
- global $wpdb;
1866
-
1867
- $message_key = $this->get_message_key_from_request();
1868
- if ($message_key == 'confirmation') {
1869
- $user = $this->get_user_from_request(false, 'preconfirm');
1870
- } else {
1871
- $user = $this->get_user_from_request();
1872
- }
1873
-
1874
- $message = apply_filters('newsletter_page_text', '', $message_key, $user);
1875
-
1876
- $options = $this->get_options('', $this->get_current_language($user));
1877
-
1878
- if (empty($message)) {
1879
- $message = $options[$message_key . '_text'];
1880
-
1881
- // TODO: the if can be removed
1882
- if ($message_key == 'confirmed') {
1883
- $message .= $options[$message_key . '_tracking'];
1884
- }
1885
- }
1886
-
1887
- // Now check what form must be added
1888
- if ($message_key == 'subscription') {
1889
- if (isset($attrs['show_form']) && $attrs['show_form'] === 'false') {
1890
- //return $this->build_field_admin_notice('The [newsletter] shortcode is configured to not show the subscription form.');
1891
- return;
1892
- }
1893
-
1894
- // Compatibility check
1895
- if (stripos($message, '<form') !== false) {
1896
- $message = str_ireplace('<form', '<form method="post" action="' . esc_attr($this->get_subscribe_url()) . '"', $message);
1897
- } else {
1898
-
1899
- if (strpos($message, '{subscription_form') === false) {
1900
- $message .= '{subscription_form}';
1901
- }
1902
-
1903
- if (isset($attrs['form'])) {
1904
- $message = str_replace('{subscription_form}', $this->get_form($attrs['form']), $message);
1905
- } else {
1906
- $message = str_replace('{subscription_form}', $this->get_subscription_form('page', null, $attrs), $message);
1907
- }
1908
- }
1909
- }
1910
-
1911
- $email = $this->get_email_from_request();
1912
-
1913
- $message = $this->replace($message, $user, $email, 'page');
1914
-
1915
- $message = do_shortcode($message);
1916
-
1917
- if (isset($_REQUEST['alert'])) {
1918
- // slashes are already added by wordpress!
1919
- $message .= '<script>alert("' . esc_js(strip_tags($_REQUEST['alert'])) . '");</script>';
1920
- }
1921
-
1922
- return $message;
1923
- }
1924
-
1925
- }
1926
-
1927
- NewsletterSubscription::instance();
1928
-
1929
- // Compatibility code
1930
-
1931
- /**
1932
- * @deprecated
1933
- * @param int $number
1934
- */
1935
- function newsletter_form($number = null) {
1936
- if ($number != null) {
1937
- echo NewsletterSubscription::instance()->get_form($number);
1938
- } else {
1939
- echo NewsletterSubscription::instance()->get_subscription_form();
1940
- }
1941
- }
1
+ <?php
2
+
3
+ defined('ABSPATH') || exit;
4
+
5
+ class NewsletterSubscription extends NewsletterModule {
6
+
7
+ const MESSAGE_CONFIRMED = 'confirmed';
8
+ const OPTIN_DOUBLE = 0;
9
+ const OPTIN_SINGLE = 1;
10
+
11
+ static $instance;
12
+
13
+ /**
14
+ * @var array
15
+ */
16
+ var $options_profile;
17
+
18
+ /**
19
+ * Contains the options for the current language to build a subscription form. Must be initialized with
20
+ * setup_form_options() before use.
21
+ *
22
+ * @var array
23
+ */
24
+ var $form_options = null;
25
+
26
+ /**
27
+ * Contains the antibot/antispam options. Must be initialized with
28
+ * setup_antibot_options() before use.
29
+ *
30
+ * @var array
31
+ */
32
+ var $antibot_options = null;
33
+
34
+ /**
35
+ * @var array
36
+ */
37
+ var $options_lists;
38
+
39
+ /**
40
+ * @return NewsletterSubscription
41
+ */
42
+ static function instance() {
43
+ if (self::$instance == null) {
44
+ self::$instance = new NewsletterSubscription();
45
+ }
46
+ return self::$instance;
47
+ }
48
+
49
+ function __construct() {
50
+
51
+ parent::__construct('subscription', '2.2.7', null, array('lists', 'template', 'profile', 'antibot'));
52
+ $this->options_profile = $this->get_options('profile');
53
+ $this->options_lists = $this->get_options('lists');
54
+
55
+ // Must be called after the Newsletter::hook_init, since some constants are defined
56
+ // there.
57
+ add_action('init', array($this, 'hook_init'), 90);
58
+ }
59
+
60
+ function hook_init() {
61
+ add_action('newsletter_action', array($this, 'hook_newsletter_action'), 10, 3);
62
+ if (is_admin()) {
63
+ add_action('admin_init', array($this, 'hook_admin_init'));
64
+ } else {
65
+ // Shortcode for the Newsletter page
66
+ add_shortcode('newsletter', array($this, 'shortcode_newsletter'));
67
+ add_shortcode('newsletter_form', array($this, 'shortcode_newsletter_form'));
68
+ add_shortcode('newsletter_field', array($this, 'shortcode_newsletter_field'));
69
+ }
70
+ }
71
+
72
+ function hook_admin_init() {
73
+ // So the user can add JS and other code
74
+ if (isset($_GET['page']) && $_GET['page'] === 'newsletter_subscription_forms') {
75
+ header('X-XSS-Protection: 0');
76
+ }
77
+
78
+ if (function_exists('register_block_type')) {
79
+ // Add custom blocks to Gutenberg
80
+ wp_register_script('tnp-blocks', plugins_url('newsletter') . '/includes/tnp-blocks.js', array('wp-block-editor', 'wp-blocks', 'wp-element', 'wp-components'), NEWSLETTER_VERSION);
81
+ register_block_type('tnp/minimal', array('editor_script' => 'tnp-blocks'));
82
+ }
83
+ }
84
+
85
+ /**
86
+ *
87
+ * @global wpdb $wpdb
88
+ * @return mixed
89
+ */
90
+ function hook_newsletter_action($action, $user, $email) {
91
+ global $wpdb;
92
+
93
+ switch ($action) {
94
+ case 'profile-change':
95
+ if ($this->antibot_form_check()) {
96
+
97
+ if (!$user || $user->status != TNP_user::STATUS_CONFIRMED) {
98
+ $this->dienow('Subscriber not found or not confirmed.', 'Even the wrong subscriber token can lead to this error.', 404);
99
+ }
100
+
101
+ if (!$email) {
102
+ $this->dienow('Newsletter not found', 'The newsletter containing the link has been deleted.', 404);
103
+ }
104
+
105
+ if (isset($_REQUEST['list'])) {
106
+ $list_id = (int) $_REQUEST['list'];
107
+
108
+ // Check if the list is public
109
+ $list = $this->get_list($list_id);
110
+ if (!$list || $list->status == TNP_List::STATUS_PRIVATE) {
111
+ $this->dienow('List change not allowed.', 'Please check if the list is marked as "private".', 400);
112
+ }
113
+
114
+ if (empty($_REQUEST['redirect'])) {
115
+ $url = home_url();
116
+ } else {
117
+ $url = esc_url_raw($_REQUEST['redirect']);
118
+ }
119
+ $this->set_user_list($user, $list_id, $_REQUEST['value']);
120
+
121
+ $user = $this->get_user($user->id);
122
+ $this->add_user_log($user, 'cta');
123
+ NewsletterStatistics::instance()->add_click($url, $user->id, $email->id);
124
+ wp_redirect($url);
125
+ die();
126
+ }
127
+ } else {
128
+ $this->request_to_antibot_form('Continue');
129
+ }
130
+
131
+ die();
132
+
133
+ case 'm':
134
+ case 'message':
135
+ include dirname(__FILE__) . '/page.php';
136
+ die();
137
+
138
+ // normal subscription
139
+ case 's':
140
+ case 'subscribe':
141
+
142
+ if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
143
+ $this->dienow('Invalid request', 'The subscription request was not made with a HTTP POST', 400);
144
+ }
145
+
146
+ $options_antibot = $this->get_options('antibot');
147
+
148
+ $captcha = !empty($options_antibot['captcha']);
149
+
150
+ if (!empty($_GET['_wp_amp_action_xhr_converted']) || !empty($options_antibot['disabled']) || $this->antibot_form_check($captcha)) {
151
+
152
+ $subscription = $this->build_subscription();
153
+
154
+ $user = $this->subscribe2($subscription);
155
+
156
+ if (is_wp_error($user)) {
157
+ if ($user->get_error_code() === 'exists') {
158
+ $language = isset($_REQUEST['nlang']) ? $_REQUEST['nlang'] : '';
159
+ $options = $this->get_options('', $language);
160
+ $this->dienow($options['error_text'], $user->get_error_message(), 200);
161
+ }
162
+ $this->dienow('Registration failed.', $user->get_error_message(), 400);
163
+ }
164
+
165
+ if ($user->status == TNP_User::STATUS_CONFIRMED) {
166
+ $this->show_message('confirmed', $user);
167
+ }
168
+ if ($user->status == TNP_User::STATUS_NOT_CONFIRMED) {
169
+ $this->show_message('confirmation', $user);
170
+ }
171
+ } else {
172
+ $language = isset($_REQUEST['nlang']) ? $_REQUEST['nlang'] : '';
173
+ $options = $this->get_form_options($language);
174
+ $this->request_to_antibot_form($options['subscribe'], $captcha);
175
+ }
176
+ die();
177
+
178
+ // AJAX subscription
179
+ case 'ajaxsub':
180
+
181
+ if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
182
+ $this->dienow('Invalid request');
183
+ }
184
+
185
+ $subscription = $this->build_subscription();
186
+
187
+ $user = $this->subscribe2($subscription);
188
+
189
+ if (is_wp_error($user)) {
190
+ if ($user->get_error_code() === 'exists') {
191
+ echo $this->options['error_text'];
192
+ die();
193
+ } else {
194
+ $this->dienow('Registration failed.', $user->get_error_message(), 400);
195
+ }
196
+ } else {
197
+ if ($user->status == TNP_User::STATUS_CONFIRMED) {
198
+ $key = 'confirmed';
199
+ }
200
+
201
+ if ($user->status == TNP_User::STATUS_NOT_CONFIRMED) {
202
+ $key = 'confirmation';
203
+ }
204
+ }
205
+
206
+ $message = $this->replace($this->options[$key . '_text'], $user);
207
+ if (isset($this->options[$key . '_tracking'])) {
208
+ $message .= $this->options[$key . '_tracking'];
209
+ }
210
+ echo $message;
211
+ die();
212
+
213
+ case 'c':
214
+ case 'confirm':
215
+ if (!$user) {
216
+ $this->dienow(__('Subscriber not found.', 'newsletter'), 'Or it is not present or the secret key does not match.', 404);
217
+ }
218
+
219
+ if ($this->antibot_form_check()) {
220
+ $user = $this->confirm($user);
221
+ setcookie('newsletter', $user->id . '-' . $user->token, time() + 60 * 60 * 24 * 365, '/');
222
+ $this->show_message('confirmed', $user);
223
+ } else {
224
+ $this->request_to_antibot_form('Confirm');
225
+ }
226
+ die();
227
+ break;
228
+
229
+ default:
230
+ return;
231
+ }
232
+ }
233
+
234
+ function upgrade() {
235
+ global $wpdb, $charset_collate;
236
+
237
+ parent::upgrade();
238
+
239
+ $newsletter = Newsletter::instance();
240
+ $lists_options = $this->get_options('lists');
241
+ $profile_options = $this->get_options('profile');
242
+
243
+ if (empty($lists_options)) {
244
+ foreach ($profile_options as $key => $value) {
245
+ if (strpos($key, 'list_') === 0) {
246
+ $lists_options[$key] = $value;
247
+ }
248
+ }
249
+ }
250
+
251
+ for ($i = 1; $i <= NEWSLETTER_LIST_MAX; $i++) {
252
+ // Options migration to the new set
253
+ if (!empty($profile_options['list_' . $i]) && empty($lists_options['list_' . $i])) {
254
+ $lists_options['list_' . $i] = $profile_options['list_' . $i];
255
+ $lists_options['list_' . $i . '_checked'] = $profile_options['list_' . $i . '_checked'];
256
+ $lists_options['list_' . $i . '_forced'] = $profile_options['list_' . $i . '_forced'];
257
+ }
258
+
259
+ if (!isset($profile_options['list_' . $i . '_forced'])) {
260
+ $profile_options['list_' . $i . '_forced'] = empty($this->options['preferences_' . $i]) ? 0 : 1;
261
+ $lists_options['list_' . $i . '_forced'] = empty($this->options['preferences_' . $i]) ? 0 : 1;
262
+ }
263
+ }
264
+
265
+ $this->save_options($profile_options, 'profile');
266
+ $this->save_options($lists_options, 'lists');
267
+
268
+ $default_options = $this->get_default_options();
269
+
270
+ if (empty($this->options['error_text'])) {
271
+ $this->options['error_text'] = $default_options['error_text'];
272
+ $this->save_options($this->options);
273
+ }
274
+
275
+ $this->init_options('template', false);
276
+
277
+ global $wpdb, $charset_collate;
278
+
279
+ require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
280
+
281
+ $sql = "CREATE TABLE `" . $wpdb->prefix . "newsletter_user_logs` (
282
+ `id` int(11) NOT NULL AUTO_INCREMENT,
283
+ `user_id` int(11) NOT NULL DEFAULT 0,
284
+ `ip` varchar(50) NOT NULL DEFAULT '',
285
+ `source` varchar(50) NOT NULL DEFAULT '',
286
+ `data` longtext,
287
+ `created` int(11) NOT NULL DEFAULT 0,
288
+ PRIMARY KEY (`id`)
289
+ ) $charset_collate;";
290
+
291
+ dbDelta($sql);
292
+
293
+ return true;
294
+ }
295
+
296
+ function first_install() {
297
+
298
+ }
299
+
300
+ function admin_menu() {
301
+ $this->add_menu_page('options', __('List building', 'newsletter'));
302
+ $this->add_admin_page('lists', __('Lists', 'newsletter'));
303
+ $this->add_admin_page('profile', __('Subscription Form', 'newsletter'));
304
+ $this->add_menu_page('antibot', __('Security', 'newsletter'));
305
+ $this->add_admin_page('forms', __('Forms', 'newsletter'));
306
+ $this->add_admin_page('template', __('Template', 'newsletter'));
307
+ }
308
+
309
+ /**
310
+ * This method has been redefined for compatibility with the old options naming. It would
311
+ * be better to change them instead. The subscription options should be named
312
+ * "newsletter_subscription" while the form field options, actually named
313
+ * "newsletter_profile", should be renamed "newsletter_subscription_profile" (since
314
+ * they are retrived with get_options('profile')) or "newsletter_subscription_fields" or
315
+ * "newsletter_subscription_form".
316
+ *
317
+ * @param array $options
318
+ * @param string $sub
319
+ */
320
+ function save_options($options, $sub = '', $autoload = null, $language = '') {
321
+ if (empty($sub) && empty($language)) {
322
+ // For compatibility the options are wrongly named
323
+ return update_option('newsletter', $options, $autoload);
324
+ }
325
+
326
+ if (empty($sub) && !empty($language)) {
327
+ return update_option('newsletter_' . $language, $options, $autoload);
328
+ }
329
+
330
+ if ($sub == 'profile') {
331
+ if (empty($language)) {
332
+ $this->options_profile = $options;
333
+ return update_option('newsletter_profile', $options, $autoload);
334
+ } else {
335
+ return update_option('newsletter_profile_' . $language, $options, $autoload);
336
+ }
337
+ // For compatibility the options are wrongly named
338
+ }
339
+
340
+ if ($sub == 'forms') {
341
+ // For compatibility the options are wrongly named
342
+ return update_option('newsletter_forms', $options, $autoload);
343
+ }
344
+
345
+ if ($sub == 'lists') {
346
+ $this->options_lists = $options;
347
+ }
348
+ return parent::save_options($options, $sub, $autoload, $language);
349
+ }
350
+
351
+ function get_options($sub = '', $language = '') {
352
+ if ($sub == '') {
353
+ // For compatibility the options are wrongly named
354
+ if ($language) {
355
+ $options = get_option('newsletter_' . $language, []);
356
+ $options = array_merge(get_option('newsletter', []), $options);
357
+ } else {
358
+ $options = get_option('newsletter', []);
359
+ }
360
+ if (!is_array($options)) {
361
+ $options = array();
362
+ }
363
+
364
+ return $options;
365
+ }
366
+ if ($sub == 'profile') {
367
+ if ($language) {
368
+ // All that because for unknown reasome, sometime the options are returned as string, maybe a WPML
369
+ // interference...
370
+ $i18n_options = get_option('newsletter_profile_' . $language, []);
371
+ if (!is_array($i18n_options)) {
372
+ $i18n_options = [];
373
+ }
374
+ $options = get_option('newsletter_profile', []);
375
+ if (!is_array($options)) {
376
+ $options = [];
377
+ }
378
+ $options = array_merge($options, array_filter($i18n_options));
379
+ } else {
380
+ $options = get_option('newsletter_profile', []);
381
+ }
382
+ // For compatibility the options are wrongly named
383
+ return $options;
384
+ }
385
+ if ($sub == 'forms') {
386
+ // For compatibility the options are wrongly named
387
+ return get_option('newsletter_forms', []);
388
+ }
389
+ return parent::get_options($sub, $language);
390
+ }
391
+
392
+ /**
393
+ * Prepares the options used to build a subscription form for the current language
394
+ * in internal variable $form_options (for optimization). Can be called many times.
395
+ */
396
+ function setup_form_options() {
397
+ if (empty($this->form_options)) {
398
+ $this->form_options = $this->get_options('profile', $this->get_current_language());
399
+ }
400
+ }
401
+
402
+ function get_form_options($language = '') {
403
+ return $this->get_options('profile', $language);
404
+ }
405
+
406
+ function set_updated($user, $time = 0, $ip = '') {
407
+ global $wpdb;
408
+ if (!$time) {
409
+ $time = time();
410
+ }
411
+
412
+ if (!$ip) {
413
+ $ip = $this->get_remote_ip();
414
+ }
415
+ $ip = $this->process_ip($ip);
416
+
417
+ if (is_object($user)) {
418
+ $id = $user->id;
419
+ } else if (is_array($user)) {
420
+ $id = $user['id'];
421
+ }
422
+
423
+ $id = (int) $id;
424
+
425
+ $wpdb->update(NEWSLETTER_USERS_TABLE, array('updated' => $time, 'ip' => $ip, 'geo' => 0), array('id' => $id));
426
+ }
427
+
428
+ /**
429
+ * Sanitize the subscription data collected before process them. Cleanup the lists, the optin mode, email, first name,
430
+ * last name, adds mandatory lists, get (if not provided) and process the IP.
431
+ *
432
+ * @param TNP_Subscription_Data $data
433
+ */
434
+ private function sanitize($data) {
435
+ $data->email = $this->normalize_email($data->email);
436
+ if (!empty($data->name)) {
437
+ $data->name = $this->normalize_name($data->name);
438
+ }
439
+ if (!empty($data->surname)) {
440
+ $data->surname = $this->normalize_name($data->surname);
441
+ }
442
+
443
+ if (empty($data->ip)) {
444
+ $data->ip = $this->get_remote_ip();
445
+ }
446
+ $data->ip = $this->process_ip($data->ip);
447
+
448
+ if (isset($data->http_referer)) {
449
+ $data->http_referer = mb_substr(strip_tags($data->http_referer), 0, 200);
450
+ }
451
+
452
+ if (isset($data->sex)) {
453
+ $data->sex = $this->normalize_sex($data->sex);
454
+ }
455
+
456
+ if (!isset($data->language)) {
457
+ $data->language = $this->get_current_language();
458
+ } else {
459
+ $data->language = strtolower(strip_tags($data->language));
460
+ }
461
+
462
+ for ($i = 1; $i <= NEWSLETTER_PROFILE_MAX; $i++) {
463
+ $key = 'profile_' . $i;
464
+ if (isset($data->$key)) {
465
+ $data->$key = trim($data->$key);
466
+ }
467
+ }
468
+ }
469
+
470
+ /**
471
+ * Builds a default subscription object to be used to collect data and subscription options.
472
+ *
473
+ * @return TNP_Subscription
474
+ */
475
+ function get_default_subscription($language = null) {
476
+ $subscription = new TNP_Subscription();
477
+
478
+ $language = is_null($language) ? $this->get_current_language() : $language;
479
+
480
+ $subscription->data->language = $language;
481
+ $subscription->optin = $this->is_double_optin() ? 'double' : 'single';
482
+
483
+ if (empty($this->options['multiple'])) {
484
+ $subscription->if_exists = TNP_Subscription::EXISTING_ERROR;
485
+ } else if ($this->options['multiple'] == 1) {
486
+ $subscription->if_exists = TNP_Subscription::EXISTING_MERGE;
487
+ } else {
488
+ $subscription->if_exists = TNP_Subscription::EXISTING_SINGLE_OPTIN;
489
+ }
490
+
491
+ $lists = $this->get_lists();
492
+ foreach ($lists as $list) {
493
+ if ($list->forced) {
494
+ $subscription->data->lists['' . $list->id] = 1;
495
+ continue;
496
+ }
497
+ // Enforced by language
498
+ if ($language && in_array($language, $list->languages)) {
499
+ $subscription->data->lists['' . $list->id] = 1;
500
+ }
501
+ }
502
+
503
+ return $subscription;
504
+ }
505
+
506
+ /**
507
+ *
508
+ * @param TNP_Subscription $subscription
509
+ *
510
+ * @return TNP_User|WP_Error
511
+ */
512
+ function subscribe2(TNP_Subscription $subscription) {
513
+
514
+ $this->logger->debug($subscription);
515
+
516
+ $this->sanitize($subscription->data);
517
+
518
+ if (empty($subscription->data->email)) {
519
+ return new WP_Error('email', 'Wrong email address');
520
+ }
521
+
522
+ if (!empty($subscription->data->country) && strlen($subscription->data->country) != 2) {
523
+ return new WP_Error('country', 'Country code length error. ISO 3166-1 alpha-2 format (2 letters)');
524
+ }
525
+
526
+ // Here we should have a clean subscription data
527
+ // Filter?
528
+
529
+ if ($subscription->spamcheck) {
530
+ // TODO: Use autoload
531
+ require_once NEWSLETTER_INCLUDES_DIR . '/antispam.php';
532
+ $antispam = NewsletterAntispam::instance();
533
+ if ($antispam->is_spam($subscription)) {
534
+ return new WP_Error('spam', 'This looks like a spam subscription');
535
+ }
536
+ }
537
+
538
+ // Exists?
539
+ $user = $this->get_user_by_email($subscription->data->email);
540
+
541
+ $subscription = apply_filters('newsletter_subscription', $subscription, $user);
542
+
543
+ // Do we accept repeated subscriptions?
544
+ if ($user != null && $subscription->if_exists === TNP_Subscription::EXISTING_ERROR) {
545
+ //$this->show_message('error', $user);
546
+ return new WP_Error('exists', 'Email address already registered and Newsletter sets to block repeated registrations. You can change this behavior or the user message above on subscription configuration panel.');
547
+ }
548
+
549
+
550
+ if ($user != null) {
551
+
552
+ $this->logger->info('Subscription of an address with status ' . $user->status);
553
+
554
+ // We cannot communicate with bounced addresses, there is no reason to proceed
555
+ // TODO: Evaluate if the bounce status is very old, possible reset it
556
+ if ($user->status == TNP_User::STATUS_BOUNCED || $user->status == TNP_User::STATUS_COMPLAINED) {
557
+ return new WP_Error('bounced', 'Subscriber present and blocked');
558
+ }
559
+
560
+ if ($user->status == TNP_User::STATUS_UNSUBSCRIBED) {
561
+ // Special behavior?
562
+ }
563
+
564
+ if ($subscription->optin == 'single') {
565
+ $user->status = TNP_User::STATUS_CONFIRMED;
566
+ } else {
567
+ if ($user->status == TNP_User::STATUS_CONFIRMED) {
568
+
569
+ set_transient('newsletter_subscription_' . $user->id, $subscription->data, 3600 * 48);
570
+
571
+ // This status is *not* stored it indicate a temporary status to show the correct messages
572
+ $user->status = TNP_User::STATUS_NOT_CONFIRMED;
573
+
574
+ $this->send_message('confirmation', $user);
575
+
576
+ return $user;
577
+ } else {
578
+ $user->status = TNP_User::STATUS_NOT_CONFIRMED;
579
+ }
580
+ }
581
+
582
+ // Can be updated on the fly?
583
+ $subscription->data->merge_in($user);
584
+ } else {
585
+ $this->logger->info('New subscriber');
586
+
587
+ $user = new TNP_User();
588
+ $subscription->data->merge_in($user);
589
+
590
+ $user->token = $this->get_token();
591
+
592
+ $user->status = $subscription->optin == 'single' ? TNP_User::STATUS_CONFIRMED : TNP_User::STATUS_NOT_CONFIRMED;
593
+ $user->updated = time();
594
+ }
595
+
596
+ $user->ip = $this->process_ip($user->ip);
597
+
598
+ $user = apply_filters('newsletter_user_subscribe', $user);
599
+
600
+ $user = $this->save_user($user);
601
+
602
+ $this->add_user_log($user, 'subscribe');
603
+
604
+ // Notification to admin (only for new confirmed subscriptions)
605
+ if ($user->status == TNP_User::STATUS_CONFIRMED) {
606
+ do_action('newsletter_user_confirmed', $user);
607
+ $this->notify_admin_on_subscription($user);
608
+ setcookie('newsletter', $user->id . '-' . $user->token, time() + 60 * 60 * 24 * 365, '/');
609
+ }
610
+
611
+ if ($subscription->send_emails) {
612
+ $this->send_message(($user->status == TNP_User::STATUS_CONFIRMED) ? 'confirmed' : 'confirmation', $user);
613
+ }
614
+
615
+ // Used by Autoresponder (probably)
616
+ do_action('newsletter_user_post_subscribe', $user);
617
+
618
+ return $user;
619
+ }
620
+
621
+ /**
622
+ * Create a subscription using the $_REQUEST data. Does security checks.
623
+ *
624
+ * @deprecated since version 6.9.0
625
+ * @param string $status The status to use for this subscription (confirmed, not confirmed, ...)
626
+ * @param bool $emails If the confirmation/welcome email should be sent or the subscription should be silent
627
+ * @return TNP_User
628
+ */
629
+ function subscribe($status = null, $emails = true) {
630
+
631
+ $this->logger->debug('Subscription start');
632
+
633
+ // Validation
634
+ $ip = $this->get_remote_ip();
635
+ $email = $this->normalize_email(stripslashes($_REQUEST['ne']));
636
+ $first_name = '';
637
+ if (isset($_REQUEST['nn'])) {
638
+ $first_name = $this->normalize_name(stripslashes($_REQUEST['nn']));
639
+ }
640
+
641
+ $last_name = '';
642
+ if (isset($_REQUEST['ns'])) {
643
+ $last_name = $this->normalize_name(stripslashes($_REQUEST['ns']));
644
+ }
645
+
646
+ $opt_in = (int) $this->options['noconfirmation']; // 0 - double, 1 - single
647
+ if (!empty($this->options['optin_override']) && isset($_REQUEST['optin'])) {
648
+ switch ($_REQUEST['optin']) {
649
+ case 'single': $opt_in = self::OPTIN_SINGLE;
650
+ break;
651
+ case 'double': $opt_in = self::OPTIN_DOUBLE;
652
+ break;
653
+ }
654
+ }
655
+
656
+ if ($status != null) {
657
+ // If a status is forced and it is requested to be "confirmed" is like a single opt in
658
+ // $status here can only be confirmed or not confirmed
659
+ // TODO: Add a check on status values
660
+ if ($status == TNP_User::STATUS_CONFIRMED) {
661
+ $opt_in = self::OPTIN_SINGLE;
662
+ } else {
663
+ $opt_in = self::OPTIN_DOUBLE;
664
+ }
665
+ }
666
+
667
+ $user = $this->get_user($email);
668
+
669
+ if ($user != null) {
670
+ // Email already registered in our database
671
+ $this->logger->info('Subscription of an address with status ' . $user->status);
672
+
673
+ // Bounced
674
+ // TODO: Manage other cases when added
675
+ if ($user->status == 'B') {
676
+ // Non persistent status to decide which message to show (error)
677
+ $user->status = 'E';
678
+ return $user;
679
+ }
680
+
681
+ // Is there any relevant data change? If so we can proceed otherwise if repeated subscriptions are disabled
682
+ // show an already subscribed message
683
+
684
+ if (empty($this->options['multiple'])) {
685
+ $user->status = 'E';
686
+ return $user;
687
+ }
688
+
689
+ // If the subscriber is confirmed, we cannot change his data in double opt in mode, we need to
690
+ // temporary store and wait for activation
691
+ if ($user->status == TNP_User::STATUS_CONFIRMED && $opt_in == self::OPTIN_DOUBLE) {
692
+
693
+ set_transient($this->get_user_key($user), $_REQUEST, 3600 * 48);
694
+
695
+ // This status is *not* stored it indicate a temporary status to show the correct messages
696
+ $user->status = 'S';
697
+
698
+ $this->send_message('confirmation', $user);
699
+
700
+ return $user;
701
+ }
702
+ }
703
+
704
+ // Here we have a new subscription or we can process the subscription even with a pre-existant user for example
705
+ // because it is not confirmed
706
+ if ($user != null) {
707
+ $this->logger->info("Email address subscribed but not confirmed");
708
+ $user = array('id' => $user->id);
709
+ } else {
710
+ $this->logger->info("New email address");
711
+ $user = array('email' => $email);
712
+ }
713
+
714
+ $user = $this->update_user_from_request($user);
715
+
716
+ $user['token'] = $this->get_token();
717
+ $ip = $this->process_ip($ip);
718
+ $user['ip'] = $ip;
719
+ $user['geo'] = 0;
720
+ $user['status'] = $opt_in == self::OPTIN_SINGLE ? TNP_User::STATUS_CONFIRMED : TNP_User::STATUS_NOT_CONFIRMED;
721
+
722
+ $user['updated'] = time();
723
+
724
+ $user = apply_filters('newsletter_user_subscribe', $user);
725
+
726
+ $user = $this->save_user($user);
727
+
728
+ $this->add_user_log($user, 'subscribe');
729
+
730
+ // Notification to admin (only for new confirmed subscriptions)
731
+ if ($user->status == TNP_User::STATUS_CONFIRMED) {
732
+ do_action('newsletter_user_confirmed', $user);
733
+ $this->notify_admin_on_subscription($user);
734
+ setcookie('newsletter', $user->id . '-' . $user->token, time() + 60 * 60 * 24 * 365, '/');
735
+ }
736
+
737
+ if ($emails) {
738
+ $this->send_message(($user->status == TNP_User::STATUS_CONFIRMED) ? 'confirmed' : 'confirmation', $user);
739
+ }
740
+
741
+ $user = apply_filters('newsletter_user_post_subscribe', $user);
742
+
743
+ return $user;
744
+ }
745
+
746
+ function add_microdata($message) {
747
+ return $message . '<span itemscope itemtype="http://schema.org/EmailMessage"><span itemprop="description" content="Email address confirmation"></span><span itemprop="action" itemscope itemtype="http://schema.org/ConfirmAction"><meta itemprop="name" content="Confirm Subscription"><span itemprop="handler" itemscope itemtype="http://schema.org/HttpActionHandler"><meta itemprop="url" content="{subscription_confirm_url}"><link itemprop="method" href="http://schema.org/HttpRequestMethod/POST"></span></span></span>';
748
+ }
749
+
750
+ /**
751
+ * Builds a subscription object starting from values in the $_REQUEST
752
+ * global variable. It DOES NOT sanitizie or formally check the values.
753
+ * Usually data comes from a form submission.
754
+ * https://www.thenewsletterplugin.com/documentation/subscription/newsletter-forms/
755
+ *
756
+ * @return TNP_Subscription
757
+ */
758
+ function build_subscription() {
759
+
760
+ $language = '';
761
+ if (!empty($_REQUEST['nlang'])) {
762
+ $language = $_REQUEST['nlang'];
763
+ } else {
764
+ $language = $this->get_current_language();
765
+ }
766
+
767
+ $subscription = $this->get_default_subscription($language);
768
+ $data = $subscription->data;
769
+
770
+ $data->email = $_REQUEST['ne'];
771
+
772
+ if (isset($_REQUEST['nn'])) {
773
+ $data->name = stripslashes($_REQUEST['nn']);
774
+ }
775
+
776
+ if (isset($_REQUEST['ns'])) {
777
+ $data->surname = stripslashes($_REQUEST['ns']);
778
+ }
779
+
780
+ if (!empty($_REQUEST['nx'])) {
781
+ $data->sex = $_REQUEST['nx'][0];
782
+ }
783
+
784
+ if (isset($_REQUEST['nr'])) {
785
+ $data->referrer = $_REQUEST['nr'];
786
+ }
787
+
788
+ // From the antibot form
789
+ if (isset($_REQUEST['nhr'])) {
790
+ $data->http_referer = stripslashes($_REQUEST['nhr']);
791
+ } else if (isset($_SERVER['HTTP_REFERER'])) {
792
+ $data->http_referer = $_SERVER['HTTP_REFERER'];
793
+ }
794
+
795
+ // New profiles
796
+ for ($i = 1; $i <= NEWSLETTER_PROFILE_MAX; $i++) {
797
+ // If the profile cannot be set by subscriber, skip it.
798
+ if ($this->options_profile['profile_' . $i . '_status'] == 0) {
799
+ continue;
800
+ }
801
+ if (isset($_REQUEST['np' . $i])) {
802
+ $data->profiles['' . $i] = stripslashes($_REQUEST['np' . $i]);
803
+ }
804
+ }
805
+
806
+ // Lists (field name is nl[] and values the list number so special forms with radio button can work)
807
+ if (isset($_REQUEST['nl']) && is_array($_REQUEST['nl'])) {
808
+ $this->logger->debug($_REQUEST['nl']);
809
+ foreach ($_REQUEST['nl'] as $list_id) {
810
+ $list = $this->get_list($list_id);
811
+ if (!$list || $list->is_private()) {
812
+ // To administrator show an error to make him aware of the wrong form configuration
813
+ if (current_user_can('administrator')) {
814
+ $this->dienow('Invalid list', 'List ' . $list_id . ' has been submitted but it is set as private. Please fix the subscription form.');
815
+ }
816
+ // Ignore this list
817
+ continue;
818
+ }
819
+ $data->lists['' . $list_id] = 1;
820
+ }
821
+ } else {
822
+ $this->logger->debug('No lists received');
823
+ }
824
+
825
+ // Opt-in mode
826
+ if (!empty($this->options['optin_override']) && isset($_REQUEST['optin'])) {
827
+ switch ($_REQUEST['optin']) {
828
+ case 'single': $subscription->optin = 'single';
829
+ break;
830
+ case 'double': $subscription->optin = 'double';
831
+ break;
832
+ }
833
+ }
834
+
835
+ return $subscription;
836
+ }
837
+
838
+ /**
839
+ * Processes the request and fill in the *array* representing a subscriber with submitted values
840
+ * (filtering when necessary).
841
+ *
842
+ * @deprecated since version 6.9.0
843
+ * @param array $user An array partially filled with subscriber data
844
+ * @return array The filled array representing a subscriber
845
+ */
846
+ function update_user_from_request($user) {
847
+
848
+ if (isset($_REQUEST['nn'])) {
849
+ $user['name'] = $this->normalize_name(stripslashes($_REQUEST['nn']));
850
+ }
851
+ // TODO: required checking
852
+
853
+ if (isset($_REQUEST['ns'])) {
854
+ $user['surname'] = $this->normalize_name(stripslashes($_REQUEST['ns']));
855
+ }
856
+ // TODO: required checking
857
+
858
+ if (!empty($_REQUEST['nx'])) {
859
+ $user['sex'] = $this->normalize_sex($_REQUEST['nx'][0]);
860
+ }
861
+ // TODO: valid values check
862
+
863
+ if (isset($_REQUEST['nr'])) {
864
+ $user['referrer'] = strip_tags(trim($_REQUEST['nr']));
865
+ }
866
+
867
+ $language = '';
868
+ if (!empty($_REQUEST['nlang'])) {
869
+ $language = strtolower(strip_tags($_REQUEST['nlang']));
870
+ // TODO: Check if it's an allowed language code
871
+ $user['language'] = $language;
872
+ } else {
873
+ $language = $this->get_current_language();
874
+ $user['language'] = $language;
875
+ }
876
+
877
+ // From the antibot form
878
+ if (isset($_REQUEST['nhr'])) {
879
+ $user['http_referer'] = strip_tags(trim($_REQUEST['nhr']));
880
+ } else if (isset($_SERVER['HTTP_REFERER'])) {
881
+ $user['http_referer'] = strip_tags(trim($_SERVER['HTTP_REFERER']));
882
+ }
883
+
884
+ if (strlen($user['http_referer']) > 200) {
885
+ $user['http_referer'] = mb_substr($user['http_referer'], 0, 200);
886
+ }
887
+
888
+ // New profiles
889
+ for ($i = 1; $i <= NEWSLETTER_PROFILE_MAX; $i++) {
890
+ // If the profile cannot be set by subscriber, skip it.
891
+ if ($this->options_profile['profile_' . $i . '_status'] == 0) {
892
+ continue;
893
+ }
894
+ if (isset($_REQUEST['np' . $i])) {
895
+ $user['profile_' . $i] = trim(stripslashes($_REQUEST['np' . $i]));
896
+ }
897
+ }
898
+
899
+ // Extra validation to explain the administrator while the submitted data could
900
+ // be interpreted only partially
901
+ if (current_user_can('administrator')) {
902
+ if (isset($_REQUEST['nl']) && is_array($_REQUEST['nl'])) {
903
+ foreach ($_REQUEST['nl'] as $list_id) {
904
+ $list = $this->get_list($list_id);
905
+ if ($list && $list->status == TNP_List::STATUS_PRIVATE) {
906
+ $this->dienow('Invalid list', '[old] List ' . $list_id . ' has been submitted but it is set as private. Please fix the subscription form.');
907
+ }
908
+ }
909
+ }
910
+ }
911
+ // Preferences (field names are nl[] and values the list number so special forms with radio button can work)
912
+ // Permetto l'aggiunta solo delle liste pubbliche
913
+ if (isset($_REQUEST['nl']) && is_array($_REQUEST['nl'])) {
914
+ $lists = $this->get_lists_public();
915
+ //$this->logger->debug($_REQUEST['nl']);
916
+ foreach ($lists as $list) {
917
+ if (in_array('' . $list->id, $_REQUEST['nl'])) {
918
+ $user['list_' . $list->id] = 1;
919
+ }
920
+ }
921
+ } else {
922
+ $this->logger->debug('No lists received');
923
+ }
924
+
925
+ // Forced lists (general or by language)
926
+ // Forzo l'aggiunta delle liste forzate
927
+ $lists = $this->get_lists();
928
+ foreach ($lists as $list) {
929
+ if ($list->forced) {
930
+ $user['list_' . $list->id] = 1;
931
+ }
932
+ if (in_array($language, $list->languages)) {
933
+ $user['list_' . $list->id] = 1;
934
+ }
935
+ }
936
+
937
+ // TODO: should be removed!!!
938
+ if (defined('NEWSLETTER_FEED_VERSION')) {
939
+ $options_feed = get_option('newsletter_feed', array());
940
+ if ($options_feed['add_new'] == 1) {
941
+ $user['feed'] = 1;
942
+ }
943
+ }
944
+ return $user;
945
+ }
946
+
947
+ /**
948
+ * Sends a service message applying the template to the HTML part
949
+ *
950
+ * @param TNP_User $user
951
+ * @param string $subject
952
+ * @param string|array $message If string it is considered HTML, if array it should contains the key "html" and "text"
953
+ * @return type
954
+ */
955
+ function mail($user, $subject, $message) {
956
+ $language = $this->get_user_language($user);
957
+
958
+ $options_template = $this->get_options('template', $language);
959
+
960
+ $template = trim($options_template['template']);
961
+ if (empty($template) || strpos($template, '{message}') === false) {
962
+ $template = '{message}';
963
+ }
964
+
965
+ if (is_array($message)) {
966
+ $message['html'] = str_replace('{message}', $message['html'], $template);
967
+ $message['html'] = $this->replace($message['html'], $user);
968
+ $message['text'] = $this->replace($message['text'], $user);
969
+ } else {
970
+ $message = str_replace('{message}', $message, $template);
971
+ $message = $this->replace($message, $user);
972
+ }
973
+
974
+ $headers = [];
975
+
976
+ // Replaces tags from the template
977
+
978
+ $subject = $this->replace($subject, $user);
979
+
980
+ return Newsletter::instance()->mail($user->email, $subject, $message, $headers);
981
+ }
982
+
983
+ /**
984
+ * Confirms a subscription changing the user status and, possibly, merging the
985
+ * temporary data if present.
986
+ *
987
+ * @param TNP_User $user Optionally it can be null (user search from requests paramaters, but deprecated, or a user id)
988
+ * @return TNP_User
989
+ */
990
+ function confirm($user = null, $emails = true) {
991
+
992
+ // Compatibility with WP Registration Addon
993
+ if (!$user) {
994
+ $user = $this->get_user_from_request(true);
995
+ } else if (is_numeric($user)) {
996
+ $user = $this->get_user($user);
997
+ }
998
+
999
+ if (!$user) {
1000
+ $this->dienow('Subscriber not found', '', 404);
1001
+ }
1002
+ // End compatibility
1003
+ // Should be merged?
1004
+ $data = get_transient('newsletter_subscription_' . $user->id);
1005
+ if ($data !== false) {
1006
+ $data->merge_in($user);
1007
+ //$this->merge($user, $data);
1008
+ $user = $this->save_user($user);
1009
+ $user->status = TNP_User::STATUS_NOT_CONFIRMED;
1010
+ delete_transient('newsletter_subscription_' . $user->id);
1011
+ } else {
1012
+ $new_email = get_transient('newsletter_user_' . $user->id . '_email');
1013
+ if ($new_email) {
1014
+ $data = ['id' => $user->id, 'email' => $new_email];
1015
+ $this->save_user($data);
1016
+ delete_transient('newsletter_user_' . $user->id . '_email');
1017
+ }
1018
+ }
1019
+
1020
+
1021
+ $this->update_user_last_activity($user);
1022
+
1023
+ setcookie('newsletter', $user->id . '-' . $user->token, time() + 60 * 60 * 24 * 365, '/');
1024
+
1025
+ if ($user->status == TNP_User::STATUS_CONFIRMED) {
1026
+ $this->add_user_log($user, 'activate');
1027
+ do_action('newsletter_user_confirmed', $user);
1028
+ return $user;
1029
+ }
1030
+
1031
+ $this->set_user_status($user, TNP_User::STATUS_CONFIRMED);
1032
+
1033
+ $user = $this->get_user($user);
1034
+
1035
+ $this->add_user_log($user, 'activate');
1036
+
1037
+ do_action('newsletter_user_confirmed', $user);
1038
+ $this->notify_admin_on_subscription($user);
1039
+
1040
+ if ($emails) {
1041
+ $this->send_message('confirmed', $user);
1042
+ }
1043
+
1044
+ return $user;
1045
+ }
1046
+
1047
+ /**
1048
+ * Sends a message (activation, welcome, cancellation, ...) with the correct template
1049
+ * and checking if the message itself is disabled
1050
+ *
1051
+ * @param string $type
1052
+ * @param TNP_User $user
1053
+ */
1054
+ function send_message($type, $user, $force = false) {
1055
+ if (!$force && !empty($this->options[$type . '_disabled'])) {
1056
+ return true;
1057
+ }
1058
+
1059
+ $language = $this->get_user_language($user);
1060
+
1061
+ $options = $this->get_options('', $language);
1062
+ $message = [];
1063
+ $message['html'] = do_shortcode($options[$type . '_message']);
1064
+ $message['text'] = $this->get_text_message($type);
1065
+ if ($user->status == TNP_User::STATUS_NOT_CONFIRMED) {
1066
+ $message['html'] = $this->add_microdata($message['html']);
1067
+ }
1068
+ $subject = $options[$type . '_subject'];
1069
+
1070
+ return $this->mail($user, $subject, $message);
1071
+ }
1072
+
1073
+ function get_text_message($type) {
1074
+ switch ($type) {
1075
+ case 'confirmation':
1076
+ return __('To confirm your subscription follow the link below.', 'newsletter') . "\n\n{subscription_confirm_url}";
1077
+ case 'confirmed':
1078
+ return __('Your subscription has been confirmed.', 'newsletter');
1079
+ }
1080
+ return '';
1081
+ }
1082
+
1083
+ function is_double_optin() {
1084
+ return $this->options['noconfirmation'] == 0;
1085
+ }
1086
+
1087
+ /**
1088
+ * Sends the activation email without conditions.
1089
+ *
1090
+ * @param stdClass $user
1091
+ * @return bool
1092
+ */
1093
+ function send_activation_email($user) {
1094
+ return $this->send_message('confirmation', $user, true);
1095
+ }
1096
+
1097
+ /**
1098
+ * Finds the right way to show the message identified by $key (welcome, unsubscription, ...) redirecting the user to the
1099
+ * WordPress page or loading the configured url or activating the standard page.
1100
+ */
1101
+ function show_message($key, $user, $alert = '', $email = null) {
1102
+ $url = '';
1103
+
1104
+ if (isset($_REQUEST['ncu'])) {
1105
+ // Custom URL from the form
1106
+ $url = $_REQUEST['ncu'];
1107
+ } else {
1108
+ // Per message custom URL from configuration (language variants could not be supported)
1109
+ $options = $this->get_options('', $this->get_user_language($user));
1110
+ if (!empty($options[$key . '_url'])) {
1111
+ $url = $options[$key . '_url'];
1112
+ }
1113
+ }
1114
+
1115
+ $url = Newsletter::instance()->build_message_url($url, $key, $user, $email, $alert);
1116
+ wp_redirect($url);
1117
+
1118
+ die();
1119
+ }
1120
+
1121
+ function get_message_key_from_request() {
1122
+ if (empty($_GET['nm'])) {
1123
+ return 'subscription';
1124
+ }
1125
+ $key = $_GET['nm'];
1126
+ switch ($key) {
1127
+ case 's': return 'confirmation';
1128
+ case 'c': return 'confirmed';
1129
+ case 'u': return 'unsubscription';
1130
+ case 'uc': return 'unsubscribed';
1131
+ case 'p':
1132
+ case 'pe':
1133
+ return 'profile';
1134
+ default: return $key;
1135
+ }
1136
+ }
1137
+
1138
+ var $privacy_url = false;
1139
+
1140
+ /**
1141
+ * Generates the privacy URL and cache it.
1142
+ *
1143
+ * @return string
1144
+ */
1145
+ function get_privacy_url() {
1146
+ if ($this->privacy_url === false) {
1147
+ $this->setup_form_options();
1148
+ if (!empty($this->form_options['privacy_use_wp_url']) && function_exists('get_privacy_policy_url')) {
1149
+ $this->privacy_url = get_privacy_policy_url();
1150
+ } else {
1151
+ $this->privacy_url = $this->form_options['privacy_url'];
1152
+ }
1153
+ }
1154
+ return $this->privacy_url;
1155
+ }
1156
+
1157
+ function get_form_javascript() {
1158
+
1159
+ }
1160
+
1161
+ /**
1162
+ * Manages the custom forms made with [newsletter_form] and internal [newsletter_field] shortcodes.
1163
+ *
1164
+ * @param array $attrs
1165
+ * @param string $content
1166
+ * @return string
1167
+ */
1168
+ function get_subscription_form_custom($attrs = [], $content = '') {
1169
+ if (!is_array($attrs)) {
1170
+ $attrs = [];
1171
+ }
1172
+
1173
+ $this->setup_form_options();
1174
+
1175
+ $attrs = array_merge(['class' => 'tnp-subscription', 'style' => ''], $attrs);
1176
+
1177
+ $action = esc_attr($this->build_action_url('s'));
1178
+ $class = esc_attr($attrs['class']);
1179
+ $style = esc_attr($attrs['style']);
1180
+ $buffer = '<form method="post" action="' . $action . '" class="' . $class . '" style="' . $style . '">' . "\n";
1181
+
1182
+ $language = $this->get_current_language();
1183
+
1184
+ $buffer .= $this->get_form_hidden_fields($attrs);
1185
+
1186
+ $buffer .= do_shortcode($content);
1187
+
1188
+ if (isset($attrs['button_label'])) {
1189
+ $label = $attrs['button_label'];
1190
+ } else {
1191
+ $label = $this->form_options['subscribe'];
1192
+ }
1193
+
1194
+ if (!empty($label)) {
1195
+ $buffer .= '<div class="tnp-field tnp-field-button">';
1196
+ if (strpos($label, 'http') === 0) {
1197
+ $buffer .= '<input class="tnp-button-image" type="image" src="' . $label . '">';
1198
+ } else {
1199
+ $buffer .= '<input class="tnp-button" type="submit" value="' . $label . '">';
1200
+ }
1201
+ $buffer .= '</div>';
1202
+ }
1203
+
1204
+ $buffer .= '</form>';
1205
+
1206
+ return $buffer;
1207
+ }
1208
+
1209
+ /** Generates the hidden field for lists which should be implicitely set with a subscription form.
1210
+ *
1211
+ * @param string $lists Comma separated directly from the shortcode "lists" attribute
1212
+ * @param string $language ???
1213
+ * @return string
1214
+ */
1215
+ function get_form_implicit_lists($lists, $language = '') {
1216
+ $buffer = '';
1217
+
1218
+ $arr = explode(',', $lists);
1219
+
1220
+ foreach ($arr as $a) {
1221
+ $a = trim($a);
1222
+ if (empty($a))
1223
+ continue;
1224
+
1225
+ $list = $this->get_list($a);
1226
+ if (!$list) {
1227
+ $buffer .= $this->build_field_admin_notice('List "' . $a . '" added to the form is not configured, skipped.');
1228
+ continue;
1229
+ }
1230
+
1231
+ if ($list->is_private()) {
1232
+ $buffer .= $this->build_field_admin_notice('List ' . $a . ' is private cannot be used in a public form.');
1233
+ continue;
1234
+ }
1235
+
1236
+ if ($list->forced) {
1237
+ $buffer .= $this->build_field_admin_notice('List ' . $a . ' is already enforced on every subscription there is no need to specify it.');
1238
+ continue;
1239
+ }
1240
+
1241
+ $buffer .= "<input type='hidden' name='nl[]' value='" . esc_attr($a) . "'>\n";
1242
+ }
1243
+ return $buffer;
1244
+ }
1245
+
1246
+ /**
1247
+ * Builds all the hidden fields of a subscription form. Implicit lists, confirmation url,
1248
+ * referrer, language, ...
1249
+ *
1250
+ * @param array $attrs Attributes of form shortcode
1251
+ * @return string HTML with the hidden fields
1252
+ */
1253
+ function get_form_hidden_fields($attrs) {
1254
+ $b = '';
1255
+
1256
+ // Compatibility
1257
+ if (isset($attrs['list'])) {
1258
+ $attrs['lists'] = $attrs['list'];
1259
+ }
1260
+ if (isset($attrs['lists'])) {
1261
+ $b .= $this->get_form_implicit_lists($attrs['lists']);
1262
+ }
1263
+
1264
+ if (isset($attrs['referrer'])) {
1265
+ $b .= '<input type="hidden" name="nr" value="' . esc_attr($attrs['referrer']) . '">';
1266
+ }
1267
+
1268
+ if (isset($attrs['confirmation_url'])) {
1269
+ if ($attrs['confirmation_url'] === '#') {
1270
+ $attrs['confirmation_url'] = esc_url_raw($_SERVER['REQUEST_URI']);
1271
+ }
1272
+
1273
+ $b .= '<input type="hidden" name="ncu" value="' . esc_attr($attrs['confirmation_url']) . '">';
1274
+ }
1275
+
1276
+ if (isset($attrs['optin'])) {
1277
+ $optin = trim(strtolower($attrs['optin']));
1278
+ if ($optin !== 'double' && $optin !== 'single') {
1279
+ $b .= $this->build_field_admin_notice('The optin is set to an invalid value.');
1280
+ } else {
1281
+ if ($optin !== 'double' && $this->is_double_optin() && empty($this->options['optin_override'])) {
1282
+ $b .= $this->build_field_admin_notice('The optin is specified but cannot be overridden (see the subscription configiraton page).');
1283
+ } else {
1284
+ $b .= '<input type="hidden" name="optin" value="' . esc_attr($optin) . '">';
1285
+ }
1286
+ }
1287
+ }
1288
+
1289
+ $language = $this->get_current_language();
1290
+ $b .= '<input type="hidden" name="nlang" value="' . esc_attr($language) . '">';
1291
+
1292
+ return $b;
1293
+ }
1294
+
1295
+ /**
1296
+ * Internal use only
1297
+ *
1298
+ * @param type $name
1299
+ * @param type $attrs
1300
+ * @param type $suffix
1301
+ * @return string
1302
+ */
1303
+ private function _shortcode_label($name, $attrs, $suffix = null) {
1304
+
1305
+ if (!$suffix) {
1306
+ $suffix = $name;
1307
+ }
1308
+ $buffer = '<label for="' . esc_attr($attrs['id']) . '">';
1309
+ if (isset($attrs['label'])) {
1310
+ if (empty($attrs['label'])) {
1311
+ return;
1312
+ } else {
1313
+ $buffer .= esc_html($attrs['label']);
1314
+ }
1315
+ } else {
1316
+ if (isset($this->form_options[$name])) {
1317
+ $buffer .= esc_html($this->form_options[$name]);
1318
+ }
1319
+ }
1320
+ $buffer .= "</label>\n";
1321
+ return $buffer;
1322
+ }
1323
+
1324
+ /**
1325
+ * Creates a notices to be displayed near a subscription form field to inform of worng configurations.
1326
+ * It is created only if the current user looking at the form is the administrator.
1327
+ *
1328
+ * @param string $message
1329
+ * @return string
1330
+ */
1331
+ function build_field_admin_notice($message) {
1332
+ if (!current_user_can('administrator')) {
1333
+ return '';
1334
+ }
1335
+ return '<p style="background-color: #eee; color: #000; padding: 10px; margin: 10px 0">' . $message . ' <strong>This notice is shown only to administrators to help with configuration.</strong></p>';
1336
+ }
1337
+
1338
+ function shortcode_newsletter_field($attrs, $content = '') {
1339
+ // Counter to create unique ID for checkbox and labels
1340
+ static $idx = 0;
1341
+
1342
+ $idx++;
1343
+ $attrs['id'] = 'tnp-' . $idx;
1344
+
1345
+ $this->setup_form_options();
1346
+ $language = $this->get_current_language();
1347
+
1348
+ $name = $attrs['name'];
1349
+
1350
+ $buffer = '';
1351
+
1352
+ if ($name == 'email') {
1353
+ $buffer .= '<div class="tnp-field tnp-field-email">';
1354
+
1355
+ $buffer .= $this->_shortcode_label('email', $attrs);
1356
+
1357
+ $buffer .= '<input class="tnp-email" type="email" name="ne" id="' . esc_attr($attrs['id']) . '" value=""';
1358
+ if (isset($attrs['placeholder'])) {
1359
+ $buffer .= ' placeholder="' . esc_attr($attrs['placeholder']) . '"';
1360
+ }
1361
+ $buffer .= ' required>';
1362
+ if (isset($attrs['button_label'])) {
1363
+ $label = $attrs['button_label'];
1364
+ if (strpos($label, 'http') === 0) {
1365
+ $buffer .= ' <input class="tnp-submit-image" type="image" src="' . esc_attr(esc_url_raw($label)) . '">';
1366
+ } else {
1367
+ $buffer .= ' <input class="tnp-submit" type="submit" value="' . esc_attr($label) . '" style="width: 29%">';
1368
+ }
1369
+ }
1370
+ $buffer .= "</div>\n";
1371
+ return $buffer;
1372
+ }
1373
+
1374
+ if ($name == 'first_name' || $name == 'name') {
1375
+ $buffer .= '<div class="tnp-field tnp-field-firstname">';
1376
+ $buffer .= $this->_shortcode_label('name', $attrs);
1377
+
1378
+ $buffer .= '<input class="tnp-name" type="text" name="nn" id="' . esc_attr($attrs['id']) . '" value=""';
1379
+ if (isset($attrs['placeholder'])) {
1380
+ $buffer .= ' placeholder="' . esc_attr($attrs['placeholder']) . '"';
1381
+ }
1382
+ if ($this->form_options['name_rules'] == 1) {
1383
+ $buffer .= ' required';
1384
+ }
1385
+ $buffer .= '>';
1386
+ $buffer .= "</div>\n";
1387
+ return $buffer;
1388
+ }
1389
+
1390
+ if ($name == 'last_name' || $name == 'surname') {
1391
+ $buffer .= '<div class="tnp-field tnp-field-surname">';
1392
+ $buffer .= $this->_shortcode_label('surname', $attrs);
1393
+
1394
+ $buffer .= '<input class="tnp-surname" type="text" name="ns" id="' . esc_attr($attrs['id']) . '" value=""';
1395
+ if (isset($attrs['placeholder'])) {
1396
+ $buffer .= ' placeholder="' . esc_attr($attrs['placeholder']) . '"';
1397
+ }
1398
+ if ($this->form_options['surname_rules'] == 1) {
1399
+ $buffer .= ' required';
1400
+ }
1401
+ $buffer .= '>';
1402
+ $buffer .= '</div>';
1403
+ return $buffer;
1404
+ }
1405
+
1406
+ // Single list
1407
+ if ($name == 'preference' || $name == 'list') {
1408
+ if (!isset($attrs['number'])) {
1409
+ return $this->build_field_admin_notice('List number not specified.');
1410
+ }
1411
+ $number = (int) $attrs['number'];
1412
+ $list = $this->get_list($number, $language);
1413
+ if (!$list) {
1414
+ return $this->build_field_admin_notice('List ' . $number . ' is not configured, cannot be shown.');
1415
+ }
1416
+
1417
+ if ($list->status == 0 || $list->forced) {
1418
+ return $this->build_field_admin_notice('List ' . $number . ' is private or enforced cannot be shown.');
1419
+ }
1420
+
1421
+ if (isset($attrs['hidden'])) {
1422
+ return '<input type="hidden" name="nl[]" value="' . esc_attr($list->id) . '">';
1423
+ }
1424
+
1425
+ $idx++;
1426
+ $buffer .= '<div class="tnp-field tnp-field-checkbox tnp-field-list"><label for="tnp-' . $idx . '">';
1427
+ $buffer .= '<input type="checkbox" id="tnp-' . $idx . '" name="nl[]" value="' . esc_attr($list->id) . '"';
1428
+ if (isset($attrs['checked'])) {
1429
+ $buffer .= ' checked';
1430
+ }
1431
+ $buffer .= '>';
1432
+ if (isset($attrs['label'])) {
1433
+ if ($attrs['label'] != '') {
1434
+ $buffer .= '&nbsp;' . esc_html($attrs['label']) . '</label>';
1435
+ }
1436
+ } else {
1437
+ $buffer .= '&nbsp;' . esc_html($list->name) . '</label>';
1438
+ }
1439
+ $buffer .= "</div>\n";
1440
+
1441
+ return $buffer;
1442
+ }
1443
+
1444
+ // All lists
1445
+ if ($name == 'lists' || $name == 'preferences') {
1446
+ $lists = $this->get_lists_for_subscription($language);
1447
+ if (!empty($lists) && isset($attrs['layout']) && $attrs['layout'] === 'dropdown') {
1448
+
1449
+ $buffer .= '<div class="tnp-field tnp-lists">';
1450
+ // There is not a default "label" for the block of lists, so it can only be specified in the shortcode attrs as "label"
1451
+ $buffer .= $this->_shortcode_label('lists', $attrs);
1452
+ $buffer .= '<select class="tnp-lists" name="nl[]" required>';
1453
+
1454
+ if (!empty($attrs['first_option_label'])) {
1455
+ $buffer .= '<option value="" selected="true" disabled="disabled">' . esc_html($attrs['first_option_label']) . '</option>';
1456
+ }
1457
+
1458
+ foreach ($lists as $list) {
1459
+ $buffer .= '<option value="' . $list->id . '">' . esc_html($list->name) . '</option>';
1460
+ }
1461
+ $buffer .= '</select>';
1462
+ $buffer .= '</div>';
1463
+ } else {
1464
+
1465
+ foreach ($lists as $list) {
1466
+ $idx++;
1467
+ $buffer .= '<div class="tnp-field tnp-field-checkbox tnp-field-list"><label for="nl' . $idx . '">';
1468
+ $buffer .= '<input type="checkbox" id="nl' . $idx . '" name="nl[]" value="' . $list->id . '"';
1469
+ if ($list->checked) {
1470
+ $buffer .= ' checked';
1471
+ }
1472
+ $buffer .= '>&nbsp;' . esc_html($list->name) . '</label>';
1473
+ $buffer .= "</div>\n";
1474
+ }
1475
+ }
1476
+ return $buffer;
1477
+ }
1478
+
1479
+ if ($name == 'sex' || $name == 'gender') {
1480
+ $buffer .= '<div class="tnp-field tnp-field-gender">';
1481
+ $buffer .= $this->_shortcode_label('sex', $attrs);
1482
+
1483
+ $buffer .= '<select name="nx" class="tnp-gender" id="tnp-gender"';
1484
+ if ($this->form_options['sex_rules']) {
1485
+ $buffer .= ' required ';
1486
+ }
1487
+ $buffer .= '>';
1488
+ if ($this->form_options['sex_rules']) {
1489
+ $buffer .= '<option value=""></option>';
1490
+ }
1491
+ $buffer .= '<option value="n">' . esc_html($this->form_options['sex_none']) . '</option>';
1492
+ $buffer .= '<option value="f">' . esc_html($this->form_options['sex_female']) . '</option>';
1493
+ $buffer .= '<option value="m">' . esc_html($this->form_options['sex_male']) . '</option>';
1494
+ $buffer .= '</select>';
1495
+ $buffer .= "</div>\n";
1496
+ return $buffer;
1497
+ }
1498
+
1499
+ if ($name == 'profile') {
1500
+ if (!isset($attrs['number'])) {
1501
+ return $this->build_field_admin_notice('Extra profile number not specified.');
1502
+ }
1503
+
1504
+ $number = (int) $attrs['number'];
1505
+
1506
+ $profile = TNP_Profile_Service::get_profile_by_id($number, $language);
1507
+
1508
+ if (!$profile) {
1509
+ return $this->build_field_admin_notice('Extra profile ' . $number . ' is not configured, cannot be shown.');
1510
+ }
1511
+
1512
+ if ($profile->status == 0) {
1513
+ return $this->build_field_admin_notice('Extra profile ' . $number . ' is private, cannot be shown.');
1514
+ }
1515
+
1516
+ $size = isset($attrs['size']) ? $attrs['size'] : '';
1517
+ $buffer .= '<div class="tnp-field tnp-field-profile">';
1518
+ $buffer .= $this->_shortcode_label('profile_' . $profile->id, $attrs);
1519
+
1520
+ $placeholder = isset($attrs['placeholder']) ? $attrs['placeholder'] : $profile->placeholder;
1521
+
1522
+ // Text field
1523
+ if ($profile->type == TNP_Profile::TYPE_TEXT) {
1524
+ $buffer .= '<input class="tnp-profile tnp-profile-' . $number . '" id="tnp-profile_' . $number . '" type="text" size="' . esc_attr($size) . '" name="np' . $number . '" placeholder="' . esc_attr($placeholder) . '"';
1525
+ if ($profile->is_required()) {
1526
+ $buffer .= ' required';
1527
+ }
1528
+ $buffer .= '>';
1529
+ }
1530
+
1531
+ // Select field
1532
+ if ($profile->type == TNP_Profile::TYPE_SELECT) {
1533
+ $buffer .= '<select class="tnp-profile tnp-profile-' . $number . '" id="tnp-profile_' . $number . '" name="np' . $number . '"';
1534
+ if ($profile->is_required()) {
1535
+ $buffer .= ' required';
1536
+ }
1537
+ $buffer .= '>';
1538
+ if (!empty($placeholder)) {
1539
+ $buffer .= '<option value="" selected disabled>' . esc_html($placeholder) . '</option>';
1540
+ }
1541
+ foreach ($profile->options as $option) {
1542
+ $buffer .= '<option>' . esc_html(trim($option)) . '</option>';
1543
+ }
1544
+ $buffer .= "</select>\n";
1545
+ }
1546
+
1547
+ $buffer .= "</div>\n";
1548
+
1549
+ return $buffer;
1550
+ }
1551
+
1552
+ if (strpos($name, 'privacy') === 0) {
1553
+ if (!isset($attrs['url'])) {
1554
+ $attrs['url'] = $this->get_privacy_url();
1555
+ }
1556
+
1557
+ if (!isset($attrs['label'])) {
1558
+ $attrs['label'] = $this->form_options['privacy'];
1559
+ }
1560
+
1561
+ $buffer .= '<div class="tnp-field tnp-field-checkbox tnp-field-privacy">';
1562
+
1563
+ $idx++;
1564
+ $buffer .= '<input type="checkbox" name="ny" required class="tnp-privacy" id="tnp-' . $idx . '"> ';
1565
+ $buffer .= '<label for="tnp-' . $idx . '">';
1566
+ if (!empty($attrs['url'])) {
1567
+ $buffer .= '<a target="_blank" href="' . esc_attr($attrs['url']) . '">';
1568
+ }
1569
+ $buffer .= $attrs['label'];
1570
+ if (!empty($attrs['url'])) {
1571
+ $buffer .= '</a>';
1572
+ }
1573
+ $buffer .= '</label>';
1574
+ $buffer .= '</div>';
1575
+
1576
+ return $buffer;
1577
+ }
1578
+ }
1579
+
1580
+ /**
1581
+ * Builds the privacy field only for completely generated forms.
1582
+ *
1583
+ * @return string Empty id the privacy filed is not configured
1584
+ */
1585
+ function get_privacy_field($pre_html = '', $post_html = '') {
1586
+ $this->setup_form_options();
1587
+ $privacy_status = (int) $this->form_options['privacy_status'];
1588
+ if (empty($privacy_status)) {
1589
+ return '';
1590
+ }
1591
+
1592
+ $buffer = '<label>';
1593
+ if ($privacy_status === 1) {
1594
+ $buffer .= '<input type="checkbox" name="ny" required class="tnp-privacy">&nbsp;';
1595
+ }
1596
+ $url = $this->get_privacy_url();
1597
+ if (!empty($url)) {
1598
+ $buffer .= '<a target="_blank" href="' . esc_attr($url) . '">';
1599
+ $buffer .= esc_attr($this->form_options['privacy']) . '</a>';
1600
+ } else {
1601
+ $buffer .= esc_html($this->form_options['privacy']);
1602
+ }
1603
+
1604
+ $buffer .= "</label>";
1605
+
1606
+ return $pre_html . $buffer . $post_html;
1607
+ }
1608
+
1609
+ /**
1610
+ * The new standard form.
1611
+ *
1612
+ * @param string $referrer Deprecated since 6.9.1, use the "referrer" key on $attrs
1613
+ * @param string $action
1614
+ * @param string $attrs
1615
+ * @return string The full HTML form
1616
+ */
1617
+ function get_subscription_form($referrer = '', $action = null, $attrs = []) {
1618
+ $language = $this->get_current_language();
1619
+ $options_profile = $this->get_options('profile', $language);
1620
+
1621
+ if (!is_array($attrs)) {
1622
+ $attrs = [];
1623
+ }
1624
+
1625
+ // Possible alternative form actions (used by...?)
1626
+ if (isset($attrs['action'])) {
1627
+ $action = $attrs['action'];
1628
+ }
1629
+
1630
+ // The referrer parameter is deprecated
1631
+ if (!empty($referrer)) {
1632
+ $attrs['referrer'] = $referrer;
1633
+ }
1634
+
1635
+ $buffer = '';
1636
+
1637
+ if (empty($action)) {
1638
+ $action = $this->build_action_url('s');
1639
+ }
1640
+
1641
+ if (isset($attrs['before'])) {
1642
+ $buffer .= $attrs['before'];
1643
+ } else {
1644
+ if (isset($attrs['class'])) {
1645
+ $buffer .= '<div class="tnp tnp-subscription ' . $attrs['class'] . '">' . "\n";
1646
+ } else {
1647
+ $buffer .= '<div class="tnp tnp-subscription">' . "\n";
1648
+ }
1649
+ }
1650
+
1651
+ $buffer .= '<form method="post" action="' . esc_attr($action) . '">' . "\n\n";
1652
+
1653
+ $buffer .= $this->get_form_hidden_fields($attrs);
1654
+
1655
+ if ($options_profile['name_status'] == 2) {
1656
+ $buffer .= $this->shortcode_newsletter_field(['name' => 'first_name']);
1657
+ }
1658
+
1659
+ if ($options_profile['surname_status'] == 2) {
1660
+ $buffer .= $this->shortcode_newsletter_field(['name' => 'last_name']);
1661
+ }
1662
+
1663
+ $buffer .= $this->shortcode_newsletter_field(['name' => 'email']);
1664
+
1665
+ if (isset($options_profile['sex_status']) && $options_profile['sex_status'] == 2) {
1666
+ $buffer .= $this->shortcode_newsletter_field(['name' => 'gender']);
1667
+ }
1668
+
1669
+ // Extra profile fields
1670
+ for ($i = 1; $i <= NEWSLETTER_PROFILE_MAX; $i++) {
1671
+ // Not for subscription form
1672
+ if ($options_profile['profile_' . $i . '_status'] != 2) {
1673
+ continue;
1674
+ }
1675
+
1676
+ $buffer .= $this->shortcode_newsletter_field(['name' => 'profile', 'number' => $i]);
1677
+ }
1678
+
1679
+ $tmp = '';
1680
+ $lists = $this->get_lists_for_subscription($language);
1681
+ if (!empty($attrs['lists_field_layout']) && $attrs['lists_field_layout'] == 'dropdown') {
1682
+ if (empty($attrs['lists_field_empty_label'])) {
1683
+ $attrs['lists_field_empty_label'] = '';
1684
+ }
1685
+ if (empty($attrs['lists_field_label'])) {
1686
+ $attrs['lists_field_label'] = '';
1687
+ }
1688
+ $buffer .= $this->shortcode_newsletter_field(['name' => 'lists', 'layout' => 'dropdown', 'first_option_label' => $attrs['lists_field_empty_label'], 'label' => $attrs['lists_field_label']]);
1689
+ } else {
1690
+ $buffer .= $this->shortcode_newsletter_field(['name' => 'lists']);
1691
+ }
1692
+
1693
+
1694
+
1695
+ // Deprecated
1696
+ $extra = apply_filters('newsletter_subscription_extra', array());
1697
+ foreach ($extra as $x) {
1698
+ $label = $x['label'];
1699
+ if (empty($label)) {
1700
+ $label = '&nbsp;';
1701
+ }
1702
+ $name = '';
1703
+ if (!empty($x['name'])) {
1704
+ $name = $x['name'];
1705
+ }
1706
+ $buffer .= '<div class="tnp-field tnp-field-' . $name . '"><label>' . $label . "</label>";
1707
+ $buffer .= $x['field'] . "</div>\n";
1708
+ }
1709
+
1710
+ $buffer .= $this->get_privacy_field('<div class="tnp-field tnp-privacy-field">', '</div>');
1711
+
1712
+ $buffer .= '<div class="tnp-field tnp-field-button">';
1713
+
1714
+ $button_style = '';
1715
+ if (!empty($attrs['button_color'])) {
1716
+ $button_style = 'style="background-color:' . esc_attr($attrs['button_color']) . '"';
1717
+ }
1718
+
1719
+ if (strpos($options_profile['subscribe'], 'http') === 0) {
1720
+ $buffer .= '<input class="tnp-submit-image" type="image" src="' . esc_attr($options_profile['subscribe']) . '">' . "\n";
1721
+ } else {
1722
+ $buffer .= '<input class="tnp-submit" type="submit" value="' . esc_attr($options_profile['subscribe']) . '" ' . $button_style . '>' . "\n";
1723
+ }
1724
+
1725
+ $buffer .= "</div>\n</form>\n";
1726
+
1727
+ if (isset($attrs['after'])) {
1728
+ $buffer .= $attrs['after'];
1729
+ } else {
1730
+ $buffer .= "</div>\n";
1731
+ }
1732
+
1733
+ return $buffer;
1734
+ }
1735
+
1736
+ function get_form($number) {
1737
+ $options = get_option('newsletter_forms');
1738
+
1739
+ $form = $options['form_' . $number];
1740
+
1741
+ $form = do_shortcode($form);
1742
+
1743
+ $action = $this->build_action_url('s');
1744
+
1745
+ if (stripos($form, '<form') === false) {
1746
+ $form = '<form method="post" action="' . $action . '">' . $form . '</form>';
1747
+ }
1748
+
1749
+ // For compatibility
1750
+ $form = str_replace('{newsletter_url}', $action, $form);
1751
+
1752
+ $form = $this->replace_lists($form);
1753
+
1754
+ return $form;
1755
+ }
1756
+
1757
+ /** Replaces on passed text the special tag {lists} that can be used to show the preferences as a list of checkbox.
1758
+ * They are called lists but on configuration panel they are named preferences!
1759
+ *
1760
+ * @param string $buffer
1761
+ * @return string
1762
+ */
1763
+ function replace_lists($buffer) {
1764
+ $checkboxes = '';
1765
+ $lists = $this->get_lists_for_subscription($this->get_current_language());
1766
+ foreach ($lists as $list) {
1767
+ $checkboxes .= '<input type="checkbox" name="nl[]" value="' . $list->id . '"/>&nbsp;' . $list->name . '<br />';
1768
+ }
1769
+ $buffer = str_replace('{lists}', $checkboxes, $buffer);
1770
+ $buffer = str_replace('{preferences}', $checkboxes, $buffer);
1771
+ return $buffer;
1772
+ }
1773
+
1774
+ function notify_admin_on_subscription($user) {
1775
+
1776
+ if (empty($this->options['notify'])) {
1777
+ return;
1778
+ }
1779
+
1780
+ $message = $this->generate_admin_notification_message($user);
1781
+ $email = trim($this->options['notify_email']);
1782
+ $subject = $this->generate_admin_notification_subject('New subscription');
1783
+
1784
+ Newsletter::instance()->mail($email, $subject, array('html' => $message));
1785
+ }
1786
+
1787
+ /**
1788
+ * Builds the minimal subscription form, with only the email field and inline
1789
+ * submit button. If enabled the privacy checkbox is added.
1790
+ *
1791
+ * @param type $attrs
1792
+ * @return string
1793
+ */
1794
+ function get_subscription_form_minimal($attrs) {
1795
+
1796
+ $this->setup_form_options();
1797
+
1798
+ if (!is_array($attrs)) {
1799
+ $attrs = [];
1800
+ }
1801
+
1802
+ $attrs = array_merge(array('class' => '', 'referrer' => 'minimal',
1803
+ 'button' => $this->form_options['subscribe'], 'button_color' => '',
1804
+ 'button_radius' => '', 'placeholder' => $this->form_options['email']), $attrs);
1805
+
1806
+ $form = '';
1807
+
1808
+ $form .= '<div class="tnp tnp-subscription-minimal ' . $attrs['class'] . '">';
1809
+ $form .= '<form action="' . esc_attr($this->build_action_url('s')) . '" method="post">';
1810
+
1811
+ $form .= $this->get_form_hidden_fields($attrs);
1812
+
1813
+ $form .= '<input class="tnp-email" type="email" required name="ne" value="" placeholder="' . esc_attr($attrs['placeholder']) . '">';
1814
+
1815
+ if (isset($attrs['button_label'])) {
1816
+ $label = $attrs['button_label'];
1817
+ } else if (isset($attrs['button'])) { // Backward compatibility
1818
+ $label = $attrs['button'];
1819
+ } else {
1820
+ $label = $this->form_options['subscribe'];
1821
+ }
1822
+
1823
+ $form .= '<input class="tnp-submit" type="submit" value="' . esc_attr($attrs['button']) . '"'
1824
+ . ' style="background-color:' . esc_attr($attrs['button_color']) . '">';
1825
+
1826
+ $form .= $this->get_privacy_field('<div class="tnp-field tnp-privacy-field">', '</div>');
1827
+
1828
+ $form .= "</form></div>\n";
1829
+
1830
+ return $form;
1831
+ }
1832
+
1833
+ function shortcode_newsletter_form($attrs, $content) {
1834
+
1835
+ if (isset($attrs['type']) && $attrs['type'] === 'minimal') {
1836
+ return $this->get_subscription_form_minimal($attrs);
1837
+ }
1838
+
1839
+ // Custom form using the [newsletter_field] shortcodes
1840
+ if (!empty($content)) {
1841
+ return $this->get_subscription_form_custom($attrs, $content);
1842
+ }
1843
+
1844
+ // Custom form hand coded and saved in the custom forms option
1845
+ if (isset($attrs['form'])) {
1846
+ return $this->get_form((int) $attrs['form']);
1847
+ }
1848
+
1849
+ // Custom hand coded form (as above, new syntax)
1850
+ if (isset($attrs['number'])) {
1851
+ return $this->get_form((int) $attrs['number']);
1852
+ }
1853
+
1854
+ return $this->get_subscription_form(null, null, $attrs);
1855
+ }
1856
+
1857
+ /**
1858
+ *
1859
+ * @global wpdb $wpdb
1860
+ * @param array $attrs
1861
+ * @param string $content
1862
+ * @return string
1863
+ */
1864
+ function shortcode_newsletter($attrs, $content) {
1865
+ global $wpdb;
1866
+
1867
+ $message_key = $this->get_message_key_from_request();
1868
+ if ($message_key == 'confirmation') {
1869
+ $user = $this->get_user_from_request(false, 'preconfirm');
1870
+ } else {
1871
+ $user = $this->get_user_from_request();
1872
+ }
1873
+
1874
+ $message = apply_filters('newsletter_page_text', '', $message_key, $user);
1875
+
1876
+ $options = $this->get_options('', $this->get_current_language($user));
1877
+
1878
+ if (empty($message)) {
1879
+ $message = $options[$message_key . '_text'];
1880
+
1881
+ // TODO: the if can be removed
1882
+ if ($message_key == 'confirmed') {
1883
+ $message .= $options[$message_key . '_tracking'];
1884
+ }
1885
+ }
1886
+
1887
+ // Now check what form must be added
1888
+ if ($message_key == 'subscription') {
1889
+ if (isset($attrs['show_form']) && $attrs['show_form'] === 'false') {
1890
+ //return $this->build_field_admin_notice('The [newsletter] shortcode is configured to not show the subscription form.');
1891
+ return;
1892
+ }
1893
+
1894
+ // Compatibility check
1895
+ if (stripos($message, '<form') !== false) {
1896
+ $message = str_ireplace('<form', '<form method="post" action="' . esc_attr($this->get_subscribe_url()) . '"', $message);
1897
+ } else {
1898
+
1899
+ if (strpos($message, '{subscription_form') === false) {
1900
+ $message .= '{subscription_form}';
1901
+ }
1902
+
1903
+ if (isset($attrs['form'])) {
1904
+ $message = str_replace('{subscription_form}', $this->get_form($attrs['form']), $message);
1905
+ } else {
1906
+ $message = str_replace('{subscription_form}', $this->get_subscription_form('page', null, $attrs), $message);
1907
+ }
1908
+ }
1909
+ }
1910
+
1911
+ $email = $this->get_email_from_request();
1912
+
1913
+ $message = $this->replace($message, $user, $email, 'page');
1914
+
1915
+ $message = do_shortcode($message);
1916
+
1917
+ if (isset($_REQUEST['alert'])) {
1918
+ // slashes are already added by wordpress!
1919
+ $message .= '<script>alert("' . esc_js(strip_tags($_REQUEST['alert'])) . '");</script>';
1920
+ }
1921
+
1922
+ return $message;
1923
+ }
1924
+
1925
+ }
1926
+
1927
+ NewsletterSubscription::instance();
1928
+
1929
+ // Compatibility code
1930
+
1931
+ /**
1932
+ * @deprecated
1933
+ * @param int $number
1934
+ */
1935
+ function newsletter_form($number = null) {
1936
+ if ($number != null) {
1937
+ echo NewsletterSubscription::instance()->get_form($number);
1938
+ } else {
1939
+ echo NewsletterSubscription::instance()->get_subscription_form();
1940
+ }
1941
+ }
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>
system/status.php CHANGED
@@ -1,1165 +1,1165 @@
1
- <?php
2
- /* @var $this NewsletterSystem */
3
- /* @var $wpdb wpdb */
4
-
5
- defined('ABSPATH') || exit;
6
-
7
- wp_enqueue_script('tnp-chart');
8
-
9
- include_once NEWSLETTER_INCLUDES_DIR . '/controls.php';
10
- $controls = new NewsletterControls();
11
-
12
- $newsletter = Newsletter::instance();
13
- $mailer = $newsletter->get_mailer();
14
-
15
- if ($controls->is_action('delete_logs')) {
16
- $files = glob(WP_CONTENT_DIR . '/logs/newsletter/*.txt');
17
- foreach ($files as $file) {
18
- if (is_file($file))
19
- unlink($file);
20
- }
21
- $secret = NewsletterModule::get_token(8);
22
- update_option('newsletter_logger_secret', $secret);
23
- $controls->messages = __('Logs deleted', 'newsletter');
24
- }
25
-
26
- if ($controls->is_action('conversion')) {
27
- $this->logger->info('Maybe convert to utf8mb4');
28
- require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
29
- if (function_exists('maybe_convert_table_to_utf8mb4')) {
30
- $r = maybe_convert_table_to_utf8mb4(NEWSLETTER_EMAILS_TABLE);
31
- if (!$r) {
32
- $controls->errors .= 'It was not possible to run the conversion for the table ' . NEWSLETTER_EMAILS_TABLE . ' - ';
33
- $controls->errors .= $wpdb->last_error . '<br>';
34
- }
35
- $r = maybe_convert_table_to_utf8mb4(NEWSLETTER_USERS_TABLE);
36
- if (!$r) {
37
- $controls->errors .= 'It was not possible to run the conversion for the table ' . NEWSLETTER_EMAILS_TABLE . ' - ';
38
- $controls->errors .= $wpdb->last_error . '<br>';
39
- }
40
- $controls->messages .= 'Done.';
41
- } else {
42
- $controls->errors = 'Table conversion function not available';
43
- }
44
- }
45
-
46
- if ($controls->is_action('reset_send_stats')) {
47
- $this->reset_send_stats();
48
- $controls->add_message_done();
49
- }
50
-
51
- if ($controls->is_action('stats_email_column_upgrade')) {
52
- $this->query("alter table " . NEWSLETTER_STATS_TABLE . " drop index email_id");
53
- $this->query("alter table " . NEWSLETTER_STATS_TABLE . " drop index user_id");
54
- $this->query("alter table `" . NEWSLETTER_STATS_TABLE . "` modify column `email_id` int(11) not null default 0");
55
- $this->query("create index email_id on " . NEWSLETTER_STATS_TABLE . " (email_id)");
56
- $this->query("create index user_id on " . NEWSLETTER_STATS_TABLE . " (user_id)");
57
- $controls->add_message_done();
58
- update_option('newsletter_stats_email_column_upgraded', true);
59
- }
60
-
61
- // Compute the number of newsletters ongoing and other stats
62
- $emails = $wpdb->get_results("select * from " . NEWSLETTER_EMAILS_TABLE . " where status='sending' and send_on<" . time() . " order by id asc");
63
- $total = 0;
64
- $queued = 0;
65
- foreach ($emails as $email) {
66
- $total += $email->total;
67
- $queued += $email->total - $email->sent;
68
- }
69
- $speed = $newsletter->get_send_speed();
70
-
71
- // Trick to access the private function (!)
72
- class TNP_WPDB extends wpdb {
73
-
74
- public function get_table_charset($table) {
75
- return parent::get_table_charset($table);
76
- }
77
-
78
- }
79
-
80
- $tnp_wpdb = new TNP_WPDB(DB_USER, DB_PASSWORD, DB_NAME, DB_HOST);
81
- ?>
82
-
83
- <style>
84
- <?php include __DIR__ . '/css/system.css' ?>
85
- </style>
86
-
87
- <div class="wrap tnp-system tnp-system-status" id="tnp-wrap">
88
-
89
- <?php include NEWSLETTER_DIR . '/tnp-header.php'; ?>
90
-
91
- <div id="tnp-heading">
92
-
93
- <h2><?php _e('System Status', 'newsletter') ?></h2>
94
-
95
- </div>
96
-
97
- <div id="tnp-body">
98
-
99
- <form method="post" action="">
100
- <?php $controls->init(); ?>
101
-
102
-
103
- <h3>Delivery</h3>
104
- <table class="widefat" id="tnp-status-table">
105
-
106
- <thead>
107
- <tr>
108
- <th>Parameter</th>
109
- <th><?php _e('Status', 'newsletter') ?></th>
110
- <th>Action</th>
111
- </tr>
112
-
113
- </thead>
114
-
115
- <tbody>
116
-
117
- <tr>
118
- <td>Delivering</td>
119
- <td class="status">
120
- &nbsp;
121
- </td>
122
- <td>
123
- <?php if (count($emails)) { ?>
124
- Delivering <?php echo count($emails) ?> newsletters to about <?php echo $queued ?> recipients.
125
- At speed of <?php echo $speed ?> emails per hour it will take <?php printf('%.1f', $queued / $speed) ?> hours to finish.
126
-
127
- <?php } else { ?>
128
- Nothing delivering right now
129
- <?php } ?>
130
- </td>
131
-
132
- </tr>
133
- <tr>
134
- <td>Mailer</td>
135
- <td>
136
- &nbsp;
137
- </td>
138
- <td>
139
- <?php echo esc_html($mailer->get_description()) ?>
140
- </td>
141
- </tr>
142
- <?php
143
- $stats = $this->get_send_stats();
144
-
145
- if ($stats) {
146
- $condition = $stats->mean > 5 ? 2 : 1;
147
- ?>
148
- <tr>
149
- <td id="tnp-speed">
150
- Send details
151
- </td>
152
- <td class="status">
153
- <?php $this->condition_flag($condition) ?>
154
-
155
- </td>
156
- <td>
157
- <?php if ($condition == 2) { ?>
158
- <strong>Sending an email is taking more than 5 seconds (by mean), rather slow.</strong>
159
- <a href="https://www.thenewsletterplugin.com/documentation/installation/status-panel/#email-speed" target="_blank">Read more</a>.
160
- <br>
161
- <?php } ?>
162
- Average time to send an email: <?php echo $stats->mean ?> seconds<br>
163
- <?php if ($stats->mean > 0) { ?>
164
- Max speed: <?php echo sprintf("%.2f", 1.0 / $stats->mean * 3600) ?> emails per hour<br>
165
- <?php } ?>
166
-
167
- Max mean time measured: <?php echo $stats->max ?> seconds<br>
168
- Min mean time measured: <?php echo $stats->min ?> seconds<br>
169
- Total emails in the sample: <?php echo $stats->total_emails ?><br>
170
- Total sending time: <?php echo $stats->total_time ?> seconds<br>
171
- Runs in the sample: <?php echo $stats->total_runs ?><br>
172
- Runs prematurely interrupted: <?php echo $stats->interrupted ?><br>
173
-
174
-
175
- <canvas id="tnp-send-chart" style="width: 550px; height: 150px"></canvas>
176
- <script>
177
- jQuery(function () {
178
- var sendChartData = {
179
- labels: <?php echo json_encode(range(1, count($stats->means))) ?>,
180
- datasets: [
181
- {
182
- label: "Batch Average Time",
183
- data: <?php echo json_encode($stats->means) ?>,
184
- borderColor: '#2980b9',
185
- fill: false
186
- }/*,
187
- {
188
- label: "Batch Average Time",
189
- data: <?php echo json_encode($stats->sizes) ?>,
190
- borderColor: '#b98028',
191
- fill: false
192
- }*/]
193
- };
194
- var sendChartConfig = {
195
- type: "line",
196
- data: sendChartData,
197
- options: {
198
- responsive: false,
199
- maintainAspectRatio: false
200
- }
201
- };
202
- new Chart('tnp-send-chart', sendChartConfig);
203
- });
204
- </script>
205
- <br>
206
- <?php $controls->button_reset('reset_send_stats') ?>
207
- </td>
208
- </tr>
209
- <?php } else { ?>
210
- <tr>
211
- <td>
212
- Sending statistics
213
- </td>
214
- <td>
215
- &nbsp;
216
- </td>
217
- <td>
218
- Not enough data available.
219
- </td>
220
- </tr>
221
- <?php } ?>
222
- </tbody>
223
- </table>
224
-
225
- <h3>General checks</h3>
226
- <table class="widefat" id="tnp-status-table">
227
-
228
- <thead>
229
- <tr>
230
- <th>Parameter</th>
231
- <th><?php _e('Status', 'newsletter') ?></th>
232
- <th>Action</th>
233
- </tr>
234
-
235
- </thead>
236
-
237
- <tbody>
238
-
239
- <tr>
240
- <?php
241
- $page_id = $newsletter->get_newsletter_page_id();
242
- $page = $newsletter->get_newsletter_page();
243
- $condition = 1;
244
- if ($page_id) {
245
- if (!$page || $page->post_status !== 'publish') {
246
- $condition = 0;
247
- }
248
- } else {
249
- $condition = 2;
250
- }
251
- ?>
252
- <td>
253
- Dedicated page<br>
254
- <small>The blog page Newsletter uses for messages</small>
255
- </td>
256
- <td>
257
- <?php $this->condition_flag($condition) ?>
258
- </td>
259
- <td>
260
- <?php if ($condition == 2) { ?>
261
- Newsletter is using a neutral page to show messages, if you want to use a dedicated page, configure it on
262
- <a href="?page=newsletter_main_main">main settings</a>.
263
- <?php } else if ($condition == 0) { ?>
264
- A dedicated page is set but it is no more available or no more published. Review the dedicated page on
265
- <a href="?page=newsletter_main_main">main settings</a>.
266
- <?php } ?>
267
- </td>
268
- </tr>
269
-
270
- <tr>
271
- <?php
272
- $page_id = $newsletter->get_newsletter_page_id();
273
- $page = $newsletter->get_newsletter_page();
274
- $condition = 1;
275
- if ($page_id) {
276
- if (!$page) {
277
- $condition = 0;
278
- } else {
279
- $content = $page->post_content;
280
- if (strpos($content, '[newsletter]') === false && strpos($content, '[newsletter ') === false) {
281
- $condition = 2;
282
- }
283
- }
284
- }
285
- ?>
286
- <td>
287
- Dedicated page content<br>
288
- </td>
289
- <td>
290
- <?php $this->condition_flag($condition) ?>
291
- </td>
292
- <td>
293
- <?php if ($condition == 2) { ?>
294
- The page seems to not contain the <code>[newsletter]</code>, but sometime it cannot be detected if you use
295
- a visual composer. <a href="post.php?post=<?php echo $page->ID ?>&action=edit" target="_blank">Please, check the page</a>.
296
- <?php } else if ($condition == 0) { ?>
297
- The dedicated page seems to not be available.
298
- <?php } ?>
299
- </td>
300
- </tr>
301
-
302
- <?php
303
- $method = '';
304
- if (function_exists('get_filesystem_method')) {
305
- $method = get_filesystem_method(array(), WP_PLUGIN_DIR);
306
- }
307
- if (empty($method))
308
- $condition = 2;
309
- else if ($method == 'direct')
310
- $condition = 1;
311
- else
312
- $condition = 0;
313
- ?>
314
- <tr>
315
- <td>Add-ons installable</td>
316
- <td>
317
- <?php $this->condition_flag($condition) ?>
318
- </td>
319
- <td>
320
- <?php if ($condition == 2) { ?>
321
- No able to check, just try the add-ons manager one click install
322
- <?php } else if ($condition == 1) { ?>
323
- The add-ons manager can install our add-ons
324
- <?php } else { ?>
325
- The plugins dir could be read-only, you can install add-ons uploading the package from the
326
- plugins panel (or uploading them directly via FTP). This is unusual you should ask te provider
327
- about file and folder permissions.
328
- <?php } ?>
329
- </td>
330
-
331
- </tr>
332
-
333
-
334
- <?php
335
- $return_path = $newsletter->options['return_path'];
336
- if (!empty($return_path)) {
337
- list($return_path_local, $return_path_domain) = explode('@', $return_path);
338
- }
339
- $sender = $newsletter->options['sender_email'];
340
- if (!empty($sender)) {
341
- list($sender_local, $sender_domain) = explode('@', $sender);
342
- }
343
- ?>
344
- <tr>
345
- <td>Return path</td>
346
- <td>
347
- <?php if (empty($return_path)) { ?>
348
- <span class="tnp-ok">OK</span>
349
- <?php } else { ?>
350
- <?php if ($sender_domain != $return_path_domain) { ?>
351
- <span class="tnp-maybe">MAYBE</span>
352
- <?php } else { ?>
353
- <span class="tnp-ok">OK</span>
354
- <?php } ?>
355
- <?php } ?>
356
-
357
- </td>
358
- <td>
359
- <?php if (!empty($return_path)) { ?>
360
- Some providers require the return path domain <code><?php echo esc_html($return_path_domain) ?></code> to be identical
361
- to the sender domain <code><?php echo esc_html($sender_domain) ?></code>. See the main settings.
362
- <?php } else { ?>
363
- <?php } ?>
364
- </td>
365
-
366
- </tr>
367
-
368
-
369
-
370
-
371
-
372
- <tr>
373
- <?php
374
- $condition = NEWSLETTER_EXTENSION_UPDATE ? 1 : 0;
375
- ?>
376
- <td>Addons update</td>
377
- <td>
378
- <?php $this->condition_flag($condition) ?>
379
- </td>
380
- <td>
381
- <?php if ($condition == 0) { ?>
382
- Newsletter Addons update is disabled (probably in your <code>wp-config.php</code> file the constant
383
- <code>NEWSLETTER_EXTENSION_UPDATE</code> is set to <code>true</code>)
384
- <?php } else { ?>
385
- Newsletter Addons can be updated
386
- <?php } ?>
387
- </td>
388
-
389
- </tr>
390
-
391
- <tr>
392
- <?php
393
- $res = true;
394
- $response = wp_remote_get('http://www.thenewsletterplugin.com/wp-content/extensions.json');
395
- $condition = 1;
396
- if (is_wp_error($response)) {
397
- $res = false;
398
- $condition = 0;
399
- $message = $response->get_error_message();
400
- } else {
401
- if (wp_remote_retrieve_response_code($response) != 200) {
402
- $res = false;
403
- $condition = 0;
404
- $message = wp_remote_retrieve_response_message($response);
405
- }
406
- }
407
- ?>
408
-
409
- <td>
410
- Addons version check<br>
411
- <small>Your blog can check the professional addon updates?</small>
412
- </td>
413
- <td>
414
- <?php $this->condition_flag($condition) ?>
415
- </td>
416
- <td>
417
- <?php if ($condition == 0) { ?>
418
- The blog cannot contact www.thenewsletterplugin.com to check the license or the extension versions.<br>
419
- Error: <?php echo esc_html($message) ?><br>
420
- <?php } else { ?>
421
-
422
- <?php } ?>
423
- </td>
424
- </tr>
425
-
426
-
427
-
428
-
429
-
430
-
431
-
432
-
433
-
434
- <?php /*
435
- $memory = intval(WP_MEMORY_LIMIT);
436
- if (false !== strpos(WP_MEMORY_LIMIT, 'G'))
437
- $memory *= 1024;
438
- ?>
439
- <tr>
440
- <td>
441
- PHP memory limit
442
- </td>
443
- <td>
444
- <?php if ($memory < 64) { ?>
445
- <span class="tnp-ko">MAYBE</span>
446
- <?php } else if ($memory < 128) { ?>
447
- <span class="tnp-maybe">MAYBE</span>
448
- <?php } else { ?>
449
- <span class="tnp-ok">OK</span>
450
- <?php } ?>
451
- </td>
452
- <td>
453
- WordPress WP_MEMORY_LIMIT is set to <?php echo $memory ?> megabyte but your PHP setting could allow more than that.
454
- Anyway we suggest to set the value to at least 64M.
455
- <a href="https://www.thenewsletterplugin.com/documentation/status-panel#status-memory" target="_blank">Read more</a>.
456
- <?php if ($memory < 64) { ?>
457
- This value is too low you should increase it adding <code>define('WP_MEMORY_LIMIT', '64M');</code> to your <code>wp-config.php</code>.
458
- <a href="https://www.thenewsletterplugin.com/documentation/status-panel#status-memory" target="_blank">Read more</a>.
459
- <?php } else if ($memory < 128) { ?>
460
- The value should be fine, it depends on how many plugins you're running and how many resource are required by your theme.
461
- Blank pages may happen with low memory problems. Eventually increase it adding <code>define('WP_MEMORY_LIMIT', '128M');</code>
462
- to your <code>wp-config.php</code>.
463
- <a href="https://www.thenewsletterplugin.com/documentation/status-panel#status-memory" target="_blank">Read more</a>.
464
- <?php } else { ?>
465
-
466
- <?php } ?>
467
-
468
- </td>
469
- </tr>
470
- */ ?>
471
-
472
- <?php
473
- $ip = gethostbyname($_SERVER['HTTP_HOST']);
474
- $name = gethostbyaddr($ip);
475
- $res = true;
476
- if (strpos($name, '.secureserver.net') !== false) {
477
- //$smtp = get_option('newsletter_main_smtp');
478
- //if (!empty($smtp['enabled']))
479
- $res = false;
480
- $message = 'If you\'re hosted with GoDaddy, be sure to set their SMTP (relay-hosting.secureserver.net, without username and password) to send emails
481
- on Newsletter SMTP panel.
482
- Remember they limits you to 250 emails per day. Open them a ticket for more details.';
483
- }
484
- if (strpos($name, '.aruba.it') !== false) {
485
- $res = false;
486
- $message = 'If you\'re hosted with Aruba consider to use an external SMTP (Sendgrid, Mailjet, Mailgun, Amazon SES, Elasticemail, Sparkpost, ...)
487
- since their mail service is not good. If you have your personal email with them, you can try to use the SMTP of your
488
- pesonal account. Ask the support for the SMTP parameters and configure them on Newsletter SMTP panel.';
489
- }
490
- ?>
491
- <tr>
492
- <td>Your Server</td>
493
- <td>
494
- <?php if ($res === false) { ?>
495
- <span class="tnp-maybe">MAYBE</span>
496
- <?php } else { ?>
497
- <span class="tnp-ok">OK</span>
498
- <?php } ?>
499
-
500
-
501
- </td>
502
- <td>
503
- <?php if ($res === false) { ?>
504
- <?php echo $message ?>
505
- <?php } else { ?>
506
-
507
- <?php } ?>
508
- IP: <?php echo $ip ?><br>
509
- Name: <?php echo $name ?><br>
510
- </td>
511
- </tr>
512
-
513
- <?php
514
- wp_mkdir_p(NEWSLETTER_LOG_DIR);
515
- $condition = is_dir(NEWSLETTER_LOG_DIR) && is_writable(NEWSLETTER_LOG_DIR) ? 1 : 0;
516
- if ($condition) {
517
- @file_put_contents(NEWSLETTER_LOG_DIR . '/test.txt', "");
518
- $condition = is_file(NEWSLETTER_LOG_DIR . '/test.txt') ? 1 : 0;
519
- if ($condition) {
520
- @unlink(NEWSLETTER_LOG_DIR . '/test.txt');
521
- }
522
- }
523
- ?>
524
- <tr>
525
- <td>
526
- Log folder
527
- </td>
528
- <td>
529
- <?php $this->condition_flag($condition) ?>
530
- </td>
531
- <td>
532
- The log folder is <?php echo esc_html(NEWSLETTER_LOG_DIR) ?><br>
533
- <?php if (!$res) { ?>
534
- Cannot create the folder or it is not writable.
535
- <?php } ?>
536
- </td>
537
- </tr>
538
- </tbody>
539
- </table>
540
-
541
- <h3>3rd party plugins</h3>
542
- <table class="widefat" id="tnp-status-table">
543
- <thead>
544
- <tr>
545
- <th>Plugin</th>
546
- <th><?php _e('Status', 'newsletter') ?></th>
547
- <th>Action</th>
548
- </tr>
549
- </thead>
550
- <tbody>
551
- <?php if (is_plugin_active('plugin-load-filter/plugin-load-filter.php')) { ?>
552
- <tr>
553
- <td><a href="https://wordpress.org/plugins/plugin-load-filter/" target="_blank">Plugin load filter</a></td>
554
- <td>
555
- <span class="tnp-maybe">MAY BE</span>
556
- </td>
557
- <td>
558
- Be sure Newsletter is set as active on EVERY context.
559
- </td>
560
- </tr>
561
- <?php } ?>
562
-
563
- <?php if (is_plugin_active('wp-asset-clean-up/wpacu.php')) { ?>
564
- <tr>
565
- <td><a href="https://wordpress.org/plugins/wp-asset-clean-up/" target="_blank">WP Asset Clean Up</a></td>
566
- <td>
567
- <span class="tnp-maybe">MAY BE</span>
568
- </td>
569
- <td>
570
- Be sure Newsletter is set as active on EVERY context.
571
- </td>
572
- </tr>
573
- <?php } ?>
574
-
575
- <?php if (is_plugin_active('freesoul-deactivate-plugins/freesoul-deactivate-plugins.php')) { ?>
576
- <tr>
577
- <td><a href="https://wordpress.org/plugins/freesoul-deactivate-plugins/" target="_blank">Freesoul Deactivate Plugins</a></td>
578
- <td>
579
- <span class="tnp-maybe">MAY BE</span>
580
- </td>
581
- <td>
582
- Be sure Newsletter is set as active on EVERY context.
583
- </td>
584
- </tr>
585
- <?php } ?>
586
-
587
- </tbody>
588
- </table>
589
-
590
-
591
- <h3>WordPress</h3>
592
-
593
- <table class="widefat" id="tnp-status-table">
594
- <thead>
595
- <tr>
596
- <th>Parameter</th>
597
- <th><?php _e('Status', 'newsletter') ?></th>
598
- <th>Action</th>
599
- </tr>
600
- </thead>
601
- <tbody>
602
-
603
- <tr>
604
- <?php
605
- $condition = (defined('WP_DEBUG') && WP_DEBUG) ? 2 : 1;
606
- ?>
607
- <td>
608
- WordPress debug mode
609
- </td>
610
- <td>
611
- <?php $this->condition_flag($condition) ?>
612
- </td>
613
- <td>
614
- <?php if (defined('WP_DEBUG') && WP_DEBUG) { ?>
615
- WordPress is in debug mode it is not recommended on a production system. See the constant <code>WP_DEBUG</code> inside the <code>wp-config.php</code>.
616
- <?php } else { ?>
617
-
618
- <?php } ?>
619
- </td>
620
- </tr>
621
-
622
-
623
-
624
- <tr>
625
- <?php
626
- $charset = get_option('blog_charset');
627
- $condition = $charset === 'UTF-8' ? 1 : 0;
628
- ?>
629
- <td>Blog Charset</td>
630
- <td>
631
- <?php $this->condition_flag($condition) ?>
632
- </td>
633
- <td>
634
- Charset: <?php echo esc_html($charset) ?>
635
- <br>
636
-
637
- <?php if ($condition == 1) { ?>
638
-
639
- <?php } else { ?>
640
- It is recommended to use
641
- the <code>UTF-8</code> charset but the <a href="https://codex.wordpress.org/Converting_Database_Character_Sets" target="_blank">conversion</a>
642
- could be tricky. If you're not experiencing problem, leave things as is.
643
- <?php } ?>
644
- </td>
645
- </tr>
646
-
647
- <tr>
648
- <?php
649
- $condition = (strpos(home_url('/'), 'http') !== 0) ? 0 : 1;
650
- ?>
651
- <td>Home URL</td>
652
- <td>
653
- <?php $this->condition_flag($condition) ?>
654
- </td>
655
- <td>
656
- Value: <?php echo home_url('/'); ?>
657
- <br>
658
- <?php if ($condition == 0) { ?>
659
- Your home URL is not absolute, emails require absolute URLs.
660
- Probably you have a protocol agnostic plugin installed to manage both HTTPS and HTTP in your
661
- blog.
662
- <?php } else { ?>
663
-
664
- <?php } ?>
665
- </td>
666
- </tr>
667
-
668
- <tr>
669
- <?php
670
- $condition = (strpos(WP_CONTENT_URL, 'http') !== 0) ? 0 : 1;
671
- ?>
672
- <td>WP_CONTENT_URL</td>
673
- <td>
674
- <?php $this->condition_flag($condition) ?>
675
- </td>
676
- <td>
677
- Value: <?php echo esc_html(WP_CONTENT_URL); ?>
678
- <br>
679
- <?php if ($condition == 0) { ?>
680
- Your content URL is not absolute, emails require absolute URLs when they have images inside.
681
- Newsletter tries to deal with this problem but when a problem with images persists, you should try to remove
682
- from your <code>wp-config.php</code> the <code>WP_CONTENT_URL</code> define and check again.
683
- <?php } else { ?>
684
-
685
- <?php } ?>
686
- </td>
687
- </tr>
688
-
689
-
690
- <tr>
691
- <?php
692
- $attachments = get_posts([
693
- 'post_type' => 'attachment',
694
- 'post_mime_type' => 'image',
695
- 'numberposts' => 1,
696
- 'post_status' => null,
697
- 'post_parent' => null
698
- ]);
699
- $condition = 1;
700
- $src = 'No media found to make a test';
701
- if ($attachments) {
702
- $src = wp_get_attachment_image_src($attachments[0]->ID);
703
- $src = $src[0];
704
- $condition = (strpos($src, 'http') !== 0) ? 0 : 1;
705
- }
706
- ?>
707
- <td>Images URL</td>
708
- <td>
709
- <?php $this->condition_flag($condition) ?>
710
- </td>
711
- <td>
712
- Example: <?php echo esc_html($src); ?>
713
-
714
- <?php if ($condition == 0) { ?>
715
- <br><br>
716
- Your uploadimages seems to be returned with a relative URL: they won't work in your newsletter. Check the <code>WP_CONTENT_URL</code>
717
- above and fix it if is showing a warning. If not, probably a plugin or some custom code is forcing relative URLs for your
718
- images. Check that with your site developer.
719
- <?php } else { ?>
720
-
721
- <?php } ?>
722
- </td>
723
- </tr>
724
-
725
-
726
-
727
- <tr>
728
- <?php
729
- $uploads = wp_upload_dir();
730
- ?>
731
- <td>Uploads dir and url</td>
732
- <td>
733
-
734
- </td>
735
- <td>
736
- <table class="widefat" style="width: auto">
737
- <thead>
738
- <tr><th>Key</th><th>Value</th></tr>
739
- </thead>
740
- <tbody>
741
- <?php foreach ($uploads as $k => $v) { ?>
742
- <tr><td><?php echo esc_html($k) ?></td><td><?php echo esc_html($v) ?></td></tr>
743
- <?php } ?>
744
- </tbody>
745
- </table>
746
-
747
- </td>
748
- </tr>
749
-
750
- <tr>
751
- <?php
752
- set_transient('newsletter_transient_test', 1, 300);
753
- delete_transient('newsletter_transient_test');
754
- $res = get_transient('newsletter_transient_test');
755
- $condition = ($res !== false) ? 0 : 1;
756
- ?>
757
- <td>WordPress transients</td>
758
- <td>
759
- <?php $this->condition_flag($condition) ?>
760
- </td>
761
- <td>
762
- <?php if ($res !== false) { ?>
763
- Transients cannot be delete. This can block the delivery engine. Usually it is due to a not well coded plugin installed.
764
- <?php } else { ?>
765
- <?php } ?>
766
- </td>
767
- </tr>
768
- </tbody>
769
- </table>
770
-
771
-
772
-
773
- <h3>PHP</h3>
774
- <table class="widefat" id="tnp-status-table">
775
- <thead>
776
- <tr>
777
- <th>Parameter</th>
778
- <th><?php _e('Status', 'newsletter') ?></th>
779
- <th>Action</th>
780
- </tr>
781
- </thead>
782
- <tbody>
783
- <tr>
784
- <td>PHP version</td>
785
- <td>
786
- <?php if (version_compare(phpversion(), '5.6', '<')) { ?>
787
- <span class="tnp-ko">KO</span>
788
- <?php } else { ?>
789
- <span class="tnp-ok">OK</span>
790
- <?php } ?>
791
-
792
- </td>
793
- <td>
794
- Your PHP version is <?php echo phpversion() ?><br>
795
- <?php if (version_compare(phpversion(), '5.3', '<')) { ?>
796
- Newsletter plugin works correctly with PHP version 5.6 or greater. Ask your provider to upgrade your PHP. Your version is
797
- unsupported even by the PHP community.
798
- <?php } ?>
799
- </td>
800
-
801
- </tr>
802
-
803
- <tr>
804
- <?php
805
- $value = (int) ini_get('max_execution_time');
806
- $res = true;
807
- $condition = 1;
808
- if ($value != 0 && $value < NEWSLETTER_CRON_INTERVAL) {
809
- $res = set_time_limit(NEWSLETTER_CRON_INTERVAL);
810
- if ($res)
811
- $condition = 1;
812
- else
813
- $condition = 0;
814
- }
815
- ?>
816
- <td>PHP execution time limit</td>
817
- <td>
818
- <?php $this->condition_flag($condition) ?>
819
- </td>
820
- <td>
821
- <?php if (!$res) { ?>
822
- Your PHP execution time limit is <?php echo $value ?> seconds. It cannot be changed and it is too lower to grant the maximum delivery rate of Newsletter.
823
- <?php } else { ?>
824
- Your PHP execution time limit is <?php echo $value ?> seconds and can be eventually changed by Newsletter.<br>
825
- <?php } ?>
826
-
827
- </td>
828
-
829
- </tr>
830
-
831
-
832
- <tr>
833
- <?php
834
- $condition = function_exists('curl_version');
835
- ?>
836
- <td>Curl version</td>
837
- <td>
838
- <?php if (!$condition) { ?>
839
- <span class="tnp-ko">KO</span>
840
- <?php } else { ?>
841
- <span class="tnp-ok">OK</span>
842
- <?php } ?>
843
-
844
- </td>
845
- <td>
846
- <?php
847
- if (!$condition) {
848
- echo 'cUrl is not available, ask the provider to install it and activate the PHP cUrl library';
849
- } else {
850
- $version = curl_version();
851
- echo 'Version: ' . $version['version'] . '<br>';
852
- echo 'SSL Version: ' . $version['ssl_version'] . '<br>';
853
- }
854
- ?>
855
- </td>
856
-
857
- </tr>
858
- <?php if (ini_get('opcache.validate_timestamps') === '0') { ?>
859
- <tr>
860
- <td>
861
- Opcache
862
- </td>
863
-
864
- <td>
865
- <span class="tnp-ko">KO</span>
866
- </td>
867
-
868
- <td>
869
- You have the PHP opcache active with file validation disable so every blog plugins update needs a webserver restart!
870
- </td>
871
- </tr>
872
- <?php } ?>
873
- </tbody>
874
- </table>
875
-
876
- <h3>Database</h3>
877
- <table class="widefat" id="tnp-status-table">
878
- <thead>
879
- <tr>
880
- <th>Parameter</th>
881
- <th><?php _e('Status', 'newsletter') ?></th>
882
- <th>Action</th>
883
- </tr>
884
- </thead>
885
- <tbody>
886
- <tr>
887
- <td>Database Charset</td>
888
- <td>
889
- <?php if ($wpdb->charset != 'utf8mb4') { ?>
890
- <span class="tnp-ko">KO</span>
891
- <?php } else { ?>
892
- <span class="tnp-ok">OK</span>
893
- <?php } ?>
894
-
895
- </td>
896
- <td>
897
- Charset: <?php echo $wpdb->charset; ?>
898
- <br>
899
- <?php if ($wpdb->charset != 'utf8mb4') { ?>
900
- The recommended charset for your database is <code>utf8mb4</code> to avoid possible saving errors when you use emoji.
901
- Read the WordPress Codex <a href="https://codex.wordpress.org/Converting_Database_Character_Sets" target="_blank">conversion
902
- instructions</a> (skilled technicia required).
903
- <?php } else { ?>
904
- If you experience newsletter saving database error
905
- <?php $controls->button('conversion', 'Try tables upgrade') ?>
906
- <?php } ?>
907
- </td>
908
- </tr>
909
-
910
- <tr>
911
- <td>get_table_charset()</td>
912
- <td>
913
-
914
- </td>
915
- <td>
916
- <?php echo esc_html(NEWSLETTER_USERS_TABLE), ': ', esc_html($tnp_wpdb->get_table_charset(NEWSLETTER_USERS_TABLE)) ?>
917
- </td>
918
- </tr>
919
-
920
-
921
-
922
-
923
- <?php
924
- $wait_timeout = $wpdb->get_var("select @@wait_timeout");
925
- $condition = ($wait_timeout < 30) ? 0 : 1;
926
- ?>
927
- <tr>
928
- <td>Database wait timeout</td>
929
- <td>
930
- <?php $this->condition_flag($condition) ?>
931
- </td>
932
- <td>
933
- Your database wait timeout is <?php echo $wait_timeout; ?> seconds<br>
934
- <?php if ($wait_timeout < 30) { ?>
935
- That value is low and could produce database connection errors while sending emails or during long import
936
- sessions. Ask the provider to raise it at least to 60 seconds.
937
- <?php } ?>
938
- </td>
939
- </tr>
940
-
941
- <?php
942
- $res = $wpdb->query("drop table if exists {$wpdb->prefix}newsletter_test");
943
- $res = $wpdb->query("create table if not exists {$wpdb->prefix}newsletter_test (id int(20))");
944
- $condition = $res === false ? 0 : 1;
945
- ?>
946
- <tr>
947
- <td>Database table creation</td>
948
- <td>
949
- <?php $this->condition_flag($condition) ?>
950
- </td>
951
- <td>
952
- <?php if ($res === false) { ?>
953
- Check the privileges of the user you use to connect to the database, it seems it cannot create tables.<br>
954
- (<?php echo esc_html($wpdb->last_error) ?>)
955
- <?php } else { ?>
956
- <?php } ?>
957
- </td>
958
- </tr>
959
-
960
- <?php
961
- $res = $wpdb->query("alter table {$wpdb->prefix}newsletter_test add column id1 int(20)");
962
- $condition = $res === false ? 0 : 1;
963
- ?>
964
- <tr>
965
- <td>Database table change</td>
966
- <td>
967
- <?php $this->condition_flag($condition) ?>
968
- </td>
969
- <td>
970
- <?php if ($res === false) { ?>
971
- Check the privileges of the user you use to connect to the database, it seems it cannot change the tables. It's require to update the
972
- plugin.<br>
973
- (<?php echo esc_html($wpdb->last_error) ?>)
974
- <?php } else { ?>
975
- <?php } ?>
976
- </td>
977
- </tr>
978
-
979
- <?php
980
- // Clean up
981
- $res = $wpdb->query("drop table if exists {$wpdb->prefix}newsletter_test");
982
- ?>
983
-
984
- <?php if (!get_option('newsletter_stats_email_column_upgraded', false)) { ?>
985
- <?php
986
- $data_type = $wpdb->get_var(
987
- $wpdb->prepare('SELECT DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = %s AND TABLE_NAME = %s AND COLUMN_NAME = %s',
988
- DB_NAME, NEWSLETTER_STATS_TABLE, 'email_id'));
989
- $to_upgrade = strtoupper($data_type) == 'INT' ? false : true;
990
- ?>
991
- <?php if ($to_upgrade) { ?>
992
- <tr>
993
- <td>Database stats table upgrade</td>
994
- <td><?php $this->condition_flag(0) ?></td>
995
- <td><?php $controls->button('stats_email_column_upgrade', 'Stats table upgrade') ?></td>
996
- </tr>
997
- <?php } ?>
998
- <?php } ?>
999
-
1000
- </tbody>
1001
- </table>
1002
-
1003
-
1004
-
1005
- <h3>General parameters</h3>
1006
- <table class="widefat" id="tnp-parameters-table">
1007
- <thead>
1008
- <tr>
1009
- <th>Parameter</th>
1010
- <th>Value</th>
1011
- </tr>
1012
- </thead>
1013
- <tbody>
1014
-
1015
- <tr>
1016
- <td>Newsletter version</td>
1017
- <td>
1018
- <?php echo NEWSLETTER_VERSION ?>
1019
- </td>
1020
- </tr>
1021
-
1022
- <tr>
1023
- <td>NEWSLETTER_MAX_EXECUTION_TIME</td>
1024
- <td>
1025
- <?php
1026
- if (defined('NEWSLETTER_MAX_EXECUTION_TIME')) {
1027
- echo NEWSLETTER_MAX_EXECUTION_TIME . ' (seconds)';
1028
- } else {
1029
- echo 'Not set';
1030
- }
1031
- ?>
1032
- </td>
1033
- </tr>
1034
-
1035
-
1036
-
1037
-
1038
- <?php /*
1039
- <tr>
1040
- <td>WordPress plugin url</td>
1041
- <td>
1042
- <?php echo WP_PLUGIN_URL; ?>
1043
- <br>
1044
- Filters:
1045
-
1046
- <?php
1047
- if (isset($wp_filter))
1048
- $filters = $wp_filter['plugins_url'];
1049
- if (!isset($filters) || !is_array($filters))
1050
- echo 'no filters attached to "plugin_urls"';
1051
- else {
1052
- echo '<ul>';
1053
- foreach ($filters as &$filter) {
1054
- foreach ($filter as &$entry) {
1055
- echo '<li>';
1056
- if (is_array($entry['function']))
1057
- echo esc_html(get_class($entry['function'][0]) . '->' . $entry['function'][1]);
1058
- else
1059
- echo esc_html($entry['function']);
1060
- echo '</li>';
1061
- }
1062
- }
1063
- echo '</ul>';
1064
- }
1065
- ?>
1066
- <p class="description">
1067
- This value should contains the full URL to your plugin folder. If there are filters
1068
- attached, the value can be different from the original generated by WordPress and sometime worng.
1069
- </p>
1070
- </td>
1071
- </tr>
1072
- */ ?>
1073
-
1074
- <tr>
1075
- <td>Absolute path</td>
1076
- <td>
1077
- <?php echo esc_html(ABSPATH); ?>
1078
- </td>
1079
- </tr>
1080
- <tr>
1081
- <td>Tables Prefix</td>
1082
- <td>
1083
- <?php echo $wpdb->prefix; ?>
1084
- </td>
1085
- </tr>
1086
- </tbody>
1087
- </table>
1088
-
1089
-
1090
- <?php if (isset($_GET['debug'])) { ?>
1091
-
1092
- <h3>Database Tables</h3>
1093
- <h4><?php echo $wpdb->prefix ?>newsletter</h4>
1094
- <?php
1095
- $rs = $wpdb->get_results("describe {$wpdb->prefix}newsletter");
1096
- ?>
1097
- <table class="tnp-db-table">
1098
- <thead>
1099
- <tr>
1100
- <th>Field</th>
1101
- <th>Type</th>
1102
- <th>Null</th>
1103
- <th>Key</th>
1104
- <th>Default</th>
1105
- <th>Extra</th>
1106
- </tr>
1107
- </thead>
1108
- <tbody>
1109
- <?php foreach ($rs as $r) { ?>
1110
- <tr>
1111
- <td><?php echo esc_html($r->Field) ?></td>
1112
- <td><?php echo esc_html($r->Type) ?></td>
1113
- <td><?php echo esc_html($r->Null) ?></td>
1114
- <td><?php echo esc_html($r->Key) ?></td>
1115
- <td><?php echo esc_html($r->Default) ?></td>
1116
- <td><?php echo esc_html($r->Extra) ?></td>
1117
- </tr>
1118
- <?php } ?>
1119
- </tbody>
1120
- </table>
1121
-
1122
- <h4><?php echo $wpdb->prefix ?>newsletter_emails</h4>
1123
- <?php
1124
- $rs = $wpdb->get_results("show full columns from {$wpdb->prefix}newsletter_emails");
1125
- ?>
1126
- <table class="tnp-db-table">
1127
- <thead>
1128
- <tr>
1129
- <th>Field</th>
1130
- <th>Type</th>
1131
- <th>Collation</th>
1132
- <th>Null</th>
1133
- <th>Key</th>
1134
- <th>Default</th>
1135
- <th>Extra</th>
1136
- </tr>
1137
- </thead>
1138
- <tbody>
1139
- <?php foreach ($rs as $r) { ?>
1140
- <tr>
1141
- <td><?php echo esc_html($r->Field) ?></td>
1142
- <td><?php echo esc_html($r->Type) ?></td>
1143
- <td><?php echo esc_html($r->Collation) ?></td>
1144
- <td><?php echo esc_html($r->Null) ?></td>
1145
- <td><?php echo esc_html($r->Key) ?></td>
1146
- <td><?php echo esc_html($r->Default) ?></td>
1147
- <td><?php echo esc_html($r->Extra) ?></td>
1148
- </tr>
1149
- <?php } ?>
1150
- </tbody>
1151
- </table>
1152
-
1153
-
1154
- <h3>Extensions</h3>
1155
- <pre style="font-size: 11px; font-family: monospace; background-color: #efefef; color: #444"><?php echo esc_html(print_r(get_option('newsletter_extension_versions'), true)); ?></pre>
1156
-
1157
- <h3>Update plugins data</h3>
1158
- <pre style="font-size: 11px; font-family: monospace; background-color: #efefef; color: #444"><?php echo esc_html(print_r(get_site_transient('update_plugins'), true)); ?></pre>
1159
-
1160
- <?php } ?>
1161
- </div>
1162
-
1163
- <?php include NEWSLETTER_DIR . '/tnp-footer.php'; ?>
1164
-
1165
- </div>
1
+ <?php
2
+ /* @var $this NewsletterSystem */
3
+ /* @var $wpdb wpdb */
4
+
5
+ defined('ABSPATH') || exit;
6
+
7
+ wp_enqueue_script('tnp-chart');
8
+
9
+ include_once NEWSLETTER_INCLUDES_DIR . '/controls.php';
10
+ $controls = new NewsletterControls();
11
+
12
+ $newsletter = Newsletter::instance();
13
+ $mailer = $newsletter->get_mailer();
14
+
15
+ if ($controls->is_action('delete_logs')) {
16
+ $files = glob(WP_CONTENT_DIR . '/logs/newsletter/*.txt');
17
+ foreach ($files as $file) {
18
+ if (is_file($file))
19
+ unlink($file);
20
+ }
21
+ $secret = NewsletterModule::get_token(8);
22
+ update_option('newsletter_logger_secret', $secret);
23
+ $controls->messages = __('Logs deleted', 'newsletter');
24
+ }
25
+
26
+ if ($controls->is_action('conversion')) {
27
+ $this->logger->info('Maybe convert to utf8mb4');
28
+ require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
29
+ if (function_exists('maybe_convert_table_to_utf8mb4')) {
30
+ $r = maybe_convert_table_to_utf8mb4(NEWSLETTER_EMAILS_TABLE);
31
+ if (!$r) {
32
+ $controls->errors .= 'It was not possible to run the conversion for the table ' . NEWSLETTER_EMAILS_TABLE . ' - ';
33
+ $controls->errors .= $wpdb->last_error . '<br>';
34
+ }
35
+ $r = maybe_convert_table_to_utf8mb4(NEWSLETTER_USERS_TABLE);
36
+ if (!$r) {
37
+ $controls->errors .= 'It was not possible to run the conversion for the table ' . NEWSLETTER_EMAILS_TABLE . ' - ';
38
+ $controls->errors .= $wpdb->last_error . '<br>';
39
+ }
40
+ $controls->messages .= 'Done.';
41
+ } else {
42
+ $controls->errors = 'Table conversion function not available';
43
+ }
44
+ }
45
+
46
+ if ($controls->is_action('reset_send_stats')) {
47
+ $this->reset_send_stats();
48
+ $controls->add_message_done();
49
+ }
50
+
51
+ if ($controls->is_action('stats_email_column_upgrade')) {
52
+ $this->query("alter table " . NEWSLETTER_STATS_TABLE . " drop index email_id");
53
+ $this->query("alter table " . NEWSLETTER_STATS_TABLE . " drop index user_id");
54
+ $this->query("alter table `" . NEWSLETTER_STATS_TABLE . "` modify column `email_id` int(11) not null default 0");
55
+ $this->query("create index email_id on " . NEWSLETTER_STATS_TABLE . " (email_id)");
56
+ $this->query("create index user_id on " . NEWSLETTER_STATS_TABLE . " (user_id)");
57
+ $controls->add_message_done();
58
+ update_option('newsletter_stats_email_column_upgraded', true);
59
+ }
60
+
61
+ // Compute the number of newsletters ongoing and other stats
62
+ $emails = $wpdb->get_results("select * from " . NEWSLETTER_EMAILS_TABLE . " where status='sending' and send_on<" . time() . " order by id asc");
63
+ $total = 0;
64
+ $queued = 0;
65
+ foreach ($emails as $email) {
66
+ $total += $email->total;
67
+ $queued += $email->total - $email->sent;
68
+ }
69
+ $speed = $newsletter->get_send_speed();
70
+
71
+ // Trick to access the private function (!)
72
+ class TNP_WPDB extends wpdb {
73
+
74
+ public function get_table_charset($table) {
75
+ return parent::get_table_charset($table);
76
+ }
77
+
78
+ }
79
+
80
+ $tnp_wpdb = new TNP_WPDB(DB_USER, DB_PASSWORD, DB_NAME, DB_HOST);
81
+ ?>
82
+
83
+ <style>
84
+ <?php include __DIR__ . '/css/system.css' ?>
85
+ </style>
86
+
87
+ <div class="wrap tnp-system tnp-system-status" id="tnp-wrap">
88
+
89
+ <?php include NEWSLETTER_DIR . '/tnp-header.php'; ?>
90
+
91
+ <div id="tnp-heading">
92
+
93
+ <h2><?php _e('System Status', 'newsletter') ?></h2>
94
+
95
+ </div>
96
+
97
+ <div id="tnp-body">
98
+
99
+ <form method="post" action="">
100
+ <?php $controls->init(); ?>
101
+
102
+
103
+ <h3>Delivery</h3>
104
+ <table class="widefat" id="tnp-status-table">
105
+
106
+ <thead>
107
+ <tr>
108
+ <th>Parameter</th>
109
+ <th><?php _e('Status', 'newsletter') ?></th>
110
+ <th>Action</th>
111
+ </tr>
112
+
113
+ </thead>
114
+
115
+ <tbody>
116
+
117
+ <tr>
118
+ <td>Delivering</td>
119
+ <td class="status">
120
+ &nbsp;
121
+ </td>
122
+ <td>
123
+ <?php if (count($emails)) { ?>
124
+ Delivering <?php echo count($emails) ?> newsletters to about <?php echo $queued ?> recipients.
125
+ At speed of <?php echo $speed ?> emails per hour it will take <?php printf('%.1f', $queued / $speed) ?> hours to finish.
126
+
127
+ <?php } else { ?>
128
+ Nothing delivering right now
129
+ <?php } ?>
130
+ </td>
131
+
132
+ </tr>
133
+ <tr>
134
+ <td>Mailer</td>
135
+ <td>
136
+ &nbsp;
137
+ </td>
138
+ <td>
139
+ <?php echo esc_html($mailer->get_description()) ?>
140
+ </td>
141
+ </tr>
142
+ <?php
143
+ $stats = $this->get_send_stats();
144
+
145
+ if ($stats) {
146
+ $condition = $stats->mean > 5 ? 2 : 1;
147
+ ?>
148
+ <tr>
149
+ <td id="tnp-speed">
150
+ Send details
151
+ </td>
152
+ <td class="status">
153
+ <?php $this->condition_flag($condition) ?>
154
+
155
+ </td>
156
+ <td>
157
+ <?php if ($condition == 2) { ?>
158
+ <strong>Sending an email is taking more than 5 seconds (by mean), rather slow.</strong>
159
+ <a href="https://www.thenewsletterplugin.com/documentation/installation/status-panel/#email-speed" target="_blank">Read more</a>.
160
+ <br>
161
+ <?php } ?>
162
+ Average time to send an email: <?php echo $stats->mean ?> seconds<br>
163
+ <?php if ($stats->mean > 0) { ?>
164
+ Max speed: <?php echo sprintf("%.2f", 1.0 / $stats->mean * 3600) ?> emails per hour<br>
165
+ <?php } ?>
166
+
167
+ Max mean time measured: <?php echo $stats->max ?> seconds<br>
168
+ Min mean time measured: <?php echo $stats->min ?> seconds<br>
169
+ Total emails in the sample: <?php echo $stats->total_emails ?><br>
170
+ Total sending time: <?php echo $stats->total_time ?> seconds<br>
171
+ Runs in the sample: <?php echo $stats->total_runs ?><br>
172
+ Runs prematurely interrupted: <?php echo $stats->interrupted ?><br>
173
+
174
+
175
+ <canvas id="tnp-send-chart" style="width: 550px; height: 150px"></canvas>
176
+ <script>
177
+ jQuery(function () {
178
+ var sendChartData = {
179
+ labels: <?php echo json_encode(range(1, count($stats->means))) ?>,
180
+ datasets: [
181
+ {
182
+ label: "Batch Average Time",
183
+ data: <?php echo json_encode($stats->means) ?>,
184
+ borderColor: '#2980b9',
185
+ fill: false
186
+ }/*,
187
+ {
188
+ label: "Batch Average Time",
189
+ data: <?php echo json_encode($stats->sizes) ?>,
190
+ borderColor: '#b98028',
191
+ fill: false
192
+ }*/]
193
+ };
194
+ var sendChartConfig = {
195
+ type: "line",
196
+ data: sendChartData,
197
+ options: {
198
+ responsive: false,
199
+ maintainAspectRatio: false
200
+ }
201
+ };
202
+ new Chart('tnp-send-chart', sendChartConfig);
203
+ });
204
+ </script>
205
+ <br>
206
+ <?php $controls->button_reset('reset_send_stats') ?>
207
+ </td>
208
+ </tr>
209
+ <?php } else { ?>
210
+ <tr>
211
+ <td>
212
+ Sending statistics
213
+ </td>
214
+ <td>
215
+ &nbsp;
216
+ </td>
217
+ <td>
218
+ Not enough data available.
219
+ </td>
220
+ </tr>
221
+ <?php } ?>
222
+ </tbody>
223
+ </table>
224
+
225
+ <h3>General checks</h3>
226
+ <table class="widefat" id="tnp-status-table">
227
+
228
+ <thead>
229
+ <tr>
230
+ <th>Parameter</th>
231
+ <th><?php _e('Status', 'newsletter') ?></th>
232
+ <th>Action</th>
233
+ </tr>
234
+
235
+ </thead>
236
+
237
+ <tbody>
238
+
239
+ <tr>
240
+ <?php
241
+ $page_id = $newsletter->get_newsletter_page_id();
242
+ $page = $newsletter->get_newsletter_page();
243
+ $condition = 1;
244
+ if ($page_id) {
245
+ if (!$page || $page->post_status !== 'publish') {
246
+ $condition = 0;
247
+ }
248
+ } else {
249
+ $condition = 2;
250
+ }
251
+ ?>
252
+ <td>
253
+ Dedicated page<br>
254
+ <small>The blog page Newsletter uses for messages</small>
255
+ </td>
256
+ <td>
257
+ <?php $this->condition_flag($condition) ?>
258
+ </td>
259
+ <td>
260
+ <?php if ($condition == 2) { ?>
261
+ Newsletter is using a neutral page to show messages, if you want to use a dedicated page, configure it on
262
+ <a href="?page=newsletter_main_main">main settings</a>.
263
+ <?php } else if ($condition == 0) { ?>
264
+ A dedicated page is set but it is no more available or no more published. Review the dedicated page on
265
+ <a href="?page=newsletter_main_main">main settings</a>.
266
+ <?php } ?>
267
+ </td>
268
+ </tr>
269
+
270
+ <tr>
271
+ <?php
272
+ $page_id = $newsletter->get_newsletter_page_id();
273
+ $page = $newsletter->get_newsletter_page();
274
+ $condition = 1;
275
+ if ($page_id) {
276
+ if (!$page) {
277
+ $condition = 0;
278
+ } else {
279
+ $content = $page->post_content;
280
+ if (strpos($content, '[newsletter]') === false && strpos($content, '[newsletter ') === false) {
281
+ $condition = 2;
282
+ }
283
+ }
284
+ }
285
+ ?>
286
+ <td>
287
+ Dedicated page content<br>
288
+ </td>
289
+ <td>
290
+ <?php $this->condition_flag($condition) ?>
291
+ </td>
292
+ <td>
293
+ <?php if ($condition == 2) { ?>
294
+ The page seems to not contain the <code>[newsletter]</code>, but sometime it cannot be detected if you use
295
+ a visual composer. <a href="post.php?post=<?php echo $page->ID ?>&action=edit" target="_blank">Please, check the page</a>.
296
+ <?php } else if ($condition == 0) { ?>
297
+ The dedicated page seems to not be available.
298
+ <?php } ?>
299
+ </td>
300
+ </tr>
301
+
302
+ <?php
303
+ $method = '';
304
+ if (function_exists('get_filesystem_method')) {
305
+ $method = get_filesystem_method(array(), WP_PLUGIN_DIR);
306
+ }
307
+ if (empty($method))
308
+ $condition = 2;
309
+ else if ($method == 'direct')
310
+ $condition = 1;
311
+ else
312
+ $condition = 0;
313
+ ?>
314
+ <tr>
315
+ <td>Add-ons installable</td>
316
+ <td>
317
+ <?php $this->condition_flag($condition) ?>
318
+ </td>
319
+ <td>
320
+ <?php if ($condition == 2) { ?>
321
+ No able to check, just try the add-ons manager one click install
322
+ <?php } else if ($condition == 1) { ?>
323
+ The add-ons manager can install our add-ons
324
+ <?php } else { ?>
325
+ The plugins dir could be read-only, you can install add-ons uploading the package from the
326
+ plugins panel (or uploading them directly via FTP). This is unusual you should ask te provider
327
+ about file and folder permissions.
328
+ <?php } ?>
329
+ </td>
330
+
331
+ </tr>
332
+
333
+
334
+ <?php
335
+ $return_path = $newsletter->options['return_path'];
336
+ if (!empty($return_path)) {
337
+ list($return_path_local, $return_path_domain) = explode('@', $return_path);
338
+ }
339
+ $sender = $newsletter->options['sender_email'];
340
+ if (!empty($sender)) {
341
+ list($sender_local, $sender_domain) = explode('@', $sender);
342
+ }
343
+ ?>
344
+ <tr>
345
+ <td>Return path</td>
346
+ <td>
347
+ <?php if (empty($return_path)) { ?>
348
+ <span class="tnp-ok">OK</span>
349
+ <?php } else { ?>
350
+ <?php if ($sender_domain != $return_path_domain) { ?>
351
+ <span class="tnp-maybe">MAYBE</span>
352
+ <?php } else { ?>
353
+ <span class="tnp-ok">OK</span>
354
+ <?php } ?>
355
+ <?php } ?>
356
+
357
+ </td>
358
+ <td>
359
+ <?php if (!empty($return_path)) { ?>
360
+ Some providers require the return path domain <code><?php echo esc_html($return_path_domain) ?></code> to be identical
361
+ to the sender domain <code><?php echo esc_html($sender_domain) ?></code>. See the main settings.
362
+ <?php } else { ?>
363
+ <?php } ?>
364
+ </td>
365
+
366
+ </tr>
367
+
368
+
369
+
370
+
371
+
372
+ <tr>
373
+ <?php
374
+ $condition = NEWSLETTER_EXTENSION_UPDATE ? 1 : 0;
375
+ ?>
376
+ <td>Addons update</td>
377
+ <td>
378
+ <?php $this->condition_flag($condition) ?>
379
+ </td>
380
+ <td>
381
+ <?php if ($condition == 0) { ?>
382
+ Newsletter Addons update is disabled (probably in your <code>wp-config.php</code> file the constant
383
+ <code>NEWSLETTER_EXTENSION_UPDATE</code> is set to <code>true</code>)
384
+ <?php } else { ?>
385
+ Newsletter Addons can be updated
386
+ <?php } ?>
387
+ </td>
388
+
389
+ </tr>
390
+
391
+ <tr>
392
+ <?php
393
+ $res = true;
394
+ $response = wp_remote_get('http://www.thenewsletterplugin.com/wp-content/extensions.json');
395
+ $condition = 1;
396
+ if (is_wp_error($response)) {
397
+ $res = false;
398
+ $condition = 0;
399
+ $message = $response->get_error_message();
400
+ } else {
401
+ if (wp_remote_retrieve_response_code($response) != 200) {
402
+ $res = false;
403
+ $condition = 0;
404
+ $message = wp_remote_retrieve_response_message($response);
405
+ }
406
+ }
407
+ ?>
408
+
409
+ <td>
410
+ Addons version check<br>
411
+ <small>Your blog can check the professional addon updates?</small>
412
+ </td>
413
+ <td>
414
+ <?php $this->condition_flag($condition) ?>
415
+ </td>
416
+ <td>
417
+ <?php if ($condition == 0) { ?>
418
+ The blog cannot contact www.thenewsletterplugin.com to check the license or the extension versions.<br>
419
+ Error: <?php echo esc_html($message) ?><br>
420
+ <?php } else { ?>
421
+
422
+ <?php } ?>
423
+ </td>
424
+ </tr>
425
+
426
+
427
+
428
+
429
+
430
+
431
+
432
+
433
+
434
+ <?php /*
435
+ $memory = intval(WP_MEMORY_LIMIT);
436
+ if (false !== strpos(WP_MEMORY_LIMIT, 'G'))
437
+ $memory *= 1024;
438
+ ?>
439
+ <tr>
440
+ <td>
441
+ PHP memory limit
442
+ </td>
443
+ <td>
444
+ <?php if ($memory < 64) { ?>
445
+ <span class="tnp-ko">MAYBE</span>
446
+ <?php } else if ($memory < 128) { ?>
447
+ <span class="tnp-maybe">MAYBE</span>
448
+ <?php } else { ?>
449
+ <span class="tnp-ok">OK</span>
450
+ <?php } ?>
451
+ </td>
452
+ <td>
453
+ WordPress WP_MEMORY_LIMIT is set to <?php echo $memory ?> megabyte but your PHP setting could allow more than that.
454
+ Anyway we suggest to set the value to at least 64M.
455
+ <a href="https://www.thenewsletterplugin.com/documentation/status-panel#status-memory" target="_blank">Read more</a>.
456
+ <?php if ($memory < 64) { ?>
457
+ This value is too low you should increase it adding <code>define('WP_MEMORY_LIMIT', '64M');</code> to your <code>wp-config.php</code>.
458
+ <a href="https://www.thenewsletterplugin.com/documentation/status-panel#status-memory" target="_blank">Read more</a>.
459
+ <?php } else if ($memory < 128) { ?>
460
+ The value should be fine, it depends on how many plugins you're running and how many resource are required by your theme.
461
+ Blank pages may happen with low memory problems. Eventually increase it adding <code>define('WP_MEMORY_LIMIT', '128M');</code>
462
+ to your <code>wp-config.php</code>.
463
+ <a href="https://www.thenewsletterplugin.com/documentation/status-panel#status-memory" target="_blank">Read more</a>.
464
+ <?php } else { ?>
465
+
466
+ <?php } ?>
467
+
468
+ </td>
469
+ </tr>
470
+ */ ?>
471
+
472
+ <?php
473
+ $ip = gethostbyname($_SERVER['HTTP_HOST']);
474
+ $name = gethostbyaddr($ip);
475
+ $res = true;
476
+ if (strpos($name, '.secureserver.net') !== false) {
477
+ //$smtp = get_option('newsletter_main_smtp');
478
+ //if (!empty($smtp['enabled']))
479
+ $res = false;
480
+ $message = 'If you\'re hosted with GoDaddy, be sure to set their SMTP (relay-hosting.secureserver.net, without username and password) to send emails
481
+ on Newsletter SMTP panel.
482
+ Remember they limits you to 250 emails per day. Open them a ticket for more details.';
483
+ }
484
+ if (strpos($name, '.aruba.it') !== false) {
485
+ $res = false;
486
+ $message = 'If you\'re hosted with Aruba consider to use an external SMTP (Sendgrid, Mailjet, Mailgun, Amazon SES, Elasticemail, Sparkpost, ...)
487
+ since their mail service is not good. If you have your personal email with them, you can try to use the SMTP of your
488
+ pesonal account. Ask the support for the SMTP parameters and configure them on Newsletter SMTP panel.';
489
+ }
490
+ ?>
491
+ <tr>
492
+ <td>Your Server</td>
493
+ <td>
494
+ <?php if ($res === false) { ?>
495
+ <span class="tnp-maybe">MAYBE</span>
496
+ <?php } else { ?>
497
+ <span class="tnp-ok">OK</span>
498
+ <?php } ?>
499
+
500
+
501
+ </td>
502
+ <td>
503
+ <?php if ($res === false) { ?>
504
+ <?php echo $message ?>
505
+ <?php } else { ?>
506
+
507
+ <?php } ?>
508
+ IP: <?php echo $ip ?><br>
509
+ Name: <?php echo $name ?><br>
510
+ </td>
511
+ </tr>
512
+
513
+ <?php
514
+ wp_mkdir_p(NEWSLETTER_LOG_DIR);
515
+ $condition = is_dir(NEWSLETTER_LOG_DIR) && is_writable(NEWSLETTER_LOG_DIR) ? 1 : 0;
516
+ if ($condition) {
517
+ @file_put_contents(NEWSLETTER_LOG_DIR . '/test.txt', "");
518
+ $condition = is_file(NEWSLETTER_LOG_DIR . '/test.txt') ? 1 : 0;
519
+ if ($condition) {
520
+ @unlink(NEWSLETTER_LOG_DIR . '/test.txt');
521
+ }
522
+ }
523
+ ?>
524
+ <tr>
525
+ <td>
526
+ Log folder
527
+ </td>
528
+ <td>
529
+ <?php $this->condition_flag($condition) ?>
530
+ </td>
531
+ <td>
532
+ The log folder is <?php echo esc_html(NEWSLETTER_LOG_DIR) ?><br>
533
+ <?php if (!$res) { ?>
534
+ Cannot create the folder or it is not writable.
535
+ <?php } ?>
536
+ </td>
537
+ </tr>
538
+ </tbody>
539
+ </table>
540
+
541
+ <h3>3rd party plugins</h3>
542
+ <table class="widefat" id="tnp-status-table">
543
+ <thead>
544
+ <tr>
545
+ <th>Plugin</th>
546
+ <th><?php _e('Status', 'newsletter') ?></th>
547
+ <th>Action</th>
548
+ </tr>
549
+ </thead>
550
+ <tbody>
551
+ <?php if (is_plugin_active('plugin-load-filter/plugin-load-filter.php')) { ?>
552
+ <tr>
553
+ <td><a href="https://wordpress.org/plugins/plugin-load-filter/" target="_blank">Plugin load filter</a></td>
554
+ <td>
555
+ <span class="tnp-maybe">MAY BE</span>
556
+ </td>
557
+ <td>
558
+ Be sure Newsletter is set as active on EVERY context.
559
+ </td>
560
+ </tr>
561
+ <?php } ?>
562
+
563
+ <?php if (is_plugin_active('wp-asset-clean-up/wpacu.php')) { ?>
564
+ <tr>
565
+ <td><a href="https://wordpress.org/plugins/wp-asset-clean-up/" target="_blank">WP Asset Clean Up</a></td>
566
+ <td>
567
+ <span class="tnp-maybe">MAY BE</span>
568
+ </td>
569
+ <td>
570
+ Be sure Newsletter is set as active on EVERY context.
571
+ </td>
572
+ </tr>
573
+ <?php } ?>
574
+
575
+ <?php if (is_plugin_active('freesoul-deactivate-plugins/freesoul-deactivate-plugins.php')) { ?>
576
+ <tr>
577
+ <td><a href="https://wordpress.org/plugins/freesoul-deactivate-plugins/" target="_blank">Freesoul Deactivate Plugins</a></td>
578
+ <td>
579
+ <span class="tnp-maybe">MAY BE</span>
580
+ </td>
581
+ <td>
582
+ Be sure Newsletter is set as active on EVERY context.
583
+ </td>
584
+ </tr>
585
+ <?php } ?>
586
+
587
+ </tbody>
588
+ </table>
589
+
590
+
591
+ <h3>WordPress</h3>
592
+
593
+ <table class="widefat" id="tnp-status-table">
594
+ <thead>
595
+ <tr>
596
+ <th>Parameter</th>
597
+ <th><?php _e('Status', 'newsletter') ?></th>
598
+ <th>Action</th>
599
+ </tr>
600
+ </thead>
601
+ <tbody>
602
+
603
+ <tr>
604
+ <?php
605
+ $condition = (defined('WP_DEBUG') && WP_DEBUG) ? 2 : 1;
606
+ ?>
607
+ <td>
608
+ WordPress debug mode
609
+ </td>
610
+ <td>
611
+ <?php $this->condition_flag($condition) ?>
612
+ </td>
613
+ <td>
614
+ <?php if (defined('WP_DEBUG') && WP_DEBUG) { ?>
615
+ WordPress is in debug mode it is not recommended on a production system. See the constant <code>WP_DEBUG</code> inside the <code>wp-config.php</code>.
616
+ <?php } else { ?>
617
+
618
+ <?php } ?>
619
+ </td>
620
+ </tr>
621
+
622
+
623
+
624
+ <tr>
625
+ <?php
626
+ $charset = get_option('blog_charset');
627
+ $condition = $charset === 'UTF-8' ? 1 : 0;
628
+ ?>
629
+ <td>Blog Charset</td>
630
+ <td>
631
+ <?php $this->condition_flag($condition) ?>
632
+ </td>
633
+ <td>
634
+ Charset: <?php echo esc_html($charset) ?>
635
+ <br>
636
+
637
+ <?php if ($condition == 1) { ?>
638
+
639
+ <?php } else { ?>
640
+ It is recommended to use
641
+ the <code>UTF-8</code> charset but the <a href="https://codex.wordpress.org/Converting_Database_Character_Sets" target="_blank">conversion</a>
642
+ could be tricky. If you're not experiencing problem, leave things as is.
643
+ <?php } ?>
644
+ </td>
645
+ </tr>
646
+
647
+ <tr>
648
+ <?php
649
+ $condition = (strpos(home_url('/'), 'http') !== 0) ? 0 : 1;
650
+ ?>
651
+ <td>Home URL</td>
652
+ <td>
653
+ <?php $this->condition_flag($condition) ?>
654
+ </td>
655
+ <td>
656
+ Value: <?php echo home_url('/'); ?>
657
+ <br>
658
+ <?php if ($condition == 0) { ?>
659
+ Your home URL is not absolute, emails require absolute URLs.
660
+ Probably you have a protocol agnostic plugin installed to manage both HTTPS and HTTP in your
661
+ blog.
662
+ <?php } else { ?>
663
+
664
+ <?php } ?>
665
+ </td>
666
+ </tr>
667
+
668
+ <tr>
669
+ <?php
670
+ $condition = (strpos(WP_CONTENT_URL, 'http') !== 0) ? 0 : 1;
671
+ ?>
672
+ <td>WP_CONTENT_URL</td>
673
+ <td>
674
+ <?php $this->condition_flag($condition) ?>
675
+ </td>
676
+ <td>
677
+ Value: <?php echo esc_html(WP_CONTENT_URL); ?>
678
+ <br>
679
+ <?php if ($condition == 0) { ?>
680
+ Your content URL is not absolute, emails require absolute URLs when they have images inside.
681
+ Newsletter tries to deal with this problem but when a problem with images persists, you should try to remove
682
+ from your <code>wp-config.php</code> the <code>WP_CONTENT_URL</code> define and check again.
683
+ <?php } else { ?>
684
+
685
+ <?php } ?>
686
+ </td>
687
+ </tr>
688
+
689
+
690
+ <tr>
691
+ <?php
692
+ $attachments = get_posts([
693
+ 'post_type' => 'attachment',
694
+ 'post_mime_type' => 'image',
695
+ 'numberposts' => 1,
696
+ 'post_status' => null,
697
+ 'post_parent' => null
698
+ ]);
699
+ $condition = 1;
700
+ $src = 'No media found to make a test';
701
+ if ($attachments) {
702
+ $src = wp_get_attachment_image_src($attachments[0]->ID);
703
+ $src = $src[0];
704
+ $condition = (strpos($src, 'http') !== 0) ? 0 : 1;
705
+ }
706
+ ?>
707
+ <td>Images URL</td>
708
+ <td>
709
+ <?php $this->condition_flag($condition) ?>
710
+ </td>
711
+ <td>
712
+ Example: <?php echo esc_html($src); ?>
713
+
714
+ <?php if ($condition == 0) { ?>
715
+ <br><br>
716
+ Your uploadimages seems to be returned with a relative URL: they won't work in your newsletter. Check the <code>WP_CONTENT_URL</code>
717
+ above and fix it if is showing a warning. If not, probably a plugin or some custom code is forcing relative URLs for your
718
+ images. Check that with your site developer.
719
+ <?php } else { ?>
720
+
721
+ <?php } ?>
722
+ </td>
723
+ </tr>
724
+
725
+
726
+
727
+ <tr>
728
+ <?php
729
+ $uploads = wp_upload_dir();
730
+ ?>
731
+ <td>Uploads dir and url</td>
732
+ <td>
733
+
734
+ </td>
735
+ <td>
736
+ <table class="widefat" style="width: auto">
737
+ <thead>
738
+ <tr><th>Key</th><th>Value</th></tr>
739
+ </thead>
740
+ <tbody>
741
+ <?php foreach ($uploads as $k => $v) { ?>
742
+ <tr><td><?php echo esc_html($k) ?></td><td><?php echo esc_html($v) ?></td></tr>
743
+ <?php } ?>
744
+ </tbody>
745
+ </table>
746
+
747
+ </td>
748
+ </tr>
749
+
750
+ <tr>
751
+ <?php
752
+ set_transient('newsletter_transient_test', 1, 300);
753
+ delete_transient('newsletter_transient_test');
754
+ $res = get_transient('newsletter_transient_test');
755
+ $condition = ($res !== false) ? 0 : 1;
756
+ ?>
757
+ <td>WordPress transients</td>
758
+ <td>
759
+ <?php $this->condition_flag($condition) ?>
760
+ </td>
761
+ <td>
762
+ <?php if ($res !== false) { ?>
763
+ Transients cannot be delete. This can block the delivery engine. Usually it is due to a not well coded plugin installed.
764
+ <?php } else { ?>
765
+ <?php } ?>
766
+ </td>
767
+ </tr>
768
+ </tbody>
769
+ </table>
770
+
771
+
772
+
773
+ <h3>PHP</h3>
774
+ <table class="widefat" id="tnp-status-table">
775
+ <thead>
776
+ <tr>
777
+ <th>Parameter</th>
778
+ <th><?php _e('Status', 'newsletter') ?></th>
779
+ <th>Action</th>
780
+ </tr>
781
+ </thead>
782
+ <tbody>
783
+ <tr>
784
+ <td>PHP version</td>
785
+ <td>
786
+ <?php if (version_compare(phpversion(), '5.6', '<')) { ?>
787
+ <span class="tnp-ko">KO</span>
788
+ <?php } else { ?>
789
+ <span class="tnp-ok">OK</span>
790
+ <?php } ?>
791
+
792
+ </td>
793
+ <td>
794
+ Your PHP version is <?php echo phpversion() ?><br>
795
+ <?php if (version_compare(phpversion(), '5.3', '<')) { ?>
796
+ Newsletter plugin works correctly with PHP version 5.6 or greater. Ask your provider to upgrade your PHP. Your version is
797
+ unsupported even by the PHP community.
798
+ <?php } ?>
799
+ </td>
800
+
801
+ </tr>
802
+
803
+ <tr>
804
+ <?php
805
+ $value = (int) ini_get('max_execution_time');
806
+ $res = true;
807
+ $condition = 1;
808
+ if ($value != 0 && $value < NEWSLETTER_CRON_INTERVAL) {
809
+ $res = set_time_limit(NEWSLETTER_CRON_INTERVAL);
810
+ if ($res)
811
+ $condition = 1;
812
+ else
813
+ $condition = 0;
814
+ }
815
+ ?>
816
+ <td>PHP execution time limit</td>
817
+ <td>
818
+ <?php $this->condition_flag($condition) ?>
819
+ </td>
820
+ <td>
821
+ <?php if (!$res) { ?>
822
+ Your PHP execution time limit is <?php echo $value ?> seconds. It cannot be changed and it is too lower to grant the maximum delivery rate of Newsletter.
823
+ <?php } else { ?>
824
+ Your PHP execution time limit is <?php echo $value ?> seconds and can be eventually changed by Newsletter.<br>
825
+ <?php } ?>
826
+
827
+ </td>
828
+
829
+ </tr>
830
+
831
+
832
+ <tr>
833
+ <?php
834
+ $condition = function_exists('curl_version');
835
+ ?>
836
+ <td>Curl version</td>
837
+ <td>
838
+ <?php if (!$condition) { ?>
839
+ <span class="tnp-ko">KO</span>
840
+ <?php } else { ?>
841
+ <span class="tnp-ok">OK</span>
842
+ <?php } ?>
843
+
844
+ </td>
845
+ <td>
846
+ <?php
847
+ if (!$condition) {
848
+ echo 'cUrl is not available, ask the provider to install it and activate the PHP cUrl library';
849
+ } else {
850
+ $version = curl_version();
851
+ echo 'Version: ' . $version['version'] . '<br>';
852
+ echo 'SSL Version: ' . $version['ssl_version'] . '<br>';
853
+ }
854
+ ?>
855
+ </td>
856
+
857
+ </tr>
858
+ <?php if (ini_get('opcache.validate_timestamps') === '0') { ?>
859
+ <tr>
860
+ <td>
861
+ Opcache
862
+ </td>
863
+
864
+ <td>
865
+ <span class="tnp-ko">KO</span>
866
+ </td>
867
+
868
+ <td>
869
+ You have the PHP opcache active with file validation disable so every blog plugins update needs a webserver restart!
870
+ </td>
871
+ </tr>
872
+ <?php } ?>
873
+ </tbody>
874
+ </table>
875
+
876
+ <h3>Database</h3>
877
+ <table class="widefat" id="tnp-status-table">
878
+ <thead>
879
+ <tr>
880
+ <th>Parameter</th>
881
+ <th><?php _e('Status', 'newsletter') ?></th>
882
+ <th>Action</th>
883
+ </tr>
884
+ </thead>
885
+ <tbody>
886
+ <tr>
887
+ <td>Database Charset</td>
888
+ <td>
889
+ <?php if ($wpdb->charset != 'utf8mb4') { ?>
890
+ <span class="tnp-ko">KO</span>
891
+ <?php } else { ?>
892
+ <span class="tnp-ok">OK</span>
893
+ <?php } ?>
894
+
895
+ </td>
896
+ <td>
897
+ Charset: <?php echo $wpdb->charset; ?>
898
+ <br>
899
+ <?php if ($wpdb->charset != 'utf8mb4') { ?>
900
+ The recommended charset for your database is <code>utf8mb4</code> to avoid possible saving errors when you use emoji.
901
+ Read the WordPress Codex <a href="https://codex.wordpress.org/Converting_Database_Character_Sets" target="_blank">conversion
902
+ instructions</a> (skilled technicia required).
903
+ <?php } else { ?>
904
+ If you experience newsletter saving database error
905
+ <?php $controls->button('conversion', 'Try tables upgrade') ?>
906
+ <?php } ?>
907
+ </td>
908
+ </tr>
909
+
910
+ <tr>
911
+ <td>get_table_charset()</td>
912
+ <td>
913
+
914
+ </td>
915
+ <td>
916
+ <?php echo esc_html(NEWSLETTER_USERS_TABLE), ': ', esc_html($tnp_wpdb->get_table_charset(NEWSLETTER_USERS_TABLE)) ?>
917
+ </td>
918
+ </tr>
919
+
920
+
921
+
922
+
923
+ <?php
924
+ $wait_timeout = $wpdb->get_var("select @@wait_timeout");
925
+ $condition = ($wait_timeout < 30) ? 0 : 1;
926
+ ?>
927
+ <tr>
928
+ <td>Database wait timeout</td>
929
+ <td>
930
+ <?php $this->condition_flag($condition) ?>
931
+ </td>
932
+ <td>
933
+ Your database wait timeout is <?php echo $wait_timeout; ?> seconds<br>
934
+ <?php if ($wait_timeout < 30) { ?>
935
+ That value is low and could produce database connection errors while sending emails or during long import
936
+ sessions. Ask the provider to raise it at least to 60 seconds.
937
+ <?php } ?>
938
+ </td>
939
+ </tr>
940
+
941
+ <?php
942
+ $res = $wpdb->query("drop table if exists {$wpdb->prefix}newsletter_test");
943
+ $res = $wpdb->query("create table if not exists {$wpdb->prefix}newsletter_test (id int(20))");
944
+ $condition = $res === false ? 0 : 1;
945
+ ?>
946
+ <tr>
947
+ <td>Database table creation</td>
948
+ <td>
949
+ <?php $this->condition_flag($condition) ?>
950
+ </td>
951
+ <td>
952
+ <?php if ($res === false) { ?>
953
+ Check the privileges of the user you use to connect to the database, it seems it cannot create tables.<br>
954
+ (<?php echo esc_html($wpdb->last_error) ?>)
955
+ <?php } else { ?>
956
+ <?php } ?>
957
+ </td>
958
+ </tr>
959
+
960
+ <?php
961
+ $res = $wpdb->query("alter table {$wpdb->prefix}newsletter_test add column id1 int(20)");
962
+ $condition = $res === false ? 0 : 1;
963
+ ?>
964
+ <tr>
965
+ <td>Database table change</td>
966
+ <td>
967
+ <?php $this->condition_flag($condition) ?>
968
+ </td>
969
+ <td>
970
+ <?php if ($res === false) { ?>
971
+ Check the privileges of the user you use to connect to the database, it seems it cannot change the tables. It's require to update the
972
+ plugin.<br>
973
+ (<?php echo esc_html($wpdb->last_error) ?>)
974
+ <?php } else { ?>
975
+ <?php } ?>
976
+ </td>
977
+ </tr>
978
+
979
+ <?php
980
+ // Clean up
981
+ $res = $wpdb->query("drop table if exists {$wpdb->prefix}newsletter_test");
982
+ ?>
983
+
984
+ <?php if (!get_option('newsletter_stats_email_column_upgraded', false)) { ?>
985
+ <?php
986
+ $data_type = $wpdb->get_var(
987
+ $wpdb->prepare('SELECT DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = %s AND TABLE_NAME = %s AND COLUMN_NAME = %s',
988
+ DB_NAME, NEWSLETTER_STATS_TABLE, 'email_id'));
989
+ $to_upgrade = strtoupper($data_type) == 'INT' ? false : true;
990
+ ?>
991
+ <?php if ($to_upgrade) { ?>
992
+ <tr>
993
+ <td>Database stats table upgrade</td>
994
+ <td><?php $this->condition_flag(0) ?></td>
995
+ <td><?php $controls->button('stats_email_column_upgrade', 'Stats table upgrade') ?></td>
996
+ </tr>
997
+ <?php } ?>
998
+ <?php } ?>
999
+
1000
+ </tbody>
1001
+ </table>
1002
+
1003
+
1004
+
1005
+ <h3>General parameters</h3>
1006
+ <table class="widefat" id="tnp-parameters-table">
1007
+ <thead>
1008
+ <tr>
1009
+ <th>Parameter</th>
1010
+ <th>Value</th>
1011
+ </tr>
1012
+ </thead>
1013
+ <tbody>
1014
+
1015
+ <tr>
1016
+ <td>Newsletter version</td>
1017
+ <td>
1018
+ <?php echo NEWSLETTER_VERSION ?>
1019
+ </td>
1020
+ </tr>
1021
+
1022
+ <tr>
1023
+ <td>NEWSLETTER_MAX_EXECUTION_TIME</td>
1024
+ <td>
1025
+ <?php
1026
+ if (defined('NEWSLETTER_MAX_EXECUTION_TIME')) {
1027
+ echo NEWSLETTER_MAX_EXECUTION_TIME . ' (seconds)';
1028
+ } else {
1029
+ echo 'Not set';
1030
+ }
1031
+ ?>
1032
+ </td>
1033
+ </tr>
1034
+
1035
+
1036
+
1037
+
1038
+ <?php /*
1039
+ <tr>
1040
+ <td>WordPress plugin url</td>
1041
+ <td>
1042
+ <?php echo WP_PLUGIN_URL; ?>
1043
+ <br>
1044
+ Filters:
1045
+
1046
+ <?php
1047
+ if (isset($wp_filter))
1048
+ $filters = $wp_filter['plugins_url'];
1049
+ if (!isset($filters) || !is_array($filters))
1050
+ echo 'no filters attached to "plugin_urls"';
1051
+ else {
1052
+ echo '<ul>';
1053
+ foreach ($filters as &$filter) {
1054
+ foreach ($filter as &$entry) {
1055
+ echo '<li>';
1056
+ if (is_array($entry['function']))
1057
+ echo esc_html(get_class($entry['function'][0]) . '->' . $entry['function'][1]);
1058
+ else
1059
+ echo esc_html($entry['function']);
1060
+ echo '</li>';
1061
+ }
1062
+ }
1063
+ echo '</ul>';
1064
+ }
1065
+ ?>
1066
+ <p class="description">
1067
+ This value should contains the full URL to your plugin folder. If there are filters
1068
+ attached, the value can be different from the original generated by WordPress and sometime worng.
1069
+ </p>
1070
+ </td>
1071
+ </tr>
1072
+ */ ?>
1073
+
1074
+ <tr>
1075
+ <td>Absolute path</td>
1076
+ <td>
1077
+ <?php echo esc_html(ABSPATH); ?>
1078
+ </td>
1079
+ </tr>
1080
+ <tr>
1081
+ <td>Tables Prefix</td>
1082
+ <td>
1083
+ <?php echo $wpdb->prefix; ?>
1084
+ </td>
1085
+ </tr>
1086
+ </tbody>
1087
+ </table>
1088
+
1089
+
1090
+ <?php if (isset($_GET['debug'])) { ?>
1091
+
1092
+ <h3>Database Tables</h3>
1093
+ <h4><?php echo $wpdb->prefix ?>newsletter</h4>
1094
+ <?php
1095
+ $rs = $wpdb->get_results("describe {$wpdb->prefix}newsletter");
1096
+ ?>
1097
+ <table class="tnp-db-table">
1098
+ <thead>
1099
+ <tr>
1100
+ <th>Field</th>
1101
+ <th>Type</th>
1102
+ <th>Null</th>
1103
+ <th>Key</th>
1104
+ <th>Default</th>
1105
+ <th>Extra</th>
1106
+ </tr>
1107
+ </thead>
1108
+ <tbody>
1109
+ <?php foreach ($rs as $r) { ?>
1110
+ <tr>
1111
+ <td><?php echo esc_html($r->Field) ?></td>
1112
+ <td><?php echo esc_html($r->Type) ?></td>
1113
+ <td><?php echo esc_html($r->Null) ?></td>
1114
+ <td><?php echo esc_html($r->Key) ?></td>
1115
+ <td><?php echo esc_html($r->Default) ?></td>
1116
+ <td><?php echo esc_html($r->Extra) ?></td>
1117
+ </tr>
1118
+ <?php } ?>
1119
+ </tbody>
1120
+ </table>
1121
+
1122
+ <h4><?php echo $wpdb->prefix ?>newsletter_emails</h4>
1123
+ <?php
1124
+ $rs = $wpdb->get_results("show full columns from {$wpdb->prefix}newsletter_emails");
1125
+ ?>
1126
+ <table class="tnp-db-table">
1127
+ <thead>
1128
+ <tr>
1129
+ <th>Field</th>
1130
+ <th>Type</th>
1131
+ <th>Collation</th>
1132
+ <th>Null</th>
1133
+ <th>Key</th>
1134
+ <th>Default</th>
1135
+ <th>Extra</th>
1136
+ </tr>
1137
+ </thead>
1138
+ <tbody>
1139
+ <?php foreach ($rs as $r) { ?>
1140
+ <tr>
1141
+ <td><?php echo esc_html($r->Field) ?></td>
1142
+ <td><?php echo esc_html($r->Type) ?></td>
1143
+ <td><?php echo esc_html($r->Collation) ?></td>
1144
+ <td><?php echo esc_html($r->Null) ?></td>
1145
+ <td><?php echo esc_html($r->Key) ?></td>
1146
+ <td><?php echo esc_html($r->Default) ?></td>
1147
+ <td><?php echo esc_html($r->Extra) ?></td>
1148
+ </tr>
1149
+ <?php } ?>
1150
+ </tbody>
1151
+ </table>
1152
+
1153
+
1154
+ <h3>Extensions</h3>
1155
+ <pre style="font-size: 11px; font-family: monospace; background-color: #efefef; color: #444"><?php echo esc_html(print_r(get_option('newsletter_extension_versions'), true)); ?></pre>
1156
+
1157
+ <h3>Update plugins data</h3>
1158
+ <pre style="font-size: 11px; font-family: monospace; background-color: #efefef; color: #444"><?php echo esc_html(print_r(get_site_transient('update_plugins'), true)); ?></pre>
1159
+
1160
+ <?php } ?>
1161
+ </div>
1162
+
1163
+ <?php include NEWSLETTER_DIR . '/tnp-footer.php'; ?>
1164
+
1165
+ </div>
tnp-footer.php CHANGED
@@ -1,33 +1,33 @@
1
- <?php
2
- defined('ABSPATH') || exit;
3
- ?>
4
- <div id="tnp-footer">
5
- <div>
6
- <ul>
7
- <li><a href="https://www.thenewsletterplugin.com" target="_blank">The Newsletter Plugin</a></li>
8
- <li><a href="https://www.thenewsletterplugin.com/premium" target="_blank"><?php _e('Get Premium', 'newsletter') ?></a></li>
9
- </ul>
10
- </div>
11
- <div>
12
- <ul>
13
- <li><a href="https://www.thenewsletterplugin.com/account"><?php _e('Your Account', 'newsletter') ?></a></li>
14
- <li><a href="https://www.thenewsletterplugin.com/forums"><?php _e('Forum', 'newsletter') ?></a></li>
15
- <li><a href="https://www.thenewsletterplugin.com/blog"><?php _e('Blog', 'newsletter') ?></a></li>
16
- </ul>
17
- </div>
18
- <div>
19
- <form target="_blank" action="https://www.thenewsletterplugin.com/?na=s" method="post" style="margin: 0">
20
- <input type="email" name="ne" placeholder="Your email" required size="20" value="<?php echo esc_attr($current_user_email) ?>">
21
- <input type="hidden" value="plugin-footer" name="nr">
22
- <input type="hidden" value="3" name="nl[]">
23
- <input type="hidden" value="1" name="nl[]">
24
- <input type="hidden" value="double" name="optin">
25
- <input type="submit" value="<?php _e('Get news and promotions', 'newsletter') ?>">
26
- <span style="color: #bbb; margin-bottom: 0px; display: block; line-height: normal">
27
- Proceeding you agree to the
28
- <a href="https://www.thenewsletterplugin.com/privacy" target="_blank" style="color: #2ECC71">privacy policy</a>
29
- </span>
30
-
31
- </form>
32
- </div>
33
  </div>
1
+ <?php
2
+ defined('ABSPATH') || exit;
3
+ ?>
4
+ <div id="tnp-footer">
5
+ <div>
6
+ <ul>
7
+ <li><a href="https://www.thenewsletterplugin.com" target="_blank">The Newsletter Plugin</a></li>
8
+ <li><a href="https://www.thenewsletterplugin.com/premium" target="_blank"><?php _e('Get Premium', 'newsletter') ?></a></li>
9
+ </ul>
10
+ </div>
11
+ <div>
12
+ <ul>
13
+ <li><a href="https://www.thenewsletterplugin.com/account"><?php _e('Your Account', 'newsletter') ?></a></li>
14
+ <li><a href="https://www.thenewsletterplugin.com/forums"><?php _e('Forum', 'newsletter') ?></a></li>
15
+ <li><a href="https://www.thenewsletterplugin.com/blog"><?php _e('Blog', 'newsletter') ?></a></li>
16
+ </ul>
17
+ </div>
18
+ <div>
19
+ <form target="_blank" action="https://www.thenewsletterplugin.com/?na=s" method="post" style="margin: 0">
20
+ <input type="email" name="ne" placeholder="Your email" required size="20" value="<?php echo esc_attr($current_user_email) ?>">
21
+ <input type="hidden" value="plugin-footer" name="nr">
22
+ <input type="hidden" value="3" name="nl[]">
23
+ <input type="hidden" value="1" name="nl[]">
24
+ <input type="hidden" value="double" name="optin">
25
+ <input type="submit" value="<?php _e('Get news and promotions', 'newsletter') ?>">
26
+ <span style="color: #bbb; margin-bottom: 0px; display: block; line-height: normal">
27
+ Proceeding you agree to the
28
+ <a href="https://www.thenewsletterplugin.com/privacy" target="_blank" style="color: #2ECC71">privacy policy</a>
29
+ </span>
30
+
31
+ </form>
32
+ </div>
33
  </div>
tnp-header.php CHANGED
@@ -1,345 +1,345 @@
1
- <?php
2
- global $current_user, $wpdb;
3
-
4
- defined('ABSPATH') || exit;
5
-
6
- $dismissed = get_option('newsletter_dismissed', []);
7
-
8
- $user_count = Newsletter::instance()->get_user_count();
9
-
10
- $is_administrator = current_user_can('administrator');
11
-
12
- function newsletter_print_entries($group) {
13
- $entries = apply_filters('newsletter_menu_' . $group, array());
14
- if (!$entries) {
15
- return;
16
- }
17
-
18
- foreach ($entries as &$entry) {
19
- echo '<li><a href="', $entry['url'], '">', $entry['label'];
20
- if (!empty($entry['description'])) {
21
- echo '<small>', $entry['description'], '</small>';
22
- }
23
- echo '</a></li>';
24
- }
25
- }
26
-
27
- // Check the status to show a warning if needed
28
- $status_options = Newsletter::instance()->get_options('status');
29
- $warning = false;
30
-
31
- //$warning |= empty($status_options['mail']);
32
-
33
- $current_user_email = ''; //$current_user->user_email;
34
- //if (strpos($current_user_email, 'admin@') === 0) {
35
- // $current_user_email = '';
36
- //}
37
- ?>
38
-
39
- <div class="tnp-drowpdown" id="tnp-header">
40
- <a href="?page=newsletter_main_index"><img src="<?php echo plugins_url('newsletter'); ?>/admin/images/logo-red.png" class="tnp-header-logo" style="vertical-align: bottom;"></a>
41
- <ul>
42
- <li><a href="#"><i class="fas fa-users"></i> <?php _e('Subscribers', 'newsletter') ?> <i class="fas fa-chevron-down"></i></a>
43
- <ul>
44
- <li>
45
- <a href="?page=newsletter_users_index"><i class="fas fa-search"></i> <?php _e('Search And Edit', 'newsletter') ?>
46
- <small><?php _e('Add, edit, search', 'newsletter') ?></small></a>
47
- </li>
48
-
49
- <li>
50
- <a href="?page=newsletter_profile_index"><i class="fas fa-user-circle"></i> <?php _e('Profile page', 'newsletter') ?>
51
- <small><?php _e('The subscriber personal profile editing panel', 'newsletter') ?></small></a>
52
- </li>
53
-
54
- <?php if (!class_exists('NewsletterImport')) { ?>
55
- <li>
56
- <a href="?page=newsletter_users_import"><i class="fas fa-upload"></i> <?php _e('Import', 'newsletter') ?>
57
- <small><?php _e('Import from external sources', 'newsletter') ?></small></a>
58
- </li>
59
- <?php } ?>
60
-
61
- <li>
62
- <a href="?page=newsletter_users_export"><i class="fas fa-download"></i> <?php _e('Export', 'newsletter') ?>
63
- <small><?php _e('Export your subscribers list', 'newsletter') ?></small></a>
64
- </li>
65
-
66
- <li>
67
- <a href="?page=newsletter_users_massive"><i class="fas fa-wrench"></i> <?php _e('Maintenance', 'newsletter') ?>
68
- <small><?php _e('Massive actions: change list, clean up, ...', 'newsletter') ?></small></a>
69
- </li>
70
-
71
- <li>
72
- <a href="?page=newsletter_users_statistics"><i class="fas fa-chart-bar"></i> <?php _e('Statistics', 'newsletter') ?>
73
- <small><?php _e('All about your subscribers', 'newsletter') ?></small></a>
74
- </li>
75
-
76
- <?php newsletter_print_entries('subscribers') ?>
77
- </ul>
78
- </li>
79
- <li><a href="#"><i class="fas fa-list"></i> <?php _e('List Building', 'newsletter') ?> <i class="fas fa-chevron-down"></i></a>
80
- <ul>
81
- <li>
82
- <a href="?page=newsletter_subscription_profile"><i class="fas fa-check-square"></i> <?php _e('Subscription Form Fields, Buttons, Labels', 'newsletter') ?>
83
- <small><?php _e('When and what data to collect', 'newsletter') ?></small></a>
84
- </li>
85
-
86
- <li>
87
- <a href="?page=newsletter_subscription_options"><i class="fas fa-sign-in-alt"></i> <?php _e('Subscription', 'newsletter') ?>
88
- <small><?php _e('The subscription process in detail', 'newsletter') ?></small></a>
89
- </li>
90
-
91
- <li>
92
- <a href="?page=newsletter_subscription_lists"><i class="fas fa-th-list"></i> <?php _e('Lists', 'newsletter') ?>
93
- <small><?php _e('Profile the subscribers for a better targeting', 'newsletter') ?></small></a>
94
- </li>
95
-
96
- <li>
97
- <a href="?page=newsletter_subscription_antibot"><i class="fas fa-lock"></i> <?php _e('Security', 'newsletter') ?>
98
- <small><?php _e('Spam subscriptions control', 'newsletter') ?></small></a>
99
- </li>
100
-
101
- <li>
102
- <a href="?page=newsletter_unsubscription_index"><i class="fas fa-sign-out-alt"></i> <?php _e('Unsubscription', 'newsletter') ?>
103
- <small><?php _e('How to give the last goodbye (or avoid it!)', 'newsletter') ?></small></a>
104
- </li>
105
-
106
- <?php
107
- newsletter_print_entries('subscription');
108
- ?>
109
- </ul>
110
- </li>
111
-
112
- <li>
113
- <a href="#"><i class="fas fa-newspaper"></i> <?php _e('Newsletters', 'newsletter') ?> <i class="fas fa-chevron-down"></i></a>
114
- <ul>
115
- <li>
116
- <a href="?page=newsletter_emails_composer"><i class="fas fa-plus"></i> <?php _e('Create newsletter', 'newsletter') ?>
117
- <small><?php _e('Start your new campaign', 'newsletter') ?></small></a>
118
- </li>
119
-
120
- <li>
121
- <a href="?page=newsletter_emails_index"><i class="fas fa-newspaper"></i> <?php _e('Newsletters', 'newsletter') ?>
122
- <small><?php _e('The classic "write & send" newsletters', 'newsletter') ?></small></a>
123
- </li>
124
-
125
- <li>
126
- <a href="<?php echo NewsletterStatistics::instance()->get_index_url() ?>"><i class="fas fa-chart-bar"></i> <?php _e('Statistics', 'newsletter') ?>
127
- <small><?php _e('Tracking configuration and basic data', 'newsletter') ?></small></a>
128
- </li>
129
- <?php
130
- newsletter_print_entries('newsletters');
131
- ?>
132
- </ul>
133
- </li>
134
-
135
- <li>
136
- <a href="#"><i class="fas fa-cog"></i> <?php _e('Settings', 'newsletter') ?> <i class="fas fa-chevron-down"></i></a>
137
- <ul>
138
- <?php if ($is_administrator) { ?>
139
- <li>
140
- <a href="?page=newsletter_main_main"><i class="fas fa-cogs"></i> <?php _e('General Settings', 'newsletter') ?>
141
- <small><?php _e('Delivery speed, sender details, ...', 'newsletter') ?></small></a>
142
- </li>
143
- <?php if (!class_exists('NewsletterSmtp')) { ?>
144
- <li>
145
- <a href="?page=newsletter_main_smtp"><i class="fas fa-server"></i> <?php _e('SMTP', 'newsletter') ?>
146
- <small><?php _e('External mail server', 'newsletter') ?></small>
147
- </a>
148
- </li>
149
- <?php } ?>
150
- <?php } ?>
151
-
152
- <li>
153
- <a href="?page=newsletter_main_info"><i class="fas fa-info"></i> <?php _e('Company Info', 'newsletter') ?>
154
- <small><?php _e('Social, address, logo and general info', 'newsletter') ?></small></a>
155
- </li>
156
-
157
- <li>
158
- <a href="?page=newsletter_subscription_template"><i class="fas fa-file-alt"></i> <?php _e('Messages Template', 'newsletter') ?>
159
- <small><?php _e('Change the look of your service emails', 'newsletter') ?></small></a>
160
- </li>
161
-
162
- <?php
163
- newsletter_print_entries('settings');
164
- ?>
165
- </ul>
166
- </li>
167
-
168
- <?php if ($is_administrator) { ?>
169
- <li>
170
- <a href="#"><i class="fas fa-thermometer"></i> <?php _e('System', 'newsletter') ?>
171
- <?php if ($warning) { ?>
172
- <i class="fas fa-exclamation-triangle" style="color: red;"></i>
173
- <?php } ?>
174
- </a>
175
- <ul>
176
- <li>
177
- <a href="<?php echo admin_url('site-health.php') ?> "><i class="fas fa-file"></i> <?php _e('Site health') ?>
178
- <small><?php _e('WP native site health checks', 'newsletter') ?></small></a>
179
- </li>
180
- <li>
181
- <a href="?page=newsletter_system_delivery"><i class="fas fa-file"></i> <?php _e('Delivery Diagnostic', 'newsletter') ?>
182
- <small><?php _e('Delivery analysis and test', 'newsletter') ?></small></a>
183
- </li>
184
- <li>
185
- <a href="?page=newsletter_system_scheduler"><i class="fas fa-robot"></i> <?php _e('Scheduler', 'newsletter') ?>
186
- <small><?php _e('WordPress background jobs', 'newsletter') ?></small></a>
187
- </li>
188
- <li>
189
- <a href="?page=newsletter_system_status"><i class="fas fa-file"></i> <?php _e('Status', 'newsletter') ?>
190
- <small><?php _e('Checks and parameters', 'newsletter') ?></small></a>
191
- </li>
192
-
193
- <li>
194
- <a href="?page=newsletter_system_logs"><i class="fas fa-file"></i> <?php _e('Logs', 'newsletter') ?>
195
- <small><?php _e('Plugin and addons logs', 'newsletter') ?></small></a>
196
- </li>
197
- </ul>
198
- </li>
199
- <?php } ?>
200
-
201
- <?php
202
- $license_data = Newsletter::instance()->get_license_data();
203
- $premium_url = 'https://www.thenewsletterplugin.com/premium?utm_source=header&utm_medium=link&utm_campaign=plugin&utm_content=' . urlencode($_GET['page']);
204
- ?>
205
-
206
- <?php if (empty($license_data)) { ?>
207
- <?php if (time() < 1638226799) { ?>
208
- <li class="tnp-professional-extensions-button" style="background-color: #000; color: #fff; border-color: #FF5F65!important; border-width: 2px !important;"><a href="https://www.thenewsletterplugin.com/premium?utm_source=header&utm_medium=link&utm_campaign=black-friday" target="_blank">
209
- <i class="fas fa-trophy"></i> BLACK FRIDAY IS LIVE</a>
210
- </li>
211
- <?php } else { ?>
212
- <li class="tnp-professional-extensions-button"><a href="<?php echo $premium_url ?>" target="_blank">
213
- <i class="fas fa-trophy"></i> <?php _e('Get Professional Addons', 'newsletter') ?></a>
214
- </li>
215
- <?php } ?>
216
- <?php } elseif (is_wp_error($license_data)) { ?>
217
- <li class="tnp-professional-extensions-button-red">
218
- <a href="?page=newsletter_main_main"><i class="fas fa-hand-paper" style="color: white"></i> <?php _e('Unable to check', 'newsletter') ?></a>
219
- </li>
220
-
221
- <?php } elseif ($license_data->expire == 0) { ?>
222
- <?php if (time() < 1638226799) { ?>
223
- <li class="tnp-professional-extensions-button" style="background-color: #000; color: #fff; border-color: #FF5F65!important; border-width: 2px !important;"><a href="https://www.thenewsletterplugin.com/premium?utm_source=header&utm_medium=link&utm_campaign=black-friday" target="_blank">
224
- <i class="fas fa-trophy"></i> BLACK FRIDAY IS LIVE</a>
225
- </li>
226
- <?php } else { ?>
227
- <li class="tnp-professional-extensions-button"><a href="<?php echo $premium_url ?>" target="_blank">
228
- <i class="fas fa-trophy"></i> <?php _e('Get Professional Addons', 'newsletter') ?></a>
229
- </li>
230
- <?php } ?>
231
-
232
- <?php } elseif ($license_data->expire < time()) { ?>
233
-
234
- <li class="tnp-professional-extensions-button-red">
235
- <a href="?page=newsletter_main_main"><i class="fas fa-hand-paper" style="color: white"></i> <?php _e('License expired', 'newsletter') ?></a>
236
- </li>
237
-
238
- <?php } elseif ($license_data->expire >= time()) { ?>
239
-
240
- <?php $p = class_exists('NewsletterExtensions') ? 'newsletter_extensions_index' : 'newsletter_main_extensions'; ?>
241
- <li class="tnp-professional-extensions-button">
242
- <a href="?page=<?php echo $p ?>"><i class="fas fa-check-square"></i> <?php _e('License active', 'newsletter') ?></a>
243
- </li>
244
-
245
- <?php } ?>
246
- </ul>
247
- </div>
248
-
249
- <?php if (isset($_GET['debug']) || !isset($dismissed['newsletter-shortcode'])) { ?>
250
- <?php
251
- // Check of Newsletter dedicated page
252
- if (!empty(Newsletter::instance()->options['page'])) {
253
- if (get_post_status(Newsletter::instance()->options['page']) === 'publish') {
254
- $content = get_post_field('post_content', Newsletter::instance()->options['page']);
255
- // With and without attributes
256
- if (strpos($content, '[newsletter]') === false && strpos($content, '[newsletter ') === false) {
257
- ?>
258
- <div class="tnp-notice">
259
- <a href="<?php echo esc_attr($_SERVER['REQUEST_URI']) . '&noheader=1&dismiss=newsletter-shortcode' ?>" class="tnp-dismiss">&times;</a>
260
- The Newsletter dedicated page does not contain the [newsletter] shortcode. If you're using a visual composer it could be ok.
261
- <a href="<?php echo site_url('/wp-admin/post.php') ?>?post=<?php echo esc_attr(Newsletter::instance()->options['page']) ?>&action=edit"><strong>Edit the page</strong></a>.
262
-
263
- </div>
264
- <?php
265
- }
266
- }
267
- }
268
- ?>
269
- <?php } ?>
270
-
271
- <?php if (isset($_GET['debug']) || !isset($dismissed['rate']) && $user_count > 300) { ?>
272
- <div class="tnp-notice">
273
- <a href="<?php echo esc_attr($_SERVER['REQUEST_URI']) . '&noheader=1&dismiss=rate' ?>" class="tnp-dismiss">&times;</a>
274
-
275
- We never asked before and we're curious: <a href="http://wordpress.org/plugins/newsletter/" target="_blank">would you rate this plugin</a>?
276
- (few seconds required - account on WordPress.org required, every blog owner should have one...). <strong>Really appreciated, The Newsletter Team</strong>.
277
-
278
- </div>
279
- <?php } ?>
280
-
281
- <?php if (isset($_GET['debug']) || !isset($dismissed['newsletter-page']) && empty(Newsletter::instance()->options['page'])) { ?>
282
- <div class="tnp-notice">
283
- <a href="<?php echo esc_attr($_SERVER['REQUEST_URI']) . '&noheader=1&dismiss=newsletter-page' ?>" class="tnp-dismiss">&times;</a>
284
-
285
- You should create a blog page to show the subscription form and the subscription messages. Go to the
286
- <a href="?page=newsletter_main_main">general settings panel</a> to configure it.
287
-
288
- </div>
289
- <?php } ?>
290
-
291
- <?php if (isset($_GET['debug']) || !isset($dismissed['newsletter-subscribe']) && get_option('newsletter_install_time') && get_option('newsletter_install_time') < time() - 86400 * 15) { ?>
292
- <div class="tnp-notice">
293
- <a href="<?php echo esc_attr($_SERVER['REQUEST_URI']) . '&noheader=1&dismiss=newsletter-subscribe' ?>" class="tnp-dismiss">&times;</a>
294
- Subscribe to our news, promotions and getting started lessons!
295
- Proceeding you agree to the <a href="https://www.thenewsletterplugin.com/privacy" target="_blank">privacy policy</a>.
296
- <br>
297
- <form action="https://www.thenewsletterplugin.com/?na=s" target="_blank" method="post">
298
- <input type="hidden" value="plugin-header" name="nr">
299
- <input type="hidden" value="3" name="nl[]">
300
- <input type="hidden" value="1" name="nl[]">
301
- <input type="hidden" value="double" name="optin">
302
- <input type="email" name="ne" value="<?php echo esc_attr($current_user_email) ?>">
303
- <input type="submit" value="<?php esc_attr_e('Subscribe', 'newsletter') ?>">
304
- </form>
305
- </div>
306
- <?php } ?>
307
-
308
- <?php
309
- if (!defined('NEWSLETTER_CRON_WARNINGS') || NEWSLETTER_CRON_WARNINGS) {
310
- $x = NewsletterSystem::instance()->get_job_status();
311
- if ($x !== NewsletterSystem::JOB_OK) {
312
- echo '<div class="tnpc-warning">The are issues with the delivery engine. Please <a href="?page=newsletter_system_scheduler">check them here</a>.</div>';
313
- }
314
- }
315
- ?>
316
-
317
- <?php
318
- if ($_GET['page'] !== 'newsletter_emails_edit') {
319
-
320
- $last_failed_newsletters = Newsletter::instance()->get_emails_by_status(TNP_Email::STATUS_ERROR);
321
- if ($last_failed_newsletters) {
322
- $c = new NewsletterControls();
323
- foreach ($last_failed_newsletters as $n) {
324
- echo '<div class="tnpc-error">';
325
- printf(__('Newsletter "%s" stopped by fatal error.', 'newsletter'), esc_html($n->subject));
326
- echo '&nbsp;';
327
-
328
- $c->btn_link('?page=newsletter_emails_edit&id=' . $n->id, __('Check', 'newsletter'));
329
- echo '</div>';
330
- }
331
- }
332
- }
333
- ?>
334
-
335
- <div id="tnp-notification">
336
- <?php
337
- if (isset($controls)) {
338
- $controls->show();
339
- $controls->messages = '';
340
- $controls->errors = '';
341
- }
342
- ?>
343
- </div>
344
-
345
-
1
+ <?php
2
+ global $current_user, $wpdb;
3
+
4
+ defined('ABSPATH') || exit;
5
+
6
+ $dismissed = get_option('newsletter_dismissed', []);
7
+
8
+ $user_count = Newsletter::instance()->get_user_count();
9
+
10
+ $is_administrator = current_user_can('administrator');
11
+
12
+ function newsletter_print_entries($group) {
13
+ $entries = apply_filters('newsletter_menu_' . $group, array());
14
+ if (!$entries) {
15
+ return;
16
+ }
17
+
18
+ foreach ($entries as &$entry) {
19
+ echo '<li><a href="', $entry['url'], '">', $entry['label'];
20
+ if (!empty($entry['description'])) {
21
+ echo '<small>', $entry['description'], '</small>';
22
+ }
23
+ echo '</a></li>';
24
+ }
25
+ }
26
+
27
+ // Check the status to show a warning if needed
28
+ $status_options = Newsletter::instance()->get_options('status');
29
+ $warning = false;
30
+
31
+ //$warning |= empty($status_options['mail']);
32
+
33
+ $current_user_email = ''; //$current_user->user_email;
34
+ //if (strpos($current_user_email, 'admin@') === 0) {
35
+ // $current_user_email = '';
36
+ //}
37
+ ?>
38
+
39
+ <div class="tnp-drowpdown" id="tnp-header">
40
+ <a href="?page=newsletter_main_index"><img src="<?php echo plugins_url('newsletter'); ?>/admin/images/logo-red.png" class="tnp-header-logo" style="vertical-align: bottom;"></a>
41
+ <ul>
42
+ <li><a href="#"><i class="fas fa-users"></i> <?php _e('Subscribers', 'newsletter') ?> <i class="fas fa-chevron-down"></i></a>
43
+ <ul>
44
+ <li>
45
+ <a href="?page=newsletter_users_index"><i class="fas fa-search"></i> <?php _e('Search And Edit', 'newsletter') ?>
46
+ <small><?php _e('Add, edit, search', 'newsletter') ?></small></a>
47
+ </li>
48
+
49
+ <li>
50
+ <a href="?page=newsletter_profile_index"><i class="fas fa-user-circle"></i> <?php _e('Profile page', 'newsletter') ?>
51
+ <small><?php _e('The subscriber personal profile editing panel', 'newsletter') ?></small></a>
52
+ </li>
53
+
54
+ <?php if (!class_exists('NewsletterImport')) { ?>
55
+ <li>
56
+ <a href="?page=newsletter_users_import"><i class="fas fa-upload"></i> <?php _e('Import', 'newsletter') ?>
57
+ <small><?php _e('Import from external sources', 'newsletter') ?></small></a>
58
+ </li>
59
+ <?php } ?>
60
+
61
+ <li>
62
+ <a href="?page=newsletter_users_export"><i class="fas fa-download"></i> <?php _e('Export', 'newsletter') ?>
63
+ <small><?php _e('Export your subscribers list', 'newsletter') ?></small></a>
64
+ </li>
65
+
66
+ <li>
67
+ <a href="?page=newsletter_users_massive"><i class="fas fa-wrench"></i> <?php _e('Maintenance', 'newsletter') ?>
68
+ <small><?php _e('Massive actions: change list, clean up, ...', 'newsletter') ?></small></a>
69
+ </li>
70
+
71
+ <li>
72
+ <a href="?page=newsletter_users_statistics"><i class="fas fa-chart-bar"></i> <?php _e('Statistics', 'newsletter') ?>
73
+ <small><?php _e('All about your subscribers', 'newsletter') ?></small></a>
74
+ </li>
75
+
76
+ <?php newsletter_print_entries('subscribers') ?>
77
+ </ul>
78
+ </li>
79
+ <li><a href="#"><i class="fas fa-list"></i> <?php _e('List Building', 'newsletter') ?> <i class="fas fa-chevron-down"></i></a>
80
+ <ul>
81
+ <li>
82
+ <a href="?page=newsletter_subscription_profile"><i class="fas fa-check-square"></i> <?php _e('Subscription Form Fields, Buttons, Labels', 'newsletter') ?>
83
+ <small><?php _e('When and what data to collect', 'newsletter') ?></small></a>
84
+ </li>
85
+
86
+ <li>
87
+ <a href="?page=newsletter_subscription_options"><i class="fas fa-sign-in-alt"></i> <?php _e('Subscription', 'newsletter') ?>
88
+ <small><?php _e('The subscription process in detail', 'newsletter') ?></small></a>
89
+ </li>
90
+
91
+ <li>
92
+ <a href="?page=newsletter_subscription_lists"><i class="fas fa-th-list"></i> <?php _e('Lists', 'newsletter') ?>
93
+ <small><?php _e('Profile the subscribers for a better targeting', 'newsletter') ?></small></a>
94
+ </li>
95
+
96
+ <li>
97
+ <a href="?page=newsletter_subscription_antibot"><i class="fas fa-lock"></i> <?php _e('Security', 'newsletter') ?>
98
+ <small><?php _e('Spam subscriptions control', 'newsletter') ?></small></a>
99
+ </li>
100
+
101
+ <li>
102
+ <a href="?page=newsletter_unsubscription_index"><i class="fas fa-sign-out-alt"></i> <?php _e('Unsubscription', 'newsletter') ?>
103
+ <small><?php _e('How to give the last goodbye (or avoid it!)', 'newsletter') ?></small></a>
104
+ </li>
105
+
106
+ <?php
107
+ newsletter_print_entries('subscription');
108
+ ?>
109
+ </ul>
110
+ </li>
111
+
112
+ <li>
113
+ <a href="#"><i class="fas fa-newspaper"></i> <?php _e('Newsletters', 'newsletter') ?> <i class="fas fa-chevron-down"></i></a>
114
+ <ul>
115
+ <li>
116
+ <a href="?page=newsletter_emails_composer"><i class="fas fa-plus"></i> <?php _e('Create newsletter', 'newsletter') ?>
117
+ <small><?php _e('Start your new campaign', 'newsletter') ?></small></a>
118
+ </li>
119
+
120
+ <li>
121
+ <a href="?page=newsletter_emails_index"><i class="fas fa-newspaper"></i> <?php _e('Newsletters', 'newsletter') ?>
122
+ <small><?php _e('The classic "write & send" newsletters', 'newsletter') ?></small></a>
123
+ </li>
124
+
125
+ <li>
126
+ <a href="<?php echo NewsletterStatistics::instance()->get_index_url() ?>"><i class="fas fa-chart-bar"></i> <?php _e('Statistics', 'newsletter') ?>
127
+ <small><?php _e('Tracking configuration and basic data', 'newsletter') ?></small></a>
128
+ </li>
129
+ <?php
130
+ newsletter_print_entries('newsletters');
131
+ ?>
132
+ </ul>
133
+ </li>
134
+
135
+ <li>
136
+ <a href="#"><i class="fas fa-cog"></i> <?php _e('Settings', 'newsletter') ?> <i class="fas fa-chevron-down"></i></a>
137
+ <ul>
138
+ <?php if ($is_administrator) { ?>
139
+ <li>
140
+ <a href="?page=newsletter_main_main"><i class="fas fa-cogs"></i> <?php _e('General Settings', 'newsletter') ?>
141
+ <small><?php _e('Delivery speed, sender details, ...', 'newsletter') ?></small></a>
142
+ </li>
143
+ <?php if (!class_exists('NewsletterSmtp')) { ?>
144
+ <li>
145
+ <a href="?page=newsletter_main_smtp"><i class="fas fa-server"></i> <?php _e('SMTP', 'newsletter') ?>
146
+ <small><?php _e('External mail server', 'newsletter') ?></small>
147
+ </a>
148
+ </li>
149
+ <?php } ?>
150
+ <?php } ?>
151
+
152
+ <li>
153
+ <a href="?page=newsletter_main_info"><i class="fas fa-info"></i> <?php _e('Company Info', 'newsletter') ?>
154
+ <small><?php _e('Social, address, logo and general info', 'newsletter') ?></small></a>
155
+ </li>
156
+
157
+ <li>
158
+ <a href="?page=newsletter_subscription_template"><i class="fas fa-file-alt"></i> <?php _e('Messages Template', 'newsletter') ?>
159
+ <small><?php _e('Change the look of your service emails', 'newsletter') ?></small></a>
160
+ </li>
161
+
162
+ <?php
163
+ newsletter_print_entries('settings');
164
+ ?>
165
+ </ul>
166
+ </li>
167
+
168
+ <?php if ($is_administrator) { ?>
169
+ <li>
170
+ <a href="#"><i class="fas fa-thermometer"></i> <?php _e('System', 'newsletter') ?>
171
+ <?php if ($warning) { ?>
172
+ <i class="fas fa-exclamation-triangle" style="color: red;"></i>
173
+ <?php } ?>
174
+ </a>
175
+ <ul>
176
+ <li>
177
+ <a href="<?php echo admin_url('site-health.php') ?> "><i class="fas fa-file"></i> <?php _e('Site health') ?>
178
+ <small><?php _e('WP native site health checks', 'newsletter') ?></small></a>
179
+ </li>
180
+ <li>
181
+ <a href="?page=newsletter_system_delivery"><i class="fas fa-file"></i> <?php _e('Delivery Diagnostic', 'newsletter') ?>
182
+ <small><?php _e('Delivery analysis and test', 'newsletter') ?></small></a>
183
+ </li>
184
+ <li>
185
+ <a href="?page=newsletter_system_scheduler"><i class="fas fa-robot"></i> <?php _e('Scheduler', 'newsletter') ?>
186
+ <small><?php _e('WordPress background jobs', 'newsletter') ?></small></a>
187
+ </li>
188
+ <li>
189
+ <a href="?page=newsletter_system_status"><i class="fas fa-file"></i> <?php _e('Status', 'newsletter') ?>
190
+ <small><?php _e('Checks and parameters', 'newsletter') ?></small></a>
191
+ </li>
192
+
193
+ <li>
194
+ <a href="?page=newsletter_system_logs"><i class="fas fa-file"></i> <?php _e('Logs', 'newsletter') ?>
195
+ <small><?php _e('Plugin and addons logs', 'newsletter') ?></small></a>
196
+ </li>
197
+ </ul>
198
+ </li>
199
+ <?php } ?>
200
+
201
+ <?php
202
+ $license_data = Newsletter::instance()->get_license_data();
203
+ $premium_url = 'https://www.thenewsletterplugin.com/premium?utm_source=header&utm_medium=link&utm_campaign=plugin&utm_content=' . urlencode($_GET['page']);
204
+ ?>
205
+
206
+ <?php if (empty($license_data)) { ?>
207
+ <?php if (time() < 1638226799) { ?>
208
+ <li class="tnp-professional-extensions-button" style="background-color: #000; color: #fff; border-color: #FF5F65!important; border-width: 2px !important;"><a href="https://www.thenewsletterplugin.com/premium?utm_source=header&utm_medium=link&utm_campaign=black-friday" target="_blank">
209
+ <i class="fas fa-trophy"></i> BLACK FRIDAY IS LIVE</a>
210
+ </li>
211
+ <?php } else { ?>
212
+ <li class="tnp-professional-extensions-button"><a href="<?php echo $premium_url ?>" target="_blank">
213
+ <i class="fas fa-trophy"></i> <?php _e('Get Professional Addons', 'newsletter') ?></a>
214
+ </li>
215
+ <?php } ?>
216
+ <?php } elseif (is_wp_error($license_data)) { ?>
217
+ <li class="tnp-professional-extensions-button-red">
218
+ <a href="?page=newsletter_main_main"><i class="fas fa-hand-paper" style="color: white"></i> <?php _e('Unable to check', 'newsletter') ?></a>
219
+ </li>
220
+
221
+ <?php } elseif ($license_data->expire == 0) { ?>
222
+ <?php if (time() < 1638226799) { ?>
223
+ <li class="tnp-professional-extensions-button" style="background-color: #000; color: #fff; border-color: #FF5F65!important; border-width: 2px !important;"><a href="https://www.thenewsletterplugin.com/premium?utm_source=header&utm_medium=link&utm_campaign=black-friday" target="_blank">
224
+ <i class="fas fa-trophy"></i> BLACK FRIDAY IS LIVE</a>
225
+ </li>
226
+ <?php } else { ?>
227
+ <li class="tnp-professional-extensions-button"><a href="<?php echo $premium_url ?>" target="_blank">
228
+ <i class="fas fa-trophy"></i> <?php _e('Get Professional Addons', 'newsletter') ?></a>
229
+ </li>
230
+ <?php } ?>
231
+
232
+ <?php } elseif ($license_data->expire < time()) { ?>
233
+
234
+ <li class="tnp-professional-extensions-button-red">
235
+ <a href="?page=newsletter_main_main"><i class="fas fa-hand-paper" style="color: white"></i> <?php _e('License expired', 'newsletter') ?></a>
236
+ </li>
237
+
238
+ <?php } elseif ($license_data->expire >= time()) { ?>
239
+
240
+ <?php $p = class_exists('NewsletterExtensions') ? 'newsletter_extensions_index' : 'newsletter_main_extensions'; ?>
241
+ <li class="tnp-professional-extensions-button">
242
+ <a href="?page=<?php echo $p ?>"><i class="fas fa-check-square"></i> <?php _e('License active', 'newsletter') ?></a>
243
+ </li>
244
+
245
+ <?php } ?>
246
+ </ul>
247
+ </div>
248
+
249
+ <?php if (isset($_GET['debug']) || !isset($dismissed['newsletter-shortcode'])) { ?>
250
+ <?php
251
+ // Check of Newsletter dedicated page
252
+ if (!empty(Newsletter::instance()->options['page'])) {
253
+ if (get_post_status(Newsletter::instance()->options['page']) === 'publish') {
254
+ $content = get_post_field('post_content', Newsletter::instance()->options['page']);
255
+ // With and without attributes
256
+ if (strpos($content, '[newsletter]') === false && strpos($content, '[newsletter ') === false) {
257
+ ?>
258
+ <div class="tnp-notice">
259
+ <a href="<?php echo esc_attr($_SERVER['REQUEST_URI']) . '&noheader=1&dismiss=newsletter-shortcode' ?>" class="tnp-dismiss">&times;</a>
260
+ The Newsletter dedicated page does not contain the [newsletter] shortcode. If you're using a visual composer it could be ok.
261
+ <a href="<?php echo site_url('/wp-admin/post.php') ?>?post=<?php echo esc_attr(Newsletter::instance()->options['page']) ?>&action=edit"><strong>Edit the page</strong></a>.
262
+
263
+ </div>
264
+ <?php
265
+ }
266
+ }
267
+ }
268
+ ?>
269
+ <?php } ?>
270
+
271
+ <?php if (isset($_GET['debug']) || !isset($dismissed['rate']) && $user_count > 300) { ?>
272
+ <div class="tnp-notice">
273
+ <a href="<?php echo esc_attr($_SERVER['REQUEST_URI']) . '&noheader=1&dismiss=rate' ?>" class="tnp-dismiss">&times;</a>
274
+
275
+ We never asked before and we're curious: <a href="http://wordpress.org/plugins/newsletter/" target="_blank">would you rate this plugin</a>?
276
+ (few seconds required - account on WordPress.org required, every blog owner should have one...). <strong>Really appreciated, The Newsletter Team</strong>.
277
+
278
+ </div>
279
+ <?php } ?>
280
+
281
+ <?php if (isset($_GET['debug']) || !isset($dismissed['newsletter-page']) && empty(Newsletter::instance()->options['page'])) { ?>
282
+ <div class="tnp-notice">
283
+ <a href="<?php echo esc_attr($_SERVER['REQUEST_URI']) . '&noheader=1&dismiss=newsletter-page' ?>" class="tnp-dismiss">&times;</a>
284
+
285
+ You should create a blog page to show the subscription form and the subscription messages. Go to the
286
+ <a href="?page=newsletter_main_main">general settings panel</a> to configure it.
287
+
288
+ </div>
289
+ <?php } ?>
290
+
291
+ <?php if (isset($_GET['debug']) || !isset($dismissed['newsletter-subscribe']) && get_option('newsletter_install_time') && get_option('newsletter_install_time') < time() - 86400 * 15) { ?>
292
+ <div class="tnp-notice">
293
+ <a href="<?php echo esc_attr($_SERVER['REQUEST_URI']) . '&noheader=1&dismiss=newsletter-subscribe' ?>" class="tnp-dismiss">&times;</a>
294
+ Subscribe to our news, promotions and getting started lessons!
295
+ Proceeding you agree to the <a href="https://www.thenewsletterplugin.com/privacy" target="_blank">privacy policy</a>.
296
+ <br>
297
+ <form action="https://www.thenewsletterplugin.com/?na=s" target="_blank" method="post">
298
+ <input type="hidden" value="plugin-header" name="nr">
299
+ <input type="hidden" value="3" name="nl[]">
300
+ <input type="hidden" value="1" name="nl[]">
301
+ <input type="hidden" value="double" name="optin">
302
+ <input type="email" name="ne" value="<?php echo esc_attr($current_user_email) ?>">
303
+ <input type="submit" value="<?php esc_attr_e('Subscribe', 'newsletter') ?>">
304
+ </form>
305
+ </div>
306
+ <?php } ?>
307
+
308
+ <?php
309
+ if (!defined('NEWSLETTER_CRON_WARNINGS') || NEWSLETTER_CRON_WARNINGS) {
310
+ $x = NewsletterSystem::instance()->get_job_status();
311
+ if ($x !== NewsletterSystem::JOB_OK) {
312
+ echo '<div class="tnpc-warning">The are issues with the delivery engine. Please <a href="?page=newsletter_system_scheduler">check them here</a>.</div>';
313
+ }
314
+ }
315
+ ?>
316
+
317
+ <?php
318
+ if ($_GET['page'] !== 'newsletter_emails_edit') {
319
+
320
+ $last_failed_newsletters = Newsletter::instance()->get_emails_by_status(TNP_Email::STATUS_ERROR);
321
+ if ($last_failed_newsletters) {
322
+ $c = new NewsletterControls();
323
+ foreach ($last_failed_newsletters as $n) {
324
+ echo '<div class="tnpc-error">';
325
+ printf(__('Newsletter "%s" stopped by fatal error.', 'newsletter'), esc_html($n->subject));
326
+ echo '&nbsp;';
327
+
328
+ $c->btn_link('?page=newsletter_emails_edit&id=' . $n->id, __('Check', 'newsletter'));
329
+ echo '</div>';
330
+ }
331
+ }
332
+ }
333
+ ?>
334
+
335
+ <div id="tnp-notification">
336
+ <?php
337
+ if (isset($controls)) {
338
+ $controls->show();
339
+ $controls->messages = '';
340
+ $controls->errors = '';
341
+ }
342
+ ?>
343
+ </div>
344
+
345
+
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();