Newsletter - Version 7.3.1

Version Description

  • Dropped old mailers support
  • Improved sending process and limits checking
  • Removed obsolete notifications
  • Fixed the wrong report on single email delivery speed
  • Added support for custom sending speed by addons
  • Improved excerpt generation (but it still depends on plugins and themes...)
  • Support for building button option on composer blocks
Download this release

Release Info

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

Code changes from version 7.3.0 to 7.3.1

admin/admin.css CHANGED
@@ -1,1524 +1,1436 @@
1
- /* WordPress admin main wrapper */
2
- #wpwrap {
3
- background-color: #222B36 !important;
4
- }
5
-
6
- #tnp-wrap * {
7
- -webkit-box-sizing: border-box;
8
- -moz-box-sizing: border-box;
9
- box-sizing: border-box;
10
- }
11
-
12
- #tnp-wrap *:before,
13
- #tnp-wrap *:after {
14
- -webkit-box-sizing: border-box;
15
- -moz-box-sizing: border-box;
16
- box-sizing: border-box;
17
- }
18
-
19
- #tnp-wrap,
20
- #tnp-wrap td,
21
- #tnp-wrap h1,
22
- #tnp-wrap h2,
23
- #tnp-wrap h3,
24
- #tnp-wrap h4,
25
- #tnp-wrap input,
26
- #tnp-wrap select,
27
- #tnp-wrap textarea,
28
- #tnp-wrap button,
29
- #tnp-wrap li,
30
- #tnp-wrap a
31
- {
32
- font-family: soleil, sans-serif;
33
- -webkit-font-smoothing: antialiased; /* Chrome, Safari */
34
- -moz-osx-font-smoothing: grayscale; /* Firefox */
35
- }
36
-
37
- .container {
38
- width: 100%;
39
- padding-right: 1rem;
40
- padding-left: 1rem;
41
- margin-right: auto;
42
- margin-left: auto;
43
- }
44
-
45
- @media (min-width: 576px) {
46
- .container {
47
- max-width: 540px;
48
- }
49
- }
50
-
51
- @media (min-width: 768px) {
52
- .container {
53
- max-width: 720px;
54
- }
55
- }
56
-
57
- @media (min-width: 992px) {
58
- .container {
59
- max-width: 960px;
60
- }
61
- }
62
-
63
- @media (min-width: 1200px) {
64
- .container {
65
- max-width: 1140px;
66
- }
67
- }
68
-
69
- .row:before,
70
- .row:after {
71
- display: table;
72
- content: " ";
73
-
74
- }
75
- .row:after {
76
- clear: both;
77
- }
78
- .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 {
79
- position: relative;
80
- min-height: 1px;
81
- padding-right: 15px;
82
- padding-left: 15px;
83
- }
84
- .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 {
85
- float: left;
86
- }
87
- .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 {
88
- float: left;
89
- }
90
- .col-md-12 {width: 100%;}
91
- .col-md-11 {width: 91.66666667%;}
92
- .col-md-10 {width: 83.33333333%;}
93
- .col-md-9 {width: 75%;}
94
- .col-md-8 {width: 66.66666667%;}
95
- .col-md-7 {width: 58.33333333%;}
96
- .col-md-6 {width: 50%;}
97
- .col-md-5 {width: 41.66666667%;}
98
- .col-md-4 {width: 33.33333333%;}
99
- .col-md-3 {width: 25%;}
100
- .col-md-2 {width: 16.66666667%;}
101
- .col-md-1 {width: 8.33333333%;}
102
-
103
- @media all and (max-width: 1100px) {
104
- .col-md-12 {width: 100%;}
105
- .col-md-11 {width: 100%;}
106
- .col-md-10 {width: 100%;}
107
- .col-md-9 {width: 100%;}
108
- .col-md-8 {width: 100%;}
109
- .col-md-7 {width: 100%;}
110
- .col-md-6 {width: 100%;}
111
- .col-md-5 {width: 100%;}
112
- .col-md-4 {width: 100%;}
113
- .col-md-3 {width: 100%;}
114
- .col-md-2 {width: 100%;}
115
- .col-md-1 {width: 100%;}
116
- }
117
-
118
- .tnp-row-padded {
119
- width: 90%;
120
- margin: 0 auto;
121
- display: flex;
122
- justify-content: space-between;
123
- }
124
-
125
- .tnp-col-3-boxed {
126
- width: 30%;
127
- border: 1px solid #3c414c;
128
- padding: 0px 0px 30px 0px;
129
- border-radius: 10px;
130
- }
131
-
132
- .tnp-margin-top {
133
- margin-top: 80px;
134
- }
135
-
136
- #tnp-promotion-bar {
137
- background-color: #FF5F65;
138
- color: ddd;
139
- padding: 10px 0;
140
- text-align: center;
141
- font-size: 16px;
142
- }
143
-
144
- #tnp-promotion-bar a {
145
- color: #fff;
146
- font-weight: normal;
147
- text-decoration: none;
148
- }
149
-
150
- /*******************************************************************************
151
- * Header
152
- */
153
-
154
- #tnp-header {
155
- text-align: left;
156
- font-size: 12px;
157
- color: #fff;
158
- font-family: soleil, sans-serif;
159
- min-width: 1200px;
160
- }
161
-
162
- #tnp-header input {
163
- font-size: 12px;
164
- }
165
-
166
- #tnp-header a {
167
- text-decoration: none;
168
- color: white;
169
- letter-spacing: 0.1em;
170
- }
171
-
172
- #tnp-header a:hover {
173
- color: #fff;
174
- }
175
-
176
- .error a, .error a:hover {
177
- color: #000!important;
178
- }
179
-
180
- .updated a, .updated a:hover {
181
- color: #000!important;
182
- }
183
-
184
- /******************************************************************************
185
- * BODY
186
- ******************************************************************************/
187
-
188
- #tnp-body {
189
- padding: 10px;
190
- /*background-color: #28313C;*/
191
- }
192
-
193
- #tnp-body h1,
194
- #tnp-body h2,
195
- #tnp-body h3,
196
- #tnp-body h4,
197
- #tnp-body p {
198
- color: #fff;
199
- font-weight: normal;
200
- }
201
-
202
- #tnp-body h3 {
203
- margin-top: 25px;
204
- clear: both;
205
- margin-bottom: 10px;
206
- }
207
-
208
-
209
- #tnp-body ul {
210
- list-style-type: circle;
211
- list-style-position: inside;
212
- }
213
-
214
- #tnp-body a, #tnp-body a:active {
215
- color: #2980B9; /* Blue */
216
- }
217
-
218
- #tnp-body a:hover {
219
- color: #3498DB;
220
- }
221
-
222
- /* Action button container */
223
- #tnp-body .tnp-submit {
224
- margin-bottom: 10px;
225
- }
226
-
227
- /* Primary button correction */
228
- #tnp-body .button,
229
- #tnp-body .button:visited,
230
- #tnp-body .button:hover,
231
- #tnp-body .button:active,
232
- #tnp-body .button:focus,
233
- #tnp-body .button-primary,
234
- #tnp-body .button-primary:visited,
235
- #tnp-body .button-primary:hover {
236
- color: #fff;
237
- text-shadow: none;
238
- width: auto;
239
- vertical-align: bottom;
240
- }
241
-
242
- #tnp-body .button-secondary,
243
- #tnp-body .button-secondary:visited,
244
- #tnp-body .button-secondary:hover {
245
- color: #fff;
246
- background-color: #999;
247
- text-shadow: none;
248
- width: auto;
249
- vertical-align: bottom;
250
- }
251
-
252
- /* Icon in button media selector */
253
- #tnp-body span.wp-media-buttons-icon:before {
254
- color: #fff;
255
- }
256
-
257
- /* Standard button */
258
- #tnp-body .tnp-button {
259
- color: #fff;
260
- background-color: #3498db;
261
- text-shadow: none;
262
- }
263
-
264
- /* White button variant */
265
- #tnp-body .button-primary.tnp-button-white, #tnp-body .tnp-button.tnp-button-white {
266
- color: #444!important;
267
- background-color: #fff!important;
268
- box-shadow: none !important;
269
- -webkit-box-shadow: none !important;
270
- width: auto;
271
- }
272
-
273
- #tnp-body tbody th,
274
- #tnp-body td,
275
- #tnp-body td p,
276
- #tnp-body td .button,
277
- #tnp-body td .button:visited,
278
- #tnp-body td .button:hover,
279
- #tnp-body td .button:active,
280
- #tnp-body td .button:focus {
281
- color: #444;
282
- }
283
-
284
- #tnp-body td a,
285
- #tnp-body td a:visited {
286
- color: #27AE60; /* Green */
287
- }
288
-
289
- /*******************************************************************************
290
- * Wide fat tables
291
- */
292
-
293
- #tnp-body .widefat {
294
- width: 90%;
295
- color: #444;
296
- }
297
-
298
- #tnp-body .widefat th {
299
- text-align: left;
300
- }
301
-
302
- #tnp-body .widefat thead {
303
- background-color: #3498DB;
304
- font-family: soleil, sans-serif;
305
- color: #fff !important;
306
- }
307
-
308
- #tnp-body .widefat thead tr th {
309
- color: #fff !important;
310
- }
311
-
312
- #tnp-body .widefat td, .widefat th {
313
- vertical-align: middle;
314
- }
315
-
316
- #tnp-body .widefat tr:nth-child(even) {
317
- background-color: #f4faff;
318
- }
319
-
320
-
321
-
322
- table.clicks td {
323
- border: 1px solid #666;
324
- padding: 2px;
325
- font-size: 10px;
326
- }
327
-
328
- table.clicks {
329
- border-collapse: collapse;
330
- }
331
-
332
- .grid {
333
- border-collapse: collapse;
334
- }
335
- .grid td, .grid th {
336
- padding: 10px;
337
- border: 1px solid #ddd;
338
- margin: 0;
339
- }
340
- .grid th {
341
- background-color: #aaa;
342
- }
343
-
344
- .tnp-buttons {
345
- /*background-color: #0073aa;*/
346
- padding: 10px 0;
347
- }
348
-
349
-
350
-
351
-
352
-
353
-
354
- .tnp-notice {
355
- padding: 15px;
356
- margin: 10px 0;
357
- padding-right: 70px;
358
- position: relative;
359
- border: 1px solid #eee;
360
- background-color: #fff;
361
- color: #444;
362
- font-size: 13px;
363
- border-left: 5px solid #27AE60;
364
- }
365
-
366
- .tnp-notice a,
367
- .tnp-warning a {
368
- color: #0073aa;
369
- text-decoration: none;
370
- font-weight: bold;
371
- }
372
-
373
- .tnp-notice a.tnp-dismiss,
374
- .tnp-warning a.tnp-dismiss{
375
- display: block;
376
- position: absolute;
377
- right: 10px;
378
- top: 13px;
379
- font-size: 25px;
380
- text-decoration: none;
381
- color: #666;
382
- }
383
-
384
- .tnp-notice input[type=email] {
385
- margin: 10px 5px 5px;
386
- width: 250px;
387
- border: none;
388
- box-shadow: none;
389
- background-color: #ECF0F1;
390
- padding: 8px;
391
- }
392
-
393
- .tnp-notice input[type=submit] {
394
- border: none;
395
- box-shadow: none;
396
- background-color: #27AE60;
397
- padding: 8px;
398
- font-family: soleil, sans-serif;
399
- font-size: 13px;
400
- color: #fff;
401
- cursor: pointer;
402
- }
403
-
404
- .tnp-paginator {
405
- margin-top: 10px;
406
- margin-bottom: 5px;
407
- }
408
-
409
- .newsletter-box {
410
- border: 1px solid #ddd;
411
- padding: 10px;
412
- background-color: #fafafa;
413
- margin-bottom: 15px;
414
- }
415
-
416
- .newsletter-box h3 {
417
- margin-top: 0;
418
- }
419
-
420
- .newsletter-textarea-preview {
421
- border: 1px solid #ddd;
422
- }
423
-
424
- .tnp-tab-notice {
425
- background-color: #fff;
426
- border: 1px solid #eee;
427
- border-left: 3px solid gray;
428
- padding: 10px;
429
- margin: 10px 0;
430
- color: #444;
431
- }
432
-
433
- .tnp-tab-warning {
434
- background-color: #fff;
435
- border: 1px solid #eee;
436
- border-left: 3px solid orange;
437
- padding: 10px;
438
- margin: 10px 0;
439
- color: #444;
440
- }
441
-
442
- .tnp-tab-success {
443
- background-color: #fff;
444
- border: 1px solid #eee;
445
- border-left: 3px solid green;
446
- padding: 10px;
447
- margin: 10px 0;
448
- color: #444;
449
- }
450
-
451
- .tnp-tab-error {
452
- background-color: #fff;
453
- border: 1px solid #eee;
454
- border-left: 3px solid red;
455
- padding: 10px;
456
- margin: 10px 0;
457
- color: #444;
458
- }
459
-
460
- /* .tnp-wrap a[target=_blank]:after {
461
- content: "»";
462
- }*/
463
-
464
-
465
- /* CSS The Newsletter Team */
466
-
467
- /* CSS Tips */
468
-
469
- .tnp-tip {
470
- margin-top: 5px;
471
- }
472
-
473
- .tip-button {
474
- padding: 0px 5px;
475
- color: #FD5F65;
476
- text-transform: uppercase;
477
- letter-spacing: 0.2em;
478
- /*font-family: soleil;*/
479
- font-size: 10px;
480
- border: 1px red solid;
481
- }
482
-
483
- .tip-content {
484
- /*font-family: soleil;*/
485
- font-weight: 500;
486
- font-size: 11px;
487
- color: #999999;
488
- }
489
-
490
- /* CSS General Font Styles */
491
-
492
-
493
-
494
-
495
- /* CSS Themes Preview */
496
-
497
- .tnp-theme-preview {
498
- display: inline-block;
499
- text-align: center;
500
- }
501
-
502
- .tnp-theme-preview p {
503
- font-family: soleil;
504
- font-size: 13px;
505
- letter-spacing: 0.2em;
506
- color: #fff;
507
- }
508
-
509
- .tnp-theme-preview img:hover {
510
- box-shadow: 3px 3px 8px 2px #293848;
511
- }
512
-
513
- .tnp-theme-preview img {
514
- border-radius: 10px;
515
- height: 190px;
516
- width: auto;
517
- }
518
-
519
- .tnp-theme-preview .tnp-theme-composer {
520
- height: 250px;
521
- width: auto;
522
- }
523
-
524
- .tnp-theme-preview .tnp-theme-html {
525
- width: auto;
526
- }
527
-
528
-
529
- .tnp-header-logo {
530
- margin-left: 10px;
531
- }
532
-
533
-
534
- /* Altrimenti si crea una striscia bianca in mezzo alla pagina! */
535
-
536
-
537
- .wp-core-ui .button-primary {
538
- background-color: #2b2f3a;
539
- color: #fff;
540
- width: auto;
541
- }
542
-
543
-
544
-
545
-
546
-
547
- .tnp-body-lite {
548
- background-color: #F1F1F1 !important;
549
- }
550
-
551
-
552
- /* Header & Sub-header Pannelli */
553
-
554
- #tnp-heading {
555
- padding: 10px;
556
- /*background-color: #28313C;*/
557
- margin-bottom: 10px;
558
- border-radius: 5px;
559
- }
560
-
561
- #tnp-heading a {
562
- color: #fff;
563
- border-bottom: 1px solid #fff;
564
- text-decoration: none;
565
- }
566
-
567
- #tnp-heading a:hover {
568
- color: #27AE60;
569
- border-bottom: 1px solid #27AE60;
570
- }
571
-
572
- #tnp-heading div p {
573
- color: #565656;
574
- }
575
-
576
- #tnp-heading h1 {
577
- color: #fff;
578
- font-family: soleil, sans-serif;
579
- font-weight: 900;
580
- }
581
-
582
- #tnp-heading h2 {
583
- color: #fff;
584
- font-family: soleil, sans-serif;
585
- letter-spacing: 0.1rem;
586
- font-size: 1.1rem;
587
- line-height: 1.8rem;
588
- text-transform: uppercase;
589
- vertical-align: middle;
590
- font-weight: 700;
591
- padding: 0;
592
- margin: 0px;
593
- margin-bottom: 15px;
594
- }
595
-
596
- #tnp-heading h3 {
597
- color: #27AE60;
598
- font-family: soleil, sans-serif;
599
- letter-spacing: 0.1rem;
600
- font-size: .8rem;
601
- line-height: 1.8rem;
602
- text-transform: uppercase;
603
- vertical-align: middle;
604
- font-weight: 700;
605
- padding: 0;
606
- margin: 0px;
607
- }
608
-
609
- #tnp-heading p, #tnp-heading ul {
610
- margin: 0px;
611
- color: #ccc;
612
- }
613
-
614
- #tnp-heading ul {
615
- list-style-type: circle;
616
- list-style-position: inside;
617
- margin-left: 2em;
618
- margin-top: 1em;
619
- }
620
-
621
- /* Style for WP global notices */
622
- #tnp-heading .notice p {
623
- margin: 0.5em 0;
624
- padding: 2px;
625
- }
626
-
627
- #tnp-heading .tnp-btn-h1 {
628
- color: #fff;
629
- background-color: #3498db;
630
- border-radius: 3px;
631
- padding: 6px 11px;
632
- text-decoration: none;
633
- text-transform: capitalize;
634
- font-family: soleil, sans-serif;
635
- margin-left: 10px;
636
- font-size: 0.75rem;
637
- font-weight: 300;
638
- border: none;
639
- }
640
-
641
- #tnp-heading .tnp-btn-h1:hover {
642
- color: #fff;
643
- background-color: #5DADE2;
644
- -webkit-transition: background-color .25s linear;
645
- transition: background-color .25s linear;
646
- -webkit-font-smoothing: subpixel-antialiased;
647
- border: none;
648
- color: #fff;
649
- }
650
-
651
- /* Dashboard Box */
652
-
653
- .metabox-holder {
654
- width: 100%;
655
- }
656
-
657
- .postbox {
658
- border: none;
659
- }
660
-
661
- .postbox h3 a {
662
- float: right;
663
-
664
- }
665
-
666
-
667
- #dashboard-widgets .postbox-container {
668
- width: 33.333%
669
- }
670
-
671
- #tnp-body .postbox p {
672
- color: #000;
673
- }
674
-
675
- #dashboard-widgets .postbox-container .postbox h3 {
676
- font-family: soleil, sans-serif;
677
- letter-spacing: 0.05rem;
678
- background-color: #415b76;
679
- color: #fff;
680
- margin: 0;
681
- padding: 9px;
682
- }
683
-
684
- #dashboard-widgets .postbox-container h3 a {
685
- color: white;
686
- text-decoration: none;
687
- margin-left: 5px;
688
- padding: 2px 8px;
689
- background-color: #26C281;
690
- border-radius: 2px;
691
- font-weight: 300;
692
- text-transform: capitalize;
693
- font-size: 0.8rem;
694
- }
695
-
696
- #dashboard-widgets .postbox-container h3 a:hover {
697
- color: white;
698
- text-decoration: none;
699
- margin-left: 5px;
700
- background-color: #2ECC71;
701
- }
702
-
703
- .postbox-container i {
704
- margin-right: 3px;
705
- }
706
- #tnp-dash-newsletters tr td:last-of-type {
707
- width: 80px;
708
- text-align: right;
709
- }
710
-
711
- #tnp-dash-subscribers tr td:last-of-type {
712
- width: 80px;
713
- text-align: right;
714
- }
715
-
716
- #tnp-dash-subscribers tr td:first-of-type {
717
- width: 250px;
718
- overflow: hidden;
719
- }
720
-
721
- #tnp-dash-subscribers table {
722
- table-layout: fixed;
723
- }
724
-
725
- #tnp-dash-documentation .inside div {
726
- margin-top: 10px;
727
- }
728
-
729
- #tnp-dash-documentation .inside a {
730
- text-decoration: none;
731
- color: #fff;
732
- display: block;
733
- font-family: soleil, sans-serif;
734
- padding: 5px 10px;
735
- }
736
-
737
-
738
- /* Footer */
739
-
740
- #tnp-footer {
741
- margin-top: 10px;
742
- padding: 20px 10px 10px 40px;
743
- background-color: #28313C;
744
- font-family: soleil, sans-serif;
745
- }
746
-
747
- #tnp-footer div {
748
- width: 33%;
749
- display: inline-block;
750
- }
751
-
752
- #tnp-footer a {
753
- color: #fff;
754
- text-decoration: none;
755
- }
756
-
757
- #tnp-footer a:hover {
758
- color: #BDC3C7;
759
- }
760
-
761
- #tnp-footer input[type="submit"] {
762
- background-color: #2ECC71;
763
- border: none;
764
- padding: 5px;
765
- color: #fff;
766
- }
767
-
768
- #tnp-footer form {
769
- white-space: nowrap;
770
- }
771
-
772
- #tnp-footer li {
773
- display: inline;
774
- margin-left: 15px;
775
- padding: 2px 5px;
776
- border-left: 3px solid #2ECC71;
777
- }
778
-
779
- /* Global buttons styles */
780
-
781
- #dashboard-widgets .button {
782
- border: none;
783
- background: none;
784
- box-shadow: none;
785
- color: #322C39;
786
- }
787
-
788
- #dashboard-widgets .button:hover {
789
- background-color: #ECF0F1;
790
- }
791
-
792
- .wp-core-ui .button-secondary, .wp-core-ui .button-primary {
793
- background-color: #3498db;
794
- border: none;
795
- box-shadow: none;
796
- color: #fff;
797
- font-family: soleil,sans-serif;
798
- margin: 0px 2px;
799
- width: auto;
800
- }
801
-
802
- .wp-core-ui .button-secondary:hover, .wp-core-ui .button-primary:hover {
803
- background-color: #5DADE2;
804
- color: #fff;
805
- width: auto;
806
- }
807
-
808
- span.wp-media-buttons-icon:before {
809
- color: #fff;
810
- }
811
-
812
- .tnp-paginator [value="Go"] {
813
- background-color: #27AE60;
814
- }
815
-
816
- .tnp-paginator [value="Go"]:hover {
817
- background-color: #2ECC71;
818
- }
819
-
820
- .notice-dismiss {
821
- padding: 3px;
822
- }
823
-
824
- /*.widefat .button-secondary {
825
- background: none;
826
- color: #3498db;
827
- }*/
828
-
829
- /* Paginator */
830
-
831
- .tnp-paginator {
832
- color: #fff;
833
- font-family: soleil,sans-serif;
834
- margin: 10px 0px;
835
- }
836
-
837
- .tnp-paginator .button-secondary {
838
- padding: 5px;
839
- line-height: normal;
840
- height: auto;
841
- font-size: 12px;
842
- height: 25px;
843
- border: none;
844
- border-radius: 3px;
845
- vertical-align: baseline;
846
- }
847
-
848
- .tnp-paginator [value="Go"] {
849
- background-color: #27AE60 !important;
850
- }
851
-
852
- .tnp-paginator [value="Go"]:hover {
853
- background-color: #2ECC71 !important;
854
- }
855
-
856
- .tnp-paginator input {
857
- background-color: #2C3E50;
858
- border: none;
859
- border-radius: 3px;
860
- color: #fff;
861
- padding: 5px;
862
- line-height: normal;
863
- font-size: 12px;
864
- height: 25px;
865
- }
866
-
867
- /* Subscribers Search Box */
868
-
869
- .tnp-subscribers-search {
870
- color: #fff;
871
- font-family: soleil, sans-serif;
872
- background-color: #2C3E50;
873
- padding: 20px;
874
- border-radius: 5px;
875
- margin-bottom: 20px;
876
- display: inline-block;
877
- }
878
-
879
- .tnp-subscribers-search select {
880
- margin-left: 5px;
881
- padding: 0;
882
- line-height: inherit;
883
- }
884
-
885
-
886
- /* Responsive Video Embeds */
887
-
888
- .tnp-video-container {
889
- position: relative;
890
- padding-bottom: 56.25%;
891
- padding-top: 30px; height: 0; overflow: hidden;
892
- }
893
-
894
- .tnp-video-container iframe,
895
- .tnp-video-container object,
896
- .tnp-video-container embed {
897
- position: absolute;
898
- top: 0;
899
- left: 0;
900
- width: 100%;
901
- height: 100%;
902
- }
903
-
904
-
905
- /* Colors Palette */
906
-
907
- .bg-white {
908
- background-color: #FFF;
909
- }
910
-
911
- .orange {
912
- background-color: #F39C12; /*Orange #F39C12 */
913
- }
914
-
915
- .blue {
916
- background-color: #2980B9; /* Blue #2980B9 */
917
- }
918
-
919
- .purple {
920
- background-color: #8E44AD; /* Purple #8E44AD */
921
- }
922
-
923
- .notice a {
924
- color: #27AE60 !important;
925
- text-decoration: underline!important;
926
- }
927
-
928
- .tnp-chart {
929
- border: 1px solid #eee;
930
- width: 100%;
931
- }
932
-
933
- .tnp-db-table {
934
- width: auto;
935
- background-color: #fff;
936
- }
937
-
938
- .tnp-db-table thead {
939
- border-bottom: 1px solid #eee;
940
- }
941
-
942
- .tnp-db-table th {
943
- font-weight: bold;
944
- }
945
-
946
- .tnp-db-table td, .tnp-db-table th {
947
- padding: 3px;
948
- font-family: monospace;
949
- border: 0;
950
- }
951
-
952
- /* STATUS PANEL */
953
-
954
- .tnp-main-status h3, .tnp-main-status h4 {
955
- color: #fff;
956
- }
957
-
958
- .tnp-ok {
959
- font-weight: bold;
960
- color: white;
961
- font-size: 14px;
962
- background-color: #27AE60;
963
- padding: 2px 10px;
964
- border-radius: 10px;
965
- display: inline-block;
966
- width: 75px;
967
- text-align: center;
968
- }
969
-
970
- .tnp-ko {
971
- font-weight: bold;
972
- color: white;
973
- font-size: 14px;
974
- background-color: #E74C41;
975
- padding: 2px 10px;
976
- border-radius: 10px;
977
- display: inline-block;
978
- width: 75px;
979
- text-align: center;
980
- }
981
-
982
- .tnp-maybe {
983
- font-weight: bold;
984
- color: white;
985
- font-size: 14px;
986
- background-color: #F1C40F;
987
- padding: 2px 10px;
988
- border-radius: 10px;
989
- display: inline-block;
990
- width: 75px;
991
- text-align: center;
992
- }
993
-
994
- .tnp-main-status .tnp-log-files li {
995
- padding-left: 15px;
996
- }
997
-
998
- .tnp-main-status .tnp-log-files li, .tnp-main-status .tnp-log-files li a {
999
- color: #fff;
1000
- }
1001
-
1002
- .tnp-main-status .tnp-log-files .tnp-log-size {
1003
- font-style: italic;
1004
- }
1005
-
1006
- table.widefat {
1007
- border: 0;
1008
- box-shadow: none;
1009
- }
1010
-
1011
- #tnp-status-table tbody tr:nth-child(2n+1) {
1012
- background-color: #ECF0F1;
1013
- border-radius: 2px;
1014
- margin: 5px;
1015
- }
1016
-
1017
- #tnp-parameters-table tbody tr:nth-child(2n+1) {
1018
- background-color: #ECF0F1;
1019
- border-radius: 2px;
1020
- margin: 5px;
1021
- }
1022
-
1023
-
1024
- /* Suggerimenti Oggetto + Inserimento Emoticons */
1025
-
1026
- .tnp-emails-edit #options-subject {
1027
- font-size: 16px;
1028
- display: inline-block;
1029
- margin: 20px 0px;
1030
- width: auto;
1031
- border-radius: 4px;
1032
- padding: 5px 10px;
1033
- }
1034
-
1035
- .tnp-suggest-button {
1036
- font-family: soleil, sans-serif;
1037
- margin-left: 8px;
1038
- border-radius: 3px;
1039
- background-color: #2980B9;
1040
- padding: 10px 15px 8px;
1041
- font-size: 14px;
1042
- color: #fff !important;
1043
- text-decoration: none;
1044
- }
1045
-
1046
- .tnp-suggest-button:hover {
1047
- background-color: #3f8dbf;
1048
- }
1049
-
1050
- .tnp-popup-overlay {
1051
- display: none;
1052
- position: fixed;
1053
- top: 0;
1054
- left: 0;
1055
- width: 100%;
1056
- height: 100%;
1057
- background-color: rgba(0, 0, 0, .8);
1058
- z-index: 10000;
1059
- }
1060
-
1061
- .tnp-popup {
1062
- width: 40vw;
1063
- height: 66vh;
1064
- overflow: auto;
1065
- margin: 100px auto 0 auto;
1066
- background-color: #181818;
1067
- padding: 20px;
1068
- position: relative;
1069
- }
1070
- .tnp-popup-close {
1071
- display: block;
1072
- position: absolute;
1073
- top: 5px;
1074
- right: 5px;
1075
- background-color: #181818;
1076
- color: #fff;
1077
- font-size: 40px;
1078
- padding: 10px;
1079
- text-align: right;
1080
- cursor: pointer;
1081
- }
1082
-
1083
- .tnp-subjects-header {
1084
- font-size: 16px;
1085
- color: #fff;
1086
- padding: 0px 70px 20px 20px;
1087
- font-family: soleil, sans-serif;
1088
- border-bottom: 1px solid #282828;
1089
- }
1090
-
1091
- #tnp-edit-subjects-list {
1092
- padding: 0px 70px 20px 20px;
1093
- }
1094
-
1095
- #tnp-edit-subjects-list a {
1096
- padding: 5px;
1097
- }
1098
-
1099
- #tnp-edit-subjects-list svg {
1100
- margin: 0px 10px 0px 0px;
1101
- vertical-align: middle;
1102
- }
1103
-
1104
- .tnp-subject-category {
1105
- color: #565656;
1106
- margin: 25px 0px 10px 0px;
1107
- /* font-family: soleil; */
1108
- font-size: 12px;
1109
- text-transform: uppercase;
1110
- letter-spacing: 0.1em;
1111
- }
1112
-
1113
-
1114
- /* Stile selettore liste - Schermata di invio newsletter */
1115
-
1116
- .tnp-list-conditions p {
1117
- margin: 0px 10px;
1118
- }
1119
-
1120
- /* Lists panel */
1121
- .tnp-lists .tnp-notes {
1122
- margin: 0;
1123
- font-size: .9em;
1124
- }
1125
-
1126
- /* Codemirror editor with preview */
1127
- iframe.tnp-editor-preview-mobile {
1128
- box-sizing: border-box;
1129
- background-color: #fff;
1130
- border: 1px solid #bbb;
1131
- box-shadow: 1px 1px 10px #777;
1132
- border-radius: 10px;
1133
- padding: 5px;
1134
- width: 320px;
1135
- height: 500px;
1136
- float: left;
1137
- }
1138
-
1139
- iframe.tnp-editor-preview-desktop {
1140
- box-sizing: border-box;
1141
- background-color: #fff;
1142
- border: 1px solid #bbb;
1143
- border-radius: 10px;
1144
- box-shadow: 1px 1px 10px #777;
1145
- padding: 15px;
1146
- width: 650px;
1147
- margin-right: 20px;
1148
- height: 500px;
1149
- float: left;
1150
- }
1151
-
1152
-
1153
- /* Form inserimento licenza in Addons Manager */
1154
-
1155
- #tnp-license-control {
1156
- border-left: 5px solid #27ae60;
1157
- display: inline-block;
1158
- padding: 15px 20px;
1159
- margin-left: -10px;
1160
- margin-top: 15px;
1161
- border-radius: 2px;
1162
- background-color: #fff;
1163
- }
1164
-
1165
- #tnp-license-control form {
1166
- margin-bottom: 10px;
1167
- margin-top: 10px;
1168
- }
1169
-
1170
- #tnp-license-control form input {
1171
- padding-left: 10px;
1172
- }
1173
-
1174
- #tnp-license-control a {
1175
- border-bottom: none;
1176
- color: #27AE60;
1177
- }
1178
-
1179
-
1180
- /* Status Box Style */
1181
-
1182
- #tnp-nl-status {
1183
- width: 100%;
1184
- background: #fffafa;
1185
- padding: 15px 25px 15px 25px;
1186
- border-left: 10px solid #27AE60;
1187
- border-radius: 0px 5px 5px 0px;
1188
- }
1189
-
1190
- #tnp-nl-status p {
1191
- font-size: 17px;
1192
- }
1193
-
1194
- .tnp-nl-status-row {
1195
- margin: 10px 0;
1196
- }
1197
-
1198
- .tnp-nl-status-title {
1199
- font-size: 26px;
1200
- line-height: 32px;
1201
- margin: 5px 0px 0px 0px;
1202
- color: #3498DB;
1203
- font-weight: 900;
1204
- vertical-align: middle;
1205
- }
1206
-
1207
- .tnp-nl-status-title-value {
1208
- font-size: 13px;
1209
- line-height: 32px;
1210
- margin: 0px 0px 0px 5px;
1211
- color: #fff;
1212
- background-color: #95A5A6;
1213
- border-radius: 4px;
1214
- text-transform: uppercase;
1215
- letter-spacing: 1px;
1216
- vertical-align: sub;
1217
- }
1218
-
1219
- .tnp-nl-status-schedule-targeting {
1220
- font-size: 15px;
1221
- color: #34495E;
1222
- }
1223
-
1224
- .tnp-nl-status-schedule-value {
1225
- font-size: 15px;
1226
- color: #34495E;
1227
- }
1228
-
1229
- .tnp-status-header #options-subject {
1230
- width: calc(100% - 150px);
1231
- }
1232
-
1233
- /* Grid Helpers */
1234
-
1235
- .tnp-one-third {
1236
- width: 40%;
1237
- display: inline-block;
1238
- vertical-align: top;
1239
- }
1240
-
1241
- .tnp-two-thirds {
1242
- width: 59%;
1243
- display: inline-block;
1244
- vertical-align: top;
1245
- }
1246
-
1247
- /* Progress bar */
1248
- .tnp-progress {
1249
- display: flex;
1250
- height: 1.5rem;
1251
- overflow: hidden;
1252
- font-size: .75rem;
1253
- background-color: #c9cccf;
1254
- border-radius: .25rem;
1255
- margin: 0px 0px 0px;
1256
- min-width: 100px;
1257
- }
1258
-
1259
- .tnp-progress-bar {
1260
- display: -ms-flexbox;
1261
- display: flex;
1262
- -ms-flex-direction: column;
1263
- flex-direction: column;
1264
- -ms-flex-pack: center;
1265
- justify-content: center;
1266
- color: #fff;
1267
- text-align: center;
1268
- white-space: nowrap;
1269
- background-color: #007bff;
1270
- transition: width .6s ease;
1271
- }
1272
-
1273
- .tnp-progress--sent .tnp-progress-bar {
1274
- background-color: green;
1275
- }
1276
-
1277
- .tnp-progress--error .tnp-progress-bar {
1278
- background-color: #E74C3C;
1279
- }
1280
-
1281
- .tnp-progress-numbers {
1282
- text-align: center;
1283
- color: #666;
1284
- }
1285
-
1286
- .tnp-progress-date {
1287
- color: #666;
1288
- font-style: italic;
1289
- }
1290
-
1291
- span.tnp-email-status {
1292
- background-color: #95A5A6;
1293
- padding: 2px 10px;
1294
- border-radius: 4px;
1295
- color: #fff;
1296
- white-space: nowrap;
1297
- }
1298
-
1299
- /* Email status label */
1300
- span.tnp-email-status.tnp-email-status--new {
1301
- background-color: #8E44AD;
1302
- }
1303
-
1304
- span.tnp-email-status.tnp-email-status--paused {
1305
- background-color: #95A5A6;
1306
- }
1307
-
1308
- span.tnp-email-status.tnp-email-status--sending {
1309
- background-color: #27AE60;
1310
- }
1311
-
1312
- span.tnp-email-status.tnp-email-status--scheduled {
1313
- background-color: #E67E22;
1314
- }
1315
-
1316
- span.tnp-email-status.tnp-email-status--sent {
1317
- background-color: #95A5A6;
1318
- }
1319
-
1320
- span.tnp-email-status.tnp-email-status--error {
1321
- background-color: #E74C3C;
1322
- }
1323
-
1324
- /* Schedule buttons styles */
1325
-
1326
- #tnp-schedule-button {
1327
- background-color: #E67E22 !important;
1328
- }
1329
-
1330
- #tnp-schedule-button:hover {
1331
- background-color: #ec913f !important;
1332
- }
1333
-
1334
- .tnp-button-cancel {
1335
- background-color: #E74C3C !important;
1336
- }
1337
-
1338
- /* Newsletter preview on targeting panel */
1339
- .tnpc-preview {
1340
- margin-top: 10px;
1341
- }
1342
-
1343
- .tnpc-preview .fake-browser-ui iframe {
1344
- width: 700px;
1345
- }
1346
-
1347
- .tnpc-preview .fake-mobile-browser-ui iframe {
1348
- width: 320px;
1349
- }
1350
-
1351
- .fake-browser-ui {
1352
- padding: 30px 0 0;
1353
- border-radius: 3px;
1354
- border-bottom: 10px solid #ccc;
1355
- background: #ddd;
1356
- display: inline-block;
1357
- position: relative;
1358
- line-height: 0;
1359
- vertical-align: top;
1360
- margin-left: 20px;
1361
- }
1362
-
1363
- .fake-mobile-browser-ui {
1364
- padding: 30px 10px 37px;
1365
- border-radius: 10px;
1366
- border-bottom: 10px solid #ccc;
1367
- background: #ddd;
1368
- display: inline-block;
1369
- position: relative;
1370
- line-height: 0;
1371
- margin-left: 30px;
1372
- }
1373
-
1374
- .fake-browser-ui .frame {
1375
- display: block;
1376
- height: 25px;
1377
- position: absolute;
1378
- top: 12px;
1379
- left: 8px;
1380
- }
1381
-
1382
- .fake-mobile-browser-ui .frame {
1383
- display: block;
1384
- height: 25px;
1385
- margin-top: 10px;
1386
- }
1387
-
1388
- .fake-browser-ui span {
1389
- height: 12px;
1390
- width: 12px;
1391
- border-radius: 8px;
1392
- background-color: #eee;
1393
- border: 1px solid #dadada;
1394
- float: left;
1395
- margin: 0 0 0 4px;
1396
- }
1397
-
1398
- .fake-mobile-browser-ui span {
1399
- height: 50px;
1400
- width: 50px;
1401
- border-radius: 60px;
1402
- background-color: #eee;
1403
- border: 2px solid #ccc;
1404
- display: block;
1405
- margin: auto;
1406
- }
1407
-
1408
- .fake-browser-ui .bt-1 {
1409
- background-color: #ED594A;
1410
- }
1411
-
1412
- .fake-browser-ui .bt-2 {
1413
- background-color: #FDD800;
1414
- }
1415
-
1416
- .fake-browser-ui .bt-3 {
1417
- background-color: #5AC05A;
1418
- }
1419
-
1420
- /* Addons page */
1421
-
1422
- #tnp-promo {
1423
- text-align: left;
1424
- background-color: #222b36;
1425
- margin: 20px;
1426
- border-radius: 5px;
1427
- padding: 20px 40px;
1428
- }
1429
- #tnp-promo .tnp-promo-how-to {
1430
- width: 50%;
1431
- padding: 5px 20px;
1432
- margin-top: 30px;
1433
- margin-bottom: 30px;
1434
- border-left: 2px solid #F1C40F;
1435
- }
1436
-
1437
- #tnp-promo .tnp-promo-how-to h3 {
1438
- color: #ECF0F1;
1439
- margin: 0px;
1440
- line-height: 36px;
1441
- }
1442
-
1443
- #tnp-promo .tnp-promo-how-to p {
1444
- color: #ECF0F1;
1445
- margin: 0px;
1446
- font-size: 16px;
1447
- line-height: 26px;
1448
- }
1449
-
1450
- #tnp-promo .tnp-promo-buttons {
1451
- margin: 50px 0px;
1452
- }
1453
-
1454
- #tnp-promo .tnp-promo-button {
1455
- background: #27AE60;
1456
- text-decoration: none;
1457
- color: white;
1458
- padding: 15px 20px;
1459
- font-size: 15px;
1460
- border-radius: 2px;
1461
- }
1462
-
1463
- #tnp-promo .tnp-promo-button:hover {
1464
- background: #2ECC71;
1465
- color: white;
1466
- }
1467
-
1468
- #tnp-promo .tnp-promo-button i {
1469
- margin-right: 3px;
1470
- }
1471
-
1472
- #tnp-body td a.tnp-table-link,
1473
- #tnp-body td a.tnp-table-link:visited {
1474
- color: #444;
1475
- }
1476
-
1477
- #tnp-body td a.tnp-table-link:hover {
1478
- color: #3498DB;
1479
- }
1480
-
1481
- .text-left {
1482
- text-align: left;
1483
- }
1484
-
1485
- .tab-min-height {
1486
- min-height: 500px;
1487
- }
1488
-
1489
- .tnp-control-all-languages-notice {
1490
- padding: 15px;
1491
- border: 1px dashed #999;
1492
- }
1493
-
1494
- /* Spectru, color picker */
1495
-
1496
- /* Down arrow */
1497
- .sp-dd {
1498
- display: none;
1499
- }
1500
-
1501
- .sp-replacer {
1502
- width: 30px!important;
1503
- height: 30px!important;
1504
- }
1505
-
1506
- #tnp-body .unsubscribed {
1507
- color: gray;
1508
- }
1509
-
1510
- #tnp-body .confirmed {
1511
- color: darkgreen;
1512
- }
1513
-
1514
- #tnp-body .not-confirmed {
1515
- color: darkgray;
1516
- }
1517
-
1518
- #tnp-body .complained {
1519
- color: red;
1520
- }
1521
-
1522
- #tnp-body .bounced {
1523
- color: darkorange;
1524
- }
1
+ /* WordPress admin main wrapper */
2
+ #wpwrap {
3
+ background-color: #222B36 !important;
4
+ }
5
+
6
+ #tnp-wrap * {
7
+ -webkit-box-sizing: border-box;
8
+ -moz-box-sizing: border-box;
9
+ box-sizing: border-box;
10
+ }
11
+
12
+ #tnp-wrap *:before,
13
+ #tnp-wrap *:after {
14
+ -webkit-box-sizing: border-box;
15
+ -moz-box-sizing: border-box;
16
+ box-sizing: border-box;
17
+ }
18
+
19
+ #tnp-wrap,
20
+ #tnp-wrap td,
21
+ #tnp-wrap h1,
22
+ #tnp-wrap h2,
23
+ #tnp-wrap h3,
24
+ #tnp-wrap h4,
25
+ #tnp-wrap input,
26
+ #tnp-wrap select,
27
+ #tnp-wrap textarea,
28
+ #tnp-wrap button,
29
+ #tnp-wrap li,
30
+ #tnp-wrap a
31
+ {
32
+ font-family: soleil, sans-serif;
33
+ -webkit-font-smoothing: antialiased; /* Chrome, Safari */
34
+ -moz-osx-font-smoothing: grayscale; /* Firefox */
35
+ }
36
+
37
+ .container {
38
+ width: 100%;
39
+ padding-right: 1rem;
40
+ padding-left: 1rem;
41
+ margin-right: auto;
42
+ margin-left: auto;
43
+ }
44
+
45
+ @media (min-width: 576px) {
46
+ .container {
47
+ max-width: 540px;
48
+ }
49
+ }
50
+
51
+ @media (min-width: 768px) {
52
+ .container {
53
+ max-width: 720px;
54
+ }
55
+ }
56
+
57
+ @media (min-width: 992px) {
58
+ .container {
59
+ max-width: 960px;
60
+ }
61
+ }
62
+
63
+ @media (min-width: 1200px) {
64
+ .container {
65
+ max-width: 1140px;
66
+ }
67
+ }
68
+
69
+ .row:before,
70
+ .row:after {
71
+ display: table;
72
+ content: " ";
73
+
74
+ }
75
+ .row:after {
76
+ clear: both;
77
+ }
78
+ .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 {
79
+ position: relative;
80
+ min-height: 1px;
81
+ padding-right: 15px;
82
+ padding-left: 15px;
83
+ }
84
+ .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 {
85
+ float: left;
86
+ }
87
+ .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 {
88
+ float: left;
89
+ }
90
+ .col-md-12 {width: 100%;}
91
+ .col-md-11 {width: 91.66666667%;}
92
+ .col-md-10 {width: 83.33333333%;}
93
+ .col-md-9 {width: 75%;}
94
+ .col-md-8 {width: 66.66666667%;}
95
+ .col-md-7 {width: 58.33333333%;}
96
+ .col-md-6 {width: 50%;}
97
+ .col-md-5 {width: 41.66666667%;}
98
+ .col-md-4 {width: 33.33333333%;}
99
+ .col-md-3 {width: 25%;}
100
+ .col-md-2 {width: 16.66666667%;}
101
+ .col-md-1 {width: 8.33333333%;}
102
+
103
+ @media all and (max-width: 1100px) {
104
+ .col-md-12 {width: 100%;}
105
+ .col-md-11 {width: 100%;}
106
+ .col-md-10 {width: 100%;}
107
+ .col-md-9 {width: 100%;}
108
+ .col-md-8 {width: 100%;}
109
+ .col-md-7 {width: 100%;}
110
+ .col-md-6 {width: 100%;}
111
+ .col-md-5 {width: 100%;}
112
+ .col-md-4 {width: 100%;}
113
+ .col-md-3 {width: 100%;}
114
+ .col-md-2 {width: 100%;}
115
+ .col-md-1 {width: 100%;}
116
+ }
117
+
118
+ .tnp-row-padded {
119
+ width: 90%;
120
+ margin: 0 auto;
121
+ display: flex;
122
+ justify-content: space-between;
123
+ }
124
+
125
+ .tnp-col-3-boxed {
126
+ width: 30%;
127
+ border: 1px solid #3c414c;
128
+ padding: 0px 0px 30px 0px;
129
+ border-radius: 10px;
130
+ }
131
+
132
+ .tnp-margin-top {
133
+ margin-top: 80px;
134
+ }
135
+
136
+ #tnp-promotion-bar {
137
+ background-color: #FF5F65;
138
+ color: ddd;
139
+ padding: 10px 0;
140
+ text-align: center;
141
+ font-size: 16px;
142
+ }
143
+
144
+ #tnp-promotion-bar a {
145
+ color: #fff;
146
+ font-weight: normal;
147
+ text-decoration: none;
148
+ }
149
+
150
+ /*******************************************************************************
151
+ * Header
152
+ */
153
+
154
+ #tnp-header {
155
+ text-align: left;
156
+ font-size: 12px;
157
+ color: #fff;
158
+ font-family: soleil, sans-serif;
159
+ min-width: 1200px;
160
+ }
161
+
162
+ #tnp-header input {
163
+ font-size: 12px;
164
+ }
165
+
166
+ #tnp-header a {
167
+ text-decoration: none;
168
+ color: white;
169
+ letter-spacing: 0.1em;
170
+ }
171
+
172
+ #tnp-header a:hover {
173
+ color: #fff;
174
+ }
175
+
176
+ .error a, .error a:hover {
177
+ color: #000!important;
178
+ }
179
+
180
+ .updated a, .updated a:hover {
181
+ color: #000!important;
182
+ }
183
+
184
+ /******************************************************************************
185
+ * BODY
186
+ ******************************************************************************/
187
+
188
+ #tnp-body {
189
+ padding: 0 10px 10px 10px;
190
+ /*background-color: #28313C;*/
191
+ }
192
+
193
+ #tnp-body h1,
194
+ #tnp-body h2,
195
+ #tnp-body h3,
196
+ #tnp-body h4,
197
+ #tnp-body p {
198
+ color: #fff;
199
+ font-weight: normal;
200
+ }
201
+
202
+ #tnp-body h3 {
203
+ margin-top: 25px;
204
+ clear: both;
205
+ margin-bottom: 10px;
206
+ }
207
+
208
+
209
+ #tnp-body ul {
210
+ list-style-type: circle;
211
+ list-style-position: inside;
212
+ }
213
+
214
+ #tnp-body a, #tnp-body a:active {
215
+ color: #2980B9; /* Blue */
216
+ }
217
+
218
+ #tnp-body a:hover {
219
+ color: #3498DB;
220
+ }
221
+
222
+ /* Action button container */
223
+ #tnp-body .tnp-submit {
224
+ margin-bottom: 10px;
225
+ }
226
+
227
+ /* Primary button correction */
228
+ #tnp-body .button,
229
+ #tnp-body .button:visited,
230
+ #tnp-body .button:hover,
231
+ #tnp-body .button:active,
232
+ #tnp-body .button:focus,
233
+ #tnp-body .button-primary,
234
+ #tnp-body .button-primary:visited,
235
+ #tnp-body .button-primary:hover {
236
+ color: #fff;
237
+ text-shadow: none;
238
+ width: auto;
239
+ vertical-align: bottom;
240
+ }
241
+
242
+ #tnp-body .button-secondary,
243
+ #tnp-body .button-secondary:visited,
244
+ #tnp-body .button-secondary:hover {
245
+ color: #fff;
246
+ background-color: #999;
247
+ text-shadow: none;
248
+ width: auto;
249
+ vertical-align: bottom;
250
+ }
251
+
252
+ /* Icon in button media selector */
253
+ #tnp-body span.wp-media-buttons-icon:before {
254
+ color: #fff;
255
+ }
256
+
257
+ /* Standard button */
258
+ #tnp-body .tnp-button {
259
+ color: #fff;
260
+ background-color: #3498db;
261
+ text-shadow: none;
262
+ }
263
+
264
+ /* White button variant */
265
+ #tnp-body .button-primary.tnp-button-white, #tnp-body .tnp-button.tnp-button-white {
266
+ color: #444!important;
267
+ background-color: #fff!important;
268
+ box-shadow: none !important;
269
+ -webkit-box-shadow: none !important;
270
+ width: auto;
271
+ }
272
+
273
+ #tnp-body tbody th,
274
+ #tnp-body td,
275
+ #tnp-body td p,
276
+ #tnp-body td .button,
277
+ #tnp-body td .button:visited,
278
+ #tnp-body td .button:hover,
279
+ #tnp-body td .button:active,
280
+ #tnp-body td .button:focus {
281
+ color: #444;
282
+ }
283
+
284
+ #tnp-body td a,
285
+ #tnp-body td a:visited {
286
+ color: #27AE60; /* Green */
287
+ }
288
+
289
+ /*******************************************************************************
290
+ * Wide fat tables
291
+ */
292
+
293
+ #tnp-body .widefat {
294
+ width: 90%;
295
+ color: #444;
296
+ }
297
+
298
+ #tnp-body .widefat th {
299
+ text-align: left;
300
+ }
301
+
302
+ #tnp-body .widefat thead {
303
+ background-color: #3498DB;
304
+ font-family: soleil, sans-serif;
305
+ color: #fff !important;
306
+ }
307
+
308
+ #tnp-body .widefat thead tr th {
309
+ color: #fff !important;
310
+ }
311
+
312
+ #tnp-body .widefat td, .widefat th {
313
+ vertical-align: middle;
314
+ }
315
+
316
+ #tnp-body .widefat tr:nth-child(even) {
317
+ background-color: #f4faff;
318
+ }
319
+
320
+
321
+
322
+ table.clicks td {
323
+ border: 1px solid #666;
324
+ padding: 2px;
325
+ font-size: 10px;
326
+ }
327
+
328
+ table.clicks {
329
+ border-collapse: collapse;
330
+ }
331
+
332
+ .grid {
333
+ border-collapse: collapse;
334
+ }
335
+ .grid td, .grid th {
336
+ padding: 10px;
337
+ border: 1px solid #ddd;
338
+ margin: 0;
339
+ }
340
+ .grid th {
341
+ background-color: #aaa;
342
+ }
343
+
344
+ .tnp-buttons {
345
+ /*background-color: #0073aa;*/
346
+ padding: 10px 0;
347
+ }
348
+
349
+
350
+
351
+
352
+
353
+
354
+ .tnp-notice {
355
+ padding: 15px;
356
+ margin: 10px 0;
357
+ padding-right: 70px;
358
+ position: relative;
359
+ border: 1px solid #eee;
360
+ background-color: #fff;
361
+ color: #444;
362
+ font-size: 13px;
363
+ border-left: 5px solid #27AE60;
364
+ }
365
+
366
+ .tnp-notice a,
367
+ .tnp-warning a {
368
+ color: #0073aa;
369
+ text-decoration: none;
370
+ font-weight: bold;
371
+ }
372
+
373
+ .tnp-notice a.tnp-dismiss,
374
+ .tnp-warning a.tnp-dismiss{
375
+ display: block;
376
+ position: absolute;
377
+ right: 10px;
378
+ top: 13px;
379
+ font-size: 25px;
380
+ text-decoration: none;
381
+ color: #666;
382
+ }
383
+
384
+ .tnp-notice input[type=email] {
385
+ margin: 10px 5px 5px;
386
+ width: 250px;
387
+ border: none;
388
+ box-shadow: none;
389
+ background-color: #ECF0F1;
390
+ padding: 8px;
391
+ }
392
+
393
+ .tnp-notice input[type=submit] {
394
+ border: none;
395
+ box-shadow: none;
396
+ background-color: #27AE60;
397
+ padding: 8px;
398
+ font-family: soleil, sans-serif;
399
+ font-size: 13px;
400
+ color: #fff;
401
+ cursor: pointer;
402
+ }
403
+
404
+ .tnp-paginator {
405
+ margin-top: 10px;
406
+ margin-bottom: 5px;
407
+ }
408
+
409
+ .newsletter-box {
410
+ border: 1px solid #ddd;
411
+ padding: 10px;
412
+ background-color: #fafafa;
413
+ margin-bottom: 15px;
414
+ }
415
+
416
+ .newsletter-box h3 {
417
+ margin-top: 0;
418
+ }
419
+
420
+ .newsletter-textarea-preview {
421
+ border: 1px solid #ddd;
422
+ }
423
+
424
+ .tnp-tab-notice {
425
+ background-color: #fff;
426
+ border: 1px solid #eee;
427
+ border-left: 3px solid gray;
428
+ padding: 10px;
429
+ margin: 10px 0;
430
+ color: #444;
431
+ }
432
+
433
+ .tnp-tab-warning {
434
+ background-color: #fff;
435
+ border: 1px solid #eee;
436
+ border-left: 3px solid orange;
437
+ padding: 10px;
438
+ margin: 10px 0;
439
+ color: #444;
440
+ }
441
+
442
+ .tnp-tab-success {
443
+ background-color: #fff;
444
+ border: 1px solid #eee;
445
+ border-left: 3px solid green;
446
+ padding: 10px;
447
+ margin: 10px 0;
448
+ color: #444;
449
+ }
450
+
451
+ .tnp-tab-error {
452
+ background-color: #fff;
453
+ border: 1px solid #eee;
454
+ border-left: 3px solid red;
455
+ padding: 10px;
456
+ margin: 10px 0;
457
+ color: #444;
458
+ }
459
+
460
+ /* .tnp-wrap a[target=_blank]:after {
461
+ content: "»";
462
+ }*/
463
+
464
+
465
+ /* CSS The Newsletter Team */
466
+
467
+ /* CSS Tips */
468
+
469
+ .tnp-tip {
470
+ margin-top: 5px;
471
+ }
472
+
473
+ .tip-button {
474
+ padding: 0px 5px;
475
+ color: #FD5F65;
476
+ text-transform: uppercase;
477
+ letter-spacing: 0.2em;
478
+ /*font-family: soleil;*/
479
+ font-size: 10px;
480
+ border: 1px red solid;
481
+ }
482
+
483
+ .tip-content {
484
+ /*font-family: soleil;*/
485
+ font-weight: 500;
486
+ font-size: 11px;
487
+ color: #999999;
488
+ }
489
+
490
+ /* CSS General Font Styles */
491
+
492
+
493
+
494
+
495
+ /* CSS Themes Preview */
496
+
497
+ .tnp-theme-preview {
498
+ display: inline-block;
499
+ text-align: center;
500
+ }
501
+
502
+ .tnp-theme-preview p {
503
+ font-family: soleil;
504
+ font-size: 13px;
505
+ letter-spacing: 0.2em;
506
+ color: #fff;
507
+ }
508
+
509
+ .tnp-theme-preview img:hover {
510
+ box-shadow: 3px 3px 8px 2px #293848;
511
+ }
512
+
513
+ .tnp-theme-preview img {
514
+ border-radius: 10px;
515
+ height: 190px;
516
+ width: auto;
517
+ }
518
+
519
+ .tnp-theme-preview .tnp-theme-composer {
520
+ height: 250px;
521
+ width: auto;
522
+ }
523
+
524
+ .tnp-theme-preview .tnp-theme-html {
525
+ width: auto;
526
+ }
527
+
528
+
529
+ .tnp-header-logo {
530
+ margin-left: 10px;
531
+ }
532
+
533
+
534
+ /* Altrimenti si crea una striscia bianca in mezzo alla pagina! */
535
+
536
+
537
+ .wp-core-ui .button-primary {
538
+ background-color: #2b2f3a;
539
+ color: #fff;
540
+ width: auto;
541
+ }
542
+
543
+
544
+
545
+
546
+
547
+ .tnp-body-lite {
548
+ background-color: #F1F1F1 !important;
549
+ }
550
+
551
+
552
+ /* Header & Sub-header Pannelli */
553
+
554
+ #tnp-heading {
555
+ padding: 10px;
556
+ /*background-color: #28313C;*/
557
+ margin-bottom: 10px;
558
+ border-radius: 5px;
559
+ }
560
+
561
+ #tnp-heading a {
562
+ color: #fff;
563
+ border-bottom: 1px solid #fff;
564
+ text-decoration: none;
565
+ }
566
+
567
+ #tnp-heading a:hover {
568
+ color: #27AE60;
569
+ border-bottom: 1px solid #27AE60;
570
+ }
571
+
572
+ #tnp-heading div p {
573
+ color: #565656;
574
+ }
575
+
576
+ #tnp-heading h1 {
577
+ color: #fff;
578
+ font-family: soleil, sans-serif;
579
+ font-weight: 900;
580
+ }
581
+
582
+ #tnp-heading h2 {
583
+ color: #fff;
584
+ font-family: soleil, sans-serif;
585
+ letter-spacing: 0.1rem;
586
+ font-size: 1.1rem;
587
+ line-height: normal;
588
+ text-transform: uppercase;
589
+ vertical-align: middle;
590
+ font-weight: 700;
591
+ padding: 0;
592
+ margin: 0px;
593
+ margin-bottom: 15px;
594
+ }
595
+
596
+ #tnp-heading h3 {
597
+ color: #27AE60;
598
+ font-family: soleil, sans-serif;
599
+ letter-spacing: 0.1rem;
600
+ font-size: .8rem;
601
+ line-height: 1.8rem;
602
+ text-transform: uppercase;
603
+ vertical-align: middle;
604
+ font-weight: 700;
605
+ padding: 0;
606
+ margin: 0px;
607
+ }
608
+
609
+ #tnp-heading p, #tnp-heading ul {
610
+ margin: 0px;
611
+ color: #ccc;
612
+ }
613
+
614
+ #tnp-heading ul {
615
+ list-style-type: circle;
616
+ list-style-position: inside;
617
+ margin-left: 2em;
618
+ margin-top: 1em;
619
+ }
620
+
621
+ /* Style for WP global notices */
622
+ #tnp-heading .notice p {
623
+ margin: 0.5em 0;
624
+ padding: 2px;
625
+ }
626
+
627
+ #tnp-heading .tnp-btn-h1 {
628
+ color: #fff;
629
+ background-color: #3498db;
630
+ border-radius: 3px;
631
+ padding: 6px 11px;
632
+ text-decoration: none;
633
+ text-transform: capitalize;
634
+ font-family: soleil, sans-serif;
635
+ margin-left: 10px;
636
+ font-size: 0.75rem;
637
+ font-weight: 300;
638
+ border: none;
639
+ }
640
+
641
+ #tnp-heading .tnp-btn-h1:hover {
642
+ color: #fff;
643
+ background-color: #5DADE2;
644
+ -webkit-transition: background-color .25s linear;
645
+ transition: background-color .25s linear;
646
+ -webkit-font-smoothing: subpixel-antialiased;
647
+ border: none;
648
+ color: #fff;
649
+ }
650
+
651
+ /* Dashboard Box */
652
+
653
+ .metabox-holder {
654
+ width: 100%;
655
+ }
656
+
657
+ .postbox {
658
+ border: none;
659
+ }
660
+
661
+ .postbox h3 a {
662
+ float: right;
663
+
664
+ }
665
+
666
+
667
+ #dashboard-widgets .postbox-container {
668
+ width: 33.333%
669
+ }
670
+
671
+ #tnp-body .postbox p {
672
+ color: #000;
673
+ }
674
+
675
+ #dashboard-widgets .postbox-container .postbox h3 {
676
+ font-family: soleil, sans-serif;
677
+ letter-spacing: 0.05rem;
678
+ background-color: #415b76;
679
+ color: #fff;
680
+ margin: 0;
681
+ padding: 9px;
682
+ }
683
+
684
+ #dashboard-widgets .postbox-container h3 a {
685
+ color: white;
686
+ text-decoration: none;
687
+ margin-left: 5px;
688
+ padding: 2px 8px;
689
+ background-color: #26C281;
690
+ border-radius: 2px;
691
+ font-weight: 300;
692
+ text-transform: capitalize;
693
+ font-size: 0.8rem;
694
+ }
695
+
696
+ #dashboard-widgets .postbox-container h3 a:hover {
697
+ color: white;
698
+ text-decoration: none;
699
+ margin-left: 5px;
700
+ background-color: #2ECC71;
701
+ }
702
+
703
+ .postbox-container i {
704
+ margin-right: 3px;
705
+ }
706
+ #tnp-dash-newsletters tr td:last-of-type {
707
+ width: 80px;
708
+ text-align: right;
709
+ }
710
+
711
+ #tnp-dash-subscribers tr td:last-of-type {
712
+ width: 80px;
713
+ text-align: right;
714
+ }
715
+
716
+ #tnp-dash-subscribers tr td:first-of-type {
717
+ width: 250px;
718
+ overflow: hidden;
719
+ }
720
+
721
+ #tnp-dash-subscribers table {
722
+ table-layout: fixed;
723
+ }
724
+
725
+ #tnp-dash-documentation .inside div {
726
+ margin-top: 10px;
727
+ }
728
+
729
+ #tnp-dash-documentation .inside a {
730
+ text-decoration: none;
731
+ color: #fff;
732
+ display: block;
733
+ font-family: soleil, sans-serif;
734
+ padding: 5px 10px;
735
+ }
736
+
737
+
738
+ /* Footer */
739
+
740
+ #tnp-footer {
741
+ margin-top: 10px;
742
+ padding: 20px 10px 10px 40px;
743
+ background-color: #28313C;
744
+ font-family: soleil, sans-serif;
745
+ }
746
+
747
+ #tnp-footer div {
748
+ width: 33%;
749
+ display: inline-block;
750
+ }
751
+
752
+ #tnp-footer a {
753
+ color: #fff;
754
+ text-decoration: none;
755
+ }
756
+
757
+ #tnp-footer a:hover {
758
+ color: #BDC3C7;
759
+ }
760
+
761
+ #tnp-footer input[type="submit"] {
762
+ background-color: #2ECC71;
763
+ border: none;
764
+ padding: 5px;
765
+ color: #fff;
766
+ }
767
+
768
+ #tnp-footer form {
769
+ white-space: nowrap;
770
+ }
771
+
772
+ #tnp-footer li {
773
+ display: inline;
774
+ margin-left: 15px;
775
+ padding: 2px 5px;
776
+ border-left: 3px solid #2ECC71;
777
+ }
778
+
779
+ /* Global buttons styles */
780
+
781
+ #dashboard-widgets .button {
782
+ border: none;
783
+ background: none;
784
+ box-shadow: none;
785
+ color: #322C39;
786
+ }
787
+
788
+ #dashboard-widgets .button:hover {
789
+ background-color: #ECF0F1;
790
+ }
791
+
792
+ .wp-core-ui .button-secondary, .wp-core-ui .button-primary {
793
+ background-color: #3498db;
794
+ border: none;
795
+ box-shadow: none;
796
+ color: #fff;
797
+ font-family: soleil,sans-serif;
798
+ margin: 0px 2px;
799
+ width: auto;
800
+ }
801
+
802
+ .wp-core-ui .button-secondary:hover, .wp-core-ui .button-primary:hover {
803
+ background-color: #5DADE2;
804
+ color: #fff;
805
+ width: auto;
806
+ }
807
+
808
+ span.wp-media-buttons-icon:before {
809
+ color: #fff;
810
+ }
811
+
812
+ .tnp-paginator [value="Go"] {
813
+ background-color: #27AE60;
814
+ }
815
+
816
+ .tnp-paginator [value="Go"]:hover {
817
+ background-color: #2ECC71;
818
+ }
819
+
820
+ .notice-dismiss {
821
+ padding: 3px;
822
+ }
823
+
824
+ /*.widefat .button-secondary {
825
+ background: none;
826
+ color: #3498db;
827
+ }*/
828
+
829
+ /* Paginator */
830
+
831
+ .tnp-paginator {
832
+ color: #fff;
833
+ font-family: soleil,sans-serif;
834
+ margin: 10px 0px;
835
+ }
836
+
837
+ .tnp-paginator .button-secondary {
838
+ padding: 5px;
839
+ line-height: normal;
840
+ height: auto;
841
+ font-size: 12px;
842
+ height: 25px;
843
+ border: none;
844
+ border-radius: 3px;
845
+ vertical-align: baseline;
846
+ }
847
+
848
+ .tnp-paginator [value="Go"] {
849
+ background-color: #27AE60 !important;
850
+ }
851
+
852
+ .tnp-paginator [value="Go"]:hover {
853
+ background-color: #2ECC71 !important;
854
+ }
855
+
856
+ .tnp-paginator input {
857
+ background-color: #2C3E50;
858
+ border: none;
859
+ border-radius: 3px;
860
+ color: #fff;
861
+ padding: 5px;
862
+ line-height: normal;
863
+ font-size: 12px;
864
+ height: 25px;
865
+ }
866
+
867
+ /* Subscribers Search Box */
868
+
869
+ .tnp-subscribers-search {
870
+ color: #fff;
871
+ font-family: soleil, sans-serif;
872
+ background-color: #2C3E50;
873
+ padding: 20px;
874
+ border-radius: 5px;
875
+ margin-bottom: 20px;
876
+ display: inline-block;
877
+ }
878
+
879
+ .tnp-subscribers-search select {
880
+ margin-left: 5px;
881
+ padding: 0;
882
+ line-height: inherit;
883
+ }
884
+
885
+
886
+ /* Responsive Video Embeds */
887
+
888
+ .tnp-video-container {
889
+ position: relative;
890
+ padding-bottom: 56.25%;
891
+ padding-top: 30px; height: 0; overflow: hidden;
892
+ }
893
+
894
+ .tnp-video-container iframe,
895
+ .tnp-video-container object,
896
+ .tnp-video-container embed {
897
+ position: absolute;
898
+ top: 0;
899
+ left: 0;
900
+ width: 100%;
901
+ height: 100%;
902
+ }
903
+
904
+
905
+ /* Colors Palette */
906
+
907
+ .bg-white {
908
+ background-color: #FFF;
909
+ }
910
+
911
+ .orange {
912
+ background-color: #F39C12; /*Orange #F39C12 */
913
+ }
914
+
915
+ .blue {
916
+ background-color: #2980B9; /* Blue #2980B9 */
917
+ }
918
+
919
+ .purple {
920
+ background-color: #8E44AD; /* Purple #8E44AD */
921
+ }
922
+
923
+ .notice a {
924
+ color: #27AE60 !important;
925
+ text-decoration: underline!important;
926
+ }
927
+
928
+ .tnp-chart {
929
+ border: 1px solid #eee;
930
+ width: 100%;
931
+ }
932
+
933
+ /* Suggerimenti Oggetto + Inserimento Emoticons */
934
+
935
+ .tnp-emails-edit #options-subject {
936
+ font-size: 16px;
937
+ display: inline-block;
938
+ margin: 20px 0px;
939
+ width: auto;
940
+ border-radius: 4px;
941
+ padding: 5px 10px;
942
+ }
943
+
944
+ .tnp-suggest-button {
945
+ font-family: soleil, sans-serif;
946
+ margin-left: 8px;
947
+ border-radius: 3px;
948
+ background-color: #2980B9;
949
+ padding: 10px 15px 8px;
950
+ font-size: 14px;
951
+ color: #fff !important;
952
+ text-decoration: none;
953
+ }
954
+
955
+ .tnp-suggest-button:hover {
956
+ background-color: #3f8dbf;
957
+ }
958
+
959
+ .tnp-popup-overlay {
960
+ display: none;
961
+ position: fixed;
962
+ top: 0;
963
+ left: 0;
964
+ width: 100%;
965
+ height: 100%;
966
+ background-color: rgba(0, 0, 0, .8);
967
+ z-index: 10000;
968
+ }
969
+
970
+ .tnp-popup {
971
+ width: 40vw;
972
+ height: 66vh;
973
+ overflow: auto;
974
+ margin: 100px auto 0 auto;
975
+ background-color: #181818;
976
+ padding: 20px;
977
+ position: relative;
978
+ }
979
+ .tnp-popup-close {
980
+ display: block;
981
+ position: absolute;
982
+ top: 5px;
983
+ right: 5px;
984
+ background-color: #181818;
985
+ color: #fff;
986
+ font-size: 40px;
987
+ padding: 10px;
988
+ text-align: right;
989
+ cursor: pointer;
990
+ }
991
+
992
+ .tnp-subjects-header {
993
+ font-size: 16px;
994
+ color: #fff;
995
+ padding: 0px 70px 20px 20px;
996
+ font-family: soleil, sans-serif;
997
+ border-bottom: 1px solid #282828;
998
+ }
999
+
1000
+ #tnp-edit-subjects-list {
1001
+ padding: 0px 70px 20px 20px;
1002
+ }
1003
+
1004
+ #tnp-edit-subjects-list a {
1005
+ padding: 5px;
1006
+ }
1007
+
1008
+ #tnp-edit-subjects-list svg {
1009
+ margin: 0px 10px 0px 0px;
1010
+ vertical-align: middle;
1011
+ }
1012
+
1013
+ .tnp-subject-category {
1014
+ color: #565656;
1015
+ margin: 25px 0px 10px 0px;
1016
+ /* font-family: soleil; */
1017
+ font-size: 12px;
1018
+ text-transform: uppercase;
1019
+ letter-spacing: 0.1em;
1020
+ }
1021
+
1022
+
1023
+ /* Stile selettore liste - Schermata di invio newsletter */
1024
+
1025
+ .tnp-list-conditions p {
1026
+ margin: 0px 10px;
1027
+ }
1028
+
1029
+ /* Lists panel */
1030
+ .tnp-lists .tnp-notes {
1031
+ margin: 0;
1032
+ font-size: .9em;
1033
+ }
1034
+
1035
+ /* Codemirror editor with preview */
1036
+ iframe.tnp-editor-preview-mobile {
1037
+ box-sizing: border-box;
1038
+ background-color: #fff;
1039
+ border: 1px solid #bbb;
1040
+ box-shadow: 1px 1px 10px #777;
1041
+ border-radius: 10px;
1042
+ padding: 5px;
1043
+ width: 320px;
1044
+ height: 500px;
1045
+ float: left;
1046
+ }
1047
+
1048
+ iframe.tnp-editor-preview-desktop {
1049
+ box-sizing: border-box;
1050
+ background-color: #fff;
1051
+ border: 1px solid #bbb;
1052
+ border-radius: 10px;
1053
+ box-shadow: 1px 1px 10px #777;
1054
+ padding: 15px;
1055
+ width: 650px;
1056
+ margin-right: 20px;
1057
+ height: 500px;
1058
+ float: left;
1059
+ }
1060
+
1061
+
1062
+ /* Form inserimento licenza in Addons Manager */
1063
+
1064
+ #tnp-license-control {
1065
+ border-left: 5px solid #27ae60;
1066
+ display: inline-block;
1067
+ padding: 15px 20px;
1068
+ margin-left: -10px;
1069
+ margin-top: 15px;
1070
+ border-radius: 2px;
1071
+ background-color: #fff;
1072
+ }
1073
+
1074
+ #tnp-license-control form {
1075
+ margin-bottom: 10px;
1076
+ margin-top: 10px;
1077
+ }
1078
+
1079
+ #tnp-license-control form input {
1080
+ padding-left: 10px;
1081
+ }
1082
+
1083
+ #tnp-license-control a {
1084
+ border-bottom: none;
1085
+ color: #27AE60;
1086
+ }
1087
+
1088
+
1089
+ /* Status Box Style */
1090
+
1091
+ #tnp-nl-status {
1092
+ width: 100%;
1093
+ background: #fffafa;
1094
+ padding: 15px 25px 15px 25px;
1095
+ border-left: 10px solid #27AE60;
1096
+ border-radius: 0px 5px 5px 0px;
1097
+ }
1098
+
1099
+ #tnp-nl-status p {
1100
+ font-size: 17px;
1101
+ }
1102
+
1103
+ .tnp-nl-status-row {
1104
+ margin: 10px 0;
1105
+ }
1106
+
1107
+ .tnp-nl-status-title {
1108
+ font-size: 26px;
1109
+ line-height: 32px;
1110
+ margin: 5px 0px 0px 0px;
1111
+ color: #3498DB;
1112
+ font-weight: 900;
1113
+ vertical-align: middle;
1114
+ }
1115
+
1116
+ .tnp-nl-status-title-value {
1117
+ font-size: 13px;
1118
+ line-height: 32px;
1119
+ margin: 0px 0px 0px 5px;
1120
+ color: #fff;
1121
+ background-color: #95A5A6;
1122
+ border-radius: 4px;
1123
+ text-transform: uppercase;
1124
+ letter-spacing: 1px;
1125
+ vertical-align: sub;
1126
+ }
1127
+
1128
+ .tnp-nl-status-schedule-targeting {
1129
+ font-size: 15px;
1130
+ color: #34495E;
1131
+ }
1132
+
1133
+ .tnp-nl-status-schedule-value {
1134
+ font-size: 15px;
1135
+ color: #34495E;
1136
+ }
1137
+
1138
+ .tnp-status-header #options-subject {
1139
+ width: calc(100% - 150px);
1140
+ }
1141
+
1142
+ /* Grid Helpers */
1143
+
1144
+ .tnp-one-third {
1145
+ width: 40%;
1146
+ display: inline-block;
1147
+ vertical-align: top;
1148
+ }
1149
+
1150
+ .tnp-two-thirds {
1151
+ width: 59%;
1152
+ display: inline-block;
1153
+ vertical-align: top;
1154
+ }
1155
+
1156
+ /* Progress bar */
1157
+ .tnp-progress {
1158
+ display: flex;
1159
+ height: 1.5rem;
1160
+ overflow: hidden;
1161
+ font-size: .75rem;
1162
+ background-color: #c9cccf;
1163
+ border-radius: .25rem;
1164
+ margin: 0px 0px 0px;
1165
+ min-width: 100px;
1166
+ }
1167
+
1168
+ .tnp-progress-bar {
1169
+ display: -ms-flexbox;
1170
+ display: flex;
1171
+ -ms-flex-direction: column;
1172
+ flex-direction: column;
1173
+ -ms-flex-pack: center;
1174
+ justify-content: center;
1175
+ color: #fff;
1176
+ text-align: center;
1177
+ white-space: nowrap;
1178
+ background-color: #007bff;
1179
+ transition: width .6s ease;
1180
+ }
1181
+
1182
+ .tnp-progress--sent .tnp-progress-bar {
1183
+ background-color: green;
1184
+ }
1185
+
1186
+ .tnp-progress--error .tnp-progress-bar {
1187
+ background-color: #E74C3C;
1188
+ }
1189
+
1190
+ .tnp-progress-numbers {
1191
+ text-align: center;
1192
+ color: #666;
1193
+ }
1194
+
1195
+ .tnp-progress-date {
1196
+ color: #666;
1197
+ font-style: italic;
1198
+ }
1199
+
1200
+ span.tnp-email-status {
1201
+ background-color: #95A5A6;
1202
+ padding: 2px 10px;
1203
+ border-radius: 4px;
1204
+ color: #fff;
1205
+ white-space: nowrap;
1206
+ }
1207
+
1208
+ /* Email status label */
1209
+ span.tnp-email-status.tnp-email-status--new {
1210
+ background-color: #8E44AD;
1211
+ }
1212
+
1213
+ span.tnp-email-status.tnp-email-status--paused {
1214
+ background-color: #95A5A6;
1215
+ }
1216
+
1217
+ span.tnp-email-status.tnp-email-status--sending {
1218
+ background-color: #27AE60;
1219
+ }
1220
+
1221
+ span.tnp-email-status.tnp-email-status--scheduled {
1222
+ background-color: #E67E22;
1223
+ }
1224
+
1225
+ span.tnp-email-status.tnp-email-status--sent {
1226
+ background-color: #95A5A6;
1227
+ }
1228
+
1229
+ span.tnp-email-status.tnp-email-status--error {
1230
+ background-color: #E74C3C;
1231
+ }
1232
+
1233
+ /* Schedule buttons styles */
1234
+
1235
+ #tnp-schedule-button {
1236
+ background-color: #E67E22 !important;
1237
+ }
1238
+
1239
+ #tnp-schedule-button:hover {
1240
+ background-color: #ec913f !important;
1241
+ }
1242
+
1243
+ .tnp-button-cancel {
1244
+ background-color: #E74C3C !important;
1245
+ }
1246
+
1247
+ /* Newsletter preview on targeting panel */
1248
+ .tnpc-preview {
1249
+ margin-top: 10px;
1250
+ }
1251
+
1252
+ .tnpc-preview .fake-browser-ui iframe {
1253
+ width: 700px;
1254
+ }
1255
+
1256
+ .tnpc-preview .fake-mobile-browser-ui iframe {
1257
+ width: 320px;
1258
+ }
1259
+
1260
+ .fake-browser-ui {
1261
+ padding: 30px 0 0;
1262
+ border-radius: 3px;
1263
+ border-bottom: 10px solid #ccc;
1264
+ background: #ddd;
1265
+ display: inline-block;
1266
+ position: relative;
1267
+ line-height: 0;
1268
+ vertical-align: top;
1269
+ margin-left: 20px;
1270
+ }
1271
+
1272
+ .fake-mobile-browser-ui {
1273
+ padding: 30px 10px 37px;
1274
+ border-radius: 10px;
1275
+ border-bottom: 10px solid #ccc;
1276
+ background: #ddd;
1277
+ display: inline-block;
1278
+ position: relative;
1279
+ line-height: 0;
1280
+ margin-left: 30px;
1281
+ }
1282
+
1283
+ .fake-browser-ui .frame {
1284
+ display: block;
1285
+ height: 25px;
1286
+ position: absolute;
1287
+ top: 12px;
1288
+ left: 8px;
1289
+ }
1290
+
1291
+ .fake-mobile-browser-ui .frame {
1292
+ display: block;
1293
+ height: 25px;
1294
+ margin-top: 10px;
1295
+ }
1296
+
1297
+ .fake-browser-ui span {
1298
+ height: 12px;
1299
+ width: 12px;
1300
+ border-radius: 8px;
1301
+ background-color: #eee;
1302
+ border: 1px solid #dadada;
1303
+ float: left;
1304
+ margin: 0 0 0 4px;
1305
+ }
1306
+
1307
+ .fake-mobile-browser-ui span {
1308
+ height: 50px;
1309
+ width: 50px;
1310
+ border-radius: 60px;
1311
+ background-color: #eee;
1312
+ border: 2px solid #ccc;
1313
+ display: block;
1314
+ margin: auto;
1315
+ }
1316
+
1317
+ .fake-browser-ui .bt-1 {
1318
+ background-color: #ED594A;
1319
+ }
1320
+
1321
+ .fake-browser-ui .bt-2 {
1322
+ background-color: #FDD800;
1323
+ }
1324
+
1325
+ .fake-browser-ui .bt-3 {
1326
+ background-color: #5AC05A;
1327
+ }
1328
+
1329
+ /* Addons page */
1330
+
1331
+ #tnp-promo {
1332
+ text-align: left;
1333
+ background-color: #222b36;
1334
+ margin: 20px;
1335
+ border-radius: 5px;
1336
+ padding: 20px 40px;
1337
+ }
1338
+ #tnp-promo .tnp-promo-how-to {
1339
+ width: 50%;
1340
+ padding: 5px 20px;
1341
+ margin-top: 30px;
1342
+ margin-bottom: 30px;
1343
+ border-left: 2px solid #F1C40F;
1344
+ }
1345
+
1346
+ #tnp-promo .tnp-promo-how-to h3 {
1347
+ color: #ECF0F1;
1348
+ margin: 0px;
1349
+ line-height: 36px;
1350
+ }
1351
+
1352
+ #tnp-promo .tnp-promo-how-to p {
1353
+ color: #ECF0F1;
1354
+ margin: 0px;
1355
+ font-size: 16px;
1356
+ line-height: 26px;
1357
+ }
1358
+
1359
+ #tnp-promo .tnp-promo-buttons {
1360
+ margin: 50px 0px;
1361
+ }
1362
+
1363
+ #tnp-promo .tnp-promo-button {
1364
+ background: #27AE60;
1365
+ text-decoration: none;
1366
+ color: white;
1367
+ padding: 15px 20px;
1368
+ font-size: 15px;
1369
+ border-radius: 2px;
1370
+ }
1371
+
1372
+ #tnp-promo .tnp-promo-button:hover {
1373
+ background: #2ECC71;
1374
+ color: white;
1375
+ }
1376
+
1377
+ #tnp-promo .tnp-promo-button i {
1378
+ margin-right: 3px;
1379
+ }
1380
+
1381
+ #tnp-body td a.tnp-table-link,
1382
+ #tnp-body td a.tnp-table-link:visited {
1383
+ color: #444;
1384
+ }
1385
+
1386
+ #tnp-body td a.tnp-table-link:hover {
1387
+ color: #3498DB;
1388
+ }
1389
+
1390
+ .text-left {
1391
+ text-align: left;
1392
+ }
1393
+
1394
+ .tab-min-height {
1395
+ min-height: 500px;
1396
+ }
1397
+
1398
+ .tnp-control-all-languages-notice {
1399
+ padding: 15px;
1400
+ border: 1px dashed #999;
1401
+ }
1402
+
1403
+ /* Spectru, color picker */
1404
+
1405
+ /* Down arrow */
1406
+ .sp-dd {
1407
+ display: none;
1408
+ }
1409
+
1410
+ .sp-replacer {
1411
+ width: 30px!important;
1412
+ height: 30px!important;
1413
+ }
1414
+
1415
+
1416
+ /* Subscriber status labels */
1417
+
1418
+ #tnp-body .unsubscribed {
1419
+ color: gray;
1420
+ }
1421
+
1422
+ #tnp-body .confirmed {
1423
+ color: darkgreen;
1424
+ }
1425
+
1426
+ #tnp-body .not-confirmed {
1427
+ color: darkgray;
1428
+ }
1429
+
1430
+ #tnp-body .complained {
1431
+ color: red;
1432
+ }
1433
+
1434
+ #tnp-body .bounced {
1435
+ color: darkorange;
1436
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
emails/emails.php CHANGED
@@ -1,1410 +1,1410 @@
1
- <?php
2
-
3
- defined('ABSPATH') || exit;
4
-
5
- class NewsletterEmails extends NewsletterModule {
6
-
7
- static $instance;
8
-
9
- const EDITOR_COMPOSER = 2;
10
- const EDITOR_HTML = 1;
11
- const EDITOR_TINYMCE = 0;
12
-
13
- static $PRESETS_LIST;
14
-
15
- const PRESET_EMAIL_TYPE = 'composer_template';
16
-
17
- // Cache
18
- var $blocks = null;
19
-
20
- /**
21
- * @return NewsletterEmails
22
- */
23
- static function instance() {
24
- if (self::$instance == null) {
25
- self::$instance = new NewsletterEmails();
26
- }
27
-
28
- return self::$instance;
29
- }
30
-
31
- function __construct() {
32
- self::$PRESETS_LIST = array("cta", "invite", "announcement", "posts", "sales", "product", "tour", "simple");
33
- $this->themes = new NewsletterThemes('emails');
34
- parent::__construct('emails', '1.1.5');
35
- add_action('newsletter_action', array($this, 'hook_newsletter_action'), 13, 3);
36
-
37
- if (is_admin()) {
38
- if (defined('DOING_AJAX') && DOING_AJAX) {
39
- add_action('wp_ajax_tnpc_render', array($this, 'tnpc_render_callback'));
40
- add_action('wp_ajax_tnpc_preview', array($this, 'tnpc_preview_callback'));
41
- add_action('wp_ajax_tnpc_css', array($this, 'tnpc_css_callback'));
42
- add_action('wp_ajax_tnpc_options', array($this, 'hook_wp_ajax_tnpc_options'));
43
- add_action('wp_ajax_tnpc_get_all_presets', array($this, 'ajax_get_all_presets'));
44
- add_action('wp_ajax_tnpc_get_preset', array($this, 'ajax_get_preset'));
45
- add_action('wp_ajax_tnpc_delete_preset', array($this, 'hook_wp_ajax_tnpc_delete_preset'));
46
- add_action('wp_ajax_tnpc_regenerate_email', array($this, 'hook_wp_ajax_tnpc_regenerate_email'));
47
- }
48
- // Thank you to plugins which add the WP editor on other admin plugin pages...
49
- if (isset($_GET['page']) && $_GET['page'] == 'newsletter_emails_edit') {
50
- global $wp_actions;
51
- $wp_actions['wp_enqueue_editor'] = 1;
52
- }
53
- }
54
- }
55
-
56
- function options_decode($options) {
57
-
58
- // Start compatibility
59
- if (is_string($options) && strpos($options, 'options[') !== false) {
60
- $opts = array();
61
- parse_str($options, $opts);
62
- $options = $opts['options'];
63
- }
64
- // End compatibility
65
-
66
- if (is_array($options)) {
67
- return $options;
68
- }
69
-
70
- $tmp = json_decode($options, true);
71
- if (is_null($tmp)) {
72
- return json_decode(base64_decode($options), true);
73
- } else {
74
- return $tmp;
75
- }
76
- }
77
-
78
- /**
79
- *
80
- * @param array $options Options array
81
- */
82
- function options_encode($options) {
83
- return base64_encode(json_encode($options, JSON_HEX_TAG | JSON_HEX_AMP));
84
- }
85
-
86
- function hook_wp_ajax_tnpc_options() {
87
- global $wpdb;
88
-
89
- // TODO: Uniform to use id everywhere
90
- // if (!isset($_REQUEST['id'])) {
91
- // $_REQUEST['id'] = $_REQUEST['b'];
92
- // }
93
-
94
- $block = $this->get_block($_REQUEST['id']);
95
- if (!$block) {
96
- die('Block not found with id ' . esc_html($_REQUEST['id']));
97
- }
98
-
99
- if (!class_exists('NewsletterControls')) {
100
- include NEWSLETTER_INCLUDES_DIR . '/controls.php';
101
- }
102
-
103
- $options = $this->options_decode(stripslashes_deep($_REQUEST['options']));
104
- $composer = isset($_POST['composer']) ? $_POST['composer'] : [];
105
-
106
- $context = array('type' => '');
107
- if (isset($_REQUEST['context_type'])) {
108
- $context['type'] = $_REQUEST['context_type'];
109
- }
110
-
111
- $controls = new NewsletterControls($options);
112
- $fields = new NewsletterFields($controls);
113
-
114
- $controls->init();
115
- echo '<input type="hidden" name="action" value="tnpc_render">';
116
- echo '<input type="hidden" name="id" value="' . esc_attr($_REQUEST['id']) . '">';
117
- echo '<input type="hidden" name="context_type" value="' . esc_attr($context['type']) . '">';
118
- $inline_edits = '';
119
- if (isset($controls->data['inline_edits'])) {
120
- $inline_edits = $controls->data['inline_edits'];
121
- }
122
- echo '<input type="hidden" name="options[inline_edits]" value="', esc_attr($this->options_encode($inline_edits)), '">';
123
-
124
- ob_start();
125
- include $block['dir'] . '/options.php';
126
- $content = ob_get_clean();
127
- echo "<h2>", esc_html($block["name"]), "</h2>";
128
- echo $content;
129
- wp_die();
130
- }
131
-
132
- /**
133
- * Retrieves the presets list (no id in GET) or a specific preset id in GET)
134
- */
135
- public function ajax_get_all_presets() {
136
- wp_send_json_success($this->get_all_preset());
137
- }
138
-
139
- public function ajax_get_preset() {
140
-
141
- if (empty($_REQUEST['id'])) {
142
- wp_send_json_error([
143
- 'msg' => __('Invalid preset ID')
144
- ]);
145
- }
146
-
147
- $preset_id = $_REQUEST['id'];
148
- $preset_content = $this->get_preset_content($preset_id);
149
- $global_options = $this->get_preset_global_options($preset_id);
150
-
151
- wp_send_json_success([
152
- 'content' => $preset_content,
153
- 'globalOptions' => $global_options,
154
- ]);
155
- }
156
-
157
- private function get_preset_content($preset_id) {
158
-
159
- $content = '';
160
-
161
- if ($this->is_a_tnp_default_preset($preset_id)) {
162
-
163
- // Get preset from file
164
- $preset = $this->get_preset_from_file($preset_id);
165
-
166
- foreach ($preset->blocks as $item) {
167
- ob_start();
168
- $this->render_block($item->block, true, (array) $item->options);
169
- $content .= trim(ob_get_clean());
170
- }
171
- } else {
172
-
173
- // Get preset from db
174
- $preset_email = $this->get_email(intval($preset_id));
175
- $global_options = $this->extract_global_options_from($preset_email);
176
- $content = $this->regenerate_email_blocks($preset_email->message, $global_options);
177
- }
178
-
179
- return $content;
180
- }
181
-
182
- private function get_preset_global_options($preset_id) {
183
-
184
- if ($this->is_a_tnp_default_preset($preset_id)) {
185
- return [];
186
- }
187
-
188
- // Get preset from db
189
- $preset_email = $this->get_email(intval($preset_id));
190
- $global_options = $this->extract_global_options_from($preset_email);
191
-
192
- return $global_options;
193
- }
194
-
195
- private function extract_global_options_from($email) {
196
- $global_options = [];
197
- foreach ($email->options as $global_option_name => $global_option) {
198
- if (strpos($global_option_name, 'composer_') === 0) {
199
- $global_options[str_replace('composer_', '', $global_option_name)] = $global_option;
200
- }
201
- }
202
-
203
- return $global_options;
204
- }
205
-
206
- private function is_a_tnp_default_preset($preset_id) {
207
- return in_array($preset_id, self::$PRESETS_LIST);
208
- }
209
-
210
- private function get_all_preset() {
211
-
212
- $content = "<div class='tnpc-preset-container'>";
213
-
214
- if ($this->is_normal_context_request()) {
215
- $content .= "<div class='tnpc-preset-legacy-themes'><a href='" . $this->get_admin_page_url('theme') . "'>" . __('Looking for legacy themes?', 'newsletter') . "</a></div>";
216
- }
217
-
218
- // LOAD USER PRESETS
219
- $user_preset_list = $this->get_emails(self::PRESET_EMAIL_TYPE);
220
-
221
- foreach ($user_preset_list as $user_preset) {
222
-
223
- $default_icon_url = NEWSLETTER_URL . "/emails/presets/default-icon.png?ver=2";
224
- $preset_name = $user_preset->subject;
225
- $delete_preset_text = __('Delete', 'newsletter');
226
- $edit_preset_text = __('Edit', 'newsletter');
227
-
228
- // esc_js() assumes the string will be in single quote (arghhh!!!)
229
- $onclick_edit = 'tnpc_edit_preset(' . ((int) $user_preset->id) . ', \'' . esc_js($preset_name) . '\', event)';
230
- $onclick_delete = 'tnpc_delete_preset(' . ((int) $user_preset->id) . ', \'' . esc_js($preset_name) . '\', event)';
231
- $onclick_load = 'tnpc_load_preset(' . ((int) $user_preset->id) . ', \'' . esc_js($preset_name) . '\', event)';
232
-
233
- $content .= "<div class='tnpc-preset' onclick='" . esc_attr($onclick_load) . "'>\n";
234
- $content .= "<img src='$default_icon_url' title='" . esc_attr($preset_name) . "' alt='" . esc_attr($preset_name) . "'>\n";
235
- $content .= "<span class='tnpc-preset-label'>" . esc_html($user_preset->subject) . "</span>\n";
236
- $content .= "<span class='tnpc-delete-preset' onclick='" . esc_attr($onclick_delete) . "' title='" . esc_attr($delete_preset_text) . "'><i class='fas fa-times'></i></span>\n";
237
- $content .= "<span class='tnpc-edit-preset' onclick='" . esc_attr($onclick_edit) . "' title='" . esc_attr($edit_preset_text) . "'><i class='fas fa-pencil-alt'></i></span>\n";
238
- $content .= "</div>";
239
- }
240
-
241
- // LOAD TNP PRESETS
242
- foreach (self::$PRESETS_LIST as $id) {
243
- $preset = $this->get_preset_from_file($id);
244
- $preset_name = esc_html($preset->name);
245
- $content .= "<div class='tnpc-preset' onclick='tnpc_load_preset(\"$id\")'>";
246
- $content .= "<img src='$preset->icon' title='$preset_name' alt='$preset_name'/>";
247
- $content .= "<span class='tnpc-preset-label'>$preset_name</span>";
248
- $content .= "</div>";
249
- }
250
-
251
- if ($this->is_normal_context_request()) {
252
- $content .= $this->get_automated_spot_element();
253
- $content .= $this->get_autoresponder_spot_element();
254
- $content .= $this->get_raw_html_preset_element();
255
- }
256
-
257
- return $content;
258
- }
259
-
260
- private function is_normal_context_request() {
261
- return empty($_REQUEST['context_type']);
262
- }
263
-
264
- private function is_automated_context_request() {
265
- return isset($_REQUEST['context_type']) && $_REQUEST['context_type'] === 'automated';
266
- }
267
-
268
- private function is_autoresponder_context_request() {
269
- return isset($_REQUEST['context_type']) && $_REQUEST['context_type'] === 'autoresponder';
270
- }
271
-
272
- private function get_automated_spot_element() {
273
- $result = "<div class='tnpc-preset'>";
274
- if (class_exists('NewsletterAutomated')) {
275
- $result .= "<a href='?page=newsletter_automated_index'>";
276
- } else {
277
- $result .= "<a href='https://www.thenewsletterplugin.com/automated?utm_source=composer&utm_campaign=plugin&utm_medium=automated'>";
278
- }
279
- $result .= "<img src='" . plugins_url('newsletter') . "/emails/images/automated.png' title='Automated addon' alt='Automated'/>";
280
- $result .= "<span class='tnpc-preset-label'>Daily, weekly and monthly newsletters</span></a>";
281
- $result .= "</div>";
282
-
283
- return $result;
284
- }
285
-
286
- private function get_autoresponder_spot_element() {
287
- $result = "<div class='tnpc-preset'>";
288
- if (class_exists('NewsletterAutoresponder')) {
289
- $result .= "<a href='?page=newsletter_autoresponder_index'>";
290
- } else {
291
- $result .= "<a href='https://www.thenewsletterplugin.com/autoresponder?utm_source=composer&utm_campaign=plugin&utm_medium=autoresponder' target='_blank'>";
292
- }
293
- $result .= "<img src='" . plugins_url('newsletter') . "/emails/images/autoresponder.png' title='Autoresponder addon' alt='Autoresponder'/>";
294
- $result .= "<span class='tnpc-preset-label'>Autoresponders</span></a>";
295
- $result .= "</div>";
296
-
297
- return $result;
298
- }
299
-
300
- private function get_raw_html_preset_element() {
301
-
302
- $result = "<div class='tnpc-preset tnpc-preset-html' onclick='location.href=\"" . wp_nonce_url('admin.php?page=newsletter_emails_new&id=rawhtml', 'newsletter-new') . "\"'>";
303
- $result .= "<img src='" . plugins_url('newsletter') . "/emails/images/rawhtml.png' title='RAW HTML' alt='RAW'/>";
304
- $result .= "<span class='tnpc-preset-label'>Raw HTML</span>";
305
- $result .= "</div>";
306
-
307
- $result .= "<div class='clear'></div>";
308
- $result .= "</div>";
309
-
310
- return $result;
311
- }
312
-
313
- /**
314
- * Check if the preset name exists and adds an incremental suffix if the name exists.
315
- *
316
- * @param string $name
317
- *
318
- * @return string
319
- */
320
- public function sanitize_preset_name($name) {
321
- global $wpdb;
322
-
323
- $name = empty($name) ? __('Empty name preset', 'newsletter') : $name;
324
- $name = sanitize_text_field($name);
325
- $type = self::PRESET_EMAIL_TYPE;
326
- $count = (int) $wpdb->get_var("SELECT COUNT(*) FROM " . NEWSLETTER_EMAILS_TABLE . " WHERE type='$type' and subject='$name'");
327
-
328
- $name = $count > 0 ? $name . " - " . ($count + 1) : $name;
329
-
330
- return $name;
331
- }
332
-
333
- function has_dynamic_blocks($theme) {
334
- preg_match_all('/data-json="(.*?)"/m', $theme, $matches, PREG_PATTERN_ORDER);
335
- foreach ($matches[1] as $match) {
336
- $a = html_entity_decode($match, ENT_QUOTES, 'UTF-8');
337
- $options = $this->options_decode($a);
338
-
339
- $block = $this->get_block($options['block_id']);
340
- if (!$block) {
341
- continue;
342
- }
343
- if ($block['type'] == 'dynamic') {
344
- return true;
345
- }
346
- }
347
- return false;
348
- }
349
-
350
- /**
351
- * Regenerates a saved composed email rendering each block. Regeneration is
352
- * conditioned (possibly) by the context. The context is usually passed to blocks
353
- * so they can act in the right manner.
354
- *
355
- * $context contains a type and, for automated, the last_run.
356
- *
357
- * $email can actually be even a string containing the full newsletter HTML code.
358
- *
359
- * @param TNP_Email $email (Rinominare)
360
- * @return string
361
- */
362
- function regenerate($email, $context = []) {
363
-
364
- // Cannot be removed due to compatibility issues with old Automated versions
365
- if (is_object($email)) {
366
- $theme = $email->message;
367
- } else {
368
- $theme = $email;
369
- }
370
-
371
- //$this->logger->debug('Starting email regeneration');
372
- //$this->logger->debug($context);
373
-
374
- if (empty($theme)) {
375
- $this->logger->debug('The email was empty');
376
- return array('body' => '', 'subject' => '');
377
- }
378
-
379
- $context = array_merge(['last_run' => 0, 'type' => ''], $context);
380
-
381
- preg_match_all('/data-json="(.*?)"/m', $theme, $matches, PREG_PATTERN_ORDER);
382
-
383
- $result = '';
384
- $subject = '';
385
-
386
- foreach ($matches[1] as $match) {
387
- $a = html_entity_decode($match, ENT_QUOTES, 'UTF-8');
388
- $options = $this->options_decode($a);
389
-
390
- $block = $this->get_block($options['block_id']);
391
- if (!$block) {
392
- $this->logger->debug('Unable to load the block ' . $options['block_id']);
393
- //continue;
394
- }
395
-
396
- ob_start();
397
- $out = $this->render_block($options['block_id'], true, $options, $context);
398
- if (is_array($out)) {
399
- if ($out['return_empty_message'] || $out['stop']) {
400
- if (is_object($email)) {
401
- return false;
402
- }
403
- return array();
404
- }
405
- if ($out['skip']) {
406
- if (NEWSLETTER_DEBUG) {
407
- $result .= 'Block removed by request';
408
- }
409
- continue;
410
- }
411
- if (empty($subject) && !empty($out['subject'])) {
412
- $subject = $out['subject'];
413
- }
414
- }
415
- $block_html = ob_get_clean();
416
- $result .= $block_html;
417
- }
418
-
419
- // We need to keep the CSS/HEAD part, the regenearion is only about blocks
420
-
421
- if (is_object($email)) {
422
- $result = TNP_Composer::get_main_wrapper_open($email) . $result . TNP_Composer::get_main_wrapper_close($email);
423
- }
424
-
425
- $x = strpos($theme, '<body');
426
- if ($x !== false) {
427
- $x = strpos($theme, '>', $x);
428
- $result = substr($theme, 0, $x + 1) . $result . '</body></html>';
429
- } else {
430
-
431
- }
432
-
433
- if (is_object($email)) {
434
- $email->message = $result;
435
- $email->subject = $subject;
436
- return true;
437
- }
438
-
439
- // Kept for compatibility
440
- return array('body' => $result, 'subject' => $subject);
441
- }
442
-
443
- function remove_block_data($text) {
444
- // TODO: Lavorare!
445
- return $text;
446
- }
447
-
448
- static function get_outlook_wrapper_open($width = 600) {
449
- return '<!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" align="center" cellspacing="0" width="' . $width . '"><tr><td width="' . $width . '" style="vertical-align:top;width:' . $width . 'px;"><![endif]-->';
450
- }
451
-
452
- static function get_outlook_wrapper_close() {
453
- return "<!--[if mso | IE]></td></tr></table><![endif]-->";
454
- }
455
-
456
- function hook_safe_style_css($rules) {
457
- $rules[] = 'display';
458
- return $rules;
459
- }
460
-
461
- /**
462
- * Renders a block identified by its id, using the block options and adding a wrapper
463
- * if required (for the first block rendering).
464
- *
465
- * @param string $block_id
466
- * @param boolean $wrapper
467
- * @param array $options
468
- * @param array $context
469
- * @param array $composer
470
- */
471
- function render_block($block_id = null, $wrapper = false, $options = [], $context = [], $composer = []) {
472
- static $kses_style_filter = false;
473
- include_once NEWSLETTER_INCLUDES_DIR . '/helper.php';
474
-
475
- //Remove 'options_composer_' prefix
476
- $composer_defaults = [];
477
- foreach (TNP_Composer::get_global_style_defaults() as $global_option_name => $global_option) {
478
- $composer_defaults[str_replace('options_composer_', '', $global_option_name)] = $global_option;
479
- }
480
- $composer = array_merge($composer_defaults, $composer);
481
-
482
- $width = 600;
483
- $font_family = 'Helvetica, Arial, sans-serif';
484
-
485
- $global_title_font_family = $composer['title_font_family'];
486
- $global_title_font_size = $composer['title_font_size'];
487
- $global_title_font_color = $composer['title_font_color'];
488
- $global_title_font_weight = $composer['title_font_weight'];
489
-
490
- $global_text_font_family = $composer['text_font_family'];
491
- $global_text_font_size = $composer['text_font_size'];
492
- $global_text_font_color = $composer['text_font_color'];
493
- $global_text_font_weight = $composer['text_font_weight'];
494
-
495
- $global_button_font_family = $composer['button_font_family'];
496
- $global_button_font_size = $composer['button_font_size'];
497
- $global_button_font_color = $composer['button_font_color'];
498
- $global_button_font_weight = $composer['button_font_weight'];
499
- $global_button_background_color = $composer['button_background_color'];
500
-
501
- $global_block_background = $composer['block_background'];
502
-
503
- $info = Newsletter::instance()->get_options('info');
504
-
505
- // Just in case...
506
- if (!is_array($options)) {
507
- $options = array();
508
- }
509
-
510
- // This code filters the HTML to remove javascript and unsecure attributes and enable the
511
- // "display" rule for CSS which is needed in blocks to force specific "block" or "inline" or "table".
512
- add_filter('safe_style_css', [$this, 'hook_safe_style_css'], 9999);
513
- $options = wp_kses_post_deep($options);
514
- remove_filter('safe_style_css', [$this, 'hook_safe_style_css']);
515
-
516
- $block_options = get_option('newsletter_main');
517
-
518
- $block = $this->get_block($block_id);
519
-
520
- if (!isset($context['type']))
521
- $context['type'] = '';
522
-
523
- // Block not found
524
- if (!$block) {
525
- if ($wrapper) {
526
- echo '<table border="0" cellpadding="0" cellspacing="0" align="center" width="100%" style="border-collapse: collapse; width: 100%;" class="tnpc-row tnpc-row-block" data-id="', esc_attr($block_id), '">';
527
- echo '<tr>';
528
- echo '<td data-options="" bgcolor="#ffffff" align="center" style="padding: 0; font-family: Helvetica, Arial, sans-serif;" class="edit-block">';
529
- }
530
- echo $this->get_outlook_wrapper_open($width);
531
-
532
- echo '<p>Ops, this block type is no more registered!</p>';
533
-
534
- echo $this->get_outlook_wrapper_close();
535
-
536
- if ($wrapper) {
537
- echo '</td></tr></table>';
538
- }
539
- return;
540
- }
541
-
542
- $out = ['subject' => '', 'return_empty_message' => false, 'stop' => false, 'skip' => false];
543
-
544
- $dir = is_rtl() ? 'rtl' : 'ltr';
545
- $align_left = is_rtl() ? 'right' : 'left';
546
- $align_right = is_rtl() ? 'left' : 'right';
547
-
548
- ob_start();
549
- $logger = $this->logger;
550
- include $block['dir'] . '/block.php';
551
- $content = trim(ob_get_clean());
552
-
553
- if (empty($content)) {
554
- return $out;
555
- }
556
-
557
- $common_defaults = array(
558
- 'block_padding_top' => 0,
559
- 'block_padding_bottom' => 0,
560
- 'block_padding_right' => 0,
561
- 'block_padding_left' => 0,
562
- 'block_background' => '',
563
- 'block_background_2' => '',
564
- 'block_width' => 600,
565
- 'block_align' => 'center'
566
- );
567
-
568
- $options = array_merge($common_defaults, $options);
569
-
570
- // Obsolete
571
- $content = str_replace('{width}', $width, $content);
572
-
573
- $content = $this->inline_css($content, true);
574
-
575
- // CSS driven by the block
576
- // Requited for the server side parsing and rendering
577
- $options['block_id'] = $block_id;
578
-
579
- $options['block_padding_top'] = (int) str_replace('px', '', $options['block_padding_top']);
580
- $options['block_padding_bottom'] = (int) str_replace('px', '', $options['block_padding_bottom']);
581
- $options['block_padding_right'] = (int) str_replace('px', '', $options['block_padding_right']);
582
- $options['block_padding_left'] = (int) str_replace('px', '', $options['block_padding_left']);
583
-
584
- $block_background = empty($options['block_background']) ? $global_block_background : $options['block_background'];
585
-
586
- // Internal TD wrapper
587
- $style = 'text-align: center; ';
588
- $style .= 'width: 100% !important; ';
589
- $style .= 'line-height: normal !important; ';
590
- $style .= 'letter-spacing: normal; ';
591
- $style .= 'padding-top: ' . $options['block_padding_top'] . 'px; ';
592
- $style .= 'padding-left: ' . $options['block_padding_left'] . 'px; ';
593
- $style .= 'padding-right: ' . $options['block_padding_right'] . 'px; ';
594
- $style .= 'padding-bottom: ' . $options['block_padding_bottom'] . 'px; ';
595
- $style .= 'background-color: ' . $block_background . ';';
596
-
597
- if (isset($options['block_background_gradient'])) {
598
- $style .= 'background: linear-gradient(180deg, ' . $block_background . ' 0%, ' . $options['block_background_2'] . ' 100%);';
599
- }
600
-
601
- $data = $this->options_encode($options);
602
- // First time block creation wrapper
603
- if ($wrapper) {
604
- echo '<table border="0" cellpadding="0" cellspacing="0" align="center" width="100%" style="border-collapse: collapse; width: 100%;" class="tnpc-row tnpc-row-block" data-id="', esc_attr($block_id), '">', "\n";
605
- echo "<tr>";
606
- echo '<td align="center" style="padding: 0;" class="edit-block">', "\n";
607
- }
608
-
609
- // Container that fixes the width and makes the block responsive
610
- echo $this->get_outlook_wrapper_open($options['block_width']);
611
-
612
- echo '<table type="options" data-json="', esc_attr($data), '" class="tnpc-block-content" border="0" cellpadding="0" align="center" cellspacing="0" width="100%" style="width: 100%!important; max-width: ', $options['block_width'], 'px!important">', "\n";
613
- echo "<tr>";
614
- echo '<td align="', esc_attr($options['block_align']), '" style="', $style, '" bgcolor="', $block_background, '" width="100%">';
615
-
616
- //echo "<!-- block generated content -->\n";
617
- echo trim($content);
618
- //echo "\n<!-- /block generated content -->\n";
619
-
620
- echo "</td></tr></table>";
621
- echo $this->get_outlook_wrapper_close();
622
-
623
- // First time block creation wrapper
624
- if ($wrapper) {
625
- echo "</td></tr></table>";
626
- }
627
-
628
- return $out;
629
- }
630
-
631
- /**
632
- * Ajax call to render a block with a new set of options after the settings popup
633
- * has been saved.
634
- *
635
- * @param type $block_id
636
- * @param type $wrapper
637
- */
638
- function tnpc_render_callback() {
639
- if (!check_ajax_referer('save')) {
640
- $this->dienow('Expired request');
641
- }
642
-
643
- $block_id = $_POST['id'];
644
- $wrapper = isset($_POST['full']);
645
- $options = $this->restore_options_from_request();
646
-
647
- $this->render_block($block_id, $wrapper, $options, [], $_POST['composer']);
648
- wp_die();
649
- }
650
-
651
- function hook_wp_ajax_tnpc_regenerate_email() {
652
-
653
- $content = stripslashes($_POST['content']);
654
- $global_options = $_POST['composer'];
655
-
656
- $regenerated_content = $this->regenerate_email_blocks($content, $global_options);
657
-
658
- wp_send_json_success([
659
- 'content' => $regenerated_content,
660
- 'message' => __('Successfully updated', 'newsletter')
661
- ]);
662
- }
663
-
664
- private function regenerate_email_blocks($content, $global_options) {
665
-
666
- $raw_block_options = $this->extract_encoded_blocks_options($content);
667
-
668
- $regenerated_content = '';
669
-
670
- foreach ($raw_block_options as $raw_block_option) {
671
-
672
- /* $a = html_entity_decode( $raw_block_option, ENT_QUOTES, 'UTF-8' );
673
- $block_options = $this->options_decode( $a ); */
674
-
675
- $block_options = $this->options_decode($raw_block_option);
676
-
677
- $block = $this->get_block($block_options['block_id']);
678
- if (!$block) {
679
- $this->logger->debug('Unable to load the block ' . $block_options['block_id']);
680
- }
681
-
682
- ob_start();
683
- $this->render_block($block_options['block_id'], true, $block_options, [], $global_options);
684
- $block_html = ob_get_clean();
685
-
686
- $regenerated_content .= $block_html;
687
- }
688
-
689
- return $regenerated_content;
690
- }
691
-
692
- /**
693
- * @param string $html_email_content Email html content
694
- *
695
- * @return string[] Encoded options of email blocks
696
- */
697
- private function extract_encoded_blocks_options($html_email_content) {
698
-
699
- preg_match_all('/data-json="(.*?)"/m', $html_email_content, $raw_block_options, PREG_PATTERN_ORDER);
700
-
701
- return $raw_block_options[1];
702
- }
703
-
704
- function tnpc_preview_callback() {
705
- $email = Newsletter::instance()->get_email($_REQUEST['id'], ARRAY_A);
706
-
707
- if (empty($email)) {
708
- echo 'Wrong email identifier';
709
- return;
710
- }
711
-
712
- echo $email['message'];
713
-
714
- wp_die();
715
- }
716
-
717
- function tnpc_css_callback() {
718
- include NEWSLETTER_DIR . '/emails/tnp-composer/css/newsletter.css';
719
- wp_die();
720
- }
721
-
722
- /** Returns the correct admin page to edit the newsletter with the correct editor. */
723
- function get_editor_url($email_id, $editor_type) {
724
- switch ($editor_type) {
725
- case NewsletterEmails::EDITOR_COMPOSER:
726
- return admin_url("admin.php") . '?page=newsletter_emails_composer&id=' . $email_id;
727
- case NewsletterEmails::EDITOR_HTML:
728
- return admin_url("admin.php") . '?page=newsletter_emails_editorhtml&id=' . $email_id;
729
- case NewsletterEmails::EDITOR_TINYMCE:
730
- return admin_url("admin.php") . '?page=newsletter_emails_editortinymce&id=' . $email_id;
731
- }
732
- }
733
-
734
- /**
735
- * Returns the button linked to the correct "edit" page for the passed newsletter. The edit page can be an editor
736
- * or the targeting page (it depends on newsletter status).
737
- *
738
- * @param TNP_Email $email
739
- */
740
- function get_edit_button($email, $only_icon = false) {
741
-
742
- $editor_type = $this->get_editor_type($email);
743
- if ($email->status == 'new') {
744
- $edit_url = $this->get_editor_url($email->id, $editor_type);
745
- } else {
746
- $edit_url = 'admin.php?page=newsletter_emails_edit&id=' . $email->id;
747
- }
748
- switch ($editor_type) {
749
- case NewsletterEmails::EDITOR_COMPOSER:
750
- $icon_class = 'th-large';
751
- break;
752
- case NewsletterEmails::EDITOR_HTML:
753
- $icon_class = 'code';
754
- break;
755
- default:
756
- $icon_class = 'edit';
757
- break;
758
- }
759
- if ($only_icon) {
760
- return '<a class="button-primary" href="' . $edit_url . '" title="' . esc_attr__('Edit', 'newsletter') . '">' .
761
- '<i class="fas fa-' . $icon_class . '"></i></a>';
762
- } else {
763
- return '<a class="button-primary" href="' . $edit_url . '" title="' . esc_attr__('Edit', 'newsletter') . '">' .
764
- '<i class="fas fa-' . $icon_class . '"></i> ' . __('Edit', 'newsletter') . '</a>';
765
- }
766
- }
767
-
768
- /** Returns the correct editor type for the provided newsletter. Contains backward compatibility code. */
769
- function get_editor_type($email) {
770
- $email = (object) $email;
771
- $editor_type = $email->editor;
772
-
773
- // Backward compatibility
774
- $email_options = maybe_unserialize($email->options);
775
- if (isset($email_options['composer'])) {
776
- $editor_type = NewsletterEmails::EDITOR_COMPOSER;
777
- }
778
- // End backward compatibility
779
-
780
- return $editor_type;
781
- }
782
-
783
- /**
784
- *
785
- * @param type $action
786
- * @param type $user
787
- * @param type $email
788
- * @return type
789
- * @global wpdb $wpdb
790
- */
791
- function hook_newsletter_action($action, $user, $email) {
792
- global $wpdb;
793
-
794
- switch ($action) {
795
- case 'v':
796
- case 'view':
797
- $id = $_GET['id'];
798
- if ($id == 'last') {
799
- $email = $wpdb->get_row("select * from " . NEWSLETTER_EMAILS_TABLE . " where private=0 and type='message' and status='sent' order by send_on desc limit 1");
800
- } else {
801
- $email = $this->get_email($_GET['id']);
802
- }
803
- if (empty($email)) {
804
- header("HTTP/1.0 404 Not Found");
805
- die('Email not found');
806
- }
807
-
808
- if (!Newsletter::instance()->is_allowed()) {
809
-
810
- if ($email->status == 'new') {
811
- header("HTTP/1.0 404 Not Found");
812
- die('Not sent yet');
813
- }
814
-
815
- if ($email->private == 1) {
816
- if (!$user) {
817
- header("HTTP/1.0 404 Not Found");
818
- die('No available for online view');
819
- }
820
- $sent = $wpdb->get_row($wpdb->prepare("select * from " . NEWSLETTER_SENT_TABLE . " where email_id=%d and user_id=%d limit 1", $email->id, $user->id));
821
- if (!$sent) {
822
- header("HTTP/1.0 404 Not Found");
823
- die('No available for online view');
824
- }
825
- }
826
- }
827
-
828
-
829
- header('Content-Type: text/html;charset=UTF-8');
830
- header('X-Robots-Tag: noindex,nofollow,noarchive');
831
- header('Cache-Control: no-cache,no-store,private');
832
-
833
- echo $this->replace($email->message, $user, $email);
834
-
835
- die();
836
- break;
837
-
838
- case 'emails-css':
839
- $email_id = (int) $_GET['id'];
840
-
841
- $body = Newsletter::instance()->get_email_field($email_id, 'message');
842
-
843
- $x = strpos($body, '<style');
844
- if ($x === false)
845
- return;
846
-
847
- $x = strpos($body, '>', $x);
848
- $y = strpos($body, '</style>');
849
-
850
- header('Content-Type: text/css;charset=UTF-8');
851
-
852
- echo substr($body, $x + 1, $y - $x - 1);
853
-
854
- die();
855
- break;
856
-
857
- case 'emails-composer-css':
858
- header('Cache: no-cache');
859
- header('Content-Type: text/css');
860
- echo $this->get_composer_css();
861
- die();
862
- break;
863
-
864
- case 'emails-preview':
865
- if (!Newsletter::instance()->is_allowed()) {
866
- die('Not enough privileges');
867
- }
868
-
869
- if (!check_admin_referer('view')) {
870
- die();
871
- }
872
-
873
- $theme_id = $_GET['id'];
874
- $theme = $this->themes->get_theme($theme_id);
875
-
876
- // Used by theme code
877
- $theme_options = $this->themes->get_options($theme_id);
878
-
879
- $theme_url = $theme['url'];
880
-
881
- header('Content-Type: text/html;charset=UTF-8');
882
-
883
- include $theme['dir'] . '/theme.php';
884
-
885
- die();
886
- break;
887
-
888
- case 'emails-preview-text':
889
- header('Content-Type: text/plain;charset=UTF-8');
890
- if (!Newsletter::instance()->is_allowed()) {
891
- die('Not enough privileges');
892
- }
893
-
894
- if (!check_admin_referer('view')) {
895
- die();
896
- }
897
-
898
- // Used by theme code
899
- $theme_options = $this->get_current_theme_options();
900
-
901
- $file = include $theme['dir'] . '/theme-text.php';
902
-
903
- if (is_file($file)) {
904
- include $file;
905
- }
906
-
907
- die();
908
- break;
909
-
910
-
911
- case 'emails-create':
912
- // Newsletter from themes are created on frontend context because sometime WP themes change the way the content,
913
- // excerpt, thumbnail are extracted.
914
- if (!Newsletter::instance()->is_allowed()) {
915
- die('Not enough privileges');
916
- }
917
-
918
- require_once NEWSLETTER_INCLUDES_DIR . '/controls.php';
919
- $controls = new NewsletterControls();
920
-
921
- if (!$controls->is_action('create')) {
922
- die('Wrong call');
923
- }
924
-
925
- $theme_id = $controls->data['id'];
926
- $theme = $this->themes->get_theme($theme_id);
927
-
928
- if (!$theme) {
929
- die('invalid theme');
930
- }
931
-
932
- $this->themes->save_options($theme_id, $controls->data);
933
-
934
- $email = array();
935
- $email['status'] = 'new';
936
- $email['subject'] = ''; //__('Here the email subject', 'newsletter');
937
- $email['track'] = 1;
938
- $email['send_on'] = time();
939
- $email['editor'] = NewsletterEmails::EDITOR_TINYMCE;
940
- $email['type'] = 'message';
941
-
942
- $theme_options = $this->themes->get_options($theme_id);
943
-
944
- $theme_url = $theme['url'];
945
- $theme_subject = '';
946
-
947
- ob_start();
948
- include $theme['dir'] . '/theme.php';
949
- $email['message'] = ob_get_clean();
950
-
951
-
952
- if (!empty($theme_subject)) {
953
- $email['subject'] = $theme_subject;
954
- }
955
-
956
- if (file_exists($theme['dir'] . '/theme-text.php')) {
957
- ob_start();
958
- include $theme['dir'] . '/theme-text.php';
959
- $email['message_text'] = ob_get_clean();
960
- } else {
961
- $email['message_text'] = 'You need a modern email client to read this email. Read it online: {email_url}.';
962
- }
963
-
964
- $email = $this->save_email($email);
965
-
966
- $edit_url = $this->get_editor_url($email->id, $email->editor);
967
-
968
- header('Location: ' . $edit_url);
969
-
970
- die();
971
- break;
972
- }
973
- }
974
-
975
- function admin_menu() {
976
- $this->add_menu_page('index', 'Newsletters');
977
- $this->add_admin_page('list', 'Email List');
978
- $this->add_admin_page('new', 'Email New');
979
- $this->add_admin_page('edit', 'Email Edit');
980
- $this->add_admin_page('theme', 'Email Themes');
981
- $this->add_admin_page('composer', 'The Composer');
982
- $this->add_admin_page('editorhtml', 'HTML Editor');
983
- $this->add_admin_page('editortinymce', 'TinyMCE Editor');
984
- }
985
-
986
- /**
987
- * Builds a block data structure starting from the folder containing the block
988
- * files.
989
- *
990
- * @param string $dir
991
- * @return array | WP_Error
992
- */
993
- function build_block($dir) {
994
- $dir = realpath($dir);
995
- $dir = wp_normalize_path($dir);
996
- $full_file = $dir . '/block.php';
997
- if (!is_file($full_file)) {
998
- return new WP_Error('1', 'Missing block.php file in ' . $dir);
999
- }
1000
-
1001
- $relative_dir = substr($dir, strlen(WP_CONTENT_DIR));
1002
- $file = basename($dir);
1003
-
1004
- $data = get_file_data($full_file, ['name' => 'Name', 'section' => 'Section', 'description' => 'Description', 'type' => 'Type']);
1005
- $defaults = ['section' => 'content', 'name' => ucfirst($file), 'descritpion' => '', 'icon' => plugins_url('newsletter') . '/admin/images/block-icon.png'];
1006
- $data = array_merge($defaults, $data);
1007
-
1008
- if (is_file($dir . '/icon.png')) {
1009
- $data['icon'] = content_url($relative_dir . '/icon.png');
1010
- }
1011
-
1012
- $data['id'] = sanitize_key($file);
1013
-
1014
- // Absolute path of the block files
1015
- $data['dir'] = $dir;
1016
- $data['url'] = content_url($relative_dir);
1017
-
1018
- return $data;
1019
- }
1020
-
1021
- /**
1022
- *
1023
- * @param type $dir
1024
- * @return type
1025
- */
1026
- function scan_blocks_dir($dir) {
1027
- $dir = realpath($dir);
1028
- if (!$dir) {
1029
- return [];
1030
- }
1031
- $dir = wp_normalize_path($dir);
1032
-
1033
- $list = [];
1034
- $handle = opendir($dir);
1035
- while ($file = readdir($handle)) {
1036
-
1037
- $data = $this->build_block($dir . '/' . $file);
1038
-
1039
- if (is_wp_error($data)) {
1040
- $this->logger->error($data);
1041
- continue;
1042
- }
1043
- $list[$data['id']] = $data;
1044
- }
1045
- closedir($handle);
1046
- return $list;
1047
- }
1048
-
1049
- /**
1050
- * Array of arrays with every registered block and legacy block converted to the new
1051
- * format.
1052
- *
1053
- * @return array
1054
- */
1055
- function get_blocks() {
1056
-
1057
- if (!is_null($this->blocks)) {
1058
- return $this->blocks;
1059
- }
1060
-
1061
- $this->blocks = $this->scan_blocks_dir(__DIR__ . '/blocks');
1062
-
1063
- $extended = $this->scan_blocks_dir(WP_CONTENT_DIR . '/extensions/newsletter/blocks');
1064
-
1065
- $this->blocks = array_merge($extended, $this->blocks);
1066
-
1067
- $dirs = apply_filters('newsletter_blocks_dir', array());
1068
-
1069
- //$this->logger->debug('Block dirs:');
1070
- //$this->logger->debug($dirs);
1071
-
1072
- foreach ($dirs as $dir) {
1073
- $list = $this->scan_blocks_dir($dir);
1074
- $this->blocks = array_merge($list, $this->blocks);
1075
- }
1076
-
1077
- do_action('newsletter_register_blocks');
1078
-
1079
- foreach (TNP_Composer::$block_dirs as $dir) {
1080
- $block = $this->build_block($dir);
1081
- if (is_wp_error($block)) {
1082
- $this->logger->error($block);
1083
- continue;
1084
- }
1085
- if (!isset($this->blocks[$block['id']])) {
1086
- $this->blocks[$block['id']] = $block;
1087
- } else {
1088
- $this->logger->error('The block "' . $block['id'] . '" has already been registered');
1089
- }
1090
- }
1091
-
1092
- $this->blocks = array_reverse($this->blocks);
1093
- return $this->blocks;
1094
- }
1095
-
1096
- /**
1097
- * Return a single block (associative array) checking for legacy ID as well.
1098
- *
1099
- * @param string $id
1100
- * @return array
1101
- */
1102
- function get_block($id) {
1103
- switch ($id) {
1104
- case 'content-03-text.block':
1105
- $id = 'text';
1106
- break;
1107
- case 'footer-03-social.block':
1108
- $id = 'social';
1109
- break;
1110
- case 'footer-02-canspam.block':
1111
- $id = 'canspam';
1112
- break;
1113
- case 'content-05-image.block':
1114
- $id = 'image';
1115
- break;
1116
- case 'header-01-header.block':
1117
- $id = 'header';
1118
- break;
1119
- case 'footer-01-footer.block':
1120
- $id = 'footer';
1121
- break;
1122
- case 'content-02-heading.block':
1123
- $id = 'heading';
1124
- break;
1125
- case 'content-07-twocols.block':
1126
- case 'content-06-posts.block':
1127
- $id = 'posts';
1128
- break;
1129
- case 'content-04-cta.block':
1130
- $id = 'cta';
1131
- break;
1132
- case 'content-01-hero.block':
1133
- $id = 'hero';
1134
- break;
1135
- // case 'content-02-heading.block': $id = '/plugins/newsletter/emails/blocks/heading';
1136
- // break;
1137
- }
1138
-
1139
- // Conversion for old full path ID
1140
- $id = sanitize_key(basename($id));
1141
-
1142
- // TODO: Correct id for compatibility
1143
- $blocks = $this->get_blocks();
1144
- if (!isset($blocks[$id])) {
1145
- return null;
1146
- }
1147
- return $blocks[$id];
1148
- }
1149
-
1150
- function scan_presets_dir($dir = null) {
1151
-
1152
- if (is_null($dir)) {
1153
- $dir = __DIR__ . '/presets';
1154
- }
1155
-
1156
- if (!is_dir($dir)) {
1157
- return array();
1158
- }
1159
-
1160
- $handle = opendir($dir);
1161
- $list = array();
1162
- $relative_dir = substr($dir, strlen(WP_CONTENT_DIR));
1163
- while ($file = readdir($handle)) {
1164
-
1165
- if ($file == '.' || $file == '..')
1166
- continue;
1167
-
1168
- // The block unique key, we should find out how to build it, maybe an hash of the (relative) dir?
1169
- $preset_id = sanitize_key($file);
1170
-
1171
- $full_file = $dir . '/' . $file . '/preset.json';
1172
-
1173
- if (!is_file($full_file)) {
1174
- continue;
1175
- }
1176
-
1177
- $icon = content_url($relative_dir . '/' . $file . '/icon.png');
1178
-
1179
- $list[$preset_id] = $icon;
1180
- }
1181
- closedir($handle);
1182
- return $list;
1183
- }
1184
-
1185
- function get_preset_from_file($id, $dir = null) {
1186
-
1187
- if (is_null($dir)) {
1188
- $dir = __DIR__ . '/presets';
1189
- }
1190
-
1191
- $id = $this->sanitize_file_name($id);
1192
-
1193
- if (!is_dir($dir . '/' . $id) || !in_array($id, self::$PRESETS_LIST)) {
1194
- return array();
1195
- }
1196
-
1197
- $json_content = file_get_contents("$dir/$id/preset.json");
1198
- $json_content = str_replace("{placeholder_base_url}", plugins_url('newsletter') . '/emails/presets', $json_content);
1199
- $json = json_decode($json_content);
1200
- $json->icon = NEWSLETTER_URL . "/emails/presets/$id/icon.png?ver=2";
1201
-
1202
- return $json;
1203
- }
1204
-
1205
- function get_composer_css() {
1206
- $css = file_get_contents(__DIR__ . '/tnp-composer/css/newsletter.css');
1207
- $css .= "\n\n";
1208
- $css .= file_get_contents(__DIR__ . '/tnp-composer/css/backend.css');
1209
- $blocks = $this->get_blocks();
1210
- foreach ($blocks as $block) {
1211
- if (!file_exists($block['dir'] . '/style.css')) {
1212
- continue;
1213
- }
1214
- $css .= "\n\n";
1215
- $css .= "/* " . $block['name'] . " */\n";
1216
- $css .= file_get_contents($block['dir'] . '/style.css');
1217
- }
1218
- return $css;
1219
- }
1220
-
1221
- /**
1222
- * Send an email to the test subscribers.
1223
- *
1224
- * @param TNP_Email $email Could be any object with the TNP_Email attributes
1225
- * @param NewsletterControls $controls
1226
- */
1227
- function send_test_email($email, $controls) {
1228
- if (!$email) {
1229
- $controls->errors = __('Newsletter should be saved before send a test', 'newsletter');
1230
- return;
1231
- }
1232
-
1233
- $original_subject = $email->subject;
1234
- $this->set_test_subject_to($email);
1235
-
1236
- $users = NewsletterUsers::instance()->get_test_users();
1237
- if (count($users) == 0) {
1238
- $controls->errors = '' . __('There are no test subscribers to send to', 'newsletter') .
1239
- '. <a href="https://www.thenewsletterplugin.com/plugins/newsletter/subscribers-module#test" target="_blank"><strong>' .
1240
- __('Read more', 'newsletter') . '</strong></a>.';
1241
- } else {
1242
- $r = Newsletter::instance()->send($email, $users, true);
1243
- $emails = array();
1244
- foreach ($users as $user) {
1245
- $emails[] = '<a href="admin.php?page=newsletter_users_edit&id=' . $user->id . '" target="_blank">' . $user->email . '</a>';
1246
- }
1247
- if (is_wp_error($r)) {
1248
- $controls->errors = 'Something went wrong. Check the error logs on status page.<br>';
1249
- $controls->errors .= __('Test subscribers:', 'newsletter');
1250
- $controls->errors .= ' ' . implode(', ', $emails);
1251
- $controls->errors .= '<br>';
1252
- $controls->errors .= '<strong>' . esc_html($r->get_error_message()) . '</strong><br>';
1253
- $controls->errors .= '<a href="https://www.thenewsletterplugin.com/documentation/email-sending-issues" target="_blank"><strong>' . __('Read more about delivery issues', 'newsletter') . '</strong></a>.';
1254
- } else {
1255
- $controls->messages = __('Test subscribers:', 'newsletter');
1256
-
1257
- $controls->messages .= ' ' . implode(', ', $emails);
1258
- $controls->messages .= '.<br>';
1259
- $controls->messages .= '<a href="https://www.thenewsletterplugin.com/documentation/subscribers#test" target="_blank"><strong>' .
1260
- __('Read more about test subscribers', 'newsletter') . '</strong></a>.<br>';
1261
- $controls->messages .= '<a href="https://www.thenewsletterplugin.com/documentation/email-sending-issues" target="_blank"><strong>' . __('Read more about delivery issues', 'newsletter') . '</strong></a>.';
1262
- }
1263
- }
1264
- $email->subject = $original_subject;
1265
- }
1266
-
1267
- /**
1268
- * Send an email to the test subscribers.
1269
- *
1270
- * @param TNP_Email $email Could be any object with the TNP_Email attributes
1271
- * @param string $email_address
1272
- *
1273
- * @throws Exception
1274
- */
1275
- function send_test_newsletter_to_email_address( $email, $email_address ) {
1276
-
1277
- if ( ! $email ) {
1278
- throw new Exception( __( 'Newsletter should be saved before send a test', 'newsletter' ) );
1279
- }
1280
-
1281
- $this->set_test_subject_to( $email );
1282
-
1283
- $dummy_subscriber = $this->make_dummy_subscriber();
1284
- $dummy_subscriber->email = $email_address;
1285
-
1286
- $result = Newsletter::instance()->send( $email, [ $dummy_subscriber ], true );
1287
-
1288
- $email = '<a href="admin.php?page=newsletter_users_edit&id=' . $dummy_subscriber->id . '" target="_blank">' . $dummy_subscriber->email . '</a>';
1289
-
1290
- if ( is_wp_error( $result ) ) {
1291
- $error_message = 'Something went wrong. Check the error logs on status page.<br>';
1292
- $error_message .= __( 'Test subscribers:', 'newsletter' );
1293
- $error_message .= ' ' . $email;
1294
- $error_message .= '<br>';
1295
- $error_message .= '<strong>' . esc_html( $result->get_error_message() ) . '</strong><br>';
1296
- $error_message .= '<a href="https://www.thenewsletterplugin.com/documentation/email-sending-issues" target="_blank"><strong>' . __( 'Read more about delivery issues', 'newsletter' ) . '</strong></a>.';
1297
- throw new Exception( $error_message );
1298
- }
1299
-
1300
- $messages = __( 'Test subscribers:', 'newsletter' );
1301
-
1302
- $messages .= ' ' . $email;
1303
- $messages .= '.<br>';
1304
- $messages .= '<a href="https://www.thenewsletterplugin.com/documentation/subscribers#test" target="_blank"><strong>' .
1305
- __( 'Read more about test subscribers', 'newsletter' ) . '</strong></a>.<br>';
1306
- $messages .= '<a href="https://www.thenewsletterplugin.com/documentation/email-sending-issues" target="_blank"><strong>' . __( 'Read more about delivery issues', 'newsletter' ) . '</strong></a>.';
1307
-
1308
- return $messages;
1309
- }
1310
-
1311
- private function set_test_subject_to($email) {
1312
- if ( $email->subject == '' ) {
1313
- $email->subject = '[TEST] Dummy subject, it was empty (remember to set it)';
1314
- } else {
1315
- $email->subject = $email->subject . ' (TEST)';
1316
- }
1317
- }
1318
-
1319
- private function make_dummy_subscriber() {
1320
- $dummy_user = new TNP_User();
1321
- $dummy_user->id = 0;
1322
- $dummy_user->email = 'john.doe@example.org';
1323
- $dummy_user->name = 'John';
1324
- $dummy_user->surname = 'Doe';
1325
- $dummy_user->sex = 'n';
1326
- $dummy_user->language = '';
1327
- $dummy_user->ip = '';
1328
-
1329
- for ( $i = 1; $i <= NEWSLETTER_PROFILE_MAX; $i ++ ) {
1330
- $profile_key = "profile_$i";
1331
- $dummy_user->$profile_key = '';
1332
- }
1333
-
1334
- return $dummy_user;
1335
- }
1336
-
1337
- function restore_options_from_request() {
1338
-
1339
- require_once NEWSLETTER_INCLUDES_DIR . '/controls.php';
1340
- $controls = new NewsletterControls();
1341
- $options = $controls->data;
1342
-
1343
- if (isset($_POST['options']) && is_array($_POST['options'])) {
1344
- // Get all block options
1345
- //$options = stripslashes_deep($_POST['options']);
1346
-
1347
- // Deserialize inline edits when
1348
- // render is preformed on saving block options
1349
- if (isset($options['inline_edits']) && !is_array($options['inline_edits'])) {
1350
- $options['inline_edits'] = $this->options_decode($options['inline_edits']);
1351
- }
1352
-
1353
- // Restore inline edits from data-json
1354
- // coming from inline editing
1355
- // and merge with current inline edit
1356
- if (isset($_POST['encoded_options'])) {
1357
- $decoded_options = $this->options_decode($_POST['encoded_options']);
1358
-
1359
- $to_merge_inline_edits = [];
1360
-
1361
- if (isset($decoded_options['inline_edits'])) {
1362
- foreach ($decoded_options['inline_edits'] as $decoded_inline_edit) {
1363
- $to_merge_inline_edits[$decoded_inline_edit['post_id'] . $decoded_inline_edit['type']] = $decoded_inline_edit;
1364
- }
1365
- }
1366
-
1367
- //Overwrite with new edited content
1368
- if (isset($options['inline_edits'])) {
1369
- foreach ($options['inline_edits'] as $inline_edit) {
1370
- $to_merge_inline_edits[$inline_edit['post_id'] . $inline_edit['type']] = $inline_edit;
1371
- }
1372
- }
1373
-
1374
- $options['inline_edits'] = array_values($to_merge_inline_edits);
1375
- $options = array_merge($decoded_options, $options);
1376
- }
1377
-
1378
- return $options;
1379
- }
1380
-
1381
- return array();
1382
- }
1383
-
1384
- public function hook_wp_ajax_tnpc_delete_preset() {
1385
-
1386
- if (!wp_verify_nonce($_POST['_wpnonce'], 'preset')) {
1387
- wp_send_json_error('Expired request');
1388
- }
1389
-
1390
- $preset_id = (int) $_REQUEST['presetId'];
1391
-
1392
- $newsletter = Newsletter::instance();
1393
-
1394
- if ($preset_id > 0) {
1395
- $preset = $newsletter->get_email($preset_id);
1396
-
1397
- if ($preset && $preset->type === self::PRESET_EMAIL_TYPE) {
1398
- Newsletter::instance()->delete_email($preset_id);
1399
- wp_send_json_success();
1400
- } else {
1401
- wp_send_json_error(__('Is not a preset!', 'newsletter'));
1402
- }
1403
- } else {
1404
- wp_send_json_error();
1405
- }
1406
- }
1407
-
1408
- }
1409
-
1410
- NewsletterEmails::instance();
1
+ <?php
2
+
3
+ defined('ABSPATH') || exit;
4
+
5
+ class NewsletterEmails extends NewsletterModule {
6
+
7
+ static $instance;
8
+
9
+ const EDITOR_COMPOSER = 2;
10
+ const EDITOR_HTML = 1;
11
+ const EDITOR_TINYMCE = 0;
12
+
13
+ static $PRESETS_LIST;
14
+
15
+ const PRESET_EMAIL_TYPE = 'composer_template';
16
+
17
+ // Cache
18
+ var $blocks = null;
19
+
20
+ /**
21
+ * @return NewsletterEmails
22
+ */
23
+ static function instance() {
24
+ if (self::$instance == null) {
25
+ self::$instance = new NewsletterEmails();
26
+ }
27
+
28
+ return self::$instance;
29
+ }
30
+
31
+ function __construct() {
32
+ self::$PRESETS_LIST = array("cta", "invite", "announcement", "posts", "sales", "product", "tour", "simple");
33
+ $this->themes = new NewsletterThemes('emails');
34
+ parent::__construct('emails', '1.1.5');
35
+ add_action('newsletter_action', array($this, 'hook_newsletter_action'), 13, 3);
36
+
37
+ if (is_admin()) {
38
+ if (defined('DOING_AJAX') && DOING_AJAX) {
39
+ add_action('wp_ajax_tnpc_render', array($this, 'tnpc_render_callback'));
40
+ add_action('wp_ajax_tnpc_preview', array($this, 'tnpc_preview_callback'));
41
+ add_action('wp_ajax_tnpc_css', array($this, 'tnpc_css_callback'));
42
+ add_action('wp_ajax_tnpc_options', array($this, 'hook_wp_ajax_tnpc_options'));
43
+ add_action('wp_ajax_tnpc_get_all_presets', array($this, 'ajax_get_all_presets'));
44
+ add_action('wp_ajax_tnpc_get_preset', array($this, 'ajax_get_preset'));
45
+ add_action('wp_ajax_tnpc_delete_preset', array($this, 'hook_wp_ajax_tnpc_delete_preset'));
46
+ add_action('wp_ajax_tnpc_regenerate_email', array($this, 'hook_wp_ajax_tnpc_regenerate_email'));
47
+ }
48
+ // Thank you to plugins which add the WP editor on other admin plugin pages...
49
+ if (isset($_GET['page']) && $_GET['page'] == 'newsletter_emails_edit') {
50
+ global $wp_actions;
51
+ $wp_actions['wp_enqueue_editor'] = 1;
52
+ }
53
+ }
54
+ }
55
+
56
+ function options_decode($options) {
57
+
58
+ // Start compatibility
59
+ if (is_string($options) && strpos($options, 'options[') !== false) {
60
+ $opts = array();
61
+ parse_str($options, $opts);
62
+ $options = $opts['options'];
63
+ }
64
+ // End compatibility
65
+
66
+ if (is_array($options)) {
67
+ return $options;
68
+ }
69
+
70
+ $tmp = json_decode($options, true);
71
+ if (is_null($tmp)) {
72
+ return json_decode(base64_decode($options), true);
73
+ } else {
74
+ return $tmp;
75
+ }
76
+ }
77
+
78
+ /**
79
+ *
80
+ * @param array $options Options array
81
+ */
82
+ function options_encode($options) {
83
+ return base64_encode(json_encode($options, JSON_HEX_TAG | JSON_HEX_AMP));
84
+ }
85
+
86
+ function hook_wp_ajax_tnpc_options() {
87
+ global $wpdb;
88
+
89
+ // TODO: Uniform to use id everywhere
90
+ // if (!isset($_REQUEST['id'])) {
91
+ // $_REQUEST['id'] = $_REQUEST['b'];
92
+ // }
93
+
94
+ $block = $this->get_block($_REQUEST['id']);
95
+ if (!$block) {
96
+ die('Block not found with id ' . esc_html($_REQUEST['id']));
97
+ }
98
+
99
+ if (!class_exists('NewsletterControls')) {
100
+ include NEWSLETTER_INCLUDES_DIR . '/controls.php';
101
+ }
102
+
103
+ $options = $this->options_decode(stripslashes_deep($_REQUEST['options']));
104
+ $composer = isset($_POST['composer']) ? $_POST['composer'] : [];
105
+
106
+ $context = array('type' => '');
107
+ if (isset($_REQUEST['context_type'])) {
108
+ $context['type'] = $_REQUEST['context_type'];
109
+ }
110
+
111
+ $controls = new NewsletterControls($options);
112
+ $fields = new NewsletterFields($controls);
113
+
114
+ $controls->init();
115
+ echo '<input type="hidden" name="action" value="tnpc_render">';
116
+ echo '<input type="hidden" name="id" value="' . esc_attr($_REQUEST['id']) . '">';
117
+ echo '<input type="hidden" name="context_type" value="' . esc_attr($context['type']) . '">';
118
+ $inline_edits = '';
119
+ if (isset($controls->data['inline_edits'])) {
120
+ $inline_edits = $controls->data['inline_edits'];
121
+ }
122
+ echo '<input type="hidden" name="options[inline_edits]" value="', esc_attr($this->options_encode($inline_edits)), '">';
123
+
124
+ ob_start();
125
+ include $block['dir'] . '/options.php';
126
+ $content = ob_get_clean();
127
+ echo "<h2>", esc_html($block["name"]), "</h2>";
128
+ echo $content;
129
+ wp_die();
130
+ }
131
+
132
+ /**
133
+ * Retrieves the presets list (no id in GET) or a specific preset id in GET)
134
+ */
135
+ public function ajax_get_all_presets() {
136
+ wp_send_json_success($this->get_all_preset());
137
+ }
138
+
139
+ public function ajax_get_preset() {
140
+
141
+ if (empty($_REQUEST['id'])) {
142
+ wp_send_json_error([
143
+ 'msg' => __('Invalid preset ID')
144
+ ]);
145
+ }
146
+
147
+ $preset_id = $_REQUEST['id'];
148
+ $preset_content = $this->get_preset_content($preset_id);
149
+ $global_options = $this->get_preset_global_options($preset_id);
150
+
151
+ wp_send_json_success([
152
+ 'content' => $preset_content,
153
+ 'globalOptions' => $global_options,
154
+ ]);
155
+ }
156
+
157
+ private function get_preset_content($preset_id) {
158
+
159
+ $content = '';
160
+
161
+ if ($this->is_a_tnp_default_preset($preset_id)) {
162
+
163
+ // Get preset from file
164
+ $preset = $this->get_preset_from_file($preset_id);
165
+
166
+ foreach ($preset->blocks as $item) {
167
+ ob_start();
168
+ $this->render_block($item->block, true, (array) $item->options);
169
+ $content .= trim(ob_get_clean());
170
+ }
171
+ } else {
172
+
173
+ // Get preset from db
174
+ $preset_email = $this->get_email(intval($preset_id));
175
+ $global_options = $this->extract_global_options_from($preset_email);
176
+ $content = $this->regenerate_email_blocks($preset_email->message, $global_options);
177
+ }
178
+
179
+ return $content;
180
+ }
181
+
182
+ private function get_preset_global_options($preset_id) {
183
+
184
+ if ($this->is_a_tnp_default_preset($preset_id)) {
185
+ return [];
186
+ }
187
+
188
+ // Get preset from db
189
+ $preset_email = $this->get_email(intval($preset_id));
190
+ $global_options = $this->extract_global_options_from($preset_email);
191
+
192
+ return $global_options;
193
+ }
194
+
195
+ private function extract_global_options_from($email) {
196
+ $global_options = [];
197
+ foreach ($email->options as $global_option_name => $global_option) {
198
+ if (strpos($global_option_name, 'composer_') === 0) {
199
+ $global_options[str_replace('composer_', '', $global_option_name)] = $global_option;
200
+ }
201
+ }
202
+
203
+ return $global_options;
204
+ }
205
+
206
+ private function is_a_tnp_default_preset($preset_id) {
207
+ return in_array($preset_id, self::$PRESETS_LIST);
208
+ }
209
+
210
+ private function get_all_preset() {
211
+
212
+ $content = "<div class='tnpc-preset-container'>";
213
+
214
+ if ($this->is_normal_context_request()) {
215
+ $content .= "<div class='tnpc-preset-legacy-themes'><a href='" . $this->get_admin_page_url('theme') . "'>" . __('Looking for legacy themes?', 'newsletter') . "</a></div>";
216
+ }
217
+
218
+ // LOAD USER PRESETS
219
+ $user_preset_list = $this->get_emails(self::PRESET_EMAIL_TYPE);
220
+
221
+ foreach ($user_preset_list as $user_preset) {
222
+
223
+ $default_icon_url = NEWSLETTER_URL . "/emails/presets/default-icon.png?ver=2";
224
+ $preset_name = $user_preset->subject;
225
+ $delete_preset_text = __('Delete', 'newsletter');
226
+ $edit_preset_text = __('Edit', 'newsletter');
227
+
228
+ // esc_js() assumes the string will be in single quote (arghhh!!!)
229
+ $onclick_edit = 'tnpc_edit_preset(' . ((int) $user_preset->id) . ', \'' . esc_js($preset_name) . '\', event)';
230
+ $onclick_delete = 'tnpc_delete_preset(' . ((int) $user_preset->id) . ', \'' . esc_js($preset_name) . '\', event)';
231
+ $onclick_load = 'tnpc_load_preset(' . ((int) $user_preset->id) . ', \'' . esc_js($preset_name) . '\', event)';
232
+
233
+ $content .= "<div class='tnpc-preset' onclick='" . esc_attr($onclick_load) . "'>\n";
234
+ $content .= "<img src='$default_icon_url' title='" . esc_attr($preset_name) . "' alt='" . esc_attr($preset_name) . "'>\n";
235
+ $content .= "<span class='tnpc-preset-label'>" . esc_html($user_preset->subject) . "</span>\n";
236
+ $content .= "<span class='tnpc-delete-preset' onclick='" . esc_attr($onclick_delete) . "' title='" . esc_attr($delete_preset_text) . "'><i class='fas fa-times'></i></span>\n";
237
+ $content .= "<span class='tnpc-edit-preset' onclick='" . esc_attr($onclick_edit) . "' title='" . esc_attr($edit_preset_text) . "'><i class='fas fa-pencil-alt'></i></span>\n";
238
+ $content .= "</div>";
239
+ }
240
+
241
+ // LOAD TNP PRESETS
242
+ foreach (self::$PRESETS_LIST as $id) {
243
+ $preset = $this->get_preset_from_file($id);
244
+ $preset_name = esc_html($preset->name);
245
+ $content .= "<div class='tnpc-preset' onclick='tnpc_load_preset(\"$id\")'>";
246
+ $content .= "<img src='$preset->icon' title='$preset_name' alt='$preset_name'/>";
247
+ $content .= "<span class='tnpc-preset-label'>$preset_name</span>";
248
+ $content .= "</div>";
249
+ }
250
+
251
+ if ($this->is_normal_context_request()) {
252
+ $content .= $this->get_automated_spot_element();
253
+ $content .= $this->get_autoresponder_spot_element();
254
+ $content .= $this->get_raw_html_preset_element();
255
+ }
256
+
257
+ return $content;
258
+ }
259
+
260
+ private function is_normal_context_request() {
261
+ return empty($_REQUEST['context_type']);
262
+ }
263
+
264
+ private function is_automated_context_request() {
265
+ return isset($_REQUEST['context_type']) && $_REQUEST['context_type'] === 'automated';
266
+ }
267
+
268
+ private function is_autoresponder_context_request() {
269
+ return isset($_REQUEST['context_type']) && $_REQUEST['context_type'] === 'autoresponder';
270
+ }
271
+
272
+ private function get_automated_spot_element() {
273
+ $result = "<div class='tnpc-preset'>";
274
+ if (class_exists('NewsletterAutomated')) {
275
+ $result .= "<a href='?page=newsletter_automated_index'>";
276
+ } else {
277
+ $result .= "<a href='https://www.thenewsletterplugin.com/automated?utm_source=composer&utm_campaign=plugin&utm_medium=automated'>";
278
+ }
279
+ $result .= "<img src='" . plugins_url('newsletter') . "/emails/images/automated.png' title='Automated addon' alt='Automated'/>";
280
+ $result .= "<span class='tnpc-preset-label'>Daily, weekly and monthly newsletters</span></a>";
281
+ $result .= "</div>";
282
+
283
+ return $result;
284
+ }
285
+
286
+ private function get_autoresponder_spot_element() {
287
+ $result = "<div class='tnpc-preset'>";
288
+ if (class_exists('NewsletterAutoresponder')) {
289
+ $result .= "<a href='?page=newsletter_autoresponder_index'>";
290
+ } else {
291
+ $result .= "<a href='https://www.thenewsletterplugin.com/autoresponder?utm_source=composer&utm_campaign=plugin&utm_medium=autoresponder' target='_blank'>";
292
+ }
293
+ $result .= "<img src='" . plugins_url('newsletter') . "/emails/images/autoresponder.png' title='Autoresponder addon' alt='Autoresponder'/>";
294
+ $result .= "<span class='tnpc-preset-label'>Autoresponders</span></a>";
295
+ $result .= "</div>";
296
+
297
+ return $result;
298
+ }
299
+
300
+ private function get_raw_html_preset_element() {
301
+
302
+ $result = "<div class='tnpc-preset tnpc-preset-html' onclick='location.href=\"" . wp_nonce_url('admin.php?page=newsletter_emails_new&id=rawhtml', 'newsletter-new') . "\"'>";
303
+ $result .= "<img src='" . plugins_url('newsletter') . "/emails/images/rawhtml.png' title='RAW HTML' alt='RAW'/>";
304
+ $result .= "<span class='tnpc-preset-label'>Raw HTML</span>";
305
+ $result .= "</div>";
306
+
307
+ $result .= "<div class='clear'></div>";
308
+ $result .= "</div>";
309
+
310
+ return $result;
311
+ }
312
+
313
+ /**
314
+ * Check if the preset name exists and adds an incremental suffix if the name exists.
315
+ *
316
+ * @param string $name
317
+ *
318
+ * @return string
319
+ */
320
+ public function sanitize_preset_name($name) {
321
+ global $wpdb;
322
+
323
+ $name = empty($name) ? __('Empty name preset', 'newsletter') : $name;
324
+ $name = sanitize_text_field($name);
325
+ $type = self::PRESET_EMAIL_TYPE;
326
+ $count = (int) $wpdb->get_var("SELECT COUNT(*) FROM " . NEWSLETTER_EMAILS_TABLE . " WHERE type='$type' and subject='$name'");
327
+
328
+ $name = $count > 0 ? $name . " - " . ($count + 1) : $name;
329
+
330
+ return $name;
331
+ }
332
+
333
+ function has_dynamic_blocks($theme) {
334
+ preg_match_all('/data-json="(.*?)"/m', $theme, $matches, PREG_PATTERN_ORDER);
335
+ foreach ($matches[1] as $match) {
336
+ $a = html_entity_decode($match, ENT_QUOTES, 'UTF-8');
337
+ $options = $this->options_decode($a);
338
+
339
+ $block = $this->get_block($options['block_id']);
340
+ if (!$block) {
341
+ continue;
342
+ }
343
+ if ($block['type'] == 'dynamic') {
344
+ return true;
345
+ }
346
+ }
347
+ return false;
348
+ }
349
+
350
+ /**
351
+ * Regenerates a saved composed email rendering each block. Regeneration is
352
+ * conditioned (possibly) by the context. The context is usually passed to blocks
353
+ * so they can act in the right manner.
354
+ *
355
+ * $context contains a type and, for automated, the last_run.
356
+ *
357
+ * $email can actually be even a string containing the full newsletter HTML code.
358
+ *
359
+ * @param TNP_Email $email (Rinominare)
360
+ * @return string
361
+ */
362
+ function regenerate($email, $context = []) {
363
+
364
+ // Cannot be removed due to compatibility issues with old Automated versions
365
+ if (is_object($email)) {
366
+ $theme = $email->message;
367
+ } else {
368
+ $theme = $email;
369
+ }
370
+
371
+ //$this->logger->debug('Starting email regeneration');
372
+ //$this->logger->debug($context);
373
+
374
+ if (empty($theme)) {
375
+ $this->logger->debug('The email was empty');
376
+ return array('body' => '', 'subject' => '');
377
+ }
378
+
379
+ $context = array_merge(['last_run' => 0, 'type' => ''], $context);
380
+
381
+ preg_match_all('/data-json="(.*?)"/m', $theme, $matches, PREG_PATTERN_ORDER);
382
+
383
+ $result = '';
384
+ $subject = '';
385
+
386
+ foreach ($matches[1] as $match) {
387
+ $a = html_entity_decode($match, ENT_QUOTES, 'UTF-8');
388
+ $options = $this->options_decode($a);
389
+
390
+ $block = $this->get_block($options['block_id']);
391
+ if (!$block) {
392
+ $this->logger->debug('Unable to load the block ' . $options['block_id']);
393
+ //continue;
394
+ }
395
+
396
+ ob_start();
397
+ $out = $this->render_block($options['block_id'], true, $options, $context);
398
+ if (is_array($out)) {
399
+ if ($out['return_empty_message'] || $out['stop']) {
400
+ if (is_object($email)) {
401
+ return false;
402
+ }
403
+ return array();
404
+ }
405
+ if ($out['skip']) {
406
+ if (NEWSLETTER_DEBUG) {
407
+ $result .= 'Block removed by request';
408
+ }
409
+ continue;
410
+ }
411
+ if (empty($subject) && !empty($out['subject'])) {
412
+ $subject = $out['subject'];
413
+ }
414
+ }
415
+ $block_html = ob_get_clean();
416
+ $result .= $block_html;
417
+ }
418
+
419
+ // We need to keep the CSS/HEAD part, the regenearion is only about blocks
420
+
421
+ if (is_object($email)) {
422
+ $result = TNP_Composer::get_main_wrapper_open($email) . $result . TNP_Composer::get_main_wrapper_close($email);
423
+ }
424
+
425
+ $x = strpos($theme, '<body');
426
+ if ($x !== false) {
427
+ $x = strpos($theme, '>', $x);
428
+ $result = substr($theme, 0, $x + 1) . $result . '</body></html>';
429
+ } else {
430
+
431
+ }
432
+
433
+ if (is_object($email)) {
434
+ $email->message = $result;
435
+ $email->subject = $subject;
436
+ return true;
437
+ }
438
+
439
+ // Kept for compatibility
440
+ return array('body' => $result, 'subject' => $subject);
441
+ }
442
+
443
+ function remove_block_data($text) {
444
+ // TODO: Lavorare!
445
+ return $text;
446
+ }
447
+
448
+ static function get_outlook_wrapper_open($width = 600) {
449
+ return '<!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" align="center" cellspacing="0" width="' . $width . '"><tr><td width="' . $width . '" style="vertical-align:top;width:' . $width . 'px;"><![endif]-->';
450
+ }
451
+
452
+ static function get_outlook_wrapper_close() {
453
+ return "<!--[if mso | IE]></td></tr></table><![endif]-->";
454
+ }
455
+
456
+ function hook_safe_style_css($rules) {
457
+ $rules[] = 'display';
458
+ return $rules;
459
+ }
460
+
461
+ /**
462
+ * Renders a block identified by its id, using the block options and adding a wrapper
463
+ * if required (for the first block rendering).
464
+ *
465
+ * @param string $block_id
466
+ * @param boolean $wrapper
467
+ * @param array $options
468
+ * @param array $context
469
+ * @param array $composer
470
+ */
471
+ function render_block($block_id = null, $wrapper = false, $options = [], $context = [], $composer = []) {
472
+ static $kses_style_filter = false;
473
+ include_once NEWSLETTER_INCLUDES_DIR . '/helper.php';
474
+
475
+ //Remove 'options_composer_' prefix
476
+ $composer_defaults = [];
477
+ foreach (TNP_Composer::get_global_style_defaults() as $global_option_name => $global_option) {
478
+ $composer_defaults[str_replace('options_composer_', '', $global_option_name)] = $global_option;
479
+ }
480
+ $composer = array_merge($composer_defaults, $composer);
481
+
482
+ $width = 600;
483
+ $font_family = 'Helvetica, Arial, sans-serif';
484
+
485
+ $global_title_font_family = $composer['title_font_family'];
486
+ $global_title_font_size = $composer['title_font_size'];
487
+ $global_title_font_color = $composer['title_font_color'];
488
+ $global_title_font_weight = $composer['title_font_weight'];
489
+
490
+ $global_text_font_family = $composer['text_font_family'];
491
+ $global_text_font_size = $composer['text_font_size'];
492
+ $global_text_font_color = $composer['text_font_color'];
493
+ $global_text_font_weight = $composer['text_font_weight'];
494
+
495
+ $global_button_font_family = $composer['button_font_family'];
496
+ $global_button_font_size = $composer['button_font_size'];
497
+ $global_button_font_color = $composer['button_font_color'];
498
+ $global_button_font_weight = $composer['button_font_weight'];
499
+ $global_button_background_color = $composer['button_background_color'];
500
+
501
+ $global_block_background = $composer['block_background'];
502
+
503
+ $info = Newsletter::instance()->get_options('info');
504
+
505
+ // Just in case...
506
+ if (!is_array($options)) {
507
+ $options = array();
508
+ }
509
+
510
+ // This code filters the HTML to remove javascript and unsecure attributes and enable the
511
+ // "display" rule for CSS which is needed in blocks to force specific "block" or "inline" or "table".
512
+ add_filter('safe_style_css', [$this, 'hook_safe_style_css'], 9999);
513
+ $options = wp_kses_post_deep($options);
514
+ remove_filter('safe_style_css', [$this, 'hook_safe_style_css']);
515
+
516
+ $block_options = get_option('newsletter_main');
517
+
518
+ $block = $this->get_block($block_id);
519
+
520
+ if (!isset($context['type']))
521
+ $context['type'] = '';
522
+
523
+ // Block not found
524
+ if (!$block) {
525
+ if ($wrapper) {
526
+ echo '<table border="0" cellpadding="0" cellspacing="0" align="center" width="100%" style="border-collapse: collapse; width: 100%;" class="tnpc-row tnpc-row-block" data-id="', esc_attr($block_id), '">';
527
+ echo '<tr>';
528
+ echo '<td data-options="" bgcolor="#ffffff" align="center" style="padding: 0; font-family: Helvetica, Arial, sans-serif;" class="edit-block">';
529
+ }
530
+ echo $this->get_outlook_wrapper_open($width);
531
+
532
+ echo '<p>Ops, this block type is no more registered!</p>';
533
+
534
+ echo $this->get_outlook_wrapper_close();
535
+
536
+ if ($wrapper) {
537
+ echo '</td></tr></table>';
538
+ }
539
+ return;
540
+ }
541
+
542
+ $out = ['subject' => '', 'return_empty_message' => false, 'stop' => false, 'skip' => false];
543
+
544
+ $dir = is_rtl() ? 'rtl' : 'ltr';
545
+ $align_left = is_rtl() ? 'right' : 'left';
546
+ $align_right = is_rtl() ? 'left' : 'right';
547
+
548
+ ob_start();
549
+ $logger = $this->logger;
550
+ include $block['dir'] . '/block.php';
551
+ $content = trim(ob_get_clean());
552
+
553
+ if (empty($content)) {
554
+ return $out;
555
+ }
556
+
557
+ $common_defaults = array(
558
+ 'block_padding_top' => 0,
559
+ 'block_padding_bottom' => 0,
560
+ 'block_padding_right' => 0,
561
+ 'block_padding_left' => 0,
562
+ 'block_background' => '',
563
+ 'block_background_2' => '',
564
+ 'block_width' => 600,
565
+ 'block_align' => 'center'
566
+ );
567
+
568
+ $options = array_merge($common_defaults, $options);
569
+
570
+ // Obsolete
571
+ $content = str_replace('{width}', $width, $content);
572
+
573
+ $content = $this->inline_css($content, true);
574
+
575
+ // CSS driven by the block
576
+ // Requited for the server side parsing and rendering
577
+ $options['block_id'] = $block_id;
578
+
579
+ $options['block_padding_top'] = (int) str_replace('px', '', $options['block_padding_top']);
580
+ $options['block_padding_bottom'] = (int) str_replace('px', '', $options['block_padding_bottom']);
581
+ $options['block_padding_right'] = (int) str_replace('px', '', $options['block_padding_right']);
582
+ $options['block_padding_left'] = (int) str_replace('px', '', $options['block_padding_left']);
583
+
584
+ $block_background = empty($options['block_background']) ? $global_block_background : $options['block_background'];
585
+
586
+ // Internal TD wrapper
587
+ $style = 'text-align: center; ';
588
+ $style .= 'width: 100% !important; ';
589
+ $style .= 'line-height: normal !important; ';
590
+ $style .= 'letter-spacing: normal; ';
591
+ $style .= 'padding-top: ' . $options['block_padding_top'] . 'px; ';
592
+ $style .= 'padding-left: ' . $options['block_padding_left'] . 'px; ';
593
+ $style .= 'padding-right: ' . $options['block_padding_right'] . 'px; ';
594
+ $style .= 'padding-bottom: ' . $options['block_padding_bottom'] . 'px; ';
595
+ $style .= 'background-color: ' . $block_background . ';';
596
+
597
+ if (isset($options['block_background_gradient'])) {
598
+ $style .= 'background: linear-gradient(180deg, ' . $block_background . ' 0%, ' . $options['block_background_2'] . ' 100%);';
599
+ }
600
+
601
+ $data = $this->options_encode($options);
602
+ // First time block creation wrapper
603
+ if ($wrapper) {
604
+ echo '<table border="0" cellpadding="0" cellspacing="0" align="center" width="100%" style="border-collapse: collapse; width: 100%;" class="tnpc-row tnpc-row-block" data-id="', esc_attr($block_id), '">', "\n";
605
+ echo "<tr>";
606
+ echo '<td align="center" style="padding: 0;" class="edit-block">', "\n";
607
+ }
608
+
609
+ // Container that fixes the width and makes the block responsive
610
+ echo $this->get_outlook_wrapper_open($options['block_width']);
611
+
612
+ echo '<table type="options" data-json="', esc_attr($data), '" class="tnpc-block-content" border="0" cellpadding="0" align="center" cellspacing="0" width="100%" style="width: 100%!important; max-width: ', $options['block_width'], 'px!important">', "\n";
613
+ echo "<tr>";
614
+ echo '<td align="', esc_attr($options['block_align']), '" style="', $style, '" bgcolor="', $block_background, '" width="100%">';
615
+
616
+ //echo "<!-- block generated content -->\n";
617
+ echo trim($content);
618
+ //echo "\n<!-- /block generated content -->\n";
619
+
620
+ echo "</td></tr></table>";
621
+ echo $this->get_outlook_wrapper_close();
622
+
623
+ // First time block creation wrapper
624
+ if ($wrapper) {
625
+ echo "</td></tr></table>";
626
+ }
627
+
628
+ return $out;
629
+ }
630
+
631
+ /**
632
+ * Ajax call to render a block with a new set of options after the settings popup
633
+ * has been saved.
634
+ *
635
+ * @param type $block_id
636
+ * @param type $wrapper
637
+ */
638
+ function tnpc_render_callback() {
639
+ if (!check_ajax_referer('save')) {
640
+ $this->dienow('Expired request');
641
+ }
642
+
643
+ $block_id = $_POST['id'];
644
+ $wrapper = isset($_POST['full']);
645
+ $options = $this->restore_options_from_request();
646
+
647
+ $this->render_block($block_id, $wrapper, $options, [], $_POST['composer']);
648
+ wp_die();
649
+ }
650
+
651
+ function hook_wp_ajax_tnpc_regenerate_email() {
652
+
653
+ $content = stripslashes($_POST['content']);
654
+ $global_options = $_POST['composer'];
655
+
656
+ $regenerated_content = $this->regenerate_email_blocks($content, $global_options);
657
+
658
+ wp_send_json_success([
659
+ 'content' => $regenerated_content,
660
+ 'message' => __('Successfully updated', 'newsletter')
661
+ ]);
662
+ }
663
+
664
+ private function regenerate_email_blocks($content, $global_options) {
665
+
666
+ $raw_block_options = $this->extract_encoded_blocks_options($content);
667
+
668
+ $regenerated_content = '';
669
+
670
+ foreach ($raw_block_options as $raw_block_option) {
671
+
672
+ /* $a = html_entity_decode( $raw_block_option, ENT_QUOTES, 'UTF-8' );
673
+ $block_options = $this->options_decode( $a ); */
674
+
675
+ $block_options = $this->options_decode($raw_block_option);
676
+
677
+ $block = $this->get_block($block_options['block_id']);
678
+ if (!$block) {
679
+ $this->logger->debug('Unable to load the block ' . $block_options['block_id']);
680
+ }
681
+
682
+ ob_start();
683
+ $this->render_block($block_options['block_id'], true, $block_options, [], $global_options);
684
+ $block_html = ob_get_clean();
685
+
686
+ $regenerated_content .= $block_html;
687
+ }
688
+
689
+ return $regenerated_content;
690
+ }
691
+
692
+ /**
693
+ * @param string $html_email_content Email html content
694
+ *
695
+ * @return string[] Encoded options of email blocks
696
+ */
697
+ private function extract_encoded_blocks_options($html_email_content) {
698
+
699
+ preg_match_all('/data-json="(.*?)"/m', $html_email_content, $raw_block_options, PREG_PATTERN_ORDER);
700
+
701
+ return $raw_block_options[1];
702
+ }
703
+
704
+ function tnpc_preview_callback() {
705
+ $email = Newsletter::instance()->get_email($_REQUEST['id'], ARRAY_A);
706
+
707
+ if (empty($email)) {
708
+ echo 'Wrong email identifier';
709
+ return;
710
+ }
711
+
712
+ echo $email['message'];
713
+
714
+ wp_die();
715
+ }
716
+
717
+ function tnpc_css_callback() {
718
+ include NEWSLETTER_DIR . '/emails/tnp-composer/css/newsletter.css';
719
+ wp_die();
720
+ }
721
+
722
+ /** Returns the correct admin page to edit the newsletter with the correct editor. */
723
+ function get_editor_url($email_id, $editor_type) {
724
+ switch ($editor_type) {
725
+ case NewsletterEmails::EDITOR_COMPOSER:
726
+ return admin_url("admin.php") . '?page=newsletter_emails_composer&id=' . $email_id;
727
+ case NewsletterEmails::EDITOR_HTML:
728
+ return admin_url("admin.php") . '?page=newsletter_emails_editorhtml&id=' . $email_id;
729
+ case NewsletterEmails::EDITOR_TINYMCE:
730
+ return admin_url("admin.php") . '?page=newsletter_emails_editortinymce&id=' . $email_id;
731
+ }
732
+ }
733
+
734
+ /**
735
+ * Returns the button linked to the correct "edit" page for the passed newsletter. The edit page can be an editor
736
+ * or the targeting page (it depends on newsletter status).
737
+ *
738
+ * @param TNP_Email $email
739
+ */
740
+ function get_edit_button($email, $only_icon = false) {
741
+
742
+ $editor_type = $this->get_editor_type($email);
743
+ if ($email->status == 'new') {
744
+ $edit_url = $this->get_editor_url($email->id, $editor_type);
745
+ } else {
746
+ $edit_url = 'admin.php?page=newsletter_emails_edit&id=' . $email->id;
747
+ }
748
+ switch ($editor_type) {
749
+ case NewsletterEmails::EDITOR_COMPOSER:
750
+ $icon_class = 'th-large';
751
+ break;
752
+ case NewsletterEmails::EDITOR_HTML:
753
+ $icon_class = 'code';
754
+ break;
755
+ default:
756
+ $icon_class = 'edit';
757
+ break;
758
+ }
759
+ if ($only_icon) {
760
+ return '<a class="button-primary" href="' . $edit_url . '" title="' . esc_attr__('Edit', 'newsletter') . '">' .
761
+ '<i class="fas fa-' . $icon_class . '"></i></a>';
762
+ } else {
763
+ return '<a class="button-primary" href="' . $edit_url . '" title="' . esc_attr__('Edit', 'newsletter') . '">' .
764
+ '<i class="fas fa-' . $icon_class . '"></i> ' . __('Edit', 'newsletter') . '</a>';
765
+ }
766
+ }
767
+
768
+ /** Returns the correct editor type for the provided newsletter. Contains backward compatibility code. */
769
+ function get_editor_type($email) {
770
+ $email = (object) $email;
771
+ $editor_type = $email->editor;
772
+
773
+ // Backward compatibility
774
+ $email_options = maybe_unserialize($email->options);
775
+ if (isset($email_options['composer'])) {
776
+ $editor_type = NewsletterEmails::EDITOR_COMPOSER;
777
+ }
778
+ // End backward compatibility
779
+
780
+ return $editor_type;
781
+ }
782
+
783
+ /**
784
+ *
785
+ * @param type $action
786
+ * @param type $user
787
+ * @param type $email
788
+ * @return type
789
+ * @global wpdb $wpdb
790
+ */
791
+ function hook_newsletter_action($action, $user, $email) {
792
+ global $wpdb;
793
+
794
+ switch ($action) {
795
+ case 'v':
796
+ case 'view':
797
+ $id = $_GET['id'];
798
+ if ($id == 'last') {
799
+ $email = $wpdb->get_row("select * from " . NEWSLETTER_EMAILS_TABLE . " where private=0 and type='message' and status='sent' order by send_on desc limit 1");
800
+ } else {
801
+ $email = $this->get_email($_GET['id']);
802
+ }
803
+ if (empty($email)) {
804
+ header("HTTP/1.0 404 Not Found");
805
+ die('Email not found');
806
+ }
807
+
808
+ if (!Newsletter::instance()->is_allowed()) {
809
+
810
+ if ($email->status == 'new') {
811
+ header("HTTP/1.0 404 Not Found");
812
+ die('Not sent yet');
813
+ }
814
+
815
+ if ($email->private == 1) {
816
+ if (!$user) {
817
+ header("HTTP/1.0 404 Not Found");
818
+ die('No available for online view');
819
+ }
820
+ $sent = $wpdb->get_row($wpdb->prepare("select * from " . NEWSLETTER_SENT_TABLE . " where email_id=%d and user_id=%d limit 1", $email->id, $user->id));
821
+ if (!$sent) {
822
+ header("HTTP/1.0 404 Not Found");
823
+ die('No available for online view');
824
+ }
825
+ }
826
+ }
827
+
828
+
829
+ header('Content-Type: text/html;charset=UTF-8');
830
+ header('X-Robots-Tag: noindex,nofollow,noarchive');
831
+ header('Cache-Control: no-cache,no-store,private');
832
+
833
+ echo $this->replace($email->message, $user, $email);
834
+
835
+ die();
836
+ break;
837
+
838
+ case 'emails-css':
839
+ $email_id = (int) $_GET['id'];
840
+
841
+ $body = Newsletter::instance()->get_email_field($email_id, 'message');
842
+
843
+ $x = strpos($body, '<style');
844
+ if ($x === false)
845
+ return;
846
+
847
+ $x = strpos($body, '>', $x);
848
+ $y = strpos($body, '</style>');
849
+
850
+ header('Content-Type: text/css;charset=UTF-8');
851
+
852
+ echo substr($body, $x + 1, $y - $x - 1);
853
+
854
+ die();
855
+ break;
856
+
857
+ case 'emails-composer-css':
858
+ header('Cache: no-cache');
859
+ header('Content-Type: text/css');
860
+ echo $this->get_composer_css();
861
+ die();
862
+ break;
863
+
864
+ case 'emails-preview':
865
+ if (!Newsletter::instance()->is_allowed()) {
866
+ die('Not enough privileges');
867
+ }
868
+
869
+ if (!check_admin_referer('view')) {
870
+ die();
871
+ }
872
+
873
+ $theme_id = $_GET['id'];
874
+ $theme = $this->themes->get_theme($theme_id);
875
+
876
+ // Used by theme code
877
+ $theme_options = $this->themes->get_options($theme_id);
878
+
879
+ $theme_url = $theme['url'];
880
+
881
+ header('Content-Type: text/html;charset=UTF-8');
882
+
883
+ include $theme['dir'] . '/theme.php';
884
+
885
+ die();
886
+ break;
887
+
888
+ case 'emails-preview-text':
889
+ header('Content-Type: text/plain;charset=UTF-8');
890
+ if (!Newsletter::instance()->is_allowed()) {
891
+ die('Not enough privileges');
892
+ }
893
+
894
+ if (!check_admin_referer('view')) {
895
+ die();
896
+ }
897
+
898
+ // Used by theme code
899
+ $theme_options = $this->get_current_theme_options();
900
+
901
+ $file = include $theme['dir'] . '/theme-text.php';
902
+
903
+ if (is_file($file)) {
904
+ include $file;
905
+ }
906
+
907
+ die();
908
+ break;
909
+
910
+
911
+ case 'emails-create':
912
+ // Newsletter from themes are created on frontend context because sometime WP themes change the way the content,
913
+ // excerpt, thumbnail are extracted.
914
+ if (!Newsletter::instance()->is_allowed()) {
915
+ die('Not enough privileges');
916
+ }
917
+
918
+ require_once NEWSLETTER_INCLUDES_DIR . '/controls.php';
919
+ $controls = new NewsletterControls();
920
+
921
+ if (!$controls->is_action('create')) {
922
+ die('Wrong call');
923
+ }
924
+
925
+ $theme_id = $controls->data['id'];
926
+ $theme = $this->themes->get_theme($theme_id);
927
+
928
+ if (!$theme) {
929
+ die('invalid theme');
930
+ }
931
+
932
+ $this->themes->save_options($theme_id, $controls->data);
933
+
934
+ $email = array();
935
+ $email['status'] = 'new';
936
+ $email['subject'] = ''; //__('Here the email subject', 'newsletter');
937
+ $email['track'] = 1;
938
+ $email['send_on'] = time();
939
+ $email['editor'] = NewsletterEmails::EDITOR_TINYMCE;
940
+ $email['type'] = 'message';
941
+
942
+ $theme_options = $this->themes->get_options($theme_id);
943
+
944
+ $theme_url = $theme['url'];
945
+ $theme_subject = '';
946
+
947
+ ob_start();
948
+ include $theme['dir'] . '/theme.php';
949
+ $email['message'] = ob_get_clean();
950
+
951
+
952
+ if (!empty($theme_subject)) {
953
+ $email['subject'] = $theme_subject;
954
+ }
955
+
956
+ if (file_exists($theme['dir'] . '/theme-text.php')) {
957
+ ob_start();
958
+ include $theme['dir'] . '/theme-text.php';
959
+ $email['message_text'] = ob_get_clean();
960
+ } else {
961
+ $email['message_text'] = 'You need a modern email client to read this email. Read it online: {email_url}.';
962
+ }
963
+
964
+ $email = $this->save_email($email);
965
+
966
+ $edit_url = $this->get_editor_url($email->id, $email->editor);
967
+
968
+ header('Location: ' . $edit_url);
969
+
970
+ die();
971
+ break;
972
+ }
973
+ }
974
+
975
+ function admin_menu() {
976
+ $this->add_menu_page('index', 'Newsletters');
977
+ $this->add_admin_page('list', 'Email List');
978
+ $this->add_admin_page('new', 'Email New');
979
+ $this->add_admin_page('edit', 'Email Edit');
980
+ $this->add_admin_page('theme', 'Email Themes');
981
+ $this->add_admin_page('composer', 'The Composer');
982
+ $this->add_admin_page('editorhtml', 'HTML Editor');
983
+ $this->add_admin_page('editortinymce', 'TinyMCE Editor');
984
+ }
985
+
986
+ /**
987
+ * Builds a block data structure starting from the folder containing the block
988
+ * files.
989
+ *
990
+ * @param string $dir
991
+ * @return array | WP_Error
992
+ */
993
+ function build_block($dir) {
994
+ $dir = realpath($dir);
995
+ $dir = wp_normalize_path($dir);
996
+ $full_file = $dir . '/block.php';
997
+ if (!is_file($full_file)) {
998
+ return new WP_Error('1', 'Missing block.php file in ' . $dir);
999
+ }
1000
+
1001
+ $relative_dir = substr($dir, strlen(WP_CONTENT_DIR));
1002
+ $file = basename($dir);
1003
+
1004
+ $data = get_file_data($full_file, ['name' => 'Name', 'section' => 'Section', 'description' => 'Description', 'type' => 'Type']);
1005
+ $defaults = ['section' => 'content', 'name' => ucfirst($file), 'descritpion' => '', 'icon' => plugins_url('newsletter') . '/admin/images/block-icon.png'];
1006
+ $data = array_merge($defaults, $data);
1007
+
1008
+ if (is_file($dir . '/icon.png')) {
1009
+ $data['icon'] = content_url($relative_dir . '/icon.png');
1010
+ }
1011
+
1012
+ $data['id'] = sanitize_key($file);
1013
+
1014
+ // Absolute path of the block files
1015
+ $data['dir'] = $dir;
1016
+ $data['url'] = content_url($relative_dir);
1017
+
1018
+ return $data;
1019
+ }
1020
+
1021
+ /**
1022
+ *
1023
+ * @param type $dir
1024
+ * @return type
1025
+ */
1026
+ function scan_blocks_dir($dir) {
1027
+ $dir = realpath($dir);
1028
+ if (!$dir) {
1029
+ return [];
1030
+ }
1031
+ $dir = wp_normalize_path($dir);
1032
+
1033
+ $list = [];
1034
+ $handle = opendir($dir);
1035
+ while ($file = readdir($handle)) {
1036
+
1037
+ $data = $this->build_block($dir . '/' . $file);
1038
+
1039
+ if (is_wp_error($data)) {
1040
+ $this->logger->error($data);
1041
+ continue;
1042
+ }
1043
+ $list[$data['id']] = $data;
1044
+ }
1045
+ closedir($handle);
1046
+ return $list;
1047
+ }
1048
+
1049
+ /**
1050
+ * Array of arrays with every registered block and legacy block converted to the new
1051
+ * format.
1052
+ *
1053
+ * @return array
1054
+ */
1055
+ function get_blocks() {
1056
+
1057
+ if (!is_null($this->blocks)) {
1058
+ return $this->blocks;
1059
+ }
1060
+
1061
+ $this->blocks = $this->scan_blocks_dir(__DIR__ . '/blocks');
1062
+
1063
+ $extended = $this->scan_blocks_dir(WP_CONTENT_DIR . '/extensions/newsletter/blocks');
1064
+
1065
+ $this->blocks = array_merge($extended, $this->blocks);
1066
+
1067
+ $dirs = apply_filters('newsletter_blocks_dir', array());
1068
+
1069
+ //$this->logger->debug('Block dirs:');
1070
+ //$this->logger->debug($dirs);
1071
+
1072
+ foreach ($dirs as $dir) {
1073
+ $list = $this->scan_blocks_dir($dir);
1074
+ $this->blocks = array_merge($list, $this->blocks);
1075
+ }
1076
+
1077
+ do_action('newsletter_register_blocks');
1078
+
1079
+ foreach (TNP_Composer::$block_dirs as $dir) {
1080
+ $block = $this->build_block($dir);
1081
+ if (is_wp_error($block)) {
1082
+ $this->logger->error($block);
1083
+ continue;
1084
+ }
1085
+ if (!isset($this->blocks[$block['id']])) {
1086
+ $this->blocks[$block['id']] = $block;
1087
+ } else {
1088
+ $this->logger->error('The block "' . $block['id'] . '" has already been registered');
1089
+ }
1090
+ }
1091
+
1092
+ $this->blocks = array_reverse($this->blocks);
1093
+ return $this->blocks;
1094
+ }
1095
+
1096
+ /**
1097
+ * Return a single block (associative array) checking for legacy ID as well.
1098
+ *
1099
+ * @param string $id
1100
+ * @return array
1101
+ */
1102
+ function get_block($id) {
1103
+ switch ($id) {
1104
+ case 'content-03-text.block':
1105
+ $id = 'text';
1106
+ break;
1107
+ case 'footer-03-social.block':
1108
+ $id = 'social';
1109
+ break;
1110
+ case 'footer-02-canspam.block':
1111
+ $id = 'canspam';
1112
+ break;
1113
+ case 'content-05-image.block':
1114
+ $id = 'image';
1115
+ break;
1116
+ case 'header-01-header.block':
1117
+ $id = 'header';
1118
+ break;
1119
+ case 'footer-01-footer.block':
1120
+ $id = 'footer';
1121
+ break;
1122
+ case 'content-02-heading.block':
1123
+ $id = 'heading';
1124
+ break;
1125
+ case 'content-07-twocols.block':
1126
+ case 'content-06-posts.block':
1127
+ $id = 'posts';
1128
+ break;
1129
+ case 'content-04-cta.block':
1130
+ $id = 'cta';
1131
+ break;
1132
+ case 'content-01-hero.block':
1133
+ $id = 'hero';
1134
+ break;
1135
+ // case 'content-02-heading.block': $id = '/plugins/newsletter/emails/blocks/heading';
1136
+ // break;
1137
+ }
1138
+
1139
+ // Conversion for old full path ID
1140
+ $id = sanitize_key(basename($id));
1141
+
1142
+ // TODO: Correct id for compatibility
1143
+ $blocks = $this->get_blocks();
1144
+ if (!isset($blocks[$id])) {
1145
+ return null;
1146
+ }
1147
+ return $blocks[$id];
1148
+ }
1149
+
1150
+ function scan_presets_dir($dir = null) {
1151
+
1152
+ if (is_null($dir)) {
1153
+ $dir = __DIR__ . '/presets';
1154
+ }
1155
+
1156
+ if (!is_dir($dir)) {
1157
+ return array();
1158
+ }
1159
+
1160
+ $handle = opendir($dir);
1161
+ $list = array();
1162
+ $relative_dir = substr($dir, strlen(WP_CONTENT_DIR));
1163
+ while ($file = readdir($handle)) {
1164
+
1165
+ if ($file == '.' || $file == '..')
1166
+ continue;
1167
+
1168
+ // The block unique key, we should find out how to build it, maybe an hash of the (relative) dir?
1169
+ $preset_id = sanitize_key($file);
1170
+
1171
+ $full_file = $dir . '/' . $file . '/preset.json';
1172
+
1173
+ if (!is_file($full_file)) {
1174
+ continue;
1175
+ }
1176
+
1177
+ $icon = content_url($relative_dir . '/' . $file . '/icon.png');
1178
+
1179
+ $list[$preset_id] = $icon;
1180
+ }
1181
+ closedir($handle);
1182
+ return $list;
1183
+ }
1184
+
1185
+ function get_preset_from_file($id, $dir = null) {
1186
+
1187
+ if (is_null($dir)) {
1188
+ $dir = __DIR__ . '/presets';
1189
+ }
1190
+
1191
+ $id = $this->sanitize_file_name($id);
1192
+
1193
+ if (!is_dir($dir . '/' . $id) || !in_array($id, self::$PRESETS_LIST)) {
1194
+ return array();
1195
+ }
1196
+
1197
+ $json_content = file_get_contents("$dir/$id/preset.json");
1198
+ $json_content = str_replace("{placeholder_base_url}", plugins_url('newsletter') . '/emails/presets', $json_content);
1199
+ $json = json_decode($json_content);
1200
+ $json->icon = NEWSLETTER_URL . "/emails/presets/$id/icon.png?ver=2";
1201
+
1202
+ return $json;
1203
+ }
1204
+
1205
+ function get_composer_css() {
1206
+ $css = file_get_contents(__DIR__ . '/tnp-composer/css/newsletter.css');
1207
+ $css .= "\n\n";
1208
+ $css .= file_get_contents(__DIR__ . '/tnp-composer/css/backend.css');
1209
+ $blocks = $this->get_blocks();
1210
+ foreach ($blocks as $block) {
1211
+ if (!file_exists($block['dir'] . '/style.css')) {
1212
+ continue;
1213
+ }
1214
+ $css .= "\n\n";
1215
+ $css .= "/* " . $block['name'] . " */\n";
1216
+ $css .= file_get_contents($block['dir'] . '/style.css');
1217
+ }
1218
+ return $css;
1219
+ }
1220
+
1221
+ /**
1222
+ * Send an email to the test subscribers.
1223
+ *
1224
+ * @param TNP_Email $email Could be any object with the TNP_Email attributes
1225
+ * @param NewsletterControls $controls
1226
+ */
1227
+ function send_test_email($email, $controls) {
1228
+ if (!$email) {
1229
+ $controls->errors = __('Newsletter should be saved before send a test', 'newsletter');
1230
+ return;
1231
+ }
1232
+
1233
+ $original_subject = $email->subject;
1234
+ $this->set_test_subject_to($email);
1235
+
1236
+ $users = NewsletterUsers::instance()->get_test_users();
1237
+ if (count($users) == 0) {
1238
+ $controls->errors = '' . __('There are no test subscribers to send to', 'newsletter') .
1239
+ '. <a href="https://www.thenewsletterplugin.com/plugins/newsletter/subscribers-module#test" target="_blank"><strong>' .
1240
+ __('Read more', 'newsletter') . '</strong></a>.';
1241
+ } else {
1242
+ $r = Newsletter::instance()->send($email, $users, true);
1243
+ $emails = array();
1244
+ foreach ($users as $user) {
1245
+ $emails[] = '<a href="admin.php?page=newsletter_users_edit&id=' . $user->id . '" target="_blank">' . $user->email . '</a>';
1246
+ }
1247
+ if (is_wp_error($r)) {
1248
+ $controls->errors = 'Something went wrong. Check the error logs on status page.<br>';
1249
+ $controls->errors .= __('Test subscribers:', 'newsletter');
1250
+ $controls->errors .= ' ' . implode(', ', $emails);
1251
+ $controls->errors .= '<br>';
1252
+ $controls->errors .= '<strong>' . esc_html($r->get_error_message()) . '</strong><br>';
1253
+ $controls->errors .= '<a href="https://www.thenewsletterplugin.com/documentation/email-sending-issues" target="_blank"><strong>' . __('Read more about delivery issues', 'newsletter') . '</strong></a>.';
1254
+ } else {
1255
+ $controls->messages = __('Test subscribers:', 'newsletter');
1256
+
1257
+ $controls->messages .= ' ' . implode(', ', $emails);
1258
+ $controls->messages .= '.<br>';
1259
+ $controls->messages .= '<a href="https://www.thenewsletterplugin.com/documentation/subscribers#test" target="_blank"><strong>' .
1260
+ __('Read more about test subscribers', 'newsletter') . '</strong></a>.<br>';
1261
+ $controls->messages .= '<a href="https://www.thenewsletterplugin.com/documentation/email-sending-issues" target="_blank"><strong>' . __('Read more about delivery issues', 'newsletter') . '</strong></a>.';
1262
+ }
1263
+ }
1264
+ $email->subject = $original_subject;
1265
+ }
1266
+
1267
+ /**
1268
+ * Send an email to the test subscribers.
1269
+ *
1270
+ * @param TNP_Email $email Could be any object with the TNP_Email attributes
1271
+ * @param string $email_address
1272
+ *
1273
+ * @throws Exception
1274
+ */
1275
+ function send_test_newsletter_to_email_address( $email, $email_address ) {
1276
+
1277
+ if ( ! $email ) {
1278
+ throw new Exception( __( 'Newsletter should be saved before send a test', 'newsletter' ) );
1279
+ }
1280
+
1281
+ $this->set_test_subject_to( $email );
1282
+
1283
+ $dummy_subscriber = $this->make_dummy_subscriber();
1284
+ $dummy_subscriber->email = $email_address;
1285
+
1286
+ $result = Newsletter::instance()->send( $email, [ $dummy_subscriber ], true );
1287
+
1288
+ $email = '<a href="admin.php?page=newsletter_users_edit&id=' . $dummy_subscriber->id . '" target="_blank">' . $dummy_subscriber->email . '</a>';
1289
+
1290
+ if ( is_wp_error( $result ) ) {
1291
+ $error_message = 'Something went wrong. Check the error logs on status page.<br>';
1292
+ $error_message .= __( 'Test subscribers:', 'newsletter' );
1293
+ $error_message .= ' ' . $email;
1294
+ $error_message .= '<br>';
1295
+ $error_message .= '<strong>' . esc_html( $result->get_error_message() ) . '</strong><br>';
1296
+ $error_message .= '<a href="https://www.thenewsletterplugin.com/documentation/email-sending-issues" target="_blank"><strong>' . __( 'Read more about delivery issues', 'newsletter' ) . '</strong></a>.';
1297
+ throw new Exception( $error_message );
1298
+ }
1299
+
1300
+ $messages = __( 'Test subscribers:', 'newsletter' );
1301
+
1302
+ $messages .= ' ' . $email;
1303
+ $messages .= '.<br>';
1304
+ $messages .= '<a href="https://www.thenewsletterplugin.com/documentation/subscribers#test" target="_blank"><strong>' .
1305
+ __( 'Read more about test subscribers', 'newsletter' ) . '</strong></a>.<br>';
1306
+ $messages .= '<a href="https://www.thenewsletterplugin.com/documentation/email-sending-issues" target="_blank"><strong>' . __( 'Read more about delivery issues', 'newsletter' ) . '</strong></a>.';
1307
+
1308
+ return $messages;
1309
+ }
1310
+
1311
+ private function set_test_subject_to($email) {
1312
+ if ( $email->subject == '' ) {
1313
+ $email->subject = '[TEST] Dummy subject, it was empty (remember to set it)';
1314
+ } else {
1315
+ $email->subject = $email->subject . ' (TEST)';
1316
+ }
1317
+ }
1318
+
1319
+ private function make_dummy_subscriber() {
1320
+ $dummy_user = new TNP_User();
1321
+ $dummy_user->id = 0;
1322
+ $dummy_user->email = 'john.doe@example.org';
1323
+ $dummy_user->name = 'John';
1324
+ $dummy_user->surname = 'Doe';
1325
+ $dummy_user->sex = 'n';
1326
+ $dummy_user->language = '';
1327
+ $dummy_user->ip = '';
1328
+
1329
+ for ( $i = 1; $i <= NEWSLETTER_PROFILE_MAX; $i ++ ) {
1330
+ $profile_key = "profile_$i";
1331
+ $dummy_user->$profile_key = '';
1332
+ }
1333
+
1334
+ return $dummy_user;
1335
+ }
1336
+
1337
+ function restore_options_from_request() {
1338
+
1339
+ require_once NEWSLETTER_INCLUDES_DIR . '/controls.php';
1340
+ $controls = new NewsletterControls();
1341
+ $options = $controls->data;
1342
+
1343
+ if (isset($_POST['options']) && is_array($_POST['options'])) {
1344
+ // Get all block options
1345
+ //$options = stripslashes_deep($_POST['options']);
1346
+
1347
+ // Deserialize inline edits when
1348
+ // render is preformed on saving block options
1349
+ if (isset($options['inline_edits']) && !is_array($options['inline_edits'])) {
1350
+ $options['inline_edits'] = $this->options_decode($options['inline_edits']);
1351
+ }
1352
+
1353
+ // Restore inline edits from data-json
1354
+ // coming from inline editing
1355
+ // and merge with current inline edit
1356
+ if (isset($_POST['encoded_options'])) {
1357
+ $decoded_options = $this->options_decode($_POST['encoded_options']);
1358
+
1359
+ $to_merge_inline_edits = [];
1360
+
1361
+ if (isset($decoded_options['inline_edits'])) {
1362
+ foreach ($decoded_options['inline_edits'] as $decoded_inline_edit) {
1363
+ $to_merge_inline_edits[$decoded_inline_edit['post_id'] . $decoded_inline_edit['type']] = $decoded_inline_edit;
1364
+ }
1365
+ }
1366
+
1367
+ //Overwrite with new edited content
1368
+ if (isset($options['inline_edits'])) {
1369
+ foreach ($options['inline_edits'] as $inline_edit) {
1370
+ $to_merge_inline_edits[$inline_edit['post_id'] . $inline_edit['type']] = $inline_edit;
1371
+ }
1372
+ }
1373
+
1374
+ $options['inline_edits'] = array_values($to_merge_inline_edits);
1375
+ $options = array_merge($decoded_options, $options);
1376
+ }
1377
+
1378
+ return $options;
1379
+ }
1380
+
1381
+ return array();
1382
+ }
1383
+
1384
+ public function hook_wp_ajax_tnpc_delete_preset() {
1385
+
1386
+ if (!wp_verify_nonce($_POST['_wpnonce'], 'preset')) {
1387
+ wp_send_json_error('Expired request');
1388
+ }
1389
+
1390
+ $preset_id = (int) $_REQUEST['presetId'];
1391
+
1392
+ $newsletter = Newsletter::instance();
1393
+
1394
+ if ($preset_id > 0) {
1395
+ $preset = $newsletter->get_email($preset_id);
1396
+
1397
+ if ($preset && $preset->type === self::PRESET_EMAIL_TYPE) {
1398
+ Newsletter::instance()->delete_email($preset_id);
1399
+ wp_send_json_success();
1400
+ } else {
1401
+ wp_send_json_error(__('Is not a preset!', 'newsletter'));
1402
+ }
1403
+ } else {
1404
+ wp_send_json_error();
1405
+ }
1406
+ }
1407
+
1408
+ }
1409
+
1410
+ NewsletterEmails::instance();
includes/composer.php CHANGED
@@ -642,6 +642,22 @@ class TNP_Composer {
642
  }
643
  return $style;
644
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
645
 
646
  }
647
 
642
  }
643
  return $style;
644
  }
645
+
646
+ static function get_button_options($options, $prefix, $composer) {
647
+ $button_options = [];
648
+ $scale = 1;
649
+ $button_options['button_font_family'] = empty($options[$prefix . '_font_family']) ? $composer['button_font_family'] : $options[$prefix . '_font_family'];
650
+ $button_options['button_font_size'] = empty($options[$prefix . '_font_size']) ? round($composer['button_font_size'] * $scale) : $options[$prefix . '_font_size'];
651
+ $button_options['button_font_color'] = empty($options[$prefix . '_font_color']) ? $composer['button_font_color'] : $options[$prefix . '_font_color'];
652
+ $button_options['button_font_weight'] = empty($options[$prefix . '_font_weight']) ? $composer['button_font_weight'] : $options[$prefix . '_font_weight'];
653
+ $button_options['button_background'] = empty($options[$prefix . '_background']) ? $composer['button_background_color'] : $options[$prefix . '_background'];
654
+ $button_options['button_align'] = empty($options[$prefix . '_align']) ? 'center' : $options[$prefix . '_align'];
655
+ $button_options['button_width'] = empty($options[$prefix . '_width']) ? 'center' : $options[$prefix . '_width'];
656
+ $button_options['button_url'] = empty($options[$prefix . '_url']) ? '#' : $options[$prefix . '_url'];
657
+ $button_options['button_label'] = empty($options[$prefix . '_label']) ? '' : $options[$prefix . '_label'];
658
+
659
+ return $button_options;
660
+ }
661
 
662
  }
663
 
includes/controls.php CHANGED
@@ -839,13 +839,20 @@ class NewsletterControls {
839
  echo 'value="', esc_attr($value), '">';
840
  }
841
 
842
- function text_email($name, $size = 40) {
 
 
 
 
 
843
  $value = $this->get_value($name);
844
  echo '<input name="options[' . esc_attr($name) . ']" type="email" placeholder="';
845
- echo esc_attr__('Valid email address', 'newsletter');
846
- echo '" size="' . esc_attr($size) . '" value="';
847
- echo esc_attr($value);
848
- echo '">';
 
 
849
  }
850
 
851
  function text_url($name, $size = 40) {
@@ -1044,7 +1051,7 @@ class NewsletterControls {
1044
  echo '<button class="button-primary" onclick="this.form.act.value=\'' . esc_attr($action) . '\'; return true;"/>', $label, '</button>';
1045
  }
1046
  }
1047
-
1048
  function button_confirm($action, $label, $message = true, $data = '') {
1049
  $this->btn($action, $label, ['data' => $data, 'confirm' => $message]);
1050
  }
839
  echo 'value="', esc_attr($value), '">';
840
  }
841
 
842
+ function text_email($name, $attrs = []) {
843
+ if (is_numeric($attrs)) {
844
+ $attrs = ['size' => $attrs];
845
+ }
846
+ $attrs = array_merge(['placeholder' => __('Valid email address', 'newsletter'), 'size' => 40, 'required' => false], $attrs);
847
+
848
  $value = $this->get_value($name);
849
  echo '<input name="options[' . esc_attr($name) . ']" type="email" placeholder="';
850
+ echo esc_attr($attrs['placeholder']);
851
+ echo '" size="', esc_attr($attrs['size']), '" value="', esc_attr($value) , '"';
852
+ if ($attrs['required']) {
853
+ echo ' required';
854
+ }
855
+ echo '>';
856
  }
857
 
858
  function text_url($name, $size = 40) {
1051
  echo '<button class="button-primary" onclick="this.form.act.value=\'' . esc_attr($action) . '\'; return true;"/>', $label, '</button>';
1052
  }
1053
  }
1054
+
1055
  function button_confirm($action, $label, $message = true, $data = '') {
1056
  $this->btn($action, $label, ['data' => $data, 'confirm' => $message]);
1057
  }
includes/helper.php CHANGED
@@ -48,11 +48,27 @@ function tnp_post_thumbnail_src($post, $size = 'thumbnail', $alternative = '') {
48
  *
49
  * @return string
50
  */
51
- function tnp_post_excerpt($post, $length = 30) {
52
- $excerpt = tnp_delete_all_shordcodes_tags(get_the_excerpt($post->ID));
53
- $excerpt = wp_trim_words($excerpt, $length);
 
 
 
54
  $excerpt = str_replace('&nbsp;', '', $excerpt);
55
 
 
 
 
 
 
 
 
 
 
 
 
 
 
56
  return $excerpt;
57
  }
58
 
@@ -124,13 +140,13 @@ function tnp_media_resize($media_id, $size) {
124
  $absolute_file = $uploads['basedir'] . '/' . $relative_file;
125
  // Relative and absolute name of the thumbnail.
126
  $pathinfo = pathinfo($relative_file);
127
-
128
  // We don't know why, but on some systems files with non-ascii characters loose the file name (grrr...)
129
  if (empty($pathinfo['filename'])) {
130
  $src = wp_get_attachment_image_src($media_id, 'full');
131
  return $src[0];
132
  }
133
-
134
  $relative_thumb = $pathinfo['dirname'] . '/' . $pathinfo['filename'] . '-' . $width . 'x' .
135
  $height . ($crop ? '-c' : '') . '.' . $pathinfo['extension'];
136
  $absolute_thumb = $uploads['basedir'] . '/newsletter/thumbnails/' . $relative_thumb;
@@ -260,12 +276,12 @@ function tnp_resize($media_id, $size) {
260
 
261
  // Relative and absolute name of the thumbnail.
262
  $pathinfo = pathinfo($relative_file);
263
-
264
  // We don't know why, but on some systems files with non-ascii characters loose the file name (grrr...)
265
  if (empty($pathinfo['filename'])) {
266
  return _tnp_get_default_media($media_id, $size);
267
  }
268
-
269
  $relative_thumb = $pathinfo['dirname'] . '/' . $pathinfo['filename'] . '-' . $width . 'x' . $height . ($crop ? '-c' : '') . '.' . $pathinfo['extension'];
270
  $absolute_thumb = $uploads['basedir'] . '/newsletter/thumbnails/' . $relative_thumb;
271
 
@@ -323,12 +339,13 @@ function tnp_resize($media_id, $size) {
323
  }
324
 
325
  function tnp_resize_2x($media_id, $size) {
326
- $size[0] = $size[0] * 2;
327
- $size[1] = $size[1] * 2;
328
- $media = tnp_resize($media_id, $size);
329
- if (!$media) return $media;
330
- $media->set_width( $size[0] / 2 );
331
- return $media;
 
332
  }
333
 
334
  /**
@@ -336,13 +353,13 @@ function tnp_resize_2x($media_id, $size) {
336
  *
337
  * @return int
338
  */
339
- function tnp_get_max_height_of( $images ) {
340
- $max_height = 0;
341
- foreach ( $images as $image ) {
342
- $max_height = $image->height > $max_height ? $image->height : $max_height;
343
- }
344
 
345
- return $max_height;
346
  }
347
 
348
  /**
@@ -351,13 +368,13 @@ function tnp_get_max_height_of( $images ) {
351
  *
352
  * @return TNP_Media[]
353
  */
354
- function tnp_resize_product_list_featured_image( $product_list, $size ) {
355
- $images = [];
356
- foreach ( $product_list as $p ) {
357
- $images[ $p->ID ] = tnp_resize_2x( TNP_Composer::get_post_thumbnail_id( $p->ID ), $size );
358
- }
359
 
360
- return $images;
361
  }
362
 
363
  /**
48
  *
49
  * @return string
50
  */
51
+ function tnp_post_excerpt($post, $length = 30, $characters = false) {
52
+ if (!$length) return '';
53
+
54
+ $excerpt = get_the_excerpt($post->ID);
55
+ $excerpt = tnp_delete_all_shordcodes_tags($excerpt);
56
+ $excerpt = trim($excerpt);
57
  $excerpt = str_replace('&nbsp;', '', $excerpt);
58
 
59
+ if ($characters) {
60
+ if (strlen($excerpt) > $length) {
61
+ $excerpt = substr($excerpt, 0, $length);
62
+ $i = strrpos($excerpt, ' ');
63
+ if ($i) {
64
+ $excerpt = substr($excerpt, 0, $i);
65
+ $excerpt .= '&hellip;';
66
+ }
67
+ }
68
+ } else {
69
+ $excerpt = wp_trim_words($excerpt, $length);
70
+ }
71
+
72
  return $excerpt;
73
  }
74
 
140
  $absolute_file = $uploads['basedir'] . '/' . $relative_file;
141
  // Relative and absolute name of the thumbnail.
142
  $pathinfo = pathinfo($relative_file);
143
+
144
  // We don't know why, but on some systems files with non-ascii characters loose the file name (grrr...)
145
  if (empty($pathinfo['filename'])) {
146
  $src = wp_get_attachment_image_src($media_id, 'full');
147
  return $src[0];
148
  }
149
+
150
  $relative_thumb = $pathinfo['dirname'] . '/' . $pathinfo['filename'] . '-' . $width . 'x' .
151
  $height . ($crop ? '-c' : '') . '.' . $pathinfo['extension'];
152
  $absolute_thumb = $uploads['basedir'] . '/newsletter/thumbnails/' . $relative_thumb;
276
 
277
  // Relative and absolute name of the thumbnail.
278
  $pathinfo = pathinfo($relative_file);
279
+
280
  // We don't know why, but on some systems files with non-ascii characters loose the file name (grrr...)
281
  if (empty($pathinfo['filename'])) {
282
  return _tnp_get_default_media($media_id, $size);
283
  }
284
+
285
  $relative_thumb = $pathinfo['dirname'] . '/' . $pathinfo['filename'] . '-' . $width . 'x' . $height . ($crop ? '-c' : '') . '.' . $pathinfo['extension'];
286
  $absolute_thumb = $uploads['basedir'] . '/newsletter/thumbnails/' . $relative_thumb;
287
 
339
  }
340
 
341
  function tnp_resize_2x($media_id, $size) {
342
+ $size[0] = $size[0] * 2;
343
+ $size[1] = $size[1] * 2;
344
+ $media = tnp_resize($media_id, $size);
345
+ if (!$media)
346
+ return $media;
347
+ $media->set_width($size[0] / 2);
348
+ return $media;
349
  }
350
 
351
  /**
353
  *
354
  * @return int
355
  */
356
+ function tnp_get_max_height_of($images) {
357
+ $max_height = 0;
358
+ foreach ($images as $image) {
359
+ $max_height = $image->height > $max_height ? $image->height : $max_height;
360
+ }
361
 
362
+ return $max_height;
363
  }
364
 
365
  /**
368
  *
369
  * @return TNP_Media[]
370
  */
371
+ function tnp_resize_product_list_featured_image($product_list, $size) {
372
+ $images = [];
373
+ foreach ($product_list as $p) {
374
+ $images[$p->ID] = tnp_resize_2x(TNP_Composer::get_post_thumbnail_id($p->ID), $size);
375
+ }
376
 
377
+ return $images;
378
  }
379
 
380
  /**
includes/logger.php CHANGED
@@ -17,13 +17,17 @@ class NewsletterLogger {
17
  var $level;
18
  var $module;
19
  var $file;
 
20
 
21
  function __construct($module) {
22
  $this->module = $module;
23
- if (defined('NEWSLETTER_LOG_LEVEL'))
24
  $this->level = NEWSLETTER_LOG_LEVEL;
25
- else
26
  $this->level = (int) get_option('newsletter_log_level', self::ERROR);
 
 
 
27
 
28
  $secret = get_option('newsletter_logger_secret', '');
29
  if (strlen($secret) < 8) {
17
  var $level;
18
  var $module;
19
  var $file;
20
+ var $is_debug = false;
21
 
22
  function __construct($module) {
23
  $this->module = $module;
24
+ if (defined('NEWSLETTER_LOG_LEVEL')) {
25
  $this->level = NEWSLETTER_LOG_LEVEL;
26
+ } else {
27
  $this->level = (int) get_option('newsletter_log_level', self::ERROR);
28
+ }
29
+
30
+ $this->is_debug = $this->level == self::DEBUG;
31
 
32
  $secret = get_option('newsletter_logger_secret', '');
33
  if (strlen($secret) < 8) {
includes/mailer.php CHANGED
@@ -2,6 +2,30 @@
2
 
3
  use TNP\Mailer\PHPMailerLoader;
4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
  /**
6
  * A basic class able to send one or more TNP_Mailer_Message objects using a
7
  * delivery method (wp-mail(), SMTP, API, ...).
@@ -18,10 +42,18 @@ class NewsletterMailer {
18
  var $options;
19
  private $delta;
20
  protected $batch_size = 1;
 
21
 
22
  public function __construct($name, $options = []) {
23
  $this->name = $name;
24
  $this->options = $options;
 
 
 
 
 
 
 
25
  }
26
 
27
  public function get_name() {
@@ -29,12 +61,16 @@ class NewsletterMailer {
29
  }
30
 
31
  public function get_description() {
32
- return $this->name;
33
  }
34
 
35
  public function get_batch_size() {
36
  return $this->batch_size;
37
  }
 
 
 
 
38
 
39
  function send_with_stats($message) {
40
  $this->delta = microtime(true);
@@ -101,6 +137,12 @@ class NewsletterMailer {
101
  return $last_result;
102
  }
103
 
 
 
 
 
 
 
104
  protected function send_chunk($messages) {
105
  $last_result = true;
106
  foreach ($messages as $message) {
@@ -146,122 +188,26 @@ class NewsletterMailer {
146
  return !is_wp_error($this->send($mailer_message));
147
  }
148
 
 
 
 
 
 
149
  function save_last_run($time) {
150
  update_option($this->prefix . '_last_run', $time);
151
  }
152
 
 
 
 
 
 
153
  function get_last_run() {
154
  return (int) get_option($this->prefix . '_last_run', 0);
155
  }
156
 
157
  }
158
 
159
- /**
160
- * @property string $to
161
- * @property string $to_name
162
- * @property string $subject
163
- * @property string $body
164
- * @property array $headers
165
- * @property string $from
166
- * @property string $from_name
167
- */
168
- class TNP_Mailer_Message {
169
-
170
- var $to_name = '';
171
- var $headers = array();
172
- var $user_id = 0;
173
- var $email_id = 0;
174
- var $error = '';
175
- var $subject = '';
176
- var $body = '';
177
- var $body_text = '';
178
- var $from = '';
179
- var $from_name = '';
180
-
181
- }
182
-
183
- /**
184
- * Wrapper mailer for old addons registering the "mail" method (ultra deprecated).
185
- */
186
- class NewsletterMailMethodWrapper extends NewsletterMailer {
187
-
188
- var $mail_method;
189
-
190
- /**
191
- * The reference to the mail method.
192
- *
193
- * @param callback $callable Must be an array with object and method to call, no other callback formats allowed.
194
- */
195
- function __construct($callable) {
196
- parent::__construct(strtolower(get_class($callable[0])), array());
197
- $this->mail_method = $callable;
198
- }
199
-
200
- function get_description() {
201
- if ($this->mail_method != null) {
202
- return 'Mail method of ' . get_class($this->mail_method[0]);
203
- } else {
204
- return 'Undetectable mailer class';
205
- }
206
- }
207
-
208
- function send($message) {
209
- if ($this->mail_method != null) {
210
- $r = call_user_func($this->mail_method, $message->to, $message->subject, array('html' => $message->body, 'text' => $message->body_text), $message->headers);
211
- if (!$r) {
212
- $message->error = 'Unreported error';
213
- return new WP_Error(self::ERROR_GENERIC, 'Unreported error');
214
- }
215
- } else {
216
- $message->error = 'Mail method not available';
217
- return new WP_Error(self::ERROR_FATAL, 'Mail method not available');
218
- }
219
- return true;
220
- }
221
-
222
- }
223
-
224
- /**
225
- * Wrapper Mailer for old addons registering the "mail" method (deprecated).
226
- */
227
- class NewsletterOldMailerWrapper extends NewsletterMailer {
228
-
229
- var $mailer;
230
-
231
- /**
232
- * Old mailer plugin (actually untyped object)
233
- * @param object $mailer
234
- */
235
- function __construct($mailer) {
236
- $this->mailer = $mailer;
237
- // We have not a name, build it from the class name... and of course, no options.
238
- parent::__construct(strtolower(get_class($mailer)), array());
239
- $this->description = 'Mailer wrapper for ' . get_class($mailer);
240
- }
241
-
242
- /**
243
- * Only send() needs to be implemented all other method will use the defail base-class implementation
244
- *
245
- * @param TNP_Mailer_Message $message
246
- * @return \WP_Error|boolean
247
- */
248
- function send($message) {
249
- // The old mailer manages itself the from field
250
- $r = $this->mailer->mail($message->to, $message->subject, array('html' => $message->body, 'text' => $message->body_text), $message->headers);
251
- if (!$r) {
252
- if (isset($this->mailer->result)) {
253
- $message->error = $this->mailer->result;
254
- return new WP_Error(self::ERROR_GENERIC, $this->mailer->result);
255
- } else {
256
- $message->error = 'Unknown error';
257
- return new WP_Error(self::ERROR_GENERIC, 'Unknown error');
258
- }
259
- }
260
- return true;
261
- }
262
-
263
- }
264
-
265
  /**
266
  * Standard Mailer which uses the wp_mail() function of WP.
267
  */
@@ -283,6 +229,10 @@ class NewsletterDefaultMailer extends NewsletterMailer {
283
  // TODO: check if overloaded
284
  return 'wp_mail() WordPress function (could be extended by a SMTP plugin)';
285
  }
 
 
 
 
286
 
287
  function fix_mailer($mailer) {
288
  // If there is not a current message, wp_mail() was not called by us
@@ -382,7 +332,9 @@ class NewsletterDefaultMailer extends NewsletterMailer {
382
  }
383
 
384
  /**
385
- * Standard Mailer which uses the wp_mail() function of WP.
 
 
386
  */
387
  class NewsletterDefaultSMTPMailer extends NewsletterMailer {
388
 
@@ -393,7 +345,7 @@ class NewsletterDefaultSMTPMailer extends NewsletterMailer {
393
  }
394
 
395
  function get_description() {
396
- return 'Internal SMTP';
397
  }
398
 
399
  /**
2
 
3
  use TNP\Mailer\PHPMailerLoader;
4
 
5
+ /**
6
+ * @property string $to
7
+ * @property string $to_name
8
+ * @property string $subject
9
+ * @property string $body
10
+ * @property array $headers
11
+ * @property string $from
12
+ * @property string $from_name
13
+ */
14
+ class TNP_Mailer_Message {
15
+
16
+ var $to_name = '';
17
+ var $headers = array();
18
+ var $user_id = 0;
19
+ var $email_id = 0;
20
+ var $error = '';
21
+ var $subject = '';
22
+ var $body = '';
23
+ var $body_text = '';
24
+ var $from = '';
25
+ var $from_name = '';
26
+
27
+ }
28
+
29
  /**
30
  * A basic class able to send one or more TNP_Mailer_Message objects using a
31
  * delivery method (wp-mail(), SMTP, API, ...).
42
  var $options;
43
  private $delta;
44
  protected $batch_size = 1;
45
+ protected $speed = 0;
46
 
47
  public function __construct($name, $options = []) {
48
  $this->name = $name;
49
  $this->options = $options;
50
+ if (!empty($this->options['speed'])) {
51
+ $this->speed = max(0, (int)$this->options['speed']);
52
+ }
53
+ if (!empty($this->options['turbo'])) {
54
+ $this->batch_size = max(1, (int)$this->options['turbo']);
55
+ }
56
+ $this->get_logger()->debug($options);
57
  }
58
 
59
  public function get_name() {
61
  }
62
 
63
  public function get_description() {
64
+ return ucfirst($this->name) . ' Addon';
65
  }
66
 
67
  public function get_batch_size() {
68
  return $this->batch_size;
69
  }
70
+
71
+ public function get_speed() {
72
+ return $this->speed;
73
+ }
74
 
75
  function send_with_stats($message) {
76
  $this->delta = microtime(true);
137
  return $last_result;
138
  }
139
 
140
+ /**
141
+ * This one should be implemented by specilized classes.
142
+ *
143
+ * @param TNP_Mailer_Message[] $messages
144
+ * @return bool|WP_Error
145
+ */
146
  protected function send_chunk($messages) {
147
  $last_result = true;
148
  foreach ($messages as $message) {
188
  return !is_wp_error($this->send($mailer_message));
189
  }
190
 
191
+ /**
192
+ * Used by bounce detection.
193
+ *
194
+ * @param int $time
195
+ */
196
  function save_last_run($time) {
197
  update_option($this->prefix . '_last_run', $time);
198
  }
199
 
200
+ /**
201
+ * Used by bounce detection.
202
+ *
203
+ * @param int $time
204
+ */
205
  function get_last_run() {
206
  return (int) get_option($this->prefix . '_last_run', 0);
207
  }
208
 
209
  }
210
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
211
  /**
212
  * Standard Mailer which uses the wp_mail() function of WP.
213
  */
229
  // TODO: check if overloaded
230
  return 'wp_mail() WordPress function (could be extended by a SMTP plugin)';
231
  }
232
+
233
+ function get_speed() {
234
+ return (int)Newsletter::instance()->options['scheduler_max'];
235
+ }
236
 
237
  function fix_mailer($mailer) {
238
  // If there is not a current message, wp_mail() was not called by us
332
  }
333
 
334
  /**
335
+ * @deprecated since version 6.2.0
336
+ * Internal SMTP mailer implementation (move to an SMTP plugin or use the
337
+ * SMTP Addon).
338
  */
339
  class NewsletterDefaultSMTPMailer extends NewsletterMailer {
340
 
345
  }
346
 
347
  function get_description() {
348
+ return 'Internal SMTP (deprecated)';
349
  }
350
 
351
  /**
includes/module.php CHANGED
@@ -1,2592 +1,2592 @@
1
- <?php
2
-
3
- defined('ABSPATH') || exit;
4
-
5
- require_once __DIR__ . '/logger.php';
6
- require_once __DIR__ . '/store.php';
7
- require_once __DIR__ . '/composer.php';
8
- require_once __DIR__ . '/addon.php';
9
- require_once __DIR__ . '/mailer.php';
10
- require_once __DIR__ . '/themes.php';
11
-
12
- class TNP_Media {
13
-
14
- var $id;
15
- var $url;
16
- var $width;
17
- var $height;
18
- var $alt;
19
- var $link;
20
- var $align = 'center';
21
-
22
- /** Sets the width recalculating the height */
23
- public function set_width($width) {
24
- $width = (int)$width;
25
- if (empty($width)) return;
26
- if ($this->width < $width) return;
27
- $this->height = floor(($width / $this->width) * $this->height);
28
- $this->width = $width;
29
- }
30
-
31
- /** Sets the height recalculating the width */
32
- public function set_height($height) {
33
- $height = (int) $height;
34
- $this->width = floor(($height / $this->height) * $this->width);
35
- $this->height = $height;
36
- }
37
-
38
- }
39
-
40
- /**
41
- * @property int $id The list unique identifier
42
- * @property string $name The list name
43
- * @property bool $forced If the list must be added to every new subscriber
44
- * @property int $status When and how the list is visible to the subscriber - see constants
45
- * @property bool $checked If it must be pre-checked on subscription form
46
- * @property array $languages The list of language used to pre-assign this list
47
- */
48
- class TNP_List {
49
-
50
- const STATUS_PRIVATE = 0;
51
- const STATUS_PUBLIC = 1;
52
- const SUBSCRIPTION_HIDE = 0;
53
- const SUBSCRIPTION_SHOW = 1;
54
- const SUBSCRIPTION_SHOW_CHECKED = 2;
55
- const PROFILE_HIDE = 0;
56
- const PROFILE_SHOW = 1;
57
-
58
- var $id;
59
- var $name;
60
- var $status;
61
- var $forced;
62
- var $checked;
63
- var $show_on_subscription;
64
- var $show_on_profile;
65
-
66
- function is_private() {
67
- return $this->status == self::STATUS_PRIVATE;
68
- }
69
-
70
- }
71
-
72
- /**
73
- * @property int $id The list unique identifier
74
- * @property string $name The list name
75
- * @property int $status When and how the list is visible to the subscriber - see constants
76
- * @property string $type Field type: text or select
77
- * @property array $options Field options (usually the select items)
78
- */
79
- class TNP_Profile {
80
-
81
- const STATUS_PRIVATE = 0;
82
- const STATUS_PUBLIC = 2;
83
- const STATUS_PROFILE_ONLY = 1;
84
- const STATUS_HIDDEN = 3; // Public but never shown (can be set with a hidden form field)
85
- const TYPE_TEXT = 'text';
86
- const TYPE_SELECT = 'select';
87
-
88
- public $id;
89
- public $name;
90
- public $status;
91
- public $type;
92
- public $options;
93
- public $placeholder;
94
- public $rule;
95
-
96
- public function __construct($id, $name, $status, $type, $options, $placeholder, $rule) {
97
- $this->id = $id;
98
- $this->name = $name;
99
- $this->status = $status;
100
- $this->type = $type;
101
- $this->options = $options;
102
- $this->placeholder = $placeholder;
103
- $this->rule = $rule;
104
- }
105
-
106
- function is_select() {
107
- return $this->type == self::TYPE_SELECT;
108
- }
109
-
110
- function is_text() {
111
- return $this->type == self::TYPE_TEXT;
112
- }
113
-
114
- function is_required() {
115
- return $this->rule == 1;
116
- }
117
-
118
- function is_private() {
119
- return $this->status == self::STATUS_PRIVATE;
120
- }
121
-
122
- function show_on_profile() {
123
- return $this->status == self::STATUS_PROFILE_ONLY || $this->status == self::STATUS_PUBLIC;
124
- }
125
-
126
- }
127
-
128
- class TNP_Profile_Service {
129
-
130
- /**
131
- *
132
- * @param string $language
133
- * @param string $type
134
- * @return TNP_Profile[]
135
- */
136
- static function get_profiles($language = '', $type = '') {
137
-
138
- static $profiles = [];
139
- $k = $language . $type;
140
-
141
- if (isset($profiles[$k])) {
142
- return $profiles[$k];
143
- }
144
-
145
- $profiles[$k] = [];
146
- $profile_options = NewsletterSubscription::instance()->get_options('profile', $language);
147
- for ($i = 1; $i <= NEWSLETTER_PROFILE_MAX; $i++) {
148
- if (empty($profile_options['profile_' . $i])) {
149
- continue;
150
- }
151
- $profile = self::create_profile_from_options($profile_options, $i);
152
-
153
- if (empty($type) ||
154
- ( $type == TNP_Profile::TYPE_SELECT && $profile->is_select() ) ||
155
- ( $type == TNP_Profile::TYPE_TEXT && $profile->is_text() )) {
156
- $profiles[$k]['' . $i] = $profile;
157
- }
158
- }
159
-
160
- return $profiles[$k];
161
- }
162
-
163
- static function get_profile_by_id($id, $language = '') {
164
-
165
- $profiles = self::get_profiles($language);
166
- if (isset($profiles[$id]))
167
- return $profiles[$id];
168
- return null;
169
- }
170
-
171
- /**
172
- * @return TNP_Profile
173
- */
174
- private static function create_profile_from_options($options, $id) {
175
- return new TNP_Profile(
176
- $id,
177
- $options['profile_' . $id],
178
- (int) $options['profile_' . $id . '_status'],
179
- $options['profile_' . $id . '_type'],
180
- self::string_db_options_to_array($options['profile_' . $id . '_options']),
181
- $options['profile_' . $id . '_placeholder'],
182
- $options['profile_' . $id . '_rules']
183
- );
184
- }
185
-
186
- /**
187
- * Returns a list of strings which are the items for the select field.
188
- * @return array
189
- */
190
- private static function string_db_options_to_array($string_options) {
191
- $items = array_map('trim', explode(',', $string_options));
192
- $items = array_combine($items, $items);
193
-
194
- return $items;
195
- }
196
-
197
- }
198
-
199
- /**
200
- * Represents the set of data collected by a subscription interface (form, API, ...). Only a valid
201
- * email is mandatory.
202
- */
203
- class TNP_Subscription_Data {
204
-
205
- var $email = null;
206
- var $name = null;
207
- var $surname = null;
208
- var $sex = null;
209
- var $language = null;
210
- var $referrer = null;
211
- var $http_referrer = null;
212
- var $ip = null;
213
- var $country = null;
214
- var $region = null;
215
- var $city = null;
216
-
217
- /**
218
- * Associative array id=>value of lists chosen by the subscriber. A list can be set to
219
- * 0 meaning the subscriber does not want to be in that list.
220
- * The lists must be public: non public lists are filtered.
221
- * @var array
222
- */
223
- var $lists = [];
224
- var $profiles = [];
225
-
226
- function merge_in($subscriber) {
227
- if (!$subscriber)
228
- $subscriber = new TNP_User();
229
- if (!empty($this->email))
230
- $subscriber->email = $this->email;
231
- if (!empty($this->name))
232
- $subscriber->name = $this->name;
233
- if (!empty($this->surname))
234
- $subscriber->surname = $this->surname;
235
- if (!empty($this->sex))
236
- $subscriber->sex = $this->sex;
237
- if (!empty($this->language))
238
- $subscriber->language = $this->language;
239
- if (!empty($this->ip))
240
- $subscriber->ip = $this->ip;
241
- if (!empty($this->referrer))
242
- $subscriber->referrer = $this->referrer;
243
- if (!empty($this->http_referrer))
244
- $subscriber->http_referrer = $this->http_referrer;
245
- if (!empty($this->country))
246
- $subscriber->country = $this->country;
247
- if (!empty($this->region))
248
- $subscriber->region = $this->region;
249
- if (!empty($this->city))
250
- $subscriber->city = $this->city;
251
-
252
-
253
- foreach ($this->lists as $id => $value) {
254
- $key = 'list_' . $id;
255
- $subscriber->$key = $value;
256
- }
257
-
258
- // Profile
259
- foreach ($this->profiles as $id => $value) {
260
- $key = 'profile_' . $id;
261
- $subscriber->$key = $value;
262
- }
263
- }
264
-
265
- /** Sets to active a set of lists. Accepts incorrect data (and ignores it).
266
- *
267
- * @param array $list_ids Array of list IDs
268
- */
269
- function add_lists($list_ids) {
270
- if (empty($list_ids) || !is_array($list_ids))
271
- return;
272
- foreach ($list_ids as $list_id) {
273
- $list_id = (int) $list_id;
274
- if ($list_id < 0 || $list_id > NEWSLETTER_LIST_MAX)
275
- continue;
276
- $this->lists[$list_id] = 1;
277
- }
278
- }
279
-
280
- }
281
-
282
- /**
283
- * Represents a subscription request with the subscriber data and actions to be taken by
284
- * the subscription engine (spam check, notifications, ...).
285
- */
286
- class TNP_Subscription {
287
-
288
- const EXISTING_ERROR = 1;
289
- const EXISTING_MERGE = 0;
290
-
291
- /**
292
- * Subscriber's data following the syntax of the TNP_User
293
- * @var TNP_Subscription_Data
294
- */
295
- var $data;
296
- var $spamcheck = true;
297
- // The optin to use, empty for the plugin default. It's a string to facilitate the use by addons (which have a selector for the desired
298
- // optin as empty (for default), 'single' or 'double'.
299
- var $optin = null;
300
- // What to do with an existing subscriber???
301
- var $if_exists = self::EXISTING_MERGE;
302
-
303
- /**
304
- * Determines if the welcome or activation email should be sent. Note: sometime an activation email is sent disregarding
305
- * this setting.
306
- * @var boolean
307
- */
308
- var $send_emails = true;
309
-
310
- public function __construct() {
311
- $this->data = new TNP_Subscription_Data();
312
- }
313
-
314
- }
315
-
316
- /**
317
- * @property int $id The subscriber unique identifier
318
- * @property string $email The subscriber email
319
- * @property string $name The subscriber name or first name
320
- * @property string $surname The subscriber last name
321
- * @property string $status The subscriber status
322
- * @property string $language The subscriber language code 2 chars lowercase
323
- * @property string $token The subscriber secret token
324
- * @property string $country The subscriber country code 2 chars uppercase
325
- */
326
- class TNP_User {
327
-
328
- const STATUS_CONFIRMED = 'C';
329
- const STATUS_NOT_CONFIRMED = 'S';
330
- const STATUS_UNSUBSCRIBED = 'U';
331
- const STATUS_BOUNCED = 'B';
332
- const STATUS_COMPLAINED = 'P';
333
-
334
- public static function get_status_label($status) {
335
- switch ($status) {
336
- case self::STATUS_NOT_CONFIRMED: return __('NOT CONFIRMED', 'newsletter');
337
- break;
338
- case self::STATUS_CONFIRMED: return __('CONFIRMED', 'newsletter');
339
- break;
340
- case self::STATUS_UNSUBSCRIBED: return __('UNSUBSCRIBED', 'newsletter');
341
- break;
342
- case self::STATUS_BOUNCED: return __('BOUNCED', 'newsletter');
343
- break;
344
- case self::STATUS_COMPLAINED: return __('COMPLAINED', 'newsletter');
345
- break;
346
- default:
347
- return __('Unknown', 'newsletter');
348
- }
349
- }
350
-
351
- }
352
-
353
- /**
354
- * @property int $id The email unique identifier
355
- * @property string $subject The email subject
356
- * @property string $message The email html message
357
- * @property int $track Check if the email stats should be active
358
- * @property array $options Email options
359
- * @property int $total Total emails to send
360
- * @property int $sent Total sent emails by now
361
- * @property int $open_count Total opened emails
362
- * @property int $click_count Total clicked emails
363
- * */
364
- class TNP_Email {
365
-
366
- const STATUS_DRAFT = 'new';
367
- const STATUS_SENT = 'sent';
368
- const STATUS_SENDING = 'sending';
369
- const STATUS_PAUSED = 'paused';
370
- const STATUS_ERROR = 'error';
371
-
372
- }
373
-
374
- class NewsletterModule {
375
-
376
- /**
377
- * @var NewsletterLogger
378
- */
379
- var $logger;
380
-
381
- /**
382
- * @var NewsletterLogger
383
- */
384
- var $admin_logger;
385
-
386
- /**
387
- * @var NewsletterStore
388
- */
389
- var $store;
390
-
391
- /**
392
- * The main module options
393
- * @var array
394
- */
395
- var $options;
396
-
397
- /**
398
- * @var string The module name
399
- */
400
- var $module;
401
-
402
- /**
403
- * The module version
404
- * @var string
405
- */
406
- var $version;
407
- var $old_version;
408
-
409
- /**
410
- * Prefix for all options stored on WordPress options table.
411
- * @var string
412
- */
413
- var $prefix;
414
-
415
- /**
416
- * @var NewsletterThemes
417
- */
418
- var $themes;
419
- var $components;
420
- static $current_language = '';
421
-
422
- function __construct($module, $version, $module_id = null, $components = array()) {
423
- $this->module = $module;
424
- $this->version = $version;
425
- $this->prefix = 'newsletter_' . $module;
426
- array_unshift($components, '');
427
- $this->components = $components;
428
-
429
- $this->logger = new NewsletterLogger($module);
430
-
431
- $this->options = $this->get_options();
432
- $this->store = NewsletterStore::singleton();
433
-
434
- //$this->logger->debug($module . ' constructed');
435
- // Version check
436
- if (is_admin()) {
437
- $this->admin_logger = new NewsletterLogger($module . '-admin');
438
- $this->old_version = get_option($this->prefix . '_version', '0.0.0');
439
-
440
- if ($this->old_version == '0.0.0') {
441
- require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
442
- $this->first_install();
443
- update_option($this->prefix . "_first_install_time", time(), FALSE);
444
- }
445
-
446
- if (strcmp($this->old_version, $this->version) != 0) {
447
- require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
448
- $this->logger->info('Version changed from ' . $this->old_version . ' to ' . $this->version);
449
- // Do all the stuff for this version change
450
- $this->upgrade();
451
- update_option($this->prefix . '_version', $this->version);
452
- }
453
-
454
- add_action('admin_menu', array($this, 'admin_menu'));
455
- }
456
- }
457
-
458
- /**
459
- *
460
- * @global wpdb $wpdb
461
- * @param string $query
462
- */
463
- function query($query) {
464
- global $wpdb;
465
-
466
- $this->logger->debug($query);
467
- //$start = microtime(true);
468
- $r = $wpdb->query($query);
469
- //$this->logger->debug($wpdb->last_query);
470
- //$this->logger->debug('Execution time: ' . (microtime(true)-$start));
471
- //$this->logger->debug('Result: ' . $r);
472
- if ($r === false) {
473
- $this->logger->fatal($query);
474
- $this->logger->fatal($wpdb->last_error);
475
- }
476
- return $r;
477
- }
478
-
479
- function get_results($query) {
480
- global $wpdb;
481
- $r = $wpdb->get_results($query);
482
- if ($r === false) {
483
- $this->logger->fatal($query);
484
- $this->logger->fatal($wpdb->last_error);
485
- }
486
- return $r;
487
- }
488
-
489
- /**
490
- *
491
- * @global wpdb $wpdb
492
- * @param string $table
493
- * @param array $data
494
- */
495
- function insert($table, $data) {
496
- global $wpdb;
497
- $this->logger->debug("inserting into table $table");
498
- $r = $wpdb->insert($table, $data);
499
- if ($r === false) {
500
- $this->logger->fatal($wpdb->last_error);
501
- }
502
- }
503
-
504
- function first_install() {
505
- $this->logger->debug('First install');
506
- }
507
-
508
- /**
509
- * Does a basic upgrade work, checking if the options is already present and if not (first
510
- * installation), recovering the defaults, saving them on database and initializing the
511
- * internal $options.
512
- */
513
- function upgrade() {
514
- foreach ($this->components as $component) {
515
- $this->logger->debug('Upgrading component ' . $component);
516
- $this->init_options($component);
517
- }
518
- }
519
-
520
- function init_options($component = '', $autoload = true) {
521
- global $wpdb;
522
- $default_options = $this->get_default_options($component);
523
- $options = $this->get_options($component);
524
- $options = array_merge($default_options, $options);
525
- $this->save_options($options, $component, $autoload);
526
- }
527
-
528
- function upgrade_query($query) {
529
- global $wpdb, $charset_collate;
530
-
531
- $this->logger->info('upgrade_query> Executing ' . $query);
532
- $suppress_errors = $wpdb->suppress_errors(true);
533
- $wpdb->query($query);
534
- if ($wpdb->last_error) {
535
- $this->logger->debug($wpdb->last_error);
536
- }
537
- $wpdb->suppress_errors($suppress_errors);
538
- }
539
-
540
- /** Returns a prefix to be used for option names and other things which need to be uniquely named. The parameter
541
- * "sub" should be used when a sub name is needed for another set of options or like.
542
- *
543
- * @param string $sub
544
- * @return string The prefix for names
545
- */
546
- function get_prefix($sub = '', $language = '') {
547
- return $this->prefix . (!empty($sub) ? '_' : '') . $sub . (!empty($language) ? '_' : '') . $language;
548
- }
549
-
550
- /**
551
- * Returns the options of a module, if not found an empty array.
552
- */
553
- function get_options($sub = '', $language = '') {
554
- $options = get_option($this->get_prefix($sub, $language), array());
555
- // Protection against scarmled database...
556
- if (!is_array($options)) {
557
- $options = array();
558
- }
559
- if ($language) {
560
- $main_options = get_option($this->get_prefix($sub));
561
- // Protection against scarmled database...
562
- if (!is_array($main_options))
563
- $main_options = array();
564
- //$options = array_merge($main_options, array_filter($options));
565
- $options = array_merge($main_options, $options);
566
- }
567
- return $options;
568
- }
569
-
570
- function get_default_options($sub = '') {
571
- if (!empty($sub)) {
572
- $sub = '-' . $sub;
573
- }
574
- $file = NEWSLETTER_DIR . '/' . $this->module . '/defaults' . $sub . '.php';
575
- if (file_exists($file)) {
576
- @include $file;
577
- }
578
-
579
- if (!isset($options) || !is_array($options)) {
580
- return array();
581
- }
582
- return $options;
583
- }
584
-
585
- function reset_options($sub = '') {
586
- $this->save_options(array_merge($this->get_options($sub), $this->get_default_options($sub)), $sub);
587
- return $this->get_options($sub);
588
- }
589
-
590
- /**
591
- * Saves the module options (or eventually a subset names as per parameter $sub). $options
592
- * should be an array (even if it can work with non array options.
593
- * The internal module options variable IS initialized with those new options only for the main
594
- * options (empty $sub parameter).
595
- * If the options contain a "theme" value, the theme-related options contained are saved as well
596
- * (used by some modules).
597
- *
598
- * @param array $options
599
- * @param string $sub
600
- */
601
- function save_options($options, $sub = '', $autoload = null, $language = '') {
602
- update_option($this->get_prefix($sub, $language), $options, $autoload);
603
- if (empty($sub) && empty($language)) {
604
- $this->options = $options;
605
- if (isset($this->themes) && isset($options['theme'])) {
606
- $this->themes->save_options($options['theme'], $options);
607
- }
608
- }
609
- }
610
-
611
- function delete_options($sub = '') {
612
- delete_option($this->get_prefix($sub));
613
- if (empty($sub)) {
614
- $this->options = array();
615
- }
616
- }
617
-
618
- function merge_options($options, $sub = '', $language = '') {
619
- if (!is_array($options)) {
620
- $options = array();
621
- }
622
- $old_options = $this->get_options($sub, $language);
623
- $this->save_options(array_merge($old_options, $options), $sub, null, $language);
624
- }
625
-
626
- function backup_options($sub) {
627
- $options = $this->get_options($sub);
628
- update_option($this->get_prefix($sub) . '_backup', $options, false);
629
- }
630
-
631
- function get_last_run($sub = '') {
632
- return get_option($this->get_prefix($sub) . '_last_run', 0);
633
- }
634
-
635
- /**
636
- * Save the module last run value. Used to store a timestamp for some modules,
637
- * for example the Feed by Mail module.
638
- *
639
- * @param int $time Unix timestamp (as returned by time() for example)
640
- * @param string $sub Sub module name (default empty)
641
- */
642
- function save_last_run($time, $sub = '') {
643
- update_option($this->get_prefix($sub) . '_last_run', $time);
644
- }
645
-
646
- /**
647
- * Sums $delta seconds to the last run time.
648
- * @param int $delta Seconds
649
- * @param string $sub Sub module name (default empty)
650
- */
651
- function add_to_last_run($delta, $sub = '') {
652
- $time = $this->get_last_run($sub);
653
- $this->save_last_run($time + $delta, $sub);
654
- }
655
-
656
- /**
657
- * Checks if the semaphore of that name (for this module) is still red. If it is active the method
658
- * returns false. If it is not active, it will be activated for $time seconds.
659
- *
660
- * Since this method activate the semaphore when called, it's name is a bit confusing.
661
- *
662
- * @param string $name Sempahore name (local to this module)
663
- * @param int $time Max time in second this semaphore should stay red
664
- * @return boolean False if the semaphore is red and you should not proceed, true is it was not active and has been activated.
665
- */
666
- function check_transient($name, $time) {
667
- if ($time < 60)
668
- $time = 60;
669
- //usleep(rand(0, 1000000));
670
- if (($value = get_transient($this->get_prefix() . '_' . $name)) !== false) {
671
- list($t, $v) = explode(';', $value, 2);
672
- $this->logger->error('Blocked by transient ' . $this->get_prefix() . '_' . $name . ' set ' . (time() - $t) . ' seconds ago by ' . $v);
673
- return false;
674
- }
675
- //$ip = ''; //gethostbyname(gethostname());
676
- $value = time() . ";" . ABSPATH . ';' . gethostname();
677
- set_transient($this->get_prefix() . '_' . $name, $value, $time);
678
- return true;
679
- }
680
-
681
- function delete_transient($name = '') {
682
- delete_transient($this->get_prefix() . '_' . $name);
683
- }
684
-
685
- /** Returns a random token of the specified size (or 10 characters if size is not specified).
686
- *
687
- * @param int $size
688
- * @return string
689
- */
690
- static function get_token($size = 10) {
691
- return substr(md5(rand()), 0, $size);
692
- }
693
-
694
- /**
695
- * Adds query string parameters to an URL checing id there are already other parameters.
696
- *
697
- * @param string $url
698
- * @param string $qs The part of query-string to add (param1=value1&param2=value2...)
699
- * @param boolean $amp If the method must use the &amp; instead of the plain & (default true)
700
- * @return string
701
- */
702
- static function add_qs($url, $qs, $amp = true) {
703
- if (strpos($url, '?') !== false) {
704
- if ($amp)
705
- return $url . '&amp;' . $qs;
706
- else
707
- return $url . '&' . $qs;
708
- } else
709
- return $url . '?' . $qs;
710
- }
711
-
712
- /**
713
- * Returns the email address normalized, lowercase with no spaces. If it's not a valid email
714
- * returns false.
715
- */
716
- static function normalize_email($email) {
717
- if (!is_string($email)) {
718
- return false;
719
- }
720
- $email = strtolower(trim($email));
721
- if (!is_email($email)) {
722
- return false;
723
- }
724
- //$email = apply_filters('newsletter_normalize_email', $email);
725
- return $email;
726
- }
727
-
728
- static function normalize_name($name) {
729
- $name = html_entity_decode($name, ENT_QUOTES);
730
- $name = str_replace(';', ' ', $name);
731
- $name = strip_tags($name);
732
-
733
- return $name;
734
- }
735
-
736
- static function normalize_sex($sex) {
737
- $sex = trim(strtolower($sex));
738
- if ($sex != 'f' && $sex != 'm') {
739
- $sex = 'n';
740
- }
741
- return $sex;
742
- }
743
-
744
- static function is_email($email, $empty_ok = false) {
745
-
746
- if (!is_string($email)) {
747
- return false;
748
- }
749
- $email = strtolower(trim($email));
750
-
751
- if ($email == '') {
752
- return $empty_ok;
753
- }
754
-
755
- if (!is_email($email)) {
756
- return false;
757
- }
758
- return true;
759
- }
760
-
761
- /**
762
- * Converts a GMT date from mysql (see the posts table columns) into a timestamp.
763
- *
764
- * @param string $s GMT date with format yyyy-mm-dd hh:mm:ss
765
- * @return int A timestamp
766
- */
767
- static function m2t($s) {
768
-
769
- // TODO: use the wordpress function I don't remember the name
770
- $s = explode(' ', $s);
771
- $d = explode('-', $s[0]);
772
- $t = explode(':', $s[1]);
773
- return gmmktime((int) $t[0], (int) $t[1], (int) $t[2], (int) $d[1], (int) $d[2], (int) $d[0]);
774
- }
775
-
776
- static function format_date($time) {
777
- if (empty($time)) {
778
- return '-';
779
- }
780
- return gmdate(get_option('date_format') . ' ' . get_option('time_format'), $time + get_option('gmt_offset') * 3600);
781
- }
782
-
783
- static function format_time_delta($delta) {
784
- $days = floor($delta / (3600 * 24));
785
- $hours = floor(($delta % (3600 * 24)) / 3600);
786
- $minutes = floor(($delta % 3600) / 60);
787
- $seconds = floor(($delta % 60));
788
- $buffer = $days . ' days, ' . $hours . ' hours, ' . $minutes . ' minutes, ' . $seconds . ' seconds';
789
- return $buffer;
790
- }
791
-
792
- /**
793
- * Formats a scheduler returned "next execution" time, managing negative or false values. Many times
794
- * used in conjuction with "last run".
795
- *
796
- * @param string $name The scheduler name
797
- * @return string
798
- */
799
- static function format_scheduler_time($name) {
800
- $time = wp_next_scheduled($name);
801
- if ($time === false) {
802
- return 'No next run scheduled';
803
- }
804
- $delta = $time - time();
805
- // If less 10 minutes late it can be a cron problem but now it is working
806
- if ($delta < 0 && $delta > -600) {
807
- return 'Probably running now';
808
- } else if ($delta <= -600) {
809
- return 'It seems the cron system is not working. Reload the page to see if this message change.';
810
- }
811
- return 'Runs in ' . self::format_time_delta($delta);
812
- }
813
-
814
- static function date($time = null, $now = false, $left = false) {
815
- if (is_null($time)) {
816
- $time = time();
817
- }
818
- if ($time == false) {
819
- $buffer = 'none';
820
- } else {
821
- $buffer = gmdate(get_option('date_format') . ' ' . get_option('time_format'), $time + get_option('gmt_offset') * 3600);
822
- }
823
- if ($now) {
824
- $buffer .= ' (now: ' . gmdate(get_option('date_format') . ' ' .
825
- get_option('time_format'), time() + get_option('gmt_offset') * 3600);
826
- $buffer .= ')';
827
- }
828
- if ($left) {
829
- $buffer .= ', ' . gmdate('H:i:s', $time - time()) . ' left';
830
- }
831
- return $buffer;
832
- }
833
-
834
- /**
835
- * Return an array of array with on first element the array of recent post and on second element the array
836
- * of old posts.
837
- *
838
- * @param array $posts
839
- * @param int $time
840
- */
841
- static function split_posts(&$posts, $time = 0) {
842
- if ($time < 0) {
843
- return array_chunk($posts, ceil(count($posts) / 2));
844
- }
845
-
846
- $result = array(array(), array());
847
-
848
- if (empty($posts))
849
- return $result;
850
-
851
- foreach ($posts as &$post) {
852
- if (self::is_post_old($post, $time))
853
- $result[1][] = $post;
854
- else
855
- $result[0][] = $post;
856
- }
857
- return $result;
858
- }
859
-
860
- static function is_post_old(&$post, $time = 0) {
861
- return self::m2t($post->post_date_gmt) <= $time;
862
- }
863
-
864
- static function get_post_image($post_id = null, $size = 'thumbnail', $alternative = null) {
865
- global $post;
866
-
867
- if (empty($post_id))
868
- $post_id = $post->ID;
869
- if (empty($post_id))
870
- return $alternative;
871
-
872
- $image_id = function_exists('get_post_thumbnail_id') ? get_post_thumbnail_id($post_id) : false;
873
- if ($image_id) {
874
- $image = wp_get_attachment_image_src($image_id, $size);
875
- return $image[0];
876
- } else {
877
- $attachments = get_children(array('post_parent' => $post_id, 'post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => 'image', 'order' => 'ASC', 'orderby' => 'menu_order ID'));
878
-
879
- if (empty($attachments)) {
880
- return $alternative;
881
- }
882
-
883
- foreach ($attachments as $id => $attachment) {
884
- $image = wp_get_attachment_image_src($id, $size);
885
- return $image[0];
886
- }
887
- }
888
- }
889
-
890
- /**
891
- * Cleans up a text containing url tags with appended the absolute URL (due to
892
- * the editor behavior) moving back them to the simple form.
893
- */
894
- static function clean_url_tags($text) {
895
- $text = str_replace('%7B', '{', $text);
896
- $text = str_replace('%7D', '}', $text);
897
-
898
- // Only tags which are {*_url}
899
- $text = preg_replace("/[\"']http[^\"']+(\\{[^\\}]+_url\\})[\"']/i", "\"\\1\"", $text);
900
- return $text;
901
- }
902
-
903
- function admin_menu() {
904
-
905
- }
906
-
907
- function add_menu_page($page, $title, $capability = '') {
908
- if (!Newsletter::instance()->is_allowed())
909
- return;
910
- $name = 'newsletter_' . $this->module . '_' . $page;
911
- add_submenu_page('newsletter_main_index', $title, $title, 'exist', $name, array($this, 'menu_page'));
912
- }
913
-
914
- function add_admin_page($page, $title) {
915
- if (!Newsletter::instance()->is_allowed()) {
916
- return;
917
- }
918
- $name = 'newsletter_' . $this->module . '_' . $page;
919
- add_submenu_page(null, $title, $title, 'exist', $name, array($this, 'menu_page'));
920
- }
921
-
922
- function sanitize_file_name($name) {
923
- return preg_replace('/[^a-z_\\-]/i', '', $name);
924
- }
925
-
926
- function menu_page() {
927
- global $plugin_page, $newsletter, $wpdb;
928
-
929
- $parts = explode('_', $plugin_page, 3);
930
- $module = $this->sanitize_file_name($parts[1]);
931
- $page = $this->sanitize_file_name($parts[2]);
932
- $page = str_replace('_', '-', $page);
933
-
934
- $file = NEWSLETTER_DIR . '/' . $module . '/' . $page . '.php';
935
-
936
- require $file;
937
- }
938
-
939
- function get_admin_page_url($page) {
940
- return admin_url('admin.php') . '?page=newsletter_' . $this->module . '_' . $page;
941
- }
942
-
943
- /** Returns all the emails of the give type (message, feed, followup, ...) and in the given format
944
- * (default as objects). Return false on error or at least an empty array. Errors should never
945
- * occur.
946
- *
947
- * @global wpdb $wpdb
948
- * @param string $type
949
- * @return boolean|array
950
- */
951
- function get_emails($type = null, $format = OBJECT) {
952
- global $wpdb;
953
- if ($type == null) {
954
- $list = $wpdb->get_results("select * from " . NEWSLETTER_EMAILS_TABLE . " order by id desc", $format);
955
- } else {
956
- $type = (string) $type;
957
- $list = $wpdb->get_results($wpdb->prepare("select * from " . NEWSLETTER_EMAILS_TABLE . " where type=%s order by id desc", $type), $format);
958
- }
959
- if ($wpdb->last_error) {
960
- $this->logger->error($wpdb->last_error);
961
- return false;
962
- }
963
- if (empty($list)) {
964
- return array();
965
- }
966
- return $list;
967
- }
968
-
969
- /**
970
- * @param string $key
971
- * @param mixed $value
972
- * @return TNP_Email[]
973
- */
974
- function get_emails_by_field($key, $value) {
975
- global $wpdb;
976
-
977
- $value_placeholder = is_int($value) ? '%d' : '%s';
978
-
979
- $query = $wpdb->prepare("SELECT * FROM " . NEWSLETTER_EMAILS_TABLE . " WHERE %1s=$value_placeholder ORDER BY id DESC", $key, $value);
980
-
981
- $email_list = $wpdb->get_results($query);
982
-
983
- if ($wpdb->last_error) {
984
- $this->logger->error($wpdb->last_error);
985
-
986
- return [];
987
- }
988
-
989
- //Unserialize options
990
- array_walk($email_list, function ($email) {
991
- $email->options = maybe_unserialize($email->options);
992
- if (!is_array($email->options)) {
993
- $email->options = [];
994
- }
995
- });
996
-
997
- return $email_list;
998
- }
999
-
1000
- /**
1001
- * Retrieves an email from DB and unserialize the options.
1002
- *
1003
- * @param mixed $id
1004
- * @param string $format
1005
- * @return TNP_Email An object with the same fields of TNP_Email, but not actually of that type
1006
- */
1007
- function get_email($id, $format = OBJECT) {
1008
- $email = $this->store->get_single(NEWSLETTER_EMAILS_TABLE, $id, $format);
1009
- if (!$email) {
1010
- return null;
1011
- }
1012
- if ($format == OBJECT) {
1013
- $email->options = maybe_unserialize($email->options);
1014
- if (!is_array($email->options)) {
1015
- $email->options = array();
1016
- }
1017
- if (empty($email->query)) {
1018
- $email->query = "select * from " . NEWSLETTER_USERS_TABLE . " where status='C'";
1019
- }
1020
- } else if ($format == ARRAY_A) {
1021
- $email['options'] = maybe_unserialize($email['options']);
1022
- if (!is_array($email['options'])) {
1023
- $email['options'] = array();
1024
- }
1025
- if (empty($email['query'])) {
1026
- $email['query'] = "select * from " . NEWSLETTER_USERS_TABLE . " where status='C'";
1027
- }
1028
- }
1029
- return $email;
1030
- }
1031
-
1032
- /**
1033
- * Save an email and provide serialization, if needed, of $email['options'].
1034
- * @return TNP_Email
1035
- */
1036
- function save_email($email, $return_format = OBJECT) {
1037
- if (is_object($email)) {
1038
- $email = (array) $email;
1039
- }
1040
-
1041
- if (isset($email['subject'])) {
1042
- if (mb_strlen($email['subject'], 'UTF-8') > 250) {
1043
- $email['subject'] = mb_substr($email['subject'], 0, 250, 'UTF-8');
1044
- }
1045
- }
1046
- if (isset($email['options']) && is_array($email['options'])) {
1047
- $email['options'] = serialize($email['options']);
1048
- }
1049
- $email = $this->store->save(NEWSLETTER_EMAILS_TABLE, $email, $return_format);
1050
- if ($return_format == OBJECT) {
1051
- $email->options = maybe_unserialize($email->options);
1052
- if (!is_array($email->options)) {
1053
- $email->options = [];
1054
- }
1055
- } else if ($return_format == ARRAY_A) {
1056
- $email['options'] = maybe_unserialize($email['options']);
1057
- if (!is_array($email['options'])) {
1058
- $email['options'] = [];
1059
- }
1060
- }
1061
- return $email;
1062
- }
1063
-
1064
- function get_email_from_request() {
1065
-
1066
- if (isset($_REQUEST['nek'])) {
1067
- list($id, $token) = @explode('-', $_REQUEST['nek'], 2);
1068
- } else if (isset($_COOKIE['tnpe'])) {
1069
- list($id, $token) = @explode('-', $_COOKIE['tnpe'], 2);
1070
- } else {
1071
- return null;
1072
- }
1073
-
1074
- $email = $this->get_email($id);
1075
-
1076
- // TODO: Check the token? It's really useful?
1077
-
1078
- return $email;
1079
- }
1080
-
1081
- /**
1082
- * Delete one or more emails identified by ID (single value or array of ID)
1083
- *
1084
- * @global wpdb $wpdb
1085
- * @param int|array $id Single numeric ID or an array of IDs to be deleted
1086
- * @return boolean
1087
- */
1088
- function delete_email($id) {
1089
- global $wpdb;
1090
- $r = $this->store->delete(NEWSLETTER_EMAILS_TABLE, $id);
1091
- if ($r !== false) {
1092
- // $id could be an array if IDs
1093
- $id = (array) $id;
1094
- foreach ($id as $email_id) {
1095
- $wpdb->delete(NEWSLETTER_STATS_TABLE, ['email_id' => $email_id]);
1096
- $wpdb->delete(NEWSLETTER_SENT_TABLE, ['email_id' => $email_id]);
1097
- }
1098
- }
1099
- return $r;
1100
- }
1101
-
1102
- function get_email_field($id, $field_name) {
1103
- return $this->store->get_field(NEWSLETTER_EMAILS_TABLE, $id, $field_name);
1104
- }
1105
-
1106
- function get_email_status_slug($email) {
1107
- $email = (object) $email;
1108
- if ($email->status == 'sending' && $email->send_on > time()) {
1109
- return 'scheduled';
1110
- }
1111
- return $email->status;
1112
- }
1113
-
1114
- function get_email_status_label($email) {
1115
- $email = (object) $email;
1116
- $status = $this->get_email_status_slug($email);
1117
- switch ($status) {
1118
- case 'sending':
1119
- return __('Sending', 'newsletter');
1120
- case 'scheduled':
1121
- return __('Scheduled', 'newsletter');
1122
- case 'sent':
1123
- return __('Sent', 'newsletter');
1124
- case 'paused':
1125
- return __('Paused', 'newsletter');
1126
- case 'new':
1127
- return __('Draft', 'newsletter');
1128
- default:
1129
- return ucfirst($email->status);
1130
- }
1131
- }
1132
-
1133
- function show_email_status_label($email) {
1134
- echo '<span class="tnp-email-status tnp-email-status--', $this->get_email_status_slug($email), '">', esc_html($this->get_email_status_label($email)), '</span>';
1135
- }
1136
-
1137
- function get_email_progress($email, $format = 'percent') {
1138
- return $email->total > 0 ? intval($email->sent / $email->total * 100) : 0;
1139
- }
1140
-
1141
- function show_email_progress_bar($email, $attrs = []) {
1142
-
1143
- $email = (object) $email;
1144
-
1145
- $attrs = array_merge(array('format' => 'percent', 'numbers' => false, 'scheduled' => false), $attrs);
1146
-
1147
- if ($email->status == 'sending' && $email->send_on > time()) {
1148
- if ($attrs['scheduled']) {
1149
- echo '<span class="tnp-progress-date">', $this->format_date($email->send_on), '</span>';
1150
- }
1151
- return;
1152
- } else if ($email->status == 'new') {
1153
- echo '';
1154
- return;
1155
- } else if ($email->status == 'sent') {
1156
- $percent = 100;
1157
- } else {
1158
- $percent = $this->get_email_progress($email);
1159
- }
1160
-
1161
- echo '<div class="tnp-progress tnp-progress--' . $email->status . '">';
1162
- echo '<div class="tnp-progress-bar" role="progressbar" style="width: ', $percent, '%;">&nbsp;', $percent, '%&nbsp;</div>';
1163
- echo '</div>';
1164
- if ($attrs['numbers']) {
1165
- if ($email->status == 'sent') {
1166
- echo '<div class="tnp-progress-numbers">', $email->total, ' ', __('of', 'newsletter'), ' ', $email->total, '</div>';
1167
- } else {
1168
- echo '<div class="tnp-progress-numbers">', $email->sent, ' ', __('of', 'newsletter'), ' ', $email->total, '</div>';
1169
- }
1170
- }
1171
- }
1172
-
1173
- function get_email_type_label($type) {
1174
-
1175
- // Is an email?
1176
- if (is_object($type))
1177
- $type = $type->type;
1178
-
1179
- $label = apply_filters('newsletter_email_type', '', $type);
1180
-
1181
- if (!empty($label))
1182
- return $label;
1183
-
1184
- switch ($type) {
1185
- case 'followup':
1186
- return 'Followup';
1187
- case 'message':
1188
- return 'Standard Newsletter';
1189
- case 'feed':
1190
- return 'Feed by Mail';
1191
- }
1192
-
1193
- if (strpos($type, 'automated') === 0) {
1194
- list($a, $id) = explode('_', $type);
1195
- return 'Automated Channel ' . $id;
1196
- }
1197
-
1198
- return ucfirst($type);
1199
- }
1200
-
1201
- function get_email_progress_label($email) {
1202
- if ($email->status == 'sent' || $email->status == 'sending') {
1203
- return $email->sent . ' ' . __('of', 'newsletter') . ' ' . $email->total;
1204
- }
1205
- return '-';
1206
- }
1207
-
1208
- /**
1209
- * Returns the email unique key
1210
- * @param TNP_User $user
1211
- * @return string
1212
- */
1213
- function get_email_key($email) {
1214
- if (!isset($email->token)) {
1215
- return $email->id . '-';
1216
- }
1217
- return $email->id . '-' . $email->token;
1218
- }
1219
-
1220
- /** Searches for a user using the nk parameter or the ni and nt parameters. Tries even with the newsletter cookie.
1221
- * If found, the user object is returned or null.
1222
- * The user is returned without regards to his status that should be checked by caller.
1223
- *
1224
- * DO NOT REMOVE EVEN IF OLD
1225
- *
1226
- * @return TNP_User
1227
- */
1228
- function check_user($context = '') {
1229
- global $wpdb;
1230
-
1231
- $user = null;
1232
-
1233
- if (isset($_REQUEST['nk'])) {
1234
- list($id, $token) = @explode('-', $_REQUEST['nk'], 2);
1235
- } else if (isset($_COOKIE['newsletter'])) {
1236
- list ($id, $token) = @explode('-', $_COOKIE['newsletter'], 2);
1237
- }
1238
-
1239
- if (isset($id)) {
1240
- $user = $this->get_user($id);
1241
- if ($user) {
1242
- if ($context == 'preconfirm') {
1243
- if ($token != md5($user->token)) {
1244
- $user = null;
1245
- }
1246
- } else {
1247
- if ($token != $user->token) {
1248
- $user = null;
1249
- }
1250
- }
1251
- }
1252
- }
1253
-
1254
- if ($user == null && is_user_logged_in()) {
1255
- $user = $this->get_user_by_wp_user_id(get_current_user_id());
1256
- }
1257
- return $user;
1258
- }
1259
-
1260
- /** Returns the user identify by an id or an email. If $id_or_email is an object or an array, it is assumed it contains
1261
- * the "id" attribute or key and that is used to load the user.
1262
- *
1263
- * @global type $wpdb
1264
- * @param string|int|object|array $id_or_email
1265
- * @param string $format
1266
- * @return TNP_User|null
1267
- */
1268
- function get_user($id_or_email, $format = OBJECT) {
1269
- global $wpdb;
1270
-
1271
- if (empty($id_or_email))
1272
- return null;
1273
-
1274
- // To simplify the reaload of a user passing the user it self.
1275
- if (is_object($id_or_email)) {
1276
- $id_or_email = $id_or_email->id;
1277
- } else if (is_array($id_or_email)) {
1278
- $id_or_email = $id_or_email['id'];
1279
- }
1280
-
1281
- $id_or_email = strtolower(trim($id_or_email));
1282
-
1283
- if (is_numeric($id_or_email)) {
1284
- $r = $wpdb->get_row($wpdb->prepare("select * from " . NEWSLETTER_USERS_TABLE . " where id=%d limit 1", $id_or_email), $format);
1285
- } else {
1286
- $r = $wpdb->get_row($wpdb->prepare("select * from " . NEWSLETTER_USERS_TABLE . " where email=%s limit 1", $id_or_email), $format);
1287
- }
1288
-
1289
- if ($wpdb->last_error) {
1290
- $this->logger->error($wpdb->last_error);
1291
- return null;
1292
- }
1293
- return $r;
1294
- }
1295
-
1296
- /**
1297
- *
1298
- * @global wpdb $wpdb
1299
- * @param string $email
1300
- * @return TNP_User
1301
- */
1302
- function get_user_by_email($email) {
1303
- global $wpdb;
1304
-
1305
- $r = $wpdb->get_row($wpdb->prepare("select * from " . NEWSLETTER_USERS_TABLE . " where email=%s limit 1", $email));
1306
-
1307
- if ($wpdb->last_error) {
1308
- $this->logger->error($wpdb->last_error);
1309
- return null;
1310
- }
1311
- return $r;
1312
- }
1313
-
1314
- /**
1315
- * Accepts a user ID or a TNP_User object. Does not check if the user really exists.
1316
- *
1317
- * @param type $user
1318
- */
1319
- function get_user_edit_url($user) {
1320
- $id = $this->to_int_id($user);
1321
- return admin_url('admin.php') . '?page=newsletter_users_edit&id=' . $id;
1322
- }
1323
-
1324
- /**
1325
- * Returns the user unique key
1326
- * @param TNP_User $user
1327
- * @return string
1328
- */
1329
- function get_user_key($user, $context = '') {
1330
- if (empty($user->token)) {
1331
- $this->refresh_user_token($user);
1332
- }
1333
-
1334
- if ($context == 'preconfirm') {
1335
- return $user->id . '-' . md5($user->token);
1336
- }
1337
- return $user->id . '-' . $user->token;
1338
- }
1339
-
1340
- function get_user_status_label($user, $html = false) {
1341
- if (!$html) return TNP_User::get_status_label($user->status);
1342
-
1343
- $label = TNP_User::get_status_label($user->status);
1344
- $class = 'unknown';
1345
- switch ($user->status) {
1346
- case TNP_User::STATUS_NOT_CONFIRMED: $class = 'not-confirmed';
1347
- break;
1348
- case TNP_User::STATUS_CONFIRMED: $class = 'confirmed';
1349
- break;
1350
- case TNP_User::STATUS_UNSUBSCRIBED: $class = 'unsubscribed';
1351
- break;
1352
- case TNP_User::STATUS_BOUNCED: $class = 'bounced';
1353
- break;
1354
- case TNP_User::STATUS_COMPLAINED: $class = 'complained';
1355
- break;
1356
- }
1357
- return '<span class="' . $class . '">' . esc_html($label) . '</span>';
1358
- }
1359
-
1360
- /**
1361
- * Return the user identified by the "nk" parameter (POST or GET).
1362
- * If no user can be found or the token is not matching, returns null.
1363
- * If die_on_fail is true it dies instead of return null.
1364
- *
1365
- * @param bool $die_on_fail
1366
- * @return TNP_User
1367
- */
1368
- function get_user_from_request($die_on_fail = false, $context = '') {
1369
- $id = 0;
1370
- if (isset($_REQUEST['nk'])) {
1371
- list($id, $token) = @explode('-', $_REQUEST['nk'], 2);
1372
- }
1373
- $user = $this->get_user($id);
1374
-
1375
- if ($user == null) {
1376
- if ($die_on_fail) {
1377
- die(__('No subscriber found.', 'newsletter'));
1378
- } else {
1379
- return $this->get_user_from_logged_in_user();
1380
- }
1381
- }
1382
-
1383
- if ($context == 'preconfirm') {
1384
- $user_token = md5($user->token);
1385
- } else {
1386
- $user_token = $user->token;
1387
- }
1388
-
1389
- if ($token != $user_token) {
1390
- if ($die_on_fail) {
1391
- die(__('No subscriber found.', 'newsletter'));
1392
- } else {
1393
- return $this->get_user_from_logged_in_user();
1394
- }
1395
- }
1396
- return $user;
1397
- }
1398
-
1399
- function get_user_from_logged_in_user() {
1400
- if (is_user_logged_in()) {
1401
- return $this->get_user_by_wp_user_id(get_current_user_id());
1402
- }
1403
- return null;
1404
- }
1405
-
1406
- function get_user_count($refresh = false) {
1407
- global $wpdb;
1408
- $user_count = get_transient('newsletter_user_count');
1409
- if ($user_count === false || $refresh) {
1410
- $user_count = $wpdb->get_var("select count(*) from " . NEWSLETTER_USERS_TABLE . " where status='C'");
1411
- set_transient('newsletter_user_count', $user_count, DAY_IN_SECONDS);
1412
- }
1413
- return $user_count;
1414
- }
1415
-
1416
- function get_profile($id, $language = '') {
1417
- return TNP_Profile_Service::get_profile_by_id($id, $language);
1418
- }
1419
-
1420
- /**
1421
- * @param string $language The language for the list labels (it does not affect the lists returned)
1422
- * @return TNP_Profile[]
1423
- */
1424
- function get_profiles($language = '') {
1425
- return TNP_Profile_Service::get_profiles($language);
1426
- }
1427
-
1428
- /**
1429
- * Returns a list of TNP_Profile which are public.
1430
- *
1431
- * @staticvar array $profiles
1432
- * @param string $language
1433
- * @return TNP_Profile[]
1434
- */
1435
- function get_profiles_public($language = '') {
1436
- static $profiles = [];
1437
- if (isset($profiles[$language])) {
1438
- return $profiles[$language];
1439
- }
1440
-
1441
- $profiles[$language] = [];
1442
- $all = $this->get_profiles($language);
1443
- foreach ($all as $profile) {
1444
- if ($profile->is_private())
1445
- continue;
1446
-
1447
- $profiles[$language]['' . $profile->id] = $profile;
1448
- }
1449
- return $profiles[$language];
1450
- }
1451
-
1452
- /**
1453
- * Really bad name!
1454
- * @staticvar array $profiles
1455
- * @param type $language
1456
- * @return array
1457
- */
1458
- function get_profiles_for_profile($language = '') {
1459
- static $profiles = [];
1460
- if (isset($profiles[$language])) {
1461
- return $profiles[$language];
1462
- }
1463
-
1464
- $profiles[$language] = [];
1465
- $all = $this->get_profiles($language);
1466
- foreach ($all as $profile) {
1467
- if (!$profile->show_on_profile())
1468
- continue;
1469
-
1470
- $profiles[$language]['' . $profile->id] = $profile;
1471
- }
1472
- return $profiles[$language];
1473
- }
1474
-
1475
- /**
1476
- * @param string $language The language for the list labels (it does not affect the lists returned)
1477
- * @return TNP_List[]
1478
- */
1479
- function get_lists($language = '') {
1480
- static $lists = array();
1481
- if (isset($lists[$language])) {
1482
- return $lists[$language];
1483
- }
1484
-
1485
- $lists[$language] = array();
1486
- $data = NewsletterSubscription::instance()->get_options('lists', $language);
1487
- for ($i = 1; $i <= NEWSLETTER_LIST_MAX; $i++) {
1488
- if (empty($data['list_' . $i])) {
1489
- continue;
1490
- }
1491
- $list = $this->create_tnp_list_from_db_lists_array($data, $i);
1492
-
1493
- $lists[$language]['' . $list->id] = $list;
1494
- }
1495
- return $lists[$language];
1496
- }
1497
-
1498
- public function create_tnp_list_from_db_lists_array($db_lists_array, $list_id) {
1499
-
1500
- $list = new TNP_List();
1501
- $list->name = $db_lists_array['list_' . $list_id];
1502
- $list->id = $list_id;
1503
-
1504
- // New format
1505
- if (isset($db_lists_array['list_' . $list_id . '_subscription'])) {
1506
- $list->forced = !empty($db_lists_array['list_' . $list_id . '_forced']);
1507
- $list->status = empty($db_lists_array['list_' . $list_id . '_status']) ? TNP_List::STATUS_PRIVATE : TNP_List::STATUS_PUBLIC;
1508
- $list->checked = $db_lists_array['list_' . $list_id . '_subscription'] == 2;
1509
- $list->show_on_subscription = $list->status != TNP_List::STATUS_PRIVATE && !empty($db_lists_array['list_' . $list_id . '_subscription']) && !$list->forced;
1510
- $list->show_on_profile = $list->status != TNP_List::STATUS_PRIVATE && !empty($db_lists_array['list_' . $list_id . '_profile']);
1511
- } else {
1512
- $list->forced = !empty($db_lists_array['list_' . $list_id . '_forced']);
1513
- $list->status = empty($db_lists_array['list_' . $list_id . '_status']) ? TNP_List::STATUS_PRIVATE : TNP_List::STATUS_PUBLIC;
1514
- $list->checked = !empty($db_lists_array['list_' . $list_id . '_checked']);
1515
- $list->show_on_subscription = $db_lists_array['list_' . $list_id . '_status'] == 2 && !$list->forced;
1516
- $list->show_on_profile = $db_lists_array['list_' . $list_id . '_status'] == 1 || $db_lists_array['list_' . $list_id . '_status'] == 2;
1517
- }
1518
- if (empty($db_lists_array['list_' . $list_id . '_languages'])) {
1519
- $list->languages = array();
1520
- } else {
1521
- $list->languages = $db_lists_array['list_' . $list_id . '_languages'];
1522
- }
1523
-
1524
- return $list;
1525
- }
1526
-
1527
- /**
1528
- * Returns an array of TNP_List objects of lists that are public.
1529
- * @return TNP_List[]
1530
- */
1531
- function get_lists_public($language = '') {
1532
- static $lists = array();
1533
- if (isset($lists[$language])) {
1534
- return $lists[$language];
1535
- }
1536
-
1537
- $lists[$language] = array();
1538
- $all = $this->get_lists($language);
1539
- foreach ($all as $list) {
1540
- if ($list->status == TNP_List::STATUS_PRIVATE) {
1541
- continue;
1542
- }
1543
- $lists[$language]['' . $list->id] = $list;
1544
- }
1545
- return $lists[$language];
1546
- }
1547
-
1548
- /**
1549
- * Lists to be shown on subscription form.
1550
- *
1551
- * @return TNP_List[]
1552
- */
1553
- function get_lists_for_subscription($language = '') {
1554
- static $lists = array();
1555
- if (isset($lists[$language])) {
1556
- return $lists[$language];
1557
- }
1558
-
1559
- $lists[$language] = array();
1560
- $all = $this->get_lists($language);
1561
- foreach ($all as $list) {
1562
- if (!$list->show_on_subscription) {
1563
- continue;
1564
- }
1565
- $lists[$language]['' . $list->id] = $list;
1566
- }
1567
- return $lists[$language];
1568
- }
1569
-
1570
- /**
1571
- * Returns the lists to be shown in the profile page. The list is associative with
1572
- * the list ID as key.
1573
- *
1574
- * @return TNP_List[]
1575
- */
1576
- function get_lists_for_profile($language = '') {
1577
- static $lists = array();
1578
- if (isset($lists[$language])) {
1579
- return $lists[$language];
1580
- }
1581
-
1582
- $lists[$language] = array();
1583
- $all = $this->get_lists($language);
1584
- foreach ($all as $list) {
1585
- if (!$list->show_on_profile) {
1586
- continue;
1587
- }
1588
- $lists[$language]['' . $list->id] = $list;
1589
- }
1590
- return $lists[$language];
1591
- }
1592
-
1593
- /**
1594
- * Returns the list object or null if not found.
1595
- *
1596
- * @param int $id
1597
- * @return TNP_List
1598
- */
1599
- function get_list($id, $language = '') {
1600
- $lists = $this->get_lists($language);
1601
- if (!isset($lists['' . $id])) {
1602
- return null;
1603
- }
1604
-
1605
- return $lists['' . $id];
1606
- }
1607
-
1608
- /**
1609
- * NEVER CHANGE THIS METHOD SIGNATURE, USER BY THIRD PARTY PLUGINS.
1610
- *
1611
- * Saves a new user on the database. Return false if the email (that must be unique) is already
1612
- * there. For a new users set the token and creation time if not passed.
1613
- *
1614
- * @param array $user
1615
- * @return TNP_User|array|boolean Returns the subscriber reloaded from DB in the specified format. Flase on failure (duplicate email).
1616
- */
1617
- function save_user($user, $return_format = OBJECT) {
1618
- if (is_object($user)) {
1619
- $user = (array) $user;
1620
- }
1621
- if (empty($user['id'])) {
1622
- $existing = $this->get_user($user['email']);
1623
- if ($existing != null) {
1624
- return false;
1625
- }
1626
- if (empty($user['token'])) {
1627
- $user['token'] = NewsletterModule::get_token();
1628
- }
1629
- }
1630
-
1631
- // We still don't know when it happens but under some conditions, matbe external, lists are passed as NULL
1632
- foreach ($user as $key => $value) {
1633
- if (strpos($key, 'list_') !== 0) {
1634
- continue;
1635
- }
1636
- if (is_null($value)) {
1637
- unset($user[$key]);
1638
- } else {
1639
- $user[$key] = (int) $value;
1640
- }
1641
- }
1642
-
1643
- // Due to the unique index on email field, this can fail.
1644
- return $this->store->save(NEWSLETTER_USERS_TABLE, $user, $return_format);
1645
- }
1646
-
1647
- /**
1648
- * Updates the user last activity timestamp.
1649
- *
1650
- * @global wpdb $wpdb
1651
- * @param TNP_User $user
1652
- */
1653
- function update_user_last_activity($user) {
1654
- global $wpdb;
1655
- $this->query($wpdb->prepare("update " . NEWSLETTER_USERS_TABLE . " set last_activity=%d where id=%d limit 1", time(), $user->id));
1656
- }
1657
-
1658
- function update_user_ip($user, $ip) {
1659
- global $wpdb;
1660
- // Only if changed
1661
- $r = $this->query($wpdb->prepare("update " . NEWSLETTER_USERS_TABLE . " set ip=%s, geo=0 where ip<>%s and id=%d limit 1", $ip, $ip, $user->id));
1662
- }
1663
-
1664
- /**
1665
- * Finds single style blocks and adds a style attribute to every HTML tag with a class exactly matching the rules in the style
1666
- * block. HTML tags can use the attribute "inline-class" to exact match a style rules if they need a composite class definition.
1667
- *
1668
- * @param string $content
1669
- * @param boolean $strip_style_blocks
1670
- * @return string
1671
- */
1672
- function inline_css($content, $strip_style_blocks = false) {
1673
- $matches = array();
1674
- // "s" skips line breaks
1675
- $styles = preg_match('|<style>(.*?)</style>|s', $content, $matches);
1676
- if (isset($matches[1])) {
1677
- $style = str_replace(array("\n", "\r"), '', $matches[1]);
1678
- $rules = array();
1679
- preg_match_all('|\s*\.(.*?)\{(.*?)\}\s*|s', $style, $rules);
1680
- for ($i = 0; $i < count($rules[1]); $i++) {
1681
- $class = trim($rules[1][$i]);
1682
- $value = trim($rules[2][$i]);
1683
- $value = preg_replace('|\s+|', ' ', $value);
1684
- $content = str_replace(' class="' . $class . '"', ' class="' . $class . '" style="' . $value . '"', $content);
1685
- $content = str_replace(' inline-class="' . $class . '"', ' style="' . $value . '"', $content);
1686
- }
1687
- }
1688
-
1689
- if ($strip_style_blocks) {
1690
- return trim(preg_replace('|<style>.*?</style>|s', '', $content));
1691
- } else {
1692
- return $content;
1693
- }
1694
- }
1695
-
1696
- /**
1697
- * Returns a list of users marked as "test user".
1698
- * @return TNP_User[]
1699
- */
1700
- function get_test_users() {
1701
- return $this->store->get_all(NEWSLETTER_USERS_TABLE, "where test=1 and status in ('C', 'S')");
1702
- }
1703
-
1704
- /**
1705
- * Deletes a subscriber and cleans up all the stats table with his correlated data.
1706
- *
1707
- * @global wpdb $wpdb
1708
- * @param int|id[] $id
1709
- */
1710
- function delete_user($id) {
1711
- global $wpdb;
1712
- $id = (array) $id;
1713
- foreach ($id as $user_id) {
1714
- $user = $this->get_user($user_id);
1715
- if ($user) {
1716
- $r = $this->store->delete(NEWSLETTER_USERS_TABLE, $user_id);
1717
- $wpdb->delete(NEWSLETTER_STATS_TABLE, array('user_id' => $user_id));
1718
- $wpdb->delete(NEWSLETTER_SENT_TABLE, array('user_id' => $user_id));
1719
- do_action('newsletter_user_deleted', $user);
1720
- }
1721
- }
1722
-
1723
- return count($id);
1724
- }
1725
-
1726
- /**
1727
- * Add to a destination URL the parameters to identify the user, the email and to show
1728
- * an alert message, if required. The parameters are then managed by the [newsletter] shortcode.
1729
- *
1730
- * @param string $url If empty the standard newsletter page URL is used (usually it is empty, but sometime a custom URL has been specified)
1731
- * @param string $message_key The message identifier
1732
- * @param TNP_User|int $user
1733
- * @param TNP_Email|int $email
1734
- * @param string $alert An optional alter message to be shown. Does not work with custom URLs
1735
- * @return string The final URL with parameters
1736
- */
1737
- function build_message_url($url = '', $message_key = '', $user = null, $email = null, $alert = '') {
1738
- $params = 'nm=' . urlencode($message_key);
1739
- $language = '';
1740
- if ($user) {
1741
- if (!is_object($user)) {
1742
- $user = $this->get_user($user);
1743
- }
1744
- if ($message_key == 'confirmation') {
1745
- $params .= '&nk=' . urlencode($this->get_user_key($user, 'preconfirm'));
1746
- } else {
1747
- $params .= '&nk=' . urlencode($this->get_user_key($user));
1748
- }
1749
-
1750
- $language = $this->get_user_language($user);
1751
- }
1752
-
1753
- if ($email) {
1754
- if (!is_object($email)) {
1755
- $email = $this->get_email($email);
1756
- }
1757
- $params .= '&nek=' . urlencode($this->get_email_key($email));
1758
- }
1759
-
1760
- if ($alert) {
1761
- $params .= '&alert=' . urlencode($alert);
1762
- }
1763
-
1764
- if (empty($url)) {
1765
- $url = Newsletter::instance()->get_newsletter_page_url($language);
1766
- }
1767
-
1768
- return self::add_qs($url, $params, false);
1769
- }
1770
-
1771
- /**
1772
- * Builds a standard Newsletter action URL for the specified action.
1773
- *
1774
- * @param string $action
1775
- * @param TNP_User $user
1776
- * @param TNP_Email $email
1777
- * @return string
1778
- */
1779
- function build_action_url($action, $user = null, $email = null) {
1780
- $url = $this->add_qs($this->get_home_url(), 'na=' . urlencode($action));
1781
- //$url = $this->add_qs(admin_url('admin-ajax.php'), 'action=newsletter&na=' . urlencode($action));
1782
- if ($user) {
1783
- $url .= '&nk=' . urlencode($this->get_user_key($user));
1784
- }
1785
- if ($email) {
1786
- $url .= '&nek=' . urlencode($this->get_email_key($email));
1787
- }
1788
- return $url;
1789
- }
1790
-
1791
- function get_subscribe_url() {
1792
- return $this->build_action_url('s');
1793
- }
1794
-
1795
- function clean_stats_table() {
1796
- global $wpdb;
1797
- $this->logger->info('Cleaning up stats table');
1798
- $this->query("delete s from `{$wpdb->prefix}newsletter_stats` s left join `{$wpdb->prefix}newsletter` u on s.user_id=u.id where u.id is null");
1799
- $this->query("delete s from `{$wpdb->prefix}newsletter_stats` s left join `{$wpdb->prefix}newsletter_emails` e on s.email_id=e.id where e.id is null");
1800
- }
1801
-
1802
- function clean_sent_table() {
1803
- global $wpdb;
1804
- $this->logger->info('Cleaning up sent table');
1805
- $this->query("delete s from `{$wpdb->prefix}newsletter_sent` s left join `{$wpdb->prefix}newsletter` u on s.user_id=u.id where u.id is null");
1806
- $this->query("delete s from `{$wpdb->prefix}newsletter_sent` s left join `{$wpdb->prefix}newsletter_emails` e on s.email_id=e.id where e.id is null");
1807
- }
1808
-
1809
- function clean_user_logs_table() {
1810
- //global $wpdb;
1811
- }
1812
-
1813
- function clean_tables() {
1814
- $this->clean_sent_table();
1815
- $this->clean_stats_table();
1816
- $this->clean_user_logs_table();
1817
- }
1818
-
1819
- function anonymize_ip($ip) {
1820
- if (empty($ip)) {
1821
- return $ip;
1822
- }
1823
- $parts = explode('.', $ip);
1824
- array_pop($parts);
1825
- return implode('.', $parts) . '.0';
1826
- }
1827
-
1828
- function process_ip($ip) {
1829
- $option = Newsletter::instance()->options['ip'];
1830
- if (empty($option)) {
1831
- return $ip;
1832
- }
1833
- if ($option == 'anonymize') {
1834
- return $this->anonymize_ip($ip);
1835
- }
1836
- return '';
1837
- }
1838
-
1839
- function anonymize_user($id) {
1840
- global $wpdb;
1841
- $user = $this->get_user($id);
1842
- if (!$user) {
1843
- return null;
1844
- }
1845
-
1846
- $user->name = '';
1847
- $user->surname = '';
1848
- $user->ip = $this->anonymize_ip($user->ip);
1849
-
1850
- for ($i = 1; $i <= NEWSLETTER_PROFILE_MAX; $i++) {
1851
- $field = 'profile_' . $i;
1852
- $user->$field = '';
1853
- }
1854
-
1855
- // [TODO] Status?
1856
- $user->status = TNP_User::STATUS_UNSUBSCRIBED;
1857
- $user->email = $user->id . '@anonymi.zed';
1858
-
1859
- $user = $this->save_user($user);
1860
-
1861
- return $user;
1862
- }
1863
-
1864
- /**
1865
- * Changes a user status. Accept a user object, user id or user email.
1866
- *
1867
- * @param TNP_User $user
1868
- * @param string $status
1869
- * @return TNP_User
1870
- */
1871
- function set_user_status($user, $status) {
1872
- global $wpdb;
1873
-
1874
- $this->logger->debug('Status change to ' . $status . ' of subscriber ' . $user->id . ' from ' . $_SERVER['REQUEST_URI']);
1875
-
1876
- $this->query($wpdb->prepare("update " . NEWSLETTER_USERS_TABLE . " set status=%s where id=%d limit 1", $status, $user->id));
1877
- $user->status = $status;
1878
- return $this->get_user($user);
1879
- }
1880
-
1881
- /**
1882
- *
1883
- * @global wpdb $wpdb
1884
- * @param TNP_User $user
1885
- * @return TNP_User
1886
- */
1887
- function refresh_user_token($user) {
1888
- global $wpdb;
1889
-
1890
- $token = $this->get_token();
1891
-
1892
- $this->query($wpdb->prepare("update " . NEWSLETTER_USERS_TABLE . " set token=%s where id=%d limit 1", $token, $user->id));
1893
- $user->token = $token;
1894
- }
1895
-
1896
- /**
1897
- * Create a log entry with the meaningful user data.
1898
- *
1899
- * @global wpdb $wpdb
1900
- * @param TNP_User $user
1901
- * @param string $source
1902
- * @return type
1903
- */
1904
- function add_user_log($user, $source = '') {
1905
- global $wpdb;
1906
-
1907
- $lists = $this->get_lists_public();
1908
- foreach ($lists as $list) {
1909
- $field_name = 'list_' . $list->id;
1910
- $data[$field_name] = $user->$field_name;
1911
- }
1912
- $data['status'] = $user->status;
1913
- $ip = $this->get_remote_ip();
1914
- $ip = $this->process_ip($ip);
1915
- $this->store->save($wpdb->prefix . 'newsletter_user_logs', array('ip' => $ip, 'user_id' => $user->id, 'source' => $source, 'created' => time(), 'data' => json_encode($data)));
1916
- }
1917
-
1918
- /**
1919
- *
1920
- * @global wpdb $wpdb
1921
- * @param TNP_User $user
1922
- * @param int $list
1923
- * @param type $value
1924
- */
1925
- function set_user_list($user, $list, $value) {
1926
- global $wpdb;
1927
-
1928
- $list = (int) $list;
1929
- $value = $value ? 1 : 0;
1930
- $r = $wpdb->update(NEWSLETTER_USERS_TABLE, array('list_' . $list => $value), array('id' => $user->id));
1931
- }
1932
-
1933
- function set_user_field($id, $field, $value) {
1934
- $this->store->set_field(NEWSLETTER_USERS_TABLE, $id, $field, $value);
1935
- }
1936
-
1937
- function set_user_wp_user_id($user_id, $wp_user_id) {
1938
- $this->store->set_field(NEWSLETTER_USERS_TABLE, $user_id, 'wp_user_id', $wp_user_id);
1939
- }
1940
-
1941
- /**
1942
- *
1943
- * @param int $wp_user_id
1944
- * @param string $format
1945
- * @return TNP_User
1946
- */
1947
- function get_user_by_wp_user_id($wp_user_id, $format = OBJECT) {
1948
- return $this->store->get_single_by_field(NEWSLETTER_USERS_TABLE, 'wp_user_id', $wp_user_id, $format);
1949
- }
1950
-
1951
- /**
1952
- * Returns the user language IF there is a supported mutilanguage plugin installed.
1953
- * @param TNP_User $user
1954
- * @return string Language code or empty
1955
- */
1956
- function get_user_language($user) {
1957
- if ($user && $this->is_multilanguage()) {
1958
- return $user->language;
1959
- }
1960
- return '';
1961
- }
1962
-
1963
- /**
1964
- * Replaces every possible Newsletter tag ({...}) in a piece of text or HTML.
1965
- *
1966
- * @global wpdb $wpdb
1967
- * @param string $text
1968
- * @param mixed $user Can be an object, associative array or id
1969
- * @param mixed $email Can be an object, associative array or id
1970
- * @param type $referrer
1971
- * @return type
1972
- */
1973
- function replace($text, $user = null, $email = null, $referrer = null) {
1974
- global $wpdb;
1975
-
1976
- if (strpos($text, '<p') !== false) {
1977
- $esc_html = true;
1978
- } else {
1979
- $esc_html = false;
1980
- }
1981
-
1982
- static $home_url = false;
1983
-
1984
- if (!$home_url) {
1985
- $home_url = home_url('/');
1986
- }
1987
-
1988
- //$this->logger->debug('Replace start');
1989
- if ($user !== null && !is_object($user)) {
1990
- if (is_array($user)) {
1991
- $user = (object) $user;
1992
- } else if (is_numeric($user)) {
1993
- $user = $this->get_user($user);
1994
- } else {
1995
- $user = null;
1996
- }
1997
- }
1998
-
1999
- if ($email !== null && !is_object($email)) {
2000
- if (is_array($email)) {
2001
- $email = (object) $email;
2002
- } else if (is_numeric($email)) {
2003
- $email = $this->get_email($email);
2004
- } else {
2005
- $email = null;
2006
- }
2007
- }
2008
-
2009
- $initial_language = $this->get_current_language();
2010
-
2011
- if ($user && $user->language) {
2012
- $this->switch_language($user->language);
2013
- }
2014
-
2015
-
2016
- $text = apply_filters('newsletter_replace', $text, $user, $email, $esc_html);
2017
-
2018
- $text = $this->replace_url($text, 'blog_url', $home_url);
2019
- $text = $this->replace_url($text, 'home_url', $home_url);
2020
-
2021
- $text = str_replace('{blog_title}', html_entity_decode(get_bloginfo('name')), $text);
2022
- $text = str_replace('{blog_description}', get_option('blogdescription'), $text);
2023
-
2024
- $text = $this->replace_date($text);
2025
-
2026
- if ($user) {
2027
- //$this->logger->debug('Replace with user ' . $user->id);
2028
- $nk = $this->get_user_key($user);
2029
- $options_profile = NewsletterSubscription::instance()->get_options('profile', $this->get_user_language($user));
2030
- $text = str_replace('{email}', $user->email, $text);
2031
- $name = apply_filters('newsletter_replace_name', $user->name, $user);
2032
- if (empty($name)) {
2033
- $text = str_replace(' {name}', '', $text);
2034
- $text = str_replace('{name}', '', $text);
2035
- } else {
2036
- $text = str_replace('{name}', esc_html($name), $text);
2037
- }
2038
-
2039
- switch ($user->sex) {
2040
- case 'm': $text = str_replace('{title}', $options_profile['title_male'], $text);
2041
- break;
2042
- case 'f': $text = str_replace('{title}', $options_profile['title_female'], $text);
2043
- break;
2044
- case 'n': $text = str_replace('{title}', $options_profile['title_none'], $text);
2045
- break;
2046
- default:
2047
- $text = str_replace('{title}', '', $text);
2048
- }
2049
-
2050
-
2051
- // Deprecated
2052
- $text = str_replace('{surname}', esc_html($user->surname), $text);
2053
- $text = str_replace('{last_name}', esc_html($user->surname), $text);
2054
-
2055
- $full_name = esc_html(trim($user->name . ' ' . $user->surname));
2056
- if (empty($full_name)) {
2057
- $text = str_replace(' {full_name}', '', $text);
2058
- $text = str_replace('{full_name}', '', $text);
2059
- } else {
2060
- $text = str_replace('{full_name}', $full_name, $text);
2061
- }
2062
-
2063
- $text = str_replace('{token}', $user->token, $text);
2064
- $text = str_replace('%7Btoken%7D', $user->token, $text);
2065
- $text = str_replace('{id}', $user->id, $text);
2066
- $text = str_replace('%7Bid%7D', $user->id, $text);
2067
- $text = str_replace('{ip}', $user->ip, $text);
2068
- $text = str_replace('{key}', $nk, $text);
2069
- $text = str_replace('%7Bkey%7D', $nk, $text);
2070
-
2071
- for ($i = 1; $i <= NEWSLETTER_PROFILE_MAX; $i++) {
2072
- $p = 'profile_' . $i;
2073
- $text = str_replace('{profile_' . $i . '}', $user->$p, $text);
2074
- }
2075
-
2076
- $base = (empty($this->options_main['url']) ? get_option('home') : $this->options_main['url']);
2077
- $id_token = '&amp;ni=' . $user->id . '&amp;nt=' . $user->token;
2078
-
2079
- $text = $this->replace_url($text, 'subscription_confirm_url', $this->build_action_url('c', $user));
2080
- $text = $this->replace_url($text, 'activation_url', $this->build_action_url('c', $user));
2081
-
2082
- // Obsolete.
2083
- $text = $this->replace_url($text, 'FOLLOWUP_SUBSCRIPTION_URL', self::add_qs($base, 'nm=fs' . $id_token));
2084
- $text = $this->replace_url($text, 'FOLLOWUP_UNSUBSCRIPTION_URL', self::add_qs($base, 'nm=fu' . $id_token));
2085
-
2086
- $text = $this->replace_url($text, 'UNLOCK_URL', $this->build_action_url('ul', $user));
2087
- } else {
2088
- //$this->logger->debug('Replace without user');
2089
- $text = $this->replace_url($text, 'subscription_confirm_url', '#');
2090
- $text = $this->replace_url($text, 'activation_url', '#');
2091
- }
2092
-
2093
- if ($email) {
2094
- //$this->logger->debug('Replace with email ' . $email->id);
2095
- $nek = $this->get_email_key($email);
2096
- $text = str_replace('{email_id}', $email->id, $text);
2097
- $text = str_replace('{email_key}', $nek, $text);
2098
- $text = str_replace('{email_subject}', $email->subject, $text);
2099
- // Deprecated
2100
- $text = str_replace('{subject}', $email->subject, $text);
2101
- $text = $this->replace_url($text, 'email_url', $this->build_action_url('v', $user) . '&id=' . $email->id);
2102
- } else {
2103
- //$this->logger->debug('Replace without email');
2104
- $text = $this->replace_url($text, 'email_url', '#');
2105
- }
2106
-
2107
- if (strpos($text, '{subscription_form}') !== false) {
2108
- $text = str_replace('{subscription_form}', NewsletterSubscription::instance()->get_subscription_form($referrer), $text);
2109
- } else {
2110
- for ($i = 1; $i <= 10; $i++) {
2111
- if (strpos($text, "{subscription_form_$i}") !== false) {
2112
- $text = str_replace("{subscription_form_$i}", NewsletterSubscription::instance()->get_form($i), $text);
2113
- break;
2114
- }
2115
- }
2116
- }
2117
-
2118
- // Company info
2119
- // TODO: Move to another module
2120
- $options = Newsletter::instance()->get_options('info');
2121
- $text = str_replace('{company_address}', $options['footer_contact'], $text);
2122
- $text = str_replace('{company_name}', $options['footer_title'], $text);
2123
- $text = str_replace('{company_legal}', $options['footer_legal'], $text);
2124
-
2125
- $this->switch_language($initial_language);
2126
- //$this->logger->debug('Replace end');
2127
- return $text;
2128
- }
2129
-
2130
- function replace_date($text) {
2131
- $text = str_replace('{date}', date_i18n(get_option('date_format')), $text);
2132
-
2133
- // Date processing
2134
- $x = 0;
2135
- while (($x = strpos($text, '{date_', $x)) !== false) {
2136
- $y = strpos($text, '}', $x);
2137
- if ($y === false)
2138
- continue;
2139
- $f = substr($text, $x + 6, $y - $x - 6);
2140
- $text = substr($text, 0, $x) . date_i18n($f) . substr($text, $y + 1);
2141
- }
2142
- return $text;
2143
- }
2144
-
2145
- function replace_url($text, $tag, $url) {
2146
- static $home = false;
2147
- if (!$home) {
2148
- $home = trailingslashit(home_url());
2149
- }
2150
- $tag_lower = strtolower($tag);
2151
- $text = str_replace('http://{' . $tag_lower . '}', $url, $text);
2152
- $text = str_replace('https://{' . $tag_lower . '}', $url, $text);
2153
- $text = str_replace($home . '{' . $tag_lower . '}', $url, $text);
2154
- $text = str_replace($home . '%7B' . $tag_lower . '%7D', $url, $text);
2155
- $text = str_replace('{' . $tag_lower . '}', $url, $text);
2156
- $text = str_replace('%7B' . $tag_lower . '%7D', $url, $text);
2157
-
2158
- $url_encoded = urlencode($url);
2159
- $text = str_replace('%7B' . $tag_lower . '_encoded%7D', $url_encoded, $text);
2160
- $text = str_replace('{' . $tag_lower . '_encoded}', $url_encoded, $text);
2161
-
2162
- // for compatibility
2163
- $text = str_replace($home . $tag, $url, $text);
2164
-
2165
- return $text;
2166
- }
2167
-
2168
- public static function antibot_form_check($captcha = false) {
2169
-
2170
- if (defined('NEWSLETTER_ANTIBOT') && !NEWSLETTER_ANTIBOT) {
2171
- return true;
2172
- }
2173
-
2174
- if (strtolower($_SERVER['REQUEST_METHOD']) != 'post') {
2175
- return false;
2176
- }
2177
-
2178
- if (!isset($_POST['ts']) || time() - $_POST['ts'] > 60) {
2179
- return false;
2180
- }
2181
-
2182
- if ($captcha) {
2183
- $n1 = (int) $_POST['n1'];
2184
- if (empty($n1)) {
2185
- return false;
2186
- }
2187
- $n2 = (int) $_POST['n2'];
2188
- if (empty($n2)) {
2189
- return false;
2190
- }
2191
- $n3 = (int) $_POST['n3'];
2192
- if ($n1 + $n2 != $n3) {
2193
- return false;
2194
- }
2195
- }
2196
-
2197
- return true;
2198
- }
2199
-
2200
- public static function request_to_antibot_form($submit_label = 'Continue...', $captcha = false) {
2201
- header('Content-Type: text/html;charset=UTF-8');
2202
- header('X-Robots-Tag: noindex,nofollow,noarchive');
2203
- header('Cache-Control: no-cache,no-store,private');
2204
- echo "<!DOCTYPE html>\n";
2205
- echo '<html><head>'
2206
- . '<style type="text/css">'
2207
- . '.tnp-captcha {text-align: center; margin: 200px auto 0 auto !important; max-width: 300px !important; padding: 10px !important; font-family: "Open Sans", sans-serif; background: #ECF0F1; border-radius: 5px; padding: 50px !important; border: none !important;}'
2208
- . 'p {text-align: center; padding: 10px; color: #7F8C8D;}'
2209
- . 'input[type=text] {width: 50px; padding: 10px 10px; border: none; border-radius: 2px; margin: 0px 5px;}'
2210
- . 'input[type=submit] {text-align: center; border: none; padding: 10px 15px; font-family: "Open Sans", sans-serif; background-color: #27AE60; color: white; cursor: pointer;}'
2211
- . '</style>'
2212
- . '</head><body>';
2213
- echo '<form method="post" action="https://www.domain.tld" id="form">';
2214
- echo '<div style="width: 1px; height: 1px; overflow: hidden">';
2215
- foreach ($_REQUEST as $name => $value) {
2216
- if ($name == 'submit')
2217
- continue;
2218
- if (is_array($value)) {
2219
- foreach ($value as $element) {
2220
- echo '<input type="text" name="';
2221
- echo esc_attr($name);
2222
- echo '[]" value="';
2223
- echo esc_attr(stripslashes($element));
2224
- echo '">';
2225
- }
2226
- } else {
2227
- echo '<input type="hidden" name="', esc_attr($name), '" value="', esc_attr(stripslashes($value)), '">';
2228
- }
2229
- }
2230
- if (isset($_SERVER['HTTP_REFERER'])) {
2231
- echo '<input type="hidden" name="nhr" value="' . esc_attr($_SERVER['HTTP_REFERER']) . '">';
2232
- }
2233
- echo '<input type="hidden" name="ts" value="' . time() . '">';
2234
- echo '</div>';
2235
-
2236
- if ($captcha) {
2237
- echo '<div class="tnp-captcha">';
2238
- echo '<p>', __('Math question', 'newsletter'), '</p>';
2239
- echo '<input type="text" name="n1" value="', rand(1, 9), '" readonly style="width: 50px">';
2240
- echo '+';
2241
- echo '<input type="text" name="n2" value="', rand(1, 9), '" readonly style="width: 50px">';
2242
- echo '=';
2243
- echo '<input type="text" name="n3" value="?" style="width: 50px">';
2244
- echo '<br><br>';
2245
- echo '<input type="submit" value="', esc_attr($submit_label), '">';
2246
- echo '</div>';
2247
- }
2248
- echo '<noscript><input type="submit" value="';
2249
- echo esc_attr($submit_label);
2250
- echo '"></noscript></form>';
2251
- echo '<script>';
2252
- echo 'document.getElementById("form").action="' . home_url('/') . '";';
2253
- if (!$captcha) {
2254
- echo 'document.getElementById("form").submit();';
2255
- }
2256
- echo '</script>';
2257
- echo '</body></html>';
2258
- die();
2259
- }
2260
-
2261
- static function extract_body($html) {
2262
- $x = stripos($html, '<body');
2263
- if ($x !== false) {
2264
- $x = strpos($html, '>', $x);
2265
- $y = strpos($html, '</body>');
2266
- return substr($html, $x + 1, $y - $x - 1);
2267
- } else {
2268
- return $html;
2269
- }
2270
- }
2271
-
2272
- /** Returns a percentage as string */
2273
- static function percent($value, $total) {
2274
- if ($total == 0)
2275
- return '-';
2276
- return sprintf("%.2f", $value / $total * 100) . '%';
2277
- }
2278
-
2279
- /** Returns a percentage as integer value */
2280
- static function percentValue($value, $total) {
2281
- if ($total == 0)
2282
- return 0;
2283
- return round($value / $total * 100);
2284
- }
2285
-
2286
- /**
2287
- * Takes in a variable and checks if object, array or scalar and return the integer representing
2288
- * a database record id.
2289
- *
2290
- * @param mixed $var
2291
- * @return in
2292
- */
2293
- static function to_int_id($var) {
2294
- if (is_object($var)) {
2295
- return (int) $var->id;
2296
- }
2297
- if (is_array($var)) {
2298
- return (int) $var['id'];
2299
- }
2300
- return (int) $var;
2301
- }
2302
-
2303
- static function to_array($text) {
2304
- $text = trim($text);
2305
- if (empty($text)) {
2306
- return array();
2307
- }
2308
- $text = preg_split("/\\r\\n/", $text);
2309
- $text = array_map('trim', $text);
2310
- $text = array_map('strtolower', $text);
2311
- $text = array_filter($text);
2312
-
2313
- return $text;
2314
- }
2315
-
2316
- static function sanitize_ip($ip) {
2317
- if (empty($ip)) {
2318
- return '';
2319
- }
2320
- return preg_replace('/[^0-9a-fA-F:., ]/', '', trim($ip));
2321
- }
2322
-
2323
- static function get_remote_ip() {
2324
- $ip = '';
2325
- if (!empty($_SERVER['HTTP_X_REAL_IP'])) {
2326
- $ip = $_SERVER['HTTP_X_REAL_IP'];
2327
- } elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
2328
- // Sometimes this is a list of IPs representing the chain of proxies
2329
- $ip = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR'], 1);
2330
- $ip = $ip[0];
2331
- } elseif (isset($_SERVER['REMOTE_ADDR'])) {
2332
- $ip = $_SERVER['REMOTE_ADDR'];
2333
- }
2334
- return self::sanitize_ip($ip);
2335
- }
2336
-
2337
- static function get_signature($text) {
2338
- $key = NewsletterStatistics::instance()->options['key'];
2339
- return md5($text . $key);
2340
- }
2341
-
2342
- static function check_signature($text, $signature) {
2343
- if (empty($signature)) {
2344
- return false;
2345
- }
2346
- $key = NewsletterStatistics::instance()->options['key'];
2347
- return md5($text . $key) === $signature;
2348
- }
2349
-
2350
- static function get_home_url() {
2351
- static $url = false;
2352
- if (!$url) {
2353
- $url = home_url('/');
2354
- }
2355
- return $url;
2356
- }
2357
-
2358
- static function clean_eol($text) {
2359
- $text = str_replace("\r\n", "\n", $text);
2360
- $text = str_replace("\r", "\n", $text);
2361
- $text = str_replace("\n", "\r\n", $text);
2362
- return $text;
2363
- }
2364
-
2365
- function set_current_language($language) {
2366
- self::$current_language = $language;
2367
- }
2368
-
2369
- /**
2370
- * Return the current language code. Optionally, if a user is passed and it has a language
2371
- * the user language is returned.
2372
- * If there is no language available, an empty string is returned.
2373
- *
2374
- * @param TNP_User $user
2375
- * @return string The language code
2376
- */
2377
- function get_current_language($user = null) {
2378
-
2379
- if ($user && $user->language) {
2380
- return $user->language;
2381
- }
2382
-
2383
- if (!empty(self::$current_language)) {
2384
- return self::$current_language;
2385
- }
2386
-
2387
- // WPML
2388
- if (class_exists('SitePress')) {
2389
- $current_language = apply_filters('wpml_current_language', '');
2390
- if ($current_language == 'all') {
2391
- $current_language = '';
2392
- }
2393
- return $current_language;
2394
- }
2395
-
2396
- // Polylang
2397
- if (function_exists('pll_current_language')) {
2398
- return pll_current_language();
2399
- }
2400
-
2401
- // Trnslatepress and/or others
2402
- $current_language = apply_filters('newsletter_current_language', '');
2403
-
2404
- return $current_language;
2405
- }
2406
-
2407
- function get_default_language() {
2408
- if (class_exists('SitePress')) {
2409
- return $current_language = apply_filters('wpml_current_language', '');
2410
- } else if (function_exists('pll_default_language')) {
2411
- return pll_default_language();
2412
- } else if (class_exists('TRP_Translate_Press')) {
2413
- // TODO: Find the default language
2414
- }
2415
- return '';
2416
- }
2417
-
2418
- function is_all_languages() {
2419
- return $this->get_current_language() == '';
2420
- }
2421
-
2422
- function is_default_language() {
2423
- return $this->get_current_language() == $this->get_default_language();
2424
- }
2425
-
2426
- /**
2427
- * Returns an array of languages with key the language code and value the language name.
2428
- * An empty array is returned if no language is available.
2429
- */
2430
- function get_languages() {
2431
- $language_options = array();
2432
-
2433
- if (class_exists('SitePress')) {
2434
- $languages = apply_filters('wpml_active_languages', null);
2435
- foreach ($languages as $language) {
2436
- $language_options[$language['language_code']] = $language['translated_name'];
2437
- }
2438
- return $language_options;
2439
- } else if (function_exists('icl_get_languages')) {
2440
- $languages = icl_get_languages();
2441
- foreach ($languages as $code => $language) {
2442
- $language_options[$code] = $language['native_name'];
2443
- }
2444
- return $language_options;
2445
- }
2446
-
2447
- return apply_filters('newsletter_languages', $language_options);
2448
- }
2449
-
2450
- function get_language_label($language) {
2451
- $languages = $this->get_languages();
2452
- if (isset($languages[$language])) {
2453
- return $languages[$language];
2454
- }
2455
- return '';
2456
- }
2457
-
2458
- /**
2459
- * Changes the current language usually before extracting the posts since WPML
2460
- * does not support the language filter in the post query (or at least we didn't
2461
- * find it).
2462
- *
2463
- * @param string $language
2464
- */
2465
- function switch_language($language) {
2466
- if (class_exists('SitePress')) {
2467
- if (empty($language)) {
2468
- $language = 'all';
2469
- }
2470
- do_action('wpml_switch_language', $language);
2471
- return;
2472
- }
2473
- }
2474
-
2475
- function is_multilanguage() {
2476
- return class_exists('SitePress') || function_exists('pll_default_language') || class_exists('TRP_Translate_Press');
2477
- }
2478
-
2479
- function get_posts($filters = array(), $language = '') {
2480
- $current_language = $this->get_current_language();
2481
-
2482
- // Language switch for WPML
2483
- if ($language) {
2484
- if (class_exists('SitePress')) {
2485
- $this->switch_language($language);
2486
- $filters['suppress_filters'] = false;
2487
- }
2488
- if (class_exists('Polylang')) {
2489
- $filters['lang'] = $language;
2490
- }
2491
- }
2492
- $posts = get_posts($filters);
2493
- if ($language) {
2494
- if (class_exists('SitePress')) {
2495
- $this->switch_language($current_language);
2496
- }
2497
- }
2498
- return $posts;
2499
- }
2500
-
2501
- function get_wp_query($filters, $langiage = '') {
2502
- if ($language) {
2503
- if (class_exists('SitePress')) {
2504
- $this->switch_language($language);
2505
- $filters['suppress_filters'] = false;
2506
- }
2507
- if (class_exists('Polylang')) {
2508
- $filters['lang'] = $language;
2509
- }
2510
- }
2511
-
2512
- $posts = new WP_Query($filters);
2513
-
2514
- if ($language) {
2515
- if (class_exists('SitePress')) {
2516
- $this->switch_language($current_language);
2517
- }
2518
- }
2519
-
2520
- return $posts;
2521
- }
2522
-
2523
- protected function generate_admin_notification_message($user) {
2524
-
2525
- $message = file_get_contents(__DIR__ . '/notification.html');
2526
-
2527
- $message = $this->replace($message, $user);
2528
- $message = str_replace('{user_admin_url}', admin_url('admin.php?page=newsletter_users_edit&id=' . $user->id), $message);
2529
-
2530
- return $message;
2531
- }
2532
-
2533
- protected function generate_admin_notification_subject($subject) {
2534
- $blogname = wp_specialchars_decode(get_option('blogname'), ENT_QUOTES);
2535
-
2536
- return '[' . $blogname . '] ' . $subject;
2537
- }
2538
-
2539
- function dienow($message, $admin_message = null, $http_code = 200) {
2540
- if ($admin_message && current_user_can('administrator')) {
2541
- $message .= '<br><br><strong>Text below only visibile to administrators</strong><br>';
2542
- $message .= $admin_message;
2543
- }
2544
- wp_die($message, $http_code);
2545
- }
2546
-
2547
- function dump($var) {
2548
- if (NEWSLETTER_DEBUG) {
2549
- var_dump($var);
2550
- }
2551
- }
2552
-
2553
- function dump_die($var) {
2554
- if (NEWSLETTER_DEBUG) {
2555
- var_dump($var);
2556
- die();
2557
- }
2558
- }
2559
-
2560
- }
2561
-
2562
- /**
2563
- * Kept for compatibility.
2564
- *
2565
- * @param type $post_id
2566
- * @param type $size
2567
- * @param type $alternative
2568
- * @return type
2569
- */
2570
- function nt_post_image($post_id = null, $size = 'thumbnail', $alternative = null) {
2571
- return NewsletterModule::get_post_image($post_id, $size, $alternative);
2572
- }
2573
-
2574
- function newsletter_get_post_image($post_id = null, $size = 'thumbnail', $alternative = null) {
2575
- echo NewsletterModule::get_post_image($post_id, $size, $alternative);
2576
- }
2577
-
2578
- /**
2579
- * Accepts a post or a post ID.
2580
- *
2581
- * @param WP_Post $post
2582
- */
2583
- function newsletter_the_excerpt($post, $words = 30) {
2584
- $post = get_post($post);
2585
- $excerpt = $post->post_excerpt;
2586
- if (empty($excerpt)) {
2587
- $excerpt = $post->post_content;
2588
- $excerpt = strip_shortcodes($excerpt);
2589
- $excerpt = wp_strip_all_tags($excerpt, true);
2590
- }
2591
- echo '<p>' . wp_trim_words($excerpt, $words) . '</p>';
2592
- }
1
+ <?php
2
+
3
+ defined('ABSPATH') || exit;
4
+
5
+ require_once __DIR__ . '/logger.php';
6
+ require_once __DIR__ . '/store.php';
7
+ require_once __DIR__ . '/composer.php';
8
+ require_once __DIR__ . '/addon.php';
9
+ require_once __DIR__ . '/mailer.php';
10
+ require_once __DIR__ . '/themes.php';
11
+
12
+ class TNP_Media {
13
+
14
+ var $id;
15
+ var $url;
16
+ var $width;
17
+ var $height;
18
+ var $alt;
19
+ var $link;
20
+ var $align = 'center';
21
+
22
+ /** Sets the width recalculating the height */
23
+ public function set_width($width) {
24
+ $width = (int)$width;
25
+ if (empty($width)) return;
26
+ if ($this->width < $width) return;
27
+ $this->height = floor(($width / $this->width) * $this->height);
28
+ $this->width = $width;
29
+ }
30
+
31
+ /** Sets the height recalculating the width */
32
+ public function set_height($height) {
33
+ $height = (int) $height;
34
+ $this->width = floor(($height / $this->height) * $this->width);
35
+ $this->height = $height;
36
+ }
37
+
38
+ }
39
+
40
+ /**
41
+ * @property int $id The list unique identifier
42
+ * @property string $name The list name
43
+ * @property bool $forced If the list must be added to every new subscriber
44
+ * @property int $status When and how the list is visible to the subscriber - see constants
45
+ * @property bool $checked If it must be pre-checked on subscription form
46
+ * @property array $languages The list of language used to pre-assign this list
47
+ */
48
+ class TNP_List {
49
+
50
+ const STATUS_PRIVATE = 0;
51
+ const STATUS_PUBLIC = 1;
52
+ const SUBSCRIPTION_HIDE = 0;
53
+ const SUBSCRIPTION_SHOW = 1;
54
+ const SUBSCRIPTION_SHOW_CHECKED = 2;
55
+ const PROFILE_HIDE = 0;
56
+ const PROFILE_SHOW = 1;
57
+
58
+ var $id;
59
+ var $name;
60
+ var $status;
61
+ var $forced;
62
+ var $checked;
63
+ var $show_on_subscription;
64
+ var $show_on_profile;
65
+
66
+ function is_private() {
67
+ return $this->status == self::STATUS_PRIVATE;
68
+ }
69
+
70
+ }
71
+
72
+ /**
73
+ * @property int $id The list unique identifier
74
+ * @property string $name The list name
75
+ * @property int $status When and how the list is visible to the subscriber - see constants
76
+ * @property string $type Field type: text or select
77
+ * @property array $options Field options (usually the select items)
78
+ */
79
+ class TNP_Profile {
80
+
81
+ const STATUS_PRIVATE = 0;
82
+ const STATUS_PUBLIC = 2;
83
+ const STATUS_PROFILE_ONLY = 1;
84
+ const STATUS_HIDDEN = 3; // Public but never shown (can be set with a hidden form field)
85
+ const TYPE_TEXT = 'text';
86
+ const TYPE_SELECT = 'select';
87
+
88
+ public $id;
89
+ public $name;
90
+ public $status;
91
+ public $type;
92
+ public $options;
93
+ public $placeholder;
94
+ public $rule;
95
+
96
+ public function __construct($id, $name, $status, $type, $options, $placeholder, $rule) {
97
+ $this->id = $id;
98
+ $this->name = $name;
99
+ $this->status = $status;
100
+ $this->type = $type;
101
+ $this->options = $options;
102
+ $this->placeholder = $placeholder;
103
+ $this->rule = $rule;
104
+ }
105
+
106
+ function is_select() {
107
+ return $this->type == self::TYPE_SELECT;
108
+ }
109
+
110
+ function is_text() {
111
+ return $this->type == self::TYPE_TEXT;
112
+ }
113
+
114
+ function is_required() {
115
+ return $this->rule == 1;
116
+ }
117
+
118
+ function is_private() {
119
+ return $this->status == self::STATUS_PRIVATE;
120
+ }
121
+
122
+ function show_on_profile() {
123
+ return $this->status == self::STATUS_PROFILE_ONLY || $this->status == self::STATUS_PUBLIC;
124
+ }
125
+
126
+ }
127
+
128
+ class TNP_Profile_Service {
129
+
130
+ /**
131
+ *
132
+ * @param string $language
133
+ * @param string $type
134
+ * @return TNP_Profile[]
135
+ */
136
+ static function get_profiles($language = '', $type = '') {
137
+
138
+ static $profiles = [];
139
+ $k = $language . $type;
140
+
141
+ if (isset($profiles[$k])) {
142
+ return $profiles[$k];
143
+ }
144
+
145
+ $profiles[$k] = [];
146
+ $profile_options = NewsletterSubscription::instance()->get_options('profile', $language);
147
+ for ($i = 1; $i <= NEWSLETTER_PROFILE_MAX; $i++) {
148
+ if (empty($profile_options['profile_' . $i])) {
149
+ continue;
150
+ }
151
+ $profile = self::create_profile_from_options($profile_options, $i);
152
+
153
+ if (empty($type) ||
154
+ ( $type == TNP_Profile::TYPE_SELECT && $profile->is_select() ) ||
155
+ ( $type == TNP_Profile::TYPE_TEXT && $profile->is_text() )) {
156
+ $profiles[$k]['' . $i] = $profile;
157
+ }
158
+ }
159
+
160
+ return $profiles[$k];
161
+ }
162
+
163
+ static function get_profile_by_id($id, $language = '') {
164
+
165
+ $profiles = self::get_profiles($language);
166
+ if (isset($profiles[$id]))
167
+ return $profiles[$id];
168
+ return null;
169
+ }
170
+
171
+ /**
172
+ * @return TNP_Profile
173
+ */
174
+ private static function create_profile_from_options($options, $id) {
175
+ return new TNP_Profile(
176
+ $id,
177
+ $options['profile_' . $id],
178
+ (int) $options['profile_' . $id . '_status'],
179
+ $options['profile_' . $id . '_type'],
180
+ self::string_db_options_to_array($options['profile_' . $id . '_options']),
181
+ $options['profile_' . $id . '_placeholder'],
182
+ $options['profile_' . $id . '_rules']
183
+ );
184
+ }
185
+
186
+ /**
187
+ * Returns a list of strings which are the items for the select field.
188
+ * @return array
189
+ */
190
+ private static function string_db_options_to_array($string_options) {
191
+ $items = array_map('trim', explode(',', $string_options));
192
+ $items = array_combine($items, $items);
193
+
194
+ return $items;
195
+ }
196
+
197
+ }
198
+
199
+ /**
200
+ * Represents the set of data collected by a subscription interface (form, API, ...). Only a valid
201
+ * email is mandatory.
202
+ */
203
+ class TNP_Subscription_Data {
204
+
205
+ var $email = null;
206
+ var $name = null;
207
+ var $surname = null;
208
+ var $sex = null;
209
+ var $language = null;
210
+ var $referrer = null;
211
+ var $http_referrer = null;
212
+ var $ip = null;
213
+ var $country = null;
214
+ var $region = null;
215
+ var $city = null;
216
+
217
+ /**
218
+ * Associative array id=>value of lists chosen by the subscriber. A list can be set to
219
+ * 0 meaning the subscriber does not want to be in that list.
220
+ * The lists must be public: non public lists are filtered.
221
+ * @var array
222
+ */
223
+ var $lists = [];
224
+ var $profiles = [];
225
+
226
+ function merge_in($subscriber) {
227
+ if (!$subscriber)
228
+ $subscriber = new TNP_User();
229
+ if (!empty($this->email))
230
+ $subscriber->email = $this->email;
231
+ if (!empty($this->name))
232
+ $subscriber->name = $this->name;
233
+ if (!empty($this->surname))
234
+ $subscriber->surname = $this->surname;
235
+ if (!empty($this->sex))
236
+ $subscriber->sex = $this->sex;
237
+ if (!empty($this->language))
238
+ $subscriber->language = $this->language;
239
+ if (!empty($this->ip))
240
+ $subscriber->ip = $this->ip;
241
+ if (!empty($this->referrer))
242
+ $subscriber->referrer = $this->referrer;
243
+ if (!empty($this->http_referrer))
244
+ $subscriber->http_referrer = $this->http_referrer;
245
+ if (!empty($this->country))
246
+ $subscriber->country = $this->country;
247
+ if (!empty($this->region))
248
+ $subscriber->region = $this->region;
249
+ if (!empty($this->city))
250
+ $subscriber->city = $this->city;
251
+
252
+
253
+ foreach ($this->lists as $id => $value) {
254
+ $key = 'list_' . $id;
255
+ $subscriber->$key = $value;
256
+ }
257
+
258
+ // Profile
259
+ foreach ($this->profiles as $id => $value) {
260
+ $key = 'profile_' . $id;
261
+ $subscriber->$key = $value;
262
+ }
263
+ }
264
+
265
+ /** Sets to active a set of lists. Accepts incorrect data (and ignores it).
266
+ *
267
+ * @param array $list_ids Array of list IDs
268
+ */
269
+ function add_lists($list_ids) {
270
+ if (empty($list_ids) || !is_array($list_ids))
271
+ return;
272
+ foreach ($list_ids as $list_id) {
273
+ $list_id = (int) $list_id;
274
+ if ($list_id < 0 || $list_id > NEWSLETTER_LIST_MAX)
275
+ continue;
276
+ $this->lists[$list_id] = 1;
277
+ }
278
+ }
279
+
280
+ }
281
+
282
+ /**
283
+ * Represents a subscription request with the subscriber data and actions to be taken by
284
+ * the subscription engine (spam check, notifications, ...).
285
+ */
286
+ class TNP_Subscription {
287
+
288
+ const EXISTING_ERROR = 1;
289
+ const EXISTING_MERGE = 0;
290
+
291
+ /**
292
+ * Subscriber's data following the syntax of the TNP_User
293
+ * @var TNP_Subscription_Data
294
+ */
295
+ var $data;
296
+ var $spamcheck = true;
297
+ // The optin to use, empty for the plugin default. It's a string to facilitate the use by addons (which have a selector for the desired
298
+ // optin as empty (for default), 'single' or 'double'.
299
+ var $optin = null;
300
+ // What to do with an existing subscriber???
301
+ var $if_exists = self::EXISTING_MERGE;
302
+
303
+ /**
304
+ * Determines if the welcome or activation email should be sent. Note: sometime an activation email is sent disregarding
305
+ * this setting.
306
+ * @var boolean
307
+ */
308
+ var $send_emails = true;
309
+
310
+ public function __construct() {
311
+ $this->data = new TNP_Subscription_Data();
312
+ }
313
+
314
+ }
315
+
316
+ /**
317
+ * @property int $id The subscriber unique identifier
318
+ * @property string $email The subscriber email
319
+ * @property string $name The subscriber name or first name
320
+ * @property string $surname The subscriber last name
321
+ * @property string $status The subscriber status
322
+ * @property string $language The subscriber language code 2 chars lowercase
323
+ * @property string $token The subscriber secret token
324
+ * @property string $country The subscriber country code 2 chars uppercase
325
+ */
326
+ class TNP_User {
327
+
328
+ const STATUS_CONFIRMED = 'C';
329
+ const STATUS_NOT_CONFIRMED = 'S';
330
+ const STATUS_UNSUBSCRIBED = 'U';
331
+ const STATUS_BOUNCED = 'B';
332
+ const STATUS_COMPLAINED = 'P';
333
+
334
+ public static function get_status_label($status) {
335
+ switch ($status) {
336
+ case self::STATUS_NOT_CONFIRMED: return __('NOT CONFIRMED', 'newsletter');
337
+ break;
338
+ case self::STATUS_CONFIRMED: return __('CONFIRMED', 'newsletter');
339
+ break;
340
+ case self::STATUS_UNSUBSCRIBED: return __('UNSUBSCRIBED', 'newsletter');
341
+ break;
342
+ case self::STATUS_BOUNCED: return __('BOUNCED', 'newsletter');
343
+ break;
344
+ case self::STATUS_COMPLAINED: return __('COMPLAINED', 'newsletter');
345
+ break;
346
+ default:
347
+ return __('Unknown', 'newsletter');
348
+ }
349
+ }
350
+
351
+ }
352
+
353
+ /**
354
+ * @property int $id The email unique identifier
355
+ * @property string $subject The email subject
356
+ * @property string $message The email html message
357
+ * @property int $track Check if the email stats should be active
358
+ * @property array $options Email options
359
+ * @property int $total Total emails to send
360
+ * @property int $sent Total sent emails by now
361
+ * @property int $open_count Total opened emails
362
+ * @property int $click_count Total clicked emails
363
+ * */
364
+ class TNP_Email {
365
+
366
+ const STATUS_DRAFT = 'new';
367
+ const STATUS_SENT = 'sent';
368
+ const STATUS_SENDING = 'sending';
369
+ const STATUS_PAUSED = 'paused';
370
+ const STATUS_ERROR = 'error';
371
+
372
+ }
373
+
374
+ class NewsletterModule {
375
+
376
+ /**
377
+ * @var NewsletterLogger
378
+ */
379
+ var $logger;
380
+
381
+ /**
382
+ * @var NewsletterLogger
383
+ */
384
+ var $admin_logger;
385
+
386
+ /**
387
+ * @var NewsletterStore
388
+ */
389
+ var $store;
390
+
391
+ /**
392
+ * The main module options
393
+ * @var array
394
+ */
395
+ var $options;
396
+
397
+ /**
398
+ * @var string The module name
399
+ */
400
+ var $module;
401
+
402
+ /**
403
+ * The module version
404
+ * @var string
405
+ */
406
+ var $version;
407
+ var $old_version;
408
+
409
+ /**
410
+ * Prefix for all options stored on WordPress options table.
411
+ * @var string
412
+ */
413
+ var $prefix;
414
+
415
+ /**
416
+ * @var NewsletterThemes
417
+ */
418
+ var $themes;
419
+ var $components;
420
+ static $current_language = '';
421
+
422
+ function __construct($module, $version, $module_id = null, $components = array()) {
423
+ $this->module = $module;
424
+ $this->version = $version;
425
+ $this->prefix = 'newsletter_' . $module;
426
+ array_unshift($components, '');
427
+ $this->components = $components;
428
+
429
+ $this->logger = new NewsletterLogger($module);
430
+
431
+ $this->options = $this->get_options();
432
+ $this->store = NewsletterStore::singleton();
433
+
434
+ //$this->logger->debug($module . ' constructed');
435
+ // Version check
436
+ if (is_admin()) {
437
+ $this->admin_logger = new NewsletterLogger($module . '-admin');
438
+ $this->old_version = get_option($this->prefix . '_version', '0.0.0');
439
+
440
+ if ($this->old_version == '0.0.0') {
441
+ require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
442
+ $this->first_install();
443
+ update_option($this->prefix . "_first_install_time", time(), FALSE);
444
+ }
445
+
446
+ if (strcmp($this->old_version, $this->version) != 0) {
447
+ require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
448
+ $this->logger->info('Version changed from ' . $this->old_version . ' to ' . $this->version);
449
+ // Do all the stuff for this version change
450
+ $this->upgrade();
451
+ update_option($this->prefix . '_version', $this->version);
452
+ }
453
+
454
+ add_action('admin_menu', array($this, 'admin_menu'));
455
+ }
456
+ }
457
+
458
+ /**
459
+ *
460
+ * @global wpdb $wpdb
461
+ * @param string $query
462
+ */
463
+ function query($query) {
464
+ global $wpdb;
465
+
466
+ $this->logger->debug($query);
467
+ //$start = microtime(true);
468
+ $r = $wpdb->query($query);
469
+ //$this->logger->debug($wpdb->last_query);
470
+ //$this->logger->debug('Execution time: ' . (microtime(true)-$start));
471
+ //$this->logger->debug('Result: ' . $r);
472
+ if ($r === false) {
473
+ $this->logger->fatal($query);
474
+ $this->logger->fatal($wpdb->last_error);
475
+ }
476
+ return $r;
477
+ }
478
+
479
+ function get_results($query) {
480
+ global $wpdb;
481
+ $r = $wpdb->get_results($query);
482
+ if ($r === false) {
483
+ $this->logger->fatal($query);
484
+ $this->logger->fatal($wpdb->last_error);
485
+ }
486
+ return $r;
487
+ }
488
+
489
+ /**
490
+ *
491
+ * @global wpdb $wpdb
492
+ * @param string $table
493
+ * @param array $data
494
+ */
495
+ function insert($table, $data) {
496
+ global $wpdb;
497
+ $this->logger->debug("inserting into table $table");
498
+ $r = $wpdb->insert($table, $data);
499
+ if ($r === false) {
500
+ $this->logger->fatal($wpdb->last_error);
501
+ }
502
+ }
503
+
504
+ function first_install() {
505
+ $this->logger->debug('First install');
506
+ }
507
+
508
+ /**
509
+ * Does a basic upgrade work, checking if the options is already present and if not (first
510
+ * installation), recovering the defaults, saving them on database and initializing the
511
+ * internal $options.
512
+ */
513
+ function upgrade() {
514
+ foreach ($this->components as $component) {
515
+ $this->logger->debug('Upgrading component ' . $component);
516
+ $this->init_options($component);
517
+ }
518
+ }
519
+
520
+ function init_options($component = '', $autoload = true) {
521
+ global $wpdb;
522
+ $default_options = $this->get_default_options($component);
523
+ $options = $this->get_options($component);
524
+ $options = array_merge($default_options, $options);
525
+ $this->save_options($options, $component, $autoload);
526
+ }
527
+
528
+ function upgrade_query($query) {
529
+ global $wpdb, $charset_collate;
530
+
531
+ $this->logger->info('upgrade_query> Executing ' . $query);
532
+ $suppress_errors = $wpdb->suppress_errors(true);
533
+ $wpdb->query($query);
534
+ if ($wpdb->last_error) {
535
+ $this->logger->debug($wpdb->last_error);
536
+ }
537
+ $wpdb->suppress_errors($suppress_errors);
538
+ }
539
+
540
+ /** Returns a prefix to be used for option names and other things which need to be uniquely named. The parameter
541
+ * "sub" should be used when a sub name is needed for another set of options or like.
542
+ *
543
+ * @param string $sub
544
+ * @return string The prefix for names
545
+ */
546
+ function get_prefix($sub = '', $language = '') {
547
+ return $this->prefix . (!empty($sub) ? '_' : '') . $sub . (!empty($language) ? '_' : '') . $language;
548
+ }
549
+
550
+ /**
551
+ * Returns the options of a module, if not found an empty array.
552
+ */
553
+ function get_options($sub = '', $language = '') {
554
+ $options = get_option($this->get_prefix($sub, $language), array());
555
+ // Protection against scarmled database...
556
+ if (!is_array($options)) {
557
+ $options = array();
558
+ }
559
+ if ($language) {
560
+ $main_options = get_option($this->get_prefix($sub));
561
+ // Protection against scarmled database...
562
+ if (!is_array($main_options))
563
+ $main_options = array();
564
+ //$options = array_merge($main_options, array_filter($options));
565
+ $options = array_merge($main_options, $options);
566
+ }
567
+ return $options;
568
+ }
569
+
570
+ function get_default_options($sub = '') {
571
+ if (!empty($sub)) {
572
+ $sub = '-' . $sub;
573
+ }
574
+ $file = NEWSLETTER_DIR . '/' . $this->module . '/defaults' . $sub . '.php';
575
+ if (file_exists($file)) {
576
+ @include $file;
577
+ }
578
+
579
+ if (!isset($options) || !is_array($options)) {
580
+ return array();
581
+ }
582
+ return $options;
583
+ }
584
+
585
+ function reset_options($sub = '') {
586
+ $this->save_options(array_merge($this->get_options($sub), $this->get_default_options($sub)), $sub);
587
+ return $this->get_options($sub);
588
+ }
589
+
590
+ /**
591
+ * Saves the module options (or eventually a subset names as per parameter $sub). $options
592
+ * should be an array (even if it can work with non array options.
593
+ * The internal module options variable IS initialized with those new options only for the main
594
+ * options (empty $sub parameter).
595
+ * If the options contain a "theme" value, the theme-related options contained are saved as well
596
+ * (used by some modules).
597
+ *
598
+ * @param array $options
599
+ * @param string $sub
600
+ */
601
+ function save_options($options, $sub = '', $autoload = null, $language = '') {
602
+ update_option($this->get_prefix($sub, $language), $options, $autoload);
603
+ if (empty($sub) && empty($language)) {
604
+ $this->options = $options;
605
+ if (isset($this->themes) && isset($options['theme'])) {
606
+ $this->themes->save_options($options['theme'], $options);
607
+ }
608
+ }
609
+ }
610
+
611
+ function delete_options($sub = '') {
612
+ delete_option($this->get_prefix($sub));
613
+ if (empty($sub)) {
614
+ $this->options = array();
615
+ }
616
+ }
617
+
618
+ function merge_options($options, $sub = '', $language = '') {
619
+ if (!is_array($options)) {
620
+ $options = array();
621
+ }
622
+ $old_options = $this->get_options($sub, $language);
623
+ $this->save_options(array_merge($old_options, $options), $sub, null, $language);
624
+ }
625
+
626
+ function backup_options($sub) {
627
+ $options = $this->get_options($sub);
628
+ update_option($this->get_prefix($sub) . '_backup', $options, false);
629
+ }
630
+
631
+ function get_last_run($sub = '') {
632
+ return get_option($this->get_prefix($sub) . '_last_run', 0);
633
+ }
634
+
635
+ /**
636
+ * Save the module last run value. Used to store a timestamp for some modules,
637
+ * for example the Feed by Mail module.
638
+ *
639
+ * @param int $time Unix timestamp (as returned by time() for example)
640
+ * @param string $sub Sub module name (default empty)
641
+ */
642
+ function save_last_run($time, $sub = '') {
643
+ update_option($this->get_prefix($sub) . '_last_run', $time);
644
+ }
645
+
646
+ /**
647
+ * Sums $delta seconds to the last run time.
648
+ * @param int $delta Seconds
649
+ * @param string $sub Sub module name (default empty)
650
+ */
651
+ function add_to_last_run($delta, $sub = '') {
652
+ $time = $this->get_last_run($sub);
653
+ $this->save_last_run($time + $delta, $sub);
654
+ }
655
+
656
+ /**
657
+ * Checks if the semaphore of that name (for this module) is still red. If it is active the method
658
+ * returns false. If it is not active, it will be activated for $time seconds.
659
+ *
660
+ * Since this method activate the semaphore when called, it's name is a bit confusing.
661
+ *
662
+ * @param string $name Sempahore name (local to this module)
663
+ * @param int $time Max time in second this semaphore should stay red
664
+ * @return boolean False if the semaphore is red and you should not proceed, true is it was not active and has been activated.
665
+ */
666
+ function check_transient($name, $time) {
667
+ if ($time < 60)
668
+ $time = 60;
669
+ //usleep(rand(0, 1000000));
670
+ if (($value = get_transient($this->get_prefix() . '_' . $name)) !== false) {
671
+ list($t, $v) = explode(';', $value, 2);
672
+ $this->logger->error('Blocked by transient ' . $this->get_prefix() . '_' . $name . ' set ' . (time() - $t) . ' seconds ago by ' . $v);
673
+ return false;
674
+ }
675
+ //$ip = ''; //gethostbyname(gethostname());
676
+ $value = time() . ";" . ABSPATH . ';' . gethostname();
677
+ set_transient($this->get_prefix() . '_' . $name, $value, $time);
678
+ return true;
679
+ }
680
+
681
+ function delete_transient($name = '') {
682
+ delete_transient($this->get_prefix() . '_' . $name);
683
+ }
684
+
685
+ /** Returns a random token of the specified size (or 10 characters if size is not specified).
686
+ *
687
+ * @param int $size
688
+ * @return string
689
+ */
690
+ static function get_token($size = 10) {
691
+ return substr(md5(rand()), 0, $size);
692
+ }
693
+
694
+ /**
695
+ * Adds query string parameters to an URL checing id there are already other parameters.
696
+ *
697
+ * @param string $url
698
+ * @param string $qs The part of query-string to add (param1=value1&param2=value2...)
699
+ * @param boolean $amp If the method must use the &amp; instead of the plain & (default true)
700
+ * @return string
701
+ */
702
+ static function add_qs($url, $qs, $amp = true) {
703
+ if (strpos($url, '?') !== false) {
704
+ if ($amp)
705
+ return $url . '&amp;' . $qs;
706
+ else
707
+ return $url . '&' . $qs;
708
+ } else
709
+ return $url . '?' . $qs;
710
+ }
711
+
712
+ /**
713
+ * Returns the email address normalized, lowercase with no spaces. If it's not a valid email
714
+ * returns false.
715
+ */
716
+ static function normalize_email($email) {
717
+ if (!is_string($email)) {
718
+ return false;
719
+ }
720
+ $email = strtolower(trim($email));
721
+ if (!is_email($email)) {
722
+ return false;
723
+ }
724
+ //$email = apply_filters('newsletter_normalize_email', $email);
725
+ return $email;
726
+ }
727
+
728
+ static function normalize_name($name) {
729
+ $name = html_entity_decode($name, ENT_QUOTES);
730
+ $name = str_replace(';', ' ', $name);
731
+ $name = strip_tags($name);
732
+
733
+ return $name;
734
+ }
735
+
736
+ static function normalize_sex($sex) {
737
+ $sex = trim(strtolower($sex));
738
+ if ($sex != 'f' && $sex != 'm') {
739
+ $sex = 'n';
740
+ }
741
+ return $sex;
742
+ }
743
+
744
+ static function is_email($email, $empty_ok = false) {
745
+
746
+ if (!is_string($email)) {
747
+ return false;
748
+ }
749
+ $email = strtolower(trim($email));
750
+
751
+ if ($email == '') {
752
+ return $empty_ok;
753
+ }
754
+
755
+ if (!is_email($email)) {
756
+ return false;
757
+ }
758
+ return true;
759
+ }
760
+
761
+ /**
762
+ * Converts a GMT date from mysql (see the posts table columns) into a timestamp.
763
+ *
764
+ * @param string $s GMT date with format yyyy-mm-dd hh:mm:ss
765
+ * @return int A timestamp
766
+ */
767
+ static function m2t($s) {
768
+
769
+ // TODO: use the wordpress function I don't remember the name
770
+ $s = explode(' ', $s);
771
+ $d = explode('-', $s[0]);
772
+ $t = explode(':', $s[1]);
773
+ return gmmktime((int) $t[0], (int) $t[1], (int) $t[2], (int) $d[1], (int) $d[2], (int) $d[0]);
774
+ }
775
+
776
+ static function format_date($time) {
777
+ if (empty($time)) {
778
+ return '-';
779
+ }
780
+ return gmdate(get_option('date_format') . ' ' . get_option('time_format'), $time + get_option('gmt_offset') * 3600);
781
+ }
782
+
783
+ static function format_time_delta($delta) {
784
+ $days = floor($delta / (3600 * 24));
785
+ $hours = floor(($delta % (3600 * 24)) / 3600);
786
+ $minutes = floor(($delta % 3600) / 60);
787
+ $seconds = floor(($delta % 60));
788
+ $buffer = $days . ' days, ' . $hours . ' hours, ' . $minutes . ' minutes, ' . $seconds . ' seconds';
789
+ return $buffer;
790
+ }
791
+
792
+ /**
793
+ * Formats a scheduler returned "next execution" time, managing negative or false values. Many times
794
+ * used in conjuction with "last run".
795
+ *
796
+ * @param string $name The scheduler name
797
+ * @return string
798
+ */
799
+ static function format_scheduler_time($name) {
800
+ $time = wp_next_scheduled($name);
801
+ if ($time === false) {
802
+ return 'No next run scheduled';
803
+ }
804
+ $delta = $time - time();
805
+ // If less 10 minutes late it can be a cron problem but now it is working
806
+ if ($delta < 0 && $delta > -600) {
807
+ return 'Probably running now';
808
+ } else if ($delta <= -600) {
809
+ return 'It seems the cron system is not working. Reload the page to see if this message change.';
810
+ }
811
+ return 'Runs in ' . self::format_time_delta($delta);
812
+ }
813
+
814
+ static function date($time = null, $now = false, $left = false) {
815
+ if (is_null($time)) {
816
+ $time = time();
817
+ }
818
+ if ($time == false) {
819
+ $buffer = 'none';
820
+ } else {
821
+ $buffer = gmdate(get_option('date_format') . ' ' . get_option('time_format'), $time + get_option('gmt_offset') * 3600);
822
+ }
823
+ if ($now) {
824
+ $buffer .= ' (now: ' . gmdate(get_option('date_format') . ' ' .
825
+ get_option('time_format'), time() + get_option('gmt_offset') * 3600);
826
+ $buffer .= ')';
827
+ }
828
+ if ($left) {
829
+ $buffer .= ', ' . gmdate('H:i:s', $time - time()) . ' left';
830
+ }
831
+ return $buffer;
832
+ }
833
+
834
+ /**
835
+ * Return an array of array with on first element the array of recent post and on second element the array
836
+ * of old posts.
837
+ *
838
+ * @param array $posts
839
+ * @param int $time
840
+ */
841
+ static function split_posts(&$posts, $time = 0) {
842
+ if ($time < 0) {
843
+ return array_chunk($posts, ceil(count($posts) / 2));
844
+ }
845
+
846
+ $result = array(array(), array());
847
+
848
+ if (empty($posts))
849
+ return $result;
850
+
851
+ foreach ($posts as &$post) {
852
+ if (self::is_post_old($post, $time))
853
+ $result[1][] = $post;
854
+ else
855
+ $result[0][] = $post;
856
+ }
857
+ return $result;
858
+ }
859
+
860
+ static function is_post_old(&$post, $time = 0) {
861
+ return self::m2t($post->post_date_gmt) <= $time;
862
+ }
863
+
864
+ static function get_post_image($post_id = null, $size = 'thumbnail', $alternative = null) {
865
+ global $post;
866
+
867
+ if (empty($post_id))
868
+ $post_id = $post->ID;
869
+ if (empty($post_id))
870
+ return $alternative;
871
+
872
+ $image_id = function_exists('get_post_thumbnail_id') ? get_post_thumbnail_id($post_id) : false;
873
+ if ($image_id) {
874
+ $image = wp_get_attachment_image_src($image_id, $size);
875
+ return $image[0];
876
+ } else {
877
+ $attachments = get_children(array('post_parent' => $post_id, 'post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => 'image', 'order' => 'ASC', 'orderby' => 'menu_order ID'));
878
+
879
+ if (empty($attachments)) {
880
+ return $alternative;
881
+ }
882
+
883
+ foreach ($attachments as $id => $attachment) {
884
+ $image = wp_get_attachment_image_src($id, $size);
885
+ return $image[0];
886
+ }
887
+ }
888
+ }
889
+
890
+ /**
891
+ * Cleans up a text containing url tags with appended the absolute URL (due to
892
+ * the editor behavior) moving back them to the simple form.
893
+ */
894
+ static function clean_url_tags($text) {
895
+ $text = str_replace('%7B', '{', $text);
896
+ $text = str_replace('%7D', '}', $text);
897
+
898
+ // Only tags which are {*_url}
899
+ $text = preg_replace("/[\"']http[^\"']+(\\{[^\\}]+_url\\})[\"']/i", "\"\\1\"", $text);
900
+ return $text;
901
+ }
902
+
903
+ function admin_menu() {
904
+
905
+ }
906
+
907
+ function add_menu_page($page, $title, $capability = '') {
908
+ if (!Newsletter::instance()->is_allowed())
909
+ return;
910
+ $name = 'newsletter_' . $this->module . '_' . $page;
911
+ add_submenu_page('newsletter_main_index', $title, $title, 'exist', $name, array($this, 'menu_page'));
912
+ }
913
+
914
+ function add_admin_page($page, $title) {
915
+ if (!Newsletter::instance()->is_allowed()) {
916
+ return;
917
+ }
918
+ $name = 'newsletter_' . $this->module . '_' . $page;
919
+ add_submenu_page(null, $title, $title, 'exist', $name, array($this, 'menu_page'));
920
+ }
921
+
922
+ function sanitize_file_name($name) {
923
+ return preg_replace('/[^a-z_\\-]/i', '', $name);
924
+ }
925
+
926
+ function menu_page() {
927
+ global $plugin_page, $newsletter, $wpdb;
928
+
929
+ $parts = explode('_', $plugin_page, 3);
930
+ $module = $this->sanitize_file_name($parts[1]);
931
+ $page = $this->sanitize_file_name($parts[2]);
932
+ $page = str_replace('_', '-', $page);
933
+
934
+ $file = NEWSLETTER_DIR . '/' . $module . '/' . $page . '.php';
935
+
936
+ require $file;
937
+ }
938
+
939
+ function get_admin_page_url($page) {
940
+ return admin_url('admin.php') . '?page=newsletter_' . $this->module . '_' . $page;
941
+ }
942
+
943
+ /** Returns all the emails of the give type (message, feed, followup, ...) and in the given format
944
+ * (default as objects). Return false on error or at least an empty array. Errors should never
945
+ * occur.
946
+ *
947
+ * @global wpdb $wpdb
948
+ * @param string $type
949
+ * @return boolean|array
950
+ */
951
+ function get_emails($type = null, $format = OBJECT) {
952
+ global $wpdb;
953
+ if ($type == null) {
954
+ $list = $wpdb->get_results("select * from " . NEWSLETTER_EMAILS_TABLE . " order by id desc", $format);
955
+ } else {
956
+ $type = (string) $type;
957
+ $list = $wpdb->get_results($wpdb->prepare("select * from " . NEWSLETTER_EMAILS_TABLE . " where type=%s order by id desc", $type), $format);
958
+ }
959
+ if ($wpdb->last_error) {
960
+ $this->logger->error($wpdb->last_error);
961
+ return false;
962
+ }
963
+ if (empty($list)) {
964
+ return array();
965
+ }
966
+ return $list;
967
+ }
968
+
969
+ /**
970
+ * @param string $key
971
+ * @param mixed $value
972
+ * @return TNP_Email[]
973
+ */
974
+ function get_emails_by_field($key, $value) {
975
+ global $wpdb;
976
+
977
+ $value_placeholder = is_int($value) ? '%d' : '%s';
978
+
979
+ $query = $wpdb->prepare("SELECT * FROM " . NEWSLETTER_EMAILS_TABLE . " WHERE %1s=$value_placeholder ORDER BY id DESC", $key, $value);
980
+
981
+ $email_list = $wpdb->get_results($query);
982
+
983
+ if ($wpdb->last_error) {
984
+ $this->logger->error($wpdb->last_error);
985
+
986
+ return [];
987
+ }
988
+
989
+ //Unserialize options
990
+ array_walk($email_list, function ($email) {
991
+ $email->options = maybe_unserialize($email->options);
992
+ if (!is_array($email->options)) {
993
+ $email->options = [];
994
+ }
995
+ });
996
+
997
+ return $email_list;
998
+ }
999
+
1000
+ /**
1001
+ * Retrieves an email from DB and unserialize the options.
1002
+ *
1003
+ * @param mixed $id
1004
+ * @param string $format
1005
+ * @return TNP_Email An object with the same fields of TNP_Email, but not actually of that type
1006
+ */
1007
+ function get_email($id, $format = OBJECT) {
1008
+ $email = $this->store->get_single(NEWSLETTER_EMAILS_TABLE, $id, $format);
1009
+ if (!$email) {
1010
+ return null;
1011
+ }
1012
+ if ($format == OBJECT) {
1013
+ $email->options = maybe_unserialize($email->options);
1014
+ if (!is_array($email->options)) {
1015
+ $email->options = array();
1016
+ }
1017
+ if (empty($email->query)) {
1018
+ $email->query = "select * from " . NEWSLETTER_USERS_TABLE . " where status='C'";
1019
+ }
1020
+ } else if ($format == ARRAY_A) {
1021
+ $email['options'] = maybe_unserialize($email['options']);
1022
+ if (!is_array($email['options'])) {
1023
+ $email['options'] = array();
1024
+ }
1025
+ if (empty($email['query'])) {
1026
+ $email['query'] = "select * from " . NEWSLETTER_USERS_TABLE . " where status='C'";
1027
+ }
1028
+ }
1029
+ return $email;
1030
+ }
1031
+
1032
+ /**
1033
+ * Save an email and provide serialization, if needed, of $email['options'].
1034
+ * @return TNP_Email
1035
+ */
1036
+ function save_email($email, $return_format = OBJECT) {
1037
+ if (is_object($email)) {
1038
+ $email = (array) $email;
1039
+ }
1040
+
1041
+ if (isset($email['subject'])) {
1042
+ if (mb_strlen($email['subject'], 'UTF-8') > 250) {
1043
+ $email['subject'] = mb_substr($email['subject'], 0, 250, 'UTF-8');
1044
+ }
1045
+ }
1046
+ if (isset($email['options']) && is_array($email['options'])) {
1047
+ $email['options'] = serialize($email['options']);
1048
+ }
1049
+ $email = $this->store->save(NEWSLETTER_EMAILS_TABLE, $email, $return_format);
1050
+ if ($return_format == OBJECT) {
1051
+ $email->options = maybe_unserialize($email->options);
1052
+ if (!is_array($email->options)) {
1053
+ $email->options = [];
1054
+ }
1055
+ } else if ($return_format == ARRAY_A) {
1056
+ $email['options'] = maybe_unserialize($email['options']);
1057
+ if (!is_array($email['options'])) {
1058
+ $email['options'] = [];
1059
+ }
1060
+ }
1061
+ return $email;
1062
+ }
1063
+
1064
+ function get_email_from_request() {
1065
+
1066
+ if (isset($_REQUEST['nek'])) {
1067
+ list($id, $token) = @explode('-', $_REQUEST['nek'], 2);
1068
+ } else if (isset($_COOKIE['tnpe'])) {
1069
+ list($id, $token) = @explode('-', $_COOKIE['tnpe'], 2);
1070
+ } else {
1071
+ return null;
1072
+ }
1073
+
1074
+ $email = $this->get_email($id);
1075
+
1076
+ // TODO: Check the token? It's really useful?
1077
+
1078
+ return $email;
1079
+ }
1080
+
1081
+ /**
1082
+ * Delete one or more emails identified by ID (single value or array of ID)
1083
+ *
1084
+ * @global wpdb $wpdb
1085
+ * @param int|array $id Single numeric ID or an array of IDs to be deleted
1086
+ * @return boolean
1087
+ */
1088
+ function delete_email($id) {
1089
+ global $wpdb;
1090
+ $r = $this->store->delete(NEWSLETTER_EMAILS_TABLE, $id);
1091
+ if ($r !== false) {
1092
+ // $id could be an array if IDs
1093
+ $id = (array) $id;
1094
+ foreach ($id as $email_id) {
1095
+ $wpdb->delete(NEWSLETTER_STATS_TABLE, ['email_id' => $email_id]);
1096
+ $wpdb->delete(NEWSLETTER_SENT_TABLE, ['email_id' => $email_id]);
1097
+ }
1098
+ }
1099
+ return $r;
1100
+ }
1101
+
1102
+ function get_email_field($id, $field_name) {
1103
+ return $this->store->get_field(NEWSLETTER_EMAILS_TABLE, $id, $field_name);
1104
+ }
1105
+
1106
+ function get_email_status_slug($email) {
1107
+ $email = (object) $email;
1108
+ if ($email->status == 'sending' && $email->send_on > time()) {
1109
+ return 'scheduled';
1110
+ }
1111
+ return $email->status;
1112
+ }
1113
+
1114
+ function get_email_status_label($email) {
1115
+ $email = (object) $email;
1116
+ $status = $this->get_email_status_slug($email);
1117
+ switch ($status) {
1118
+ case 'sending':
1119
+ return __('Sending', 'newsletter');
1120
+ case 'scheduled':
1121
+ return __('Scheduled', 'newsletter');
1122
+ case 'sent':
1123
+ return __('Sent', 'newsletter');
1124
+ case 'paused':
1125
+ return __('Paused', 'newsletter');
1126
+ case 'new':
1127
+ return __('Draft', 'newsletter');
1128
+ default:
1129
+ return ucfirst($email->status);
1130
+ }
1131
+ }
1132
+
1133
+ function show_email_status_label($email) {
1134
+ echo '<span class="tnp-email-status tnp-email-status--', $this->get_email_status_slug($email), '">', esc_html($this->get_email_status_label($email)), '</span>';
1135
+ }
1136
+
1137
+ function get_email_progress($email, $format = 'percent') {
1138
+ return $email->total > 0 ? intval($email->sent / $email->total * 100) : 0;
1139
+ }
1140
+
1141
+ function show_email_progress_bar($email, $attrs = []) {
1142
+
1143
+ $email = (object) $email;
1144
+
1145
+ $attrs = array_merge(array('format' => 'percent', 'numbers' => false, 'scheduled' => false), $attrs);
1146
+
1147
+ if ($email->status == 'sending' && $email->send_on > time()) {
1148
+ if ($attrs['scheduled']) {
1149
+ echo '<span class="tnp-progress-date">', $this->format_date($email->send_on), '</span>';
1150
+ }
1151
+ return;
1152
+ } else if ($email->status == 'new') {
1153
+ echo '';
1154
+ return;
1155
+ } else if ($email->status == 'sent') {
1156
+ $percent = 100;
1157
+ } else {
1158
+ $percent = $this->get_email_progress($email);
1159
+ }
1160
+
1161
+ echo '<div class="tnp-progress tnp-progress--' . $email->status . '">';
1162
+ echo '<div class="tnp-progress-bar" role="progressbar" style="width: ', $percent, '%;">&nbsp;', $percent, '%&nbsp;</div>';
1163
+ echo '</div>';
1164
+ if ($attrs['numbers']) {
1165
+ if ($email->status == 'sent') {
1166
+ echo '<div class="tnp-progress-numbers">', $email->total, ' ', __('of', 'newsletter'), ' ', $email->total, '</div>';
1167
+ } else {
1168
+ echo '<div class="tnp-progress-numbers">', $email->sent, ' ', __('of', 'newsletter'), ' ', $email->total, '</div>';
1169
+ }
1170
+ }
1171
+ }
1172
+
1173
+ function get_email_type_label($type) {
1174
+
1175
+ // Is an email?
1176
+ if (is_object($type))
1177
+ $type = $type->type;
1178
+
1179
+ $label = apply_filters('newsletter_email_type', '', $type);
1180
+
1181
+ if (!empty($label))
1182
+ return $label;
1183
+
1184
+ switch ($type) {
1185
+ case 'followup':
1186
+ return 'Followup';
1187
+ case 'message':
1188
+ return 'Standard Newsletter';
1189
+ case 'feed':
1190
+ return 'Feed by Mail';
1191
+ }
1192
+
1193
+ if (strpos($type, 'automated') === 0) {
1194
+ list($a, $id) = explode('_', $type);
1195
+ return 'Automated Channel ' . $id;
1196
+ }
1197
+
1198
+ return ucfirst($type);
1199
+ }
1200
+
1201
+ function get_email_progress_label($email) {
1202
+ if ($email->status == 'sent' || $email->status == 'sending') {
1203
+ return $email->sent . ' ' . __('of', 'newsletter') . ' ' . $email->total;
1204
+ }
1205
+ return '-';
1206
+ }
1207
+
1208
+ /**
1209
+ * Returns the email unique key
1210
+ * @param TNP_User $user
1211
+ * @return string
1212
+ */
1213
+ function get_email_key($email) {
1214
+ if (!isset($email->token)) {
1215
+ return $email->id . '-';
1216
+ }
1217
+ return $email->id . '-' . $email->token;
1218
+ }
1219
+
1220
+ /** Searches for a user using the nk parameter or the ni and nt parameters. Tries even with the newsletter cookie.
1221
+ * If found, the user object is returned or null.
1222
+ * The user is returned without regards to his status that should be checked by caller.
1223
+ *
1224
+ * DO NOT REMOVE EVEN IF OLD
1225
+ *
1226
+ * @return TNP_User
1227
+ */
1228
+ function check_user($context = '') {
1229
+ global $wpdb;
1230
+
1231
+ $user = null;
1232
+
1233
+ if (isset($_REQUEST['nk'])) {
1234
+ list($id, $token) = @explode('-', $_REQUEST['nk'], 2);
1235
+ } else if (isset($_COOKIE['newsletter'])) {
1236
+ list ($id, $token) = @explode('-', $_COOKIE['newsletter'], 2);
1237
+ }
1238
+
1239
+ if (isset($id)) {
1240
+ $user = $this->get_user($id);
1241
+ if ($user) {
1242
+ if ($context == 'preconfirm') {
1243
+ if ($token != md5($user->token)) {
1244
+ $user = null;
1245
+ }
1246
+ } else {
1247
+ if ($token != $user->token) {
1248
+ $user = null;
1249
+ }
1250
+ }
1251
+ }
1252
+ }
1253
+
1254
+ if ($user == null && is_user_logged_in()) {
1255
+ $user = $this->get_user_by_wp_user_id(get_current_user_id());
1256
+ }
1257
+ return $user;
1258
+ }
1259
+
1260
+ /** Returns the user identify by an id or an email. If $id_or_email is an object or an array, it is assumed it contains
1261
+ * the "id" attribute or key and that is used to load the user.
1262
+ *
1263
+ * @global type $wpdb
1264
+ * @param string|int|object|array $id_or_email
1265
+ * @param string $format
1266
+ * @return TNP_User|null
1267
+ */
1268
+ function get_user($id_or_email, $format = OBJECT) {
1269
+ global $wpdb;
1270
+
1271
+ if (empty($id_or_email))
1272
+ return null;
1273
+
1274
+ // To simplify the reaload of a user passing the user it self.
1275
+ if (is_object($id_or_email)) {
1276
+ $id_or_email = $id_or_email->id;
1277
+ } else if (is_array($id_or_email)) {
1278
+ $id_or_email = $id_or_email['id'];
1279
+ }
1280
+
1281
+ $id_or_email = strtolower(trim($id_or_email));
1282
+
1283
+ if (is_numeric($id_or_email)) {
1284
+ $r = $wpdb->get_row($wpdb->prepare("select * from " . NEWSLETTER_USERS_TABLE . " where id=%d limit 1", $id_or_email), $format);
1285
+ } else {
1286
+ $r = $wpdb->get_row($wpdb->prepare("select * from " . NEWSLETTER_USERS_TABLE . " where email=%s limit 1", $id_or_email), $format);
1287
+ }
1288
+
1289
+ if ($wpdb->last_error) {
1290
+ $this->logger->error($wpdb->last_error);
1291
+ return null;
1292
+ }
1293
+ return $r;
1294
+ }
1295
+
1296
+ /**
1297
+ *
1298
+ * @global wpdb $wpdb
1299
+ * @param string $email
1300
+ * @return TNP_User
1301
+ */
1302
+ function get_user_by_email($email) {
1303
+ global $wpdb;
1304
+
1305
+ $r = $wpdb->get_row($wpdb->prepare("select * from " . NEWSLETTER_USERS_TABLE . " where email=%s limit 1", $email));
1306
+
1307
+ if ($wpdb->last_error) {
1308
+ $this->logger->error($wpdb->last_error);
1309
+ return null;
1310
+ }
1311
+ return $r;
1312
+ }
1313
+
1314
+ /**
1315
+ * Accepts a user ID or a TNP_User object. Does not check if the user really exists.
1316
+ *
1317
+ * @param type $user
1318
+ */
1319
+ function get_user_edit_url($user) {
1320
+ $id = $this->to_int_id($user);
1321
+ return admin_url('admin.php') . '?page=newsletter_users_edit&id=' . $id;
1322
+ }
1323
+
1324
+ /**
1325
+ * Returns the user unique key
1326
+ * @param TNP_User $user
1327
+ * @return string
1328
+ */
1329
+ function get_user_key($user, $context = '') {
1330
+ if (empty($user->token)) {
1331
+ $this->refresh_user_token($user);
1332
+ }
1333
+
1334
+ if ($context == 'preconfirm') {
1335
+ return $user->id . '-' . md5($user->token);
1336
+ }
1337
+ return $user->id . '-' . $user->token;
1338
+ }
1339
+
1340
+ function get_user_status_label($user, $html = false) {
1341
+ if (!$html) return TNP_User::get_status_label($user->status);
1342
+
1343
+ $label = TNP_User::get_status_label($user->status);
1344
+ $class = 'unknown';
1345
+ switch ($user->status) {
1346
+ case TNP_User::STATUS_NOT_CONFIRMED: $class = 'not-confirmed';
1347
+ break;
1348
+ case TNP_User::STATUS_CONFIRMED: $class = 'confirmed';
1349
+ break;
1350
+ case TNP_User::STATUS_UNSUBSCRIBED: $class = 'unsubscribed';
1351
+ break;
1352
+ case TNP_User::STATUS_BOUNCED: $class = 'bounced';
1353
+ break;
1354
+ case TNP_User::STATUS_COMPLAINED: $class = 'complained';
1355
+ break;
1356
+ }
1357
+ return '<span class="' . $class . '">' . esc_html($label) . '</span>';
1358
+ }
1359
+
1360
+ /**
1361
+ * Return the user identified by the "nk" parameter (POST or GET).
1362
+ * If no user can be found or the token is not matching, returns null.
1363
+ * If die_on_fail is true it dies instead of return null.
1364
+ *
1365
+ * @param bool $die_on_fail
1366
+ * @return TNP_User
1367
+ */
1368
+ function get_user_from_request($die_on_fail = false, $context = '') {
1369
+ $id = 0;
1370
+ if (isset($_REQUEST['nk'])) {
1371
+ list($id, $token) = @explode('-', $_REQUEST['nk'], 2);
1372
+ }
1373
+ $user = $this->get_user($id);
1374
+
1375
+ if ($user == null) {
1376
+ if ($die_on_fail) {
1377
+ die(__('No subscriber found.', 'newsletter'));
1378
+ } else {
1379
+ return $this->get_user_from_logged_in_user();
1380
+ }
1381
+ }
1382
+
1383
+ if ($context == 'preconfirm') {
1384
+ $user_token = md5($user->token);
1385
+ } else {
1386
+ $user_token = $user->token;
1387
+ }
1388
+
1389
+ if ($token != $user_token) {
1390
+ if ($die_on_fail) {
1391
+ die(__('No subscriber found.', 'newsletter'));
1392
+ } else {
1393
+ return $this->get_user_from_logged_in_user();
1394
+ }
1395
+ }
1396
+ return $user;
1397
+ }
1398
+
1399
+ function get_user_from_logged_in_user() {
1400
+ if (is_user_logged_in()) {
1401
+ return $this->get_user_by_wp_user_id(get_current_user_id());
1402
+ }
1403
+ return null;
1404
+ }
1405
+
1406
+ function get_user_count($refresh = false) {
1407
+ global $wpdb;
1408
+ $user_count = get_transient('newsletter_user_count');
1409
+ if ($user_count === false || $refresh) {
1410
+ $user_count = $wpdb->get_var("select count(*) from " . NEWSLETTER_USERS_TABLE . " where status='C'");
1411
+ set_transient('newsletter_user_count', $user_count, DAY_IN_SECONDS);
1412
+ }
1413
+ return $user_count;
1414
+ }
1415
+
1416
+ function get_profile($id, $language = '') {
1417
+ return TNP_Profile_Service::get_profile_by_id($id, $language);
1418
+ }
1419
+
1420
+ /**
1421
+ * @param string $language The language for the list labels (it does not affect the lists returned)
1422
+ * @return TNP_Profile[]
1423
+ */
1424
+ function get_profiles($language = '') {
1425
+ return TNP_Profile_Service::get_profiles($language);
1426
+ }
1427
+
1428
+ /**
1429
+ * Returns a list of TNP_Profile which are public.
1430
+ *
1431
+ * @staticvar array $profiles
1432
+ * @param string $language
1433
+ * @return TNP_Profile[]
1434
+ */
1435
+ function get_profiles_public($language = '') {
1436
+ static $profiles = [];
1437
+ if (isset($profiles[$language])) {
1438
+ return $profiles[$language];
1439
+ }
1440
+
1441
+ $profiles[$language] = [];
1442
+ $all = $this->get_profiles($language);
1443
+ foreach ($all as $profile) {
1444
+ if ($profile->is_private())
1445
+ continue;
1446
+
1447
+ $profiles[$language]['' . $profile->id] = $profile;
1448
+ }
1449
+ return $profiles[$language];
1450
+ }
1451
+
1452
+ /**
1453
+ * Really bad name!
1454
+ * @staticvar array $profiles
1455
+ * @param type $language
1456
+ * @return array
1457
+ */
1458
+ function get_profiles_for_profile($language = '') {
1459
+ static $profiles = [];
1460
+ if (isset($profiles[$language])) {
1461
+ return $profiles[$language];
1462
+ }
1463
+
1464
+ $profiles[$language] = [];
1465
+ $all = $this->get_profiles($language);
1466
+ foreach ($all as $profile) {
1467
+ if (!$profile->show_on_profile())
1468
+ continue;
1469
+
1470
+ $profiles[$language]['' . $profile->id] = $profile;
1471
+ }
1472
+ return $profiles[$language];
1473
+ }
1474
+
1475
+ /**
1476
+ * @param string $language The language for the list labels (it does not affect the lists returned)
1477
+ * @return TNP_List[]
1478
+ */
1479
+ function get_lists($language = '') {
1480
+ static $lists = array();
1481
+ if (isset($lists[$language])) {
1482
+ return $lists[$language];
1483
+ }
1484
+
1485
+ $lists[$language] = array();
1486
+ $data = NewsletterSubscription::instance()->get_options('lists', $language);
1487
+ for ($i = 1; $i <= NEWSLETTER_LIST_MAX; $i++) {
1488
+ if (empty($data['list_' . $i])) {
1489
+ continue;
1490
+ }
1491
+ $list = $this->create_tnp_list_from_db_lists_array($data, $i);
1492
+
1493
+ $lists[$language]['' . $list->id] = $list;
1494
+ }
1495
+ return $lists[$language];
1496
+ }
1497
+
1498
+ public function create_tnp_list_from_db_lists_array($db_lists_array, $list_id) {
1499
+
1500
+ $list = new TNP_List();
1501
+ $list->name = $db_lists_array['list_' . $list_id];
1502
+ $list->id = $list_id;
1503
+
1504
+ // New format
1505
+ if (isset($db_lists_array['list_' . $list_id . '_subscription'])) {
1506
+ $list->forced = !empty($db_lists_array['list_' . $list_id . '_forced']);
1507
+ $list->status = empty($db_lists_array['list_' . $list_id . '_status']) ? TNP_List::STATUS_PRIVATE : TNP_List::STATUS_PUBLIC;
1508
+ $list->checked = $db_lists_array['list_' . $list_id . '_subscription'] == 2;
1509
+ $list->show_on_subscription = $list->status != TNP_List::STATUS_PRIVATE && !empty($db_lists_array['list_' . $list_id . '_subscription']) && !$list->forced;
1510
+ $list->show_on_profile = $list->status != TNP_List::STATUS_PRIVATE && !empty($db_lists_array['list_' . $list_id . '_profile']);
1511
+ } else {
1512
+ $list->forced = !empty($db_lists_array['list_' . $list_id . '_forced']);
1513
+ $list->status = empty($db_lists_array['list_' . $list_id . '_status']) ? TNP_List::STATUS_PRIVATE : TNP_List::STATUS_PUBLIC;
1514
+ $list->checked = !empty($db_lists_array['list_' . $list_id . '_checked']);
1515
+ $list->show_on_subscription = $db_lists_array['list_' . $list_id . '_status'] == 2 && !$list->forced;
1516
+ $list->show_on_profile = $db_lists_array['list_' . $list_id . '_status'] == 1 || $db_lists_array['list_' . $list_id . '_status'] == 2;
1517
+ }
1518
+ if (empty($db_lists_array['list_' . $list_id . '_languages'])) {
1519
+ $list->languages = array();
1520
+ } else {
1521
+ $list->languages = $db_lists_array['list_' . $list_id . '_languages'];
1522
+ }
1523
+
1524
+ return $list;
1525
+ }
1526
+
1527
+ /**
1528
+ * Returns an array of TNP_List objects of lists that are public.
1529
+ * @return TNP_List[]
1530
+ */
1531
+ function get_lists_public($language = '') {
1532
+ static $lists = array();
1533
+ if (isset($lists[$language])) {
1534
+ return $lists[$language];
1535
+ }
1536
+
1537
+ $lists[$language] = array();
1538
+ $all = $this->get_lists($language);
1539
+ foreach ($all as $list) {
1540
+ if ($list->status == TNP_List::STATUS_PRIVATE) {
1541
+ continue;
1542
+ }
1543
+ $lists[$language]['' . $list->id] = $list;
1544
+ }
1545
+ return $lists[$language];
1546
+ }
1547
+
1548
+ /**
1549
+ * Lists to be shown on subscription form.
1550
+ *
1551
+ * @return TNP_List[]
1552
+ */
1553
+ function get_lists_for_subscription($language = '') {
1554
+ static $lists = array();
1555
+ if (isset($lists[$language])) {
1556
+ return $lists[$language];
1557
+ }
1558
+
1559
+ $lists[$language] = array();
1560
+ $all = $this->get_lists($language);
1561
+ foreach ($all as $list) {
1562
+ if (!$list->show_on_subscription) {
1563
+ continue;
1564
+ }
1565
+ $lists[$language]['' . $list->id] = $list;
1566
+ }
1567
+ return $lists[$language];
1568
+ }
1569
+
1570
+ /**
1571
+ * Returns the lists to be shown in the profile page. The list is associative with
1572
+ * the list ID as key.
1573
+ *
1574
+ * @return TNP_List[]
1575
+ */
1576
+ function get_lists_for_profile($language = '') {
1577
+ static $lists = array();
1578
+ if (isset($lists[$language])) {
1579
+ return $lists[$language];
1580
+ }
1581
+
1582
+ $lists[$language] = array();
1583
+ $all = $this->get_lists($language);
1584
+ foreach ($all as $list) {
1585
+ if (!$list->show_on_profile) {
1586
+ continue;
1587
+ }
1588
+ $lists[$language]['' . $list->id] = $list;
1589
+ }
1590
+ return $lists[$language];
1591
+ }
1592
+
1593
+ /**
1594
+ * Returns the list object or null if not found.
1595
+ *
1596
+ * @param int $id
1597
+ * @return TNP_List
1598
+ */
1599
+ function get_list($id, $language = '') {
1600
+ $lists = $this->get_lists($language);
1601
+ if (!isset($lists['' . $id])) {
1602
+ return null;
1603
+ }
1604
+
1605
+ return $lists['' . $id];
1606
+ }
1607
+
1608
+ /**
1609
+ * NEVER CHANGE THIS METHOD SIGNATURE, USER BY THIRD PARTY PLUGINS.
1610
+ *
1611
+ * Saves a new user on the database. Return false if the email (that must be unique) is already
1612
+ * there. For a new users set the token and creation time if not passed.
1613
+ *
1614
+ * @param array $user
1615
+ * @return TNP_User|array|boolean Returns the subscriber reloaded from DB in the specified format. Flase on failure (duplicate email).
1616
+ */
1617
+ function save_user($user, $return_format = OBJECT) {
1618
+ if (is_object($user)) {
1619
+ $user = (array) $user;
1620
+ }
1621
+ if (empty($user['id'])) {
1622
+ $existing = $this->get_user($user['email']);
1623
+ if ($existing != null) {
1624
+ return false;
1625
+ }
1626
+ if (empty($user['token'])) {
1627
+ $user['token'] = NewsletterModule::get_token();
1628
+ }
1629
+ }
1630
+
1631
+ // We still don't know when it happens but under some conditions, matbe external, lists are passed as NULL
1632
+ foreach ($user as $key => $value) {
1633
+ if (strpos($key, 'list_') !== 0) {
1634
+ continue;
1635
+ }
1636
+ if (is_null($value)) {
1637
+ unset($user[$key]);
1638
+ } else {
1639
+ $user[$key] = (int) $value;
1640
+ }
1641
+ }
1642
+
1643
+ // Due to the unique index on email field, this can fail.
1644
+ return $this->store->save(NEWSLETTER_USERS_TABLE, $user, $return_format);
1645
+ }
1646
+
1647
+ /**
1648
+ * Updates the user last activity timestamp.
1649
+ *
1650
+ * @global wpdb $wpdb
1651
+ * @param TNP_User $user
1652
+ */
1653
+ function update_user_last_activity($user) {
1654
+ global $wpdb;
1655
+ $this->query($wpdb->prepare("update " . NEWSLETTER_USERS_TABLE . " set last_activity=%d where id=%d limit 1", time(), $user->id));
1656
+ }
1657
+
1658
+ function update_user_ip($user, $ip) {
1659
+ global $wpdb;
1660
+ // Only if changed
1661
+ $r = $this->query($wpdb->prepare("update " . NEWSLETTER_USERS_TABLE . " set ip=%s, geo=0 where ip<>%s and id=%d limit 1", $ip, $ip, $user->id));
1662
+ }
1663
+
1664
+ /**
1665
+ * Finds single style blocks and adds a style attribute to every HTML tag with a class exactly matching the rules in the style
1666
+ * block. HTML tags can use the attribute "inline-class" to exact match a style rules if they need a composite class definition.
1667
+ *
1668
+ * @param string $content
1669
+ * @param boolean $strip_style_blocks
1670
+ * @return string
1671
+ */
1672
+ function inline_css($content, $strip_style_blocks = false) {
1673
+ $matches = array();
1674
+ // "s" skips line breaks
1675
+ $styles = preg_match('|<style>(.*?)</style>|s', $content, $matches);
1676
+ if (isset($matches[1])) {
1677
+ $style = str_replace(array("\n", "\r"), '', $matches[1]);
1678
+ $rules = array();
1679
+ preg_match_all('|\s*\.(.*?)\{(.*?)\}\s*|s', $style, $rules);
1680
+ for ($i = 0; $i < count($rules[1]); $i++) {
1681
+ $class = trim($rules[1][$i]);
1682
+ $value = trim($rules[2][$i]);
1683
+ $value = preg_replace('|\s+|', ' ', $value);
1684
+ $content = str_replace(' class="' . $class . '"', ' class="' . $class . '" style="' . $value . '"', $content);
1685
+ $content = str_replace(' inline-class="' . $class . '"', ' style="' . $value . '"', $content);
1686
+ }
1687
+ }
1688
+
1689
+ if ($strip_style_blocks) {
1690
+ return trim(preg_replace('|<style>.*?</style>|s', '', $content));
1691
+ } else {
1692
+ return $content;
1693
+ }
1694
+ }
1695
+
1696
+ /**
1697
+ * Returns a list of users marked as "test user".
1698
+ * @return TNP_User[]
1699
+ */
1700
+ function get_test_users() {
1701
+ return $this->store->get_all(NEWSLETTER_USERS_TABLE, "where test=1 and status in ('C', 'S')");
1702
+ }
1703
+
1704
+ /**
1705
+ * Deletes a subscriber and cleans up all the stats table with his correlated data.
1706
+ *
1707
+ * @global wpdb $wpdb
1708
+ * @param int|id[] $id
1709
+ */
1710
+ function delete_user($id) {
1711
+ global $wpdb;
1712
+ $id = (array) $id;
1713
+ foreach ($id as $user_id) {
1714
+ $user = $this->get_user($user_id);
1715
+ if ($user) {
1716
+ $r = $this->store->delete(NEWSLETTER_USERS_TABLE, $user_id);
1717
+ $wpdb->delete(NEWSLETTER_STATS_TABLE, array('user_id' => $user_id));
1718
+ $wpdb->delete(NEWSLETTER_SENT_TABLE, array('user_id' => $user_id));
1719
+ do_action('newsletter_user_deleted', $user);
1720
+ }
1721
+ }
1722
+
1723
+ return count($id);
1724
+ }
1725
+
1726
+ /**
1727
+ * Add to a destination URL the parameters to identify the user, the email and to show
1728
+ * an alert message, if required. The parameters are then managed by the [newsletter] shortcode.
1729
+ *
1730
+ * @param string $url If empty the standard newsletter page URL is used (usually it is empty, but sometime a custom URL has been specified)
1731
+ * @param string $message_key The message identifier
1732
+ * @param TNP_User|int $user
1733
+ * @param TNP_Email|int $email
1734
+ * @param string $alert An optional alter message to be shown. Does not work with custom URLs
1735
+ * @return string The final URL with parameters
1736
+ */
1737
+ function build_message_url($url = '', $message_key = '', $user = null, $email = null, $alert = '') {
1738
+ $params = 'nm=' . urlencode($message_key);
1739
+ $language = '';
1740
+ if ($user) {
1741
+ if (!is_object($user)) {
1742
+ $user = $this->get_user($user);
1743
+ }
1744
+ if ($message_key == 'confirmation') {
1745
+ $params .= '&nk=' . urlencode($this->get_user_key($user, 'preconfirm'));
1746
+ } else {
1747
+ $params .= '&nk=' . urlencode($this->get_user_key($user));
1748
+ }
1749
+
1750
+ $language = $this->get_user_language($user);
1751
+ }
1752
+
1753
+ if ($email) {
1754
+ if (!is_object($email)) {
1755
+ $email = $this->get_email($email);
1756
+ }
1757
+ $params .= '&nek=' . urlencode($this->get_email_key($email));
1758
+ }
1759
+
1760
+ if ($alert) {
1761
+ $params .= '&alert=' . urlencode($alert);
1762
+ }
1763
+
1764
+ if (empty($url)) {
1765
+ $url = Newsletter::instance()->get_newsletter_page_url($language);
1766
+ }
1767
+
1768
+ return self::add_qs($url, $params, false);
1769
+ }
1770
+
1771
+ /**
1772
+ * Builds a standard Newsletter action URL for the specified action.
1773
+ *
1774
+ * @param string $action
1775
+ * @param TNP_User $user
1776
+ * @param TNP_Email $email
1777
+ * @return string
1778
+ */
1779
+ function build_action_url($action, $user = null, $email = null) {
1780
+ $url = $this->add_qs($this->get_home_url(), 'na=' . urlencode($action));
1781
+ //$url = $this->add_qs(admin_url('admin-ajax.php'), 'action=newsletter&na=' . urlencode($action));
1782
+ if ($user) {
1783
+ $url .= '&nk=' . urlencode($this->get_user_key($user));
1784
+ }
1785
+ if ($email) {
1786
+ $url .= '&nek=' . urlencode($this->get_email_key($email));
1787
+ }
1788
+ return $url;
1789
+ }
1790
+
1791
+ function get_subscribe_url() {
1792
+ return $this->build_action_url('s');
1793
+ }
1794
+
1795
+ function clean_stats_table() {
1796
+ global $wpdb;
1797
+ $this->logger->info('Cleaning up stats table');
1798
+ $this->query("delete s from `{$wpdb->prefix}newsletter_stats` s left join `{$wpdb->prefix}newsletter` u on s.user_id=u.id where u.id is null");
1799
+ $this->query("delete s from `{$wpdb->prefix}newsletter_stats` s left join `{$wpdb->prefix}newsletter_emails` e on s.email_id=e.id where e.id is null");
1800
+ }
1801
+
1802
+ function clean_sent_table() {
1803
+ global $wpdb;
1804
+ $this->logger->info('Cleaning up sent table');
1805
+ $this->query("delete s from `{$wpdb->prefix}newsletter_sent` s left join `{$wpdb->prefix}newsletter` u on s.user_id=u.id where u.id is null");
1806
+ $this->query("delete s from `{$wpdb->prefix}newsletter_sent` s left join `{$wpdb->prefix}newsletter_emails` e on s.email_id=e.id where e.id is null");
1807
+ }
1808
+
1809
+ function clean_user_logs_table() {
1810
+ //global $wpdb;
1811
+ }
1812
+
1813
+ function clean_tables() {
1814
+ $this->clean_sent_table();
1815
+ $this->clean_stats_table();
1816
+ $this->clean_user_logs_table();
1817
+ }
1818
+
1819
+ function anonymize_ip($ip) {
1820
+ if (empty($ip)) {
1821
+ return $ip;
1822
+ }
1823
+ $parts = explode('.', $ip);
1824
+ array_pop($parts);
1825
+ return implode('.', $parts) . '.0';
1826
+ }
1827
+
1828
+ function process_ip($ip) {
1829
+ $option = Newsletter::instance()->options['ip'];
1830
+ if (empty($option)) {
1831
+ return $ip;
1832
+ }
1833
+ if ($option == 'anonymize') {
1834
+ return $this->anonymize_ip($ip);
1835
+ }
1836
+ return '';
1837
+ }
1838
+
1839
+ function anonymize_user($id) {
1840
+ global $wpdb;
1841
+ $user = $this->get_user($id);
1842
+ if (!$user) {
1843
+ return null;
1844
+ }
1845
+
1846
+ $user->name = '';
1847
+ $user->surname = '';
1848
+ $user->ip = $this->anonymize_ip($user->ip);
1849
+
1850
+ for ($i = 1; $i <= NEWSLETTER_PROFILE_MAX; $i++) {
1851
+ $field = 'profile_' . $i;
1852
+ $user->$field = '';
1853
+ }
1854
+
1855
+ // [TODO] Status?
1856
+ $user->status = TNP_User::STATUS_UNSUBSCRIBED;
1857
+ $user->email = $user->id . '@anonymi.zed';
1858
+
1859
+ $user = $this->save_user($user);
1860
+
1861
+ return $user;
1862
+ }
1863
+
1864
+ /**
1865
+ * Changes a user status. Accept a user object, user id or user email.
1866
+ *
1867
+ * @param TNP_User $user
1868
+ * @param string $status
1869
+ * @return TNP_User
1870
+ */
1871
+ function set_user_status($user, $status) {
1872
+ global $wpdb;
1873
+
1874
+ $this->logger->debug('Status change to ' . $status . ' of subscriber ' . $user->id . ' from ' . $_SERVER['REQUEST_URI']);
1875
+
1876
+ $this->query($wpdb->prepare("update " . NEWSLETTER_USERS_TABLE . " set status=%s where id=%d limit 1", $status, $user->id));
1877
+ $user->status = $status;
1878
+ return $this->get_user($user);
1879
+ }
1880
+
1881
+ /**
1882
+ *
1883
+ * @global wpdb $wpdb
1884
+ * @param TNP_User $user
1885
+ * @return TNP_User
1886
+ */
1887
+ function refresh_user_token($user) {
1888
+ global $wpdb;
1889
+
1890
+ $token = $this->get_token();
1891
+
1892
+ $this->query($wpdb->prepare("update " . NEWSLETTER_USERS_TABLE . " set token=%s where id=%d limit 1", $token, $user->id));
1893
+ $user->token = $token;
1894
+ }
1895
+
1896
+ /**
1897
+ * Create a log entry with the meaningful user data.
1898
+ *
1899
+ * @global wpdb $wpdb
1900
+ * @param TNP_User $user
1901
+ * @param string $source
1902
+ * @return type
1903
+ */
1904
+ function add_user_log($user, $source = '') {
1905
+ global $wpdb;
1906
+
1907
+ $lists = $this->get_lists_public();
1908
+ foreach ($lists as $list) {
1909
+ $field_name = 'list_' . $list->id;
1910
+ $data[$field_name] = $user->$field_name;
1911
+ }
1912
+ $data['status'] = $user->status;
1913
+ $ip = $this->get_remote_ip();
1914
+ $ip = $this->process_ip($ip);
1915
+ $this->store->save($wpdb->prefix . 'newsletter_user_logs', array('ip' => $ip, 'user_id' => $user->id, 'source' => $source, 'created' => time(), 'data' => json_encode($data)));
1916
+ }
1917
+
1918
+ /**
1919
+ *
1920
+ * @global wpdb $wpdb
1921
+ * @param TNP_User $user
1922
+ * @param int $list
1923
+ * @param type $value
1924
+ */
1925
+ function set_user_list($user, $list, $value) {
1926
+ global $wpdb;
1927
+
1928
+ $list = (int) $list;
1929
+ $value = $value ? 1 : 0;
1930
+ $r = $wpdb->update(NEWSLETTER_USERS_TABLE, array('list_' . $list => $value), array('id' => $user->id));
1931
+ }
1932
+
1933
+ function set_user_field($id, $field, $value) {
1934
+ $this->store->set_field(NEWSLETTER_USERS_TABLE, $id, $field, $value);
1935
+ }
1936
+
1937
+ function set_user_wp_user_id($user_id, $wp_user_id) {
1938
+ $this->store->set_field(NEWSLETTER_USERS_TABLE, $user_id, 'wp_user_id', $wp_user_id);
1939
+ }
1940
+
1941
+ /**
1942
+ *
1943
+ * @param int $wp_user_id
1944
+ * @param string $format
1945
+ * @return TNP_User
1946
+ */
1947
+ function get_user_by_wp_user_id($wp_user_id, $format = OBJECT) {
1948
+ return $this->store->get_single_by_field(NEWSLETTER_USERS_TABLE, 'wp_user_id', $wp_user_id, $format);
1949
+ }
1950
+
1951
+ /**
1952
+ * Returns the user language IF there is a supported mutilanguage plugin installed.
1953
+ * @param TNP_User $user
1954
+ * @return string Language code or empty
1955
+ */
1956
+ function get_user_language($user) {
1957
+ if ($user && $this->is_multilanguage()) {
1958
+ return $user->language;
1959
+ }
1960
+ return '';
1961
+ }
1962
+
1963
+ /**
1964
+ * Replaces every possible Newsletter tag ({...}) in a piece of text or HTML.
1965
+ *
1966
+ * @global wpdb $wpdb
1967
+ * @param string $text
1968
+ * @param mixed $user Can be an object, associative array or id
1969
+ * @param mixed $email Can be an object, associative array or id
1970
+ * @param type $referrer
1971
+ * @return type
1972
+ */
1973
+ function replace($text, $user = null, $email = null, $referrer = null) {
1974
+ global $wpdb;
1975
+
1976
+ if (strpos($text, '<p') !== false) {
1977
+ $esc_html = true;
1978
+ } else {
1979
+ $esc_html = false;
1980
+ }
1981
+
1982
+ static $home_url = false;
1983
+
1984
+ if (!$home_url) {
1985
+ $home_url = home_url('/');
1986
+ }
1987
+
1988
+ //$this->logger->debug('Replace start');
1989
+ if ($user !== null && !is_object($user)) {
1990
+ if (is_array($user)) {
1991
+ $user = (object) $user;
1992
+ } else if (is_numeric($user)) {
1993
+ $user = $this->get_user($user);
1994
+ } else {
1995
+ $user = null;
1996
+ }
1997
+ }
1998
+
1999
+ if ($email !== null && !is_object($email)) {
2000
+ if (is_array($email)) {
2001
+ $email = (object) $email;
2002
+ } else if (is_numeric($email)) {
2003
+ $email = $this->get_email($email);
2004
+ } else {
2005
+ $email = null;
2006
+ }
2007
+ }
2008
+
2009
+ $initial_language = $this->get_current_language();
2010
+
2011
+ if ($user && $user->language) {
2012
+ $this->switch_language($user->language);
2013
+ }
2014
+
2015
+
2016
+ $text = apply_filters('newsletter_replace', $text, $user, $email, $esc_html);
2017
+
2018
+ $text = $this->replace_url($text, 'blog_url', $home_url);
2019
+ $text = $this->replace_url($text, 'home_url', $home_url);
2020
+
2021
+ $text = str_replace('{blog_title}', html_entity_decode(get_bloginfo('name')), $text);
2022
+ $text = str_replace('{blog_description}', get_option('blogdescription'), $text);
2023
+
2024
+ $text = $this->replace_date($text);
2025
+
2026
+ if ($user) {
2027
+ //$this->logger->debug('Replace with user ' . $user->id);
2028
+ $nk = $this->get_user_key($user);
2029
+ $options_profile = NewsletterSubscription::instance()->get_options('profile', $this->get_user_language($user));
2030
+ $text = str_replace('{email}', $user->email, $text);
2031
+ $name = apply_filters('newsletter_replace_name', $user->name, $user);
2032
+ if (empty($name)) {
2033
+ $text = str_replace(' {name}', '', $text);
2034
+ $text = str_replace('{name}', '', $text);
2035
+ } else {
2036
+ $text = str_replace('{name}', esc_html($name), $text);
2037
+ }
2038
+
2039
+ switch ($user->sex) {
2040
+ case 'm': $text = str_replace('{title}', $options_profile['title_male'], $text);
2041
+ break;
2042
+ case 'f': $text = str_replace('{title}', $options_profile['title_female'], $text);
2043
+ break;
2044
+ case 'n': $text = str_replace('{title}', $options_profile['title_none'], $text);
2045
+ break;
2046
+ default:
2047
+ $text = str_replace('{title}', '', $text);
2048
+ }
2049
+
2050
+
2051
+ // Deprecated
2052
+ $text = str_replace('{surname}', esc_html($user->surname), $text);
2053
+ $text = str_replace('{last_name}', esc_html($user->surname), $text);
2054
+
2055
+ $full_name = esc_html(trim($user->name . ' ' . $user->surname));
2056
+ if (empty($full_name)) {
2057
+ $text = str_replace(' {full_name}', '', $text);
2058
+ $text = str_replace('{full_name}', '', $text);
2059
+ } else {
2060
+ $text = str_replace('{full_name}', $full_name, $text);
2061
+ }
2062
+
2063
+ $text = str_replace('{token}', $user->token, $text);
2064
+ $text = str_replace('%7Btoken%7D', $user->token, $text);
2065
+ $text = str_replace('{id}', $user->id, $text);
2066
+ $text = str_replace('%7Bid%7D', $user->id, $text);
2067
+ $text = str_replace('{ip}', $user->ip, $text);
2068
+ $text = str_replace('{key}', $nk, $text);
2069
+ $text = str_replace('%7Bkey%7D', $nk, $text);
2070
+
2071
+ for ($i = 1; $i <= NEWSLETTER_PROFILE_MAX; $i++) {
2072
+ $p = 'profile_' . $i;
2073
+ $text = str_replace('{profile_' . $i . '}', $user->$p, $text);
2074
+ }
2075
+
2076
+ $base = (empty($this->options_main['url']) ? get_option('home') : $this->options_main['url']);
2077
+ $id_token = '&amp;ni=' . $user->id . '&amp;nt=' . $user->token;
2078
+
2079
+ $text = $this->replace_url($text, 'subscription_confirm_url', $this->build_action_url('c', $user));
2080
+ $text = $this->replace_url($text, 'activation_url', $this->build_action_url('c', $user));
2081
+
2082
+ // Obsolete.
2083
+ $text = $this->replace_url($text, 'FOLLOWUP_SUBSCRIPTION_URL', self::add_qs($base, 'nm=fs' . $id_token));
2084
+ $text = $this->replace_url($text, 'FOLLOWUP_UNSUBSCRIPTION_URL', self::add_qs($base, 'nm=fu' . $id_token));
2085
+
2086
+ $text = $this->replace_url($text, 'UNLOCK_URL', $this->build_action_url('ul', $user));
2087
+ } else {
2088
+ //$this->logger->debug('Replace without user');
2089
+ $text = $this->replace_url($text, 'subscription_confirm_url', '#');
2090
+ $text = $this->replace_url($text, 'activation_url', '#');
2091
+ }
2092
+
2093
+ if ($email) {
2094
+ //$this->logger->debug('Replace with email ' . $email->id);
2095
+ $nek = $this->get_email_key($email);
2096
+ $text = str_replace('{email_id}', $email->id, $text);
2097
+ $text = str_replace('{email_key}', $nek, $text);
2098
+ $text = str_replace('{email_subject}', $email->subject, $text);
2099
+ // Deprecated
2100
+ $text = str_replace('{subject}', $email->subject, $text);
2101
+ $text = $this->replace_url($text, 'email_url', $this->build_action_url('v', $user) . '&id=' . $email->id);
2102
+ } else {
2103
+ //$this->logger->debug('Replace without email');
2104
+ $text = $this->replace_url($text, 'email_url', '#');
2105
+ }
2106
+
2107
+ if (strpos($text, '{subscription_form}') !== false) {
2108
+ $text = str_replace('{subscription_form}', NewsletterSubscription::instance()->get_subscription_form($referrer), $text);
2109
+ } else {
2110
+ for ($i = 1; $i <= 10; $i++) {
2111
+ if (strpos($text, "{subscription_form_$i}") !== false) {
2112
+ $text = str_replace("{subscription_form_$i}", NewsletterSubscription::instance()->get_form($i), $text);
2113
+ break;
2114
+ }
2115
+ }
2116
+ }
2117
+
2118
+ // Company info
2119
+ // TODO: Move to another module
2120
+ $options = Newsletter::instance()->get_options('info');
2121
+ $text = str_replace('{company_address}', $options['footer_contact'], $text);
2122
+ $text = str_replace('{company_name}', $options['footer_title'], $text);
2123
+ $text = str_replace('{company_legal}', $options['footer_legal'], $text);
2124
+
2125
+ $this->switch_language($initial_language);
2126
+ //$this->logger->debug('Replace end');
2127
+ return $text;
2128
+ }
2129
+
2130
+ function replace_date($text) {
2131
+ $text = str_replace('{date}', date_i18n(get_option('date_format')), $text);
2132
+
2133
+ // Date processing
2134
+ $x = 0;
2135
+ while (($x = strpos($text, '{date_', $x)) !== false) {
2136
+ $y = strpos($text, '}', $x);
2137
+ if ($y === false)
2138
+ continue;
2139
+ $f = substr($text, $x + 6, $y - $x - 6);
2140
+ $text = substr($text, 0, $x) . date_i18n($f) . substr($text, $y + 1);
2141
+ }
2142
+ return $text;
2143
+ }
2144
+
2145
+ function replace_url($text, $tag, $url) {
2146
+ static $home = false;
2147
+ if (!$home) {
2148
+ $home = trailingslashit(home_url());
2149
+ }
2150
+ $tag_lower = strtolower($tag);
2151
+ $text = str_replace('http://{' . $tag_lower . '}', $url, $text);
2152
+ $text = str_replace('https://{' . $tag_lower . '}', $url, $text);
2153
+ $text = str_replace($home . '{' . $tag_lower . '}', $url, $text);
2154
+ $text = str_replace($home . '%7B' . $tag_lower . '%7D', $url, $text);
2155
+ $text = str_replace('{' . $tag_lower . '}', $url, $text);
2156
+ $text = str_replace('%7B' . $tag_lower . '%7D', $url, $text);
2157
+
2158
+ $url_encoded = urlencode($url);
2159
+ $text = str_replace('%7B' . $tag_lower . '_encoded%7D', $url_encoded, $text);
2160
+ $text = str_replace('{' . $tag_lower . '_encoded}', $url_encoded, $text);
2161
+
2162
+ // for compatibility
2163
+ $text = str_replace($home . $tag, $url, $text);
2164
+
2165
+ return $text;
2166
+ }
2167
+
2168
+ public static function antibot_form_check($captcha = false) {
2169
+
2170
+ if (defined('NEWSLETTER_ANTIBOT') && !NEWSLETTER_ANTIBOT) {
2171
+ return true;
2172
+ }
2173
+
2174
+ if (strtolower($_SERVER['REQUEST_METHOD']) != 'post') {
2175
+ return false;
2176
+ }
2177
+
2178
+ if (!isset($_POST['ts']) || time() - $_POST['ts'] > 60) {
2179
+ return false;
2180
+ }
2181
+
2182
+ if ($captcha) {
2183
+ $n1 = (int) $_POST['n1'];
2184
+ if (empty($n1)) {
2185
+ return false;
2186
+ }
2187
+ $n2 = (int) $_POST['n2'];
2188
+ if (empty($n2)) {
2189
+ return false;
2190
+ }
2191
+ $n3 = (int) $_POST['n3'];
2192
+ if ($n1 + $n2 != $n3) {
2193
+ return false;
2194
+ }
2195
+ }
2196
+
2197
+ return true;
2198
+ }
2199
+
2200
+ public static function request_to_antibot_form($submit_label = 'Continue...', $captcha = false) {
2201
+ header('Content-Type: text/html;charset=UTF-8');
2202
+ header('X-Robots-Tag: noindex,nofollow,noarchive');
2203
+ header('Cache-Control: no-cache,no-store,private');
2204
+ echo "<!DOCTYPE html>\n";
2205
+ echo '<html><head>'
2206
+ . '<style type="text/css">'
2207
+ . '.tnp-captcha {text-align: center; margin: 200px auto 0 auto !important; max-width: 300px !important; padding: 10px !important; font-family: "Open Sans", sans-serif; background: #ECF0F1; border-radius: 5px; padding: 50px !important; border: none !important;}'
2208
+ . 'p {text-align: center; padding: 10px; color: #7F8C8D;}'
2209
+ . 'input[type=text] {width: 50px; padding: 10px 10px; border: none; border-radius: 2px; margin: 0px 5px;}'
2210
+ . 'input[type=submit] {text-align: center; border: none; padding: 10px 15px; font-family: "Open Sans", sans-serif; background-color: #27AE60; color: white; cursor: pointer;}'
2211
+ . '</style>'
2212
+ . '</head><body>';
2213
+ echo '<form method="post" action="https://www.domain.tld" id="form">';
2214
+ echo '<div style="width: 1px; height: 1px; overflow: hidden">';
2215
+ foreach ($_REQUEST as $name => $value) {
2216
+ if ($name == 'submit')
2217
+ continue;
2218
+ if (is_array($value)) {
2219
+ foreach ($value as $element) {
2220
+ echo '<input type="text" name="';
2221
+ echo esc_attr($name);
2222
+ echo '[]" value="';
2223
+ echo esc_attr(stripslashes($element));
2224
+ echo '">';
2225
+ }
2226
+ } else {
2227
+ echo '<input type="hidden" name="', esc_attr($name), '" value="', esc_attr(stripslashes($value)), '">';
2228
+ }
2229
+ }
2230
+ if (isset($_SERVER['HTTP_REFERER'])) {
2231
+ echo '<input type="hidden" name="nhr" value="' . esc_attr($_SERVER['HTTP_REFERER']) . '">';
2232
+ }
2233
+ echo '<input type="hidden" name="ts" value="' . time() . '">';
2234
+ echo '</div>';
2235
+
2236
+ if ($captcha) {
2237
+ echo '<div class="tnp-captcha">';
2238
+ echo '<p>', __('Math question', 'newsletter'), '</p>';
2239
+ echo '<input type="text" name="n1" value="', rand(1, 9), '" readonly style="width: 50px">';
2240
+ echo '+';
2241
+ echo '<input type="text" name="n2" value="', rand(1, 9), '" readonly style="width: 50px">';
2242
+ echo '=';
2243
+ echo '<input type="text" name="n3" value="?" style="width: 50px">';
2244
+ echo '<br><br>';
2245
+ echo '<input type="submit" value="', esc_attr($submit_label), '">';
2246
+ echo '</div>';
2247
+ }
2248
+ echo '<noscript><input type="submit" value="';
2249
+ echo esc_attr($submit_label);
2250
+ echo '"></noscript></form>';
2251
+ echo '<script>';
2252
+ echo 'document.getElementById("form").action="' . home_url('/') . '";';
2253
+ if (!$captcha) {
2254
+ echo 'document.getElementById("form").submit();';
2255
+ }
2256
+ echo '</script>';
2257
+ echo '</body></html>';
2258
+ die();
2259
+ }
2260
+
2261
+ static function extract_body($html) {
2262
+ $x = stripos($html, '<body');
2263
+ if ($x !== false) {
2264
+ $x = strpos($html, '>', $x);
2265
+ $y = strpos($html, '</body>');
2266
+ return substr($html, $x + 1, $y - $x - 1);
2267
+ } else {
2268
+ return $html;
2269
+ }
2270
+ }
2271
+
2272
+ /** Returns a percentage as string */
2273
+ static function percent($value, $total) {
2274
+ if ($total == 0)
2275
+ return '-';
2276
+ return sprintf("%.2f", $value / $total * 100) . '%';
2277
+ }
2278
+
2279
+ /** Returns a percentage as integer value */
2280
+ static function percentValue($value, $total) {
2281
+ if ($total == 0)
2282
+ return 0;
2283
+ return round($value / $total * 100);
2284
+ }
2285
+
2286
+ /**
2287
+ * Takes in a variable and checks if object, array or scalar and return the integer representing
2288
+ * a database record id.
2289
+ *
2290
+ * @param mixed $var
2291
+ * @return in
2292
+ */
2293
+ static function to_int_id($var) {
2294
+ if (is_object($var)) {
2295
+ return (int) $var->id;
2296
+ }
2297
+ if (is_array($var)) {
2298
+ return (int) $var['id'];
2299
+ }
2300
+ return (int) $var;
2301
+ }
2302
+
2303
+ static function to_array($text) {
2304
+ $text = trim($text);
2305
+ if (empty($text)) {
2306
+ return array();
2307
+ }
2308
+ $text = preg_split("/\\r\\n/", $text);
2309
+ $text = array_map('trim', $text);
2310
+ $text = array_map('strtolower', $text);
2311
+ $text = array_filter($text);
2312
+
2313
+ return $text;
2314
+ }
2315
+
2316
+ static function sanitize_ip($ip) {
2317
+ if (empty($ip)) {
2318
+ return '';
2319
+ }
2320
+ return preg_replace('/[^0-9a-fA-F:., ]/', '', trim($ip));
2321
+ }
2322
+
2323
+ static function get_remote_ip() {
2324
+ $ip = '';
2325
+ if (!empty($_SERVER['HTTP_X_REAL_IP'])) {
2326
+ $ip = $_SERVER['HTTP_X_REAL_IP'];
2327
+ } elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
2328
+ // Sometimes this is a list of IPs representing the chain of proxies
2329
+ $ip = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR'], 1);
2330
+ $ip = $ip[0];
2331
+ } elseif (isset($_SERVER['REMOTE_ADDR'])) {
2332
+ $ip = $_SERVER['REMOTE_ADDR'];
2333
+ }
2334
+ return self::sanitize_ip($ip);
2335
+ }
2336
+
2337
+ static function get_signature($text) {
2338
+ $key = NewsletterStatistics::instance()->options['key'];
2339
+ return md5($text . $key);
2340
+ }
2341
+
2342
+ static function check_signature($text, $signature) {
2343
+ if (empty($signature)) {
2344
+ return false;
2345
+ }
2346
+ $key = NewsletterStatistics::instance()->options['key'];
2347
+ return md5($text . $key) === $signature;
2348
+ }
2349
+
2350
+ static function get_home_url() {
2351
+ static $url = false;
2352
+ if (!$url) {
2353
+ $url = home_url('/');
2354
+ }
2355
+ return $url;
2356
+ }
2357
+
2358
+ static function clean_eol($text) {
2359
+ $text = str_replace("\r\n", "\n", $text);
2360
+ $text = str_replace("\r", "\n", $text);
2361
+ $text = str_replace("\n", "\r\n", $text);
2362
+ return $text;
2363
+ }
2364
+
2365
+ function set_current_language($language) {
2366
+ self::$current_language = $language;
2367
+ }
2368
+
2369
+ /**
2370
+ * Return the current language code. Optionally, if a user is passed and it has a language
2371
+ * the user language is returned.
2372
+ * If there is no language available, an empty string is returned.
2373
+ *
2374
+ * @param TNP_User $user
2375
+ * @return string The language code
2376
+ */
2377
+ function get_current_language($user = null) {
2378
+
2379
+ if ($user && $user->language) {
2380
+ return $user->language;
2381
+ }
2382
+
2383
+ if (!empty(self::$current_language)) {
2384
+ return self::$current_language;
2385
+ }
2386
+
2387
+ // WPML
2388
+ if (class_exists('SitePress')) {
2389
+ $current_language = apply_filters('wpml_current_language', '');
2390
+ if ($current_language == 'all') {
2391
+ $current_language = '';
2392
+ }
2393
+ return $current_language;
2394
+ }
2395
+
2396
+ // Polylang
2397
+ if (function_exists('pll_current_language')) {
2398
+ return pll_current_language();
2399
+ }
2400
+
2401
+ // Trnslatepress and/or others
2402
+ $current_language = apply_filters('newsletter_current_language', '');
2403
+
2404
+ return $current_language;
2405
+ }
2406
+
2407
+ function get_default_language() {
2408
+ if (class_exists('SitePress')) {
2409
+ return $current_language = apply_filters('wpml_current_language', '');
2410
+ } else if (function_exists('pll_default_language')) {
2411
+ return pll_default_language();
2412
+ } else if (class_exists('TRP_Translate_Press')) {
2413
+ // TODO: Find the default language
2414
+ }
2415
+ return '';
2416
+ }
2417
+
2418
+ function is_all_languages() {
2419
+ return $this->get_current_language() == '';
2420
+ }
2421
+
2422
+ function is_default_language() {
2423
+ return $this->get_current_language() == $this->get_default_language();
2424
+ }
2425
+
2426
+ /**
2427
+ * Returns an array of languages with key the language code and value the language name.
2428
+ * An empty array is returned if no language is available.
2429
+ */
2430
+ function get_languages() {
2431
+ $language_options = array();
2432
+
2433
+ if (class_exists('SitePress')) {
2434
+ $languages = apply_filters('wpml_active_languages', null);
2435
+ foreach ($languages as $language) {
2436
+ $language_options[$language['language_code']] = $language['translated_name'];
2437
+ }
2438
+ return $language_options;
2439
+ } else if (function_exists('icl_get_languages')) {
2440
+ $languages = icl_get_languages();
2441
+ foreach ($languages as $code => $language) {
2442
+ $language_options[$code] = $language['native_name'];
2443
+ }
2444
+ return $language_options;
2445
+ }
2446
+
2447
+ return apply_filters('newsletter_languages', $language_options);
2448
+ }
2449
+
2450
+ function get_language_label($language) {
2451
+ $languages = $this->get_languages();
2452
+ if (isset($languages[$language])) {
2453
+ return $languages[$language];
2454
+ }
2455
+ return '';
2456
+ }
2457
+
2458
+ /**
2459
+ * Changes the current language usually before extracting the posts since WPML
2460
+ * does not support the language filter in the post query (or at least we didn't
2461
+ * find it).
2462
+ *
2463
+ * @param string $language
2464
+ */
2465
+ function switch_language($language) {
2466
+ if (class_exists('SitePress')) {
2467
+ if (empty($language)) {
2468
+ $language = 'all';
2469
+ }
2470
+ do_action('wpml_switch_language', $language);
2471
+ return;
2472
+ }
2473
+ }
2474
+
2475
+ function is_multilanguage() {
2476
+ return class_exists('SitePress') || function_exists('pll_default_language') || class_exists('TRP_Translate_Press');
2477
+ }
2478
+
2479
+ function get_posts($filters = array(), $language = '') {
2480
+ $current_language = $this->get_current_language();
2481
+
2482
+ // Language switch for WPML
2483
+ if ($language) {
2484
+ if (class_exists('SitePress')) {
2485
+ $this->switch_language($language);
2486
+ $filters['suppress_filters'] = false;
2487
+ }
2488
+ if (class_exists('Polylang')) {
2489
+ $filters['lang'] = $language;
2490
+ }
2491
+ }
2492
+ $posts = get_posts($filters);
2493
+ if ($language) {
2494
+ if (class_exists('SitePress')) {
2495
+ $this->switch_language($current_language);
2496
+ }
2497
+ }
2498
+ return $posts;
2499
+ }
2500
+
2501
+ function get_wp_query($filters, $langiage = '') {
2502
+ if ($language) {
2503
+ if (class_exists('SitePress')) {
2504
+ $this->switch_language($language);
2505
+ $filters['suppress_filters'] = false;
2506
+ }
2507
+ if (class_exists('Polylang')) {
2508
+ $filters['lang'] = $language;
2509
+ }
2510
+ }
2511
+
2512
+ $posts = new WP_Query($filters);
2513
+
2514
+ if ($language) {
2515
+ if (class_exists('SitePress')) {
2516
+ $this->switch_language($current_language);
2517
+ }
2518
+ }
2519
+
2520
+ return $posts;
2521
+ }
2522
+
2523
+ protected function generate_admin_notification_message($user) {
2524
+
2525
+ $message = file_get_contents(__DIR__ . '/notification.html');
2526
+
2527
+ $message = $this->replace($message, $user);
2528
+ $message = str_replace('{user_admin_url}', admin_url('admin.php?page=newsletter_users_edit&id=' . $user->id), $message);
2529
+
2530
+ return $message;
2531
+ }
2532
+
2533
+ protected function generate_admin_notification_subject($subject) {
2534
+ $blogname = wp_specialchars_decode(get_option('blogname'), ENT_QUOTES);
2535
+
2536
+ return '[' . $blogname . '] ' . $subject;
2537
+ }
2538
+
2539
+ function dienow($message, $admin_message = null, $http_code = 200) {
2540
+ if ($admin_message && current_user_can('administrator')) {
2541
+ $message .= '<br><br><strong>Text below only visibile to administrators</strong><br>';
2542
+ $message .= $admin_message;
2543
+ }
2544
+ wp_die($message, $http_code);
2545
+ }
2546
+
2547
+ function dump($var) {
2548
+ if (NEWSLETTER_DEBUG) {
2549
+ var_dump($var);
2550
+ }
2551
+ }
2552
+
2553
+ function dump_die($var) {
2554
+ if (NEWSLETTER_DEBUG) {
2555
+ var_dump($var);
2556
+ die();
2557
+ }
2558
+ }
2559
+
2560
+ }
2561
+
2562
+ /**
2563
+ * Kept for compatibility.
2564
+ *
2565
+ * @param type $post_id
2566
+ * @param type $size
2567
+ * @param type $alternative
2568
+ * @return type
2569
+ */
2570
+ function nt_post_image($post_id = null, $size = 'thumbnail', $alternative = null) {
2571
+ return NewsletterModule::get_post_image($post_id, $size, $alternative);
2572
+ }
2573
+
2574
+ function newsletter_get_post_image($post_id = null, $size = 'thumbnail', $alternative = null) {
2575
+ echo NewsletterModule::get_post_image($post_id, $size, $alternative);
2576
+ }
2577
+
2578
+ /**
2579
+ * Accepts a post or a post ID.
2580
+ *
2581
+ * @param WP_Post $post
2582
+ */
2583
+ function newsletter_the_excerpt($post, $words = 30) {
2584
+ $post = get_post($post);
2585
+ $excerpt = $post->post_excerpt;
2586
+ if (empty($excerpt)) {
2587
+ $excerpt = $post->post_content;
2588
+ $excerpt = strip_shortcodes($excerpt);
2589
+ $excerpt = wp_strip_all_tags($excerpt, true);
2590
+ }
2591
+ echo '<p>' . wp_trim_words($excerpt, $words) . '</p>';
2592
+ }
main/css/extensions.css ADDED
@@ -0,0 +1,152 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* Used even by the addons manager addon */
2
+
3
+ .tnp-extension-premium-box, .tnp-extension-free-box, .tnp-integration-box {
4
+ width: 300px;
5
+ height: 220px;
6
+ /*background-color: #222B36;*/
7
+ background-color: #28313C;
8
+ text-align: center;
9
+ margin: 20px 20px 0px 0px;
10
+ float: left;
11
+ position: relative;
12
+ }
13
+
14
+ .tnp-extension-premium-box:hover, .tnp-extension-free-box:hover, .tnp-integration-box:hover {
15
+ /*background-color: #232C35;*/
16
+ background-color: #2B343F;
17
+ box-shadow: 1px 1px 15px #222B36;
18
+ }
19
+
20
+ .tnp-extension-premium-box p, .tnp-extension-free-box p, .tnp-integration-box p {
21
+ padding: 5px 10px;
22
+ color: #72777c;
23
+ font-size: 14px;
24
+ margin-top: 0px;
25
+ }
26
+
27
+ .tnp-extension-premium-box h3 {
28
+ font-family: soleil, sans-serif;
29
+ padding: 5px 8px !important;
30
+ border-radius: 3px;
31
+ display: inline-block;
32
+ font-size: 16px;
33
+ color: #fff;
34
+ margin-bottom: 0px !important;
35
+ margin-top: 25px !important;
36
+ font-weight: 300;
37
+ width: auto !important;
38
+ border-bottom: none !important;
39
+ }
40
+
41
+ .tnp-extension-free-box h3 {
42
+ font-family: soleil, sans-serif;
43
+ padding: 5px 8px !important;
44
+ border-radius: 3px;
45
+ display: inline-block;
46
+ font-size: 16px;
47
+ color: #fff;
48
+ margin-bottom: 0px !important;
49
+ margin-top: 25px !important;
50
+ font-weight: 300;
51
+ width: auto !important;
52
+ border-bottom: none !important;
53
+ }
54
+
55
+ .tnp-integration-box h3 {
56
+ font-family: soleil, sans-serif;
57
+ padding: 5px 8px !important;
58
+ border-radius: 3px;
59
+ display: inline-block;
60
+ font-size: 16px;
61
+ color: #fff;
62
+ margin-bottom: 0px !important;
63
+ margin-top: 25px !important;
64
+ font-weight: 300;
65
+ width: auto !important;
66
+ border-bottom: none !important;
67
+ }
68
+
69
+ .tnp-extension-premium-action {
70
+ bottom: 0;
71
+ position: absolute;
72
+ width: 100%;
73
+ padding: 12px;
74
+ font-family: soleil, sans-serif;
75
+ }
76
+
77
+ .tnp-extension-free-action {
78
+ bottom: 0;
79
+ position: absolute;
80
+ width: 100%;
81
+ padding: 12px;
82
+ font-family: soleil, sans-serif;
83
+ }
84
+
85
+ .tnp-integration-action {
86
+ bottom: 0;
87
+ position: absolute;
88
+ width: 100%;
89
+ padding: 12px;
90
+ font-family: soleil, sans-serif;
91
+ }
92
+
93
+
94
+ .tnp-extension-premium-action span {
95
+ color: #27AE60;
96
+ }
97
+
98
+ .tnp-extension-free-action span {
99
+ color: #27AE60;
100
+ }
101
+
102
+ .tnp-integration-action span {
103
+ color: #27AE60;
104
+ }
105
+
106
+ .tnp-extension-activate {
107
+ color: #1ABC9C;
108
+ padding: 5px 8px;
109
+ text-decoration: none;
110
+ cursor: pointer;
111
+ }
112
+
113
+ .tnp-extension-install {
114
+ color: #2980B9;
115
+ padding: 5px 8px;
116
+ text-decoration: none;
117
+ cursor: pointer;
118
+ }
119
+
120
+ .tnp-extension-buy {
121
+ color: #F1C40F;
122
+ padding: 5px 8px;
123
+ text-decoration: none;
124
+ cursor: pointer;
125
+ }
126
+
127
+ #tnp-body a.tnp-extension-details{
128
+ color: #999999;
129
+ padding: 5px 8px;
130
+ text-decoration: none;
131
+ cursor: pointer;
132
+ }
133
+
134
+ .tnp-extension-free {
135
+ color: #D35400;
136
+ padding: 5px 8px;
137
+ text-decoration: none;
138
+ cursor: pointer;
139
+ position: relative;
140
+ }
141
+
142
+ img.tnp-extensions-free-badge {
143
+ position: absolute;
144
+ display: block;
145
+ right: 0;
146
+ top: 0;
147
+ width: 70px;
148
+ }
149
+
150
+ .tnp-extensions-image img {
151
+ margin: 25px 0px -10px;
152
+ }
main/extensions.php CHANGED
@@ -1,124 +1,133 @@
1
- <?php
2
- /* @var $this Newsletter */
3
- defined('ABSPATH') || exit;
4
-
5
- include_once NEWSLETTER_INCLUDES_DIR . '/controls.php';
6
- $controls = new NewsletterControls();
7
- $extensions = $this->getTnpExtensions();
8
-
9
- if ($controls->is_action('activate')) {
10
- $result = activate_plugin('newsletter-extensions/extensions.php');
11
- if (is_wp_error($result)) {
12
- $controls->errors .= __('Error while activating:', 'newsletter') . " " . $result->get_error_message();
13
- } else {
14
- wp_clean_plugins_cache(false);
15
- $this->clear_extensions_cache();
16
- $controls->js_redirect('admin.php?page=newsletter_extensions_index');
17
- }
18
- }
19
-
20
- ?>
21
-
22
- <div class="wrap" id="tnp-wrap">
23
-
24
- <?php include NEWSLETTER_DIR . '/tnp-header.php'; ?>
25
-
26
- <div id="tnp-body">
27
- <?php if (is_wp_error(validate_plugin('newsletter-extensions/extensions.php'))) { ?>
28
- <div id="tnp-promo">
29
-
30
- <h1>Supercharge Newsletter with our Professional Addons</h1>
31
- <div class="tnp-promo-how-to">
32
- <h3>How to install:</h3>
33
- <p>To add our addons, free or professional, you need to install our Addons Manager. But don't worry, it's super easy! Just click on "Download" button to download the zip file of
34
- the Addon Manager from our website, then click on "Install" to upload the same zip file to your WordPress installation.</p>
35
- </div>
36
- <div class="tnp-promo-buttons">
37
- <a class="tnp-promo-button" href="https://www.thenewsletterplugin.com/get-addons-manager"><i class="fas fa-cloud-download-alt"></i> Download Addons Manager</a>
38
- <a class="tnp-promo-button" href="<?php echo admin_url('plugin-install.php?tab=upload') ?>"><i class="fas fa-cloud-upload-alt"></i> Install</a>
39
- </div>
40
-
41
- </div>
42
- <?php } elseif (is_plugin_inactive('newsletter-extensions/extensions.php')) { ?>
43
- <div id="tnp-promo">
44
- <div class="tnp-promo-how-to">
45
- <p>Addons Manager seems installed but not active.</p>
46
- <p>Activate it to install and update our free and professional addons.</p>
47
- </div>
48
- <div class="tnp-promo-buttons">
49
- <a class="tnp-promo-button" href="<?php echo wp_nonce_url(admin_url('admin.php') . '?page=newsletter_main_extensions&act=activate', 'save'); ?>"><i class="fas fa-power-off"></i> Activate</a>
50
- </div>
51
- </div>
52
- <?php } ?>
53
-
54
- <?php if (is_array($extensions)) { ?>
55
-
56
- <!-- Extensions -->
57
- <h3 class="tnp-section-title">Additional professional features</h3>
58
- <?php foreach ($extensions AS $e) { ?>
59
-
60
- <?php if ($e->type == "extension" || $e->type == "premium") { ?>
61
- <div class="<?php echo $e->free ? 'tnp-extension-free-box' : 'tnp-extension-premium-box' ?> <?php echo $e->slug ?>">
62
-
63
- <?php if ($e->free) { ?>
64
- <img class="tnp-extensions-free-badge" src="<?php echo plugins_url('newsletter') ?>/images/extension-free.png">
65
- <?php } ?>
66
- <div class="tnp-extensions-image"><img src="<?php echo $e->image ?>" alt="" /></div>
67
- <h3><?php echo $e->title ?></h3>
68
- <p><?php echo $e->description ?></p>
69
- </div>
70
- <?php } ?>
71
- <?php } ?>
72
-
73
- <!-- Integrations -->
74
- <h3 class="tnp-section-title">Integrations with 3rd party plugins</h3>
75
- <?php foreach ($extensions AS $e) { ?>
76
-
77
- <?php if ($e->type == "integration") { ?>
78
-
79
- <div class="<?php echo $e->free ? 'tnp-extension-free-box' : 'tnp-integration-box' ?> <?php echo $e->slug ?>">
80
-
81
- <?php if ($e->free) { ?>
82
- <img class="tnp-extensions-free-badge" src="<?php echo plugins_url('newsletter') ?>/images/extension-free.png">
83
- <?php } ?>
84
- <div class="tnp-extensions-image"><img src="<?php echo $e->image ?>"></div>
85
- <h3><?php echo $e->title ?></h3>
86
- <p><?php echo $e->description ?></p>
87
- </div>
88
- <?php } ?>
89
-
90
- <?php } ?>
91
-
92
- <!-- Delivery -->
93
- <h3 class="tnp-section-title">Integrations with reliable mail delivery services</h3>
94
- <?php foreach ($extensions AS $e) { ?>
95
-
96
- <?php if ($e->type == "delivery") { ?>
97
- <div class="<?php echo $e->free ? 'tnp-extension-free-box' : 'tnp-integration-box' ?> <?php echo $e->slug ?>">
98
-
99
- <?php if ($e->free) { ?>
100
- <img class="tnp-extensions-free-badge" src="<?php echo plugins_url('newsletter') ?>/images/extension-free.png">
101
- <?php } ?>
102
- <div class="tnp-extensions-image"><img src="<?php echo $e->image ?>" alt="" /></div>
103
- <h3><?php echo $e->title ?></h3>
104
- <p><?php echo $e->description ?></p>
105
- </div>
106
- <?php } ?>
107
-
108
- <?php } ?>
109
-
110
-
111
- <?php } else { ?>
112
-
113
- <p style="color: white;">No addons available, try later.</p>
114
-
115
- <?php } ?>
116
-
117
-
118
- <p class="clear"></p>
119
-
120
- </div>
121
-
122
- <?php include NEWSLETTER_DIR . '/tnp-footer.php'; ?>
123
-
124
- </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
+ $extensions = $this->getTnpExtensions();
8
+
9
+ if ($controls->is_action('activate')) {
10
+ $result = activate_plugin('newsletter-extensions/extensions.php');
11
+ if (is_wp_error($result)) {
12
+ $controls->errors .= __('Error while activating:', 'newsletter') . " " . $result->get_error_message();
13
+ } else {
14
+ wp_clean_plugins_cache(false);
15
+ $this->clear_extensions_cache();
16
+ $controls->js_redirect('admin.php?page=newsletter_extensions_index');
17
+ }
18
+ }
19
+ ?>
20
+
21
+ <style>
22
+ <?php include __DIR__ . '/css/extensions.css' ?>
23
+ </style>
24
+
25
+ <div class="wrap tnp-main tnp-main-extensions" id="tnp-wrap">
26
+
27
+ <?php include NEWSLETTER_DIR . '/tnp-header.php'; ?>
28
+
29
+ <div id="tnp-body">
30
+ <?php if (is_wp_error(validate_plugin('newsletter-extensions/extensions.php'))) { ?>
31
+ <div id="tnp-promo">
32
+
33
+ <h1>Supercharge Newsletter with our Professional Addons</h1>
34
+ <div class="tnp-promo-how-to">
35
+ <h3>How to install:</h3>
36
+ <p>To add our addons, free or professional, you need to install our Addons Manager. But don't worry, it's super easy! Just click on "Download" button to download the zip file of
37
+ the Addon Manager from our website, then click on "Install" to upload the same zip file to your WordPress installation.</p>
38
+ </div>
39
+ <div class="tnp-promo-buttons">
40
+ <a class="tnp-promo-button" href="https://www.thenewsletterplugin.com/get-addons-manager"><i class="fas fa-cloud-download-alt"></i> Download Addons Manager</a>
41
+ <a class="tnp-promo-button" href="<?php echo admin_url('plugin-install.php?tab=upload') ?>"><i class="fas fa-cloud-upload-alt"></i> Install</a>
42
+ </div>
43
+
44
+ </div>
45
+ <?php } elseif (is_plugin_inactive('newsletter-extensions/extensions.php')) { ?>
46
+ <div id="tnp-promo">
47
+ <div class="tnp-promo-how-to">
48
+ <p>Addons Manager seems installed but not active.</p>
49
+ <p>Activate it to install and update our free and professional addons.</p>
50
+ </div>
51
+ <div class="tnp-promo-buttons">
52
+ <a class="tnp-promo-button" href="<?php echo wp_nonce_url(admin_url('admin.php') . '?page=newsletter_main_extensions&act=activate', 'save'); ?>"><i class="fas fa-power-off"></i> Activate</a>
53
+ </div>
54
+ </div>
55
+ <?php } ?>
56
+
57
+ <?php if (is_array($extensions)) { ?>
58
+
59
+ <!-- Extensions -->
60
+ <div class="tnp-section">
61
+ <h3 class="tnp-section-title">Additional professional features</h3>
62
+ <?php foreach ($extensions AS $e) { ?>
63
+
64
+ <?php if ($e->type == "extension" || $e->type == "premium") { ?>
65
+ <div class="<?php echo $e->free ? 'tnp-extension-free-box' : 'tnp-extension-premium-box' ?> <?php echo $e->slug ?>">
66
+
67
+ <?php if ($e->free) { ?>
68
+ <img class="tnp-extensions-free-badge" src="<?php echo plugins_url('newsletter') ?>/images/extension-free.png">
69
+ <?php } ?>
70
+ <div class="tnp-extensions-image"><img src="<?php echo $e->image ?>" alt="" /></div>
71
+ <h3><?php echo $e->title ?></h3>
72
+ <p><?php echo $e->description ?></p>
73
+ </div>
74
+ <?php } ?>
75
+ <?php } ?>
76
+ </div>
77
+
78
+ <!-- Integrations -->
79
+ <div class="tnp-section">
80
+ <h3 class="tnp-section-title">Integrations with 3rd party plugins</h3>
81
+ <?php foreach ($extensions AS $e) { ?>
82
+
83
+ <?php if ($e->type == "integration") { ?>
84
+
85
+ <div class="<?php echo $e->free ? 'tnp-extension-free-box' : 'tnp-integration-box' ?> <?php echo $e->slug ?>">
86
+
87
+ <?php if ($e->free) { ?>
88
+ <img class="tnp-extensions-free-badge" src="<?php echo plugins_url('newsletter') ?>/images/extension-free.png">
89
+ <?php } ?>
90
+ <div class="tnp-extensions-image"><img src="<?php echo $e->image ?>"></div>
91
+ <h3><?php echo $e->title ?></h3>
92
+ <p><?php echo $e->description ?></p>
93
+ </div>
94
+ <?php } ?>
95
+
96
+ <?php } ?>
97
+ </div>
98
+
99
+ <!-- Delivery -->
100
+ <div class="tnp-section">
101
+ <h3 class="tnp-section-title">Integrations with reliable mail delivery services</h3>
102
+ <?php foreach ($extensions AS $e) { ?>
103
+
104
+ <?php if ($e->type == "delivery") { ?>
105
+ <div class="<?php echo $e->free ? 'tnp-extension-free-box' : 'tnp-integration-box' ?> <?php echo $e->slug ?>">
106
+
107
+ <?php if ($e->free) { ?>
108
+ <img class="tnp-extensions-free-badge" src="<?php echo plugins_url('newsletter') ?>/images/extension-free.png">
109
+ <?php } ?>
110
+ <div class="tnp-extensions-image"><img src="<?php echo $e->image ?>" alt="" /></div>
111
+ <h3><?php echo $e->title ?></h3>
112
+ <p><?php echo $e->description ?></p>
113
+ </div>
114
+ <?php } ?>
115
+
116
+ <?php } ?>
117
+ </div>
118
+
119
+
120
+ <?php } else { ?>
121
+
122
+ <p style="color: white;">No addons available, try later.</p>
123
+
124
+ <?php } ?>
125
+
126
+
127
+ <p class="clear"></p>
128
+
129
+ </div>
130
+
131
+ <?php include NEWSLETTER_DIR . '/tnp-footer.php'; ?>
132
+
133
+ </div>
main/index.php CHANGED
@@ -81,37 +81,37 @@ $lists = $this->get_lists();
81
  <div class="tnp-dashboard">
82
  <div class="tnp-cards-container">
83
  <div class="tnp-card tnp-mimosa">
84
- <div class="tnp-card-title">Forms</div>
85
- <div class="tnp-card-description">Setup the form fields and labels.</div>
86
  <div class="tnp-card-button-container">
87
- <a href="?page=newsletter_subscription_profile">Edit forms</a>
88
  </div>
89
  </div>
90
  <div class="tnp-card">
91
- <div class="tnp-card-title">Lists</div>
92
  <div class="tnp-card-description">You have <?php echo count($lists) ?> lists.</div>
93
  <div class="tnp-card-button-container">
94
- <a href="?page=newsletter_subscription_lists">Manage</a>
95
  </div>
96
  </div>
97
  <div class="tnp-card">
98
- <div class="tnp-card-title">Delivery</div>
99
- <div class="tnp-card-description">Change the delivery speed, sender name and return path.</div>
100
  <div class="tnp-card-button-container">
101
- <a href="?page=newsletter_main_main">Change the delivery settings</a>
102
  </div>
103
  </div>
104
  <div class="tnp-card">
105
- <div class="tnp-card-title">Personal Info</div>
106
- <div class="tnp-card-description">Set your company name, address, socials.</div>
107
  <div class="tnp-card-button-container">
108
- <a href="?page=newsletter_main_info">Edit your info</a>
109
  </div>
110
  </div>
111
  </div>
112
  <div class="tnp-cards-container">
113
  <div class="tnp-card">
114
- <div class="tnp-card-title">Newsletters</div>
115
  <div class="tnp-card-upper-buttons"><a href="?page=newsletter_emails_composer"><?php _e('New', 'newsletter') ?></a></div>
116
  <div class="tnp-card-upper-buttons"><a href="?page=newsletter_emails_index"><?php _e('List', 'newsletter') ?></a></div>
117
  <div class="tnp-card-content">
81
  <div class="tnp-dashboard">
82
  <div class="tnp-cards-container">
83
  <div class="tnp-card tnp-mimosa">
84
+ <div class="tnp-card-title"><?php esc_html_e('Forms', 'newsletter')?></div>
85
+ <div class="tnp-card-description"><?php esc_html_e('Setup the form fields and labels.', 'newsletter')?></div>
86
  <div class="tnp-card-button-container">
87
+ <a href="?page=newsletter_subscription_profile"><?php esc_html_e('Edit forms', 'newsletter')?></a>
88
  </div>
89
  </div>
90
  <div class="tnp-card">
91
+ <div class="tnp-card-title"><?php esc_html_e('Lists', 'newsletter')?></div>
92
  <div class="tnp-card-description">You have <?php echo count($lists) ?> lists.</div>
93
  <div class="tnp-card-button-container">
94
+ <a href="?page=newsletter_subscription_lists"><?php esc_html_e('Manage', 'newsletter')?></a>
95
  </div>
96
  </div>
97
  <div class="tnp-card">
98
+ <div class="tnp-card-title"><?php esc_html_e('Delivery', 'newsletter')?></div>
99
+ <div class="tnp-card-description"><?php esc_html_e('Change the delivery speed, sender name and return path.', 'newsletter')?></div>
100
  <div class="tnp-card-button-container">
101
+ <a href="?page=newsletter_main_main"><?php esc_html_e('Change the delivery settings', 'newsletter')?></a>
102
  </div>
103
  </div>
104
  <div class="tnp-card">
105
+ <div class="tnp-card-title"><?php esc_html_e('Company Info', 'newsletter')?></div>
106
+ <div class="tnp-card-description"><?php esc_html_e('Set your company name, address, socials.', 'newsletter')?></div>
107
  <div class="tnp-card-button-container">
108
+ <a href="?page=newsletter_main_info"><?php esc_html_e('Edit your info', 'newsletter')?></a>
109
  </div>
110
  </div>
111
  </div>
112
  <div class="tnp-cards-container">
113
  <div class="tnp-card">
114
+ <div class="tnp-card-title"><?php esc_html_e('Newsletters', 'newsletter')?></div>
115
  <div class="tnp-card-upper-buttons"><a href="?page=newsletter_emails_composer"><?php _e('New', 'newsletter') ?></a></div>
116
  <div class="tnp-card-upper-buttons"><a href="?page=newsletter_emails_index"><?php _e('List', 'newsletter') ?></a></div>
117
  <div class="tnp-card-content">
main/main.php CHANGED
@@ -35,8 +35,8 @@ if (!$controls->is_action()) {
35
  }
36
 
37
  $controls->data['scheduler_max'] = (int) $controls->data['scheduler_max'];
38
- if ($controls->data['scheduler_max'] < 10)
39
- $controls->data['scheduler_max'] = 10;
40
 
41
 
42
  if (!$this->is_email($controls->data['reply_to'], true)) {
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)) {
plugin.php CHANGED
@@ -1,1450 +1,1419 @@
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.3.0
8
- Author: Stefano Lissa & The Newsletter Team
9
- Author URI: https://www.thenewsletterplugin.com
10
- Disclaimer: Use at your own risk. No warranty expressed or implied is provided.
11
- Text Domain: newsletter
12
- License: GPLv2 or later
13
- Requires at least: 4.6
14
- Requires PHP: 5.6
15
-
16
- Copyright 2009-2021 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.3.0');
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
-
88
- class Newsletter extends NewsletterModule {
89
-
90
- // Limits to respect to avoid memory, time or provider limits
91
- var $time_start;
92
- var $time_limit;
93
- var $email_limit = 10; // Per run, every 5 minutes
94
- var $limits_set = false;
95
- var $max_emails = 20;
96
-
97
- /**
98
- * @var PHPMailer
99
- */
100
- var $mailer;
101
- // Message shown when the interaction is inside a WordPress page
102
- var $message;
103
- var $user;
104
- var $error;
105
- var $theme;
106
-
107
- var $action = '';
108
-
109
- /** @var Newsletter */
110
- static $instance;
111
-
112
- const STATUS_NOT_CONFIRMED = 'S';
113
- const STATUS_CONFIRMED = 'C';
114
-
115
- /**
116
- * @return Newsletter
117
- */
118
- static function instance() {
119
- if (self::$instance == null) {
120
- self::$instance = new Newsletter();
121
- }
122
- return self::$instance;
123
- }
124
-
125
- function __construct() {
126
-
127
- // Grab it before a plugin decides to remove it.
128
- if (isset($_GET['na'])) {
129
- $this->action = $_GET['na'];
130
- }
131
- if (isset($_POST['na'])) {
132
- $this->action = $_POST['na'];
133
- }
134
-
135
- $this->time_start = time();
136
-
137
- parent::__construct('main', '1.6.7', null, array('info', 'smtp'));
138
-
139
- $max = $this->options['scheduler_max'];
140
- if (!is_numeric($max)) {
141
- $max = 100;
142
- }
143
- $this->max_emails = max(floor($max / (3600 / NEWSLETTER_CRON_INTERVAL)), 1);
144
-
145
- add_action('plugins_loaded', array($this, 'hook_plugins_loaded'));
146
- add_action('init', array($this, 'hook_init'), 1);
147
- add_action('wp_loaded', array($this, 'hook_wp_loaded'), 1);
148
-
149
- add_action('newsletter', array($this, 'hook_newsletter'), 1);
150
-
151
- register_activation_hook(__FILE__, array($this, 'hook_activate'));
152
- register_deactivation_hook(__FILE__, array($this, 'hook_deactivate'));
153
-
154
- add_action('admin_init', array($this, 'hook_admin_init'));
155
-
156
- if (is_admin()) {
157
- add_action('admin_head', array($this, 'hook_admin_head'));
158
-
159
- // Protection against strange schedule removal on some installations
160
- if (!wp_next_scheduled('newsletter') && (!defined('WP_INSTALLING') || !WP_INSTALLING)) {
161
- wp_schedule_event(time() + 30, 'newsletter', 'newsletter');
162
- }
163
-
164
- add_action('admin_menu', array($this, 'add_extensions_menu'), 90);
165
-
166
- add_filter('display_post_states', array($this, 'add_notice_to_chosen_profile_page_hook'), 10, 2);
167
-
168
- if ($this->is_admin_page()) {
169
- // Remove the emoji replacer to save to database the original emoji characters (see even woocommerce for the same problem)
170
- remove_action('admin_print_scripts', 'print_emoji_detection_script');
171
- add_action('admin_enqueue_scripts', array($this, 'hook_wp_admin_enqueue_scripts'));
172
- }
173
-
174
- add_action('wp_ajax_tnp_hide_promotion', function () {
175
- update_option('newsletter_promotion', $_POST['id']);
176
- die();
177
- });
178
- }
179
- }
180
-
181
- function hook_plugins_loaded() {
182
-
183
- do_action('newsletter_loaded', NEWSLETTER_VERSION);
184
-
185
- if (function_exists('load_plugin_textdomain')) {
186
- load_plugin_textdomain('newsletter', false, plugin_basename(dirname(__FILE__)) . '/languages');
187
- }
188
- }
189
-
190
- function hook_init() {
191
- global $wpdb;
192
-
193
- if (isset($this->options['debug']) && $this->options['debug'] == 1) {
194
- ini_set('log_errors', 1);
195
- ini_set('error_log', WP_CONTENT_DIR . '/logs/newsletter/php-' . date('Y-m') . '-' . get_option('newsletter_logger_secret') . '.txt');
196
- }
197
-
198
- add_shortcode('newsletter_replace', array($this, 'shortcode_newsletter_replace'));
199
-
200
- add_filter('site_transient_update_plugins', array($this, 'hook_site_transient_update_plugins'));
201
-
202
- if (is_admin()) {
203
- if (!class_exists('NewsletterExtensions')) {
204
-
205
- add_filter('plugin_row_meta', function ($plugin_meta, $plugin_file) {
206
-
207
- static $slugs = array();
208
- if (empty($slugs)) {
209
- $addons = $this->getTnpExtensions();
210
- if ($addons) {
211
- foreach ($addons as $addon) {
212
- $slugs[] = $addon->wp_slug;
213
- }
214
- }
215
- }
216
- if (array_search($plugin_file, $slugs) !== false) {
217
-
218
- $plugin_meta[] = '<a href="admin.php?page=newsletter_main_extensions" style="font-weight: bold">Newsletter Addons Manager required</a>';
219
- }
220
- return $plugin_meta;
221
- }, 10, 2);
222
- }
223
-
224
- add_action('in_admin_header', array($this, 'hook_in_admin_header'), 1000);
225
-
226
- if ($this->is_admin_page()) {
227
-
228
- $dismissed = get_option('newsletter_dismissed', array());
229
-
230
- if (isset($_GET['dismiss'])) {
231
- $dismissed[$_GET['dismiss']] = 1;
232
- update_option('newsletter_dismissed', $dismissed);
233
- wp_redirect($_SERVER['HTTP_REFERER']);
234
- exit();
235
- }
236
- }
237
- } else {
238
- add_action('wp_enqueue_scripts', array($this, 'hook_wp_enqueue_scripts'));
239
- }
240
-
241
- do_action('newsletter_init');
242
- }
243
-
244
- function hook_wp_loaded() {
245
- if (empty($this->action)) {
246
- return;
247
- }
248
-
249
- if ($this->action == 'test') {
250
- // This response is tested, do not change it!
251
- echo 'ok';
252
- die();
253
- }
254
-
255
- if ($this->action === 'nul') {
256
- $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.');
257
- }
258
-
259
- $user = $this->get_user_from_request();
260
- $email = $this->get_email_from_request();
261
- do_action('newsletter_action', $this->action, $user, $email);
262
- }
263
-
264
- function hook_activate() {
265
- // Ok, why? When the plugin is not active WordPress may remove the scheduled "newsletter" action because
266
- // the every-five-minutes schedule named "newsletter" is not present.
267
- // Since the activation does not forces an upgrade, that schedule must be reactivated here. It is activated on
268
- // the upgrade method as well for the user which upgrade the plugin without deactivte it (many).
269
- if (!wp_next_scheduled('newsletter')) {
270
- wp_schedule_event(time() + 30, 'newsletter', 'newsletter');
271
- }
272
-
273
- $install_time = get_option('newsletter_install_time');
274
- if (!$install_time) {
275
- update_option('newsletter_install_time', time(), false);
276
- }
277
-
278
- touch(NEWSLETTER_LOG_DIR . '/index.html');
279
-
280
- Newsletter::instance()->upgrade();
281
- NewsletterUsers::instance()->upgrade();
282
- NewsletterEmails::instance()->upgrade();
283
- NewsletterSubscription::instance()->upgrade();
284
- NewsletterStatistics::instance()->upgrade();
285
- NewsletterProfile::instance()->upgrade();
286
- }
287
-
288
- function first_install() {
289
- parent::first_install();
290
- update_option('newsletter_show_welcome', '1', false);
291
- }
292
-
293
- function upgrade() {
294
- global $wpdb, $charset_collate;
295
-
296
- require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
297
-
298
- parent::upgrade();
299
-
300
-
301
- $sql = "CREATE TABLE `" . $wpdb->prefix . "newsletter_emails` (
302
- `id` int(11) NOT NULL AUTO_INCREMENT,
303
- `language` varchar(10) NOT NULL DEFAULT '',
304
- `subject` varchar(255) NOT NULL DEFAULT '',
305
- `message` longtext,
306
- `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
307
- `status` enum('new','sending','sent','paused','error') NOT NULL DEFAULT 'new',
308
- `total` int(11) NOT NULL DEFAULT '0',
309
- `last_id` int(11) NOT NULL DEFAULT '0',
310
- `sent` int(11) NOT NULL DEFAULT '0',
311
- `track` int(11) NOT NULL DEFAULT '1',
312
- `list` int(11) NOT NULL DEFAULT '0',
313
- `type` varchar(50) NOT NULL DEFAULT '',
314
- `query` longtext,
315
- `editor` tinyint(4) NOT NULL DEFAULT '0',
316
- `sex` varchar(20) NOT NULL DEFAULT '',
317
- `theme` varchar(50) NOT NULL DEFAULT '',
318
- `message_text` longtext,
319
- `preferences` longtext,
320
- `send_on` int(11) NOT NULL DEFAULT '0',
321
- `token` varchar(10) NOT NULL DEFAULT '',
322
- `options` longtext,
323
- `private` tinyint(1) NOT NULL DEFAULT '0',
324
- `click_count` int(10) unsigned NOT NULL DEFAULT '0',
325
- `version` varchar(10) NOT NULL DEFAULT '',
326
- `open_count` int(10) unsigned NOT NULL DEFAULT '0',
327
- `unsub_count` int(10) unsigned NOT NULL DEFAULT '0',
328
- `error_count` int(10) unsigned NOT NULL DEFAULT '0',
329
- `stats_time` int(10) unsigned NOT NULL DEFAULT '0',
330
- `updated` int(10) unsigned NOT NULL DEFAULT '0',
331
- PRIMARY KEY (`id`)
332
- ) $charset_collate;";
333
-
334
- dbDelta($sql);
335
-
336
- // WP does not manage composite primary key when it tries to upgrade a table...
337
- $suppress_errors = $wpdb->suppress_errors(true);
338
-
339
- dbDelta("CREATE TABLE " . $wpdb->prefix . "newsletter_sent (
340
- email_id int(10) unsigned NOT NULL DEFAULT '0',
341
- user_id int(10) unsigned NOT NULL DEFAULT '0',
342
- status tinyint(1) unsigned NOT NULL DEFAULT '0',
343
- open tinyint(1) unsigned NOT NULL DEFAULT '0',
344
- time int(10) unsigned NOT NULL DEFAULT '0',
345
- error varchar(255) NOT NULL DEFAULT '',
346
- ip varchar(100) NOT NULL DEFAULT '',
347
- PRIMARY KEY (email_id,user_id),
348
- KEY user_id (user_id),
349
- KEY email_id (email_id)
350
- ) $charset_collate;");
351
- $wpdb->suppress_errors($suppress_errors);
352
-
353
- // Some setting check to avoid the common support request for mis-configurations
354
- $options = $this->get_options();
355
-
356
- if (empty($options['scheduler_max']) || !is_numeric($options['scheduler_max'])) {
357
- $options['scheduler_max'] = 100;
358
- $this->save_options($options);
359
- }
360
-
361
- wp_clear_scheduled_hook('newsletter');
362
- wp_schedule_event(time() + 30, 'newsletter', 'newsletter');
363
-
364
- if (!empty($this->options['editor'])) {
365
- if (empty($this->options['roles'])) {
366
- $this->options['roles'] = array('editor');
367
- unset($this->options['editor']);
368
- }
369
- $this->save_options($this->options);
370
- }
371
-
372
- // Clear the addons and license caches
373
- delete_transient('newsletter_license_data');
374
- delete_transient("tnp_extensions_json");
375
-
376
- touch(NEWSLETTER_LOG_DIR . '/index.html');
377
-
378
- return true;
379
- }
380
-
381
- function is_allowed() {
382
- if (current_user_can('administrator')) {
383
- return true;
384
- }
385
- if (!empty($this->options['roles'])) {
386
- foreach ($this->options['roles'] as $role) {
387
- if (current_user_can($role)) {
388
- return true;
389
- }
390
- }
391
- }
392
- return false;
393
- }
394
-
395
- function admin_menu() {
396
- if (!$this->is_allowed()) {
397
- return;
398
- }
399
-
400
- add_menu_page('Newsletter', 'Newsletter', 'exist', 'newsletter_main_index', '', plugins_url('newsletter') . '/admin/images/menu-icon.png', '30.333');
401
-
402
- $this->add_menu_page('index', __('Dashboard', 'newsletter'));
403
- $this->add_admin_page('info', __('Company info', 'newsletter'));
404
-
405
- if (current_user_can('administrator')) {
406
- $this->add_menu_page('welcome', __('Welcome', 'newsletter'));
407
- $this->add_menu_page('main', __('Settings', 'newsletter'));
408
-
409
- // Pages not on menu
410
- $this->add_admin_page('smtp', 'SMTP');
411
- $this->add_admin_page('status', __('Status', 'newsletter'));
412
- $this->add_admin_page('delivery', __('Delivery Diagnostic', 'newsletter'));
413
- $this->add_admin_page('logs', __('Logs', 'newsletter'));
414
- $this->add_admin_page('scheduler', __('Scheduler', 'newsletter'));
415
- $this->add_admin_page('diagnostic', __('Diagnostic', 'newsletter'));
416
- $this->add_admin_page('test', __('Test', 'newsletter'));
417
- }
418
- }
419
-
420
- function add_extensions_menu() {
421
- if (!class_exists('NewsletterExtensions')) {
422
- $this->add_menu_page('extensions', '<span style="color:#27AE60; font-weight: bold;">' . __('Addons', 'newsletter') . '</span>');
423
- }
424
- }
425
-
426
- function hook_in_admin_header() {
427
- if (!$this->is_admin_page()) {
428
- add_action('admin_notices', array($this, 'hook_admin_notices'));
429
- return;
430
- }
431
- remove_all_actions('admin_notices');
432
- remove_all_actions('all_admin_notices');
433
- add_action('admin_notices', array($this, 'hook_admin_notices'));
434
- }
435
-
436
- function hook_admin_notices() {
437
- // Check of Newsletter dedicated page
438
- if (!empty($this->options['page'])) {
439
- if (get_post_status($this->options['page']) !== 'publish') {
440
- 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>';
441
- }
442
- }
443
-
444
- if (isset($this->options['debug']) && $this->options['debug'] == 1) {
445
- 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>';
446
- }
447
- }
448
-
449
- function hook_wp_enqueue_scripts() {
450
- if (empty($this->options['css_disabled']) && apply_filters('newsletter_enqueue_style', true)) {
451
- wp_enqueue_style('newsletter', plugins_url('newsletter') . '/style.css', [], NEWSLETTER_VERSION);
452
- if (!empty($this->options['css'])) {
453
- wp_add_inline_style('newsletter', $this->options['css']);
454
- }
455
- } else {
456
- if (!empty($this->options['css'])) {
457
- add_action('wp_head', function () {
458
- echo '<style>', $this->options['css'], '</style>';
459
- });
460
- }
461
- }
462
- }
463
-
464
- function hook_wp_admin_enqueue_scripts() {
465
-
466
- $newsletter_url = plugins_url('newsletter');
467
- wp_enqueue_script('jquery-ui-tabs');
468
- wp_enqueue_script('jquery-ui-tooltip');
469
- wp_enqueue_script('jquery-ui-draggable');
470
- wp_enqueue_media();
471
-
472
- wp_enqueue_script('tnp-admin', $newsletter_url . '/admin/admin.js', ['jquery'], NEWSLETTER_VERSION);
473
-
474
- wp_enqueue_style('tnp-select2', $newsletter_url . '/vendor/select2/css/select2.min.css', [], NEWSLETTER_VERSION);
475
- wp_enqueue_script('tnp-select2', $newsletter_url . '/vendor/select2/js/select2.min.js', ['jquery'], NEWSLETTER_VERSION);
476
-
477
- wp_enqueue_style('tnp-modal', $newsletter_url . '/admin/modal.css', [], NEWSLETTER_VERSION);
478
- wp_enqueue_script('tnp-modal', $newsletter_url . '/admin/modal.js', ['jquery'], NEWSLETTER_VERSION, true);
479
-
480
- wp_enqueue_style('tnp-toast', $newsletter_url . '/admin/toast.css', [], NEWSLETTER_VERSION);
481
- wp_enqueue_script('tnp-toast', $newsletter_url . '/admin/toast.js', ['jquery'], NEWSLETTER_VERSION);
482
-
483
- wp_enqueue_style('tnp-admin-font', 'https://use.typekit.net/jlj2wjy.css');
484
- wp_enqueue_style('tnp-admin-fontawesome', $newsletter_url . '/vendor/fa/css/all.min.css', [], NEWSLETTER_VERSION);
485
- wp_enqueue_style('tnp-admin-jquery-ui', $newsletter_url . '/vendor/jquery-ui/jquery-ui.min.css', [], NEWSLETTER_VERSION);
486
- wp_enqueue_style('tnp-admin', $newsletter_url . '/admin/admin.css', [], NEWSLETTER_VERSION);
487
- wp_enqueue_style('tnp-admin-dropdown', $newsletter_url . '/admin/dropdown.css', [], NEWSLETTER_VERSION);
488
- wp_enqueue_style('tnp-admin-tabs', $newsletter_url . '/admin/tabs.css', [], NEWSLETTER_VERSION);
489
- wp_enqueue_style('tnp-admin-controls', $newsletter_url . '/admin/controls.css', [], NEWSLETTER_VERSION);
490
- wp_enqueue_style('tnp-admin-fields', $newsletter_url . '/admin/fields.css', [], NEWSLETTER_VERSION);
491
- wp_enqueue_style('tnp-admin-widgets', $newsletter_url . '/admin/widgets.css', [], NEWSLETTER_VERSION);
492
- wp_enqueue_style('tnp-admin-extensions', $newsletter_url . '/admin/extensions.css', [], NEWSLETTER_VERSION);
493
-
494
- $translations_array = array(
495
- 'save_to_update_counter' => __('Save the newsletter to update the counter!', 'newsletter')
496
- );
497
- wp_localize_script('tnp-admin', 'tnp_translations', $translations_array);
498
-
499
- wp_enqueue_script('tnp-jquery-vmap', $newsletter_url . '/vendor/jqvmap/jquery.vmap.min.js', ['jquery'], NEWSLETTER_VERSION);
500
- wp_enqueue_script('tnp-jquery-vmap-world', $newsletter_url . '/vendor/jqvmap/jquery.vmap.world.js', ['tnp-jquery-vmap'], NEWSLETTER_VERSION);
501
- wp_enqueue_style('tnp-jquery-vmap', $newsletter_url . '/vendor/jqvmap/jqvmap.min.css', [], NEWSLETTER_VERSION);
502
-
503
- wp_register_script('tnp-chart', $newsletter_url . '/vendor/chartjs/Chart.min.js', ['jquery'], NEWSLETTER_VERSION);
504
-
505
- wp_enqueue_script('tnp-color-picker', $newsletter_url . '/vendor/spectrum/spectrum.min.js', ['jquery']);
506
- wp_enqueue_style('tnp-color-picker', $newsletter_url . '/vendor/spectrum/spectrum.min.css', [], NEWSLETTER_VERSION);
507
- }
508
-
509
- function shortcode_newsletter_replace($attrs, $content) {
510
- $content = do_shortcode($content);
511
- $content = $this->replace($content, $this->get_user_from_request(), $this->get_email_from_request());
512
- return $content;
513
- }
514
-
515
- function is_admin_page() {
516
- if (!isset($_GET['page'])) {
517
- return false;
518
- }
519
- $page = $_GET['page'];
520
- return strpos($page, 'newsletter_') === 0;
521
- }
522
-
523
- function hook_admin_init() {
524
- // Verificare il contesto
525
- if (isset($_GET['page']) && $_GET['page'] === 'newsletter_main_welcome')
526
- return;
527
- if (get_option('newsletter_show_welcome')) {
528
- delete_option('newsletter_show_welcome');
529
- wp_redirect(admin_url('admin.php?page=newsletter_main_welcome'));
530
- }
531
- }
532
-
533
- function hook_admin_head() {
534
- // Small global rule for sidebar menu entries
535
- echo '<style>';
536
- echo '.tnp-side-menu { color: #E67E22!important; }';
537
- echo '</style>';
538
- }
539
-
540
- function relink($text, $email_id, $user_id, $email_token = '') {
541
- return NewsletterStatistics::instance()->relink($text, $email_id, $user_id, $email_token);
542
- }
543
-
544
- /**
545
- * Runs every 5 minutes and look for emails that need to be processed.
546
- */
547
- function hook_newsletter() {
548
- global $wpdb;
549
-
550
- $this->logger->debug(__METHOD__ . '> Start');
551
-
552
- // Do not accept job activation before at least 4 minutes are elapsed from the last run.
553
- if (!$this->check_transient('engine', NEWSLETTER_CRON_INTERVAL)) {
554
- return;
555
- }
556
-
557
- // Retrieve all emails in "sending" status
558
- $emails = $this->get_results("select * from " . NEWSLETTER_EMAILS_TABLE . " where status='sending' and send_on<" . time() . " order by id asc");
559
- $this->logger->debug(__METHOD__ . '> Emails found in sending status: ' . count($emails));
560
-
561
- foreach ($emails as $email) {
562
- $this->logger->info(__METHOD__ . '> Start newsletter ' . $email->id);
563
- $r = $this->send($email);
564
- if ($this->limits_exceeded()) {
565
- break;
566
- }
567
- $this->logger->info(__METHOD__ . '> End newsletter ' . $email->id);
568
- }
569
- // Remove the semaphore so the delivery engine can be activated again
570
- $this->delete_transient('engine');
571
-
572
- $this->logger->debug(__METHOD__ . '> End');
573
- }
574
-
575
- /**
576
- * Sends an email to targeted users or to given users. If a list of users is given (usually a list of test users)
577
- * the query inside the email to retrieve users is not used.
578
- *
579
- * @global wpdb $wpdb
580
- * @global type $newsletter_feed
581
- * @param TNP_Email $email
582
- * @param array $users
583
- * @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.
584
- */
585
- function send($email, $users = null, $test = false) {
586
- //TODO proposte:
587
- // - Separare funzione che spedisce normalmente da quella che spedisce dei test
588
- // - Utilizzare le eccezioni per evitare di controllare se il risultato della funzione è un errore oppure no
589
- // - Le eccezioni dovrebbero essere tipizzate
590
- global $wpdb;
591
-
592
- ignore_user_abort(true);
593
-
594
- if (is_array($email)) {
595
- $email = (object) $email;
596
- }
597
-
598
- // Could be a test
599
- if (empty($email->id)) {
600
- $email->id = 0;
601
- }
602
-
603
- $this->logger->info(__METHOD__ . '> Start run for email ' . $email->id);
604
-
605
- // This stops the update of last_id and sent fields since
606
- // it's not a scheduled delivery but a test or something else (like an autoresponder)
607
- $supplied_users = $users != null;
608
-
609
- if ($users == null) {
610
-
611
- $skip_this_run = apply_filters('newsletter_send_skip', false, $email);
612
- if ($skip_this_run) {
613
- return false;
614
- }
615
-
616
- //If query is empty send to all confirmed subscribers
617
- if (empty($email->query)) {
618
- $email->query = "select * from " . NEWSLETTER_USERS_TABLE . " where status='C'";
619
- }
620
-
621
- // TODO: Ask the max emails per hour/run (to be decided) to the mailer
622
-
623
- $email->options = maybe_unserialize($email->options);
624
- // Retrieve max shipping emails per run
625
- $max_emails = apply_filters('newsletter_send_max_emails', $this->max_emails, $email);
626
- $this->logger->debug(__METHOD__ . '> Max emails per run: ' . $max_emails);
627
-
628
- if (empty($max_emails)) {
629
- $this->logger->debug(__METHOD__ . '> Max emails empty after the filter');
630
- $max_emails = $this->max_emails;
631
- }
632
-
633
- // Add limit to query
634
- //$query = apply_filters('newsletter_send_query', $email->query, $email);
635
- $query = $email->query;
636
- $query .= " and id>" . $email->last_id . " order by id limit " . $max_emails;
637
-
638
- $this->logger->debug(__METHOD__ . '> Query: ' . $query);
639
-
640
- //Retrieve subscribers
641
- $users = $this->get_results($query);
642
-
643
- $this->logger->debug(__METHOD__ . '> Loaded users: ' . count($users));
644
-
645
- // If there was a database error, return error
646
- if ($users === false) {
647
- return new WP_Error('1', 'Unable to query subscribers, check the logs');
648
- }
649
-
650
- if (empty($users)) {
651
- $this->logger->info(__METHOD__ . '> No more users, set as sent');
652
- $wpdb->query("update " . NEWSLETTER_EMAILS_TABLE . " set status='sent', total=sent where id=" . $email->id . " limit 1");
653
- do_action('newsletter_ended_sending_newsletter', $email);
654
- return true;
655
- }
656
- } else {
657
- $this->logger->info(__METHOD__ . '> Subscribers supplied');
658
- }
659
-
660
- $start_time = microtime(true);
661
- $count = 0;
662
- $result = true;
663
-
664
- $mailer = $this->get_mailer();
665
-
666
- $batch_size = $mailer->get_batch_size();
667
-
668
- $this->logger->debug(__METHOD__ . '> Batch size: ' . $batch_size);
669
-
670
- // For batch size == 1 (normal condition) we optimize
671
- if ($batch_size == 1) {
672
-
673
- foreach ($users as $user) {
674
-
675
- if (!$supplied_users && !$test && $this->limits_exceeded()) {
676
- $result = false;
677
- break;
678
- }
679
-
680
- $this->logger->debug(__METHOD__ . '> Processing user ID: ' . $user->id);
681
- $user = apply_filters('newsletter_send_user', $user);
682
- $message = $this->build_message($email, $user);
683
-
684
- // 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
685
- $this->save_sent_message($message);
686
-
687
- //Se non è un test incremento il contatore delle email spedite. Perchè incremento prima di spedire??
688
- if (!$test) {
689
- $wpdb->query("update " . NEWSLETTER_EMAILS_TABLE . " set sent=sent+1, last_id=" . $user->id . " where id=" . $email->id . " limit 1");
690
- }
691
-
692
- $r = $mailer->send($message);
693
-
694
- //Perchè dentro al messaggio vado a impostare un errore (viene fatto dentro alla $mailer->send())??? boh
695
- //in ogni caso, se ho un messaggio di errore loggo e salvo nella sent che la spedizione mi ha dato un errore
696
- if (!empty($message->error)) {
697
- $this->logger->error($message);
698
- $this->save_sent_message($message);
699
- }
700
-
701
- //Se la send mi ritorna un WP_Error loggo e ritorno al chiamante l'errore
702
- if (is_wp_error($r)) {
703
- $this->logger->error($r);
704
-
705
- // For fatal error, the newsletter status i changed to error (and the delivery stopped)
706
- if (!$test && $r->get_error_code() == NewsletterMailer::ERROR_FATAL) {
707
- $this->set_error_state_of_email($email, $r->get_error_message());
708
- }
709
-
710
- return $r;
711
- }
712
- }
713
-
714
- // TODO: Review if they're useful
715
- $this->email_limit--; //Diminuisce il limite delle email?? differenza tra $this->email_limit e $this->max_emails??
716
- $count++;
717
- } else {
718
-
719
- $chunks = array_chunk($users, $batch_size);
720
-
721
- foreach ($chunks as $chunk) {
722
-
723
- //Se gli utenti non sono stati forniti esternamente
724
- //e se non è un test
725
- //e se è stato superato il limite ALLORA imposto $result-false e interrompo il ciclo
726
- if (!$supplied_users && !$test && $this->limits_exceeded()) {
727
- $result = false;
728
- break;
729
- }
730
-
731
- $messages = array();
732
-
733
- foreach ($chunk as $user) {
734
-
735
- $this->logger->debug(__METHOD__ . '> Processing user ID: ' . $user->id);
736
- $user = apply_filters('newsletter_send_user', $user);
737
- $message = $this->build_message($email, $user);
738
- $this->save_sent_message($message);
739
- $messages[] = $message;
740
-
741
- if (!$test) {
742
- $wpdb->query("update " . NEWSLETTER_EMAILS_TABLE . " set sent=sent+1, last_id=" . $user->id . " where id=" . $email->id . " limit 1");
743
- }
744
- $this->email_limit--;
745
- $count++;
746
- }
747
-
748
- $r = $mailer->send_batch($messages);
749
-
750
- foreach ($messages as $message) {
751
- if (!empty($message->error)) {
752
- $this->save_sent_message($message);
753
- }
754
- }
755
-
756
- if (is_wp_error($r)) {
757
-
758
- if (!$test && $r->get_error_code() == NewsletterMailer::ERROR_FATAL) {
759
- $this->set_error_state_of_email($email, $r->get_error_message());
760
- }
761
-
762
- $this->logger->error($r);
763
- return $r;
764
- }
765
- }
766
- }
767
-
768
- $end_time = microtime(true);
769
-
770
- // We sent to all supplied users, but warning that no more should be processed
771
- if (!$test && $supplied_users && $this->limits_exceeded()) {
772
- $result = false;
773
- }
774
-
775
- // Stats only for newsletter with enough emails in a batch (we exclude the Autoresponder since it send one email per call)
776
- if (!$test && !$supplied_users && $count > 5) {
777
-
778
-
779
-
780
- $send_calls = get_option('newsletter_diagnostic_send_calls', []);
781
- $send_calls[] = array($start_time, $end_time, $count, $result);
782
-
783
- if (count($send_calls) > 100)
784
- array_shift($send_calls);
785
-
786
- update_option('newsletter_diagnostic_send_calls', $send_calls, false);
787
- }
788
-
789
- // Cached general statistics are reset
790
- if (!$test) {
791
- NewsletterStatistics::instance()->reset_stats_time($email->id);
792
- }
793
-
794
- $this->logger->info(__METHOD__ . '> End run for email ' . $email->id);
795
-
796
- return $result;
797
- }
798
-
799
- /**
800
- * @param TNP_Email $email
801
- */
802
- private function set_error_state_of_email($email, $message = '') {
803
- // Handle only message type at the moment
804
- if ($email->type !== 'message') {
805
- return;
806
- }
807
-
808
- do_action('newsletter_error_on_sending', $email, $message);
809
-
810
- $edited_email = new TNP_Email();
811
- $edited_email->id = $email->id;
812
- $edited_email->status = TNP_Email::STATUS_ERROR;
813
- $edited_email->options = $email->options;
814
- $edited_email->options['error_message'] = $message;
815
-
816
- $this->save_email($edited_email);
817
- }
818
-
819
- /**
820
- *
821
- * @param TNP_Email $email
822
- * @param TNP_User $user
823
- * @return \TNP_Mailer_Message
824
- */
825
- function build_message($email, $user) {
826
-
827
- $message = new TNP_Mailer_Message();
828
-
829
- $message->to = $user->email;
830
-
831
- $message->headers = [];
832
- $message->headers['Precedence'] = 'bulk';
833
- $message->headers['X-Newsletter-Email-Id'] = $email->id;
834
- $message->headers['X-Auto-Response-Suppress'] = 'OOF, AutoReply';
835
- $message->headers = apply_filters('newsletter_message_headers', $message->headers, $email, $user);
836
-
837
- $message->body = preg_replace('/data-json=".*?"/is', '', $email->message);
838
- $message->body = preg_replace('/ +/s', ' ', $message->body);
839
- $message->body = $this->replace($message->body, $user, $email);
840
- if ($this->options['do_shortcodes']) {
841
- $message->body = do_shortcode($message->body);
842
- }
843
- $message->body = apply_filters('newsletter_message_html', $message->body, $email, $user);
844
-
845
- $message->body_text = $this->replace($email->message_text, $user, $email);
846
- $message->body_text = apply_filters('newsletter_message_text', $message->body_text, $email, $user);
847
-
848
- if ($email->track == 1) {
849
- $message->body = $this->relink($message->body, $email->id, $user->id, $email->token);
850
- }
851
-
852
- $message->subject = $this->replace($email->subject, $user);
853
- $message->subject = apply_filters('newsletter_message_subject', $message->subject, $email, $user);
854
-
855
- if (!empty($email->options['sender_email'])) {
856
- $message->from = $email->options['sender_email'];
857
- } else {
858
- $message->from = $this->options['sender_email'];
859
- }
860
-
861
- if (!empty($email->options['sender_name'])) {
862
- $message->from_name = $email->options['sender_name'];
863
- } else {
864
- $message->from_name = $this->options['sender_name'];
865
- }
866
-
867
- $message->email_id = $email->id;
868
- $message->user_id = $user->id;
869
-
870
- return apply_filters('newsletter_message', $message, $email, $user);
871
- }
872
-
873
- /**
874
- *
875
- * @param TNP_Mailer_Message $message
876
- * @param int $status
877
- * @param string $error
878
- */
879
- function save_sent_message($message) {
880
- global $wpdb;
881
-
882
- if (!$message->user_id || !$message->email_id) {
883
- return;
884
- }
885
- $status = empty($message->error) ? 0 : 1;
886
-
887
- $error = mb_substr($message->error, 0, 250);
888
-
889
- $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));
890
- }
891
-
892
- /**
893
- * This function checks is, during processing, we are getting to near to system limits and should stop any further
894
- * work (when returns true).
895
- */
896
- function limits_exceeded() {
897
- global $wpdb;
898
-
899
- if (!$this->limits_set) {
900
- $this->logger->debug(__METHOD__ . '> Setting the limits for the first time');
901
-
902
- @set_time_limit(NEWSLETTER_CRON_INTERVAL + 30);
903
-
904
- $max_time = (int) (@ini_get('max_execution_time') * 0.95);
905
- if ($max_time == 0 || $max_time > NEWSLETTER_CRON_INTERVAL) {
906
- $max_time = (int) (NEWSLETTER_CRON_INTERVAL * 0.95);
907
- }
908
-
909
- $this->time_limit = $this->time_start + $max_time;
910
-
911
- $this->logger->info(__METHOD__ . '> Max time set to ' . $max_time);
912
-
913
- $max = (int) $this->options['scheduler_max'];
914
- if (!$max) {
915
- $max = 100;
916
- }
917
- $this->email_limit = max(floor($max / 12), 1);
918
- $this->logger->debug(__METHOD__ . '> Max number of emails can send: ' . $this->email_limit);
919
-
920
- $wpdb->query("set session wait_timeout=300");
921
- // From default-constants.php
922
- if (function_exists('memory_get_usage') && ( (int) @ini_get('memory_limit') < 128 )) {
923
- @ini_set('memory_limit', '256M');
924
- }
925
-
926
- $this->limits_set = true;
927
- }
928
-
929
- // The time limit is set on constructor, since it has to be set as early as possible
930
- if (time() > $this->time_limit) {
931
- $this->logger->info(__METHOD__ . '> Max execution time limit reached');
932
- return true;
933
- }
934
-
935
- if ($this->email_limit <= 0) {
936
- $this->logger->info(__METHOD__ . '> Max emails limit reached');
937
- return true;
938
- }
939
- return false;
940
- }
941
-
942
- /**
943
- * @deprecated since version 6.0.0
944
- * @param callback $callable
945
- */
946
- function register_mail_method($callable) {
947
- $this->mailer = new NewsletterMailMethodWrapper($callable);
948
- }
949
-
950
- function register_mailer($mailer) {
951
- //$this->logger->debug($mailer);
952
- if (!$mailer)
953
- return;
954
- if ($mailer instanceof NewsletterMailer) {
955
- $this->mailer = $mailer;
956
- } else {
957
- $this->logger->debug('Wrapping mailer: ' . get_class($mailer));
958
- $this->mailer = new NewsletterOldMailerWrapper($mailer);
959
- }
960
- }
961
-
962
- /**
963
- * Returns the current registered mailer which must be used to send emails.
964
- *
965
- * @return NewsletterMailer
966
- */
967
- function get_mailer() {
968
- if ($this->mailer) {
969
- return $this->mailer;
970
- }
971
-
972
- do_action('newsletter_register_mailer');
973
-
974
- if (!$this->mailer) {
975
- // Compatibility
976
- $smtp = $this->get_options('smtp');
977
- if (!empty($smtp['enabled'])) {
978
- $this->mailer = new NewsletterDefaultSMTPMailer($smtp);
979
- } else {
980
- $this->mailer = new NewsletterDefaultMailer();
981
- }
982
- }
983
- return $this->mailer;
984
- }
985
-
986
- /**
987
- *
988
- * @param TNP_Mailer_Message $message
989
- * @return type
990
- */
991
- function deliver($message) {
992
- $mailer = $this->get_mailer();
993
- if (empty($message->from))
994
- $message->from = $this->options['sender_email'];
995
- if (empty($message->from_name))
996
- $mailer->from_name = $this->options['sender_name'];
997
- return $mailer->send($message);
998
- }
999
-
1000
- /**
1001
- *
1002
- * @param type $to
1003
- * @param type $subject
1004
- * @param string|array $message If string is considered HTML, is array should contains the keys "html" and "text"
1005
- * @param type $headers
1006
- * @param type $enqueue
1007
- * @param type $from
1008
- * @return boolean
1009
- */
1010
- function mail($to, $subject, $message, $headers = array(), $enqueue = false, $from = false) {
1011
-
1012
- if (empty($subject)) {
1013
- $this->logger->error('mail> Subject empty, skipped');
1014
- return true;
1015
- }
1016
-
1017
- $mailer_message = new TNP_Mailer_Message();
1018
- $mailer_message->to = $to;
1019
- $mailer_message->subject = $subject;
1020
- $mailer_message->from = $this->options['sender_email'];
1021
- $mailer_message->from_name = $this->options['sender_name'];
1022
-
1023
- if (!empty($headers)) {
1024
- $mailer_message->headers = $headers;
1025
- }
1026
- $mailer_message->headers['X-Auto-Response-Suppress'] = 'OOF, AutoReply';
1027
-
1028
- // Message carrige returns and line feeds clean up
1029
- if (!is_array($message)) {
1030
- $mailer_message->body = $this->clean_eol($message);
1031
- } else {
1032
- if (!empty($message['text'])) {
1033
- $mailer_message->body_text = $this->clean_eol($message['text']);
1034
- }
1035
-
1036
- if (!empty($message['html'])) {
1037
- $mailer_message->body = $this->clean_eol($message['html']);
1038
- }
1039
- }
1040
-
1041
- $this->logger->debug($mailer_message);
1042
-
1043
- $mailer = $this->get_mailer();
1044
-
1045
- $r = $mailer->send($mailer_message);
1046
-
1047
- return !is_wp_error($r);
1048
- }
1049
-
1050
- function hook_deactivate() {
1051
- wp_clear_scheduled_hook('newsletter');
1052
- }
1053
-
1054
- function find_file($file1, $file2) {
1055
- if (is_file($file1))
1056
- return $file1;
1057
- return $file2;
1058
- }
1059
-
1060
- function hook_site_transient_update_plugins($value) {
1061
- static $extra_response = array();
1062
-
1063
- //$this->logger->debug('Update plugins transient called');
1064
-
1065
- if (!$value || !is_object($value)) {
1066
- //$this->logger->info('Empty object');
1067
- return $value;
1068
- }
1069
-
1070
- if (!isset($value->response) || !is_array($value->response)) {
1071
- $value->response = array();
1072
- }
1073
-
1074
- // Already computed? Use it! (this filter is called many times in a single request)
1075
- if ($extra_response) {
1076
- //$this->logger->debug('Already updated');
1077
- $value->response = array_merge($value->response, $extra_response);
1078
- return $value;
1079
- }
1080
-
1081
- $extensions = $this->getTnpExtensions();
1082
-
1083
- // Ops...
1084
- if (!$extensions) {
1085
- return $value;
1086
- }
1087
-
1088
- foreach ($extensions as $extension) {
1089
- unset($value->response[$extension->wp_slug]);
1090
- unset($value->no_update[$extension->wp_slug]);
1091
- }
1092
-
1093
- // Someone doesn't want our addons updated, let respect it (this constant should be defined in wp-config.php)
1094
- if (!NEWSLETTER_EXTENSION_UPDATE) {
1095
- //$this->logger->info('Updates disabled');
1096
- return $value;
1097
- }
1098
-
1099
- include_once(ABSPATH . 'wp-admin/includes/plugin.php');
1100
-
1101
- // Ok, that is really bad (should we remove it? is there a minimum WP version?)
1102
- if (!function_exists('get_plugin_data')) {
1103
- //$this->logger->error('No get_plugin_data function available!');
1104
- return $value;
1105
- }
1106
-
1107
- $license_key = $this->get_license_key();
1108
-
1109
- // Here we prepare the update information BUT do not add the link to the package which is privided
1110
- // by our Addons Manager (due to WP policies)
1111
- foreach ($extensions as $extension) {
1112
-
1113
- // Patch for names convention
1114
- $extension->plugin = $extension->wp_slug;
1115
-
1116
- //$this->logger->debug('Processing ' . $extension->plugin);
1117
- //$this->logger->debug($extension);
1118
-
1119
- $plugin_data = false;
1120
- if (file_exists(WP_PLUGIN_DIR . '/' . $extension->plugin)) {
1121
- $plugin_data = get_plugin_data(WP_PLUGIN_DIR . '/' . $extension->plugin, false, false);
1122
- } else if (file_exists(WPMU_PLUGIN_DIR . '/' . $extension->plugin)) {
1123
- $plugin_data = get_plugin_data(WPMU_PLUGIN_DIR . '/' . $extension->plugin, false, false);
1124
- }
1125
-
1126
- if (!$plugin_data) {
1127
- //$this->logger->debug('Seems not installed');
1128
- continue;
1129
- }
1130
-
1131
- $plugin = new stdClass();
1132
- $plugin->id = $extension->id;
1133
- $plugin->slug = $extension->slug;
1134
- $plugin->plugin = $extension->plugin;
1135
- $plugin->new_version = $extension->version;
1136
- $plugin->url = $extension->url;
1137
- if (class_exists('NewsletterExtensions')) {
1138
- // NO filters here!
1139
- $plugin->package = NewsletterExtensions::$instance->get_package($extension->id, $license_key);
1140
- } else {
1141
- $plugin->package = '';
1142
- }
1143
- // [banners] => Array
1144
- // (
1145
- // [2x] => https://ps.w.org/wp-rss-aggregator/assets/banner-1544x500.png?rev=2040548
1146
- // [1x] => https://ps.w.org/wp-rss-aggregator/assets/banner-772x250.png?rev=2040548
1147
- // )
1148
- // [icons] => Array
1149
- // (
1150
- // [2x] => https://ps.w.org/advanced-custom-fields/assets/icon-256x256.png?rev=1082746
1151
- // [1x] => https://ps.w.org/advanced-custom-fields/assets/icon-128x128.png?rev=1082746
1152
- // )
1153
- if (version_compare($extension->version, $plugin_data['Version']) > 0) {
1154
- //$this->logger->debug('There is a new version');
1155
- $extra_response[$extension->plugin] = $plugin;
1156
- } else {
1157
- // Maybe useless...
1158
- //$this->logger->debug('There is NOT a new version');
1159
- $value->no_update[$extension->plugin] = $plugin;
1160
- }
1161
- //$this->logger->debug('Added');
1162
- }
1163
-
1164
- $value->response = array_merge($value->response, $extra_response);
1165
-
1166
- return $value;
1167
- }
1168
-
1169
- /**
1170
- * @deprecated since version 6.1.9
1171
- */
1172
- function get_extension_version($extension_id) {
1173
- $extensions = $this->getTnpExtensions();
1174
-
1175
- if (!is_array($extensions)) {
1176
- return null;
1177
- }
1178
- foreach ($extensions as $extension) {
1179
- if ($extension->id == $extension_id) {
1180
- return $extension->version;
1181
- }
1182
- }
1183
-
1184
- return null;
1185
- }
1186
-
1187
- /**
1188
- * MUST be kept for old addons.
1189
- *
1190
- * @deprecated since version 6.1.9
1191
- */
1192
- function set_extension_update_data($value, $extension) {
1193
- return $value;
1194
- }
1195
-
1196
- /**
1197
- * Retrieve the extensions form the tnp site
1198
- * @return array
1199
- */
1200
- function getTnpExtensions() {
1201
-
1202
- $extensions_json = get_transient('tnp_extensions_json');
1203
-
1204
- if (empty($extensions_json)) {
1205
- $url = "http://www.thenewsletterplugin.com/wp-content/extensions.json?ver=" . NEWSLETTER_VERSION;
1206
- $extensions_response = wp_remote_get($url);
1207
-
1208
- if (is_wp_error($extensions_response)) {
1209
- // Cache anyway for blogs which cannot connect outside
1210
- $extensions_json = '[]';
1211
- set_transient('tnp_extensions_json', $extensions_json, 72 * 60 * 60);
1212
- $this->logger->error($extensions_response);
1213
- } else {
1214
-
1215
- $extensions_json = wp_remote_retrieve_body($extensions_response);
1216
-
1217
- // Not clear cases
1218
- if (empty($extensions_json) || !json_decode($extensions_json)) {
1219
- $this->logger->error('Invalid json from thenewsletterplugin.com: retrying in 72 hours');
1220
- $this->logger->error('JSON: ' . $extensions_json);
1221
- $extensions_json = '[]';
1222
- }
1223
- set_transient('tnp_extensions_json', $extensions_json, 72 * 60 * 60);
1224
- }
1225
- }
1226
-
1227
- $extensions = json_decode($extensions_json);
1228
-
1229
- return $extensions;
1230
- }
1231
-
1232
- function clear_extensions_cache() {
1233
- delete_transient('tnp_extensions_json');
1234
- }
1235
-
1236
-
1237
-
1238
- var $panels = array();
1239
-
1240
- function add_panel($key, $panel) {
1241
- if (!isset($this->panels[$key]))
1242
- $this->panels[$key] = array();
1243
- if (!isset($panel['id']))
1244
- $panel['id'] = sanitize_key($panel['label']);
1245
- $this->panels[$key][] = $panel;
1246
- }
1247
-
1248
- function has_license() {
1249
- return !empty($this->options['contract_key']);
1250
- }
1251
-
1252
- function get_sender_name() {
1253
- return $this->options['sender_name'];
1254
- }
1255
-
1256
- function get_sender_email() {
1257
- return $this->options['sender_email'];
1258
- }
1259
-
1260
- /**
1261
- *
1262
- * @return int
1263
- */
1264
- function get_newsletter_page_id() {
1265
- return (int) $this->options['page'];
1266
- }
1267
-
1268
- /**
1269
- *
1270
- * @return WP_Post
1271
- */
1272
- function get_newsletter_page() {
1273
- return get_post($this->get_newsletter_page_id());
1274
- }
1275
-
1276
- /**
1277
- * Returns the Newsletter dedicated page URL or an alternative URL if that page if not
1278
- * configured or not available.
1279
- *
1280
- * @staticvar string $url
1281
- * @return string
1282
- */
1283
- function get_newsletter_page_url($language = '') {
1284
-
1285
- $page = $this->get_newsletter_page();
1286
-
1287
- if (!$page || $page->post_status !== 'publish') {
1288
- return $this->build_action_url('m');
1289
- }
1290
-
1291
- $newsletter_page_url = get_permalink($page->ID);
1292
- if ($language && $newsletter_page_url) {
1293
- if (class_exists('SitePress')) {
1294
- $newsletter_page_url = apply_filters('wpml_permalink', $newsletter_page_url, $language, true);
1295
- }
1296
- if (function_exists('pll_get_post')) {
1297
- $translated_page = get_permalink(pll_get_post($page->ID, $language));
1298
- if ($translated_page) {
1299
- $newsletter_page_url = $translated_page;
1300
- }
1301
- }
1302
- }
1303
-
1304
- return $newsletter_page_url;
1305
- }
1306
-
1307
- function get_license_key() {
1308
- if (defined('NEWSLETTER_LICENSE_KEY')) {
1309
- return NEWSLETTER_LICENSE_KEY;
1310
- } else {
1311
- if (!empty($this->options['contract_key'])) {
1312
- return trim($this->options['contract_key']);
1313
- }
1314
- }
1315
- return false;
1316
- }
1317
-
1318
- /**
1319
- * Get the data connected to the specified license code on man settings.
1320
- *
1321
- * - false if no license is present
1322
- * - WP_Error if something went wrong if getting the license data
1323
- * - object with expiration and addons list
1324
- *
1325
- * @param boolean $refresh
1326
- * @return \WP_Error|boolean|object
1327
- */
1328
- function get_license_data($refresh = false) {
1329
-
1330
- $this->logger->debug('Getting license data');
1331
-
1332
- $license_key = $this->get_license_key();
1333
- if (empty($license_key)) {
1334
- $this->logger->debug('License was empty');
1335
- delete_transient('newsletter_license_data');
1336
- return false;
1337
- }
1338
-
1339
- if (!$refresh) {
1340
- $license_data = get_transient('newsletter_license_data');
1341
- if ($license_data !== false && is_object($license_data)) {
1342
- $this->logger->debug('License data found on cache');
1343
- return $license_data;
1344
- }
1345
- }
1346
-
1347
- $this->logger->debug('Refreshing the license data');
1348
-
1349
- $license_data_url = 'https://www.thenewsletterplugin.com/wp-content/plugins/file-commerce-pro/get-license-data.php';
1350
-
1351
- $response = wp_remote_post($license_data_url, array(
1352
- 'body' => array('k' => $license_key)
1353
- ));
1354
-
1355
- // Fall back to http...
1356
- if (is_wp_error($response)) {
1357
- $this->logger->error($response);
1358
- $this->logger->error('Falling back to http');
1359
- $license_data_url = str_replace('https', 'http', $license_data_url);
1360
- $response = wp_remote_post($license_data_url, array(
1361
- 'body' => array('k' => $license_key)
1362
- ));
1363
- if (is_wp_error($response)) {
1364
- $this->logger->error($response);
1365
- set_transient('newsletter_license_data', $response, DAY_IN_SECONDS);
1366
- return $response;
1367
- }
1368
- }
1369
-
1370
- $download_message = 'You can download all addons from www.thenewsletterplugin.com if your license is valid.';
1371
-
1372
- if (wp_remote_retrieve_response_code($response) != '200') {
1373
- $this->logger->error('license data error: ' . wp_remote_retrieve_response_code($response));
1374
- $data = new WP_Error(wp_remote_retrieve_response_code($response), 'License validation service error. <br>' . $download_message);
1375
- set_transient('newsletter_license_data', $data, DAY_IN_SECONDS);
1376
- return $data;
1377
- }
1378
-
1379
- $json = wp_remote_retrieve_body($response);
1380
- $data = json_decode($json);
1381
-
1382
- if (!is_object($data)) {
1383
- $this->logger->error($json);
1384
- $data = new WP_Error(1, 'License validation service error. <br>' . $download_message);
1385
- set_transient('newsletter_license_data', $data, DAY_IN_SECONDS);
1386
- return $data;
1387
- }
1388
-
1389
- if (isset($data->message)) {
1390
- $data = new WP_Error(1, $data->message . ' (check the license on Newsletter main settings)');
1391
- set_transient('newsletter_license_data', $data, DAY_IN_SECONDS);
1392
- return $data;
1393
- }
1394
-
1395
- $expiration = WEEK_IN_SECONDS;
1396
- // If the license expires in few days, make the transient live only few days, so it will be refreshed
1397
- if ($data->expire > time() && $data->expire - time() < WEEK_IN_SECONDS) {
1398
- $expiration = $data->expire - time();
1399
- }
1400
- set_transient('newsletter_license_data', $data, $expiration);
1401
-
1402
- return $data;
1403
- }
1404
-
1405
- /**
1406
- * @deprecated
1407
- * @param type $license_key
1408
- * @return \WP_Error
1409
- */
1410
- public static function check_license($license_key) {
1411
- $response = wp_remote_get('http://www.thenewsletterplugin.com/wp-content/plugins/file-commerce-pro/check.php?k=' . urlencode($license_key), array('sslverify' => false));
1412
- if (is_wp_error($response)) {
1413
- /* @var $response WP_Error */
1414
- 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>'
1415
- . esc_html($response->get_error_code()) . ' - ' . esc_html($response->get_error_message()));
1416
- } else if ($response['response']['code'] != 200) {
1417
- 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.'
1418
- . '<br>You can anyway download the professional extension from https://www.thenewsletterplugin.com.');
1419
- } elseif ($expires = json_decode(wp_remote_retrieve_body($response))) {
1420
- return array('expires' => $expires->expire, 'message' => 'Your license is valid and expires on ' . esc_html(date('Y-m-d', $expires->expire)));
1421
- } else {
1422
- 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>');
1423
- }
1424
- }
1425
-
1426
- function add_notice_to_chosen_profile_page_hook($post_states, $post) {
1427
-
1428
- if ($post->ID == $this->options['page']) {
1429
- $post_states[] = __('Newsletter plugin page, do not delete', 'newsletter');
1430
- }
1431
-
1432
- return $post_states;
1433
- }
1434
-
1435
- }
1436
-
1437
- $newsletter = Newsletter::instance();
1438
-
1439
- if (is_admin()) {
1440
- require_once NEWSLETTER_INCLUDES_DIR . '/system.php';
1441
- }
1442
-
1443
- require_once NEWSLETTER_DIR . '/subscription/subscription.php';
1444
- require_once NEWSLETTER_DIR . '/unsubscription/unsubscription.php';
1445
- require_once NEWSLETTER_DIR . '/profile/profile.php';
1446
- require_once NEWSLETTER_DIR . '/emails/emails.php';
1447
- require_once NEWSLETTER_DIR . '/users/users.php';
1448
- require_once NEWSLETTER_DIR . '/statistics/statistics.php';
1449
- require_once NEWSLETTER_DIR . '/widget/standard.php';
1450
- 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.3.1
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-2021 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.3.1');
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
+ delete_transient("tnp_extensions_json");
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
+ $r = $this->send($email);
536
+ $this->logger->debug(__METHOD__ . '> End newsletter ' . $email->id);
537
+ if (!$r) {
538
+ $this->logger->debug(__METHOD__ . '> Engine returned false, there is no more capacity');
539
+ break;
540
+ }
541
+ }
542
+ // Remove the semaphore so the delivery engine can be activated again
543
+ $this->delete_transient('engine');
544
+
545
+ $this->logger->debug(__METHOD__ . '> End');
546
+ }
547
+
548
+ function get_send_speed($email = null) {
549
+ $this->logger->debug(__METHOD__ . '> Computing delivery speed');
550
+ $mailer = $this->get_mailer();
551
+ $speed = (int) $mailer->get_speed();
552
+ if (!$speed) {
553
+ $this->logger->debug(__METHOD__ . '> Speed not set by mailer, use the default');
554
+ $speed = (int) $this->options['scheduler_max'];
555
+ } else {
556
+ $this->logger->debug(__METHOD__ . '> Speed set by mailer');
557
+ }
558
+
559
+ //$speed = (int) apply_filters('newsletter_send_speed', $speed, $email);
560
+ // Fail safe setting
561
+ $runs_per_hour = $this->get_runs_per_hour();
562
+ if (!$speed || $speed < $runs_per_hour) {
563
+ return $runs_per_hour;
564
+ }
565
+
566
+ $this->logger->debug(__METHOD__ . '> Speed: ' . $speed);
567
+ return $speed;
568
+ }
569
+
570
+ function get_send_delay() {
571
+ return 0;
572
+ }
573
+
574
+ function skip_this_run($email = null) {
575
+ return (boolean) apply_filters('newsletter_send_skip', false, $email);
576
+ }
577
+
578
+ function get_runs_per_hour() {
579
+ return (int) (3600 / NEWSLETTER_CRON_INTERVAL);
580
+ }
581
+
582
+ function get_emails_per_run() {
583
+ $speed = $this->get_send_speed();
584
+ $max = (int)($speed / $this->get_runs_per_hour());
585
+
586
+ return $max;
587
+ }
588
+
589
+ function get_max_emails($email) {
590
+ // Obsolete, here from Speed Control Addon
591
+ $max = (int) apply_filters('newsletter_send_max_emails', $this->max_emails, $email);
592
+
593
+ return min($max, $this->max_emails);
594
+ }
595
+
596
+ function fix_email($email) {
597
+ if (empty($email->query)) {
598
+ $email->query = "select * from " . NEWSLETTER_USERS_TABLE . " where status='C'";
599
+ }
600
+ if (empty($email->id)) {
601
+ $email->id = 0;
602
+ }
603
+ }
604
+
605
+ function send_setup() {
606
+ $this->logger->debug(__METHOD__ . '> Setup delivery engine');
607
+ if (is_null($this->max_emails)) {
608
+ $this->max_emails = $this->get_emails_per_run();
609
+ $this->logger->debug(__METHOD__ . '> Max emails: ' . $this->max_emails);
610
+ ignore_user_abort(true);
611
+
612
+ @set_time_limit(NEWSLETTER_CRON_INTERVAL + 30);
613
+
614
+ $max_time = (int) (@ini_get('max_execution_time') * 0.95);
615
+ if ($max_time == 0 || $max_time > NEWSLETTER_CRON_INTERVAL) {
616
+ $max_time = (int) (NEWSLETTER_CRON_INTERVAL * 0.95);
617
+ }
618
+
619
+ $this->time_limit = $this->time_start + $max_time;
620
+
621
+ $this->logger->debug(__METHOD__ . '> Max time set to ' . $max_time);
622
+ } else {
623
+ $this->logger->debug(__METHOD__ . '> Already setup');
624
+ }
625
+ }
626
+
627
+ function time_exceeded() {
628
+ if ($this->time_limit && time() > $this->time_limit) {
629
+ $this->logger->info(__METHOD__ . '> Max execution time limit reached');
630
+ return true;
631
+ }
632
+ }
633
+
634
+ /**
635
+ * Sends an email to targeted users or to given users. If a list of users is given (usually a list of test users)
636
+ * the query inside the email to retrieve users is not used.
637
+ *
638
+ * @global wpdb $wpdb
639
+ * @global type $newsletter_feed
640
+ * @param TNP_Email $email
641
+ * @param array $users
642
+ * @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.
643
+ */
644
+ function send($email, $users = null, $test = false) {
645
+ global $wpdb;
646
+
647
+ $this->logger->info(__METHOD__ . '> Send email ' . $email->id);
648
+
649
+ $this->send_setup();
650
+
651
+ if ($this->max_emails <= 0) {
652
+ $this->logger->info(__METHOD__ . '> No more capacity');
653
+ return false;
654
+ }
655
+
656
+ if (is_array($email)) {
657
+ $email = (object) $email;
658
+ }
659
+
660
+ $this->fix_email($email);
661
+
662
+ // This stops the update of last_id and sent fields since
663
+ // it's not a scheduled delivery but a test or something else (like an autoresponder)
664
+ $supplied_users = $users != null;
665
+
666
+ if (!$supplied_users) {
667
+
668
+ if ($this->skip_this_run($email)) {
669
+ return true;
670
+ }
671
+
672
+ // Speed change for specific email by Speed Control Addon
673
+ $max_emails = $this->get_max_emails($email);
674
+ if ($max_emails <= 0) {
675
+ return true;
676
+ }
677
+
678
+ $query = $email->query;
679
+ $query .= " and id>" . $email->last_id . " order by id limit " . $max_emails;
680
+
681
+ $this->logger->debug(__METHOD__ . '> Query: ' . $query);
682
+
683
+ //Retrieve subscribers
684
+ $users = $this->get_results($query);
685
+
686
+ $this->logger->debug(__METHOD__ . '> Loaded subscribers: ' . count($users));
687
+
688
+ // If there was a database error, return error
689
+ if ($users === false) {
690
+ return new WP_Error('1', 'Unable to query subscribers, check the logs');
691
+ }
692
+
693
+ if (empty($users)) {
694
+ $this->logger->info(__METHOD__ . '> No more users, set as sent');
695
+ $wpdb->query("update " . NEWSLETTER_EMAILS_TABLE . " set status='sent', total=sent where id=" . $email->id . " limit 1");
696
+ do_action('newsletter_ended_sending_newsletter', $email);
697
+ return true;
698
+ }
699
+ } else {
700
+ $this->logger->info(__METHOD__ . '> Subscribers supplied');
701
+ }
702
+
703
+ $start_time = microtime(true);
704
+ $count = 0;
705
+ $result = true;
706
+
707
+ $mailer = $this->get_mailer();
708
+
709
+ $batch_size = $mailer->get_batch_size();
710
+
711
+ $this->logger->debug(__METHOD__ . '> Batch size: ' . $batch_size);
712
+
713
+ // For batch size == 1 (normal condition) we optimize
714
+ if ($batch_size == 1) {
715
+
716
+ foreach ($users as $user) {
717
+
718
+ $this->logger->debug(__METHOD__ . '> Processing user ID: ' . $user->id);
719
+ $user = apply_filters('newsletter_send_user', $user);
720
+ $message = $this->build_message($email, $user);
721
+
722
+ // 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
723
+ $this->save_sent_message($message);
724
+
725
+ //Se non è un test incremento il contatore delle email spedite. Perchè incremento prima di spedire??
726
+ if (!$test) {
727
+ $wpdb->query("update " . NEWSLETTER_EMAILS_TABLE . " set sent=sent+1, last_id=" . $user->id . " where id=" . $email->id . " limit 1");
728
+ }
729
+
730
+ $r = $mailer->send($message);
731
+
732
+ if (!empty($message->error)) {
733
+ $this->logger->error($message);
734
+ $this->save_sent_message($message);
735
+ }
736
+
737
+ if (is_wp_error($r)) {
738
+ $this->logger->error($r);
739
+
740
+ // For fatal error, the newsletter status i changed to error (and the delivery stopped)
741
+ if (!$test && $r->get_error_code() == NewsletterMailer::ERROR_FATAL) {
742
+ $this->set_error_state_of_email($email, $r->get_error_message());
743
+ }
744
+
745
+ return $r;
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
+
791
+ if (!$test && $r->get_error_code() == NewsletterMailer::ERROR_FATAL) {
792
+ $this->set_error_state_of_email($email, $r->get_error_message());
793
+ }
794
+
795
+ $this->logger->error($r);
796
+ return $r;
797
+ }
798
+
799
+ if (!$supplied_users && !$test && $this->time_exceeded()) {
800
+ $result = false;
801
+ break;
802
+ }
803
+ }
804
+ }
805
+
806
+ $end_time = microtime(true);
807
+
808
+ // Stats only for newsletter with enough emails in a batch (we exclude the Autoresponder since it send one email per call)
809
+ if (!$test && !$supplied_users && $count > 5) {
810
+ $this->update_send_stats($start_time, $end_time, $count, $result);
811
+ }
812
+
813
+ // Cached general statistics are reset
814
+ if (!$test) {
815
+ NewsletterStatistics::instance()->reset_stats_time($email->id);
816
+ }
817
+
818
+ $this->logger->info(__METHOD__ . '> End run for email ' . $email->id);
819
+
820
+ return $result;
821
+ }
822
+
823
+ function update_send_stats($start_time, $end_time, $count, $result) {
824
+ $send_calls = get_option('newsletter_diagnostic_send_calls', []);
825
+ $send_calls[] = [$start_time, $end_time, $count, $result];
826
+
827
+ if (count($send_calls) > 100) {
828
+ array_shift($send_calls);
829
+ }
830
+
831
+ update_option('newsletter_diagnostic_send_calls', $send_calls, false);
832
+ }
833
+
834
+ /**
835
+ * @param TNP_Email $email
836
+ */
837
+ private function set_error_state_of_email($email, $message = '') {
838
+ // Handle only message type at the moment
839
+ if ($email->type !== 'message') {
840
+ return;
841
+ }
842
+
843
+ do_action('newsletter_error_on_sending', $email, $message);
844
+
845
+ $edited_email = new TNP_Email();
846
+ $edited_email->id = $email->id;
847
+ $edited_email->status = TNP_Email::STATUS_ERROR;
848
+ $edited_email->options = $email->options;
849
+ $edited_email->options['error_message'] = $message;
850
+
851
+ $this->save_email($edited_email);
852
+ }
853
+
854
+ /**
855
+ *
856
+ * @param TNP_Email $email
857
+ * @param TNP_User $user
858
+ * @return \TNP_Mailer_Message
859
+ */
860
+ function build_message($email, $user) {
861
+
862
+ $message = new TNP_Mailer_Message();
863
+
864
+ $message->to = $user->email;
865
+
866
+ $message->headers = [];
867
+ $message->headers['Precedence'] = 'bulk';
868
+ $message->headers['X-Newsletter-Email-Id'] = $email->id;
869
+ $message->headers['X-Auto-Response-Suppress'] = 'OOF, AutoReply';
870
+ $message->headers = apply_filters('newsletter_message_headers', $message->headers, $email, $user);
871
+
872
+ $message->body = preg_replace('/data-json=".*?"/is', '', $email->message);
873
+ $message->body = preg_replace('/ +/s', ' ', $message->body);
874
+ $message->body = $this->replace($message->body, $user, $email);
875
+ if ($this->options['do_shortcodes']) {
876
+ $message->body = do_shortcode($message->body);
877
+ }
878
+ $message->body = apply_filters('newsletter_message_html', $message->body, $email, $user);
879
+
880
+ $message->body_text = $this->replace($email->message_text, $user, $email);
881
+ $message->body_text = apply_filters('newsletter_message_text', $message->body_text, $email, $user);
882
+
883
+ if ($email->track == 1) {
884
+ $message->body = $this->relink($message->body, $email->id, $user->id, $email->token);
885
+ }
886
+
887
+ $message->subject = $this->replace($email->subject, $user);
888
+ $message->subject = apply_filters('newsletter_message_subject', $message->subject, $email, $user);
889
+
890
+ if (!empty($email->options['sender_email'])) {
891
+ $message->from = $email->options['sender_email'];
892
+ } else {
893
+ $message->from = $this->options['sender_email'];
894
+ }
895
+
896
+ if (!empty($email->options['sender_name'])) {
897
+ $message->from_name = $email->options['sender_name'];
898
+ } else {
899
+ $message->from_name = $this->options['sender_name'];
900
+ }
901
+
902
+ $message->email_id = $email->id;
903
+ $message->user_id = $user->id;
904
+
905
+ return apply_filters('newsletter_message', $message, $email, $user);
906
+ }
907
+
908
+ /**
909
+ *
910
+ * @param TNP_Mailer_Message $message
911
+ * @param int $status
912
+ * @param string $error
913
+ */
914
+ function save_sent_message($message) {
915
+ global $wpdb;
916
+
917
+ if (!$message->user_id || !$message->email_id) {
918
+ return;
919
+ }
920
+ $status = empty($message->error) ? 0 : 1;
921
+
922
+ $error = mb_substr($message->error, 0, 250);
923
+
924
+ $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));
925
+ }
926
+
927
+ /**
928
+ * @deprecated since version 7.3.0
929
+ */
930
+ function limits_exceeded() {
931
+ return false;
932
+ }
933
+
934
+ /**
935
+ * @deprecated since version 6.0.0
936
+ */
937
+ function register_mail_method($callable) {
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
+ return get_post($this->get_newsletter_page_id());
1243
+ }
1244
+
1245
+ /**
1246
+ * Returns the Newsletter dedicated page URL or an alternative URL if that page if not
1247
+ * configured or not available.
1248
+ *
1249
+ * @staticvar string $url
1250
+ * @return string
1251
+ */
1252
+ function get_newsletter_page_url($language = '') {
1253
+
1254
+ $page = $this->get_newsletter_page();
1255
+
1256
+ if (!$page || $page->post_status !== 'publish') {
1257
+ return $this->build_action_url('m');
1258
+ }
1259
+
1260
+ $newsletter_page_url = get_permalink($page->ID);
1261
+ if ($language && $newsletter_page_url) {
1262
+ if (class_exists('SitePress')) {
1263
+ $newsletter_page_url = apply_filters('wpml_permalink', $newsletter_page_url, $language, true);
1264
+ }
1265
+ if (function_exists('pll_get_post')) {
1266
+ $translated_page = get_permalink(pll_get_post($page->ID, $language));
1267
+ if ($translated_page) {
1268
+ $newsletter_page_url = $translated_page;
1269
+ }
1270
+ }
1271
+ }
1272
+
1273
+ return $newsletter_page_url;
1274
+ }
1275
+
1276
+ function get_license_key() {
1277
+ if (defined('NEWSLETTER_LICENSE_KEY')) {
1278
+ return NEWSLETTER_LICENSE_KEY;
1279
+ } else {
1280
+ if (!empty($this->options['contract_key'])) {
1281
+ return trim($this->options['contract_key']);
1282
+ }
1283
+ }
1284
+ return false;
1285
+ }
1286
+
1287
+ /**
1288
+ * Get the data connected to the specified license code on man settings.
1289
+ *
1290
+ * - false if no license is present
1291
+ * - WP_Error if something went wrong if getting the license data
1292
+ * - object with expiration and addons list
1293
+ *
1294
+ * @param boolean $refresh
1295
+ * @return \WP_Error|boolean|object
1296
+ */
1297
+ function get_license_data($refresh = false) {
1298
+
1299
+ $this->logger->debug('Getting license data');
1300
+
1301
+ $license_key = $this->get_license_key();
1302
+ if (empty($license_key)) {
1303
+ $this->logger->debug('License was empty');
1304
+ delete_transient('newsletter_license_data');
1305
+ return false;
1306
+ }
1307
+
1308
+ if (!$refresh) {
1309
+ $license_data = get_transient('newsletter_license_data');
1310
+ if ($license_data !== false && is_object($license_data)) {
1311
+ $this->logger->debug('License data found on cache');
1312
+ return $license_data;
1313
+ }
1314
+ }
1315
+
1316
+ $this->logger->debug('Refreshing the license data');
1317
+
1318
+ $license_data_url = 'https://www.thenewsletterplugin.com/wp-content/plugins/file-commerce-pro/get-license-data.php';
1319
+
1320
+ $response = wp_remote_post($license_data_url, array(
1321
+ 'body' => array('k' => $license_key)
1322
+ ));
1323
+
1324
+ // Fall back to http...
1325
+ if (is_wp_error($response)) {
1326
+ $this->logger->error($response);
1327
+ $this->logger->error('Falling back to http');
1328
+ $license_data_url = str_replace('https', 'http', $license_data_url);
1329
+ $response = wp_remote_post($license_data_url, array(
1330
+ 'body' => array('k' => $license_key)
1331
+ ));
1332
+ if (is_wp_error($response)) {
1333
+ $this->logger->error($response);
1334
+ set_transient('newsletter_license_data', $response, DAY_IN_SECONDS);
1335
+ return $response;
1336
+ }
1337
+ }
1338
+
1339
+ $download_message = 'You can download all addons from www.thenewsletterplugin.com if your license is valid.';
1340
+
1341
+ if (wp_remote_retrieve_response_code($response) != '200') {
1342
+ $this->logger->error('license data error: ' . wp_remote_retrieve_response_code($response));
1343
+ $data = new WP_Error(wp_remote_retrieve_response_code($response), 'License validation service error. <br>' . $download_message);
1344
+ set_transient('newsletter_license_data', $data, DAY_IN_SECONDS);
1345
+ return $data;
1346
+ }
1347
+
1348
+ $json = wp_remote_retrieve_body($response);
1349
+ $data = json_decode($json);
1350
+
1351
+ if (!is_object($data)) {
1352
+ $this->logger->error($json);
1353
+ $data = new WP_Error(1, 'License validation service error. <br>' . $download_message);
1354
+ set_transient('newsletter_license_data', $data, DAY_IN_SECONDS);
1355
+ return $data;
1356
+ }
1357
+
1358
+ if (isset($data->message)) {
1359
+ $data = new WP_Error(1, $data->message . ' (check the license on Newsletter main settings)');
1360
+ set_transient('newsletter_license_data', $data, DAY_IN_SECONDS);
1361
+ return $data;
1362
+ }
1363
+
1364
+ $expiration = WEEK_IN_SECONDS;
1365
+ // If the license expires in few days, make the transient live only few days, so it will be refreshed
1366
+ if ($data->expire > time() && $data->expire - time() < WEEK_IN_SECONDS) {
1367
+ $expiration = $data->expire - time();
1368
+ }
1369
+ set_transient('newsletter_license_data', $data, $expiration);
1370
+
1371
+ return $data;
1372
+ }
1373
+
1374
+ /**
1375
+ * @deprecated
1376
+ * @param type $license_key
1377
+ * @return \WP_Error
1378
+ */
1379
+ public static function check_license($license_key) {
1380
+ $response = wp_remote_get('http://www.thenewsletterplugin.com/wp-content/plugins/file-commerce-pro/check.php?k=' . urlencode($license_key), array('sslverify' => false));
1381
+ if (is_wp_error($response)) {
1382
+ /* @var $response WP_Error */
1383
+ 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>'
1384
+ . esc_html($response->get_error_code()) . ' - ' . esc_html($response->get_error_message()));
1385
+ } else if ($response['response']['code'] != 200) {
1386
+ 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.'
1387
+ . '<br>You can anyway download the professional extension from https://www.thenewsletterplugin.com.');
1388
+ } elseif ($expires = json_decode(wp_remote_retrieve_body($response))) {
1389
+ return array('expires' => $expires->expire, 'message' => 'Your license is valid and expires on ' . esc_html(date('Y-m-d', $expires->expire)));
1390
+ } else {
1391
+ 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>');
1392
+ }
1393
+ }
1394
+
1395
+ function add_notice_to_chosen_profile_page_hook($post_states, $post) {
1396
+
1397
+ if ($post->ID == $this->options['page']) {
1398
+ $post_states[] = __('Newsletter plugin page, do not delete', 'newsletter');
1399
+ }
1400
+
1401
+ return $post_states;
1402
+ }
1403
+
1404
+ }
1405
+
1406
+ $newsletter = Newsletter::instance();
1407
+
1408
+ if (is_admin()) {
1409
+ require_once NEWSLETTER_DIR . '/system/system.php';
1410
+ }
1411
+
1412
+ require_once NEWSLETTER_DIR . '/subscription/subscription.php';
1413
+ require_once NEWSLETTER_DIR . '/unsubscription/unsubscription.php';
1414
+ require_once NEWSLETTER_DIR . '/profile/profile.php';
1415
+ require_once NEWSLETTER_DIR . '/emails/emails.php';
1416
+ require_once NEWSLETTER_DIR . '/users/users.php';
1417
+ require_once NEWSLETTER_DIR . '/statistics/statistics.php';
1418
+ require_once NEWSLETTER_DIR . '/widget/standard.php';
1419
+ require_once NEWSLETTER_DIR . '/widget/minimal.php';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
readme.txt CHANGED
@@ -1,341 +1,351 @@
1
- === Newsletter ===
2
- Tags: newsletter, email marketing, welcome email, signup forms, contact, lead generation, marketing automation
3
- Tested up to: 5.8.1
4
- Stable tag: 7.3.0
5
- Contributors: satollo,webagile,michael-travan
6
- License: GPLv2 or later
7
- License URI: https://www.gnu.org/licenses/gpl-2.0.html
8
-
9
- Add a real newsletter system to your blog. For free. With unlimited newsletters and subscribers.
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
-
67
- = Professional Addons =
68
-
69
- 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 : )
70
-
71
- * [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!
72
- * [Autoresponder](https://www.thenewsletterplugin.com/autoresponder) - creates email series to follow up your subscribers
73
- * [Extended Composer Blocks](https://www.thenewsletterplugin.com/composer) - adds new blocks to the drag & drop composer
74
- * [WooCommerce Integration](https://www.thenewsletterplugin.com/woocommerce) - subscribe customers to a mailing list and generate product newletters.
75
- * [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.
76
- * [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
77
- * [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.
78
- * [Contact Form 7 Integration](https://www.thenewsletterplugin.com/documentation/contact-form-7-extension) - integrate the subscription on Contact Form 7 forms
79
- * [Ninja Forms Integration](https://www.thenewsletterplugin.com/documentation/ninjaforms-extension) - integrate the subscription on Ninja Forms
80
- * [WP Forms Integration](https://www.thenewsletterplugin.com/documentation/wpforms-extension) - integrate the subscription on WP Forms
81
- * Events Manager and The Events Calendar (By Modern Tribe) integrations - easily add events to your newsletters
82
- * [Google Analytics](https://www.thenewsletterplugin.com/google-analytics) - track newsletter links with Google UTM tracking paramaters
83
- * [Subscribe on Comment](https://www.thenewsletterplugin.com/documentation/comments-extension) - adds the subscription option to your blog comment form
84
- * [Geolocation](https://www.thenewsletterplugin.com/documentation/geolocation-extension) - adds geolocation capability to target subscribers by location
85
-
86
- = GDPR =
87
-
88
- 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.
89
- 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.
90
- 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.
91
-
92
- = Support =
93
-
94
- 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).
95
-
96
- Premium Users with an active license have access to one-to-one support via our [ticketing system](https://www.thenewsletterplugin.com/support-ticket).
97
-
98
- = Follow Us =
99
-
100
- * **Our Official Website** - [https://www.thenewsletterplugin.com/](https://www.thenewsletterplugin.com/)
101
- * **LinkedIn** - [https://www.linkedin.com/company/the-newsletter-plugin](https://www.linkedin.com/company/the-newsletter-plugin)
102
- * **Our Facebook Page** - [https://www.facebook.com/thenewsletterplugin](https://www.facebook.com/thenewsletterplugin)
103
- * **Our Twitter Account** - [https://twitter.com/newsletterwp](https://twitter.com/newsletterwp)
104
-
105
- == Frequently Asked Questions ==
106
-
107
- See the [Newsletter Forum](https://www.thenewsletterplugin.com/forums) to ask for help.
108
-
109
- For documentation start from [Newsletter documentation](https://www.thenewsletterplugin.com/documentation).
110
-
111
- Thank you, The Newsletter Team
112
-
113
- == Screenshots ==
114
-
115
- 1. The responsive email Drag & Drop composer
116
- 2. The plugin dashboard
117
- 3. The Reports extension
118
-
119
- == Changelog ==
120
-
121
- = 7.3.0 =
122
-
123
- * Fixed header block layout with (logo only layout)
124
- * Check for conflicts on newsletter saving
125
- * Added the subscriber complained status (actually not managed automatically)
126
-
127
- = 7.2.9 =
128
-
129
- * Fixed generic action button confirmation popup not showing the message
130
- * [SECURITY] Pre parsing of IP addresses on security panel
131
- * [SECURITY] Comments allowed on IP address list on security panel
132
- * [COMPOSER] Fixed preset name/subject
133
- * Improved generated forms for accessibility
134
-
135
- = 7.2.8 =
136
-
137
- * Fixed the print_date() when no time is provided
138
- * Fixed date alignment on posts block
139
- * Folders reorganization
140
- * Social block with a new set of icons
141
- * Boosted performances of the lists management page (for big databases)
142
- * Added a new scheduler diagnostic panel
143
- * Seriously improved the cron statistics and diagnostic panel (check it out!)
144
- * Removed obsolete migration code from ancient versions
145
-
146
- = 7.2.7 =
147
-
148
- * Fixed JS error on composer sometimes preventing the correct initialization
149
-
150
- = 7.2.6 =
151
-
152
- * Fixed links on test emails sent to a free email address
153
- * Added attachment explanation
154
- * Added link to explain the use of the snippet
155
-
156
- = 7.2.5 =
157
-
158
- * Fixed subject not saved under specific circumstance
159
-
160
- = 7.2.4 =
161
-
162
- * Fixed the composer not starting for blog with SSL plugin but still HTTP configured on main WP settings
163
- * Changed labels on subscriber maintenance panel
164
- * Updated requirements for WP version
165
-
166
- = 7.2.3 =
167
-
168
- * [COMPOSER] Added approx. indicators of the subsject visibile part in Apple and Android clients (experimental)
169
- * [COMPOSER] New mobile version view directly while composing (experimental)
170
- * [COMPOSER] New test email to test subscribers or to specific email address
171
- * [COMPOSER] Fixed missing background when creating a new message from a preset
172
- * [COMPOSER] Added media selector to the CTA block
173
- * [COMPOSER] Added reference to the tags on HTML and Text blocks
174
- * [COMPOSER] Move the snippet (preheader) field near the subject
175
- * [COMPOSER] Footer block with three link options: unsubscribe, manage and view online
176
- * [COMPOSER] Improve font coherence between blocks (by default)
177
- * [ANTISPAM] Improved the antispam checks on subscription
178
- * [GENERAL] Removed obsolete folders and code
179
- * [NEWSLETTERS] Refactored subject ideas selector
180
- * [SUBSCRIPTION] Inverted extra profile fields and lists on standard subscription form
181
- * [GENERAL] IP address extracted checking proxy variables
182
- * [GENERAL] Improved sending stats collection and display for the delivery engine (not related to click/open stats)
183
-
184
- = 7.2.2 =
185
-
186
- * [COMPOSER] Posts block excerpt removed when set to 0-length
187
- * [GENERAL]Added special characters on test message
188
- * [GENERAL]Added more specific error for action calls on System/Status panel
189
- * [SUBSCRIPTION] Check for the _wp_amp_action_xhr_converted parameter by AMP plugin
190
-
191
- = 7.2.1 =
192
-
193
- * [GENERAL] Added more detailed admin logging
194
- * [NEWSLETTERS] Fixed scheduled date sometimes reset to 1/1/1970
195
-
196
- = 7.2.0 =
197
-
198
- * [PROFILE] Fixed activation email on profile change
199
- * [NEWSLETTERS] Extended year selection on newsletter scheduling
200
- * [DELIVERY] Breaking change: old enqueue() and flush() methods have been removed
201
- * [GENERAL] Fixed alert message on some buttons
202
- * [SUBSCRIPTION] Fixed error message on multiple subscriptions (when not allowed)
203
- * [GENERAL] Fixed erratic error log line on main log
204
-
205
- = 7.1.9 =
206
-
207
- * [GENERAL] Removed the encodign defatlt to Base 64 when not specified since it seems incompatible with some SMTP plugins
208
- * [GENERAL] Review some controls layout and behavior
209
- * [DEBUG] Improved logging on database errors
210
- * [GENERAL] Added TikTok, Discord and Twitch socials
211
- * [GENERAL] Fixed odd error reported related to the cron call statistics collection
212
- * [DEBUG] Added a delivery diagnostic panel
213
-
214
- = 7.1.8 =
215
-
216
- * [COMPOSER] Fixed alignment of single big image on Outlook Android
217
-
218
- = 7.1.7 =
219
-
220
- * [GENERAL] Fix of permalink onm email with multilanguage plugins
221
-
222
- = 7.1.6 =
223
-
224
- * [COMPOSER] Fixed one column big image Read more links
225
-
226
- = 7.1.5 =
227
-
228
- * [COMPOSER] Improve buttons on posts layout
229
- * [COMPOSER] Improved header layout and logo
230
- * [COMPOSER] Generally improved block spacing
231
-
232
- = 7.1.4 =
233
-
234
- * [COMPOSER] Fixed image block for Outlook
235
- * [GENERAL] Fix undefined values in Gutenberg block
236
-
237
- = 7.1.3 =
238
-
239
- * [COMPOSER] Improvements on blocks layout compatibility
240
- * [COMPOSER] Fixed preset global options saving
241
- * [COMPOSER] Content regeneration on preset selection
242
- * [GENERAL] Added to System menu the Site Health link, a not well known native page of WordPress with system information
243
- * [GENERAL] Added sending statistics reset button on status panel
244
-
245
- = 7.1.2 =
246
-
247
- * [ADDONS] Fixed the addons list
248
-
249
- = 7.1.1 =
250
-
251
- * [GENERAL] Improved profile related functions
252
- * [GENERAL] Fixed check on field names (thanks Peter P.)
253
- * [COMPOSER] Fix on preset selection
254
- * [GENERAL] Fix ajax subscription on error
255
-
256
- = 7.1.0 =
257
-
258
- * [COMPOSER] Added link to tags documentation to inject subscriber's data
259
- * [COMPOSER] Fixed layout of posts block for Outlook 365
260
- * [GENERAL] Improved caching of addons json (even on error)
261
- * [GENERAL] Status menu changed to System/Status and System/Logs
262
- * [API] Fixed the subscriber status management (the API Addon should be updated as well)
263
- * [GENERAL] Fixed management of fatal errors on sending
264
- * [GENERAL] Limited error string per message to 250 chars
265
- * [GENERAL] Fixed check on field names (thanks Peter P.)
266
-
267
-
268
- = 7.0.9 =
269
-
270
- * [CAPTCHA] Fixed button label translation
271
- * [DELIVERY] Mailing fatal error management with newsletter stop and reporting
272
- * [SMTP] Marked obsolete the internal SMTP and made available the free SMTP addon
273
- * [DELIVERY] Better management of delivery fatal errors with a new "error" status for newsletters
274
- * [GENERAL] New log files panel
275
- * [IMPORT] Old import panel replaced by the new (really better) import addon (file, copy and paste, bounced addresses import)
276
- * [NEWSLETTERS] It's now possible to specify the sender name and email per newsletter (thanks to Matthew S.)
277
-
278
- = 7.0.8 =
279
-
280
- * [SUBSCRIBERS] Changed action buttons
281
- * [GENERAL] Reorganization of CSS and removal of unused files
282
- * [DASHBOARD] New window open for links and fix of invalid URLs
283
- * [NEWSLETTERS] New action buttons
284
- * [GENERAL] Minor fixes and optimizations
285
- * [COMPOSER] Fixed rare size error on gif images
286
-
287
- = 7.0.7 =
288
-
289
- * [COMPOSER] Fixed a warning in some inline editable blocks
290
- * [NEWSLETTERS] Fixed style which made list labels badly readable
291
- * [NEWSLETTERS] Fixed style which made bullet lists white (not on delivered newsletters)
292
- * [GENERAL] Added some references to the not working scheduler warning
293
-
294
- = 7.0.6 =
295
-
296
- * [COMPOSER] CTA block not grabbing settings from the "old" blocks
297
- * [COMPOSER] CSS issue on button settings group
298
- * [COMPOSER] Posts block two columns layout (author and padding)
299
- * [IMPORT] Removed the old low-featured import (the free import addon has everything needed!)
300
- * [GENERAL] Compatibility check with WP 5.7
301
-
302
- = 7.0.5 =
303
-
304
- * [COMPOSER] Hero CTA button not working
305
- * Fixed to the hero block button
306
- * Added support for the SMTP addon
307
-
308
- = 7.0.4 =
309
-
310
- * [COMPOSER] Redesigned drag and drop composer
311
- * [COMPOSER] NEW! Save drag and drop composed newsletters as templates to reuse easily
312
- * Redesigned dashboard
313
- * Several bug and fixes
314
-
315
- = 7.0.3 =
316
-
317
- * Option to choose between unsubscription and profile link in the footer block
318
- * Direct image src URL for image block
319
- * New media selector for blocks
320
- * Minor fixes
321
- * Updated codemirror
322
- * Updated default theme
323
- * Fixed tag filter on posts block (when tags have parathesis or like)
324
- * Fixed composer visualization of bullet points
325
-
326
- = 7.0.2 =
327
-
328
- * Fixed media 2x resize
329
-
330
- = 7.0.1 =
331
-
332
- * Fixed enforced lists by language with Polylang
333
-
334
- = 7.0.0 =
335
-
336
- * Added multiple newsletter selection for deletion
337
- * Added text part on welcome and activation email
338
- * Added the attribute "show_form" to "newsletter" shortcode
339
- * Added filter on subscriber saving (from external systems) with wrong list field values
340
- * Added index.html on log folder
341
-
 
 
 
 
 
 
 
 
 
 
1
+ === Newsletter ===
2
+ Tags: newsletter, email marketing, welcome email, signup forms, contact, lead generation, marketing automation
3
+ Tested up to: 5.8.1
4
+ Stable tag: 7.3.1
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
+ Add a real newsletter system to your blog. For free. With unlimited newsletters and subscribers.
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
+
67
+ = Professional Addons =
68
+
69
+ 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 : )
70
+
71
+ * [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!
72
+ * [Autoresponder](https://www.thenewsletterplugin.com/autoresponder) - creates email series to follow up your subscribers
73
+ * [Extended Composer Blocks](https://www.thenewsletterplugin.com/composer) - adds new blocks to the drag & drop composer
74
+ * [WooCommerce Integration](https://www.thenewsletterplugin.com/woocommerce) - subscribe customers to a mailing list and generate product newletters.
75
+ * [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.
76
+ * [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
77
+ * [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.
78
+ * [Contact Form 7 Integration](https://www.thenewsletterplugin.com/documentation/contact-form-7-extension) - integrate the subscription on Contact Form 7 forms
79
+ * [Ninja Forms Integration](https://www.thenewsletterplugin.com/documentation/ninjaforms-extension) - integrate the subscription on Ninja Forms
80
+ * [WP Forms Integration](https://www.thenewsletterplugin.com/documentation/wpforms-extension) - integrate the subscription on WP Forms
81
+ * Events Manager and The Events Calendar (By Modern Tribe) integrations - easily add events to your newsletters
82
+ * [Google Analytics](https://www.thenewsletterplugin.com/google-analytics) - track newsletter links with Google UTM tracking paramaters
83
+ * [Subscribe on Comment](https://www.thenewsletterplugin.com/documentation/comments-extension) - adds the subscription option to your blog comment form
84
+ * [Geolocation](https://www.thenewsletterplugin.com/documentation/geolocation-extension) - adds geolocation capability to target subscribers by location
85
+
86
+ = GDPR =
87
+
88
+ 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.
89
+ 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.
90
+ 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.
91
+
92
+ = Support =
93
+
94
+ 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).
95
+
96
+ Premium Users with an active license have access to one-to-one support via our [ticketing system](https://www.thenewsletterplugin.com/support-ticket).
97
+
98
+ = Follow Us =
99
+
100
+ * **Our Official Website** - [https://www.thenewsletterplugin.com/](https://www.thenewsletterplugin.com/)
101
+ * **LinkedIn** - [https://www.linkedin.com/company/the-newsletter-plugin](https://www.linkedin.com/company/the-newsletter-plugin)
102
+ * **Our Facebook Page** - [https://www.facebook.com/thenewsletterplugin](https://www.facebook.com/thenewsletterplugin)
103
+ * **Our Twitter Account** - [https://twitter.com/newsletterwp](https://twitter.com/newsletterwp)
104
+
105
+ == Frequently Asked Questions ==
106
+
107
+ See the [Newsletter Forum](https://www.thenewsletterplugin.com/forums) to ask for help.
108
+
109
+ For documentation start from [Newsletter documentation](https://www.thenewsletterplugin.com/documentation).
110
+
111
+ Thank you, The Newsletter Team
112
+
113
+ == Screenshots ==
114
+
115
+ 1. The responsive email Drag & Drop composer
116
+ 2. The plugin dashboard
117
+ 3. The Reports extension
118
+
119
+ == Changelog ==
120
+
121
+ = 7.3.1 =
122
+
123
+ * Dropped old mailers support
124
+ * Improved sending process and limits checking
125
+ * Removed obsolete notifications
126
+ * Fixed the wrong report on single email delivery speed
127
+ * Added support for custom sending speed by addons
128
+ * Improved excerpt generation (but it still depends on plugins and themes...)
129
+ * Support for building button option on composer blocks
130
+
131
+ = 7.3.0 =
132
+
133
+ * Fixed header block layout with (logo only layout)
134
+ * Check for conflicts on newsletter saving
135
+ * Added the subscriber complained status (actually not managed automatically)
136
+
137
+ = 7.2.9 =
138
+
139
+ * Fixed generic action button confirmation popup not showing the message
140
+ * [SECURITY] Pre parsing of IP addresses on security panel
141
+ * [SECURITY] Comments allowed on IP address list on security panel
142
+ * [COMPOSER] Fixed preset name/subject
143
+ * Improved generated forms for accessibility
144
+
145
+ = 7.2.8 =
146
+
147
+ * Fixed the print_date() when no time is provided
148
+ * Fixed date alignment on posts block
149
+ * Folders reorganization
150
+ * Social block with a new set of icons
151
+ * Boosted performances of the lists management page (for big databases)
152
+ * Added a new scheduler diagnostic panel
153
+ * Seriously improved the cron statistics and diagnostic panel (check it out!)
154
+ * Removed obsolete migration code from ancient versions
155
+
156
+ = 7.2.7 =
157
+
158
+ * Fixed JS error on composer sometimes preventing the correct initialization
159
+
160
+ = 7.2.6 =
161
+
162
+ * Fixed links on test emails sent to a free email address
163
+ * Added attachment explanation
164
+ * Added link to explain the use of the snippet
165
+
166
+ = 7.2.5 =
167
+
168
+ * Fixed subject not saved under specific circumstance
169
+
170
+ = 7.2.4 =
171
+
172
+ * Fixed the composer not starting for blog with SSL plugin but still HTTP configured on main WP settings
173
+ * Changed labels on subscriber maintenance panel
174
+ * Updated requirements for WP version
175
+
176
+ = 7.2.3 =
177
+
178
+ * [COMPOSER] Added approx. indicators of the subsject visibile part in Apple and Android clients (experimental)
179
+ * [COMPOSER] New mobile version view directly while composing (experimental)
180
+ * [COMPOSER] New test email to test subscribers or to specific email address
181
+ * [COMPOSER] Fixed missing background when creating a new message from a preset
182
+ * [COMPOSER] Added media selector to the CTA block
183
+ * [COMPOSER] Added reference to the tags on HTML and Text blocks
184
+ * [COMPOSER] Move the snippet (preheader) field near the subject
185
+ * [COMPOSER] Footer block with three link options: unsubscribe, manage and view online
186
+ * [COMPOSER] Improve font coherence between blocks (by default)
187
+ * [ANTISPAM] Improved the antispam checks on subscription
188
+ * [GENERAL] Removed obsolete folders and code
189
+ * [NEWSLETTERS] Refactored subject ideas selector
190
+ * [SUBSCRIPTION] Inverted extra profile fields and lists on standard subscription form
191
+ * [GENERAL] IP address extracted checking proxy variables
192
+ * [GENERAL] Improved sending stats collection and display for the delivery engine (not related to click/open stats)
193
+
194
+ = 7.2.2 =
195
+
196
+ * [COMPOSER] Posts block excerpt removed when set to 0-length
197
+ * [GENERAL]Added special characters on test message
198
+ * [GENERAL]Added more specific error for action calls on System/Status panel
199
+ * [SUBSCRIPTION] Check for the _wp_amp_action_xhr_converted parameter by AMP plugin
200
+
201
+ = 7.2.1 =
202
+
203
+ * [GENERAL] Added more detailed admin logging
204
+ * [NEWSLETTERS] Fixed scheduled date sometimes reset to 1/1/1970
205
+
206
+ = 7.2.0 =
207
+
208
+ * [PROFILE] Fixed activation email on profile change
209
+ * [NEWSLETTERS] Extended year selection on newsletter scheduling
210
+ * [DELIVERY] Breaking change: old enqueue() and flush() methods have been removed
211
+ * [GENERAL] Fixed alert message on some buttons
212
+ * [SUBSCRIPTION] Fixed error message on multiple subscriptions (when not allowed)
213
+ * [GENERAL] Fixed erratic error log line on main log
214
+
215
+ = 7.1.9 =
216
+
217
+ * [GENERAL] Removed the encodign defatlt to Base 64 when not specified since it seems incompatible with some SMTP plugins
218
+ * [GENERAL] Review some controls layout and behavior
219
+ * [DEBUG] Improved logging on database errors
220
+ * [GENERAL] Added TikTok, Discord and Twitch socials
221
+ * [GENERAL] Fixed odd error reported related to the cron call statistics collection
222
+ * [DEBUG] Added a delivery diagnostic panel
223
+
224
+ = 7.1.8 =
225
+
226
+ * [COMPOSER] Fixed alignment of single big image on Outlook Android
227
+
228
+ = 7.1.7 =
229
+
230
+ * [GENERAL] Fix of permalink onm email with multilanguage plugins
231
+
232
+ = 7.1.6 =
233
+
234
+ * [COMPOSER] Fixed one column big image Read more links
235
+
236
+ = 7.1.5 =
237
+
238
+ * [COMPOSER] Improve buttons on posts layout
239
+ * [COMPOSER] Improved header layout and logo
240
+ * [COMPOSER] Generally improved block spacing
241
+
242
+ = 7.1.4 =
243
+
244
+ * [COMPOSER] Fixed image block for Outlook
245
+ * [GENERAL] Fix undefined values in Gutenberg block
246
+
247
+ = 7.1.3 =
248
+
249
+ * [COMPOSER] Improvements on blocks layout compatibility
250
+ * [COMPOSER] Fixed preset global options saving
251
+ * [COMPOSER] Content regeneration on preset selection
252
+ * [GENERAL] Added to System menu the Site Health link, a not well known native page of WordPress with system information
253
+ * [GENERAL] Added sending statistics reset button on status panel
254
+
255
+ = 7.1.2 =
256
+
257
+ * [ADDONS] Fixed the addons list
258
+
259
+ = 7.1.1 =
260
+
261
+ * [GENERAL] Improved profile related functions
262
+ * [GENERAL] Fixed check on field names (thanks Peter P.)
263
+ * [COMPOSER] Fix on preset selection
264
+ * [GENERAL] Fix ajax subscription on error
265
+
266
+ = 7.1.0 =
267
+
268
+ * [COMPOSER] Added link to tags documentation to inject subscriber's data
269
+ * [COMPOSER] Fixed layout of posts block for Outlook 365
270
+ * [GENERAL] Improved caching of addons json (even on error)
271
+ * [GENERAL] Status menu changed to System/Status and System/Logs
272
+ * [API] Fixed the subscriber status management (the API Addon should be updated as well)
273
+ * [GENERAL] Fixed management of fatal errors on sending
274
+ * [GENERAL] Limited error string per message to 250 chars
275
+ * [GENERAL] Fixed check on field names (thanks Peter P.)
276
+
277
+
278
+ = 7.0.9 =
279
+
280
+ * [CAPTCHA] Fixed button label translation
281
+ * [DELIVERY] Mailing fatal error management with newsletter stop and reporting
282
+ * [SMTP] Marked obsolete the internal SMTP and made available the free SMTP addon
283
+ * [DELIVERY] Better management of delivery fatal errors with a new "error" status for newsletters
284
+ * [GENERAL] New log files panel
285
+ * [IMPORT] Old import panel replaced by the new (really better) import addon (file, copy and paste, bounced addresses import)
286
+ * [NEWSLETTERS] It's now possible to specify the sender name and email per newsletter (thanks to Matthew S.)
287
+
288
+ = 7.0.8 =
289
+
290
+ * [SUBSCRIBERS] Changed action buttons
291
+ * [GENERAL] Reorganization of CSS and removal of unused files
292
+ * [DASHBOARD] New window open for links and fix of invalid URLs
293
+ * [NEWSLETTERS] New action buttons
294
+ * [GENERAL] Minor fixes and optimizations
295
+ * [COMPOSER] Fixed rare size error on gif images
296
+
297
+ = 7.0.7 =
298
+
299
+ * [COMPOSER] Fixed a warning in some inline editable blocks
300
+ * [NEWSLETTERS] Fixed style which made list labels badly readable
301
+ * [NEWSLETTERS] Fixed style which made bullet lists white (not on delivered newsletters)
302
+ * [GENERAL] Added some references to the not working scheduler warning
303
+
304
+ = 7.0.6 =
305
+
306
+ * [COMPOSER] CTA block not grabbing settings from the "old" blocks
307
+ * [COMPOSER] CSS issue on button settings group
308
+ * [COMPOSER] Posts block two columns layout (author and padding)
309
+ * [IMPORT] Removed the old low-featured import (the free import addon has everything needed!)
310
+ * [GENERAL] Compatibility check with WP 5.7
311
+
312
+ = 7.0.5 =
313
+
314
+ * [COMPOSER] Hero CTA button not working
315
+ * Fixed to the hero block button
316
+ * Added support for the SMTP addon
317
+
318
+ = 7.0.4 =
319
+
320
+ * [COMPOSER] Redesigned drag and drop composer
321
+ * [COMPOSER] NEW! Save drag and drop composed newsletters as templates to reuse easily
322
+ * Redesigned dashboard
323
+ * Several bug and fixes
324
+
325
+ = 7.0.3 =
326
+
327
+ * Option to choose between unsubscription and profile link in the footer block
328
+ * Direct image src URL for image block
329
+ * New media selector for blocks
330
+ * Minor fixes
331
+ * Updated codemirror
332
+ * Updated default theme
333
+ * Fixed tag filter on posts block (when tags have parathesis or like)
334
+ * Fixed composer visualization of bullet points
335
+
336
+ = 7.0.2 =
337
+
338
+ * Fixed media 2x resize
339
+
340
+ = 7.0.1 =
341
+
342
+ * Fixed enforced lists by language with Polylang
343
+
344
+ = 7.0.0 =
345
+
346
+ * Added multiple newsletter selection for deletion
347
+ * Added text part on welcome and activation email
348
+ * Added the attribute "show_form" to "newsletter" shortcode
349
+ * Added filter on subscriber saving (from external systems) with wrong list field values
350
+ * Added index.html on log folder
351
+
statistics/index.php CHANGED
@@ -1,226 +1,226 @@
1
- <?php
2
- /* @var $wpdb wpdb */
3
- /* @var $this NewsletterStatistics */
4
-
5
- defined('ABSPATH') || exit;
6
-
7
- require_once NEWSLETTER_INCLUDES_DIR . '/controls.php';
8
- $controls = new NewsletterControls();
9
-
10
- wp_enqueue_script('tnp-chart');
11
-
12
- // Optimized query with a reduced set of fields
13
- $emails = $wpdb->get_results("select send_on, id, subject, total, status, type from " . NEWSLETTER_EMAILS_TABLE . " where status='sent' and type='message' order by send_on desc limit 10");
14
-
15
- $report = new TNP_Statistics();
16
-
17
- $overview_labels = array();
18
- $overview_titles = array();
19
- $overview_open_rate = array();
20
- $overview_click_rate = array();
21
-
22
- $total_sent = 0;
23
- $open_count_total = 0;
24
- $click_count_total = 0;
25
- foreach ($emails as $email) {
26
- $data = $this->get_statistics($email);
27
-
28
- $entry = array();
29
-
30
- if (empty($data->total)) {
31
- continue;
32
- }
33
-
34
- // Used later for the tabled view
35
- $email->report = $data;
36
-
37
- $report->total += $data->total;
38
- $report->open_count += $data->open_count;
39
- $report->click_count += $data->click_count;
40
-
41
- $overview_labels[] = strftime('%a, %e %b %y', $email->send_on);
42
-
43
- $overview_open_rate[] = $data->open_rate;
44
- $overview_click_rate[] = $data->click_rate;
45
- $overview_titles[] = $email->subject;
46
- }
47
-
48
- $report->update();
49
-
50
- $overview_labels = array_reverse($overview_labels);
51
- $overview_open_rate = array_reverse($overview_open_rate);
52
- $overview_click_rate = array_reverse($overview_click_rate);
53
-
54
- if (empty($emails)) {
55
- $controls->warnings[] = __('No newsletters have been sent till now', 'newsletter');
56
- }
57
- ?>
58
-
59
- <script>
60
- var titles = <?php echo json_encode(array_reverse($overview_titles)) ?>;
61
- </script>
62
-
63
- <div class="wrap tnp-statistics tnp-statistics-index" id="tnp-wrap">
64
- <?php include NEWSLETTER_DIR . '/tnp-header.php' ?>
65
- <div id="tnp-heading">
66
- <h2><?php _e('Overall basic statistics (last 20 newsletters)', 'newsletter') ?></h2>
67
- <p>More details, including Automated and Autoresponder newsletter statistics are available with the <a href="https://www.thenewsletterplugin.com/reports?utm_source=reports&utm_campaign=plugin" target="_blank">Reports Addon</a>.</p>
68
- </div>
69
-
70
- <div id="tnp-body" class="tnp-statistics">
71
-
72
- <div class="row">
73
- <div class="col-md-3">
74
- <div class="tnp-widget tnp-number">
75
- <h3>Emails Sent</h3>
76
- <div class="tnp-icon"><i class="fas fa-users"></i></div>
77
- <div class="tnp-value"><?php echo number_format_i18n($report->total, 0) ?></div>
78
- </div>
79
- </div>
80
-
81
- <div class="col-md-3">
82
- <div class="tnp-widget tnp-number">
83
- <h3>Overall Opens</h3>
84
- <div class="tnp-icon tnp-blue"><i class="fas fa-envelope-open"></i></div>
85
- <div class="tnp-value"><?php echo $report->open_rate; ?>%</div>
86
- <div class="tnp-value-2">(<?php echo $report->open_count; ?>)</div>
87
- </div>
88
- </div>
89
-
90
- <div class="col-md-3">
91
- <div class="tnp-widget tnp-number">
92
- <h3>Overall Clicks</h3>
93
- <div class="tnp-icon tnp-orange"><i class="fas fa-mouse-pointer"></i></div>
94
- <div class="tnp-value"><?php echo $report->click_rate; ?>%</div>
95
- <div class="tnp-value-2">(<?php echo $report->click_count; ?>)</div>
96
- </div>
97
- </div>
98
-
99
- <div class="col-md-3">
100
- <div class="tnp-widget tnp-number tnp-inactive">
101
- <h3>Overall Reactivity</h3>
102
- <div class="tnp-icon tnp-gray"><i class="fas fa-star"></i></div>
103
- <div class="tnp-value">-%</div>
104
- </div>
105
- </div>
106
- </div>
107
-
108
- <div class="row">
109
-
110
- <div class="col-md-6">
111
- <div class="tnp-widget">
112
- <h3>Open rate</h3>
113
-
114
- <div id="tnp-opens-chart">
115
- <canvas id="tnp-opens-chart-canvas"></canvas>
116
- </div>
117
-
118
- <script type="text/javascript">
119
- var open_config = {
120
- type: 'line',
121
- data: {
122
- labels: <?php echo json_encode($overview_labels) ?>,
123
- datasets: [
124
- {
125
- label: "Open",
126
- fill: false,
127
- strokeColor: "#2980b9",
128
- backgroundColor: "#2980b9",
129
- borderColor: "#2980b9",
130
- //pointBorderColor: "#27AE60",
131
- pointBackgroundColor: "#2980b9",
132
- data: <?php echo json_encode($overview_open_rate) ?>
133
- }
134
- ]
135
- },
136
- options: {
137
- scales: {
138
- xAxes: [{type: "category", "id": "x-axis-1", gridLines: {display: false}, ticks: {fontFamily: "Source Sans Pro"}}],
139
- yAxes: [
140
- {type: "linear", "id": "y-axis-1", gridLines: {display: false}, ticks: {fontColor: "#333", fontFamily: "Source Sans Pro"}}
141
- ]
142
- },
143
- tooltips: {
144
- callbacks: {
145
- afterTitle: function (data) {
146
- return titles[data[0].index];
147
- },
148
- label: function (tooltipItem, data) {
149
- return data.datasets[0].label + ": " + data.datasets[0].data[tooltipItem.index] + "%";
150
-
151
- }
152
- }
153
- }
154
- }
155
- };
156
-
157
- jQuery(document).ready(function ($) {
158
- eventsLineChart = new Chart("tnp-opens-chart-canvas", open_config);
159
- });
160
- </script>
161
-
162
- </div>
163
- </div>
164
-
165
- <div class="col-md-6">
166
- <div class="tnp-widget">
167
- <h3>Click rate</h3>
168
-
169
-
170
- <div id="tnp-clicks-chart">
171
- <canvas id="tnp-clicks-chart-canvas"></canvas>
172
- </div>
173
-
174
- <script type="text/javascript">
175
- var click_config = {
176
- type: 'line',
177
- data: {
178
- labels: <?php echo json_encode($overview_labels) ?>,
179
- datasets: [
180
-
181
- {
182
- label: "Click",
183
- fill: false,
184
- strokeColor: "#2980b9",
185
- backgroundColor: "#2980b9",
186
- borderColor: "#2980b9",
187
- pointBorderColor: "#2980b9",
188
- pointBackgroundColor: "#2980b9",
189
- data: <?php echo json_encode($overview_click_rate) ?>,
190
- }
191
- ]
192
- },
193
- options: {
194
- scales: {
195
- xAxes: [{type: "category", "id": "x-axis-1", gridLines: {display: false}, ticks: {fontFamily: "Source Sans Pro"}}],
196
- yAxes: [
197
- {type: "linear", "id": "y-axis-1", gridLines: {display: false}, ticks: {fontColor: "#333", fontFamily: "Source Sans Pro"}}
198
- ]
199
- },
200
- tooltips: {
201
- callbacks: {
202
- afterTitle: function (data) {
203
- return titles[data[0].index];
204
- },
205
- label: function (tooltipItem, data) {
206
- return data.datasets[0].label + ": " + data.datasets[0].data[tooltipItem.index] + "%";
207
- }
208
- }
209
- }
210
- }
211
- };
212
-
213
- jQuery(document).ready(function ($) {
214
- eventsLineChart = new Chart("tnp-clicks-chart-canvas", click_config);
215
- });
216
- </script>
217
-
218
-
219
- </div>
220
-
221
- </div>
222
- </div>
223
-
224
- </div>
225
- <?php include NEWSLETTER_DIR . '/tnp-footer.php' ?>
226
- </div>
1
+ <?php
2
+ /* @var $wpdb wpdb */
3
+ /* @var $this NewsletterStatistics */
4
+
5
+ defined('ABSPATH') || exit;
6
+
7
+ require_once NEWSLETTER_INCLUDES_DIR . '/controls.php';
8
+ $controls = new NewsletterControls();
9
+
10
+ wp_enqueue_script('tnp-chart');
11
+
12
+ // Optimized query with a reduced set of fields
13
+ $emails = $wpdb->get_results("select send_on, id, subject, total, status, type from " . NEWSLETTER_EMAILS_TABLE . " where status='sent' and type='message' order by send_on desc limit 10");
14
+
15
+ $report = new TNP_Statistics();
16
+
17
+ $overview_labels = array();
18
+ $overview_titles = array();
19
+ $overview_open_rate = array();
20
+ $overview_click_rate = array();
21
+
22
+ $total_sent = 0;
23
+ $open_count_total = 0;
24
+ $click_count_total = 0;
25
+ foreach ($emails as $email) {
26
+ $data = $this->get_statistics($email);
27
+
28
+ $entry = array();
29
+
30
+ if (empty($data->total)) {
31
+ continue;
32
+ }
33
+
34
+ // Used later for the tabled view
35
+ $email->report = $data;
36
+
37
+ $report->total += $data->total;
38
+ $report->open_count += $data->open_count;
39
+ $report->click_count += $data->click_count;
40
+
41
+ $overview_labels[] = strftime('%a, %e %b %y', $email->send_on);
42
+
43
+ $overview_open_rate[] = $data->open_rate;
44
+ $overview_click_rate[] = $data->click_rate;
45
+ $overview_titles[] = $email->subject;
46
+ }
47
+
48
+ $report->update();
49
+
50
+ $overview_labels = array_reverse($overview_labels);
51
+ $overview_open_rate = array_reverse($overview_open_rate);
52
+ $overview_click_rate = array_reverse($overview_click_rate);
53
+
54
+ if (empty($emails)) {
55
+ $controls->warnings[] = __('No newsletters have been sent till now', 'newsletter');
56
+ }
57
+ ?>
58
+
59
+ <script>
60
+ var titles = <?php echo json_encode(array_reverse($overview_titles)) ?>;
61
+ </script>
62
+
63
+ <div class="wrap tnp-statistics tnp-statistics-index" id="tnp-wrap">
64
+ <?php include NEWSLETTER_DIR . '/tnp-header.php' ?>
65
+ <div id="tnp-heading">
66
+ <h2><?php _e('Overall basic statistics (last 20 newsletters)', 'newsletter') ?></h2>
67
+ <p>More details, including Automated and Autoresponder newsletter statistics are available with the <a href="https://www.thenewsletterplugin.com/reports?utm_source=reports&utm_campaign=plugin" target="_blank">Reports Addon</a>.</p>
68
+ </div>
69
+
70
+ <div id="tnp-body" class="tnp-statistics">
71
+
72
+ <div class="row">
73
+ <div class="col-md-3">
74
+ <div class="tnp-widget tnp-number">
75
+ <h3>Emails Sent</h3>
76
+ <div class="tnp-icon"><i class="fas fa-users"></i></div>
77
+ <div class="tnp-value"><?php echo number_format_i18n($report->total, 0) ?></div>
78
+ </div>
79
+ </div>
80
+
81
+ <div class="col-md-3">
82
+ <div class="tnp-widget tnp-number">
83
+ <h3>Overall Opens</h3>
84
+ <div class="tnp-icon tnp-blue"><i class="fas fa-envelope-open"></i></div>
85
+ <div class="tnp-value"><?php echo $report->open_rate; ?>%</div>
86
+ <div class="tnp-value-2">(<?php echo $report->open_count; ?>)</div>
87
+ </div>
88
+ </div>
89
+
90
+ <div class="col-md-3">
91
+ <div class="tnp-widget tnp-number">
92
+ <h3>Overall Clicks</h3>
93
+ <div class="tnp-icon tnp-orange"><i class="fas fa-mouse-pointer"></i></div>
94
+ <div class="tnp-value"><?php echo $report->click_rate; ?>%</div>
95
+ <div class="tnp-value-2">(<?php echo $report->click_count; ?>)</div>
96
+ </div>
97
+ </div>
98
+
99
+ <div class="col-md-3">
100
+ <div class="tnp-widget tnp-number tnp-inactive">
101
+ <h3>Overall Reactivity</h3>
102
+ <div class="tnp-icon tnp-gray"><i class="fas fa-star"></i></div>
103
+ <div class="tnp-value">-%</div>
104
+ </div>
105
+ </div>
106
+ </div>
107
+
108
+ <div class="row">
109
+
110
+ <div class="col-md-6">
111
+ <div class="tnp-widget">
112
+ <h3>Open rate</h3>
113
+
114
+ <div id="tnp-opens-chart">
115
+ <canvas id="tnp-opens-chart-canvas"></canvas>
116
+ </div>
117
+
118
+ <script type="text/javascript">
119
+ var open_config = {
120
+ type: 'line',
121
+ data: {
122
+ labels: <?php echo json_encode($overview_labels) ?>,
123
+ datasets: [
124
+ {
125
+ label: "Open",
126
+ fill: false,
127
+ strokeColor: "#2980b9",
128
+ backgroundColor: "#2980b9",
129
+ borderColor: "#2980b9",
130
+ //pointBorderColor: "#27AE60",
131
+ pointBackgroundColor: "#2980b9",
132
+ data: <?php echo json_encode($overview_open_rate) ?>
133
+ }
134
+ ]
135
+ },
136
+ options: {
137
+ scales: {
138
+ xAxes: [{type: "category", "id": "x-axis-1", gridLines: {display: false}, ticks: {fontFamily: "Source Sans Pro"}}],
139
+ yAxes: [
140
+ {type: "linear", "id": "y-axis-1", gridLines: {display: false}, ticks: {fontColor: "#333", fontFamily: "Source Sans Pro"}}
141
+ ]
142
+ },
143
+ tooltips: {
144
+ callbacks: {
145
+ afterTitle: function (data) {
146
+ return titles[data[0].index];
147
+ },
148
+ label: function (tooltipItem, data) {
149
+ return data.datasets[0].label + ": " + data.datasets[0].data[tooltipItem.index] + "%";
150
+
151
+ }
152
+ }
153
+ }
154
+ }
155
+ };
156
+
157
+ jQuery(document).ready(function ($) {
158
+ eventsLineChart = new Chart("tnp-opens-chart-canvas", open_config);
159
+ });
160
+ </script>
161
+
162
+ </div>
163
+ </div>
164
+
165
+ <div class="col-md-6">
166
+ <div class="tnp-widget">
167
+ <h3>Click rate</h3>
168
+
169
+
170
+ <div id="tnp-clicks-chart">
171
+ <canvas id="tnp-clicks-chart-canvas"></canvas>
172
+ </div>
173
+
174
+ <script type="text/javascript">
175
+ var click_config = {
176
+ type: 'line',
177
+ data: {
178
+ labels: <?php echo json_encode($overview_labels) ?>,
179
+ datasets: [
180
+
181
+ {
182
+ label: "Click",
183
+ fill: false,
184
+ strokeColor: "#2980b9",
185
+ backgroundColor: "#2980b9",
186
+ borderColor: "#2980b9",
187
+ pointBorderColor: "#2980b9",
188
+ pointBackgroundColor: "#2980b9",
189
+ data: <?php echo json_encode($overview_click_rate) ?>,
190
+ }
191
+ ]
192
+ },
193
+ options: {
194
+ scales: {
195
+ xAxes: [{type: "category", "id": "x-axis-1", gridLines: {display: false}, ticks: {fontFamily: "Source Sans Pro"}}],
196
+ yAxes: [
197
+ {type: "linear", "id": "y-axis-1", gridLines: {display: false}, ticks: {fontColor: "#333", fontFamily: "Source Sans Pro"}}
198
+ ]
199
+ },
200
+ tooltips: {
201
+ callbacks: {
202
+ afterTitle: function (data) {
203
+ return titles[data[0].index];
204
+ },
205
+ label: function (tooltipItem, data) {
206
+ return data.datasets[0].label + ": " + data.datasets[0].data[tooltipItem.index] + "%";
207
+ }
208
+ }
209
+ }
210
+ }
211
+ };
212
+
213
+ jQuery(document).ready(function ($) {
214
+ eventsLineChart = new Chart("tnp-clicks-chart-canvas", click_config);
215
+ });
216
+ </script>
217
+
218
+
219
+ </div>
220
+
221
+ </div>
222
+ </div>
223
+
224
+ </div>
225
+ <?php include NEWSLETTER_DIR . '/tnp-footer.php' ?>
226
+ </div>
statistics/view.php CHANGED
@@ -1,110 +1,110 @@
1
- <?php
2
- /* @var $this NewsletterStatistics */
3
-
4
- defined('ABSPATH') || exit;
5
-
6
- wp_enqueue_script('tnp-chart');
7
-
8
- require_once NEWSLETTER_INCLUDES_DIR . '/controls.php';
9
- $controls = new NewsletterControls();
10
-
11
- $email = $this->get_email($_GET['id']);
12
- $report = $this->get_statistics($email);
13
-
14
- if ($email->status == 'new') {
15
- $controls->warnings[] = __('Draft newsletter, no data available', 'newsletter');
16
- } else if ($email->status == 'sending') {
17
- $controls->warnings[] = __('Newsletter still sending', 'newsletter');
18
- }
19
-
20
- if (empty($email->track)) {
21
- $controls->warnings[] = __('This newsletter has the tracking disabled. No statistics will be available.', 'newsletter');
22
- }
23
-
24
- ?>
25
-
26
- <div class="wrap tnp-statistics tnp-statistics-view" id="tnp-wrap">
27
- <?php include NEWSLETTER_DIR . '/tnp-header.php' ?>
28
- <div id="tnp-heading">
29
- <h2><?php _e('Statistics of', 'newsletter') ?> "<?php echo htmlspecialchars($email->subject); ?>"</h2>
30
- <p>Retargeting and subscriber detailed list are available with <a href="https://www.thenewsletterplugin.com/reports" target="_blank">Reports Addon</a>.</p>
31
- </div>
32
-
33
- <div id="tnp-body" style="min-width: 500px">
34
-
35
- <div class="row">
36
- <div class="col-md-3">
37
- <div class="tnp-widget tnp-number">
38
- <h3><?php _e('Reach', 'newsletter') ?></h3>
39
- <div class="tnp-icon"><i class="fas fa-users"></i></div>
40
- <div class="tnp-value"><?php echo number_format_i18n($report->total, 0) ?></div>
41
- </div>
42
- </div>
43
-
44
- <div class="col-md-3">
45
- <div class="tnp-widget tnp-number">
46
- <h3><?php _e('Opens', 'newsletter') ?></h3>
47
- <div class="tnp-icon tnp-blue"><i class="fas fa-envelope-open"></i></div>
48
- <div class="tnp-value"><?php echo $report->open_rate; ?>%</div>
49
- <div class="tnp-value-2">(<?php echo $report->open_count; ?>)</div>
50
- </div>
51
- </div>
52
-
53
- <div class="col-md-3">
54
- <div class="tnp-widget tnp-number">
55
- <h3><?php _e('Clicks', 'newsletter') ?></h3>
56
- <div class="tnp-icon tnp-orange"><i class="fas fa-mouse-pointer"></i></div>
57
- <div class="tnp-value"><?php echo $report->click_rate; ?>%</div>
58
- <div class="tnp-value-2">(<?php echo $report->click_count; ?>)</div>
59
- </div>
60
- </div>
61
-
62
- <div class="col-md-3">
63
- <div class="tnp-widget tnp-number tnp-inactive">
64
- <h3>Reactivity</h3>
65
- <div class="tnp-icon tnp-gray"><i class="fas fa-star"></i></div>
66
- <div class="tnp-value">-%</div>
67
- </div>
68
- </div>
69
- </div>
70
-
71
- <div class="row">
72
- <div class="col-md-4">
73
- <div class="tnp-widget tnp-inactive">
74
- <h3>Clicked URLs</h3>
75
- <div class="tnp-placeholder">
76
- <a href="https://www.thenewsletterplugin.com/premium?utm_source=reports&utm_medium=urls&utm_campaign=plugin" target="_blank">
77
- <img src="<?php echo plugins_url('newsletter') ?>/statistics/images/clicked-urls@2x.png">
78
- </a>
79
- </div>
80
- </div>
81
- </div>
82
-
83
- <div class="col-md-4">
84
- <div class="tnp-widget tnp-inactive">
85
- <h3>World Map</h3>
86
- <div class="tnp-placeholder">
87
- <a href="https://www.thenewsletterplugin.com/premium?utm_source=reports&utm_medium=map&utm_campaign=plugin" target="_blank">
88
- <img src="<?php echo plugins_url('newsletter') ?>/statistics/images/world-map@2x.png">
89
- </a>
90
- </div>
91
-
92
- </div>
93
- </div>
94
-
95
- <div class="col-md-4">
96
- <div class="tnp-widget tnp-inactive">
97
- <h3>Interactions</h3>
98
- <div class="tnp-placeholder">
99
- <a href="https://www.thenewsletterplugin.com/premium?utm_source=reports&utm_medium=interactions&utm_campaign=plugin" target="_blank">
100
- <img src="<?php echo plugins_url('newsletter') ?>/statistics/images/interactions@2x.png">
101
- </a>
102
- </div>
103
- </div>
104
- </div>
105
-
106
- </div>
107
-
108
- </div>
109
- <?php include NEWSLETTER_DIR . '/tnp-footer.php' ?>
110
- </div>
1
+ <?php
2
+ /* @var $this NewsletterStatistics */
3
+
4
+ defined('ABSPATH') || exit;
5
+
6
+ wp_enqueue_script('tnp-chart');
7
+
8
+ require_once NEWSLETTER_INCLUDES_DIR . '/controls.php';
9
+ $controls = new NewsletterControls();
10
+
11
+ $email = $this->get_email($_GET['id']);
12
+ $report = $this->get_statistics($email);
13
+
14
+ if ($email->status == 'new') {
15
+ $controls->warnings[] = __('Draft newsletter, no data available', 'newsletter');
16
+ } else if ($email->status == 'sending') {
17
+ $controls->warnings[] = __('Newsletter still sending', 'newsletter');
18
+ }
19
+
20
+ if (empty($email->track)) {
21
+ $controls->warnings[] = __('This newsletter has the tracking disabled. No statistics will be available.', 'newsletter');
22
+ }
23
+
24
+ ?>
25
+
26
+ <div class="wrap tnp-statistics tnp-statistics-view" id="tnp-wrap">
27
+ <?php include NEWSLETTER_DIR . '/tnp-header.php' ?>
28
+ <div id="tnp-heading">
29
+ <h2><?php _e('Statistics of', 'newsletter') ?> "<?php echo htmlspecialchars($email->subject); ?>"</h2>
30
+ <p>Retargeting and subscriber detailed list are available with <a href="https://www.thenewsletterplugin.com/reports" target="_blank">Reports Addon</a>.</p>
31
+ </div>
32
+
33
+ <div id="tnp-body" style="min-width: 500px">
34
+
35
+ <div class="row">
36
+ <div class="col-md-3">
37
+ <div class="tnp-widget tnp-number">
38
+ <h3><?php _e('Reach', 'newsletter') ?></h3>
39
+ <div class="tnp-icon"><i class="fas fa-users"></i></div>
40
+ <div class="tnp-value"><?php echo number_format_i18n($report->total, 0) ?></div>
41
+ </div>
42
+ </div>
43
+
44
+ <div class="col-md-3">
45
+ <div class="tnp-widget tnp-number">
46
+ <h3><?php _e('Opens', 'newsletter') ?></h3>
47
+ <div class="tnp-icon tnp-blue"><i class="fas fa-envelope-open"></i></div>
48
+ <div class="tnp-value"><?php echo $report->open_rate; ?>%</div>
49
+ <div class="tnp-value-2">(<?php echo $report->open_count; ?>)</div>
50
+ </div>
51
+ </div>
52
+
53
+ <div class="col-md-3">
54
+ <div class="tnp-widget tnp-number">
55
+ <h3><?php _e('Clicks', 'newsletter') ?></h3>
56
+ <div class="tnp-icon tnp-orange"><i class="fas fa-mouse-pointer"></i></div>
57
+ <div class="tnp-value"><?php echo $report->click_rate; ?>%</div>
58
+ <div class="tnp-value-2">(<?php echo $report->click_count; ?>)</div>
59
+ </div>
60
+ </div>
61
+
62
+ <div class="col-md-3">
63
+ <div class="tnp-widget tnp-number tnp-inactive">
64
+ <h3>Reactivity</h3>
65
+ <div class="tnp-icon tnp-gray"><i class="fas fa-star"></i></div>
66
+ <div class="tnp-value">-%</div>
67
+ </div>
68
+ </div>
69
+ </div>
70
+
71
+ <div class="row">
72
+ <div class="col-md-4">
73
+ <div class="tnp-widget tnp-inactive">
74
+ <h3>Clicked URLs</h3>
75
+ <div class="tnp-placeholder">
76
+ <a href="https://www.thenewsletterplugin.com/premium?utm_source=reports&utm_medium=urls&utm_campaign=plugin" target="_blank">
77
+ <img src="<?php echo plugins_url('newsletter') ?>/statistics/images/clicked-urls@2x.png">
78
+ </a>
79
+ </div>
80
+ </div>
81
+ </div>
82
+
83
+ <div class="col-md-4">
84
+ <div class="tnp-widget tnp-inactive">
85
+ <h3>World Map</h3>
86
+ <div class="tnp-placeholder">
87
+ <a href="https://www.thenewsletterplugin.com/premium?utm_source=reports&utm_medium=map&utm_campaign=plugin" target="_blank">
88
+ <img src="<?php echo plugins_url('newsletter') ?>/statistics/images/world-map@2x.png">
89
+ </a>
90
+ </div>
91
+
92
+ </div>
93
+ </div>
94
+
95
+ <div class="col-md-4">
96
+ <div class="tnp-widget tnp-inactive">
97
+ <h3>Interactions</h3>
98
+ <div class="tnp-placeholder">
99
+ <a href="https://www.thenewsletterplugin.com/premium?utm_source=reports&utm_medium=interactions&utm_campaign=plugin" target="_blank">
100
+ <img src="<?php echo plugins_url('newsletter') ?>/statistics/images/interactions@2x.png">
101
+ </a>
102
+ </div>
103
+ </div>
104
+ </div>
105
+
106
+ </div>
107
+
108
+ </div>
109
+ <?php include NEWSLETTER_DIR . '/tnp-footer.php' ?>
110
+ </div>
subscription/subscription.php CHANGED
@@ -296,12 +296,12 @@ class NewsletterSubscription extends NewsletterModule {
296
  }
297
 
298
  function admin_menu() {
299
- $this->add_menu_page('options', 'List building');
300
- $this->add_admin_page('lists', 'Lists');
301
- $this->add_admin_page('profile', 'Subscription Form');
302
- $this->add_menu_page('antibot', 'Security');
303
- $this->add_admin_page('forms', 'Forms');
304
- $this->add_admin_page('template', 'Template');
305
  }
306
 
307
  /**
296
  }
297
 
298
  function admin_menu() {
299
+ $this->add_menu_page('options', __('List building', 'newsletter'));
300
+ $this->add_admin_page('lists', __('Lists', 'newsletter'));
301
+ $this->add_admin_page('profile', __('Subscription Form', 'newsletter'));
302
+ $this->add_menu_page('antibot', __('Security', 'newsletter'));
303
+ $this->add_admin_page('forms', __('Forms', 'newsletter'));
304
+ $this->add_admin_page('template', __('Template', 'newsletter'));
305
  }
306
 
307
  /**
system/css/system.css ADDED
@@ -0,0 +1,134 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ .tnp-system-status table.widefat tbody tr>td:first-child {
3
+ width: 150px!important;
4
+ }
5
+
6
+ .tnp-system h3, .tnp-system h4 {
7
+ color: #fff;
8
+ }
9
+
10
+ .tnp-system #tnp-body table.widefat td {
11
+ vertical-align: top;
12
+ }
13
+ .tnp-system #tnp-body table.widefat td.status {
14
+ text-align: center;
15
+ }
16
+
17
+ .tnp-db-table {
18
+ width: auto;
19
+ background-color: #fff;
20
+ }
21
+
22
+ .tnp-db-table thead {
23
+ border-bottom: 1px solid #eee;
24
+ }
25
+
26
+ .tnp-db-table th {
27
+ font-weight: bold;
28
+ }
29
+
30
+ .tnp-db-table td, .tnp-db-table th {
31
+ padding: 3px;
32
+ font-family: monospace;
33
+ border: 0;
34
+ }
35
+
36
+
37
+ table.widefat {
38
+ border: 0;
39
+ box-shadow: none;
40
+ }
41
+
42
+ #tnp-status-table tbody tr:nth-child(2n+1) {
43
+ background-color: #ECF0F1;
44
+ border-radius: 2px;
45
+ margin: 5px;
46
+ }
47
+
48
+ #tnp-parameters-table tbody tr:nth-child(2n+1) {
49
+ background-color: #ECF0F1;
50
+ border-radius: 2px;
51
+ margin: 5px;
52
+ }
53
+
54
+ .tnp-ok {
55
+ font-weight: bold;
56
+ color: white;
57
+ font-size: 14px;
58
+ background-color: #27AE60;
59
+ padding: 2px 10px;
60
+ border-radius: 10px;
61
+ display: inline-block;
62
+ width: 75px;
63
+ text-align: center;
64
+ }
65
+
66
+ .tnp-ko {
67
+ font-weight: bold;
68
+ color: white;
69
+ font-size: 14px;
70
+ background-color: #E74C41;
71
+ padding: 2px 10px;
72
+ border-radius: 10px;
73
+ display: inline-block;
74
+ width: 75px;
75
+ text-align: center;
76
+ }
77
+
78
+ .tnp-maybe {
79
+ font-weight: bold;
80
+ color: white;
81
+ font-size: 14px;
82
+ background-color: #F1C40F;
83
+ padding: 2px 10px;
84
+ border-radius: 10px;
85
+ display: inline-block;
86
+ width: 75px;
87
+ text-align: center;
88
+ }
89
+
90
+
91
+ /* Logs page */
92
+
93
+ .tnp-log-files li {
94
+ padding-left: 15px;
95
+ }
96
+
97
+ .tnp-log-files li, .tnp-log-files li a {
98
+ color: #fff;
99
+ }
100
+
101
+ .tnp-log-files .tnp-log-size {
102
+ font-style: italic;
103
+ }
104
+
105
+
106
+ /* Delivery page */
107
+
108
+ .tnp-flow {
109
+ margin-top: 30px;
110
+ }
111
+ .tnp-flow div {
112
+ display: inline-block;
113
+ vertical-align: middle;
114
+ color: #fff;
115
+ text-align: center;
116
+ margin: 0 20px;
117
+ padding: 1em;
118
+ }
119
+ .tnp-flow div i {
120
+ font-size: 50px;
121
+ }
122
+
123
+ .tnp-flow div.tnp-arrow {
124
+ font-size: 40px;
125
+ color: #bbb;
126
+ padding: 0;
127
+ margin: 0;
128
+ }
129
+
130
+ .tnp-mail, .tnp-addon, .tnp-service {
131
+ height: 150px;
132
+ width: 150px;
133
+ border: 1px solid #fff;
134
+ }
{main → system}/delivery.php RENAMED
@@ -1,11 +1,12 @@
1
  <?php
2
- /* @var $this Newsletter */
3
  /* @var $wpdb wpdb */
4
 
5
  defined('ABSPATH') || exit;
6
 
7
  include_once NEWSLETTER_INCLUDES_DIR . '/controls.php';
8
  $controls = new NewsletterControls();
 
9
 
10
  if ($controls->is_action('test')) {
11
 
@@ -17,13 +18,13 @@ if ($controls->is_action('test')) {
17
 
18
  $options = $controls->data;
19
 
20
- if ($controls->data['test_email'] == $this->options['sender_email']) {
21
  $controls->messages .= '<strong>Warning:</strong> you are using as test email the same address configured as sender in main configuration. Test can fail because of that.<br>';
22
  }
23
 
24
  $message = NewsletterMailerAddon::get_test_message($controls->data['test_email'], 'Newsletter test email at ' . date(DATE_ISO8601));
25
 
26
- $r = $this->deliver($message);
27
 
28
  if (!is_wp_error($r)) {
29
  $options['mail'] = 1;
@@ -34,21 +35,21 @@ if ($controls->is_action('test')) {
34
  $options['mail'] = 0;
35
  $options['mail_error'] = $r->get_error_message();
36
 
37
- $controls->errors .= '<strong>FAILED</strong> (' . $r->get_error_message() . ')<br>';
38
 
39
- if (!empty($this->options['return_path'])) {
40
  $controls->errors .= '- Try to remove the return path on main settings.<br>';
41
  }
42
 
43
  $controls->errors .= '<a href="https://www.thenewsletterplugin.com/documentation/?p=15170" target="_blank"><strong>' . __('Read more', 'newsletter') . '</strong></a>.';
44
 
45
- $parts = explode('@', $this->options['sender_email']);
46
  $sitename = strtolower($_SERVER['SERVER_NAME']);
47
  if (substr($sitename, 0, 4) == 'www.') {
48
  $sitename = substr($sitename, 4);
49
  }
50
  if (strtolower($sitename) != strtolower($parts[1])) {
51
- $controls->errors .= '- Try to set on main setting a sender address with the same domain of your blog: ' . $sitename . ' (you are using ' . $this->options['sender_email'] . ')<br>';
52
  }
53
  }
54
  $this->save_options($options, 'status');
@@ -77,7 +78,7 @@ if ($mailer instanceof NewsletterDefaultMailer) {
77
  if (is_object($mailer)) {
78
  if (method_exists($mailer, 'get_description')) {
79
  $mailer_name = esc_html($mailer->get_description());
80
- $service_name = esc_html(ucfirst($mailer->get_name()));
81
  } else {
82
  $mailer_name = esc_html(get_class($mailer));
83
  $service_name = $mailer_name;
@@ -114,37 +115,14 @@ function tnp_get_hook_functions($tag) {
114
  return $b;
115
  }
116
 
117
- $speed = Newsletter::$instance->options['scheduler_max'];
118
  ?>
 
119
  <style>
120
- .tnp-flow {
121
- margin-top: 30px;
122
- }
123
- .tnp-flow div {
124
- display: inline-block;
125
- vertical-align: middle;
126
- color: #fff;
127
- text-align: center;
128
- margin: 0 20px;
129
- padding: 1em;
130
- }
131
- .tnp-flow div i {
132
- font-size: 50px;
133
- }
134
- .tnp-flow div.tnp-arrow {
135
- font-size: 40px;
136
- color: #bbb;
137
- padding: 0;
138
- margin: 0;
139
- }
140
- .tnp-mail, .tnp-addon, .tnp-service {
141
- height: 150px;
142
- width: 150px;
143
- border: 1px solid #fff;
144
- }
145
  </style>
146
 
147
- <div class="wrap tnp-main-status" id="tnp-wrap">
148
 
149
  <?php include NEWSLETTER_DIR . '/tnp-header.php'; ?>
150
 
@@ -160,7 +138,8 @@ $speed = Newsletter::$instance->options['scheduler_max'];
160
  <h3>Mailing test</h3>
161
 
162
  <p>
163
- <?php $controls->text_email('test_email') ?> <?php $controls->button('test', __('Send a test message')) ?>
 
164
  <?php if (empty($options['mail'])) { ?>
165
  <span class="tnp-ko">KO</span>
166
  <?php } else { ?>
@@ -214,7 +193,7 @@ $speed = Newsletter::$instance->options['scheduler_max'];
214
  <p><?php echo $functions ?></p>
215
  <?php } ?>
216
 
217
-
218
  </div>
219
  <?php include NEWSLETTER_DIR . '/tnp-footer.php'; ?>
220
  </div>
1
  <?php
2
+ /* @var $this NewsletterSystem */
3
  /* @var $wpdb wpdb */
4
 
5
  defined('ABSPATH') || exit;
6
 
7
  include_once NEWSLETTER_INCLUDES_DIR . '/controls.php';
8
  $controls = new NewsletterControls();
9
+ $newsletter = Newsletter::instance();
10
 
11
  if ($controls->is_action('test')) {
12
 
18
 
19
  $options = $controls->data;
20
 
21
+ if ($controls->data['test_email'] == $newsletter->get_sender_email()) {
22
  $controls->messages .= '<strong>Warning:</strong> you are using as test email the same address configured as sender in main configuration. Test can fail because of that.<br>';
23
  }
24
 
25
  $message = NewsletterMailerAddon::get_test_message($controls->data['test_email'], 'Newsletter test email at ' . date(DATE_ISO8601));
26
 
27
+ $r = $newsletter->deliver($message);
28
 
29
  if (!is_wp_error($r)) {
30
  $options['mail'] = 1;
35
  $options['mail'] = 0;
36
  $options['mail_error'] = $r->get_error_message();
37
 
38
+ $controls->errors .= '<strong>FAILED</strong> (' . esc_html($r->get_error_message()) . ')<br>';
39
 
40
+ if (!empty($newsletter->options['return_path'])) {
41
  $controls->errors .= '- Try to remove the return path on main settings.<br>';
42
  }
43
 
44
  $controls->errors .= '<a href="https://www.thenewsletterplugin.com/documentation/?p=15170" target="_blank"><strong>' . __('Read more', 'newsletter') . '</strong></a>.';
45
 
46
+ $parts = explode('@', $newsletter->get_sender_email());
47
  $sitename = strtolower($_SERVER['SERVER_NAME']);
48
  if (substr($sitename, 0, 4) == 'www.') {
49
  $sitename = substr($sitename, 4);
50
  }
51
  if (strtolower($sitename) != strtolower($parts[1])) {
52
+ $controls->errors .= '- Try to set on main setting a sender address with the same domain of your blog: ' . esc_html($sitename) . ' (you are using ' . esc_html($newsletter->get_sender_email()) . ')<br>';
53
  }
54
  }
55
  $this->save_options($options, 'status');
78
  if (is_object($mailer)) {
79
  if (method_exists($mailer, 'get_description')) {
80
  $mailer_name = esc_html($mailer->get_description());
81
+ $service_name = esc_html(ucfirst($mailer->get_name()) . ' Service');
82
  } else {
83
  $mailer_name = esc_html(get_class($mailer));
84
  $service_name = $mailer_name;
115
  return $b;
116
  }
117
 
118
+ $speed = Newsletter::$instance->get_send_speed();
119
  ?>
120
+
121
  <style>
122
+ <?php include __DIR__ . '/css/system.css' ?>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
123
  </style>
124
 
125
+ <div class="wrap tnp-system tnp-system-delivery" id="tnp-wrap">
126
 
127
  <?php include NEWSLETTER_DIR . '/tnp-header.php'; ?>
128
 
138
  <h3>Mailing test</h3>
139
 
140
  <p>
141
+ <?php $controls->text_email('test_email', ['required' => true]) ?>
142
+ <?php $controls->button('test', __('Send a test message')) ?>
143
  <?php if (empty($options['mail'])) { ?>
144
  <span class="tnp-ko">KO</span>
145
  <?php } else { ?>
193
  <p><?php echo $functions ?></p>
194
  <?php } ?>
195
 
196
+
197
  </div>
198
  <?php include NEWSLETTER_DIR . '/tnp-footer.php'; ?>
199
  </div>
{main → system}/logs.php RENAMED
@@ -20,8 +20,11 @@ if ($controls->is_action('delete_logs')) {
20
 
21
  ?>
22
 
 
 
 
23
 
24
- <div class="wrap tnp-main-status" id="tnp-wrap">
25
 
26
  <?php include NEWSLETTER_DIR . '/tnp-header.php'; ?>
27
 
20
 
21
  ?>
22
 
23
+ <style>
24
+ <?php include __DIR__ . '/css/system.css' ?>
25
+ </style>
26
 
27
+ <div class="wrap tnp-system tnp-system-logs" id="tnp-wrap">
28
 
29
  <?php include NEWSLETTER_DIR . '/tnp-header.php'; ?>
30
 
{main → system}/scheduler.php RENAMED
@@ -1,5 +1,5 @@
1
  <?php
2
- /* @var $this Newsletter */
3
  /* @var $wpdb wpdb */
4
 
5
  defined('ABSPATH') || exit;
@@ -8,10 +8,9 @@ wp_enqueue_script('tnp-chart');
8
 
9
  include_once NEWSLETTER_INCLUDES_DIR . '/controls.php';
10
  $controls = new NewsletterControls();
11
- $system = NewsletterSystem::instance();
12
 
13
  if ($controls->is_action('reset')) {
14
- $system->reset_cron_stats();
15
  $controls->add_message_done();
16
  }
17
 
@@ -43,32 +42,13 @@ if ($controls->is_action('test')) {
43
  }
44
  }
45
 
46
- function tnp_status_print_flag($condition, $url = '') {
47
- switch ($condition) {
48
- case 0: echo ' <span class="tnp-ko">KO</span>';
49
- break;
50
- case 1: echo '<span class="tnp-ok">OK</span>';
51
- break;
52
- case 2: echo '<span class="tnp-maybe">MAYBE</span>';
53
- break;
54
- }
55
- if ($url) {
56
- echo '<br><a href="', $url, '" target="_blank">Read more</a>';
57
- }
58
- }
59
-
60
- $system = NewsletterSystem::instance();
61
  ?>
62
 
63
  <style>
64
- #tnp-body table.widefat td {
65
- vertical-align: top;
66
- }
67
- #tnp-body table.widefat td.status {
68
- text-align: center;
69
- }
70
  </style>
71
- <div class="wrap tnp-main-status" id="tnp-wrap">
 
72
 
73
  <?php include NEWSLETTER_DIR . '/tnp-header.php'; ?>
74
 
@@ -97,13 +77,13 @@ $system = NewsletterSystem::instance();
97
 
98
  <table class="widefat">
99
  <?php
100
- $status = $system->get_job_status();
101
  $condition = $status == NewsletterSystem::JOB_OK ? 1 : 0;
102
  ?>
103
  <tr>
104
  <td>Delivery background job</td>
105
  <td class="status">
106
- <?php tnp_status_print_flag($condition, 'https://www.thenewsletterplugin.com/documentation/delivery-and-spam/newsletter-delivery-engine/') ?>
107
  </td>
108
  <td>
109
  <?php
@@ -123,7 +103,7 @@ $system = NewsletterSystem::instance();
123
  }
124
  ?>
125
  <br><br>
126
- Next run: <?php echo $controls->print_date($system->get_job_schedule(), false, true) ?>
127
  <br><br>
128
  <?php
129
  if ($status == NewsletterSystem::JOB_LATE) {
@@ -138,7 +118,7 @@ $system = NewsletterSystem::instance();
138
  <td>Last cron call</td>
139
  <td class="status">&nbsp;</td>
140
  <td>
141
- <?php echo $controls->print_date($system->get_last_cron_call()) ?>
142
  </td>
143
  </tr>
144
 
@@ -165,7 +145,7 @@ $system = NewsletterSystem::instance();
165
  ?>
166
 
167
  <td class="status">
168
- <?php tnp_status_print_flag($condition, 'https://www.thenewsletterplugin.com/documentation/delivery-and-spam/newsletter-delivery-engine/') ?>
169
  </td>
170
  <td>
171
  <?php if ($condition == 0) { ?>
@@ -211,14 +191,14 @@ $system = NewsletterSystem::instance();
211
  </tr>
212
 
213
  <?php
214
- $condition = $system->has_newsletter_schedule() ? 1 : 0;
215
  $schedules = wp_get_schedules();
216
  ?>
217
  <tr>
218
  <td>
219
  Newsletter engine schedule
220
  </td>
221
- <td class="status"><?php tnp_status_print_flag($condition) ?></td>
222
  <td>
223
  <?php if (!$condition) { ?>
224
  The Newsletter schedule is not present probably another plugin is interfering with the starndard WordPress scheuling system.<br>
@@ -262,7 +242,7 @@ $system = NewsletterSystem::instance();
262
  WordPress scheduler auto trigger
263
  </td>
264
  <td class="status">
265
- <?php //tnp_status_print_flag($condition) ?>
266
  </td>
267
  <td>
268
  <?php $controls->button_test() ?>
@@ -277,7 +257,7 @@ $system = NewsletterSystem::instance();
277
  <code>DISABLE_WP_CRON</code>
278
  </td>
279
  <td class="status">
280
- <?php tnp_status_print_flag($condition) ?>
281
  </td>
282
  <td>
283
  <?php if ($condition == 2) { ?>
@@ -309,7 +289,7 @@ $system = NewsletterSystem::instance();
309
  <tr>
310
  <td><code>NEWSLETTER_CRON_INTERVAL</code></td>
311
  <td class="status">
312
- <?php tnp_status_print_flag($condition) ?>
313
  </td>
314
  <td>
315
  <?php echo NEWSLETTER_CRON_INTERVAL, ' seconds'; ?>
@@ -326,7 +306,7 @@ $system = NewsletterSystem::instance();
326
  <tr>
327
  <td><code>WP_CRON_LOCK_TIMEOUT</code></td>
328
  <td class="status">
329
- <?php tnp_status_print_flag($condition) ?>
330
  </td>
331
  <td>
332
  <?php echo WP_CRON_LOCK_TIMEOUT, ' seconds'; ?>
@@ -348,7 +328,7 @@ $system = NewsletterSystem::instance();
348
  <code>NEWSLETTER_CRON_WARNINGS</code>
349
  </td>
350
  <td class="status">
351
- <?php tnp_status_print_flag($condition) ?>
352
  </td>
353
  <td>
354
  <?php if ($condition == 2) { ?>
@@ -361,12 +341,12 @@ $system = NewsletterSystem::instance();
361
 
362
  <?php
363
  $condition = has_filter('pre_reschedule_event') ? 2 : 1;
364
- $functions = $system->get_hook_functions('pre_reschedule_event');
365
  ?>
366
  <tr>
367
  <td><code>pre_reschedule_event</code></td>
368
  <td class="status">
369
- <?php tnp_status_print_flag($condition) ?>
370
  </td>
371
  <td>
372
  <?php if ($condition == 2) { ?>
@@ -384,7 +364,7 @@ $system = NewsletterSystem::instance();
384
  <tr>
385
  <td>Transient <code>doing_cron</code></td>
386
  <td class="status">
387
- <?php //tnp_status_print_flag($condition) ?>
388
  </td>
389
  <td>
390
  <?php if ($transient) { ?>
1
  <?php
2
+ /* @var $this NewsletterSystem */
3
  /* @var $wpdb wpdb */
4
 
5
  defined('ABSPATH') || exit;
8
 
9
  include_once NEWSLETTER_INCLUDES_DIR . '/controls.php';
10
  $controls = new NewsletterControls();
 
11
 
12
  if ($controls->is_action('reset')) {
13
+ $this->reset_cron_stats();
14
  $controls->add_message_done();
15
  }
16
 
42
  }
43
  }
44
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
  ?>
46
 
47
  <style>
48
+ <?php include __DIR__ . '/css/system.css' ?>
 
 
 
 
 
49
  </style>
50
+
51
+ <div class="wrap tnp-system tnp-system-scheduler" id="tnp-wrap">
52
 
53
  <?php include NEWSLETTER_DIR . '/tnp-header.php'; ?>
54
 
77
 
78
  <table class="widefat">
79
  <?php
80
+ $status = $this->get_job_status();
81
  $condition = $status == NewsletterSystem::JOB_OK ? 1 : 0;
82
  ?>
83
  <tr>
84
  <td>Delivery background job</td>
85
  <td class="status">
86
+ <?php $this->condition_flag($condition, 'https://www.thenewsletterplugin.com/documentation/delivery-and-spam/newsletter-delivery-engine/') ?>
87
  </td>
88
  <td>
89
  <?php
103
  }
104
  ?>
105
  <br><br>
106
+ Next run: <?php echo $controls->print_date($this->get_job_schedule(), false, true) ?>
107
  <br><br>
108
  <?php
109
  if ($status == NewsletterSystem::JOB_LATE) {
118
  <td>Last cron call</td>
119
  <td class="status">&nbsp;</td>
120
  <td>
121
+ <?php echo $controls->print_date($this->get_last_cron_call()) ?>
122
  </td>
123
  </tr>
124
 
145
  ?>
146
 
147
  <td class="status">
148
+ <?php $this->condition_flag($condition, 'https://www.thenewsletterplugin.com/documentation/delivery-and-spam/newsletter-delivery-engine/') ?>
149
  </td>
150
  <td>
151
  <?php if ($condition == 0) { ?>
191
  </tr>
192
 
193
  <?php
194
+ $condition = $this->has_newsletter_schedule() ? 1 : 0;
195
  $schedules = wp_get_schedules();
196
  ?>
197
  <tr>
198
  <td>
199
  Newsletter engine schedule
200
  </td>
201
+ <td class="status"><?php $this->condition_flag($condition) ?></td>
202
  <td>
203
  <?php if (!$condition) { ?>
204
  The Newsletter schedule is not present probably another plugin is interfering with the starndard WordPress scheuling system.<br>
242
  WordPress scheduler auto trigger
243
  </td>
244
  <td class="status">
245
+ <?php //$this->condition_flag($condition) ?>
246
  </td>
247
  <td>
248
  <?php $controls->button_test() ?>
257
  <code>DISABLE_WP_CRON</code>
258
  </td>
259
  <td class="status">
260
+ <?php $this->condition_flag($condition) ?>
261
  </td>
262
  <td>
263
  <?php if ($condition == 2) { ?>
289
  <tr>
290
  <td><code>NEWSLETTER_CRON_INTERVAL</code></td>
291
  <td class="status">
292
+ <?php $this->condition_flag($condition) ?>
293
  </td>
294
  <td>
295
  <?php echo NEWSLETTER_CRON_INTERVAL, ' seconds'; ?>
306
  <tr>
307
  <td><code>WP_CRON_LOCK_TIMEOUT</code></td>
308
  <td class="status">
309
+ <?php $this->condition_flag($condition) ?>
310
  </td>
311
  <td>
312
  <?php echo WP_CRON_LOCK_TIMEOUT, ' seconds'; ?>
328
  <code>NEWSLETTER_CRON_WARNINGS</code>
329
  </td>
330
  <td class="status">
331
+ <?php $this->condition_flag($condition) ?>
332
  </td>
333
  <td>
334
  <?php if ($condition == 2) { ?>
341
 
342
  <?php
343
  $condition = has_filter('pre_reschedule_event') ? 2 : 1;
344
+ $functions = $this->get_hook_functions('pre_reschedule_event');
345
  ?>
346
  <tr>
347
  <td><code>pre_reschedule_event</code></td>
348
  <td class="status">
349
+ <?php $this->condition_flag($condition) ?>
350
  </td>
351
  <td>
352
  <?php if ($condition == 2) { ?>
364
  <tr>
365
  <td>Transient <code>doing_cron</code></td>
366
  <td class="status">
367
+ <?php //$this->condition_flag($condition) ?>
368
  </td>
369
  <td>
370
  <?php if ($transient) { ?>
{main → system}/status.php RENAMED
@@ -1,5 +1,5 @@
1
  <?php
2
- /* @var $this Newsletter */
3
  /* @var $wpdb wpdb */
4
 
5
  defined('ABSPATH') || exit;
@@ -9,7 +9,8 @@ wp_enqueue_script('tnp-chart');
9
  include_once NEWSLETTER_INCLUDES_DIR . '/controls.php';
10
  $controls = new NewsletterControls();
11
 
12
- $system = NewsletterSystem::instance();
 
13
 
14
  if ($controls->is_action('delete_logs')) {
15
  $files = glob(WP_CONTENT_DIR . '/logs/newsletter/*.txt');
@@ -19,11 +20,9 @@ if ($controls->is_action('delete_logs')) {
19
  }
20
  $secret = NewsletterModule::get_token(8);
21
  update_option('newsletter_logger_secret', $secret);
22
- $controls->messages = 'Logs deleted';
23
  }
24
 
25
-
26
-
27
  if ($controls->is_action('conversion')) {
28
  $this->logger->info('Maybe convert to utf8mb4');
29
  require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
@@ -45,58 +44,10 @@ if ($controls->is_action('conversion')) {
45
  }
46
 
47
  if ($controls->is_action('reset_send_stats')) {
48
- $system->reset_send_stats();
49
  $controls->add_message_done();
50
  }
51
 
52
- if ($controls->is_action('test')) {
53
-
54
- if (!NewsletterModule::is_email($controls->data['test_email'])) {
55
- $controls->errors = 'The test email address is not set or is not correct.';
56
- }
57
-
58
- if (empty($controls->errors)) {
59
-
60
- $options = $controls->data;
61
-
62
- if ($controls->data['test_email'] == $this->options['sender_email']) {
63
- $controls->messages .= '<strong>Warning:</strong> you are using as test email the same address configured as sender in main configuration. Test can fail because of that.<br>';
64
- }
65
-
66
- $message = NewsletterMailerAddon::get_test_message($controls->data['test_email'], 'Newsletter test email at ' . date(DATE_ISO8601));
67
-
68
- $r = $this->deliver($message);
69
-
70
- if (!is_wp_error($r)) {
71
- $options['mail'] = 1;
72
- $controls->messages .= '<strong>SUCCESS</strong><br>';
73
- $controls->messages .= 'Anyway if the message does not appear the mailbox (check even the spam folder) you can ';
74
- $controls->messages .= '<a href="https://www.thenewsletterplugin.com/documentation/?p=15170" target="_blank"><strong>read more here</strong></a>.';
75
- } else {
76
- $options['mail'] = 0;
77
- $options['mail_error'] = $r->get_error_message();
78
-
79
- $controls->errors .= '<strong>FAILED</strong> (' . $r->get_error_message() . ')<br>';
80
-
81
- if (!empty($this->options['return_path'])) {
82
- $controls->errors .= '- Try to remove the return path on main settings.<br>';
83
- }
84
-
85
- $controls->errors .= '<a href="https://www.thenewsletterplugin.com/documentation/?p=15170" target="_blank"><strong>' . __('Read more', 'newsletter') . '</strong></a>.';
86
-
87
- $parts = explode('@', $this->options['sender_email']);
88
- $sitename = strtolower($_SERVER['SERVER_NAME']);
89
- if (substr($sitename, 0, 4) == 'www.') {
90
- $sitename = substr($sitename, 4);
91
- }
92
- if (strtolower($sitename) != strtolower($parts[1])) {
93
- $controls->errors .= '- Try to set on main setting a sender address with the same domain of your blog: ' . $sitename . ' (you are using ' . $this->options['sender_email'] . ')<br>';
94
- }
95
- }
96
- $this->save_options($options, 'status');
97
- }
98
- }
99
-
100
  if ($controls->is_action('stats_email_column_upgrade')) {
101
  $this->query("alter table " . NEWSLETTER_STATS_TABLE . " drop index email_id");
102
  $this->query("alter table " . NEWSLETTER_STATS_TABLE . " drop index user_id");
@@ -107,8 +58,6 @@ if ($controls->is_action('stats_email_column_upgrade')) {
107
  update_option('newsletter_stats_email_column_upgraded', true);
108
  }
109
 
110
- $options = $this->get_options('status');
111
-
112
  // Compute the number of newsletters ongoing and other stats
113
  $emails = $wpdb->get_results("select * from " . NEWSLETTER_EMAILS_TABLE . " where status='sending' and send_on<" . time() . " order by id asc");
114
  $total = 0;
@@ -117,19 +66,9 @@ foreach ($emails as $email) {
117
  $total += $email->total;
118
  $queued += $email->total - $email->sent;
119
  }
120
- $speed = Newsletter::$instance->options['scheduler_max'];
121
-
122
- function tnp_status_print_flag($condition) {
123
- switch ($condition) {
124
- case 0: echo ' <span class="tnp-ko">KO</span>';
125
- break;
126
- case 1: echo '<span class="tnp-ok">OK</span>';
127
- break;
128
- case 2: echo '<span class="tnp-maybe">MAYBE</span>';
129
- break;
130
- }
131
- }
132
 
 
133
  class TNP_WPDB extends wpdb {
134
 
135
  public function get_table_charset($table) {
@@ -140,13 +79,12 @@ class TNP_WPDB extends wpdb {
140
 
141
  $tnp_wpdb = new TNP_WPDB(DB_USER, DB_PASSWORD, DB_NAME, DB_HOST);
142
  ?>
 
143
  <style>
144
- table.widefat tbody tr>td:first-child {
145
- width: 150px!important;
146
- }
147
  </style>
148
 
149
- <div class="wrap tnp-main-status" id="tnp-wrap">
150
 
151
  <?php include NEWSLETTER_DIR . '/tnp-header.php'; ?>
152
 
@@ -198,23 +136,11 @@ $tnp_wpdb = new TNP_WPDB(DB_USER, DB_PASSWORD, DB_NAME, DB_HOST);
198
  &nbsp;
199
  </td>
200
  <td>
201
- <?php
202
- $mailer = Newsletter::instance()->get_mailer();
203
- $name = 'Unknown';
204
- if (is_object($mailer)) {
205
- if (method_exists($mailer, 'get_description')) {
206
- $name = $mailer->get_description();
207
- } else {
208
- $name = get_class($mailer);
209
- }
210
- }
211
- ?>
212
-
213
- <?php echo esc_html($name) ?>
214
  </td>
215
  </tr>
216
  <?php
217
- $stats = $system->get_send_stats();
218
 
219
  if ($stats) {
220
  $condition = $stats->mean > 2 ? 2 : 1;
@@ -224,11 +150,11 @@ $tnp_wpdb = new TNP_WPDB(DB_USER, DB_PASSWORD, DB_NAME, DB_HOST);
224
  Send details
225
  </td>
226
  <td class="status">
227
- <?php tnp_status_print_flag($condition) ?>
228
 
229
  </td>
230
  <td>
231
- <?php if ($condition) { ?>
232
  <strong>Sending an email is taking more than 1 second, rather slow.</strong>
233
  <a href="https://www.thenewsletterplugin.com/documentation/status-panel#status-performance" target="_blank">Read more</a>.
234
  <br>
@@ -312,8 +238,8 @@ $tnp_wpdb = new TNP_WPDB(DB_USER, DB_PASSWORD, DB_NAME, DB_HOST);
312
 
313
  <tr>
314
  <?php
315
- $page_id = $this->get_newsletter_page_id();
316
- $page = $this->get_newsletter_page();
317
  $condition = 1;
318
  if ($page_id) {
319
  if (!$page || $page->post_status !== 'publish') {
@@ -328,7 +254,7 @@ $tnp_wpdb = new TNP_WPDB(DB_USER, DB_PASSWORD, DB_NAME, DB_HOST);
328
  <small>The blog page Newsletter uses for messages</small>
329
  </td>
330
  <td>
331
- <?php tnp_status_print_flag($condition) ?>
332
  </td>
333
  <td>
334
  <?php if ($condition == 2) { ?>
@@ -343,8 +269,8 @@ $tnp_wpdb = new TNP_WPDB(DB_USER, DB_PASSWORD, DB_NAME, DB_HOST);
343
 
344
  <tr>
345
  <?php
346
- $page_id = $this->get_newsletter_page_id();
347
- $page = $this->get_newsletter_page();
348
  $condition = 1;
349
  if ($page_id) {
350
  if (!$page) {
@@ -361,7 +287,7 @@ $tnp_wpdb = new TNP_WPDB(DB_USER, DB_PASSWORD, DB_NAME, DB_HOST);
361
  Dedicated page content<br>
362
  </td>
363
  <td>
364
- <?php tnp_status_print_flag($condition) ?>
365
  </td>
366
  <td>
367
  <?php if ($condition == 2) { ?>
@@ -388,7 +314,7 @@ $tnp_wpdb = new TNP_WPDB(DB_USER, DB_PASSWORD, DB_NAME, DB_HOST);
388
  <tr>
389
  <td>Add-ons installable</td>
390
  <td>
391
- <?php tnp_status_print_flag($condition) ?>
392
  </td>
393
  <td>
394
  <?php if ($condition == 2) { ?>
@@ -406,11 +332,11 @@ $tnp_wpdb = new TNP_WPDB(DB_USER, DB_PASSWORD, DB_NAME, DB_HOST);
406
 
407
 
408
  <?php
409
- $return_path = $this->options['return_path'];
410
  if (!empty($return_path)) {
411
  list($return_path_local, $return_path_domain) = explode('@', $return_path);
412
  }
413
- $sender = $this->options['sender_email'];
414
  if (!empty($sender)) {
415
  list($sender_local, $sender_domain) = explode('@', $sender);
416
  }
@@ -449,7 +375,7 @@ $tnp_wpdb = new TNP_WPDB(DB_USER, DB_PASSWORD, DB_NAME, DB_HOST);
449
  ?>
450
  <td>Addons update</td>
451
  <td>
452
- <?php tnp_status_print_flag($condition) ?>
453
  </td>
454
  <td>
455
  <?php if ($condition == 0) { ?>
@@ -523,7 +449,7 @@ $tnp_wpdb = new TNP_WPDB(DB_USER, DB_PASSWORD, DB_NAME, DB_HOST);
523
  <small>Your blog can check the professional addon updates?</small>
524
  </td>
525
  <td>
526
- <?php tnp_status_print_flag($condition) ?>
527
  </td>
528
  <td>
529
  <?php if ($condition == 0) { ?>
@@ -638,7 +564,7 @@ $tnp_wpdb = new TNP_WPDB(DB_USER, DB_PASSWORD, DB_NAME, DB_HOST);
638
  Log folder
639
  </td>
640
  <td>
641
- <?php tnp_status_print_flag($condition) ?>
642
  </td>
643
  <td>
644
  The log folder is <?php echo esc_html(NEWSLETTER_LOG_DIR) ?><br>
@@ -671,7 +597,7 @@ $tnp_wpdb = new TNP_WPDB(DB_USER, DB_PASSWORD, DB_NAME, DB_HOST);
671
  WordPress debug mode
672
  </td>
673
  <td>
674
- <?php tnp_status_print_flag($condition) ?>
675
  </td>
676
  <td>
677
  <?php if (defined('WP_DEBUG') && WP_DEBUG) { ?>
@@ -691,7 +617,7 @@ $tnp_wpdb = new TNP_WPDB(DB_USER, DB_PASSWORD, DB_NAME, DB_HOST);
691
  ?>
692
  <td>Blog Charset</td>
693
  <td>
694
- <?php tnp_status_print_flag($condition) ?>
695
  </td>
696
  <td>
697
  Charset: <?php echo esc_html($charset) ?>
@@ -713,7 +639,7 @@ $tnp_wpdb = new TNP_WPDB(DB_USER, DB_PASSWORD, DB_NAME, DB_HOST);
713
  ?>
714
  <td>Home URL</td>
715
  <td>
716
- <?php tnp_status_print_flag($condition) ?>
717
  </td>
718
  <td>
719
  Value: <?php echo home_url('/'); ?>
@@ -734,7 +660,7 @@ $tnp_wpdb = new TNP_WPDB(DB_USER, DB_PASSWORD, DB_NAME, DB_HOST);
734
  ?>
735
  <td>WP_CONTENT_URL</td>
736
  <td>
737
- <?php tnp_status_print_flag($condition) ?>
738
  </td>
739
  <td>
740
  Value: <?php echo esc_html(WP_CONTENT_URL); ?>
@@ -758,7 +684,7 @@ $tnp_wpdb = new TNP_WPDB(DB_USER, DB_PASSWORD, DB_NAME, DB_HOST);
758
  ?>
759
  <td>WordPress transients</td>
760
  <td>
761
- <?php tnp_status_print_flag($condition) ?>
762
  </td>
763
  <td>
764
  <?php if ($res !== false) { ?>
@@ -817,7 +743,7 @@ $tnp_wpdb = new TNP_WPDB(DB_USER, DB_PASSWORD, DB_NAME, DB_HOST);
817
  ?>
818
  <td>PHP execution time limit</td>
819
  <td>
820
- <?php tnp_status_print_flag($condition) ?>
821
  </td>
822
  <td>
823
  <?php if (!$res) { ?>
@@ -929,7 +855,7 @@ $tnp_wpdb = new TNP_WPDB(DB_USER, DB_PASSWORD, DB_NAME, DB_HOST);
929
  <tr>
930
  <td>Database wait timeout</td>
931
  <td>
932
- <?php tnp_status_print_flag($condition) ?>
933
  </td>
934
  <td>
935
  Your database wait timeout is <?php echo $wait_timeout; ?> seconds<br>
@@ -948,7 +874,7 @@ $tnp_wpdb = new TNP_WPDB(DB_USER, DB_PASSWORD, DB_NAME, DB_HOST);
948
  <tr>
949
  <td>Database table creation</td>
950
  <td>
951
- <?php tnp_status_print_flag($condition) ?>
952
  </td>
953
  <td>
954
  <?php if ($res === false) { ?>
@@ -966,7 +892,7 @@ $tnp_wpdb = new TNP_WPDB(DB_USER, DB_PASSWORD, DB_NAME, DB_HOST);
966
  <tr>
967
  <td>Database table change</td>
968
  <td>
969
- <?php tnp_status_print_flag($condition) ?>
970
  </td>
971
  <td>
972
  <?php if ($res === false) { ?>
@@ -993,7 +919,7 @@ $tnp_wpdb = new TNP_WPDB(DB_USER, DB_PASSWORD, DB_NAME, DB_HOST);
993
  <?php if ($to_upgrade) { ?>
994
  <tr>
995
  <td>Database stats table upgrade</td>
996
- <td><?php tnp_status_print_flag(0) ?></td>
997
  <td><?php $controls->button('stats_email_column_upgrade', 'Stats table upgrade') ?></td>
998
  </tr>
999
  <?php } ?>
1
  <?php
2
+ /* @var $this NewsletterSystem */
3
  /* @var $wpdb wpdb */
4
 
5
  defined('ABSPATH') || exit;
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');
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');
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");
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;
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) {
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
 
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 > 2 ? 2 : 1;
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 1 second, rather slow.</strong>
159
  <a href="https://www.thenewsletterplugin.com/documentation/status-panel#status-performance" target="_blank">Read more</a>.
160
  <br>
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') {
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) { ?>
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) {
287
  Dedicated page content<br>
288
  </td>
289
  <td>
290
+ <?php $this->condition_flag($condition) ?>
291
  </td>
292
  <td>
293
  <?php if ($condition == 2) { ?>
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) { ?>
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
  }
375
  ?>
376
  <td>Addons update</td>
377
  <td>
378
+ <?php $this->condition_flag($condition) ?>
379
  </td>
380
  <td>
381
  <?php if ($condition == 0) { ?>
449
  <small>Your blog can check the professional addon updates?</small>
450
  </td>
451
  <td>
452
+ <?php $this->condition_flag($condition) ?>
453
  </td>
454
  <td>
455
  <?php if ($condition == 0) { ?>
564
  Log folder
565
  </td>
566
  <td>
567
+ <?php $this->condition_flag($condition) ?>
568
  </td>
569
  <td>
570
  The log folder is <?php echo esc_html(NEWSLETTER_LOG_DIR) ?><br>
597
  WordPress debug mode
598
  </td>
599
  <td>
600
+ <?php $this->condition_flag($condition) ?>
601
  </td>
602
  <td>
603
  <?php if (defined('WP_DEBUG') && WP_DEBUG) { ?>
617
  ?>
618
  <td>Blog Charset</td>
619
  <td>
620
+ <?php $this->condition_flag($condition) ?>
621
  </td>
622
  <td>
623
  Charset: <?php echo esc_html($charset) ?>
639
  ?>
640
  <td>Home URL</td>
641
  <td>
642
+ <?php $this->condition_flag($condition) ?>
643
  </td>
644
  <td>
645
  Value: <?php echo home_url('/'); ?>
660
  ?>
661
  <td>WP_CONTENT_URL</td>
662
  <td>
663
+ <?php $this->condition_flag($condition) ?>
664
  </td>
665
  <td>
666
  Value: <?php echo esc_html(WP_CONTENT_URL); ?>
684
  ?>
685
  <td>WordPress transients</td>
686
  <td>
687
+ <?php $this->condition_flag($condition) ?>
688
  </td>
689
  <td>
690
  <?php if ($res !== false) { ?>
743
  ?>
744
  <td>PHP execution time limit</td>
745
  <td>
746
+ <?php $this->condition_flag($condition) ?>
747
  </td>
748
  <td>
749
  <?php if (!$res) { ?>
855
  <tr>
856
  <td>Database wait timeout</td>
857
  <td>
858
+ <?php $this->condition_flag($condition) ?>
859
  </td>
860
  <td>
861
  Your database wait timeout is <?php echo $wait_timeout; ?> seconds<br>
874
  <tr>
875
  <td>Database table creation</td>
876
  <td>
877
+ <?php $this->condition_flag($condition) ?>
878
  </td>
879
  <td>
880
  <?php if ($res === false) { ?>
892
  <tr>
893
  <td>Database table change</td>
894
  <td>
895
+ <?php $this->condition_flag($condition) ?>
896
  </td>
897
  <td>
898
  <?php if ($res === false) { ?>
919
  <?php if ($to_upgrade) { ?>
920
  <tr>
921
  <td>Database stats table upgrade</td>
922
+ <td><?php $this->condition_flag(0) ?></td>
923
  <td><?php $controls->button('stats_email_column_upgrade', 'Stats table upgrade') ?></td>
924
  </tr>
925
  <?php } ?>
{includes → system}/system.php RENAMED
@@ -2,7 +2,7 @@
2
 
3
  defined('ABSPATH') || exit;
4
 
5
- class NewsletterSystem {
6
 
7
  static $instance;
8
 
@@ -22,7 +22,18 @@ class NewsletterSystem {
22
  }
23
 
24
  function __construct() {
 
 
25
 
 
 
 
 
 
 
 
 
 
26
  }
27
 
28
  function reset_cron_stats() {
@@ -223,6 +234,20 @@ class NewsletterSystem {
223
  return $b;
224
  }
225
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
226
  }
227
 
228
  class TNP_Cron_Stats {
@@ -238,8 +263,8 @@ class TNP_Cron_Stats {
238
  }
239
 
240
  class TNP_Send_Stats {
241
-
242
  /* Emails sent */
 
243
  var $total_emails;
244
  /* Total batches collected (1 sized-batch by Autoresponder are ignored) */
245
  var $total_runs;
@@ -261,3 +286,5 @@ class TNP_Send_Stats {
261
  var $sizes = [];
262
 
263
  }
 
 
2
 
3
  defined('ABSPATH') || exit;
4
 
5
+ class NewsletterSystem extends NewsletterModule {
6
 
7
  static $instance;
8
 
22
  }
23
 
24
  function __construct() {
25
+ parent::__construct('system', '1.1.0');
26
+ }
27
 
28
+ function admin_menu() {
29
+ if (current_user_can('administrator')) {
30
+ $this->add_admin_page('status', __('Status', 'newsletter'));
31
+ $this->add_admin_page('delivery', __('Delivery Diagnostic', 'newsletter'));
32
+ $this->add_admin_page('logs', __('Logs', 'newsletter'));
33
+ $this->add_admin_page('scheduler', __('Scheduler', 'newsletter'));
34
+ $this->add_admin_page('diagnostic', __('Diagnostic', 'newsletter'));
35
+ $this->add_admin_page('test', __('Test', 'newsletter'));
36
+ }
37
  }
38
 
39
  function reset_cron_stats() {
234
  return $b;
235
  }
236
 
237
+ function condition_flag($condition, $url = '') {
238
+ switch ($condition) {
239
+ case 0: echo ' <span class="tnp-ko">KO</span>';
240
+ break;
241
+ case 1: echo '<span class="tnp-ok">OK</span>';
242
+ break;
243
+ case 2: echo '<span class="tnp-maybe">MAYBE</span>';
244
+ break;
245
+ }
246
+ if ($url) {
247
+ echo '<br><a href="', $url, '" target="_blank">Read more</a>';
248
+ }
249
+ }
250
+
251
  }
252
 
253
  class TNP_Cron_Stats {
263
  }
264
 
265
  class TNP_Send_Stats {
 
266
  /* Emails sent */
267
+
268
  var $total_emails;
269
  /* Total batches collected (1 sized-batch by Autoresponder are ignored) */
270
  var $total_runs;
286
  var $sizes = [];
287
 
288
  }
289
+
290
+ NewsletterSystem::instance();
tnp-header.php CHANGED
@@ -1,340 +1,325 @@
1
- <?php
2
- global $current_user, $wpdb;
3
-
4
- defined('ABSPATH') || exit;
5
-
6
- $dismissed = get_option('newsletter_dismissed', []);
7
-
8
- $user_count = Newsletter::instance()->get_user_count();
9
-
10
- $is_administrator = current_user_can('administrator');
11
-
12
- function newsletter_print_entries($group) {
13
- $entries = apply_filters('newsletter_menu_' . $group, array());
14
- if (!$entries) {
15
- return;
16
- }
17
-
18
- foreach ($entries as &$entry) {
19
- echo '<li><a href="', $entry['url'], '">', $entry['label'];
20
- if (!empty($entry['description'])) {
21
- echo '<small>', $entry['description'], '</small>';
22
- }
23
- echo '</a></li>';
24
- }
25
- }
26
-
27
- // Check the status to show a warning if needed
28
- $status_options = Newsletter::instance()->get_options('status');
29
- $warning = false;
30
-
31
- $warning |= empty($status_options['mail']);
32
- ?>
33
- <script>
34
- function tnp_close_promotion() {
35
- jQuery.post(ajaxurl + "?action=tnp_hide_promotion", {id: 'june-2020'});
36
- document.getElementById('tnp-promotion-bar').style.display = 'none';
37
- }
38
- </script>
39
-
40
- <?php if (get_option('newsletter_promotion') !== 'june-2020' && time() < gmmktime(0, 0, 0, 7, 15, 2020)) { ?>
41
- <div id="tnp-promotion-bar">
42
- <a href="https://www.thenewsletterplugin.com/premium?utm_source=plugin-bar&utm_campaign=june-2020" onclick="tnp_close_promotion(); return true;" target="_blank">50% discount offer until July, 15. Check it out.</a>
43
- &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
44
- <a href="javascript:void(tnp_close_promotion('june-2020'))">No, thank you!</a>
45
- </div>
46
- <?php } ?>
47
-
48
-
49
- <div class="tnp-drowpdown" id="tnp-header">
50
- <a href="?page=newsletter_main_index"><img src="<?php echo plugins_url('newsletter'); ?>/admin/images/logo-red.png" class="tnp-header-logo" style="vertical-align: bottom;"></a>
51
- <ul>
52
- <li><a href="#"><i class="fas fa-users"></i> <?php _e('Subscribers', 'newsletter') ?> <i class="fas fa-chevron-down"></i></a>
53
- <ul>
54
- <li>
55
- <a href="?page=newsletter_users_index"><i class="fas fa-search"></i> <?php _e('Search And Edit', 'newsletter') ?>
56
- <small><?php _e('Add, edit, search', 'newsletter') ?></small></a>
57
- </li>
58
-
59
- <li>
60
- <a href="?page=newsletter_profile_index"><i class="fas fa-user-circle"></i> <?php _e('Profile page', 'newsletter') ?>
61
- <small><?php _e('The subscriber personal profile editing panel', 'newsletter') ?></small></a>
62
- </li>
63
-
64
- <?php if (!class_exists('NewsletterImport')) { ?>
65
- <li>
66
- <a href="?page=newsletter_users_import"><i class="fas fa-upload"></i> <?php _e('Import', 'newsletter') ?>
67
- <small><?php _e('Import from external sources', 'newsletter') ?></small></a>
68
- </li>
69
- <?php } ?>
70
-
71
- <li>
72
- <a href="?page=newsletter_users_export"><i class="fas fa-download"></i> <?php _e('Export', 'newsletter') ?>
73
- <small><?php _e('Export your subscribers list', 'newsletter') ?></small></a>
74
- </li>
75
-
76
- <li>
77
- <a href="?page=newsletter_users_massive"><i class="fas fa-wrench"></i> <?php _e('Maintenance', 'newsletter') ?>
78
- <small><?php _e('Massive actions: change list, clean up, ...', 'newsletter') ?></small></a>
79
- </li>
80
-
81
- <li>
82
- <a href="?page=newsletter_users_statistics"><i class="fas fa-chart-bar"></i> <?php _e('Statistics', 'newsletter') ?>
83
- <small><?php _e('All about your subscribers', 'newsletter') ?></small></a>
84
- </li>
85
-
86
- <?php newsletter_print_entries('subscribers') ?>
87
- </ul>
88
- </li>
89
- <li><a href="#"><i class="fas fa-list"></i> <?php _e('List Building', 'newsletter') ?> <i class="fas fa-chevron-down"></i></a>
90
- <ul>
91
- <li>
92
- <a href="?page=newsletter_subscription_profile"><i class="fas fa-check-square"></i> <?php _e('Subscription Form Fields, Buttons, Labels', 'newsletter') ?>
93
- <small><?php _e('When and what data to collect', 'newsletter') ?></small></a>
94
- </li>
95
-
96
- <li>
97
- <a href="?page=newsletter_subscription_options"><i class="fas fa-sign-in-alt"></i> <?php _e('Subscription', 'newsletter') ?>
98
- <small><?php _e('The subscription process in detail', 'newsletter') ?></small></a>
99
- </li>
100
-
101
- <li>
102
- <a href="?page=newsletter_subscription_lists"><i class="fas fa-th-list"></i> <?php _e('Lists', 'newsletter') ?>
103
- <small><?php _e('Profile the subscribers for a better targeting', 'newsletter') ?></small></a>
104
- </li>
105
-
106
- <li>
107
- <a href="?page=newsletter_subscription_antibot"><i class="fas fa-lock"></i> <?php _e('Security', 'newsletter') ?>
108
- <small><?php _e('Spam subscriptions control', 'newsletter') ?></small></a>
109
- </li>
110
-
111
- <li>
112
- <a href="?page=newsletter_unsubscription_index"><i class="fas fa-sign-out-alt"></i> <?php _e('Unsubscription', 'newsletter') ?>
113
- <small><?php _e('How to give the last goodbye (or avoid it!)', 'newsletter') ?></small></a>
114
- </li>
115
-
116
- <?php
117
- newsletter_print_entries('subscription');
118
- ?>
119
- </ul>
120
- </li>
121
-
122
- <li>
123
- <a href="#"><i class="fas fa-newspaper"></i> <?php _e('Newsletters', 'newsletter') ?> <i class="fas fa-chevron-down"></i></a>
124
- <ul>
125
- <li>
126
- <a href="?page=newsletter_emails_composer"><i class="fas fa-plus"></i> <?php _e('Create newsletter', 'newsletter') ?>
127
- <small><?php _e('Start your new campaign', 'newsletter') ?></small></a>
128
- </li>
129
-
130
- <li>
131
- <a href="?page=newsletter_emails_index"><i class="fas fa-newspaper"></i> <?php _e('Newsletters', 'newsletter') ?>
132
- <small><?php _e('The classic "write & send" newsletters', 'newsletter') ?></small></a>
133
- </li>
134
-
135
- <li>
136
- <a href="<?php echo NewsletterStatistics::instance()->get_index_url() ?>"><i class="fas fa-chart-bar"></i> <?php _e('Statistics', 'newsletter') ?>
137
- <small><?php _e('Tracking configuration and basic data', 'newsletter') ?></small></a>
138
- </li>
139
- <?php
140
- newsletter_print_entries('newsletters');
141
- ?>
142
- </ul>
143
- </li>
144
-
145
- <li>
146
- <a href="#"><i class="fas fa-cog"></i> <?php _e('Settings', 'newsletter') ?> <i class="fas fa-chevron-down"></i></a>
147
- <ul>
148
- <?php if ($is_administrator) { ?>
149
- <li>
150
- <a href="?page=newsletter_main_main"><i class="fas fa-cogs"></i> <?php _e('General Settings', 'newsletter') ?>
151
- <small><?php _e('Delivery speed, sender details, ...', 'newsletter') ?></small></a>
152
- </li>
153
- <?php if (!class_exists('NewsletterSmtp')) { ?>
154
- <li>
155
- <a href="?page=newsletter_main_smtp"><i class="fas fa-server"></i> <?php _e('SMTP', 'newsletter') ?>
156
- <small><?php _e('External mail server', 'newsletter') ?></small>
157
- </a>
158
- </li>
159
- <?php } ?>
160
- <?php } ?>
161
-
162
- <li>
163
- <a href="?page=newsletter_main_info"><i class="fas fa-info"></i> <?php _e('Company Info', 'newsletter') ?>
164
- <small><?php _e('Social, address, logo and general info', 'newsletter') ?></small></a>
165
- </li>
166
-
167
- <li>
168
- <a href="?page=newsletter_subscription_template"><i class="fas fa-file-alt"></i> <?php _e('Messages Template', 'newsletter') ?>
169
- <small><?php _e('Change the look of your service emails', 'newsletter') ?></small></a>
170
- </li>
171
-
172
- <?php
173
- newsletter_print_entries('settings');
174
- ?>
175
- </ul>
176
- </li>
177
-
178
- <?php if ($is_administrator) { ?>
179
- <li>
180
- <a href="#"><i class="fas fa-thermometer"></i> <?php _e('System', 'newsletter') ?>
181
- <?php if ($warning) { ?>
182
- <i class="fas fa-exclamation-triangle" style="color: red;"></i>
183
- <?php } ?>
184
- </a>
185
- <ul>
186
- <li>
187
- <a href="<?php echo admin_url('site-health.php') ?> "><i class="fas fa-file"></i> <?php _e('Site health') ?>
188
- <small><?php _e('WP native site health checks', 'newsletter') ?></small></a>
189
- </li>
190
- <li>
191
- <a href="?page=newsletter_main_delivery"><i class="fas fa-file"></i> <?php _e('Delivery Diagnostic', 'newsletter') ?>
192
- <small><?php _e('Delivery analysis and test', 'newsletter') ?></small></a>
193
- </li>
194
- <li>
195
- <a href="?page=newsletter_main_scheduler"><i class="fas fa-robot"></i> <?php _e('Scheduler', 'newsletter') ?>
196
- <small><?php _e('', 'newsletter') ?></small></a>
197
- </li>
198
- <li>
199
- <a href="?page=newsletter_main_status"><i class="fas fa-file"></i> <?php _e('Status', 'newsletter') ?>
200
- <small><?php _e('Checks and parameters', 'newsletter') ?></small></a>
201
- </li>
202
-
203
- <li>
204
- <a href="?page=newsletter_main_logs"><i class="fas fa-file"></i> <?php _e('Logs', 'newsletter') ?>
205
- <small><?php _e('Plugin and addons logs', 'newsletter') ?></small></a>
206
- </li>
207
- </ul>
208
- </li>
209
- <?php } ?>
210
-
211
- <?php
212
- $license_data = Newsletter::instance()->get_license_data();
213
- $premium_url = 'https://www.thenewsletterplugin.com/premium?utm_source=header&utm_medium=link&utm_campaign=plugin&utm_content=' . urlencode($_GET['page']);
214
- ?>
215
-
216
- <?php if (empty($license_data)) { ?>
217
- <li class="tnp-professional-extensions-button"><a href="<?php echo $premium_url ?>" target="_blank">
218
- <i class="fas fa-trophy"></i> <?php _e('Get Professional Addons', 'newsletter') ?></a>
219
- </li>
220
- <?php } elseif (is_wp_error($license_data)) { ?>
221
- <li class="tnp-professional-extensions-button-red">
222
- <a href="?page=newsletter_main_main"><i class="fas fa-hand-paper" style="color: white"></i> <?php _e('Unable to check', 'newsletter') ?></a>
223
- </li>
224
-
225
- <?php } elseif ($license_data->expire == 0) { ?>
226
-
227
- <li class="tnp-professional-extensions-button"><a href="<?php echo $premium_url ?>" target="_blank">
228
- <i class="fas fa-trophy"></i> <?php _e('Get Professional Addons', 'newsletter') ?></a>
229
- </li>
230
-
231
- <?php } elseif ($license_data->expire < time()) { ?>
232
-
233
- <li class="tnp-professional-extensions-button-red">
234
- <a href="?page=newsletter_main_main"><i class="fas fa-hand-paper" style="color: white"></i> <?php _e('License expired', 'newsletter') ?></a>
235
- </li>
236
-
237
- <?php } elseif ($license_data->expire >= time()) { ?>
238
-
239
- <?php $p = class_exists('NewsletterExtensions') ? 'newsletter_extensions_index' : 'newsletter_main_extensions'; ?>
240
- <li class="tnp-professional-extensions-button">
241
- <a href="?page=<?php echo $p ?>"><i class="fas fa-check-square"></i> <?php _e('License active', 'newsletter') ?></a>
242
- </li>
243
-
244
-
245
- <?php } ?>
246
- </ul>
247
- </div>
248
-
249
- <?php if (isset($_GET['debug']) || !isset($dismissed['newsletter-shortcode'])) { ?>
250
- <?php
251
- // Check of Newsletter dedicated page
252
- if (!empty(Newsletter::instance()->options['page'])) {
253
- if (get_post_status(Newsletter::instance()->options['page']) === 'publish') {
254
- $content = get_post_field('post_content', Newsletter::instance()->options['page']);
255
- // With and without attributes
256
- if (strpos($content, '[newsletter]') === false && strpos($content, '[newsletter ') === false) {
257
- ?>
258
- <div class="tnp-notice">
259
- <a href="<?php echo $_SERVER['REQUEST_URI'] . '&noheader=1&dismiss=newsletter-shortcode' ?>" class="tnp-dismiss">&times;</a>
260
- The Newsletter dedicated page does not contain the [newsletter] shortcode. If you're using a visual composer it could be ok.
261
- <a href="<?php echo site_url('/wp-admin/post.php') ?>?post=<?php echo esc_attr(Newsletter::instance()->options['page']) ?>&action=edit"><strong>Edit the page</strong></a>.
262
-
263
- </div>
264
- <?php
265
- }
266
- }
267
- }
268
- ?>
269
- <?php } ?>
270
-
271
- <?php if (isset($_GET['debug']) || !isset($dismissed['rate']) && $user_count > 300) { ?>
272
- <div class="tnp-notice">
273
- <a href="<?php echo $_SERVER['REQUEST_URI'] . '&noheader=1&dismiss=rate' ?>" class="tnp-dismiss">&times;</a>
274
-
275
- We never asked before and we're curious: <a href="http://wordpress.org/plugins/newsletter/" target="_blank">would you rate this plugin</a>?
276
- (few seconds required - account on WordPress.org required, every blog owner should have one...). <strong>Really appreciated, The Newsletter Team</strong>.
277
-
278
- </div>
279
- <?php } ?>
280
-
281
- <?php if (isset($_GET['debug']) || !isset($dismissed['newsletter-page']) && empty(Newsletter::instance()->options['page'])) { ?>
282
- <div class="tnp-notice">
283
- <a href="<?php echo $_SERVER['REQUEST_URI'] . '&noheader=1&dismiss=newsletter-page' ?>" class="tnp-dismiss">&times;</a>
284
-
285
- You should create a blog page to show the subscription form and the subscription messages. Go to the
286
- <a href="?page=newsletter_main_main">general settings panel</a> to configure it.
287
-
288
- </div>
289
- <?php } ?>
290
-
291
- <?php if (isset($_GET['debug']) || !isset($dismissed['newsletter-subscribe']) && get_option('newsletter_install_time') && get_option('newsletter_install_time') < time() - 86400 * 15) { ?>
292
- <div class="tnp-notice">
293
- <a href="<?php echo $_SERVER['REQUEST_URI'] . '&noheader=1&dismiss=newsletter-subscribe' ?>" class="tnp-dismiss">&times;</a>
294
- If you want to be informed of important updates of Newsletter, you may want to subscribe to our newsletter<br>
295
- <form action="https://www.thenewsletterplugin.com/?na=s" target="_blank" method="post">
296
- <input type="hidden" value="plugin-header" name="nr">
297
- <input type="hidden" value="3" name="nl[]">
298
- <input type="hidden" value="single" name="optin">
299
- <input type="email" name="ne" value="<?php echo esc_attr(get_option('admin_email')) ?>">
300
- <input type="submit" value="<?php esc_attr_e('Subscribe', 'newsletter') ?>">
301
- </form>
302
- </div>
303
- <?php } ?>
304
-
305
- <?php
306
- if (!defined('NEWSLETTER_CRON_WARNINGS') || NEWSLETTER_CRON_WARNINGS) {
307
- $x = NewsletterSystem::instance()->get_job_status();
308
- if ($x !== NewsletterSystem::JOB_OK) {
309
- echo '<div class="tnpc-warning">The are issues with the delivery engine. Please <a href="?page=newsletter_main_scheduler">check them here</a>.</div>';
310
- }
311
- }
312
- ?>
313
-
314
- <?php
315
- if ($_GET['page'] !== 'newsletter_emails_edit') {
316
-
317
- $last_failed_newsletters = Newsletter::instance()->get_emails_by_field('status', TNP_Email::STATUS_ERROR);
318
-
319
- foreach ($last_failed_newsletters as $newsletter) {
320
- echo '<div class="tnpc-error">';
321
- printf(__('Newsletter "%s" stopped by fatal error.', 'newsletter'), esc_html($newsletter->subject));
322
- echo '&nbsp;';
323
- $c = new NewsletterControls();
324
- $c->btn_link('?page=newsletter_emails_edit&id=' . $newsletter->id, __('Check', 'newsletter'));
325
- echo '</div>';
326
- }
327
- }
328
- ?>
329
-
330
- <div id="tnp-notification">
331
- <?php
332
- if (isset($controls)) {
333
- $controls->show();
334
- $controls->messages = '';
335
- $controls->errors = '';
336
- }
337
- ?>
338
- </div>
339
-
340
-
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
+
34
+ <div class="tnp-drowpdown" id="tnp-header">
35
+ <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>
36
+ <ul>
37
+ <li><a href="#"><i class="fas fa-users"></i> <?php _e('Subscribers', 'newsletter') ?> <i class="fas fa-chevron-down"></i></a>
38
+ <ul>
39
+ <li>
40
+ <a href="?page=newsletter_users_index"><i class="fas fa-search"></i> <?php _e('Search And Edit', 'newsletter') ?>
41
+ <small><?php _e('Add, edit, search', 'newsletter') ?></small></a>
42
+ </li>
43
+
44
+ <li>
45
+ <a href="?page=newsletter_profile_index"><i class="fas fa-user-circle"></i> <?php _e('Profile page', 'newsletter') ?>
46
+ <small><?php _e('The subscriber personal profile editing panel', 'newsletter') ?></small></a>
47
+ </li>
48
+
49
+ <?php if (!class_exists('NewsletterImport')) { ?>
50
+ <li>
51
+ <a href="?page=newsletter_users_import"><i class="fas fa-upload"></i> <?php _e('Import', 'newsletter') ?>
52
+ <small><?php _e('Import from external sources', 'newsletter') ?></small></a>
53
+ </li>
54
+ <?php } ?>
55
+
56
+ <li>
57
+ <a href="?page=newsletter_users_export"><i class="fas fa-download"></i> <?php _e('Export', 'newsletter') ?>
58
+ <small><?php _e('Export your subscribers list', 'newsletter') ?></small></a>
59
+ </li>
60
+
61
+ <li>
62
+ <a href="?page=newsletter_users_massive"><i class="fas fa-wrench"></i> <?php _e('Maintenance', 'newsletter') ?>
63
+ <small><?php _e('Massive actions: change list, clean up, ...', 'newsletter') ?></small></a>
64
+ </li>
65
+
66
+ <li>
67
+ <a href="?page=newsletter_users_statistics"><i class="fas fa-chart-bar"></i> <?php _e('Statistics', 'newsletter') ?>
68
+ <small><?php _e('All about your subscribers', 'newsletter') ?></small></a>
69
+ </li>
70
+
71
+ <?php newsletter_print_entries('subscribers') ?>
72
+ </ul>
73
+ </li>
74
+ <li><a href="#"><i class="fas fa-list"></i> <?php _e('List Building', 'newsletter') ?> <i class="fas fa-chevron-down"></i></a>
75
+ <ul>
76
+ <li>
77
+ <a href="?page=newsletter_subscription_profile"><i class="fas fa-check-square"></i> <?php _e('Subscription Form Fields, Buttons, Labels', 'newsletter') ?>
78
+ <small><?php _e('When and what data to collect', 'newsletter') ?></small></a>
79
+ </li>
80
+
81
+ <li>
82
+ <a href="?page=newsletter_subscription_options"><i class="fas fa-sign-in-alt"></i> <?php _e('Subscription', 'newsletter') ?>
83
+ <small><?php _e('The subscription process in detail', 'newsletter') ?></small></a>
84
+ </li>
85
+
86
+ <li>
87
+ <a href="?page=newsletter_subscription_lists"><i class="fas fa-th-list"></i> <?php _e('Lists', 'newsletter') ?>
88
+ <small><?php _e('Profile the subscribers for a better targeting', 'newsletter') ?></small></a>
89
+ </li>
90
+
91
+ <li>
92
+ <a href="?page=newsletter_subscription_antibot"><i class="fas fa-lock"></i> <?php _e('Security', 'newsletter') ?>
93
+ <small><?php _e('Spam subscriptions control', 'newsletter') ?></small></a>
94
+ </li>
95
+
96
+ <li>
97
+ <a href="?page=newsletter_unsubscription_index"><i class="fas fa-sign-out-alt"></i> <?php _e('Unsubscription', 'newsletter') ?>
98
+ <small><?php _e('How to give the last goodbye (or avoid it!)', 'newsletter') ?></small></a>
99
+ </li>
100
+
101
+ <?php
102
+ newsletter_print_entries('subscription');
103
+ ?>
104
+ </ul>
105
+ </li>
106
+
107
+ <li>
108
+ <a href="#"><i class="fas fa-newspaper"></i> <?php _e('Newsletters', 'newsletter') ?> <i class="fas fa-chevron-down"></i></a>
109
+ <ul>
110
+ <li>
111
+ <a href="?page=newsletter_emails_composer"><i class="fas fa-plus"></i> <?php _e('Create newsletter', 'newsletter') ?>
112
+ <small><?php _e('Start your new campaign', 'newsletter') ?></small></a>
113
+ </li>
114
+
115
+ <li>
116
+ <a href="?page=newsletter_emails_index"><i class="fas fa-newspaper"></i> <?php _e('Newsletters', 'newsletter') ?>
117
+ <small><?php _e('The classic "write & send" newsletters', 'newsletter') ?></small></a>
118
+ </li>
119
+
120
+ <li>
121
+ <a href="<?php echo NewsletterStatistics::instance()->get_index_url() ?>"><i class="fas fa-chart-bar"></i> <?php _e('Statistics', 'newsletter') ?>
122
+ <small><?php _e('Tracking configuration and basic data', 'newsletter') ?></small></a>
123
+ </li>
124
+ <?php
125
+ newsletter_print_entries('newsletters');
126
+ ?>
127
+ </ul>
128
+ </li>
129
+
130
+ <li>
131
+ <a href="#"><i class="fas fa-cog"></i> <?php _e('Settings', 'newsletter') ?> <i class="fas fa-chevron-down"></i></a>
132
+ <ul>
133
+ <?php if ($is_administrator) { ?>
134
+ <li>
135
+ <a href="?page=newsletter_main_main"><i class="fas fa-cogs"></i> <?php _e('General Settings', 'newsletter') ?>
136
+ <small><?php _e('Delivery speed, sender details, ...', 'newsletter') ?></small></a>
137
+ </li>
138
+ <?php if (!class_exists('NewsletterSmtp')) { ?>
139
+ <li>
140
+ <a href="?page=newsletter_main_smtp"><i class="fas fa-server"></i> <?php _e('SMTP', 'newsletter') ?>
141
+ <small><?php _e('External mail server', 'newsletter') ?></small>
142
+ </a>
143
+ </li>
144
+ <?php } ?>
145
+ <?php } ?>
146
+
147
+ <li>
148
+ <a href="?page=newsletter_main_info"><i class="fas fa-info"></i> <?php _e('Company Info', 'newsletter') ?>
149
+ <small><?php _e('Social, address, logo and general info', 'newsletter') ?></small></a>
150
+ </li>
151
+
152
+ <li>
153
+ <a href="?page=newsletter_subscription_template"><i class="fas fa-file-alt"></i> <?php _e('Messages Template', 'newsletter') ?>
154
+ <small><?php _e('Change the look of your service emails', 'newsletter') ?></small></a>
155
+ </li>
156
+
157
+ <?php
158
+ newsletter_print_entries('settings');
159
+ ?>
160
+ </ul>
161
+ </li>
162
+
163
+ <?php if ($is_administrator) { ?>
164
+ <li>
165
+ <a href="#"><i class="fas fa-thermometer"></i> <?php _e('System', 'newsletter') ?>
166
+ <?php if ($warning) { ?>
167
+ <i class="fas fa-exclamation-triangle" style="color: red;"></i>
168
+ <?php } ?>
169
+ </a>
170
+ <ul>
171
+ <li>
172
+ <a href="<?php echo admin_url('site-health.php') ?> "><i class="fas fa-file"></i> <?php _e('Site health') ?>
173
+ <small><?php _e('WP native site health checks', 'newsletter') ?></small></a>
174
+ </li>
175
+ <li>
176
+ <a href="?page=newsletter_system_delivery"><i class="fas fa-file"></i> <?php _e('Delivery Diagnostic', 'newsletter') ?>
177
+ <small><?php _e('Delivery analysis and test', 'newsletter') ?></small></a>
178
+ </li>
179
+ <li>
180
+ <a href="?page=newsletter_system_scheduler"><i class="fas fa-robot"></i> <?php _e('Scheduler', 'newsletter') ?>
181
+ <small><?php _e('WordPress background jobs', 'newsletter') ?></small></a>
182
+ </li>
183
+ <li>
184
+ <a href="?page=newsletter_system_status"><i class="fas fa-file"></i> <?php _e('Status', 'newsletter') ?>
185
+ <small><?php _e('Checks and parameters', 'newsletter') ?></small></a>
186
+ </li>
187
+
188
+ <li>
189
+ <a href="?page=newsletter_system_logs"><i class="fas fa-file"></i> <?php _e('Logs', 'newsletter') ?>
190
+ <small><?php _e('Plugin and addons logs', 'newsletter') ?></small></a>
191
+ </li>
192
+ </ul>
193
+ </li>
194
+ <?php } ?>
195
+
196
+ <?php
197
+ $license_data = Newsletter::instance()->get_license_data();
198
+ $premium_url = 'https://www.thenewsletterplugin.com/premium?utm_source=header&utm_medium=link&utm_campaign=plugin&utm_content=' . urlencode($_GET['page']);
199
+ ?>
200
+
201
+ <?php if (empty($license_data)) { ?>
202
+ <li class="tnp-professional-extensions-button"><a href="<?php echo $premium_url ?>" target="_blank">
203
+ <i class="fas fa-trophy"></i> <?php _e('Get Professional Addons', 'newsletter') ?></a>
204
+ </li>
205
+ <?php } elseif (is_wp_error($license_data)) { ?>
206
+ <li class="tnp-professional-extensions-button-red">
207
+ <a href="?page=newsletter_main_main"><i class="fas fa-hand-paper" style="color: white"></i> <?php _e('Unable to check', 'newsletter') ?></a>
208
+ </li>
209
+
210
+ <?php } elseif ($license_data->expire == 0) { ?>
211
+
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
+
216
+ <?php } elseif ($license_data->expire < time()) { ?>
217
+
218
+ <li class="tnp-professional-extensions-button-red">
219
+ <a href="?page=newsletter_main_main"><i class="fas fa-hand-paper" style="color: white"></i> <?php _e('License expired', 'newsletter') ?></a>
220
+ </li>
221
+
222
+ <?php } elseif ($license_data->expire >= time()) { ?>
223
+
224
+ <?php $p = class_exists('NewsletterExtensions') ? 'newsletter_extensions_index' : 'newsletter_main_extensions'; ?>
225
+ <li class="tnp-professional-extensions-button">
226
+ <a href="?page=<?php echo $p ?>"><i class="fas fa-check-square"></i> <?php _e('License active', 'newsletter') ?></a>
227
+ </li>
228
+
229
+
230
+ <?php } ?>
231
+ </ul>
232
+ </div>
233
+
234
+ <?php if (isset($_GET['debug']) || !isset($dismissed['newsletter-shortcode'])) { ?>
235
+ <?php
236
+ // Check of Newsletter dedicated page
237
+ if (!empty(Newsletter::instance()->options['page'])) {
238
+ if (get_post_status(Newsletter::instance()->options['page']) === 'publish') {
239
+ $content = get_post_field('post_content', Newsletter::instance()->options['page']);
240
+ // With and without attributes
241
+ if (strpos($content, '[newsletter]') === false && strpos($content, '[newsletter ') === false) {
242
+ ?>
243
+ <div class="tnp-notice">
244
+ <a href="<?php echo $_SERVER['REQUEST_URI'] . '&noheader=1&dismiss=newsletter-shortcode' ?>" class="tnp-dismiss">&times;</a>
245
+ The Newsletter dedicated page does not contain the [newsletter] shortcode. If you're using a visual composer it could be ok.
246
+ <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>.
247
+
248
+ </div>
249
+ <?php
250
+ }
251
+ }
252
+ }
253
+ ?>
254
+ <?php } ?>
255
+
256
+ <?php if (isset($_GET['debug']) || !isset($dismissed['rate']) && $user_count > 300) { ?>
257
+ <div class="tnp-notice">
258
+ <a href="<?php echo $_SERVER['REQUEST_URI'] . '&noheader=1&dismiss=rate' ?>" class="tnp-dismiss">&times;</a>
259
+
260
+ We never asked before and we're curious: <a href="http://wordpress.org/plugins/newsletter/" target="_blank">would you rate this plugin</a>?
261
+ (few seconds required - account on WordPress.org required, every blog owner should have one...). <strong>Really appreciated, The Newsletter Team</strong>.
262
+
263
+ </div>
264
+ <?php } ?>
265
+
266
+ <?php if (isset($_GET['debug']) || !isset($dismissed['newsletter-page']) && empty(Newsletter::instance()->options['page'])) { ?>
267
+ <div class="tnp-notice">
268
+ <a href="<?php echo $_SERVER['REQUEST_URI'] . '&noheader=1&dismiss=newsletter-page' ?>" class="tnp-dismiss">&times;</a>
269
+
270
+ You should create a blog page to show the subscription form and the subscription messages. Go to the
271
+ <a href="?page=newsletter_main_main">general settings panel</a> to configure it.
272
+
273
+ </div>
274
+ <?php } ?>
275
+
276
+ <?php if (isset($_GET['debug']) || !isset($dismissed['newsletter-subscribe']) && get_option('newsletter_install_time') && get_option('newsletter_install_time') < time() - 86400 * 15) { ?>
277
+ <div class="tnp-notice">
278
+ <a href="<?php echo $_SERVER['REQUEST_URI'] . '&noheader=1&dismiss=newsletter-subscribe' ?>" class="tnp-dismiss">&times;</a>
279
+ If you want to be informed of important updates of Newsletter, you may want to subscribe to our newsletter<br>
280
+ <form action="https://www.thenewsletterplugin.com/?na=s" target="_blank" method="post">
281
+ <input type="hidden" value="plugin-header" name="nr">
282
+ <input type="hidden" value="3" name="nl[]">
283
+ <input type="hidden" value="single" name="optin">
284
+ <input type="email" name="ne" value="<?php echo esc_attr(get_option('admin_email')) ?>">
285
+ <input type="submit" value="<?php esc_attr_e('Subscribe', 'newsletter') ?>">
286
+ </form>
287
+ </div>
288
+ <?php } ?>
289
+
290
+ <?php
291
+ if (!defined('NEWSLETTER_CRON_WARNINGS') || NEWSLETTER_CRON_WARNINGS) {
292
+ $x = NewsletterSystem::instance()->get_job_status();
293
+ if ($x !== NewsletterSystem::JOB_OK) {
294
+ echo '<div class="tnpc-warning">The are issues with the delivery engine. Please <a href="?page=newsletter_main_scheduler">check them here</a>.</div>';
295
+ }
296
+ }
297
+ ?>
298
+
299
+ <?php
300
+ if ($_GET['page'] !== 'newsletter_emails_edit') {
301
+
302
+ $last_failed_newsletters = Newsletter::instance()->get_emails_by_field('status', TNP_Email::STATUS_ERROR);
303
+
304
+ foreach ($last_failed_newsletters as $newsletter) {
305
+ echo '<div class="tnpc-error">';
306
+ printf(__('Newsletter "%s" stopped by fatal error.', 'newsletter'), esc_html($newsletter->subject));
307
+ echo '&nbsp;';
308
+ $c = new NewsletterControls();
309
+ $c->btn_link('?page=newsletter_emails_edit&id=' . $newsletter->id, __('Check', 'newsletter'));
310
+ echo '</div>';
311
+ }
312
+ }
313
+ ?>
314
+
315
+ <div id="tnp-notification">
316
+ <?php
317
+ if (isset($controls)) {
318
+ $controls->show();
319
+ $controls->messages = '';
320
+ $controls->errors = '';
321
+ }
322
+ ?>
323
+ </div>
324
+
325
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
users/edit.php CHANGED
@@ -1,350 +1,350 @@
1
- <?php
2
- /* @var $this NewsletterUsers */
3
-
4
- defined('ABSPATH') || exit;
5
-
6
- require_once NEWSLETTER_INCLUDES_DIR . '/controls.php';
7
- $controls = new NewsletterControls();
8
-
9
- $id = (int) $_GET['id'];
10
- $user = $this->get_user($id);
11
-
12
- if ($controls->is_action('save')) {
13
-
14
- $email = $this->normalize_email($controls->data['email']);
15
- if (empty($email)) {
16
- $controls->errors = __('Wrong email address', 'newsletter');
17
- } else {
18
- $controls->data['email'] = $email;
19
- }
20
-
21
-
22
- if (empty($controls->errors)) {
23
- $u = $this->get_user($controls->data['email']);
24
- if ($u && $u->id != $id) {
25
- $controls->errors = __('The email address is already in use', 'newsletter');
26
- }
27
- }
28
-
29
- if (empty($controls->errors)) {
30
- // For unselected preferences, force the zero value
31
- for ($i = 1; $i <= NEWSLETTER_LIST_MAX; $i++) {
32
- if (!isset($controls->data['list_' . $i])) {
33
- $controls->data['list_' . $i] = 0;
34
- }
35
- }
36
-
37
- if (empty($controls->data['token'])) {
38
- $controls->data['token'] = $this->get_token();
39
- }
40
-
41
- $controls->data['id'] = $id;
42
- $user = $this->save_user($controls->data);
43
- $this->add_user_log($user, 'edit');
44
- if ($user === false) {
45
- $controls->errors = __('Error. Check the log files.', 'newsletter');
46
- } else {
47
- $controls->add_message_saved();
48
- $controls->data = (array) $user;
49
- }
50
- }
51
- }
52
-
53
- if ($controls->is_action('delete')) {
54
- $this->delete_user($id);
55
- $controls->js_redirect($this->get_admin_page_url('index'));
56
- return;
57
- }
58
-
59
- if (!$controls->is_action()) {
60
- $controls->data = (array) $user;
61
- }
62
-
63
- $options_profile = NewsletterSubscription::instance()->get_options('profile');
64
-
65
- function percent($value, $total) {
66
- if ($total == 0) {
67
- return '-';
68
- }
69
- return sprintf("%.2f", $value / $total * 100) . '%';
70
- }
71
-
72
- function percentValue($value, $total) {
73
- if ($total == 0) {
74
- return 0;
75
- }
76
- return round($value / $total * 100);
77
- }
78
- ?>
79
-
80
- <script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
81
- <script type="text/javascript">
82
- google.charts.load('current', {'packages': ['corechart', 'geomap']});
83
- </script>
84
-
85
- <div class="wrap tnp-users tnp-users-edit" id="tnp-wrap">
86
-
87
- <?php include NEWSLETTER_DIR . '/tnp-header.php'; ?>
88
-
89
- <div id="tnp-heading">
90
-
91
- <h2><?php _e('Editing', 'newsletter') ?> <?php echo esc_html($controls->data['email']) ?></h2>
92
-
93
- </div>
94
-
95
- <div id="tnp-body">
96
-
97
- <form method="post" action="">
98
- <p>
99
- <?php $controls->button_icon_back('?page=newsletter_users_index'); ?>
100
- <?php $controls->button_save(); ?>
101
- </p>
102
- <?php $controls->init(); ?>
103
-
104
- <div id="tabs">
105
-
106
- <ul>
107
- <li><a href="#tabs-general"><?php _e('General', 'newsletter') ?></a></li>
108
- <li><a href="#tabs-preferences"><?php _e('Lists', 'newsletter') ?></a></li>
109
- <li><a href="#tabs-profile"><?php _e('Extra fields', 'newsletter') ?></a></li>
110
- <li><a href="#tabs-other"><?php _e('Other', 'newsletter') ?></a></li>
111
- <li><a href="#tabs-newsletters"><?php _e('Newsletters', 'newsletter') ?></a></li>
112
- <li><a href="#tabs-history"><?php _e('Logs', 'newsletter') ?></a></li>
113
-
114
- </ul>
115
-
116
- <div id="tabs-general" class="tnp-tab">
117
-
118
- <?php do_action('newsletter_users_edit_general', $id, $controls) ?>
119
-
120
- <table class="form-table">
121
-
122
- <tr>
123
- <th><?php _e('Email', 'newsletter'); ?></th>
124
- <td>
125
- <?php $controls->text_email('email', 60); ?>
126
- </td>
127
- </tr>
128
- <tr>
129
- <th><?php _e('First name', 'newsletter'); ?></th>
130
- <td>
131
- <?php $controls->text('name', 50); ?>
132
- </td>
133
- </tr>
134
- <tr>
135
- <th><?php _e('Last name', 'newsletter'); ?></th>
136
- <td>
137
- <?php $controls->text('surname', 50); ?>
138
- </td>
139
- </tr>
140
- <tr>
141
- <th><?php _e('Gender', 'newsletter'); ?></th>
142
- <td>
143
- <?php $controls->select('sex', array('n' => __('Not specified', 'newsletter'), 'f' => __('Female', 'newsletter'), 'm' => __('Male', 'newsletter'))); ?>
144
- </td>
145
- </tr>
146
- <tr>
147
- <th><?php _e('Status', 'newsletter'); ?></th>
148
- <td>
149
- <?php
150
- $controls->select('status', [
151
- 'C' => TNP_User::get_status_label('C'),
152
- 'S' => TNP_User::get_status_label('S'),
153
- 'U' => TNP_User::get_status_label('U'),
154
- 'B' => TNP_User::get_status_label('B'),
155
- 'P' => TNP_User::get_status_label('P')
156
- ]);
157
- ?>
158
- </td>
159
- </tr>
160
- <tr>
161
- <th><?php _e('Language', 'newsletter'); ?></th>
162
- <td>
163
- <?php $controls->language('language', __('None', 'newsletter')); ?>
164
- </td>
165
- </tr>
166
- <tr>
167
- <th><?php _e('Test subscriber', 'newsletter'); ?>
168
- <br><?php $controls->help('https://www.thenewsletterplugin.com/documentation/subscribers#test-subscribers') ?></th>
169
- <td>
170
- <?php $controls->yesno('test'); ?>
171
- </td>
172
- </tr>
173
-
174
- <?php do_action('newsletter_user_edit_extra', $controls); ?>
175
-
176
- <tr>
177
- <th>Feed by mail</th>
178
- <td>
179
- <?php $controls->yesno('feed'); ?>
180
- </td>
181
- </tr>
182
- </table>
183
- </div>
184
- <div id="tabs-preferences" class="tnp-tab">
185
- <table class="form-table">
186
- <tr>
187
- <th><?php _e('Lists', 'newsletter') ?><br><?php echo $controls->help('https://www.thenewsletterplugin.com/plugins/newsletter/newsletter-preferences') ?></th>
188
- <td>
189
- <?php $controls->preferences('list'); ?>
190
- </td>
191
- </tr>
192
- </table>
193
- </div>
194
-
195
- <div id="tabs-profile" class="tnp-tab">
196
-
197
- <table class="widefat">
198
- <thead>
199
- <tr>
200
- <th>#</th>
201
- <th><?php _e('Name', 'newsletter'); ?></th>
202
- <th><?php _e('Value', 'newsletter'); ?></th>
203
- </tr>
204
- </thead>
205
- <tbody>
206
- <?php
207
- for ($i = 1; $i <= NEWSLETTER_PROFILE_MAX; $i++) {
208
- echo '<tr><td>';
209
- echo $i;
210
- echo '</td><td>';
211
- echo esc_html($options_profile['profile_' . $i]);
212
- echo '</td><td>';
213
- $controls->text('profile_' . $i, 70);
214
- echo '</td></tr>';
215
- }
216
- ?>
217
- </tbody>
218
- </table>
219
- </div>
220
-
221
- <div id="tabs-other" class="tnp-tab">
222
-
223
- <table class="form-table">
224
- <tr>
225
- <th>ID</th>
226
- <td>
227
- <?php $controls->value('id'); ?>
228
- </td>
229
- </tr>
230
- <tr>
231
- <th><?php _e('Created', 'newsletter') ?></th>
232
- <td>
233
- <?php echo $controls->print_date(strtotime($controls->data['created'])); ?>
234
- </td>
235
- </tr>
236
- <tr>
237
- <th><?php _e('Referrer', 'newsletter') ?></th>
238
- <td>
239
- <?php echo $controls->value('referrer'); ?>
240
- </td>
241
- </tr>
242
- <tr>
243
- <th><?php _e('Last activity', 'newsletter') ?></th>
244
- <td>
245
- <?php echo $controls->print_date($controls->data['last_activity']); ?>
246
- </td>
247
- </tr>
248
- <tr>
249
- <th><?php _e('WP user ID', 'newsletter') ?></th>
250
- <td>
251
- <?php $controls->text('wp_user_id'); ?>
252
- </td>
253
- </tr>
254
- <tr>
255
- <th><?php _e('IP address', 'newsletter'); ?></th>
256
- <td>
257
- <?php $controls->value('ip'); ?>
258
- </td>
259
- </tr>
260
- <tr>
261
- <th><?php _e('Secret token', 'newsletter'); ?></th>
262
- <td>
263
- <?php $controls->text('token', 50); ?>
264
- </td>
265
- </tr>
266
- <tr>
267
- <th><?php _e('Profile URL', 'newsletter'); ?></th>
268
- <td>
269
- <?php $profile_url = NewsletterProfile::instance()->get_profile_url($user) ?>
270
- <a href='<?php echo $profile_url ?>' target="_blank"><?php echo $profile_url ?></a>
271
- </td>
272
- </tr>
273
-
274
- </table>
275
- </div>
276
- <div id="tabs-newsletters" class="tnp-tab">
277
- <?php if (!has_action('newsletter_user_newsletters_tab') && !has_action('newsletter_users_edit_newsletters')) { ?>
278
- <div class="tnp-tab-notice">
279
- This panel requires the <a href="https://www.thenewsletterplugin.com/plugins/newsletter/reports-module" target="_blank">Reports Extension 4+</a>.
280
- </div>
281
- <?php
282
- } else {
283
- do_action('newsletter_user_newsletters_tab', $id);
284
- do_action('newsletter_users_edit_newsletters', $id);
285
- }
286
- ?>
287
- </div>
288
-
289
- <div id="tabs-history" class="tnp-tab">
290
- <?php
291
- $logs = $wpdb->get_results($wpdb->prepare("select * from {$wpdb->prefix}newsletter_user_logs where user_id=%d order by id desc", $id));
292
- ?>
293
- <?php if (empty($logs)) { ?>
294
- <p>No logs available</p>
295
- <?php } else { ?>
296
- <p>Only public lists are recorded.</p>
297
- <table class="widefat" style="width: auto">
298
- <thead>
299
- <tr>
300
- <th>ID</th>
301
- <th>Date</th>
302
- <th>Source</th>
303
- <th>IP</th>
304
- <th>Lists</th>
305
- </tr>
306
-
307
- <tbody>
308
- <?php foreach ($logs as $log) { ?>
309
- <?php
310
- $data = json_decode($log->data, ARRAY_A);
311
- if (isset($data['new']))
312
- $data = $data['new'];
313
- ?>
314
- <tr>
315
- <td><?php echo $log->id ?></td>
316
- <td><?php echo $controls->print_date($log->created) ?></td>
317
- <td><?php echo esc_html($log->source) ?></td>
318
- <td><?php echo esc_html($log->ip) ?></td>
319
- <td>
320
- <?php
321
- if (is_array($data)) {
322
- foreach ($data as $key => $value) {
323
- echo esc_html(str_replace('_', ' ', $key)), ': ', esc_html($value) . '<br>';
324
- }
325
- }
326
- ?>
327
- </td>
328
- </tr>
329
- <?php } ?>
330
- </tbody>
331
-
332
- </table>
333
- <?php } ?>
334
-
335
-
336
- </div>
337
-
338
- </div>
339
-
340
- <p>
341
- <?php $controls->button_save(); ?>
342
- <?php $controls->button_delete(); ?>
343
- </p>
344
-
345
- </form>
346
- </div>
347
-
348
- <?php include NEWSLETTER_DIR . '/tnp-footer.php'; ?>
349
-
350
- </div>
1
+ <?php
2
+ /* @var $this NewsletterUsers */
3
+
4
+ defined('ABSPATH') || exit;
5
+
6
+ require_once NEWSLETTER_INCLUDES_DIR . '/controls.php';
7
+ $controls = new NewsletterControls();
8
+
9
+ $id = (int) $_GET['id'];
10
+ $user = $this->get_user($id);
11
+
12
+ if ($controls->is_action('save')) {
13
+
14
+ $email = $this->normalize_email($controls->data['email']);
15
+ if (empty($email)) {
16
+ $controls->errors = __('Wrong email address', 'newsletter');
17
+ } else {
18
+ $controls->data['email'] = $email;
19
+ }
20
+
21
+
22
+ if (empty($controls->errors)) {
23
+ $u = $this->get_user($controls->data['email']);
24
+ if ($u && $u->id != $id) {
25
+ $controls->errors = __('The email address is already in use', 'newsletter');
26
+ }
27
+ }
28
+
29
+ if (empty($controls->errors)) {
30
+ // For unselected preferences, force the zero value
31
+ for ($i = 1; $i <= NEWSLETTER_LIST_MAX; $i++) {
32
+ if (!isset($controls->data['list_' . $i])) {
33
+ $controls->data['list_' . $i] = 0;
34
+ }
35
+ }
36
+
37
+ if (empty($controls->data['token'])) {
38
+ $controls->data['token'] = $this->get_token();
39
+ }
40
+
41
+ $controls->data['id'] = $id;
42
+ $user = $this->save_user($controls->data);
43
+ $this->add_user_log($user, 'edit');
44
+ if ($user === false) {
45
+ $controls->errors = __('Error. Check the log files.', 'newsletter');
46
+ } else {
47
+ $controls->add_message_saved();
48
+ $controls->data = (array) $user;
49
+ }
50
+ }
51
+ }
52
+
53
+ if ($controls->is_action('delete')) {
54
+ $this->delete_user($id);
55
+ $controls->js_redirect($this->get_admin_page_url('index'));
56
+ return;
57
+ }
58
+
59
+ if (!$controls->is_action()) {
60
+ $controls->data = (array) $user;
61
+ }
62
+
63
+ $options_profile = NewsletterSubscription::instance()->get_options('profile');
64
+
65
+ function percent($value, $total) {
66
+ if ($total == 0) {
67
+ return '-';
68
+ }
69
+ return sprintf("%.2f", $value / $total * 100) . '%';
70
+ }
71
+
72
+ function percentValue($value, $total) {
73
+ if ($total == 0) {
74
+ return 0;
75
+ }
76
+ return round($value / $total * 100);
77
+ }
78
+ ?>
79
+
80
+ <script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
81
+ <script type="text/javascript">
82
+ google.charts.load('current', {'packages': ['corechart', 'geomap']});
83
+ </script>
84
+
85
+ <div class="wrap tnp-users tnp-users-edit" id="tnp-wrap">
86
+
87
+ <?php include NEWSLETTER_DIR . '/tnp-header.php'; ?>
88
+
89
+ <div id="tnp-heading">
90
+
91
+ <h2><?php _e('Editing', 'newsletter') ?> <?php echo esc_html($controls->data['email']) ?></h2>
92
+
93
+ </div>
94
+
95
+ <div id="tnp-body">
96
+
97
+ <form method="post" action="">
98
+ <p>
99
+ <?php $controls->button_icon_back('?page=newsletter_users_index'); ?>
100
+ <?php $controls->button_save(); ?>
101
+ </p>
102
+ <?php $controls->init(); ?>
103
+
104
+ <div id="tabs">
105
+
106
+ <ul>
107
+ <li><a href="#tabs-general"><?php _e('General', 'newsletter') ?></a></li>
108
+ <li><a href="#tabs-preferences"><?php _e('Lists', 'newsletter') ?></a></li>
109
+ <li><a href="#tabs-profile"><?php _e('Extra fields', 'newsletter') ?></a></li>
110
+ <li><a href="#tabs-other"><?php _e('Other', 'newsletter') ?></a></li>
111
+ <li><a href="#tabs-newsletters"><?php _e('Newsletters', 'newsletter') ?></a></li>
112
+ <li><a href="#tabs-history"><?php _e('Logs', 'newsletter') ?></a></li>
113
+
114
+ </ul>
115
+
116
+ <div id="tabs-general" class="tnp-tab">
117
+
118
+ <?php do_action('newsletter_users_edit_general', $id, $controls) ?>
119
+
120
+ <table class="form-table">
121
+
122
+ <tr>
123
+ <th><?php _e('Email', 'newsletter'); ?></th>
124
+ <td>
125
+ <?php $controls->text_email('email', 60); ?>
126
+ </td>
127
+ </tr>
128
+ <tr>
129
+ <th><?php _e('First name', 'newsletter'); ?></th>
130
+ <td>
131
+ <?php $controls->text('name', 50); ?>
132
+ </td>
133
+ </tr>
134
+ <tr>
135
+ <th><?php _e('Last name', 'newsletter'); ?></th>
136
+ <td>
137
+ <?php $controls->text('surname', 50); ?>
138
+ </td>
139
+ </tr>
140
+ <tr>
141
+ <th><?php _e('Gender', 'newsletter'); ?></th>
142
+ <td>
143
+ <?php $controls->select('sex', array('n' => __('Not specified', 'newsletter'), 'f' => __('Female', 'newsletter'), 'm' => __('Male', 'newsletter'))); ?>
144
+ </td>
145
+ </tr>
146
+ <tr>
147
+ <th><?php _e('Status', 'newsletter'); ?></th>
148
+ <td>
149
+ <?php
150
+ $controls->select('status', [
151
+ 'C' => TNP_User::get_status_label('C'),
152
+ 'S' => TNP_User::get_status_label('S'),
153
+ 'U' => TNP_User::get_status_label('U'),
154
+ 'B' => TNP_User::get_status_label('B'),
155
+ 'P' => TNP_User::get_status_label('P')
156
+ ]);
157
+ ?>
158
+ </td>
159
+ </tr>
160
+ <tr>
161
+ <th><?php _e('Language', 'newsletter'); ?></th>
162
+ <td>
163
+ <?php $controls->language('language', __('None', 'newsletter')); ?>
164
+ </td>
165
+ </tr>
166
+ <tr>
167
+ <th><?php _e('Test subscriber', 'newsletter'); ?>
168
+ <br><?php $controls->help('https://www.thenewsletterplugin.com/documentation/subscribers#test-subscribers') ?></th>
169
+ <td>
170
+ <?php $controls->yesno('test'); ?>
171
+ </td>
172
+ </tr>
173
+
174
+ <?php do_action('newsletter_user_edit_extra', $controls); ?>
175
+
176
+ <tr>
177
+ <th>Feed by mail</th>
178
+ <td>
179
+ <?php $controls->yesno('feed'); ?>
180
+ </td>
181
+ </tr>
182
+ </table>
183
+ </div>
184
+ <div id="tabs-preferences" class="tnp-tab">
185
+ <table class="form-table">
186
+ <tr>
187
+ <th><?php _e('Lists', 'newsletter') ?><br><?php echo $controls->help('https://www.thenewsletterplugin.com/plugins/newsletter/newsletter-preferences') ?></th>
188
+ <td>
189
+ <?php $controls->preferences('list'); ?>
190
+ </td>
191
+ </tr>
192
+ </table>
193
+ </div>
194
+
195
+ <div id="tabs-profile" class="tnp-tab">
196
+
197
+ <table class="widefat">
198
+ <thead>
199
+ <tr>
200
+ <th>#</th>
201
+ <th><?php _e('Name', 'newsletter'); ?></th>
202
+ <th><?php _e('Value', 'newsletter'); ?></th>
203
+ </tr>
204
+ </thead>
205
+ <tbody>
206
+ <?php
207
+ for ($i = 1; $i <= NEWSLETTER_PROFILE_MAX; $i++) {
208
+ echo '<tr><td>';
209
+ echo $i;
210
+ echo '</td><td>';
211
+ echo esc_html($options_profile['profile_' . $i]);
212
+ echo '</td><td>';
213
+ $controls->text('profile_' . $i, 70);
214
+ echo '</td></tr>';
215
+ }
216
+ ?>
217
+ </tbody>
218
+ </table>
219
+ </div>
220
+
221
+ <div id="tabs-other" class="tnp-tab">
222
+
223
+ <table class="form-table">
224
+ <tr>
225
+ <th>ID</th>
226
+ <td>
227
+ <?php $controls->value('id'); ?>
228
+ </td>
229
+ </tr>
230
+ <tr>
231
+ <th><?php _e('Created', 'newsletter') ?></th>
232
+ <td>
233
+ <?php echo $controls->print_date(strtotime($controls->data['created'])); ?>
234
+ </td>
235
+ </tr>
236
+ <tr>
237
+ <th><?php _e('Referrer', 'newsletter') ?></th>
238
+ <td>
239
+ <?php echo $controls->value('referrer'); ?>
240
+ </td>
241
+ </tr>
242
+ <tr>
243
+ <th><?php _e('Last activity', 'newsletter') ?></th>
244
+ <td>
245
+ <?php echo $controls->print_date($controls->data['last_activity']); ?>
246
+ </td>
247
+ </tr>
248
+ <tr>
249
+ <th><?php _e('WP user ID', 'newsletter') ?></th>
250
+ <td>
251
+ <?php $controls->text('wp_user_id'); ?>
252
+ </td>
253
+ </tr>
254
+ <tr>
255
+ <th><?php _e('IP address', 'newsletter'); ?></th>
256
+ <td>
257
+ <?php $controls->value('ip'); ?>
258
+ </td>
259
+ </tr>
260
+ <tr>
261
+ <th><?php _e('Secret token', 'newsletter'); ?></th>
262
+ <td>
263
+ <?php $controls->text('token', 50); ?>
264
+ </td>
265
+ </tr>
266
+ <tr>
267
+ <th><?php _e('Profile URL', 'newsletter'); ?></th>
268
+ <td>
269
+ <?php $profile_url = NewsletterProfile::instance()->get_profile_url($user) ?>
270
+ <a href='<?php echo $profile_url ?>' target="_blank"><?php echo $profile_url ?></a>
271
+ </td>
272
+ </tr>
273
+
274
+ </table>
275
+ </div>
276
+ <div id="tabs-newsletters" class="tnp-tab">
277
+ <?php if (!has_action('newsletter_user_newsletters_tab') && !has_action('newsletter_users_edit_newsletters')) { ?>
278
+ <div class="tnp-tab-notice">
279
+ This panel requires the <a href="https://www.thenewsletterplugin.com/plugins/newsletter/reports-module" target="_blank">Reports Extension 4+</a>.
280
+ </div>
281
+ <?php
282
+ } else {
283
+ do_action('newsletter_user_newsletters_tab', $id);
284
+ do_action('newsletter_users_edit_newsletters', $id);
285
+ }
286
+ ?>
287
+ </div>
288
+
289
+ <div id="tabs-history" class="tnp-tab">
290
+ <?php
291
+ $logs = $wpdb->get_results($wpdb->prepare("select * from {$wpdb->prefix}newsletter_user_logs where user_id=%d order by id desc", $id));
292
+ ?>
293
+ <?php if (empty($logs)) { ?>
294
+ <p>No logs available</p>
295
+ <?php } else { ?>
296
+ <p>Only public lists are recorded.</p>
297
+ <table class="widefat" style="width: auto">
298
+ <thead>
299
+ <tr>
300
+ <th>ID</th>
301
+ <th>Date</th>
302
+ <th>Source</th>
303
+ <th>IP</th>
304
+ <th>Lists</th>
305
+ </tr>
306
+
307
+ <tbody>
308
+ <?php foreach ($logs as $log) { ?>
309
+ <?php
310
+ $data = json_decode($log->data, ARRAY_A);
311
+ if (isset($data['new']))
312
+ $data = $data['new'];
313
+ ?>
314
+ <tr>
315
+ <td><?php echo $log->id ?></td>
316
+ <td><?php echo $controls->print_date($log->created) ?></td>
317
+ <td><?php echo esc_html($log->source) ?></td>
318
+ <td><?php echo esc_html($log->ip) ?></td>
319
+ <td>
320
+ <?php
321
+ if (is_array($data)) {
322
+ foreach ($data as $key => $value) {
323
+ echo esc_html(str_replace('_', ' ', $key)), ': ', esc_html($value) . '<br>';
324
+ }
325
+ }
326
+ ?>
327
+ </td>
328
+ </tr>
329
+ <?php } ?>
330
+ </tbody>
331
+
332
+ </table>
333
+ <?php } ?>
334
+
335
+
336
+ </div>
337
+
338
+ </div>
339
+
340
+ <p>
341
+ <?php $controls->button_save(); ?>
342
+ <?php $controls->button_delete(); ?>
343
+ </p>
344
+
345
+ </form>
346
+ </div>
347
+
348
+ <?php include NEWSLETTER_DIR . '/tnp-footer.php'; ?>
349
+
350
+ </div>
users/index.php CHANGED
@@ -1,251 +1,251 @@
1
- <?php
2
- /* @var $this NewsletterUsers */
3
- defined('ABSPATH') || exit;
4
-
5
- require_once NEWSLETTER_INCLUDES_DIR . '/controls.php';
6
-
7
- $controls = new NewsletterControls();
8
-
9
- $options = $controls->data;
10
- $options_profile = get_option('newsletter_profile');
11
- $options_main = get_option('newsletter_main');
12
-
13
- // Move to base zero
14
- if ($controls->is_action()) {
15
- if ($controls->is_action('reset')) {
16
- $controls->data = array();
17
- } else {
18
- $controls->data['search_page'] = (int) $controls->data['search_page'] - 1;
19
- }
20
- $this->save_options($controls->data, 'search');
21
- } else {
22
- $controls->data = $this->get_options('search');
23
- if (empty($controls->data['search_page']))
24
- $controls->data['search_page'] = 0;
25
- }
26
-
27
- if ($controls->is_action('resend')) {
28
- $user = $this->get_user($controls->button_data);
29
- NewsletterSubscription::instance()->send_message('confirmation', $user, true);
30
- $controls->messages = __('Activation email sent.', 'newsletter');
31
- }
32
-
33
- if ($controls->is_action('resend_welcome')) {
34
- $user = $this->get_user($controls->button_data);
35
- NewsletterSubscription::instance()->send_message('confirmed', $user, true);
36
- $controls->messages = __('Welcome email sent.', 'newsletter');
37
- }
38
-
39
- if ($controls->is_action('delete')) {
40
- $this->delete_user($controls->button_data);
41
- unset($controls->data['subscriber_id']);
42
- }
43
-
44
- if ($controls->is_action('delete_selected')) {
45
- $r = Newsletter::instance()->delete_user($_POST['ids']);
46
- $controls->messages .= $r . ' user(s) deleted';
47
- }
48
-
49
- // We build the query condition
50
- $where = 'where 1=1';
51
- $query_args = array();
52
- $text = trim($controls->get_value('search_text'));
53
- if ($text) {
54
- $query_args[] = '%' . $text . '%';
55
- $query_args[] = '%' . $text . '%';
56
- $query_args[] = '%' . $text . '%';
57
- $where .= " and (email like %s or name like %s or surname like %s)";
58
- }
59
-
60
- if (!empty($controls->data['search_status'])) {
61
- if ($controls->data['search_status'] == 'T') {
62
- $where .= " and test=1";
63
- } else {
64
- $query_args[] = $controls->data['search_status'];
65
- $where .= " and status=%s";
66
- }
67
- }
68
-
69
- if (!empty($controls->data['search_list'])) {
70
- $where .= " and list_" . ((int) $controls->data['search_list']) . "=1";
71
- }
72
-
73
- $filtered = $where != 'where 1=1';
74
-
75
- // Total items, total pages
76
- $items_per_page = 20;
77
- if (!empty($query_args)) {
78
- $where = $wpdb->prepare($where, $query_args);
79
- }
80
- $count = Newsletter::instance()->store->get_count(NEWSLETTER_USERS_TABLE, $where);
81
- $last_page = floor($count / $items_per_page) - ($count % $items_per_page == 0 ? 1 : 0);
82
- if ($last_page < 0)
83
- $last_page = 0;
84
-
85
- if ($controls->is_action('last')) {
86
- $controls->data['search_page'] = $last_page;
87
- }
88
- if ($controls->is_action('first')) {
89
- $controls->data['search_page'] = 0;
90
- }
91
- if ($controls->is_action('next')) {
92
- $controls->data['search_page'] = (int) $controls->data['search_page'] + 1;
93
- }
94
- if ($controls->is_action('prev')) {
95
- $controls->data['search_page'] = (int) $controls->data['search_page'] - 1;
96
- }
97
- if ($controls->is_action('search')) {
98
- $controls->data['search_page'] = 0;
99
- }
100
-
101
- // Eventually fix the page
102
- if (!isset($controls->data['search_page']) || $controls->data['search_page'] < 0)
103
- $controls->data['search_page'] = 0;
104
- if ($controls->data['search_page'] > $last_page)
105
- $controls->data['search_page'] = $last_page;
106
-
107
- $query = "select * from " . NEWSLETTER_USERS_TABLE . ' ' . $where . " order by id desc";
108
- $query .= " limit " . ($controls->data['search_page'] * $items_per_page) . "," . $items_per_page;
109
- $list = $wpdb->get_results($query);
110
-
111
- // Move to base 1
112
- $controls->data['search_page'] ++;
113
- ?>
114
-
115
- <div class="wrap tnp-users tnp-users-index" id="tnp-wrap">
116
-
117
- <?php include NEWSLETTER_DIR . '/tnp-header.php'; ?>
118
-
119
- <div id="tnp-heading">
120
-
121
- <h2><?php _e('Subscribers', 'newsletter') ?>
122
- <a class="tnp-btn-h1" href="?page=newsletter_users_new"><?php _e('Add a subscriber', 'newsletter') ?></a>
123
- </h2>
124
-
125
- <p>
126
- See the <a href="admin.php?page=newsletter_users_massive">maintenance panel</a> to move subscribers between list, massively delete and so on.
127
- </p>
128
-
129
- </div>
130
-
131
- <div id="tnp-body">
132
-
133
- <form id="channel" method="post" action="">
134
- <?php $controls->init(); ?>
135
-
136
- <div class="tnp-subscribers-search">
137
- <?php $controls->text('search_text', 45, __('Search text', 'newsletter')); ?>
138
-
139
- <?php _e('filter by', 'newsletter') ?>:
140
- <?php $controls->select('search_status', array('' => 'Any status', 'T' => 'Test subscribers', 'C' => 'Confirmed', 'S' => 'Not confirmed', 'U' => 'Unsubscribed', 'B' => 'Bounced')); ?>
141
- <?php $controls->lists_select('search_list', '-'); ?>
142
-
143
- <?php $controls->button('search', __('Search', 'newsletter')); ?>
144
- <?php if ($where != "where 1=1") { ?>
145
- <?php $controls->button('reset', __('Reset Filters', 'newsletter')); ?>
146
- <?php } ?>
147
- <br>
148
- <?php $controls->checkbox('show_preferences', __('Show lists', 'newsletter')); ?>
149
- </div>
150
-
151
- <?php if ($filtered) { ?>
152
- <p><?php _e('The list below is filtered.', 'newsletter') ?></p>
153
- <?php } ?>
154
-
155
- <div class="tnp-paginator">
156
-
157
- <?php $controls->button('first', '«'); ?>
158
- <?php $controls->button('prev', '‹'); ?>
159
- <?php $controls->text('search_page', 3); ?> of <?php echo $last_page + 1 ?> <?php $controls->button('go', __('Go', 'newsletter')); ?>
160
- <?php $controls->button('next', '›'); ?>
161
- <?php $controls->button('last', '»'); ?>
162
-
163
- <?php echo $count ?> <?php _e('subscriber(s) found', 'newsletter') ?>
164
-
165
- <?php $controls->button_confirm('delete_selected', __('Delete selected', 'newsletter')); ?>
166
-
167
- </div>
168
-
169
- <table class="widefat">
170
- <thead>
171
- <tr>
172
- <td class="check-column"><input type="checkbox" onchange="jQuery('input.tnp-selector').prop('checked', this.checked)"></th>
173
- <th>Id</th>
174
- <th>Email</th>
175
- <th><?php _e('Name', 'newsletter') ?></th>
176
- <th><?php _e('Status', 'newsletter') ?></th>
177
- <?php if (isset($options['show_preferences']) && $options['show_preferences'] == 1) { ?>
178
- <th><?php _e('Lists', 'newsletter') ?></th>
179
- <?php } ?>
180
- <th>&nbsp;</th>
181
-
182
- <th>&nbsp;</th>
183
- </tr>
184
- </thead>
185
- <?php $i = 0; ?>
186
- <?php foreach ($list as $s) { ?>
187
- <tr>
188
- <th scope="row" class="check-column" style="vertical-align: middle"><input class="tnp-selector" type="checkbox" name="ids[]" value="<?php echo $s->id; ?>"/></td>
189
- <td>
190
- <?php echo $s->id; ?>
191
- </td>
192
-
193
- <td>
194
- <?php echo esc_html($s->email); ?>
195
- </td>
196
-
197
- <td>
198
- <?php echo esc_html($s->name); ?> <?php echo esc_html($s->surname); ?>
199
- </td>
200
-
201
- <td>
202
- <small>
203
- <?php echo $this->get_user_status_label($s, true) ?>
204
- </small>
205
- </td>
206
-
207
- <?php if (isset($options['show_preferences']) && $options['show_preferences'] == 1) { ?>
208
- <td>
209
- <small>
210
- <?php
211
- $lists = $this->get_lists();
212
- foreach ($lists as $item) {
213
- $l = 'list_' . $item->id;
214
- if ($s->$l == 1)
215
- echo esc_html($item->name) . '<br>';
216
- }
217
- ?>
218
- </small>
219
- </td>
220
- <?php } ?>
221
-
222
- <td>
223
- <?php $controls->button_icon_edit($this->get_admin_page_url('edit') . '&amp;id=' . $s->id)?>
224
- </td>
225
-
226
- <td style="white-space: nowrap">
227
- <?php $controls->button_icon_delete($s->id); ?>
228
-
229
- <?php if ($s->status == "C") { ?>
230
- <?php $controls->button_icon('resend_welcome', 'fa-redo', __('Resend welcome', 'newsletter'), $s->id, true); ?>
231
- <?php } else { ?>
232
- <?php $controls->button_icon('resend', 'fa-redo', __('Resend activation', 'newsletter'), $s->id, true); ?>
233
- <?php } ?>
234
- </td>
235
-
236
- </tr>
237
- <?php } ?>
238
- </table>
239
- <div class="tnp-paginator">
240
-
241
- <?php $controls->button('first', '«'); ?>
242
- <?php $controls->button('prev', '‹'); ?>
243
- <?php $controls->button('next', '›'); ?>
244
- <?php $controls->button('last', '»'); ?>
245
- </div>
246
- </form>
247
- </div>
248
-
249
- <?php include NEWSLETTER_DIR . '/tnp-footer.php'; ?>
250
-
251
- </div>
1
+ <?php
2
+ /* @var $this NewsletterUsers */
3
+ defined('ABSPATH') || exit;
4
+
5
+ require_once NEWSLETTER_INCLUDES_DIR . '/controls.php';
6
+
7
+ $controls = new NewsletterControls();
8
+
9
+ $options = $controls->data;
10
+ $options_profile = get_option('newsletter_profile');
11
+ $options_main = get_option('newsletter_main');
12
+
13
+ // Move to base zero
14
+ if ($controls->is_action()) {
15
+ if ($controls->is_action('reset')) {
16
+ $controls->data = array();
17
+ } else {
18
+ $controls->data['search_page'] = (int) $controls->data['search_page'] - 1;
19
+ }
20
+ $this->save_options($controls->data, 'search');
21
+ } else {
22
+ $controls->data = $this->get_options('search');
23
+ if (empty($controls->data['search_page']))
24
+ $controls->data['search_page'] = 0;
25
+ }
26
+
27
+ if ($controls->is_action('resend')) {
28
+ $user = $this->get_user($controls->button_data);
29
+ NewsletterSubscription::instance()->send_message('confirmation', $user, true);
30
+ $controls->messages = __('Activation email sent.', 'newsletter');
31
+ }
32
+
33
+ if ($controls->is_action('resend_welcome')) {
34
+ $user = $this->get_user($controls->button_data);
35
+ NewsletterSubscription::instance()->send_message('confirmed', $user, true);
36
+ $controls->messages = __('Welcome email sent.', 'newsletter');
37
+ }
38
+
39
+ if ($controls->is_action('delete')) {
40
+ $this->delete_user($controls->button_data);
41
+ unset($controls->data['subscriber_id']);
42
+ }
43
+
44
+ if ($controls->is_action('delete_selected')) {
45
+ $r = Newsletter::instance()->delete_user($_POST['ids']);
46
+ $controls->messages .= $r . ' user(s) deleted';
47
+ }
48
+
49
+ // We build the query condition
50
+ $where = 'where 1=1';
51
+ $query_args = array();
52
+ $text = trim($controls->get_value('search_text'));
53
+ if ($text) {
54
+ $query_args[] = '%' . $text . '%';
55
+ $query_args[] = '%' . $text . '%';
56
+ $query_args[] = '%' . $text . '%';
57
+ $where .= " and (email like %s or name like %s or surname like %s)";
58
+ }
59
+
60
+ if (!empty($controls->data['search_status'])) {
61
+ if ($controls->data['search_status'] == 'T') {
62
+ $where .= " and test=1";
63
+ } else {
64
+ $query_args[] = $controls->data['search_status'];
65
+ $where .= " and status=%s";
66
+ }
67
+ }
68
+
69
+ if (!empty($controls->data['search_list'])) {
70
+ $where .= " and list_" . ((int) $controls->data['search_list']) . "=1";
71
+ }
72
+
73
+ $filtered = $where != 'where 1=1';
74
+
75
+ // Total items, total pages
76
+ $items_per_page = 20;
77
+ if (!empty($query_args)) {
78
+ $where = $wpdb->prepare($where, $query_args);
79
+ }
80
+ $count = Newsletter::instance()->store->get_count(NEWSLETTER_USERS_TABLE, $where);
81
+ $last_page = floor($count / $items_per_page) - ($count % $items_per_page == 0 ? 1 : 0);
82
+ if ($last_page < 0)
83
+ $last_page = 0;
84
+
85
+ if ($controls->is_action('last')) {
86
+ $controls->data['search_page'] = $last_page;
87
+ }
88
+ if ($controls->is_action('first')) {
89
+ $controls->data['search_page'] = 0;
90
+ }
91
+ if ($controls->is_action('next')) {
92
+ $controls->data['search_page'] = (int) $controls->data['search_page'] + 1;
93
+ }
94
+ if ($controls->is_action('prev')) {
95
+ $controls->data['search_page'] = (int) $controls->data['search_page'] - 1;
96
+ }
97
+ if ($controls->is_action('search')) {
98
+ $controls->data['search_page'] = 0;
99
+ }
100
+
101
+ // Eventually fix the page
102
+ if (!isset($controls->data['search_page']) || $controls->data['search_page'] < 0)
103
+ $controls->data['search_page'] = 0;
104
+ if ($controls->data['search_page'] > $last_page)
105
+ $controls->data['search_page'] = $last_page;
106
+
107
+ $query = "select * from " . NEWSLETTER_USERS_TABLE . ' ' . $where . " order by id desc";
108
+ $query .= " limit " . ($controls->data['search_page'] * $items_per_page) . "," . $items_per_page;
109
+ $list = $wpdb->get_results($query);
110
+
111
+ // Move to base 1
112
+ $controls->data['search_page'] ++;
113
+ ?>
114
+
115
+ <div class="wrap tnp-users tnp-users-index" id="tnp-wrap">
116
+
117
+ <?php include NEWSLETTER_DIR . '/tnp-header.php'; ?>
118
+
119
+ <div id="tnp-heading">
120
+
121
+ <h2><?php _e('Subscribers', 'newsletter') ?>
122
+ <a class="tnp-btn-h1" href="?page=newsletter_users_new"><?php _e('Add a subscriber', 'newsletter') ?></a>
123
+ </h2>
124
+
125
+ <p>
126
+ See the <a href="admin.php?page=newsletter_users_massive">maintenance panel</a> to move subscribers between list, massively delete and so on.
127
+ </p>
128
+
129
+ </div>
130
+
131
+ <div id="tnp-body">
132
+
133
+ <form id="channel" method="post" action="">
134
+ <?php $controls->init(); ?>
135
+
136
+ <div class="tnp-subscribers-search">
137
+ <?php $controls->text('search_text', 45, __('Search text', 'newsletter')); ?>
138
+
139
+ <?php _e('filter by', 'newsletter') ?>:
140
+ <?php $controls->select('search_status', array('' => 'Any status', 'T' => 'Test subscribers', 'C' => 'Confirmed', 'S' => 'Not confirmed', 'U' => 'Unsubscribed', 'B' => 'Bounced')); ?>
141
+ <?php $controls->lists_select('search_list', '-'); ?>
142
+
143
+ <?php $controls->button('search', __('Search', 'newsletter')); ?>
144
+ <?php if ($where != "where 1=1") { ?>
145
+ <?php $controls->button('reset', __('Reset Filters', 'newsletter')); ?>
146
+ <?php } ?>
147
+ <br>
148
+ <?php $controls->checkbox('show_preferences', __('Show lists', 'newsletter')); ?>
149
+ </div>
150
+
151
+ <?php if ($filtered) { ?>
152
+ <p><?php _e('The list below is filtered.', 'newsletter') ?></p>
153
+ <?php } ?>
154
+
155
+ <div class="tnp-paginator">
156
+
157
+ <?php $controls->button('first', '«'); ?>
158
+ <?php $controls->button('prev', '‹'); ?>
159
+ <?php $controls->text('search_page', 3); ?> of <?php echo $last_page + 1 ?> <?php $controls->button('go', __('Go', 'newsletter')); ?>
160
+ <?php $controls->button('next', '›'); ?>
161
+ <?php $controls->button('last', '»'); ?>
162
+
163
+ <?php echo $count ?> <?php _e('subscriber(s) found', 'newsletter') ?>
164
+
165
+ <?php $controls->button_confirm('delete_selected', __('Delete selected', 'newsletter')); ?>
166
+
167
+ </div>
168
+
169
+ <table class="widefat">
170
+ <thead>
171
+ <tr>
172
+ <td class="check-column"><input type="checkbox" onchange="jQuery('input.tnp-selector').prop('checked', this.checked)"></th>
173
+ <th>Id</th>
174
+ <th>Email</th>
175
+ <th><?php _e('Name', 'newsletter') ?></th>
176
+ <th><?php _e('Status', 'newsletter') ?></th>
177
+ <?php if (isset($options['show_preferences']) && $options['show_preferences'] == 1) { ?>
178
+ <th><?php _e('Lists', 'newsletter') ?></th>
179
+ <?php } ?>
180
+ <th>&nbsp;</th>
181
+
182
+ <th>&nbsp;</th>
183
+ </tr>
184
+ </thead>
185
+ <?php $i = 0; ?>
186
+ <?php foreach ($list as $s) { ?>
187
+ <tr>
188
+ <th scope="row" class="check-column" style="vertical-align: middle"><input class="tnp-selector" type="checkbox" name="ids[]" value="<?php echo $s->id; ?>"/></td>
189
+ <td>
190
+ <?php echo $s->id; ?>
191
+ </td>
192
+
193
+ <td>
194
+ <?php echo esc_html($s->email); ?>
195
+ </td>
196
+
197
+ <td>
198
+ <?php echo esc_html($s->name); ?> <?php echo esc_html($s->surname); ?>
199
+ </td>
200
+
201
+ <td>
202
+ <small>
203
+ <?php echo $this->get_user_status_label($s, true) ?>
204
+ </small>
205
+ </td>
206
+
207
+ <?php if (isset($options['show_preferences']) && $options['show_preferences'] == 1) { ?>
208
+ <td>
209
+ <small>
210
+ <?php
211
+ $lists = $this->get_lists();
212
+ foreach ($lists as $item) {
213
+ $l = 'list_' . $item->id;
214
+ if ($s->$l == 1)
215
+ echo esc_html($item->name) . '<br>';
216
+ }
217
+ ?>
218
+ </small>
219
+ </td>
220
+ <?php } ?>
221
+
222
+ <td>
223
+ <?php $controls->button_icon_edit($this->get_admin_page_url('edit') . '&amp;id=' . $s->id)?>
224
+ </td>
225
+
226
+ <td style="white-space: nowrap">
227
+ <?php $controls->button_icon_delete($s->id); ?>
228
+
229
+ <?php if ($s->status == "C") { ?>
230
+ <?php $controls->button_icon('resend_welcome', 'fa-redo', __('Resend welcome', 'newsletter'), $s->id, true); ?>
231
+ <?php } else { ?>
232
+ <?php $controls->button_icon('resend', 'fa-redo', __('Resend activation', 'newsletter'), $s->id, true); ?>
233
+ <?php } ?>
234
+ </td>
235
+
236
+ </tr>
237
+ <?php } ?>
238
+ </table>
239
+ <div class="tnp-paginator">
240
+
241
+ <?php $controls->button('first', '«'); ?>
242
+ <?php $controls->button('prev', '‹'); ?>
243
+ <?php $controls->button('next', '›'); ?>
244
+ <?php $controls->button('last', '»'); ?>
245
+ </div>
246
+ </form>
247
+ </div>
248
+
249
+ <?php include NEWSLETTER_DIR . '/tnp-footer.php'; ?>
250
+
251
+ </div>
users/massive.php CHANGED
@@ -1,296 +1,296 @@
1
- <?php
2
- /* @var $wpdb wpdb */
3
- /* @var $this NewsletterUsers */
4
-
5
- defined('ABSPATH') || exit;
6
-
7
- include_once NEWSLETTER_INCLUDES_DIR . '/controls.php';
8
-
9
- $controls = new NewsletterControls();
10
-
11
- if ($controls->is_action('remove_unconfirmed')) {
12
- $r = $wpdb->query("delete from " . NEWSLETTER_USERS_TABLE . " where status='S'");
13
- $controls->messages = __('Subscribers not confirmed deleted: ', 'newsletter') . $r . '.';
14
- }
15
-
16
- if ($controls->is_action('remove_unsubscribed')) {
17
- $r = $wpdb->query("delete from " . NEWSLETTER_USERS_TABLE . " where status='U'");
18
- $controls->messages = __('Subscribers unsubscribed deleted: ', 'newsletter') . $r . '.';
19
- }
20
-
21
- if ($controls->is_action('remove_complained')) {
22
- $r = $wpdb->query("delete from " . NEWSLETTER_USERS_TABLE . " where status='P'");
23
- $controls->messages = __('Subscribers complained deleted: ', 'newsletter') . $r . '.';
24
- }
25
-
26
- if ($controls->is_action('remove_bounced')) {
27
- $r = $wpdb->query("delete from " . NEWSLETTER_USERS_TABLE . " where status='B'");
28
- $controls->messages = __('Subscribers bounced deleted: ', 'newsletter') . $r . '.';
29
- }
30
-
31
- if ($controls->is_action('unconfirm_all')) {
32
- $r = $wpdb->query("update " . NEWSLETTER_USERS_TABLE . " set status='S' where status='C'");
33
- $controls->messages = __('Subscribers changed to not confirmed: ', 'newsletter') . $r . '.';
34
- }
35
-
36
- if ($controls->is_action('confirm_all')) {
37
- $r = $wpdb->query("update " . NEWSLETTER_USERS_TABLE . " set status='C' where status='S'");
38
- $controls->messages = __('Subscribers changed to confirmed: ', 'newsletter') . $r . '.';
39
- }
40
-
41
- if ($controls->is_action('remove_all')) {
42
- $r = $wpdb->query("delete from " . NEWSLETTER_USERS_TABLE);
43
- $controls->messages = __('Subscribers deleted: ', 'newsletter') . $r . '.';
44
- }
45
-
46
- if ($controls->is_action('list_add')) {
47
- $r = $wpdb->query("update " . NEWSLETTER_USERS_TABLE . " set list_" . ((int) $controls->data['list']) . "=1");
48
- $controls->messages = $r . ' ' . __('added to list', 'newsletter') . ' ' . $controls->data['list'];
49
- }
50
-
51
- if ($controls->is_action('list_remove')) {
52
- $r = $wpdb->query("update " . NEWSLETTER_USERS_TABLE . " set list_" . ((int) $controls->data['list']) . "=0");
53
- $controls->messages = $r . ' ' . __('removed from list', 'newsletter') . ' ' . $controls->data['list'];
54
- }
55
-
56
- if ($controls->is_action('list_delete')) {
57
- $count = $wpdb->query("delete from " . NEWSLETTER_USERS_TABLE . " where list_" . ((int) $controls->data['list']) . "<>0");
58
- $this->clean_sent_table();
59
- $this->clean_stats_table();
60
-
61
- $controls->messages = $count . ' ' . __('deleted', 'newsletter');
62
- }
63
-
64
- if ($controls->is_action('language')) {
65
- $count = $wpdb->query($wpdb->prepare("update " . NEWSLETTER_USERS_TABLE . " set language=%s where language=''", $controls->data['language']));
66
- $controls->add_message_done();
67
- }
68
-
69
- if ($controls->is_action('list_manage')) {
70
- if ($controls->data['list_action'] == 'move') {
71
- $wpdb->query("update " . NEWSLETTER_USERS_TABLE . ' set list_' . ((int) $controls->data['list_1']) . '=0, list_' . ((int) $controls->data['list_2']) . '=1' .
72
- ' where list_' . $controls->data['list_1'] . '=1');
73
- }
74
-
75
- if ($controls->data['list_action'] == 'add') {
76
- $wpdb->query("update " . NEWSLETTER_USERS_TABLE . ' set list_' . ((int) $controls->data['list_2']) . '=1' .
77
- ' where list_' . $controls->data['list_1'] . '=1');
78
- }
79
- }
80
-
81
- if ($controls->is_action('list_none')) {
82
- $where = '1=1';
83
-
84
- for ($i = 1; $i <= NEWSLETTER_LIST_MAX; $i++) {
85
- $where .= ' and list_' . $i . '=0';
86
- }
87
-
88
- $count = $wpdb->query("update " . NEWSLETTER_USERS_TABLE . ' set list_' . ((int) $controls->data['list_3']) . '=1' .
89
- ' where ' . $where);
90
- $controls->messages = $count . ' subscribers updated';
91
- }
92
-
93
- if ($controls->is_action('update_inactive')) {
94
- //Update users 'last_activity' column
95
- $wpdb->query("update `{$wpdb->prefix}newsletter` n join (select user_id, max(s.time) as max_time from `{$wpdb->prefix}newsletter_sent` s where s.open>0 group by user_id) as ss
96
- on n.id=ss.user_id set last_activity=ss.max_time");
97
-
98
- $inactive_time = (int) $controls->data['inactive_time'];
99
-
100
- $where = 'last_activity > 0 and last_activity<' . (time() - $inactive_time * 30 * 24 * 3600);
101
-
102
- $count = $wpdb->query("update " . NEWSLETTER_USERS_TABLE . ' set list_' . ((int) $controls->data['list_inactive']) . '=1 where ' . $where);
103
- $controls->messages = $count . ' subscribers updated';
104
- }
105
- ?>
106
-
107
- <div class="wrap tnp-users tnp-users-massive" id="tnp-wrap">
108
-
109
- <?php include NEWSLETTER_DIR . '/tnp-header.php'; ?>
110
-
111
- <div id="tnp-heading">
112
-
113
- <h2><?php _e('Subscribers Maintenance', 'newsletter') ?></h2>
114
- <p><?php _e('Please, backup before run a massive action.', 'newsletter') ?></p>
115
-
116
- </div>
117
-
118
- <div id="tnp-body">
119
-
120
- <?php if (!empty($results)) { ?>
121
-
122
- <h3>Results</h3>
123
-
124
- <textarea wrap="off" style="width: 100%; height: 150px; font-size: 11px; font-family: monospace"><?php echo htmlspecialchars($results) ?></textarea>
125
-
126
- <?php } ?>
127
-
128
-
129
- <form method="post" action="">
130
- <?php $controls->init(); ?>
131
-
132
- <div id="tabs">
133
- <ul>
134
- <li><a href="#tabs-1"><?php _e('General', 'newsletter') ?></a></li>
135
- <li><a href="#tabs-2"><?php _e('Lists', 'newsletter') ?></a></li>
136
- </ul>
137
-
138
- <div id="tabs-1">
139
- <table class="widefat" style="width: auto">
140
- <thead>
141
- <tr>
142
- <th><?php _e('Status', 'newsletter') ?></th>
143
- <th><?php _e('Total', 'newsletter') ?></th>
144
- <th><?php _e('Actions', 'newsletter') ?></th>
145
- </tr>
146
- </thead>
147
- <tr>
148
- <td><?php _e('Total', 'newsletter') ?></td>
149
- <td>
150
- <?php echo $wpdb->get_var("select count(*) from " . NEWSLETTER_USERS_TABLE); ?>
151
- </td>
152
- <td nowrap>
153
- <?php $controls->button_confirm('remove_all', __('Delete all', 'newsletter'), __('Are you sure you want to remove ALL subscribers?', 'newsletter')); ?>
154
- </td>
155
- </tr>
156
- <tr>
157
- <td><?php _e('Confirmed', 'newsletter') ?></td>
158
- <td>
159
- <?php echo $wpdb->get_var("select count(*) from " . NEWSLETTER_USERS_TABLE . " where status='C'"); ?>
160
- </td>
161
- <td nowrap>
162
- <?php $controls->button_confirm('unconfirm_all', __('Unconfirm all', 'newsletter')); ?>
163
- </td>
164
- </tr>
165
- <tr>
166
- <td>Not confirmed</td>
167
- <td>
168
- <?php echo $wpdb->get_var("select count(*) from " . NEWSLETTER_USERS_TABLE . " where status='S'"); ?>
169
- </td>
170
- <td nowrap>
171
- <?php $controls->button_confirm('remove_unconfirmed', __('Delete all not confirmed', 'newsletter'), __('Are you sure you want to delete ALL not confirmed subscribers?', 'newsletter')); ?>
172
- <?php $controls->button_confirm('confirm_all', __('Confirm all', 'newsletter'), __('Are you sure you want to mark ALL subscribers as confirmed?', 'newsletter')); ?>
173
- <p class="description">
174
- <a href="https://www.thenewsletterplugin.com/plugins/newsletter/subscribers-module#resend-activation" target="_blank"><?php _e('We have some tips about global actions, read more.', 'newsletter') ?></a>
175
- </p>
176
- </td>
177
- </tr>
178
- <tr>
179
- <td><?php _e('Unsubscribed', 'newsletter') ?></td>
180
- <td>
181
- <?php echo $wpdb->get_var("select count(*) from " . NEWSLETTER_USERS_TABLE . " where status='U'"); ?>
182
- </td>
183
- <td>
184
- <?php $controls->button_confirm('remove_unsubscribed', __('Delete all', 'newsletter')); ?>
185
- </td>
186
- </tr>
187
-
188
- <tr>
189
- <td><?php _e('Bounced', 'newsletter') ?></td>
190
- <td>
191
- <?php echo $wpdb->get_var("select count(*) from " . NEWSLETTER_USERS_TABLE . " where status='B'"); ?>
192
- </td>
193
- <td>
194
- <?php $controls->button_confirm('remove_bounced', __('Delete all', 'newsletter')); ?>
195
- </td>
196
- </tr>
197
-
198
- <tr>
199
- <td><?php _e('Complained', 'newsletter') ?></td>
200
- <td>
201
- <?php echo $wpdb->get_var("select count(*) from " . NEWSLETTER_USERS_TABLE . " where status='P'"); ?>
202
- </td>
203
- <td>
204
- <?php $controls->button_confirm('remove_complained', __('Delete all', 'newsletter')); ?>
205
- </td>
206
- </tr>
207
- <tr>
208
- <td>
209
- <?php _e('Inactive since', 'newsletter') ?>
210
- <?php $controls->field_help('https://www.thenewsletterplugin.com/documentation/subscribers-and-management/subscribers/#inactive')?>
211
- </td>
212
- <td>
213
- <?php
214
- $controls->select('inactive_time', array(
215
- '3' => '3 ' . __('months', 'newsletter'),
216
- '6' => '6 ' . __('months', 'newsletter'),
217
- '12' => '1 ' . __('year', 'newsletter'),
218
- '24' => '2 ' . __('years', 'newsletter'),
219
- '36' => '3 ' . __('years', 'newsletter'),
220
- '48' => '4 ' . __('years', 'newsletter'),
221
- '60' => '5 ' . __('years', 'newsletter'),
222
- '72' => '6 ' . __('years', 'newsletter'),
223
- '84' => '7 ' . __('years', 'newsletter'),
224
- '96' => '8 ' . __('years', 'newsletter'),
225
- '108' => '9 ' . __('years', 'newsletter'),
226
- '120' => '10 ' . __('years', 'newsletter')
227
- ))
228
- ?>
229
- add to
230
- <?php $controls->lists_select('list_inactive'); ?>
231
-
232
- </td>
233
- <td>
234
- <?php $controls->btn('update_inactive', __('Update', 'newsletter'), ['confirm'=>true]); ?>
235
- </td>
236
- </tr>
237
-
238
- <?php if ($this->is_multilanguage()) { ?>
239
- <tr>
240
- <td>Language</td>
241
- <td>
242
- <?php _e('Set to', 'newsletter') ?>
243
- <?php $controls->language('language', false) ?> <?php _e('subscribers without a language', 'newsletter') ?>
244
- </td>
245
- <td>
246
- <?php $controls->btn('language', '&raquo;', ['confirm'=>true]); ?>
247
- </td>
248
- </tr>
249
- <?php } ?>
250
- </table>
251
-
252
-
253
- </div>
254
-
255
-
256
- <div id="tabs-2">
257
- <table class="form-table">
258
- <tr>
259
- <th>&nbsp;</th>
260
- <td>
261
- <?php $controls->lists_select('list') ?>:
262
- <?php $controls->button_confirm('list_add', 'Activate for everyone'); ?>
263
- <?php $controls->button_confirm('list_remove', 'Deactivate for everyone'); ?>
264
- <?php $controls->button_confirm('list_delete', 'Delete everyone in that list'); ?>
265
- <br><br>
266
- <?php $controls->select('list_action', array('move' => 'Change', 'add' => 'Add')); ?>
267
- <?php _e('all subscribers in', 'newsletter') ?> <?php $controls->lists_select('list_1'); ?>
268
- <?php _e('to', 'newsletter') ?> <?php $controls->lists_select('list_2'); ?>
269
- <?php $controls->button_confirm('list_manage', '&raquo;'); ?>
270
- <p class="description">
271
- If you choose to <strong>delete</strong> users in a list, they will be
272
- <strong>physically deleted</strong> from the database (no way back).
273
- </p>
274
- </td>
275
- </tr>
276
- <tr>
277
- <th>&nbsp;</th>
278
- <td>
279
- <?php _e('Add to list', 'newsletter') ?>
280
- <?php $controls->lists_select('list_3') ?> <?php _e('subscribers without a list', 'newsletter') ?> <?php $controls->button_confirm('list_none', '&raquo;'); ?>
281
- </td>
282
- </tr>
283
-
284
-
285
-
286
- </table>
287
- </div>
288
-
289
- </div>
290
-
291
- </form>
292
- </div>
293
-
294
- <?php include NEWSLETTER_DIR . '/tnp-footer.php'; ?>
295
-
296
- </div>
1
+ <?php
2
+ /* @var $wpdb wpdb */
3
+ /* @var $this NewsletterUsers */
4
+
5
+ defined('ABSPATH') || exit;
6
+
7
+ include_once NEWSLETTER_INCLUDES_DIR . '/controls.php';
8
+
9
+ $controls = new NewsletterControls();
10
+
11
+ if ($controls->is_action('remove_unconfirmed')) {
12
+ $r = $wpdb->query("delete from " . NEWSLETTER_USERS_TABLE . " where status='S'");
13
+ $controls->messages = __('Subscribers not confirmed deleted: ', 'newsletter') . $r . '.';
14
+ }
15
+
16
+ if ($controls->is_action('remove_unsubscribed')) {
17
+ $r = $wpdb->query("delete from " . NEWSLETTER_USERS_TABLE . " where status='U'");
18
+ $controls->messages = __('Subscribers unsubscribed deleted: ', 'newsletter') . $r . '.';
19
+ }
20
+
21
+ if ($controls->is_action('remove_complained')) {
22
+ $r = $wpdb->query("delete from " . NEWSLETTER_USERS_TABLE . " where status='P'");
23
+ $controls->messages = __('Subscribers complained deleted: ', 'newsletter') . $r . '.';
24
+ }
25
+
26
+ if ($controls->is_action('remove_bounced')) {
27
+ $r = $wpdb->query("delete from " . NEWSLETTER_USERS_TABLE . " where status='B'");
28
+ $controls->messages = __('Subscribers bounced deleted: ', 'newsletter') . $r . '.';
29
+ }
30
+
31
+ if ($controls->is_action('unconfirm_all')) {
32
+ $r = $wpdb->query("update " . NEWSLETTER_USERS_TABLE . " set status='S' where status='C'");
33
+ $controls->messages = __('Subscribers changed to not confirmed: ', 'newsletter') . $r . '.';
34
+ }
35
+
36
+ if ($controls->is_action('confirm_all')) {
37
+ $r = $wpdb->query("update " . NEWSLETTER_USERS_TABLE . " set status='C' where status='S'");
38
+ $controls->messages = __('Subscribers changed to confirmed: ', 'newsletter') . $r . '.';
39
+ }
40
+
41
+ if ($controls->is_action('remove_all')) {
42
+ $r = $wpdb->query("delete from " . NEWSLETTER_USERS_TABLE);
43
+ $controls->messages = __('Subscribers deleted: ', 'newsletter') . $r . '.';
44
+ }
45
+
46
+ if ($controls->is_action('list_add')) {
47
+ $r = $wpdb->query("update " . NEWSLETTER_USERS_TABLE . " set list_" . ((int) $controls->data['list']) . "=1");
48
+ $controls->messages = $r . ' ' . __('added to list', 'newsletter') . ' ' . $controls->data['list'];
49
+ }
50
+
51
+ if ($controls->is_action('list_remove')) {
52
+ $r = $wpdb->query("update " . NEWSLETTER_USERS_TABLE . " set list_" . ((int) $controls->data['list']) . "=0");
53
+ $controls->messages = $r . ' ' . __('removed from list', 'newsletter') . ' ' . $controls->data['list'];
54
+ }
55
+
56
+ if ($controls->is_action('list_delete')) {
57
+ $count = $wpdb->query("delete from " . NEWSLETTER_USERS_TABLE . " where list_" . ((int) $controls->data['list']) . "<>0");
58
+ $this->clean_sent_table();
59
+ $this->clean_stats_table();
60
+
61
+ $controls->messages = $count . ' ' . __('deleted', 'newsletter');
62
+ }
63
+
64
+ if ($controls->is_action('language')) {
65
+ $count = $wpdb->query($wpdb->prepare("update " . NEWSLETTER_USERS_TABLE . " set language=%s where language=''", $controls->data['language']));
66
+ $controls->add_message_done();
67
+ }
68
+
69
+ if ($controls->is_action('list_manage')) {
70
+ if ($controls->data['list_action'] == 'move') {
71
+ $wpdb->query("update " . NEWSLETTER_USERS_TABLE . ' set list_' . ((int) $controls->data['list_1']) . '=0, list_' . ((int) $controls->data['list_2']) . '=1' .
72
+ ' where list_' . $controls->data['list_1'] . '=1');
73
+ }
74
+
75
+ if ($controls->data['list_action'] == 'add') {
76
+ $wpdb->query("update " . NEWSLETTER_USERS_TABLE . ' set list_' . ((int) $controls->data['list_2']) . '=1' .
77
+ ' where list_' . $controls->data['list_1'] . '=1');
78
+ }
79
+ }
80
+
81
+ if ($controls->is_action('list_none')) {
82
+ $where = '1=1';
83
+
84
+ for ($i = 1; $i <= NEWSLETTER_LIST_MAX; $i++) {
85
+ $where .= ' and list_' . $i . '=0';
86
+ }
87
+
88
+ $count = $wpdb->query("update " . NEWSLETTER_USERS_TABLE . ' set list_' . ((int) $controls->data['list_3']) . '=1' .
89
+ ' where ' . $where);
90
+ $controls->messages = $count . ' subscribers updated';
91
+ }
92
+
93
+ if ($controls->is_action('update_inactive')) {
94
+ //Update users 'last_activity' column
95
+ $wpdb->query("update `{$wpdb->prefix}newsletter` n join (select user_id, max(s.time) as max_time from `{$wpdb->prefix}newsletter_sent` s where s.open>0 group by user_id) as ss
96
+ on n.id=ss.user_id set last_activity=ss.max_time");
97
+
98
+ $inactive_time = (int) $controls->data['inactive_time'];
99
+
100
+ $where = 'last_activity > 0 and last_activity<' . (time() - $inactive_time * 30 * 24 * 3600);
101
+
102
+ $count = $wpdb->query("update " . NEWSLETTER_USERS_TABLE . ' set list_' . ((int) $controls->data['list_inactive']) . '=1 where ' . $where);
103
+ $controls->messages = $count . ' subscribers updated';
104
+ }
105
+ ?>
106
+
107
+ <div class="wrap tnp-users tnp-users-massive" id="tnp-wrap">
108
+
109
+ <?php include NEWSLETTER_DIR . '/tnp-header.php'; ?>
110
+
111
+ <div id="tnp-heading">
112
+
113
+ <h2><?php _e('Subscribers Maintenance', 'newsletter') ?></h2>
114
+ <p><?php _e('Please, backup before run a massive action.', 'newsletter') ?></p>
115
+
116
+ </div>
117
+
118
+ <div id="tnp-body">
119
+
120
+ <?php if (!empty($results)) { ?>
121
+
122
+ <h3>Results</h3>
123
+
124
+ <textarea wrap="off" style="width: 100%; height: 150px; font-size: 11px; font-family: monospace"><?php echo htmlspecialchars($results) ?></textarea>
125
+
126
+ <?php } ?>
127
+
128
+
129
+ <form method="post" action="">
130
+ <?php $controls->init(); ?>
131
+
132
+ <div id="tabs">
133
+ <ul>
134
+ <li><a href="#tabs-1"><?php _e('General', 'newsletter') ?></a></li>
135
+ <li><a href="#tabs-2"><?php _e('Lists', 'newsletter') ?></a></li>
136
+ </ul>
137
+
138
+ <div id="tabs-1">
139
+ <table class="widefat" style="width: auto">
140
+ <thead>
141
+ <tr>
142
+ <th><?php _e('Status', 'newsletter') ?></th>
143
+ <th><?php _e('Total', 'newsletter') ?></th>
144
+ <th><?php _e('Actions', 'newsletter') ?></th>
145
+ </tr>
146
+ </thead>
147
+ <tr>
148
+ <td><?php _e('Total', 'newsletter') ?></td>
149
+ <td>
150
+ <?php echo $wpdb->get_var("select count(*) from " . NEWSLETTER_USERS_TABLE); ?>
151
+ </td>
152
+ <td nowrap>
153
+ <?php $controls->button_confirm('remove_all', __('Delete all', 'newsletter'), __('Are you sure you want to remove ALL subscribers?', 'newsletter')); ?>
154
+ </td>
155
+ </tr>
156
+ <tr>
157
+ <td><?php _e('Confirmed', 'newsletter') ?></td>
158
+ <td>
159
+ <?php echo $wpdb->get_var("select count(*) from " . NEWSLETTER_USERS_TABLE . " where status='C'"); ?>
160
+ </td>
161
+ <td nowrap>
162
+ <?php $controls->button_confirm('unconfirm_all', __('Unconfirm all', 'newsletter')); ?>
163
+ </td>
164
+ </tr>
165
+ <tr>
166
+ <td>Not confirmed</td>
167
+ <td>
168
+ <?php echo $wpdb->get_var("select count(*) from " . NEWSLETTER_USERS_TABLE . " where status='S'"); ?>
169
+ </td>
170
+ <td nowrap>
171
+ <?php $controls->button_confirm('remove_unconfirmed', __('Delete all not confirmed', 'newsletter'), __('Are you sure you want to delete ALL not confirmed subscribers?', 'newsletter')); ?>
172
+ <?php $controls->button_confirm('confirm_all', __('Confirm all', 'newsletter'), __('Are you sure you want to mark ALL subscribers as confirmed?', 'newsletter')); ?>
173
+ <p class="description">
174
+ <a href="https://www.thenewsletterplugin.com/plugins/newsletter/subscribers-module#resend-activation" target="_blank"><?php _e('We have some tips about global actions, read more.', 'newsletter') ?></a>
175
+ </p>
176
+ </td>
177
+ </tr>
178
+ <tr>
179
+ <td><?php _e('Unsubscribed', 'newsletter') ?></td>
180
+ <td>
181
+ <?php echo $wpdb->get_var("select count(*) from " . NEWSLETTER_USERS_TABLE . " where status='U'"); ?>
182
+ </td>
183
+ <td>
184
+ <?php $controls->button_confirm('remove_unsubscribed', __('Delete all', 'newsletter')); ?>
185
+ </td>
186
+ </tr>
187
+
188
+ <tr>
189
+ <td><?php _e('Bounced', 'newsletter') ?></td>
190
+ <td>
191
+ <?php echo $wpdb->get_var("select count(*) from " . NEWSLETTER_USERS_TABLE . " where status='B'"); ?>
192
+ </td>
193
+ <td>
194
+ <?php $controls->button_confirm('remove_bounced', __('Delete all', 'newsletter')); ?>
195
+ </td>
196
+ </tr>
197
+
198
+ <tr>
199
+ <td><?php _e('Complained', 'newsletter') ?></td>
200
+ <td>
201
+ <?php echo $wpdb->get_var("select count(*) from " . NEWSLETTER_USERS_TABLE . " where status='P'"); ?>
202
+ </td>
203
+ <td>
204
+ <?php $controls->button_confirm('remove_complained', __('Delete all', 'newsletter')); ?>
205
+ </td>
206
+ </tr>
207
+ <tr>
208
+ <td>
209
+ <?php _e('Inactive since', 'newsletter') ?>
210
+ <?php $controls->field_help('https://www.thenewsletterplugin.com/documentation/subscribers-and-management/subscribers/#inactive')?>
211
+ </td>
212
+ <td>
213
+ <?php
214
+ $controls->select('inactive_time', array(
215
+ '3' => '3 ' . __('months', 'newsletter'),
216
+ '6' => '6 ' . __('months', 'newsletter'),
217
+ '12' => '1 ' . __('year', 'newsletter'),
218
+ '24' => '2 ' . __('years', 'newsletter'),
219
+ '36' => '3 ' . __('years', 'newsletter'),
220
+ '48' => '4 ' . __('years', 'newsletter'),
221
+ '60' => '5 ' . __('years', 'newsletter'),
222
+ '72' => '6 ' . __('years', 'newsletter'),
223
+ '84' => '7 ' . __('years', 'newsletter'),
224
+ '96' => '8 ' . __('years', 'newsletter'),
225
+ '108' => '9 ' . __('years', 'newsletter'),
226
+ '120' => '10 ' . __('years', 'newsletter')
227
+ ))
228
+ ?>
229
+ add to
230
+ <?php $controls->lists_select('list_inactive'); ?>
231
+
232
+ </td>
233
+ <td>
234
+ <?php $controls->btn('update_inactive', __('Update', 'newsletter'), ['confirm'=>true]); ?>
235
+ </td>
236
+ </tr>
237
+
238
+ <?php if ($this->is_multilanguage()) { ?>
239
+ <tr>
240
+ <td>Language</td>
241
+ <td>
242
+ <?php _e('Set to', 'newsletter') ?>
243
+ <?php $controls->language('language', false) ?> <?php _e('subscribers without a language', 'newsletter') ?>
244
+ </td>
245
+ <td>
246
+ <?php $controls->btn('language', '&raquo;', ['confirm'=>true]); ?>
247
+ </td>
248
+ </tr>
249
+ <?php } ?>
250
+ </table>
251
+
252
+
253
+ </div>
254
+
255
+
256
+ <div id="tabs-2">
257
+ <table class="form-table">
258
+ <tr>
259
+ <th>&nbsp;</th>
260
+ <td>
261
+ <?php $controls->lists_select('list') ?>:
262
+ <?php $controls->button_confirm('list_add', 'Activate for everyone'); ?>
263
+ <?php $controls->button_confirm('list_remove', 'Deactivate for everyone'); ?>
264
+ <?php $controls->button_confirm('list_delete', 'Delete everyone in that list'); ?>
265
+ <br><br>
266
+ <?php $controls->select('list_action', array('move' => 'Change', 'add' => 'Add')); ?>
267
+ <?php _e('all subscribers in', 'newsletter') ?> <?php $controls->lists_select('list_1'); ?>
268
+ <?php _e('to', 'newsletter') ?> <?php $controls->lists_select('list_2'); ?>
269
+ <?php $controls->button_confirm('list_manage', '&raquo;'); ?>
270
+ <p class="description">
271
+ If you choose to <strong>delete</strong> users in a list, they will be
272
+ <strong>physically deleted</strong> from the database (no way back).
273
+ </p>
274
+ </td>
275
+ </tr>
276
+ <tr>
277
+ <th>&nbsp;</th>
278
+ <td>
279
+ <?php _e('Add to list', 'newsletter') ?>
280
+ <?php $controls->lists_select('list_3') ?> <?php _e('subscribers without a list', 'newsletter') ?> <?php $controls->button_confirm('list_none', '&raquo;'); ?>
281
+ </td>
282
+ </tr>
283
+
284
+
285
+
286
+ </table>
287
+ </div>
288
+
289
+ </div>
290
+
291
+ </form>
292
+ </div>
293
+
294
+ <?php include NEWSLETTER_DIR . '/tnp-footer.php'; ?>
295
+
296
+ </div>
users/statistics.php CHANGED
@@ -1,313 +1,313 @@
1
- <?php
2
- /* @var $this NewsletterUsers */
3
- defined('ABSPATH') || exit;
4
-
5
-
6
- include_once NEWSLETTER_INCLUDES_DIR . '/controls.php';
7
- $controls = new NewsletterControls();
8
-
9
- wp_enqueue_script('tnp-chart');
10
-
11
- $all_count = $wpdb->get_var("select count(*) from " . NEWSLETTER_USERS_TABLE);
12
- $options_profile = get_option('newsletter_profile');
13
-
14
- ?>
15
-
16
- <script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
17
- <script type="text/javascript">
18
-
19
- google.charts.load("current", {packages: ['corechart', 'geochart', 'geomap']});
20
-
21
- </script>
22
-
23
- <div class="wrap" id="tnp-wrap">
24
-
25
- <?php include NEWSLETTER_DIR . '/tnp-header.php'; ?>
26
-
27
- <div id="tnp-heading">
28
-
29
- <h2><?php _e('Subscriber statistics', 'newsletter') ?></h2>
30
-
31
- </div>
32
-
33
- <div id="tnp-body" class="tnp-users-statistics">
34
-
35
- <?php $controls->init(); ?>
36
-
37
- <div id="tabs">
38
-
39
- <ul>
40
- <li><a href="#tab-overview">Overview</a></li>
41
- <li><a href="#tabs-lists">Lists</a></li>
42
- <li><a href="#tabs-countries">World Map</a></li>
43
- <li><a href="#tabs-referrers">Referrer</a></li>
44
- <li><a href="#tabs-sources">Sources</a></li>
45
- <li><a href="#tabs-gender">Gender</a></li>
46
- <li><a href="#tabs-time">By time</a></li>
47
- </ul>
48
-
49
- <div id="tab-overview">
50
-
51
- <table class="widefat" style="width: auto">
52
- <thead>
53
- <tr>
54
- <th><?php _e('Status', 'newsletter') ?></th>
55
- <th><?php _e('Total', 'newsletter') ?></th>
56
- </tr>
57
- </thead>
58
- <tbody>
59
- <tr>
60
- <td><?php _e('Any', 'newsletter') ?></td>
61
- <td>
62
- <?php echo $wpdb->get_var("select count(*) from " . NEWSLETTER_USERS_TABLE); ?>
63
- </td>
64
- </tr>
65
- <tr>
66
- <td><?php _e('Confirmed', 'newsletter') ?></td>
67
- <td>
68
- <?php echo $wpdb->get_var("select count(*) from " . NEWSLETTER_USERS_TABLE . " where status='C'"); ?>
69
- </td>
70
- </tr>
71
- <tr>
72
- <td><?php _e('Not confirmed', 'newsletter') ?></td>
73
- <td>
74
- <?php echo $wpdb->get_var("select count(*) from " . NEWSLETTER_USERS_TABLE . " where status='S'"); ?>
75
- </td>
76
- </tr>
77
- <tr>
78
- <td><?php _e('Unsubscribed', 'newsletter') ?></td>
79
- <td>
80
- <?php echo $wpdb->get_var("select count(*) from " . NEWSLETTER_USERS_TABLE . " where status='U'"); ?>
81
- </td>
82
- </tr>
83
- <tr>
84
- <td><?php _e('Bounced', 'newsletter') ?></td>
85
- <td>
86
- <?php echo $wpdb->get_var("select count(*) from " . NEWSLETTER_USERS_TABLE . " where status='B'"); ?>
87
- </td>
88
- </tr>
89
- <tr>
90
- <td><?php _e('Complained', 'newsletter') ?></td>
91
- <td>
92
- <?php echo $wpdb->get_var("select count(*) from " . NEWSLETTER_USERS_TABLE . " where status='P'"); ?>
93
- </td>
94
- </tr>
95
- </tbody>
96
- </table>
97
-
98
- <?php if ($this->is_multilanguage()) { ?>
99
- <h3>By language</h3>
100
- <?php $languages = $this->get_languages(); ?>
101
-
102
- <table class="widefat" style="width: auto">
103
- <thead>
104
- <tr>
105
- <th><?php _e('Status', 'newsletter') ?></th>
106
- <th><?php _e('Total', 'newsletter') ?></th>
107
- </tr>
108
- <tbody>
109
- <?php foreach ($languages as $code=>$label) { ?>
110
- <tr>
111
- <td><?php echo esc_html($label) ?></td>
112
- <td>
113
- <?php echo $wpdb->get_var($wpdb->prepare("select count(*) from " . NEWSLETTER_USERS_TABLE . " where language=%s", $code)); ?>
114
- </td>
115
- </tr>
116
- <?php } ?>
117
- <tr>
118
- <td><?php _e('Without language', 'newsletter') ?></td>
119
- <td>
120
- <?php echo $wpdb->get_var("select count(*) from " . NEWSLETTER_USERS_TABLE . " where language=''"); ?>
121
- </td>
122
- </tr>
123
- </thead>
124
- </tbody>
125
- </table>
126
- <?php } ?>
127
-
128
- </div>
129
-
130
-
131
- <div id="tabs-lists">
132
-
133
- <table class="widefat" style="width: auto">
134
- <thead>
135
- <tr>
136
- <th>&nbsp;</th>
137
- <th><?php _e('List', 'newsletter') ?></th>
138
- <th><?php _e('Total', 'newsletter') ?> (*)</th>
139
- </tr>
140
- </thead>
141
- <tbody>
142
- <?php $lists = $this->get_lists(); ?>
143
- <?php foreach ($lists as $list) { ?>
144
- <tr>
145
- <td><?php echo $list->id ?></td>
146
- <td><?php echo esc_html($list->name) ?></td>
147
- <td>
148
- <?php echo $wpdb->get_var("select count(*) from " . NEWSLETTER_USERS_TABLE . " where list_" . $list->id . "=1 and status='C'"); ?>
149
- </td>
150
- </tr>
151
- <?php } ?>
152
- </tbody>
153
- </table>
154
- (*) <?php _e('Confirmed', 'newsletter') ?>
155
- </div>
156
-
157
-
158
- <div id="tabs-countries">
159
- <?php
160
- if (!has_action('newsletter_users_statistics_countries')) {
161
- include __DIR__ . '/statistics-countries.php';
162
- } else {
163
- do_action('newsletter_users_statistics_countries', $controls);
164
- }
165
- ?>
166
- </div>
167
-
168
-
169
- <div id="tabs-referrers">
170
- <p>
171
- <?php $controls->panel_help('https://www.thenewsletterplugin.com/documentation/subscribers-statistics#referrer') ?>
172
- </p>
173
- <?php
174
- $list = $wpdb->get_results("select referrer, SUM(if(status='C', 1, 0)) as confirmed, SUM(if(status='S', 1, 0)) as unconfirmed, SUM(if(status='B', 1, 0)) as bounced, SUM(if(status='U', 1, 0)) as unsubscribed, SUM(if(status='P', 1, 0)) as complained from " . NEWSLETTER_USERS_TABLE . " group by referrer order by confirmed desc");
175
- ?>
176
- <table class="widefat" style="width: auto">
177
- <thead>
178
- <tr>
179
- <th><?php _e('Referrer', 'newsletter') ?></th>
180
- <th><?php _e('Confirmed', 'newsletter') ?></th>
181
- <th><?php _e('Not confirmed', 'newsletter') ?></th>
182
- <th><?php _e('Unsubscribed', 'newsletter') ?></th>
183
- <th><?php _e('Bounced', 'newsletter') ?></th>
184
- <th><?php _e('Complained', 'newsletter') ?></th>
185
- </tr>
186
- </thead>
187
- <tbody>
188
- <?php foreach ($list as $row) { ?>
189
- <tr>
190
- <td><?php echo empty($row->referrer) ? '[not set]' : esc_html($row->referrer) ?></td>
191
- <td><?php echo $row->confirmed; ?></td>
192
- <td><?php echo $row->unconfirmed; ?></td>
193
- <td><?php echo $row->unsubscribed; ?></td>
194
- <td><?php echo $row->bounced; ?></td>
195
- <td><?php echo $row->complained; ?></td>
196
- </tr>
197
- <?php } ?>
198
- </tbody>
199
- </table>
200
-
201
- </div>
202
-
203
-
204
- <div id="tabs-sources">
205
- <p>
206
- <?php $controls->panel_help('https://www.thenewsletterplugin.com/documentation/subscribers-statistics#source') ?>
207
- </p>
208
- <?php
209
- $list = $wpdb->get_results("select http_referer, SUM(if(status='C', 1, 0)) as confirmed, SUM(if(status='S', 1, 0)) as unconfirmed, SUM(if(status='B', 1, 0)) as bounced, SUM(if(status='U', 1, 0)) as unsubscribed from " . NEWSLETTER_USERS_TABLE . " group by http_referer order by count(*) desc limit 100");
210
- ?>
211
- <table class="widefat" style="width: auto">
212
- <thead>
213
- <tr>
214
- <th>URL</th>
215
- <th><?php _e('Confirmed', 'newsletter') ?></th>
216
- <th><?php _e('Not confirmed', 'newsletter') ?></th>
217
- <th><?php _e('Unsubscribed', 'newsletter') ?></th>
218
- <th><?php _e('Bounced', 'newsletter') ?></th>
219
- </tr>
220
- </thead>
221
- <tbody>
222
- <?php foreach ($list as $row) { ?>
223
- <tr>
224
- <td><?php echo empty($row->http_referer) ? '[not set]' : $controls->print_truncated($row->http_referer, 120) ?></td>
225
- <td><?php echo $row->confirmed; ?></td>
226
- <td><?php echo $row->unconfirmed; ?></td>
227
- <td><?php echo $row->unsubscribed; ?></td>
228
- <td><?php echo $row->bounced; ?></td>
229
- </tr>
230
- <?php } ?>
231
- </tbody>
232
- </table>
233
-
234
- </div>
235
-
236
-
237
- <div id="tabs-gender">
238
-
239
-
240
- <?php
241
- $male_count = $wpdb->get_row("select SUM(if(status='C', 1, 0)) as confirmed, SUM(if(status='S', 1, 0)) as unconfirmed, SUM(if(status='B', 1, 0)) as bounced, SUM(if(status='U', 1, 0)) as unsubscribed from " . NEWSLETTER_USERS_TABLE . " where sex='m'");
242
- $female_count = $wpdb->get_row("select SUM(if(status='C', 1, 0)) as confirmed, SUM(if(status='S', 1, 0)) as unconfirmed, SUM(if(status='B', 1, 0)) as bounced, SUM(if(status='U', 1, 0)) as unsubscribed from " . NEWSLETTER_USERS_TABLE . " where sex='f'");
243
- $none_count = $wpdb->get_row("select SUM(if(status='C', 1, 0)) as confirmed, SUM(if(status='S', 1, 0)) as unconfirmed, SUM(if(status='B', 1, 0)) as bounced, SUM(if(status='U', 1, 0)) as unsubscribed from " . NEWSLETTER_USERS_TABLE . " where sex='n'");
244
- ?>
245
-
246
- <table class="widefat">
247
- <thead>
248
- <tr>
249
- <th><?php _e('Gender', 'newsletter')?></th>
250
- <th><?php _e('Confirmed', 'newsletter') ?></th>
251
- <th><?php _e('Not confirmed', 'newsletter') ?></th>
252
- <th><?php _e('Unsubscribed', 'newsletter') ?></th>
253
- <th><?php _e('Bounced', 'newsletter') ?></th>
254
- </tr>
255
- </thead>
256
- <tbody>
257
- <tr>
258
- <td><?php _e('Female', 'newsletter')?></td>
259
- <td><?php echo $female_count->confirmed; ?></td>
260
- <td><?php echo $female_count->unconfirmed; ?></td>
261
- <td><?php echo $female_count->unsubscribed; ?></td>
262
- <td><?php echo $female_count->bounced; ?></td>
263
- </tr>
264
- <tr>
265
- <td><?php _e('Male', 'newsletter')?></td>
266
- <td><?php echo $male_count->confirmed; ?></td>
267
- <td><?php echo $male_count->unconfirmed; ?></td>
268
- <td><?php echo $male_count->unsubscribed; ?></td>
269
- <td><?php echo $male_count->bounced; ?></td>
270
- </tr>
271
- <tr>
272
- <td><?php _e('Not specified', 'newsletter')?></td>
273
- <td><?php echo $none_count->confirmed; ?></td>
274
- <td><?php echo $none_count->unconfirmed; ?></td>
275
- <td><?php echo $none_count->unsubscribed; ?></td>
276
- <td><?php echo $none_count->bounced; ?></td>
277
- </tr>
278
- </tbody>
279
- </table>
280
-
281
-
282
- </div>
283
-
284
-
285
- <div id="tabs-time">
286
-
287
- <?php
288
- if (!has_action('newsletter_users_statistics_time')) {
289
- include __DIR__ . '/statistics-time.php';
290
- } else {
291
- do_action('newsletter_users_statistics_time', $controls);
292
- }
293
- ?>
294
-
295
- </div>
296
-
297
- <?php
298
- if (isset($panels['user_statistics'])) {
299
- foreach ($panels['user_statistics'] as $panel) {
300
- call_user_func($panel['callback'], $id, $controls);
301
- }
302
- }
303
- ?>
304
- </div>
305
-
306
- </div>
307
-
308
- <?php include NEWSLETTER_DIR . '/tnp-footer.php'; ?>
309
-
310
- </div>
311
-
312
-
313
-
1
+ <?php
2
+ /* @var $this NewsletterUsers */
3
+ defined('ABSPATH') || exit;
4
+
5
+
6
+ include_once NEWSLETTER_INCLUDES_DIR . '/controls.php';
7
+ $controls = new NewsletterControls();
8
+
9
+ wp_enqueue_script('tnp-chart');
10
+
11
+ $all_count = $wpdb->get_var("select count(*) from " . NEWSLETTER_USERS_TABLE);
12
+ $options_profile = get_option('newsletter_profile');
13
+
14
+ ?>
15
+
16
+ <script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
17
+ <script type="text/javascript">
18
+
19
+ google.charts.load("current", {packages: ['corechart', 'geochart', 'geomap']});
20
+
21
+ </script>
22
+
23
+ <div class="wrap" id="tnp-wrap">
24
+
25
+ <?php include NEWSLETTER_DIR . '/tnp-header.php'; ?>
26
+
27
+ <div id="tnp-heading">
28
+
29
+ <h2><?php _e('Subscriber statistics', 'newsletter') ?></h2>
30
+
31
+ </div>
32
+
33
+ <div id="tnp-body" class="tnp-users-statistics">
34
+
35
+ <?php $controls->init(); ?>
36
+
37
+ <div id="tabs">
38
+
39
+ <ul>
40
+ <li><a href="#tab-overview">Overview</a></li>
41
+ <li><a href="#tabs-lists">Lists</a></li>
42
+ <li><a href="#tabs-countries">World Map</a></li>
43
+ <li><a href="#tabs-referrers">Referrer</a></li>
44
+ <li><a href="#tabs-sources">Sources</a></li>
45
+ <li><a href="#tabs-gender">Gender</a></li>
46
+ <li><a href="#tabs-time">By time</a></li>
47
+ </ul>
48
+
49
+ <div id="tab-overview">
50
+
51
+ <table class="widefat" style="width: auto">
52
+ <thead>
53
+ <tr>
54
+ <th><?php _e('Status', 'newsletter') ?></th>
55
+ <th><?php _e('Total', 'newsletter') ?></th>
56
+ </tr>
57
+ </thead>
58
+ <tbody>
59
+ <tr>
60
+ <td><?php _e('Any', 'newsletter') ?></td>
61
+ <td>
62
+ <?php echo $wpdb->get_var("select count(*) from " . NEWSLETTER_USERS_TABLE); ?>
63
+ </td>
64
+ </tr>
65
+ <tr>
66
+ <td><?php _e('Confirmed', 'newsletter') ?></td>
67
+ <td>
68
+ <?php echo $wpdb->get_var("select count(*) from " . NEWSLETTER_USERS_TABLE . " where status='C'"); ?>
69
+ </td>
70
+ </tr>
71
+ <tr>
72
+ <td><?php _e('Not confirmed', 'newsletter') ?></td>
73
+ <td>
74
+ <?php echo $wpdb->get_var("select count(*) from " . NEWSLETTER_USERS_TABLE . " where status='S'"); ?>
75
+ </td>
76
+ </tr>
77
+ <tr>
78
+ <td><?php _e('Unsubscribed', 'newsletter') ?></td>
79
+ <td>
80
+ <?php echo $wpdb->get_var("select count(*) from " . NEWSLETTER_USERS_TABLE . " where status='U'"); ?>
81
+ </td>
82
+ </tr>
83
+ <tr>
84
+ <td><?php _e('Bounced', 'newsletter') ?></td>
85
+ <td>
86
+ <?php echo $wpdb->get_var("select count(*) from " . NEWSLETTER_USERS_TABLE . " where status='B'"); ?>
87
+ </td>
88
+ </tr>
89
+ <tr>
90
+ <td><?php _e('Complained', 'newsletter') ?></td>
91
+ <td>
92
+ <?php echo $wpdb->get_var("select count(*) from " . NEWSLETTER_USERS_TABLE . " where status='P'"); ?>
93
+ </td>
94
+ </tr>
95
+ </tbody>
96
+ </table>
97
+
98
+ <?php if ($this->is_multilanguage()) { ?>
99
+ <h3>By language</h3>
100
+ <?php $languages = $this->get_languages(); ?>
101
+
102
+ <table class="widefat" style="width: auto">
103
+ <thead>
104
+ <tr>
105
+ <th><?php _e('Status', 'newsletter') ?></th>
106
+ <th><?php _e('Total', 'newsletter') ?></th>
107
+ </tr>
108
+ <tbody>
109
+ <?php foreach ($languages as $code=>$label) { ?>
110
+ <tr>
111
+ <td><?php echo esc_html($label) ?></td>
112
+ <td>
113
+ <?php echo $wpdb->get_var($wpdb->prepare("select count(*) from " . NEWSLETTER_USERS_TABLE . " where language=%s", $code)); ?>
114
+ </td>
115
+ </tr>
116
+ <?php } ?>
117
+ <tr>
118
+ <td><?php _e('Without language', 'newsletter') ?></td>
119
+ <td>
120
+ <?php echo $wpdb->get_var("select count(*) from " . NEWSLETTER_USERS_TABLE . " where language=''"); ?>
121
+ </td>
122
+ </tr>
123
+ </thead>
124
+ </tbody>
125
+ </table>
126
+ <?php } ?>
127
+
128
+ </div>
129
+
130
+
131
+ <div id="tabs-lists">
132
+
133
+ <table class="widefat" style="width: auto">
134
+ <thead>
135
+ <tr>
136
+ <th>&nbsp;</th>
137
+ <th><?php _e('List', 'newsletter') ?></th>
138
+ <th><?php _e('Total', 'newsletter') ?> (*)</th>
139
+ </tr>
140
+ </thead>
141
+ <tbody>
142
+ <?php $lists = $this->get_lists(); ?>
143
+ <?php foreach ($lists as $list) { ?>
144
+ <tr>
145
+ <td><?php echo $list->id ?></td>
146
+ <td><?php echo esc_html($list->name) ?></td>
147
+ <td>
148
+ <?php echo $wpdb->get_var("select count(*) from " . NEWSLETTER_USERS_TABLE . " where list_" . $list->id . "=1 and status='C'"); ?>
149
+ </td>
150
+ </tr>
151
+ <?php } ?>
152
+ </tbody>
153
+ </table>
154
+ (*) <?php _e('Confirmed', 'newsletter') ?>
155
+ </div>
156
+
157
+
158
+ <div id="tabs-countries">
159
+ <?php
160
+ if (!has_action('newsletter_users_statistics_countries')) {
161
+ include __DIR__ . '/statistics-countries.php';
162
+ } else {
163
+ do_action('newsletter_users_statistics_countries', $controls);
164
+ }
165
+ ?>
166
+ </div>
167
+
168
+
169
+ <div id="tabs-referrers">
170
+ <p>
171
+ <?php $controls->panel_help('https://www.thenewsletterplugin.com/documentation/subscribers-statistics#referrer') ?>
172
+ </p>
173
+ <?php
174
+ $list = $wpdb->get_results("select referrer, SUM(if(status='C', 1, 0)) as confirmed, SUM(if(status='S', 1, 0)) as unconfirmed, SUM(if(status='B', 1, 0)) as bounced, SUM(if(status='U', 1, 0)) as unsubscribed, SUM(if(status='P', 1, 0)) as complained from " . NEWSLETTER_USERS_TABLE . " group by referrer order by confirmed desc");
175
+ ?>
176
+ <table class="widefat" style="width: auto">
177
+ <thead>
178
+ <tr>
179
+ <th><?php _e('Referrer', 'newsletter') ?></th>
180
+ <th><?php _e('Confirmed', 'newsletter') ?></th>
181
+ <th><?php _e('Not confirmed', 'newsletter') ?></th>
182
+ <th><?php _e('Unsubscribed', 'newsletter') ?></th>
183
+ <th><?php _e('Bounced', 'newsletter') ?></th>
184
+ <th><?php _e('Complained', 'newsletter') ?></th>
185
+ </tr>
186
+ </thead>
187
+ <tbody>
188
+ <?php foreach ($list as $row) { ?>
189
+ <tr>
190
+ <td><?php echo empty($row->referrer) ? '[not set]' : esc_html($row->referrer) ?></td>
191
+ <td><?php echo $row->confirmed; ?></td>
192
+ <td><?php echo $row->unconfirmed; ?></td>
193
+ <td><?php echo $row->unsubscribed; ?></td>
194
+ <td><?php echo $row->bounced; ?></td>
195
+ <td><?php echo $row->complained; ?></td>
196
+ </tr>
197
+ <?php } ?>
198
+ </tbody>
199
+ </table>
200
+
201
+ </div>
202
+
203
+
204
+ <div id="tabs-sources">
205
+ <p>
206
+ <?php $controls->panel_help('https://www.thenewsletterplugin.com/documentation/subscribers-statistics#source') ?>
207
+ </p>
208
+ <?php
209
+ $list = $wpdb->get_results("select http_referer, SUM(if(status='C', 1, 0)) as confirmed, SUM(if(status='S', 1, 0)) as unconfirmed, SUM(if(status='B', 1, 0)) as bounced, SUM(if(status='U', 1, 0)) as unsubscribed from " . NEWSLETTER_USERS_TABLE . " group by http_referer order by count(*) desc limit 100");
210
+ ?>
211
+ <table class="widefat" style="width: auto">
212
+ <thead>
213
+ <tr>
214
+ <th>URL</th>
215
+ <th><?php _e('Confirmed', 'newsletter') ?></th>
216
+ <th><?php _e('Not confirmed', 'newsletter') ?></th>
217
+ <th><?php _e('Unsubscribed', 'newsletter') ?></th>
218
+ <th><?php _e('Bounced', 'newsletter') ?></th>
219
+ </tr>
220
+ </thead>
221
+ <tbody>
222
+ <?php foreach ($list as $row) { ?>
223
+ <tr>
224
+ <td><?php echo empty($row->http_referer) ? '[not set]' : $controls->print_truncated($row->http_referer, 120) ?></td>
225
+ <td><?php echo $row->confirmed; ?></td>
226
+ <td><?php echo $row->unconfirmed; ?></td>
227
+ <td><?php echo $row->unsubscribed; ?></td>
228
+ <td><?php echo $row->bounced; ?></td>
229
+ </tr>
230
+ <?php } ?>
231
+ </tbody>
232
+ </table>
233
+
234
+ </div>
235
+
236
+
237
+ <div id="tabs-gender">
238
+
239
+
240
+ <?php
241
+ $male_count = $wpdb->get_row("select SUM(if(status='C', 1, 0)) as confirmed, SUM(if(status='S', 1, 0)) as unconfirmed, SUM(if(status='B', 1, 0)) as bounced, SUM(if(status='U', 1, 0)) as unsubscribed from " . NEWSLETTER_USERS_TABLE . " where sex='m'");
242
+ $female_count = $wpdb->get_row("select SUM(if(status='C', 1, 0)) as confirmed, SUM(if(status='S', 1, 0)) as unconfirmed, SUM(if(status='B', 1, 0)) as bounced, SUM(if(status='U', 1, 0)) as unsubscribed from " . NEWSLETTER_USERS_TABLE . " where sex='f'");
243
+ $none_count = $wpdb->get_row("select SUM(if(status='C', 1, 0)) as confirmed, SUM(if(status='S', 1, 0)) as unconfirmed, SUM(if(status='B', 1, 0)) as bounced, SUM(if(status='U', 1, 0)) as unsubscribed from " . NEWSLETTER_USERS_TABLE . " where sex='n'");
244
+ ?>
245
+
246
+ <table class="widefat">
247
+ <thead>
248
+ <tr>
249
+ <th><?php _e('Gender', 'newsletter')?></th>
250
+ <th><?php _e('Confirmed', 'newsletter') ?></th>
251
+ <th><?php _e('Not confirmed', 'newsletter') ?></th>
252
+ <th><?php _e('Unsubscribed', 'newsletter') ?></th>
253
+ <th><?php _e('Bounced', 'newsletter') ?></th>
254
+ </tr>
255
+ </thead>
256
+ <tbody>
257
+ <tr>
258
+ <td><?php _e('Female', 'newsletter')?></td>
259
+ <td><?php echo $female_count->confirmed; ?></td>
260
+ <td><?php echo $female_count->unconfirmed; ?></td>
261
+ <td><?php echo $female_count->unsubscribed; ?></td>
262
+ <td><?php echo $female_count->bounced; ?></td>
263
+ </tr>
264
+ <tr>
265
+ <td><?php _e('Male', 'newsletter')?></td>
266
+ <td><?php echo $male_count->confirmed; ?></td>
267
+ <td><?php echo $male_count->unconfirmed; ?></td>
268
+ <td><?php echo $male_count->unsubscribed; ?></td>
269
+ <td><?php echo $male_count->bounced; ?></td>
270
+ </tr>
271
+ <tr>
272
+ <td><?php _e('Not specified', 'newsletter')?></td>
273
+ <td><?php echo $none_count->confirmed; ?></td>
274
+ <td><?php echo $none_count->unconfirmed; ?></td>
275
+ <td><?php echo $none_count->unsubscribed; ?></td>
276
+ <td><?php echo $none_count->bounced; ?></td>
277
+ </tr>
278
+ </tbody>
279
+ </table>
280
+
281
+
282
+ </div>
283
+
284
+
285
+ <div id="tabs-time">
286
+
287
+ <?php
288
+ if (!has_action('newsletter_users_statistics_time')) {
289
+ include __DIR__ . '/statistics-time.php';
290
+ } else {
291
+ do_action('newsletter_users_statistics_time', $controls);
292
+ }
293
+ ?>
294
+
295
+ </div>
296
+
297
+ <?php
298
+ if (isset($panels['user_statistics'])) {
299
+ foreach ($panels['user_statistics'] as $panel) {
300
+ call_user_func($panel['callback'], $id, $controls);
301
+ }
302
+ }
303
+ ?>
304
+ </div>
305
+
306
+ </div>
307
+
308
+ <?php include NEWSLETTER_DIR . '/tnp-footer.php'; ?>
309
+
310
+ </div>
311
+
312
+
313
+