Newsletter - Version 7.2.8

Version Description

  • Fixed the print_date() when no time is provided
  • Fixed date alignment on posts block
  • Folders reorganization
  • Social block with a new set of icons
  • Boosted performances of the lists management page (for big databases)
  • Added a new scheduler diagnostic panel
  • Seriously improved the cron statistics and diagnostic panel (check it out!)
  • Removed obsolete migration code from ancient versions
Download this release

Release Info

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

Code changes from version 7.2.7 to 7.2.8

Files changed (102) hide show
  1. admin/admin-dark.css +0 -34
  2. {css → admin}/controls.css +170 -170
  3. {css → admin}/dropdown.css +137 -137
  4. {css → admin}/extensions.css +152 -152
  5. {css → admin}/fields.css +176 -176
  6. {images → admin/images}/block-icon.png +0 -0
  7. images/header/tnp-logo-red-header.png → admin/images/logo-red.png +0 -0
  8. images/header/tnp-logo-white-header@2x1.png → admin/images/logo-white.png +0 -0
  9. {images → admin/images}/menu-icon.png +0 -0
  10. {images → admin/images}/subject/android.png +0 -0
  11. {images → admin/images}/subject/gmail.png +0 -0
  12. {images → admin/images}/subject/iphone.png +0 -0
  13. admin/modal.css +188 -189
  14. {css → admin}/tabs.css +94 -94
  15. {css → admin}/widgets.css +377 -377
  16. {css → admin}/wp-editor.css +16 -16
  17. css/controls-dark.css +0 -61
  18. emails/blocks/posts/layout-one.php +135 -135
  19. emails/blocks/posts/layout-two.php +208 -208
  20. emails/blocks/social/block.php +6 -2
  21. emails/blocks/social/options.php +13 -9
  22. emails/emails.php +1410 -1410
  23. {images → emails/images}/arrow.png +0 -0
  24. {images → emails/images}/theme-screenshot.png +0 -0
  25. emails/new.php +174 -174
  26. emails/tnp-composer/_css/newsletter-builder-v2.css +1173 -1173
  27. emails/tnp-composer/_scripts/newsletter-builder-v2.js +980 -980
  28. emails/tnp-composer/blocks/_/content-01-hero.block.php +0 -48
  29. emails/tnp-composer/blocks/_/content-01-hero.block.png +0 -0
  30. emails/tnp-composer/css/backend.css +43 -40
  31. emails/tnp-composer/css/newsletter.css +38 -91
  32. emails/tnp-composer/css/newsletter.min.css +0 -1
  33. emails/tnp-composer/index-v2.php +198 -198
  34. emails/tnp-composer/modal/attachment.php +19 -19
  35. images/animated-overlay.gif +0 -0
  36. images/cd-icon-navigation.svg +0 -50
  37. images/errors.png +0 -0
  38. images/facebook.png +0 -0
  39. images/header/debug.png +0 -0
  40. images/header/documentation.png +0 -0
  41. images/header/facebook.png +0 -0
  42. images/header/forum.png +0 -0
  43. images/header/logo.png +0 -0
  44. images/header/tnp-logo-white-header.png +0 -0
  45. images/idea.svg +0 -1
  46. images/img-1.jpg +0 -0
  47. images/img-2.jpg +0 -0
  48. images/img-3.jpg +0 -0
  49. images/img-4.jpg +0 -0
  50. images/messages.png +0 -0
  51. images/modal-background.png +0 -0
  52. images/popup/bg.png +0 -0
  53. images/popup/button.png +0 -0
  54. images/preamble.png +0 -0
  55. images/social-1/discord.png +0 -0
  56. images/social-1/facebook.png +0 -0
  57. images/social-1/instagram.png +0 -0
  58. images/social-1/linkedin.png +0 -0
  59. images/social-1/pinterest.png +0 -0
  60. images/social-1/soundcloud.png +0 -0
  61. images/social-1/telegram.png +0 -0
  62. images/social-1/tiktok.png +0 -0
  63. images/social-1/tumblr.png +0 -0
  64. images/social-1/twitch.png +0 -0
  65. images/social-1/twitter.png +0 -0
  66. images/social-1/vimeo.png +0 -0
  67. images/social-1/vk.png +0 -0
  68. images/social-1/youtube.png +0 -0
  69. images/social-2/discord.png +0 -0
  70. images/social-2/facebook.png +0 -0
  71. images/social-2/instagram.png +0 -0
  72. images/social-2/linkedin.png +0 -0
  73. images/social-2/pinterest.png +0 -0
  74. images/social-2/soundcloud.png +0 -0
  75. images/social-2/telegram.png +0 -0
  76. images/social-2/tiktok.png +0 -0
  77. images/social-2/tumblr.png +0 -0
  78. images/social-2/twitch.png +0 -0
  79. images/social-2/twitter.png +0 -0
  80. images/social-2/vimeo.png +0 -0
  81. images/social-2/vk.png +0 -0
  82. images/social-2/youtube.png +0 -0
  83. images/tnp-logo-colore-text-white@2x.png +0 -0
  84. includes/controls.php +2107 -2074
  85. includes/cron.php +36 -0
  86. includes/fields.php +745 -745
  87. includes/system.php +263 -0
  88. includes/themes.php +204 -204
  89. includes/tnp-blocks.js +252 -252
  90. {css → main/css}/dashboard.css +165 -165
  91. {css → main/css}/welcome.css +348 -348
  92. main/delivery.php +0 -1
  93. main/diagnostic.php +0 -84
  94. main/index.php +318 -318
  95. main/js/main.js +0 -178
  96. main/js/{snap.svg-min.js → welcome.js} +200 -21
  97. main/logs.php +58 -57
  98. main/scheduler.php +411 -0
  99. main/status.php +1189 -1458
  100. main/welcome.php +255 -256
  101. plugin.php +19 -97
  102. readme.txt +2 -1
admin/admin-dark.css DELETED
@@ -1,34 +0,0 @@
1
- @import "css/controls-dark.css";
2
-
3
- #tnp-body {
4
- background-color: transparent;
5
- }
6
-
7
- /* Tabs */
8
- #tnp-body #tabs p {
9
- color: #999;
10
- }
11
-
12
- #tnp-body #tabs .ui-tabs-panel {
13
- background-color: transparent;
14
- padding-left: 0 !important;
15
- }
16
-
17
- #tnp-body #tabs .ui-widget-header .ui-state-default {
18
- background-color: transparent;
19
- }
20
-
21
- #tnp-body #tabs .ui-widget-header .ui-state-default a {
22
- color: #fff;
23
- }
24
-
25
- #tnp-body #tabs .ui-widget-header .ui-state-active a {
26
- color: #fff;
27
- background-color: #28313C;
28
- }
29
-
30
- #tnp-body #tabs h3,
31
- #tnp-body #tabs h4 {
32
- color: #fff;
33
- }
34
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
{css → admin}/controls.css RENAMED
@@ -1,171 +1,171 @@
1
- /* Classes used by controls. Should be prefixed with "tnpc-" */
2
-
3
-
4
- /* Top page messages */
5
- .tnpc-error, .tnpc-warning, .tnpc-message,
6
- .tnp-error, .tnp-warning, .tnp-message {
7
- background-color: #fff;
8
- padding: 15px;
9
- margin: 15px 0;
10
- font-size: 1.2em;
11
- line-height: 1.5em;
12
- margin-left: 10px;
13
- }
14
-
15
- .tnpc-error, .tnp-error {
16
- border-left: 5px solid #dd0000;
17
- }
18
-
19
- .tnpc-warning, .tnp-warning {
20
- border-left: 5px solid #ffb900;
21
- }
22
-
23
- .tnpc-message, .tnp-message {
24
- border-left: 5px solid #46b450;
25
- }
26
-
27
-
28
- /* Structure */
29
-
30
- /* Tables with controls */
31
- #tnp-body .form-table {
32
- background-color: #fff;
33
- border: 1px solid #ECF0F1;
34
- margin-top: 2em;
35
- border-spacing: 4px;
36
- border-collapse: separate;
37
- }
38
-
39
- #tnp-body .form-table h4,
40
- #tnp-body .form-table h3 {
41
- color: #444;
42
- }
43
-
44
- /* WP has different padding for TH and TD (why???) */
45
- #tnp-body .form-table th, #tnp-body .form-table td {
46
- padding: 15px;
47
- }
48
-
49
- #tnp-body .form-table th {
50
- text-align: right;
51
- font-weight: bold;
52
- max-width: 200px;
53
- color: #000000;
54
- background-color: #ECF0F1;
55
- vertical-align: middle;
56
- }
57
-
58
- #tnp-body .form-table th small {
59
- font-weight: normal;
60
- }
61
-
62
- #tnp-body .form-table textarea {
63
- width: 100%;
64
- }
65
-
66
- /* Subtables contained in a main form table TD */
67
- #tnp-body .form-table td table {
68
- border: 0;
69
- border-collapse: collapse;
70
- }
71
-
72
- #tnp-body .form-table td table th {
73
- background-color: transparent;
74
- text-align: right;
75
- width: auto;
76
- border: 0;
77
- padding: 3px;
78
- font-weight: normal;
79
- vertical-align: middle;
80
- padding-right: 15px;
81
- }
82
-
83
- #tnp-body .form-table td table td {
84
- background-color: transparent;
85
- border: 0;
86
- padding: 3px;
87
- vertical-align: middle;
88
- }
89
-
90
- #tnp-body .form-table td table.widefat th {
91
- background-color: transparent;
92
- text-align: left;
93
- width: auto;
94
- padding: 5px;
95
- font-weight: normal;
96
- }
97
-
98
- #tnp-body .form-table td table.widefat td {
99
- background-color: transparent;
100
- padding: 5px;
101
- vertical-align: middle;
102
- }
103
-
104
- /* Fix for select 2 */
105
- #tnp-body .form-table td ul {
106
- margin-left: 0;
107
- }
108
-
109
-
110
- /* Set of checkboxes */
111
- .tnpc-checkboxes label {
112
- display: block;
113
- float: left;
114
- width: 250px;
115
- border: 1px solid #ccc;
116
- background-color: #f4f4f4;
117
- margin-bottom: 5px;
118
- padding: 5px;
119
- white-space: nowrap;
120
- margin-right: 5px;
121
- overflow: hidden;
122
- }
123
-
124
- .tnpc-lists label {
125
- display: block;
126
- padding: 5px 0;
127
- }
128
-
129
- .tnpc-lists label span {
130
- border-radius: 3px;
131
- color: #fff;
132
- background-color: #aaa;
133
- display: inline-block;
134
- padding: 1px 5px;
135
- min-width: 2em;
136
- font-size: .9em;
137
- text-align: center;
138
- }
139
-
140
-
141
- /* Hint text after a field */
142
- .tnpc-hint, p.description {
143
- margin-top: 15px;
144
- clear: both;
145
- font-size: 12px !important;
146
- color: #666;
147
- font-style: italic;
148
- }
149
-
150
- /* Color picker */
151
- .tnpc-color {
152
- width: 40px;
153
- font-size: 12px;
154
- }
155
-
156
- /* Buttons */
157
- .tnpc-button, .tnpc-button:visited, .tnpc-button:hover, .tnpc-button:active {
158
- min-width: 3em;
159
- text-align: center;
160
- color: #fff!important;
161
- width: auto;
162
- }
163
-
164
- /* Lists select with triggers */
165
- .tnpc_lists_notes {
166
- color: #999;
167
- font-size: .9em;
168
- padding: 0px 0 0 15px;
169
- border-left: 3px solid #888;
170
- margin-top: 10px;
171
  }
1
+ /* Classes used by controls. Should be prefixed with "tnpc-" */
2
+
3
+
4
+ /* Top page messages */
5
+ .tnpc-error, .tnpc-warning, .tnpc-message,
6
+ .tnp-error, .tnp-warning, .tnp-message {
7
+ background-color: #fff;
8
+ padding: 15px;
9
+ margin: 15px 0;
10
+ font-size: 1.2em;
11
+ line-height: 1.5em;
12
+ margin-left: 10px;
13
+ }
14
+
15
+ .tnpc-error, .tnp-error {
16
+ border-left: 5px solid #dd0000;
17
+ }
18
+
19
+ .tnpc-warning, .tnp-warning {
20
+ border-left: 5px solid #ffb900;
21
+ }
22
+
23
+ .tnpc-message, .tnp-message {
24
+ border-left: 5px solid #46b450;
25
+ }
26
+
27
+
28
+ /* Structure */
29
+
30
+ /* Tables with controls */
31
+ #tnp-body .form-table {
32
+ background-color: #fff;
33
+ border: 1px solid #ECF0F1;
34
+ margin-top: 2em;
35
+ border-spacing: 4px;
36
+ border-collapse: separate;
37
+ }
38
+
39
+ #tnp-body .form-table h4,
40
+ #tnp-body .form-table h3 {
41
+ color: #444;
42
+ }
43
+
44
+ /* WP has different padding for TH and TD (why???) */
45
+ #tnp-body .form-table th, #tnp-body .form-table td {
46
+ padding: 15px;
47
+ }
48
+
49
+ #tnp-body .form-table th {
50
+ text-align: right;
51
+ font-weight: bold;
52
+ max-width: 200px;
53
+ color: #000000;
54
+ background-color: #ECF0F1;
55
+ vertical-align: middle;
56
+ }
57
+
58
+ #tnp-body .form-table th small {
59
+ font-weight: normal;
60
+ }
61
+
62
+ #tnp-body .form-table textarea {
63
+ width: 100%;
64
+ }
65
+
66
+ /* Subtables contained in a main form table TD */
67
+ #tnp-body .form-table td table {
68
+ border: 0;
69
+ border-collapse: collapse;
70
+ }
71
+
72
+ #tnp-body .form-table td table th {
73
+ background-color: transparent;
74
+ text-align: right;
75
+ width: auto;
76
+ border: 0;
77
+ padding: 3px;
78
+ font-weight: normal;
79
+ vertical-align: middle;
80
+ padding-right: 15px;
81
+ }
82
+
83
+ #tnp-body .form-table td table td {
84
+ background-color: transparent;
85
+ border: 0;
86
+ padding: 3px;
87
+ vertical-align: middle;
88
+ }
89
+
90
+ #tnp-body .form-table td table.widefat th {
91
+ background-color: transparent;
92
+ text-align: left;
93
+ width: auto;
94
+ padding: 5px;
95
+ font-weight: normal;
96
+ }
97
+
98
+ #tnp-body .form-table td table.widefat td {
99
+ background-color: transparent;
100
+ padding: 5px;
101
+ vertical-align: middle;
102
+ }
103
+
104
+ /* Fix for select 2 */
105
+ #tnp-body .form-table td ul {
106
+ margin-left: 0;
107
+ }
108
+
109
+
110
+ /* Set of checkboxes */
111
+ .tnpc-checkboxes label {
112
+ display: block;
113
+ float: left;
114
+ width: 250px;
115
+ border: 1px solid #ccc;
116
+ background-color: #f4f4f4;
117
+ margin-bottom: 5px;
118
+ padding: 5px;
119
+ white-space: nowrap;
120
+ margin-right: 5px;
121
+ overflow: hidden;
122
+ }
123
+
124
+ .tnpc-lists label {
125
+ display: block;
126
+ padding: 5px 0;
127
+ }
128
+
129
+ .tnpc-lists label span {
130
+ border-radius: 3px;
131
+ color: #fff;
132
+ background-color: #aaa;
133
+ display: inline-block;
134
+ padding: 1px 5px;
135
+ min-width: 2em;
136
+ font-size: .9em;
137
+ text-align: center;
138
+ }
139
+
140
+
141
+ /* Hint text after a field */
142
+ .tnpc-hint, p.description {
143
+ margin-top: 15px;
144
+ clear: both;
145
+ font-size: 12px !important;
146
+ color: #666;
147
+ font-style: italic;
148
+ }
149
+
150
+ /* Color picker */
151
+ .tnpc-color {
152
+ width: 40px;
153
+ font-size: 12px;
154
+ }
155
+
156
+ /* Buttons */
157
+ .tnpc-button, .tnpc-button:visited, .tnpc-button:hover, .tnpc-button:active {
158
+ min-width: 3em;
159
+ text-align: center;
160
+ color: #fff!important;
161
+ width: auto;
162
+ }
163
+
164
+ /* Lists select with triggers */
165
+ .tnpc_lists_notes {
166
+ color: #999;
167
+ font-size: .9em;
168
+ padding: 0px 0 0 15px;
169
+ border-left: 3px solid #888;
170
+ margin-top: 10px;
171
  }
{css → admin}/dropdown.css RENAMED
@@ -1,138 +1,138 @@
1
- .tnp-drowpdown ul, .tnp-drowpdown li {
2
- margin: 0; padding: 0; border: 0;
3
- }
4
-
5
- .tnp-drowpdown {
6
- margin-top: 10px;
7
- -webkit-transition-timing-function: ease;
8
- padding: 5px;
9
- font-size: 12px;
10
- margin-bottom: 10px;
11
- color: #fff;
12
- }
13
-
14
- .tnp-drowpdown a {
15
- text-decoration: none;
16
- color: white;
17
- letter-spacing: 0.1em;
18
- }
19
-
20
- .tnp-drowpdown a:hover {
21
- color: #fff;
22
- }
23
-
24
- .tnp-drowpdown {
25
- -webkit-transition: width 2s;
26
- transition: width 2s;
27
- }
28
-
29
- .tnp-drowpdown ul ul {
30
- display: none;
31
- z-index: 10000;
32
- }
33
-
34
- .tnp-drowpdown ul li:hover > ul {
35
- display: block;
36
- }
37
-
38
- .tnp-drowpdown ul {
39
- padding: 0 20px;
40
- list-style: none;
41
- position: relative;
42
- display: inline-table;
43
- }
44
- .tnp-drowpdown ul:after {
45
- content: "";
46
- clear: both;
47
- display: block;
48
- }
49
-
50
- .tnp-drowpdown ul li {
51
- float: left;
52
- margin-left: 10px;
53
- text-transform: uppercase;
54
- -webkit-transition: all 0.2s ease-in-out;
55
- -moz-transition: all 0.2s ease-in-out;
56
- -o-transition: all 0.2s ease-in-out;
57
- transition: all 0.2s ease-in-out;
58
- }
59
- .tnp-drowpdown ul li:hover {
60
- background-color: #34495E;
61
- /*border: 1px solid #34495E;*/
62
- }
63
-
64
- .tnp-drowpdown ul li a {
65
- display: block;
66
- padding: 10px 10px;
67
- text-decoration: none;
68
- }
69
-
70
- .tnp-drowpdown ul li a small {
71
- display: block;
72
- color: #afafaf;
73
- float: none !important;
74
- }
75
-
76
- .tnp-drowpdown ul ul {
77
- background: #332D39;
78
- border-radius: 0px;
79
- padding: 0;
80
- position: absolute;
81
- }
82
- .tnp-drowpdown ul ul li {
83
- float: none;
84
- position: relative;
85
- margin-left: 0;
86
- border: none;
87
- text-transform: none;
88
- line-height: 1.6em;
89
- }
90
-
91
- .tnp-drowpdown ul ul li:nth-child(even) {
92
- background-color: #3b3342;
93
- }
94
-
95
- .tnp-drowpdown ul ul li:hover {
96
- background: inherit;
97
- }
98
- .tnp-drowpdown ul ul li a {
99
- padding: 5px 15px;
100
- color: #fff;
101
- }
102
- .tnp-drowpdown ul ul li a:hover {
103
- background: #34495E;
104
- }
105
-
106
- .tnp-drowpdown ul ul ul {
107
- position: absolute; left: 100%; top:0;
108
- }
109
-
110
- .tnp-professional-extensions-button {
111
- background-color: #27AE60;
112
- border: 1px solid #27AE60 !important;
113
- -webkit-transition: all 1s ease-in-out;
114
- -moz-transition: all 1s ease-in-out;
115
- -o-transition: all 1s ease-in-out;
116
- transition: all 1s ease-in-out;
117
- border-radius: 3px;
118
- }
119
-
120
- .tnp-professional-extensions-button:hover {
121
- background-color: #2ECC71 !important;
122
- border: 1px solid #2ECC71 !important;
123
- }
124
-
125
- .tnp-professional-extensions-button-red {
126
- background-color: #C0392B;
127
- border: 1px solid #C0392B !important;
128
- -webkit-transition: all 1s ease-in-out;
129
- -moz-transition: all 1s ease-in-out;
130
- -o-transition: all 1s ease-in-out;
131
- transition: all 1s ease-in-out;
132
- border-radius: 3px;
133
- }
134
-
135
- .tnp-professional-extensions-button-red:hover {
136
- background-color: #E74C3C !important;
137
- border: 1px solid #E74C3C !important;
138
  }
1
+ .tnp-drowpdown ul, .tnp-drowpdown li {
2
+ margin: 0; padding: 0; border: 0;
3
+ }
4
+
5
+ .tnp-drowpdown {
6
+ margin-top: 10px;
7
+ -webkit-transition-timing-function: ease;
8
+ padding: 5px;
9
+ font-size: 12px;
10
+ margin-bottom: 10px;
11
+ color: #fff;
12
+ }
13
+
14
+ .tnp-drowpdown a {
15
+ text-decoration: none;
16
+ color: white;
17
+ letter-spacing: 0.1em;
18
+ }
19
+
20
+ .tnp-drowpdown a:hover {
21
+ color: #fff;
22
+ }
23
+
24
+ .tnp-drowpdown {
25
+ -webkit-transition: width 2s;
26
+ transition: width 2s;
27
+ }
28
+
29
+ .tnp-drowpdown ul ul {
30
+ display: none;
31
+ z-index: 10000;
32
+ }
33
+
34
+ .tnp-drowpdown ul li:hover > ul {
35
+ display: block;
36
+ }
37
+
38
+ .tnp-drowpdown ul {
39
+ padding: 0 20px;
40
+ list-style: none;
41
+ position: relative;
42
+ display: inline-table;
43
+ }
44
+ .tnp-drowpdown ul:after {
45
+ content: "";
46
+ clear: both;
47
+ display: block;
48
+ }
49
+
50
+ .tnp-drowpdown ul li {
51
+ float: left;
52
+ margin-left: 10px;
53
+ text-transform: uppercase;
54
+ -webkit-transition: all 0.2s ease-in-out;
55
+ -moz-transition: all 0.2s ease-in-out;
56
+ -o-transition: all 0.2s ease-in-out;
57
+ transition: all 0.2s ease-in-out;
58
+ }
59
+ .tnp-drowpdown ul li:hover {
60
+ background-color: #34495E;
61
+ /*border: 1px solid #34495E;*/
62
+ }
63
+
64
+ .tnp-drowpdown ul li a {
65
+ display: block;
66
+ padding: 10px 10px;
67
+ text-decoration: none;
68
+ }
69
+
70
+ .tnp-drowpdown ul li a small {
71
+ display: block;
72
+ color: #afafaf;
73
+ float: none !important;
74
+ }
75
+
76
+ .tnp-drowpdown ul ul {
77
+ background: #332D39;
78
+ border-radius: 0px;
79
+ padding: 0;
80
+ position: absolute;
81
+ }
82
+ .tnp-drowpdown ul ul li {
83
+ float: none;
84
+ position: relative;
85
+ margin-left: 0;
86
+ border: none;
87
+ text-transform: none;
88
+ line-height: 1.6em;
89
+ }
90
+
91
+ .tnp-drowpdown ul ul li:nth-child(even) {
92
+ background-color: #3b3342;
93
+ }
94
+
95
+ .tnp-drowpdown ul ul li:hover {
96
+ background: inherit;
97
+ }
98
+ .tnp-drowpdown ul ul li a {
99
+ padding: 5px 15px;
100
+ color: #fff;
101
+ }
102
+ .tnp-drowpdown ul ul li a:hover {
103
+ background: #34495E;
104
+ }
105
+
106
+ .tnp-drowpdown ul ul ul {
107
+ position: absolute; left: 100%; top:0;
108
+ }
109
+
110
+ .tnp-professional-extensions-button {
111
+ background-color: #27AE60;
112
+ border: 1px solid #27AE60 !important;
113
+ -webkit-transition: all 1s ease-in-out;
114
+ -moz-transition: all 1s ease-in-out;
115
+ -o-transition: all 1s ease-in-out;
116
+ transition: all 1s ease-in-out;
117
+ border-radius: 3px;
118
+ }
119
+
120
+ .tnp-professional-extensions-button:hover {
121
+ background-color: #2ECC71 !important;
122
+ border: 1px solid #2ECC71 !important;
123
+ }
124
+
125
+ .tnp-professional-extensions-button-red {
126
+ background-color: #C0392B;
127
+ border: 1px solid #C0392B !important;
128
+ -webkit-transition: all 1s ease-in-out;
129
+ -moz-transition: all 1s ease-in-out;
130
+ -o-transition: all 1s ease-in-out;
131
+ transition: all 1s ease-in-out;
132
+ border-radius: 3px;
133
+ }
134
+
135
+ .tnp-professional-extensions-button-red:hover {
136
+ background-color: #E74C3C !important;
137
+ border: 1px solid #E74C3C !important;
138
  }
{css → admin}/extensions.css RENAMED
@@ -1,152 +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
- }
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
+ }
{css → admin}/fields.css RENAMED
@@ -1,176 +1,176 @@
1
-
2
-
3
- /* STRUCTURE */
4
- .tnp-field-row {
5
- clear: both;
6
- margin-left: -10px;
7
- margin-right: -10px;
8
- }
9
-
10
- .tnp-field-col-2 {
11
- width: 50%;
12
- box-sizing: border-box;
13
- padding: 10px;
14
- float: left;
15
- }
16
-
17
- .tnp-field-col-3 {
18
- width: 33%;
19
- box-sizing: border-box;
20
- padding: 10px;
21
- float: left;
22
- }
23
-
24
- .tnp-field-col-20 {
25
- width: 20%;
26
- box-sizing: border-box;
27
- padding: 10px;
28
- float: left;
29
- }
30
-
31
- .tnp-field-col-33 {
32
- width: 33%;
33
- box-sizing: border-box;
34
- padding: 10px;
35
- float: left;
36
- }
37
-
38
- .tnp-field-col-66 {
39
- width: 66%;
40
- box-sizing: border-box;
41
- padding: 10px;
42
- float: left;
43
- }
44
-
45
- .tnp-field-col-80 {
46
- width: 80%;
47
- box-sizing: border-box;
48
- padding: 10px;
49
- float: left;
50
- }
51
-
52
- .tnp-field-box {
53
- padding: 10px;
54
- background-color: #eee !important;
55
- border: 1px solid #bbb;
56
- }
57
-
58
- .tnp-section {
59
-
60
- }
61
-
62
- .tnp-field.tnp-separator {
63
- border-top: 1px solid #ddd;
64
- line-height: 0;
65
- }
66
-
67
- /* Single field container */
68
- .tnp-field {
69
- display: block;
70
- width: 100%;
71
- margin-bottom: 10px;
72
- }
73
-
74
- .tnp-field select.tnp-small {
75
- font-size: .9em;
76
- }
77
-
78
- .tnp-field label.tnp-label, .tnp-field-row label.tnp-row-label {
79
- display: block;
80
- font-size: 12px;
81
- font-weight: 300;
82
- border-bottom: 1px solid #fff;
83
- margin: 10px 0px 10px 0px;
84
- font-family: soleil, sans-serif;
85
- font-weight: 300;
86
- padding-bottom: 5px;
87
- color: #868686;
88
- }
89
-
90
- /* Compesate the negative row margin */
91
- .tnp-field-row label.tnp-row-label {
92
- margin-left: 10px;
93
- }
94
-
95
- .tnp-field.tnp-checkbox label {
96
- display: inline;
97
- }
98
-
99
- /* Set of inlined field for font, size, color... */
100
- tnp-field.tnp-font {
101
-
102
- }
103
-
104
- .tnp-field:not(.tnp-colorpicker) input {
105
- width: 100%;
106
- }
107
-
108
- .tnp-field input[type=number] {
109
- width: 100px;
110
- }
111
-
112
- .tnp-field.tnp-size input {
113
- width: 60px;
114
- display: inline;
115
- }
116
-
117
- .tnp-field .tnp-padding-fields {
118
- display: inline;
119
- }
120
-
121
- /* Four field for block padding selection */
122
- .tnp-field .tnp-padding-fields input {
123
- width: 40px;
124
- display: inline;
125
- }
126
-
127
- .tnp-field select {
128
- width: auto!important;
129
- color: #34495E;
130
- }
131
-
132
- .tnp-field input[type=url] {
133
- font-family: monospace;
134
- }
135
-
136
- .tnp-field input[type=color] {
137
- width: 40px;
138
- min-height: 30px;
139
- vertical-align: middle;
140
- }
141
-
142
- .tnp-field input[type=submit] {
143
- width: auto;
144
- }
145
-
146
- .tnp-field input[type=checkbox] {
147
- width: auto;
148
- }
149
-
150
- .tnp-field.tnpf-button .tnpf-font-family {
151
- font-size: 13px;
152
- }
153
-
154
- .tnp-field.tnpf-button .tnpf-font-size {
155
- font-size: 13px;
156
- }
157
-
158
- .tnp-field.tnpf-button .tnpf-font-weight {
159
- font-size: 13px;
160
- }
161
-
162
- .tnp-field.tnp-categories {
163
-
164
- }
165
-
166
- /* The label for each category checkbox (should override the generic label appearance) */
167
- .tnp-field.tnp-categories label {
168
-
169
- }
170
-
171
- .tnp-description {
172
- margin-top: 7px;
173
- font-style: italic;
174
- font-weight: normal;
175
- color: #999999;
176
- }
1
+
2
+
3
+ /* STRUCTURE */
4
+ .tnp-field-row {
5
+ clear: both;
6
+ margin-left: -10px;
7
+ margin-right: -10px;
8
+ }
9
+
10
+ .tnp-field-col-2 {
11
+ width: 50%;
12
+ box-sizing: border-box;
13
+ padding: 10px;
14
+ float: left;
15
+ }
16
+
17
+ .tnp-field-col-3 {
18
+ width: 33%;
19
+ box-sizing: border-box;
20
+ padding: 10px;
21
+ float: left;
22
+ }
23
+
24
+ .tnp-field-col-20 {
25
+ width: 20%;
26
+ box-sizing: border-box;
27
+ padding: 10px;
28
+ float: left;
29
+ }
30
+
31
+ .tnp-field-col-33 {
32
+ width: 33%;
33
+ box-sizing: border-box;
34
+ padding: 10px;
35
+ float: left;
36
+ }
37
+
38
+ .tnp-field-col-66 {
39
+ width: 66%;
40
+ box-sizing: border-box;
41
+ padding: 10px;
42
+ float: left;
43
+ }
44
+
45
+ .tnp-field-col-80 {
46
+ width: 80%;
47
+ box-sizing: border-box;
48
+ padding: 10px;
49
+ float: left;
50
+ }
51
+
52
+ .tnp-field-box {
53
+ padding: 10px;
54
+ background-color: #eee !important;
55
+ border: 1px solid #bbb;
56
+ }
57
+
58
+ .tnp-section {
59
+
60
+ }
61
+
62
+ .tnp-field.tnp-separator {
63
+ border-top: 1px solid #ddd;
64
+ line-height: 0;
65
+ }
66
+
67
+ /* Single field container */
68
+ .tnp-field {
69
+ display: block;
70
+ width: 100%;
71
+ margin-bottom: 10px;
72
+ }
73
+
74
+ .tnp-field select.tnp-small {
75
+ font-size: .9em;
76
+ }
77
+
78
+ .tnp-field label.tnp-label, .tnp-field-row label.tnp-row-label {
79
+ display: block;
80
+ font-size: 12px;
81
+ font-weight: 300;
82
+ border-bottom: 1px solid #fff;
83
+ margin: 10px 0px 10px 0px;
84
+ font-family: soleil, sans-serif;
85
+ font-weight: 300;
86
+ padding-bottom: 5px;
87
+ color: #868686;
88
+ }
89
+
90
+ /* Compesate the negative row margin */
91
+ .tnp-field-row label.tnp-row-label {
92
+ margin-left: 10px;
93
+ }
94
+
95
+ .tnp-field.tnp-checkbox label {
96
+ display: inline;
97
+ }
98
+
99
+ /* Set of inlined field for font, size, color... */
100
+ tnp-field.tnp-font {
101
+
102
+ }
103
+
104
+ .tnp-field:not(.tnp-colorpicker) input {
105
+ width: 100%;
106
+ }
107
+
108
+ .tnp-field input[type=number] {
109
+ width: 100px;
110
+ }
111
+
112
+ .tnp-field.tnp-size input {
113
+ width: 60px;
114
+ display: inline;
115
+ }
116
+
117
+ .tnp-field .tnp-padding-fields {
118
+ display: inline;
119
+ }
120
+
121
+ /* Four field for block padding selection */
122
+ .tnp-field .tnp-padding-fields input {
123
+ width: 40px;
124
+ display: inline;
125
+ }
126
+
127
+ .tnp-field select {
128
+ width: auto!important;
129
+ color: #34495E;
130
+ }
131
+
132
+ .tnp-field input[type=url] {
133
+ font-family: monospace;
134
+ }
135
+
136
+ .tnp-field input[type=color] {
137
+ width: 40px;
138
+ min-height: 30px;
139
+ vertical-align: middle;
140
+ }
141
+
142
+ .tnp-field input[type=submit] {
143
+ width: auto;
144
+ }
145
+
146
+ .tnp-field input[type=checkbox] {
147
+ width: auto;
148
+ }
149
+
150
+ .tnp-field.tnpf-button .tnpf-font-family {
151
+ font-size: 13px;
152
+ }
153
+
154
+ .tnp-field.tnpf-button .tnpf-font-size {
155
+ font-size: 13px;
156
+ }
157
+
158
+ .tnp-field.tnpf-button .tnpf-font-weight {
159
+ font-size: 13px;
160
+ }
161
+
162
+ .tnp-field.tnp-categories {
163
+
164
+ }
165
+
166
+ /* The label for each category checkbox (should override the generic label appearance) */
167
+ .tnp-field.tnp-categories label {
168
+
169
+ }
170
+
171
+ .tnp-description {
172
+ margin-top: 7px;
173
+ font-style: italic;
174
+ font-weight: normal;
175
+ color: #999999;
176
+ }
{images → admin/images}/block-icon.png RENAMED
File without changes
images/header/tnp-logo-red-header.png → admin/images/logo-red.png RENAMED
File without changes
images/header/tnp-logo-white-header@2x1.png → admin/images/logo-white.png RENAMED
File without changes
{images → admin/images}/menu-icon.png RENAMED
File without changes
{images → admin/images}/subject/android.png RENAMED
File without changes
{images → admin/images}/subject/gmail.png RENAMED
File without changes
{images → admin/images}/subject/iphone.png RENAMED
File without changes
admin/modal.css CHANGED
@@ -1,189 +1,188 @@
1
- .tnp-modal {
2
- display: none; /* Hidden by default */
3
- position: fixed; /* Stay in place */
4
- z-index: 100000; /* Sit on top */
5
- left: 0;
6
- top: 0;
7
- width: 100%; /* Full width */
8
- height: 100%; /* Full height */
9
- overflow: auto; /* Enable scroll if needed */
10
- }
11
-
12
- .tnp-modal.open {
13
- display: flex;
14
- align-items: center;
15
- justify-content: center;
16
- background-color: rgba(0, 0, 0, 0);
17
- animation: modal-in .4s ease-out forwards;
18
- }
19
-
20
- @keyframes modal-in {
21
- 100% {
22
- background-color: rgba(0, 0, 0, 0.3);
23
- }
24
- }
25
-
26
- .tnp-modal.on-close {
27
- background-color: rgba(0, 0, 0, 0);
28
- transition: background-color .4s ease-out;
29
- }
30
-
31
- .tnp-modal-container {
32
- position: relative;
33
-
34
- padding: 50px;
35
- border-radius: 3px;
36
- background-color: white;
37
- width: 400px;
38
- min-height: 100px;
39
- max-height: 400px;
40
- overflow-y: auto;
41
- animation: modal-fadein .4s ease-out forwards;
42
- }
43
-
44
- .tnp-modal-container.on-close {
45
- animation: modal-fadeout .4s ease-in forwards;
46
- }
47
-
48
- @keyframes modal-fadein {
49
- 0% {
50
- opacity: 0;
51
- transform: translateY(-100%);
52
- }
53
-
54
- 100% {
55
- opacity: 1;
56
- transform: translateY(0);
57
- }
58
- }
59
-
60
- @keyframes modal-fadeout {
61
- 0% {
62
- opacity: 1;
63
- transform: translateY(0);
64
- }
65
-
66
- 20% {
67
- transform: translateY(50px);
68
- }
69
-
70
- 100% {
71
- opacity: 0;
72
- transform: translateY(-100%);
73
- }
74
- }
75
-
76
- .tnp-modal-close {
77
- position: absolute;
78
- cursor: pointer;
79
- right: 0;
80
- top: 0;
81
- font-weight: bold;
82
- padding: 10px;
83
- font-size: 40px;
84
- }
85
-
86
- .tnp-modal-confirm {
87
- margin: 10px 0 0 0;
88
- }
89
-
90
- .tnp-modal-confirm button {
91
- float: right;
92
- }
93
-
94
- .tnp-modal .button-danger {
95
- background-color: darkred;
96
- color: #ffffff;
97
- text-shadow: none;
98
- width: auto;
99
- }
100
-
101
- .tnp-modal .button-danger:hover {
102
- background-color: darkred;
103
- }
104
-
105
-
106
- /* TNP MODAL 2 */
107
- .tnp-modal2 {
108
- display: none; /* Hidden by default */
109
- position: fixed; /* Stay in place */
110
- z-index: 100000; /* Sit on top */
111
- left: 0;
112
- top: 0;
113
- width: 100%; /* Full width */
114
- height: 100%; /* Full height */
115
- overflow: auto; /* Enable scroll if needed */
116
- }
117
-
118
- .tnp-modal2.open {
119
- display: flex;
120
- align-items: center;
121
- justify-content: center;
122
- align-items: center;
123
- justify-content: center;
124
- background-color: rgba(0, 0, 0, 0);
125
- animation: modal-in .4s ease-out forwards;
126
- }
127
-
128
- .tnp-modal2__content {
129
- position: relative;
130
- display: flex;
131
- flex-direction: column;
132
- border-radius: 3px;
133
- background-color: white;
134
- width: 400px;
135
- min-height: 100px;
136
- max-height: 400px;
137
- overflow-y: hidden;
138
- animation: modal-fadein .4s ease-out forwards;
139
- }
140
-
141
- .tnp-modal2__header {
142
- padding: 20px 50px;
143
- background-color: #ECF0F1;
144
- position: relative;
145
- text-align: center;
146
- }
147
-
148
- .tnp-modal2__header h2{
149
- margin: 0;
150
- }
151
-
152
- .tnp-modal2__body {
153
- overflow-y: auto;
154
- padding: 2rem;
155
- flex: 1 1 auto;
156
- }
157
-
158
- .tnp-modal2__close {
159
- position: absolute;
160
- cursor: pointer;
161
- right: 0;
162
- top: 50%;
163
- transform: translateY(-50%);
164
- font-weight: bold;
165
- padding: 10px;
166
- font-size: 40px;
167
- }
168
-
169
- .tnp-modal2__close:before {
170
- content: '×';
171
- }
172
-
173
- .tnp-modal2.on-close {
174
- background-color: rgba(0, 0, 0, 0);
175
- transition: background-color .4s ease-out;
176
- }
177
-
178
- .tnp-modal2.on-close .tnp-modal2__content {
179
- animation: modal-fadeout .4s ease-in forwards;
180
- }
181
-
182
- .tnp-modal2__body .row {
183
- display: flex;
184
- }
185
-
186
- .tnp-modal2__body .col {
187
- flex: 1;
188
- }
189
-
1
+ .tnp-modal {
2
+ display: none;
3
+ position: fixed;
4
+ z-index: 100000;
5
+ left: 0;
6
+ top: 0;
7
+ width: 100%;
8
+ height: 100%;
9
+ overflow: auto;
10
+ }
11
+
12
+ .tnp-modal.open {
13
+ display: flex;
14
+ align-items: center;
15
+ justify-content: center;
16
+ background-color: rgba(0, 0, 0, 0);
17
+ animation: modal-in .4s ease-out forwards;
18
+ }
19
+
20
+ @keyframes modal-in {
21
+ 100% {
22
+ background-color: rgba(0, 0, 0, 0.3);
23
+ }
24
+ }
25
+
26
+ .tnp-modal.on-close {
27
+ background-color: rgba(0, 0, 0, 0);
28
+ transition: background-color .4s ease-out;
29
+ }
30
+
31
+ .tnp-modal-container {
32
+ position: relative;
33
+ padding: 50px;
34
+ border-radius: 3px;
35
+ background-color: white;
36
+ width: 400px;
37
+ min-height: 100px;
38
+ max-height: 400px;
39
+ overflow-y: auto;
40
+ animation: modal-fadein .4s ease-out forwards;
41
+ }
42
+
43
+ .tnp-modal-container.on-close {
44
+ animation: modal-fadeout .4s ease-in forwards;
45
+ }
46
+
47
+ @keyframes modal-fadein {
48
+ 0% {
49
+ opacity: 0;
50
+ transform: translateY(-100%);
51
+ }
52
+
53
+ 100% {
54
+ opacity: 1;
55
+ transform: translateY(0);
56
+ }
57
+ }
58
+
59
+ @keyframes modal-fadeout {
60
+ 0% {
61
+ opacity: 1;
62
+ transform: translateY(0);
63
+ }
64
+
65
+ 20% {
66
+ transform: translateY(50px);
67
+ }
68
+
69
+ 100% {
70
+ opacity: 0;
71
+ transform: translateY(-100%);
72
+ }
73
+ }
74
+
75
+ .tnp-modal-close {
76
+ position: absolute;
77
+ cursor: pointer;
78
+ right: 0;
79
+ top: 0;
80
+ font-weight: bold;
81
+ padding: 10px;
82
+ font-size: 40px;
83
+ }
84
+
85
+ .tnp-modal-confirm {
86
+ margin: 10px 0 0 0;
87
+ }
88
+
89
+ .tnp-modal-confirm button {
90
+ float: right;
91
+ }
92
+
93
+ .tnp-modal .button-danger {
94
+ background-color: darkred;
95
+ color: #ffffff;
96
+ text-shadow: none;
97
+ width: auto;
98
+ }
99
+
100
+ .tnp-modal .button-danger:hover {
101
+ background-color: darkred;
102
+ }
103
+
104
+
105
+ /* TNP MODAL 2 */
106
+ .tnp-modal2 {
107
+ display: none;
108
+ position: fixed;
109
+ z-index: 100000;
110
+ left: 0;
111
+ top: 0;
112
+ width: 100%;
113
+ height: 100%;
114
+ overflow: auto;
115
+ }
116
+
117
+ .tnp-modal2.open {
118
+ display: flex;
119
+ align-items: center;
120
+ justify-content: center;
121
+ align-items: center;
122
+ justify-content: center;
123
+ background-color: rgba(0, 0, 0, 0);
124
+ animation: modal-in .4s ease-out forwards;
125
+ }
126
+
127
+ .tnp-modal2__content {
128
+ position: relative;
129
+ display: flex;
130
+ flex-direction: column;
131
+ border-radius: 3px;
132
+ background-color: white;
133
+ width: 400px;
134
+ min-height: 100px;
135
+ max-height: 400px;
136
+ overflow-y: hidden;
137
+ animation: modal-fadein .4s ease-out forwards;
138
+ }
139
+
140
+ .tnp-modal2__header {
141
+ padding: 20px 50px;
142
+ background-color: #ECF0F1!important;
143
+ position: relative;
144
+ text-align: center;
145
+ }
146
+
147
+ .tnp-modal2__header h2{
148
+ margin: 0;
149
+ }
150
+
151
+ .tnp-modal2__body {
152
+ overflow-y: auto;
153
+ padding: 2rem;
154
+ flex: 1 1 auto;
155
+ }
156
+
157
+ .tnp-modal2__close {
158
+ position: absolute;
159
+ cursor: pointer;
160
+ right: 0;
161
+ top: 50%;
162
+ transform: translateY(-50%);
163
+ font-weight: bold;
164
+ padding: 10px;
165
+ font-size: 40px;
166
+ }
167
+
168
+ .tnp-modal2__close:before {
169
+ content: '×';
170
+ }
171
+
172
+ .tnp-modal2.on-close {
173
+ background-color: rgba(0, 0, 0, 0);
174
+ transition: background-color .4s ease-out;
175
+ }
176
+
177
+ .tnp-modal2.on-close .tnp-modal2__content {
178
+ animation: modal-fadeout .4s ease-in forwards;
179
+ }
180
+
181
+ .tnp-modal2__body .row {
182
+ display: flex;
183
+ }
184
+
185
+ .tnp-modal2__body .col {
186
+ flex: 1;
187
+ }
188
+
 
{css → admin}/tabs.css RENAMED
@@ -1,94 +1,94 @@
1
- /* Since #tabs has a white background, all elements should be made dark */
2
-
3
- #tnp-body #tabs h3,
4
- #tnp-body #tabs h4,
5
- #tnp-body #tabs p,
6
- #tnp-body #tabs li,
7
- #tnp-body #tabs input,
8
- #tnp-body #tabs select,
9
- #tnp-body #tabs textarea {
10
- color: #444;
11
- }
12
-
13
- #tnp-body td .tnp-tabs {
14
-
15
- }
16
-
17
- #tnp-body td .tnp-tabs .ui-widget-header {
18
- background-color: #ddd;
19
- }
20
-
21
- #tnp-body td .tnp-tabs .ui-tabs-anchor {
22
- color: #000;
23
- }
24
-
25
- #tnp-body .ui-widget {
26
- font-family: soleil, sans-serif;
27
- }
28
-
29
- #tnp-body #tabs {
30
- background-color: transparent;
31
- border: 0!important;
32
- padding: 0;
33
- margin: 0;
34
- }
35
-
36
- #tnp-body #tabs .ui-widget-header {
37
- background: #28313C;
38
- border: 0;
39
- }
40
-
41
- #tnp-body #tabs .ui-tabs-panel {
42
- padding: 15px!important;
43
- background-color: #fff;
44
- }
45
-
46
- #tnp-body #tabs a.ui-tabs-anchor,
47
- #tnp-body #tabs a.ui-tabs-anchor:visited {
48
- color: #444;
49
- }
50
-
51
- #tnp-body .ui-tabs .ui-tabs-nav {
52
- padding: 0;
53
- margin: 0;
54
- }
55
-
56
- #tnp-body .ui-tabs .ui-tabs-nav li a {
57
- font-size: 14px;
58
- }
59
-
60
- #tnp-body #tabs .ui-tabs {
61
- border-color: #28313C;
62
- background-color: #28313C;
63
- border: 0;
64
- }
65
-
66
- #tnp-body #tabs .ui-tabs .ui-tabs-nav {
67
- margin: 0;
68
- padding: .2em .2em 0 0;
69
- background-color: #f2f2f2;
70
- }
71
-
72
- #tnp-body #tabs .ui-tabs .ui-tabs-panel {
73
- padding: 1em 0;
74
- background-color: #f2f2f2;
75
- margin: 0;
76
- border: 0;
77
- }
78
-
79
- #tnp-body .ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active {
80
- background: #fff !important;
81
- font-weight: normal;
82
- font-family: soleil, sans-serif;
83
- }
84
-
85
-
86
- #tnp-body .ui-widget-content {
87
- background: #fff;
88
- }
89
-
90
- #tnp-body .ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default {
91
- border: none;
92
- background: #ECF0F1;
93
- font-family: soleil, sans-serif;
94
- }
1
+ /* Since #tabs has a white background, all elements should be made dark */
2
+
3
+ #tnp-body #tabs h3,
4
+ #tnp-body #tabs h4,
5
+ #tnp-body #tabs p,
6
+ #tnp-body #tabs li,
7
+ #tnp-body #tabs input,
8
+ #tnp-body #tabs select,
9
+ #tnp-body #tabs textarea {
10
+ color: #444;
11
+ }
12
+
13
+ #tnp-body td .tnp-tabs {
14
+
15
+ }
16
+
17
+ #tnp-body td .tnp-tabs .ui-widget-header {
18
+ background-color: #ddd;
19
+ }
20
+
21
+ #tnp-body td .tnp-tabs .ui-tabs-anchor {
22
+ color: #000;
23
+ }
24
+
25
+ #tnp-body .ui-widget {
26
+ font-family: soleil, sans-serif;
27
+ }
28
+
29
+ #tnp-body #tabs {
30
+ background-color: transparent;
31
+ border: 0!important;
32
+ padding: 0;
33
+ margin: 0;
34
+ }
35
+
36
+ #tnp-body #tabs .ui-widget-header {
37
+ background: #28313C;
38
+ border: 0;
39
+ }
40
+
41
+ #tnp-body #tabs .ui-tabs-panel {
42
+ padding: 15px!important;
43
+ background-color: #fff;
44
+ }
45
+
46
+ #tnp-body #tabs a.ui-tabs-anchor,
47
+ #tnp-body #tabs a.ui-tabs-anchor:visited {
48
+ color: #444;
49
+ }
50
+
51
+ #tnp-body .ui-tabs .ui-tabs-nav {
52
+ padding: 0;
53
+ margin: 0;
54
+ }
55
+
56
+ #tnp-body .ui-tabs .ui-tabs-nav li a {
57
+ font-size: 14px;
58
+ }
59
+
60
+ #tnp-body #tabs .ui-tabs {
61
+ border-color: #28313C;
62
+ background-color: #28313C;
63
+ border: 0;
64
+ }
65
+
66
+ #tnp-body #tabs .ui-tabs .ui-tabs-nav {
67
+ margin: 0;
68
+ padding: .2em .2em 0 0;
69
+ background-color: #f2f2f2;
70
+ }
71
+
72
+ #tnp-body #tabs .ui-tabs .ui-tabs-panel {
73
+ padding: 1em 0;
74
+ background-color: #f2f2f2;
75
+ margin: 0;
76
+ border: 0;
77
+ }
78
+
79
+ #tnp-body .ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active {
80
+ background: #fff !important;
81
+ font-weight: normal;
82
+ font-family: soleil, sans-serif;
83
+ }
84
+
85
+
86
+ #tnp-body .ui-widget-content {
87
+ background: #fff;
88
+ }
89
+
90
+ #tnp-body .ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default {
91
+ border: none;
92
+ background: #ECF0F1;
93
+ font-family: soleil, sans-serif;
94
+ }
{css → admin}/widgets.css RENAMED
@@ -1,377 +1,377 @@
1
- /*
2
- Widgets are blocks used within rows and columns (bootstrap like) and identified by the
3
- class "tnp-widget"
4
- */
5
-
6
- .tnp-widget {
7
- width: 100%;
8
- box-shadow: 0 2px 2px rgba(0, 0, 0, .1);
9
- background: #fff;
10
- min-height: 320px;
11
- color: #000;
12
- padding-bottom: 20px;
13
- position: relative;
14
- }
15
-
16
- .tnp-height-full {
17
- height: 500px !important;
18
- }
19
-
20
- #tnp-body .tnp-widget p {
21
- color: #000;
22
- margin-left: 15px;
23
- }
24
-
25
- /* Widget title */
26
- .tnp-widget h3 {
27
- font-family: soleil, sans-serif;
28
- letter-spacing: 0.05rem;
29
- background-color: #2980B9;
30
- color: #fff !important;
31
- margin: 15px 0px;
32
- padding: 9px;
33
- border: 0;
34
- font-size: 14px;
35
- font-weight: 400;
36
- }
37
-
38
- /* Buttons on widgets top bar */
39
- #tnp-body .tnp-widget h3 a,
40
- #tnp-body .tnp-widget h3 a:visited,
41
- #tnp-body .tnp-widget h3 a:hover {
42
- color: #fff;
43
- }
44
-
45
- .tnp-widget h3.red {
46
- background-color: darkred;
47
- }
48
-
49
- .tnp-widget h3.gray {
50
- background-color: darkgray;
51
- }
52
-
53
- .tnp-widget.tnp-inactive {
54
- color: #999 !important;
55
- background-color: #ddd !important;
56
- }
57
-
58
- .tnp-widget.tnp-inactive h3 {
59
- background-color: #999;
60
- color: #ccc !important;
61
- }
62
-
63
- /* Widget title button */
64
- .tnp-widget h3 a {
65
- float: right;
66
- color: white;
67
- text-decoration: none;
68
- margin-left: 5px;
69
- padding: 2px 8px;
70
- background-color: #26C281;
71
- border-radius: 2px;
72
- font-weight: 300;
73
- text-transform: capitalize;
74
- font-size: 0.8rem;
75
- }
76
-
77
- /* Widget title button hover */
78
- .tnp-widget h3 a:hover {
79
- color: white;
80
- text-decoration: none;
81
- margin-left: 5px;
82
- background-color: #2ECC71;
83
- }
84
-
85
- .tnp-widget .inside li {
86
- font-size: 12px;
87
- margin: 10px;
88
- }
89
-
90
- .tnp-widget .inside ul {
91
- list-style-type: circle;
92
- list-style-position: inside;
93
- }
94
-
95
- .tnp-widget .tnp-note {
96
- color: #999;
97
- font-size: 12px;
98
- margin-top: 20px;
99
- text-align: center;
100
- padding: 0 10px;
101
- position: absolute;
102
- margin: 0 auto;
103
- bottom: 10px;
104
- }
105
-
106
-
107
- /* Widgets with a placeholder image */
108
-
109
- .tnp-widget .tnp-placeholder {
110
- padding: 10px;
111
- text-align: center;
112
- }
113
-
114
- .tnp-widget .tnp-placeholder img {
115
- max-width: 100%
116
- }
117
-
118
-
119
- /* Widgets with two numbers (primary and secondary)*/
120
-
121
- .tnp-widget.tnp-number {
122
- text-align: center;
123
- min-height: 0 !important;
124
- height: 250px;
125
- }
126
-
127
- .tnp-widget .tnp-value {
128
- font-size: 45px;
129
- margin-top: 20px;
130
- }
131
-
132
- .tnp-widget .tnp-value-2 {
133
- font-size: 20px;
134
- margin-top: 15px;
135
- margin-bottom: 20px;
136
- }
137
-
138
-
139
- /* Icons and colors inside widgets */
140
-
141
- .tnp-icon {
142
- margin-top: 20px;
143
- margin-bottom: 25px;
144
- }
145
-
146
- .tnp-widget .tnp-icon i {
147
- font-size: 40px;
148
- }
149
-
150
- .tnp-widget .tnp-blue {
151
- color: #2980b9;
152
- }
153
-
154
- .tnp-widget .tnp-green {
155
- color: #62cb31;
156
- }
157
-
158
- .tnp-widget .tnp-orange {
159
- color: #ea6557;
160
- }
161
-
162
- .tnp-widget .tnp-red {
163
- color: #dd3333;
164
- }
165
-
166
- .tnp-widget .tnp-gray {
167
- color: #999;
168
- }
169
-
170
-
171
- /* Widefat tables inside a widget */
172
-
173
- #tnp-body .tnp-widget table.widefat {
174
- margin: 10px 15px;
175
- width: auto;
176
- }
177
-
178
- #tnp-body .tnp-widget.tnp-table table.widefat thead {
179
- background-color: #fff !important;
180
- }
181
-
182
- #tnp-body .tnp-widget.tnp-table table.widefat thead th {
183
- font-weight: bold;
184
- background-color: #fff !important;
185
- color: #000 !important;
186
- }
187
-
188
- .tnp-cards-container {
189
- display: flex;
190
- flex-wrap: wrap;
191
- flex-direction: row;
192
- }
193
-
194
- .tnp-card {
195
- flex: 1 0;
196
- align-content: flex-start;
197
- margin: 15px;
198
- font-family: soleil, sans-serif;
199
- border-radius: 15px;
200
- background-color: #232D3B;
201
- -webkit-box-shadow: 1px 1px 7px 0px rgba(0, 0, 0, 0.15);
202
- -moz-box-shadow: 1px 1px 7px 0px rgba(0, 0, 0, 0.15);
203
- box-shadow: 1px 1px 7px 0px rgba(0, 0, 0, 0.15);
204
- padding: 15px;
205
- color: #FFF;
206
- display: flex;
207
- flex-wrap: wrap;
208
- position: relative;
209
- }
210
-
211
- .tnp-card.tnp-card-col-2 {
212
- flex: 0 0 calc(50% - 31px);
213
- }
214
-
215
- @media screen and (max-width: 700px) {
216
- .tnp-card {
217
- flex: 1 0 100%;
218
- }
219
- }
220
-
221
- @media screen and (min-width: 700px) and (max-width: 1200px) {
222
- .tnp-card {
223
- flex: 1 0 40%;
224
- }
225
- }
226
-
227
- .tnp-card .tnp-card-title {
228
- flex-basis: 100%;
229
- font-size: 19px;
230
- margin-bottom: 25px;
231
- }
232
-
233
- .tnp-card .tnp-card-value {
234
- flex-basis: 75%;
235
- font-size: 39px;
236
- font-weight: bold;
237
- }
238
-
239
- .tnp-counter-animation.percentage {
240
- display: inline-block;
241
- min-width: 75px;
242
- }
243
-
244
- .tnp-card-button-container {
245
- flex-basis: 100%;
246
- margin-top: 60px;
247
- }
248
-
249
- .tnp-card-button-container a {
250
- padding: 5px 18px;
251
- font-size: 12px;
252
- color: #FFF !important;
253
- background-color: #2980B9;
254
- border-radius: 4px;
255
- text-decoration: none;
256
- position: absolute;
257
- bottom: 15px;
258
- left: 50%;
259
- -webkit-transform: translateX(-50%);
260
- transform: translateX(-50%);
261
- }
262
-
263
- .tnp-card-button-container a:hover {
264
- background-color: #2887c1;
265
- }
266
-
267
- .tnp-card-button-container a:hover {
268
- color: #FFF !important;
269
- }
270
-
271
- .tnp-card-icon {
272
- flex-basis: 25%;
273
- display: flex;
274
- justify-content: flex-end;
275
- }
276
-
277
- .tnp-card .green {
278
- color: #81B4A3;
279
- }
280
-
281
- .tnp-card .yellow {
282
- color: #F8CF61;
283
- }
284
-
285
- .tnp-card .red {
286
- color: #F36C7F;
287
- }
288
-
289
- .tnp-card .tnp-card-description {
290
- font-size: 11px;
291
- line-height: 13px;
292
- font-weight: 500;
293
- color: #7f8286;
294
- margin-top: 20px;
295
- font-family: -apple-system, system-ui, BlinkMacSystemFont,
296
- 'Segoe UI', Roboto, 'Helvetica Neue',
297
- Ubuntu, Arial, sans-serif;
298
- }
299
-
300
- .tnp-card .tnp-card-description .value {
301
- color: #919498;
302
- font-weight: 700;
303
- }
304
-
305
- .tnp-card .tnp-card-chart {
306
- flex-basis: 100%;
307
- }
308
-
309
- .tnp-card .tnp-card-chart .mini-chart {
310
- max-height: 200px;
311
- max-width: 200px;
312
- margin: 0 auto;
313
- }
314
-
315
- .tnp-card .tnp-card-chart.h-400 {
316
- height: 400px;
317
- }
318
-
319
- .tnp-card-table,
320
- .tnp-card-table table {
321
- width: 100%;
322
- }
323
-
324
- .tnp-card-table table {
325
- border-collapse: collapse;
326
- }
327
-
328
- .tnp-card-table table .w-25 {
329
- width: 25%;
330
- }
331
-
332
- .tnp-card-table table .w-50 {
333
- width: 50%;
334
- }
335
-
336
- .tnp-card-table table .w-75 {
337
- width: 75%;
338
- }
339
-
340
- .tnp-card-table table .w-33 {
341
- width: 33%;
342
- }
343
-
344
- .tnp-card-table table .w-20 {
345
- width: 20%;
346
- }
347
-
348
- .tnp-card-table table .w-10 {
349
- width: 10%;
350
- }
351
-
352
- .tnp-card-table table .w-80 {
353
- width: 80%;
354
- }
355
-
356
- .tnp-card-table table tbody::before {
357
- content: '';
358
- display: table-row;
359
- height: 15px;
360
- }
361
-
362
- .tnp-card-table table tbody tr {
363
- line-height: 30px;
364
- }
365
-
366
- #tnp-body .tnp-card-table table tbody td,
367
- .tnp-card-table table tbody td {
368
- color: #FFF;
369
- }
370
-
371
- .tnp-card-table table tbody tr:nth-child(even) {
372
- background-color: #26303E;
373
- }
374
-
375
- .mt-25 {
376
- margin-top: 25px;
377
- }
1
+ /*
2
+ Widgets are blocks used within rows and columns (bootstrap like) and identified by the
3
+ class "tnp-widget"
4
+ */
5
+
6
+ .tnp-widget {
7
+ width: 100%;
8
+ box-shadow: 0 2px 2px rgba(0, 0, 0, .1);
9
+ background: #fff;
10
+ min-height: 320px;
11
+ color: #000;
12
+ padding-bottom: 20px;
13
+ position: relative;
14
+ }
15
+
16
+ .tnp-height-full {
17
+ height: 500px !important;
18
+ }
19
+
20
+ #tnp-body .tnp-widget p {
21
+ color: #000;
22
+ margin-left: 15px;
23
+ }
24
+
25
+ /* Widget title */
26
+ .tnp-widget h3 {
27
+ font-family: soleil, sans-serif;
28
+ letter-spacing: 0.05rem;
29
+ background-color: #2980B9;
30
+ color: #fff !important;
31
+ margin: 15px 0px;
32
+ padding: 9px;
33
+ border: 0;
34
+ font-size: 14px;
35
+ font-weight: 400;
36
+ }
37
+
38
+ /* Buttons on widgets top bar */
39
+ #tnp-body .tnp-widget h3 a,
40
+ #tnp-body .tnp-widget h3 a:visited,
41
+ #tnp-body .tnp-widget h3 a:hover {
42
+ color: #fff;
43
+ }
44
+
45
+ .tnp-widget h3.red {
46
+ background-color: darkred;
47
+ }
48
+
49
+ .tnp-widget h3.gray {
50
+ background-color: darkgray;
51
+ }
52
+
53
+ .tnp-widget.tnp-inactive {
54
+ color: #999 !important;
55
+ background-color: #ddd !important;
56
+ }
57
+
58
+ .tnp-widget.tnp-inactive h3 {
59
+ background-color: #999;
60
+ color: #ccc !important;
61
+ }
62
+
63
+ /* Widget title button */
64
+ .tnp-widget h3 a {
65
+ float: right;
66
+ color: white;
67
+ text-decoration: none;
68
+ margin-left: 5px;
69
+ padding: 2px 8px;
70
+ background-color: #26C281;
71
+ border-radius: 2px;
72
+ font-weight: 300;
73
+ text-transform: capitalize;
74
+ font-size: 0.8rem;
75
+ }
76
+
77
+ /* Widget title button hover */
78
+ .tnp-widget h3 a:hover {
79
+ color: white;
80
+ text-decoration: none;
81
+ margin-left: 5px;
82
+ background-color: #2ECC71;
83
+ }
84
+
85
+ .tnp-widget .inside li {
86
+ font-size: 12px;
87
+ margin: 10px;
88
+ }
89
+
90
+ .tnp-widget .inside ul {
91
+ list-style-type: circle;
92
+ list-style-position: inside;
93
+ }
94
+
95
+ .tnp-widget .tnp-note {
96
+ color: #999;
97
+ font-size: 12px;
98
+ margin-top: 20px;
99
+ text-align: center;
100
+ padding: 0 10px;
101
+ position: absolute;
102
+ margin: 0 auto;
103
+ bottom: 10px;
104
+ }
105
+
106
+
107
+ /* Widgets with a placeholder image */
108
+
109
+ .tnp-widget .tnp-placeholder {
110
+ padding: 10px;
111
+ text-align: center;
112
+ }
113
+
114
+ .tnp-widget .tnp-placeholder img {
115
+ max-width: 100%
116
+ }
117
+
118
+
119
+ /* Widgets with two numbers (primary and secondary)*/
120
+
121
+ .tnp-widget.tnp-number {
122
+ text-align: center;
123
+ min-height: 0 !important;
124
+ height: 250px;
125
+ }
126
+
127
+ .tnp-widget .tnp-value {
128
+ font-size: 45px;
129
+ margin-top: 20px;
130
+ }
131
+
132
+ .tnp-widget .tnp-value-2 {
133
+ font-size: 20px;
134
+ margin-top: 15px;
135
+ margin-bottom: 20px;
136
+ }
137
+
138
+
139
+ /* Icons and colors inside widgets */
140
+
141
+ .tnp-icon {
142
+ margin-top: 20px;
143
+ margin-bottom: 25px;
144
+ }
145
+
146
+ .tnp-widget .tnp-icon i {
147
+ font-size: 40px;
148
+ }
149
+
150
+ .tnp-widget .tnp-blue {
151
+ color: #2980b9;
152
+ }
153
+
154
+ .tnp-widget .tnp-green {
155
+ color: #62cb31;
156
+ }
157
+
158
+ .tnp-widget .tnp-orange {
159
+ color: #ea6557;
160
+ }
161
+
162
+ .tnp-widget .tnp-red {
163
+ color: #dd3333;
164
+ }
165
+
166
+ .tnp-widget .tnp-gray {
167
+ color: #999;
168
+ }
169
+
170
+
171
+ /* Widefat tables inside a widget */
172
+
173
+ #tnp-body .tnp-widget table.widefat {
174
+ margin: 10px 15px;
175
+ width: auto;
176
+ }
177
+
178
+ #tnp-body .tnp-widget.tnp-table table.widefat thead {
179
+ background-color: #fff !important;
180
+ }
181
+
182
+ #tnp-body .tnp-widget.tnp-table table.widefat thead th {
183
+ font-weight: bold;
184
+ background-color: #fff !important;
185
+ color: #000 !important;
186
+ }
187
+
188
+ .tnp-cards-container {
189
+ display: flex;
190
+ flex-wrap: wrap;
191
+ flex-direction: row;
192
+ }
193
+
194
+ .tnp-card {
195
+ flex: 1 0;
196
+ align-content: flex-start;
197
+ margin: 15px;
198
+ font-family: soleil, sans-serif;
199
+ border-radius: 15px;
200
+ background-color: #232D3B;
201
+ -webkit-box-shadow: 1px 1px 7px 0px rgba(0, 0, 0, 0.15);
202
+ -moz-box-shadow: 1px 1px 7px 0px rgba(0, 0, 0, 0.15);
203
+ box-shadow: 1px 1px 7px 0px rgba(0, 0, 0, 0.15);
204
+ padding: 15px;
205
+ color: #FFF;
206
+ display: flex;
207
+ flex-wrap: wrap;
208
+ position: relative;
209
+ }
210
+
211
+ .tnp-card.tnp-card-col-2 {
212
+ flex: 0 0 calc(50% - 31px);
213
+ }
214
+
215
+ @media screen and (max-width: 700px) {
216
+ .tnp-card {
217
+ flex: 1 0 100%;
218
+ }
219
+ }
220
+
221
+ @media screen and (min-width: 700px) and (max-width: 1200px) {
222
+ .tnp-card {
223
+ flex: 1 0 40%;
224
+ }
225
+ }
226
+
227
+ .tnp-card .tnp-card-title {
228
+ flex-basis: 100%;
229
+ font-size: 19px;
230
+ margin-bottom: 25px;
231
+ }
232
+
233
+ .tnp-card .tnp-card-value {
234
+ flex-basis: 75%;
235
+ font-size: 39px;
236
+ font-weight: bold;
237
+ }
238
+
239
+ .tnp-counter-animation.percentage {
240
+ display: inline-block;
241
+ min-width: 75px;
242
+ }
243
+
244
+ .tnp-card-button-container {
245
+ flex-basis: 100%;
246
+ margin-top: 60px;
247
+ }
248
+
249
+ .tnp-card-button-container a {
250
+ padding: 5px 18px;
251
+ font-size: 12px;
252
+ color: #FFF !important;
253
+ background-color: #2980B9;
254
+ border-radius: 4px;
255
+ text-decoration: none;
256
+ position: absolute;
257
+ bottom: 15px;
258
+ left: 50%;
259
+ -webkit-transform: translateX(-50%);
260
+ transform: translateX(-50%);
261
+ }
262
+
263
+ .tnp-card-button-container a:hover {
264
+ background-color: #2887c1;
265
+ }
266
+
267
+ .tnp-card-button-container a:hover {
268
+ color: #FFF !important;
269
+ }
270
+
271
+ .tnp-card-icon {
272
+ flex-basis: 25%;
273
+ display: flex;
274
+ justify-content: flex-end;
275
+ }
276
+
277
+ .tnp-card .green {
278
+ color: #81B4A3;
279
+ }
280
+
281
+ .tnp-card .yellow {
282
+ color: #F8CF61;
283
+ }
284
+
285
+ .tnp-card .red {
286
+ color: #F36C7F;
287
+ }
288
+
289
+ .tnp-card .tnp-card-description {
290
+ font-size: 11px;
291
+ line-height: 13px;
292
+ font-weight: 500;
293
+ color: #7f8286;
294
+ margin-top: 20px;
295
+ font-family: -apple-system, system-ui, BlinkMacSystemFont,
296
+ 'Segoe UI', Roboto, 'Helvetica Neue',
297
+ Ubuntu, Arial, sans-serif;
298
+ }
299
+
300
+ .tnp-card .tnp-card-description .value {
301
+ color: #919498;
302
+ font-weight: 700;
303
+ }
304
+
305
+ .tnp-card .tnp-card-chart {
306
+ flex-basis: 100%;
307
+ }
308
+
309
+ .tnp-card .tnp-card-chart .mini-chart {
310
+ max-height: 200px;
311
+ max-width: 200px;
312
+ margin: 0 auto;
313
+ }
314
+
315
+ .tnp-card .tnp-card-chart.h-400 {
316
+ height: 400px;
317
+ }
318
+
319
+ .tnp-card-table,
320
+ .tnp-card-table table {
321
+ width: 100%;
322
+ }
323
+
324
+ .tnp-card-table table {
325
+ border-collapse: collapse;
326
+ }
327
+
328
+ .tnp-card-table table .w-25 {
329
+ width: 25%;
330
+ }
331
+
332
+ .tnp-card-table table .w-50 {
333
+ width: 50%;
334
+ }
335
+
336
+ .tnp-card-table table .w-75 {
337
+ width: 75%;
338
+ }
339
+
340
+ .tnp-card-table table .w-33 {
341
+ width: 33%;
342
+ }
343
+
344
+ .tnp-card-table table .w-20 {
345
+ width: 20%;
346
+ }
347
+
348
+ .tnp-card-table table .w-10 {
349
+ width: 10%;
350
+ }
351
+
352
+ .tnp-card-table table .w-80 {
353
+ width: 80%;
354
+ }
355
+
356
+ .tnp-card-table table tbody::before {
357
+ content: '';
358
+ display: table-row;
359
+ height: 15px;
360
+ }
361
+
362
+ .tnp-card-table table tbody tr {
363
+ line-height: 30px;
364
+ }
365
+
366
+ #tnp-body .tnp-card-table table tbody td,
367
+ .tnp-card-table table tbody td {
368
+ color: #FFF;
369
+ }
370
+
371
+ .tnp-card-table table tbody tr:nth-child(even) {
372
+ background-color: #26303E;
373
+ }
374
+
375
+ .mt-25 {
376
+ margin-top: 25px;
377
+ }
{css → admin}/wp-editor.css RENAMED
@@ -1,16 +1,16 @@
1
- /* The CSS used for the WP editor on administration panels */
2
- body {
3
- font-size: 16px;
4
- font-family: sans-serif;
5
- margin: 20px;
6
- }
7
-
8
- .mce-content-body p, table, ul, ol {
9
- margin-bottom: 15px;
10
- width: 650px;
11
- line-height: 24px;
12
- }
13
-
14
- img {
15
- max-width: 100%;
16
- }
1
+ /* The CSS used for the WP editor on administration panels */
2
+ body {
3
+ font-size: 16px;
4
+ font-family: sans-serif;
5
+ margin: 20px;
6
+ }
7
+
8
+ .mce-content-body p, table, ul, ol {
9
+ margin-bottom: 15px;
10
+ width: 650px;
11
+ line-height: 24px;
12
+ }
13
+
14
+ img {
15
+ max-width: 100%;
16
+ }
css/controls-dark.css DELETED
@@ -1,61 +0,0 @@
1
-
2
- .tnpc-error, .tnpc-warning, .tnpc-message {
3
- background-color: #28313C;
4
- color: #fff;
5
- }
6
-
7
- #tnp-body table.form-table {
8
- border: 1px solid #666;
9
- border-radius: 3px;
10
- margin-top: 0;
11
- margin-bottom: 1em;
12
- }
13
-
14
- #tnp-body table.form-table th {
15
- width: 150px;
16
- }
17
-
18
- #tnp-body table.form-table, #tnp-body table.form-table th {
19
- background-color: transparent;
20
- color: #fff;
21
- }
22
-
23
- #tnp-body table.form-table th, #tnp-body table.form-table td {
24
- vertical-align: top;
25
- }
26
-
27
- #tnp-body table.form-table td {
28
- color: #999;
29
- }
30
-
31
- #tnp-body table.form-table p.description {
32
- color: #aaa;
33
- }
34
-
35
- #tnp-body table.widefat {
36
- background-color: transparent !important;
37
- }
38
-
39
- #tnp-body table.widefat input[type="text"] {
40
- background-color: #28313C;
41
- color: #fff !important;
42
- }
43
-
44
- #tnp-body table.widefat thead {
45
- background-color: #28313C;
46
- color: #fff!important;
47
- }
48
-
49
- #tnp-body table.widefat td {
50
- color: #999;
51
- }
52
-
53
- #tnp-body table.widefat tr:nth-child(even) {
54
- background-color: #28313C;
55
- }
56
-
57
- /* Set of checkboxes */
58
- .tnpc-checkboxes label {
59
- border: 1px solid #666;
60
- background-color: transparent;
61
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
emails/blocks/posts/layout-one.php CHANGED
@@ -1,135 +1,135 @@
1
- <?php
2
- $size = ['width' => 600, 'height' => 0];
3
- $total_width = 600 - $options['block_padding_left'] - $options['block_padding_right'];
4
- $column_width = $total_width / 2 - 10;
5
-
6
- $title_style = TNP_Composer::get_style($options, 'title', $composer, 'title', ['scale' => .8]);
7
- $text_style = TNP_Composer::get_style($options, '', $composer, 'text');
8
- ?>
9
- <style>
10
- .title {
11
- <?php $title_style->echo_css()?>
12
- line-height: normal !important;
13
- padding: 0 0 5px 0;
14
- }
15
-
16
- .excerpt {
17
- <?php $text_style->echo_css()?>
18
- line-height: 1.5em !important;
19
- padding: 10px 0 15px 0;
20
- }
21
-
22
- .meta {
23
- font-family: <?php echo $text_style->font_family ?>;
24
- color: <?php echo $text_style->font_color ?>;
25
- font-size: <?php echo round($text_style->font_size * 0.9) ?>px;
26
- font-weight: <?php echo $text_style->font_weight ?>;
27
- font-style: italic;
28
- padding: 0 0 10px 0;
29
- line-height: normal !important;
30
- }
31
- .button {
32
- padding: 15px 0;
33
- }
34
- </style>
35
-
36
-
37
- <table border="0" cellpadding="0" cellspacing="0" width="100%" class="responsive-table">
38
-
39
- <?php foreach ($posts as $post) { ?>
40
- <?php
41
- $url = tnp_post_permalink($post);
42
-
43
- $media = null;
44
- if ($show_image) {
45
- $media = tnp_composer_block_posts_get_media($post, $size);
46
- if ($media) {
47
- $media->link = $url;
48
- $media->set_width($column_width);
49
- }
50
- }
51
-
52
- $meta = [];
53
-
54
- if ($show_date) {
55
- $meta[] = tnp_post_date($post);
56
- }
57
-
58
- if ($show_author) {
59
- $author_object = get_user_by('id', $post->post_author);
60
- if ($author_object) {
61
- $meta[] = $author_object->display_name;
62
- }
63
- }
64
-
65
- $button_options['button_url'] = $url;
66
- $button_options['button_align'] = 'left';
67
- ?>
68
-
69
- <tr>
70
- <td valign="top" style="padding: 20px 0 0 0;">
71
-
72
- <?php if ($media) { ?>
73
- <table width="<?php echo $column_width ?>" cellpadding="0" cellspacing="0" border="0" align="left" class="responsive">
74
- <tr>
75
- <td style="padding-bottom: 20px;">
76
- <?php echo TNP_Composer::image($media, ['class' => 'fluid']) ?>
77
- </td>
78
- </tr>
79
- </table>
80
- <?php } ?>
81
-
82
- <table width="<?php echo $media ? $column_width : '100%' ?>" cellpadding="0" cellspacing="0" border="0" class="responsive" align="right"><tr><td>
83
-
84
- <table border="0" cellspacing="0" cellpadding="0" width="100%">
85
- <?php if ($meta) { ?>
86
- <tr>
87
- <td inline-class="meta" dir="<?php echo $dir ?>" align="<?php echo $align_left ?>">
88
- <?php echo esc_html(implode(' - ', $meta)) ?>
89
- </td>
90
- </tr>
91
- <?php } ?>
92
-
93
- <tr>
94
- <td align="<?php echo $align_left ?>" inline-class="title" class="title tnpc-row-edit tnpc-inline-editable"
95
- data-type="title" data-id="<?php echo $post->ID ?>" dir="<?php echo $dir ?>">
96
- <span>
97
- <?php
98
- echo TNP_Composer::is_post_field_edited_inline($options['inline_edits'], 'title', $post->ID) ?
99
- TNP_Composer::get_edited_inline_post_field($options['inline_edits'], 'title', $post->ID) :
100
- tnp_post_title($post)
101
- ?>
102
- </span>
103
- </td>
104
- </tr>
105
-
106
- <?php if ($excerpt_length) { ?>
107
- <tr>
108
- <td align="<?php echo $align_left ?>" inline-class="excerpt" class="tnpc-row-edit tnpc-inline-editable"
109
- data-type="text" data-id="<?php echo $post->ID ?>" dir="<?php echo $dir ?>">
110
- <?php
111
- echo TNP_Composer::is_post_field_edited_inline($options['inline_edits'], 'text', $post->ID) ?
112
- TNP_Composer::get_edited_inline_post_field($options['inline_edits'], 'text', $post->ID) :
113
- tnp_post_excerpt($post, $excerpt_length)
114
- ?>
115
- </td>
116
- </tr>
117
- <?php } ?>
118
-
119
- <?php if ($show_read_more_button) { ?>
120
- <tr>
121
- <td align="<?php echo $align_left ?>" inline-class="button">
122
- <?php echo TNP_Composer::button($button_options) ?>
123
- </td>
124
- </tr>
125
- <?php } ?>
126
- </table>
127
-
128
- </td></tr></table>
129
-
130
- </td>
131
- </tr>
132
-
133
- <?php } ?>
134
-
135
- </table>
1
+ <?php
2
+ $size = ['width' => 600, 'height' => 0];
3
+ $total_width = 600 - $options['block_padding_left'] - $options['block_padding_right'];
4
+ $column_width = $total_width / 2 - 10;
5
+
6
+ $title_style = TNP_Composer::get_style($options, 'title', $composer, 'title', ['scale' => .8]);
7
+ $text_style = TNP_Composer::get_style($options, '', $composer, 'text');
8
+ ?>
9
+ <style>
10
+ .title {
11
+ <?php $title_style->echo_css()?>
12
+ line-height: normal !important;
13
+ padding: 0 0 5px 0;
14
+ }
15
+
16
+ .excerpt {
17
+ <?php $text_style->echo_css()?>
18
+ line-height: 1.5em !important;
19
+ padding: 10px 0 15px 0;
20
+ }
21
+
22
+ .meta {
23
+ font-family: <?php echo $text_style->font_family ?>;
24
+ color: <?php echo $text_style->font_color ?>;
25
+ font-size: <?php echo round($text_style->font_size * 0.9) ?>px;
26
+ font-weight: <?php echo $text_style->font_weight ?>;
27
+ font-style: italic;
28
+ padding: 0 0 10px 0;
29
+ line-height: normal !important;
30
+ }
31
+ .button {
32
+ padding: 15px 0;
33
+ }
34
+ </style>
35
+
36
+
37
+ <table border="0" cellpadding="0" cellspacing="0" width="100%" class="responsive">
38
+
39
+ <?php foreach ($posts as $post) { ?>
40
+ <?php
41
+ $url = tnp_post_permalink($post);
42
+
43
+ $media = null;
44
+ if ($show_image) {
45
+ $media = tnp_composer_block_posts_get_media($post, $size);
46
+ if ($media) {
47
+ $media->link = $url;
48
+ $media->set_width($column_width);
49
+ }
50
+ }
51
+
52
+ $meta = [];
53
+
54
+ if ($show_date) {
55
+ $meta[] = tnp_post_date($post);
56
+ }
57
+
58
+ if ($show_author) {
59
+ $author_object = get_user_by('id', $post->post_author);
60
+ if ($author_object) {
61
+ $meta[] = $author_object->display_name;
62
+ }
63
+ }
64
+
65
+ $button_options['button_url'] = $url;
66
+ $button_options['button_align'] = 'left';
67
+ ?>
68
+
69
+ <tr>
70
+ <td valign="top" style="padding: 20px 0 0 0;">
71
+
72
+ <?php if ($media) { ?>
73
+ <table width="<?php echo $column_width ?>" cellpadding="0" cellspacing="0" border="0" align="left" class="responsive">
74
+ <tr>
75
+ <td style="padding-bottom: 20px;" width="100%">
76
+ <?php echo TNP_Composer::image($media, ['class' => 'fluid']) ?>
77
+ </td>
78
+ </tr>
79
+ </table>
80
+ <?php } ?>
81
+
82
+ <table width="<?php echo $media ? $column_width : '100%' ?>" cellpadding="0" cellspacing="0" border="0" class="responsive" align="right"><tr><td>
83
+
84
+ <table border="0" cellspacing="0" cellpadding="0" width="100%">
85
+ <?php if ($meta) { ?>
86
+ <tr>
87
+ <td inline-class="meta" dir="<?php echo $dir ?>" align="<?php echo $align_left ?>">
88
+ <?php echo esc_html(implode(' - ', $meta)) ?>
89
+ </td>
90
+ </tr>
91
+ <?php } ?>
92
+
93
+ <tr>
94
+ <td align="<?php echo $align_left ?>" inline-class="title" class="title tnpc-row-edit tnpc-inline-editable"
95
+ data-type="title" data-id="<?php echo $post->ID ?>" dir="<?php echo $dir ?>">
96
+ <span>
97
+ <?php
98
+ echo TNP_Composer::is_post_field_edited_inline($options['inline_edits'], 'title', $post->ID) ?
99
+ TNP_Composer::get_edited_inline_post_field($options['inline_edits'], 'title', $post->ID) :
100
+ tnp_post_title($post)
101
+ ?>
102
+ </span>
103
+ </td>
104
+ </tr>
105
+
106
+ <?php if ($excerpt_length) { ?>
107
+ <tr>
108
+ <td align="<?php echo $align_left ?>" inline-class="excerpt" class="tnpc-row-edit tnpc-inline-editable"
109
+ data-type="text" data-id="<?php echo $post->ID ?>" dir="<?php echo $dir ?>">
110
+ <?php
111
+ echo TNP_Composer::is_post_field_edited_inline($options['inline_edits'], 'text', $post->ID) ?
112
+ TNP_Composer::get_edited_inline_post_field($options['inline_edits'], 'text', $post->ID) :
113
+ tnp_post_excerpt($post, $excerpt_length)
114
+ ?>
115
+ </td>
116
+ </tr>
117
+ <?php } ?>
118
+
119
+ <?php if ($show_read_more_button) { ?>
120
+ <tr>
121
+ <td align="<?php echo $align_left ?>" inline-class="button">
122
+ <?php echo TNP_Composer::button($button_options) ?>
123
+ </td>
124
+ </tr>
125
+ <?php } ?>
126
+ </table>
127
+
128
+ </td></tr></table>
129
+
130
+ </td>
131
+ </tr>
132
+
133
+ <?php } ?>
134
+
135
+ </table>
emails/blocks/posts/layout-two.php CHANGED
@@ -1,208 +1,208 @@
1
- <?php
2
- $size = array('width' => 600, 'height' => 400, "crop" => true);
3
- $total_width = 600 - $options['block_padding_left'] - $options['block_padding_right'];
4
- $column_width = $total_width / 2 - 20;
5
-
6
- $title_style = TNP_Composer::get_style($options, 'title', $composer, 'title', ['scale' => .8]);
7
- $text_style = TNP_Composer::get_style($options, '', $composer, 'text');
8
- ?>
9
- <style>
10
- .title {
11
- font-family: <?php echo $title_style->font_family ?>;
12
- font-size: <?php echo $title_style->font_size ?>px;
13
- font-weight: <?php echo $title_style->font_weight ?>;
14
- color: <?php echo $title_style->font_color ?>;
15
- line-height: 1.3em;
16
- padding: 15px 0 0 0;
17
- }
18
-
19
- .excerpt {
20
- font-family: <?php echo $text_style->font_family ?>;
21
- font-size: <?php echo $text_style->font_size ?>px;
22
- font-weight: <?php echo $text_style->font_weight ?>;
23
- color: <?php echo $text_style->font_color ?>;
24
- line-height: 1.4em;
25
- padding: 5px 0 0 0;
26
- }
27
-
28
- .meta {
29
- font-family: <?php echo $text_style->font_family ?>;
30
- color: <?php echo $text_style->font_color ?>;
31
- font-size: <?php echo round($text_style->font_size * 0.9) ?>px;
32
- font-weight: <?php echo $text_style->font_weight ?>;
33
- padding: 10px 0 0 0;
34
- font-style: italic;
35
- line-height: normal !important;
36
- }
37
- .button {
38
- padding: 15px 0;
39
- }
40
- .column-left {
41
- padding-right: 10px;
42
- padding-bottom: 20px;
43
- }
44
- .column-right {
45
- padding-left: 10px;
46
- padding-bottom: 20px;
47
- }
48
-
49
- </style>
50
-
51
- <table cellspacing="0" cellpadding="0" border="0" width="100%">
52
-
53
- <?php foreach (array_chunk($posts, 2) AS $row) { ?>
54
- <?php
55
- $media = null;
56
- if ($show_image) {
57
- $media = tnp_composer_block_posts_get_media($row[0], $size, $image_placeholder_url);
58
- $media->link = tnp_post_permalink($row[0]);
59
- $media->set_width($column_width);
60
- }
61
-
62
- $meta = [];
63
-
64
- if ($show_date) {
65
- $meta[] = tnp_post_date($row[0]);
66
- }
67
-
68
- if ($show_author) {
69
- $author_object = get_user_by('id', $row[0]->post_author);
70
- if ($author_object) {
71
- $meta[] = $author_object->display_name;
72
- }
73
- }
74
-
75
- $button_options['button_url'] = tnp_post_permalink($row[0]);
76
- ?>
77
- <tr>
78
- <td inline-class="column-left" width="50%" valign="top" class="responsive">
79
-
80
-
81
- <table cellpadding="0" cellspacing="0" border="0" width="100%">
82
- <?php if ($media) { ?>
83
- <tr>
84
- <td align="center" valign="middle">
85
- <?php echo TNP_Composer::image($media, ['class' => 'fluid']) ?>
86
- </td>
87
- </tr>
88
- <?php } ?>
89
- <tr>
90
- <td align="center" inline-class="title" class="title tnpc-row-edit tnpc-inline-editable" data-type="title" data-id="<?php echo $row[0]->ID ?>">
91
- <?php
92
- echo TNP_Composer::is_post_field_edited_inline($options['inline_edits'], 'title', $row[0]->ID) ?
93
- TNP_Composer::get_edited_inline_post_field($options['inline_edits'], 'title', $row[0]->ID) :
94
- tnp_post_title($row[0])
95
- ?>
96
- </td>
97
- </tr>
98
- <?php if ($meta) { ?>
99
- <tr>
100
- <td inline-class="meta" class="meta">
101
- <?php echo esc_html(implode(' - ', $meta)) ?>
102
- </td>
103
- </tr>
104
- <?php } ?>
105
-
106
-
107
- <?php if ($excerpt_length) { ?>
108
- <tr>
109
- <td align="center" inline-class="excerpt" class="title tnpc-row-edit tnpc-inline-editable" data-type="text" data-id="<?php echo $row[0]->ID ?>">
110
- <?php
111
- echo TNP_Composer::is_post_field_edited_inline($options['inline_edits'], 'text', $row[0]->ID) ?
112
- TNP_Composer::get_edited_inline_post_field($options['inline_edits'], 'text', $row[0]->ID) :
113
- tnp_post_excerpt($row[0], $excerpt_length)
114
- ?>
115
- </td>
116
- </tr>
117
- <?php } ?>
118
-
119
- <?php if ($show_read_more_button) { ?>
120
- <tr>
121
- <td align="center" inline-class="button">
122
- <?php echo TNP_Composer::button($button_options) ?>
123
- </td>
124
- </tr>
125
- <?php } ?>
126
- </table>
127
- </td>
128
-
129
- <td inline-class="column-right" width="50%" valign="top" class="responsive">
130
- <?php
131
- if (isset($row[1])) {
132
-
133
- $media = null;
134
- if ($show_image) {
135
- $media = tnp_composer_block_posts_get_media($row[1], $size, $image_placeholder_url);
136
- $media->link = tnp_post_permalink($row[1]);
137
- $media->set_width($column_width);
138
- }
139
-
140
- $meta = [];
141
-
142
- if ($show_date) {
143
- $meta[] = tnp_post_date($row[1]);
144
- }
145
-
146
- if ($show_author) {
147
- $author_object = get_user_by('id', $row[1]->post_author);
148
- if ($author_object) {
149
- $meta[] = $author_object->display_name;
150
- }
151
- }
152
-
153
- $button_options['button_url'] = tnp_post_permalink($row[1]);
154
- ?>
155
-
156
-
157
- <table cellpadding="0" cellspacing="0" border="0" width="100%">
158
- <?php if ($media) { ?>
159
- <tr>
160
- <td align="center" valign="middle">
161
- <?php echo TNP_Composer::image($media, ['class' => 'fluid']) ?>
162
- </td>
163
- </tr>
164
- <?php } ?>
165
- <tr>
166
- <td align="center" inline-class="title" class="tnpc-row-edit tnpc-inline-editable" data-type="title" data-id="<?php echo $row[1]->ID ?>">
167
- <?php
168
- echo TNP_Composer::is_post_field_edited_inline($options['inline_edits'], 'title', $row[1]->ID) ?
169
- TNP_Composer::get_edited_inline_post_field($options['inline_edits'], 'title', $row[1]->ID) :
170
- tnp_post_title($row[1])
171
- ?>
172
- </td>
173
- </tr>
174
- <?php if ($meta) { ?>
175
- <tr>
176
- <td inline-class="meta">
177
- <?php echo esc_html(implode(' - ', $meta)) ?>
178
- </td>
179
- </tr>
180
- <?php } ?>
181
-
182
- <tr>
183
- <td align="center" inline-class="excerpt" class="tnpc-row-edit tnpc-inline-editable" data-type="text" data-id="<?php echo $row[1]->ID ?>">
184
- <?php
185
- echo TNP_Composer::is_post_field_edited_inline($options['inline_edits'], 'text', $row[1]->ID) ?
186
- TNP_Composer::get_edited_inline_post_field($options['inline_edits'], 'text', $row[1]->ID) :
187
- tnp_post_excerpt($row[1], $excerpt_length)
188
- ?>
189
- </td>
190
- </tr>
191
- <?php if ($show_read_more_button) { ?>
192
- <tr>
193
- <td align="center" inline-class="button">
194
- <?php echo TNP_Composer::button($button_options) ?>
195
- </td>
196
- </tr>
197
- <?php } ?>
198
- </table>
199
- <?php } ?>
200
-
201
-
202
- </td>
203
- </tr>
204
-
205
- <?php } ?>
206
-
207
- </table>
208
-
1
+ <?php
2
+ $size = array('width' => 600, 'height' => 400, "crop" => true);
3
+ $total_width = 600 - $options['block_padding_left'] - $options['block_padding_right'];
4
+ $column_width = $total_width / 2 - 20;
5
+
6
+ $title_style = TNP_Composer::get_style($options, 'title', $composer, 'title', ['scale' => .8]);
7
+ $text_style = TNP_Composer::get_style($options, '', $composer, 'text');
8
+ ?>
9
+ <style>
10
+ .title {
11
+ font-family: <?php echo $title_style->font_family ?>;
12
+ font-size: <?php echo $title_style->font_size ?>px;
13
+ font-weight: <?php echo $title_style->font_weight ?>;
14
+ color: <?php echo $title_style->font_color ?>;
15
+ line-height: 1.3em;
16
+ padding: 15px 0 0 0;
17
+ }
18
+
19
+ .excerpt {
20
+ font-family: <?php echo $text_style->font_family ?>;
21
+ font-size: <?php echo $text_style->font_size ?>px;
22
+ font-weight: <?php echo $text_style->font_weight ?>;
23
+ color: <?php echo $text_style->font_color ?>;
24
+ line-height: 1.4em;
25
+ padding: 5px 0 0 0;
26
+ }
27
+
28
+ .meta {
29
+ font-family: <?php echo $text_style->font_family ?>;
30
+ color: <?php echo $text_style->font_color ?>;
31
+ font-size: <?php echo round($text_style->font_size * 0.9) ?>px;
32
+ font-weight: <?php echo $text_style->font_weight ?>;
33
+ padding: 10px 0 0 0;
34
+ font-style: italic;
35
+ line-height: normal !important;
36
+ }
37
+ .button {
38
+ padding: 15px 0;
39
+ }
40
+ .column-left {
41
+ padding-right: 10px;
42
+ padding-bottom: 20px;
43
+ }
44
+ .column-right {
45
+ padding-left: 10px;
46
+ padding-bottom: 20px;
47
+ }
48
+
49
+ </style>
50
+
51
+ <table cellspacing="0" cellpadding="0" border="0" width="100%">
52
+
53
+ <?php foreach (array_chunk($posts, 2) AS $row) { ?>
54
+ <?php
55
+ $media = null;
56
+ if ($show_image) {
57
+ $media = tnp_composer_block_posts_get_media($row[0], $size, $image_placeholder_url);
58
+ $media->link = tnp_post_permalink($row[0]);
59
+ $media->set_width($column_width);
60
+ }
61
+
62
+ $meta = [];
63
+
64
+ if ($show_date) {
65
+ $meta[] = tnp_post_date($row[0]);
66
+ }
67
+
68
+ if ($show_author) {
69
+ $author_object = get_user_by('id', $row[0]->post_author);
70
+ if ($author_object) {
71
+ $meta[] = $author_object->display_name;
72
+ }
73
+ }
74
+
75
+ $button_options['button_url'] = tnp_post_permalink($row[0]);
76
+ ?>
77
+ <tr>
78
+ <td inline-class="column-left" width="50%" valign="top" class="responsive">
79
+
80
+
81
+ <table cellpadding="0" cellspacing="0" border="0" width="100%">
82
+ <?php if ($media) { ?>
83
+ <tr>
84
+ <td align="center" valign="middle">
85
+ <?php echo TNP_Composer::image($media, ['class' => 'fluid']) ?>
86
+ </td>
87
+ </tr>
88
+ <?php } ?>
89
+ <tr>
90
+ <td align="center" inline-class="title" class="title tnpc-row-edit tnpc-inline-editable" data-type="title" data-id="<?php echo $row[0]->ID ?>">
91
+ <?php
92
+ echo TNP_Composer::is_post_field_edited_inline($options['inline_edits'], 'title', $row[0]->ID) ?
93
+ TNP_Composer::get_edited_inline_post_field($options['inline_edits'], 'title', $row[0]->ID) :
94
+ tnp_post_title($row[0])
95
+ ?>
96
+ </td>
97
+ </tr>
98
+ <?php if ($meta) { ?>
99
+ <tr>
100
+ <td align="center" inline-class="meta" class="meta">
101
+ <?php echo esc_html(implode(' - ', $meta)) ?>
102
+ </td>
103
+ </tr>
104
+ <?php } ?>
105
+
106
+
107
+ <?php if ($excerpt_length) { ?>
108
+ <tr>
109
+ <td align="center" inline-class="excerpt" class="title tnpc-row-edit tnpc-inline-editable" data-type="text" data-id="<?php echo $row[0]->ID ?>">
110
+ <?php
111
+ echo TNP_Composer::is_post_field_edited_inline($options['inline_edits'], 'text', $row[0]->ID) ?
112
+ TNP_Composer::get_edited_inline_post_field($options['inline_edits'], 'text', $row[0]->ID) :
113
+ tnp_post_excerpt($row[0], $excerpt_length)
114
+ ?>
115
+ </td>
116
+ </tr>
117
+ <?php } ?>
118
+
119
+ <?php if ($show_read_more_button) { ?>
120
+ <tr>
121
+ <td align="center" inline-class="button">
122
+ <?php echo TNP_Composer::button($button_options) ?>
123
+ </td>
124
+ </tr>
125
+ <?php } ?>
126
+ </table>
127
+ </td>
128
+
129
+ <td inline-class="column-right" width="50%" valign="top" class="responsive">
130
+ <?php
131
+ if (isset($row[1])) {
132
+
133
+ $media = null;
134
+ if ($show_image) {
135
+ $media = tnp_composer_block_posts_get_media($row[1], $size, $image_placeholder_url);
136
+ $media->link = tnp_post_permalink($row[1]);
137
+ $media->set_width($column_width);
138
+ }
139
+
140
+ $meta = [];
141
+
142
+ if ($show_date) {
143
+ $meta[] = tnp_post_date($row[1]);
144
+ }
145
+
146
+ if ($show_author) {
147
+ $author_object = get_user_by('id', $row[1]->post_author);
148
+ if ($author_object) {
149
+ $meta[] = $author_object->display_name;
150
+ }
151
+ }
152
+
153
+ $button_options['button_url'] = tnp_post_permalink($row[1]);
154
+ ?>
155
+
156
+
157
+ <table cellpadding="0" cellspacing="0" border="0" width="100%">
158
+ <?php if ($media) { ?>
159
+ <tr>
160
+ <td align="center" valign="middle">
161
+ <?php echo TNP_Composer::image($media, ['class' => 'fluid']) ?>
162
+ </td>
163
+ </tr>
164
+ <?php } ?>
165
+ <tr>
166
+ <td align="center" inline-class="title" class="tnpc-row-edit tnpc-inline-editable" data-type="title" data-id="<?php echo $row[1]->ID ?>">
167
+ <?php
168
+ echo TNP_Composer::is_post_field_edited_inline($options['inline_edits'], 'title', $row[1]->ID) ?
169
+ TNP_Composer::get_edited_inline_post_field($options['inline_edits'], 'title', $row[1]->ID) :
170
+ tnp_post_title($row[1])
171
+ ?>
172
+ </td>
173
+ </tr>
174
+ <?php if ($meta) { ?>
175
+ <tr>
176
+ <td inline-class="meta">
177
+ <?php echo esc_html(implode(' - ', $meta)) ?>
178
+ </td>
179
+ </tr>
180
+ <?php } ?>
181
+
182
+ <tr>
183
+ <td align="center" inline-class="excerpt" class="tnpc-row-edit tnpc-inline-editable" data-type="text" data-id="<?php echo $row[1]->ID ?>">
184
+ <?php
185
+ echo TNP_Composer::is_post_field_edited_inline($options['inline_edits'], 'text', $row[1]->ID) ?
186
+ TNP_Composer::get_edited_inline_post_field($options['inline_edits'], 'text', $row[1]->ID) :
187
+ tnp_post_excerpt($row[1], $excerpt_length)
188
+ ?>
189
+ </td>
190
+ </tr>
191
+ <?php if ($show_read_more_button) { ?>
192
+ <tr>
193
+ <td align="center" inline-class="button">
194
+ <?php echo TNP_Composer::button($button_options) ?>
195
+ </td>
196
+ </tr>
197
+ <?php } ?>
198
+ </table>
199
+ <?php } ?>
200
+
201
+
202
+ </td>
203
+ </tr>
204
+
205
+ <?php } ?>
206
+
207
+ </table>
208
+
emails/blocks/social/block.php CHANGED
@@ -9,6 +9,8 @@
9
  /* @var $options array */
10
 
11
  $defaults = array(
 
 
12
  'block_padding_left' => 15,
13
  'block_padding_right' => 15,
14
  'block_padding_bottom' => 15,
@@ -17,7 +19,9 @@ $defaults = array(
17
  );
18
  $options = array_merge($defaults, $options);
19
 
20
- $social_icon_url = plugins_url('newsletter') . '/emails/themes/default/images';
 
 
21
 
22
  $socials = ['facebook', 'twitter', 'pinterest', 'linkedin', 'tumblr', 'youtube', 'soundcloud', 'instagram', 'vimeo', 'telegram', 'vk', 'discord', 'tiktok', 'twitch'];
23
 
@@ -43,7 +47,7 @@ if (!$valid_socials) {
43
  <tr>
44
  <td align="center" valign="middle">
45
  <?php foreach ($valid_socials as &$social) { ?>
46
- <a href="<?php echo esc_url($block_options[$social . '_url']) ?>" inline-class="link"><img src="<?php echo $social_icon_url ?>/<?php echo $social ?>.png" alt="<?php echo $social ?>"></a>&nbsp;
47
  <?php } ?>
48
  </td>
49
  </tr>
9
  /* @var $options array */
10
 
11
  $defaults = array(
12
+ 'type' => 1,
13
+ 'width' => 32,
14
  'block_padding_left' => 15,
15
  'block_padding_right' => 15,
16
  'block_padding_bottom' => 15,
19
  );
20
  $options = array_merge($defaults, $options);
21
 
22
+ $type = (int) $options['type'];
23
+ $width = (int) $options['width'];
24
+ $social_icon_url = plugins_url('newsletter') . '/images/social-' . $type;
25
 
26
  $socials = ['facebook', 'twitter', 'pinterest', 'linkedin', 'tumblr', 'youtube', 'soundcloud', 'instagram', 'vimeo', 'telegram', 'vk', 'discord', 'tiktok', 'twitch'];
27
 
47
  <tr>
48
  <td align="center" valign="middle">
49
  <?php foreach ($valid_socials as &$social) { ?>
50
+ <a href="<?php echo esc_url($block_options[$social . '_url']) ?>" inline-class="link"><img src="<?php echo $social_icon_url ?>/<?php echo $social ?>.png" width="<?php echo $width?>" height="<?php echo $width?>" alt="<?php echo $social ?>"></a>&nbsp;
51
  <?php } ?>
52
  </td>
53
  </tr>
emails/blocks/social/options.php CHANGED
@@ -1,9 +1,13 @@
1
- <?php
2
- /* @var $options array contains all the options the current block we're ediging contains */
3
- /* @var $controls NewsletterControls */
4
- /* @var $fields NewsletterFields */
5
- ?>
6
-
7
- <p>Social profiles can be configured on company info panel.</p>
8
-
9
- <?php $fields->block_commons() ?>
 
 
 
 
1
+ <?php
2
+ /* @var $options array contains all the options the current block we're ediging contains */
3
+ /* @var $controls NewsletterControls */
4
+ /* @var $fields NewsletterFields */
5
+ ?>
6
+
7
+ <p>Social profiles can be configured on company info panel.</p>
8
+
9
+ <?php $fields->select('type', 'Appearance', ['1'=>'Round colored', '2'=>'Round monochrome']) ?>
10
+
11
+ <?php $fields->select('width', 'Size', ['16'=>'16 px', '24'=>'24 px', '32'=>'32 px']) ?>
12
+
13
+ <?php $fields->block_commons() ?>
emails/emails.php CHANGED
@@ -1,1410 +1,1410 @@
1
- <?php
2
-
3
- defined('ABSPATH') || exit;
4
-
5
- class NewsletterEmails extends NewsletterModule {
6
-
7
- static $instance;
8
-
9
- const EDITOR_COMPOSER = 2;
10
- const EDITOR_HTML = 1;
11
- const EDITOR_TINYMCE = 0;
12
-
13
- static $PRESETS_LIST;
14
-
15
- const PRESET_EMAIL_TYPE = 'composer_template';
16
-
17
- // Cache
18
- var $blocks = null;
19
-
20
- /**
21
- * @return NewsletterEmails
22
- */
23
- static function instance() {
24
- if (self::$instance == null) {
25
- self::$instance = new NewsletterEmails();
26
- }
27
-
28
- return self::$instance;
29
- }
30
-
31
- function __construct() {
32
- self::$PRESETS_LIST = array("cta", "invite", "announcement", "posts", "sales", "product", "tour", "simple");
33
- $this->themes = new NewsletterThemes('emails');
34
- parent::__construct('emails', '1.1.5');
35
- add_action('newsletter_action', array($this, 'hook_newsletter_action'), 13, 3);
36
-
37
- if (is_admin()) {
38
- if (defined('DOING_AJAX') && DOING_AJAX) {
39
- add_action('wp_ajax_tnpc_render', array($this, 'tnpc_render_callback'));
40
- add_action('wp_ajax_tnpc_preview', array($this, 'tnpc_preview_callback'));
41
- add_action('wp_ajax_tnpc_css', array($this, 'tnpc_css_callback'));
42
- add_action('wp_ajax_tnpc_options', array($this, 'hook_wp_ajax_tnpc_options'));
43
- add_action('wp_ajax_tnpc_get_all_presets', array($this, 'ajax_get_all_presets'));
44
- add_action('wp_ajax_tnpc_get_preset', array($this, 'ajax_get_preset'));
45
- add_action('wp_ajax_tnpc_delete_preset', array($this, 'hook_wp_ajax_tnpc_delete_preset'));
46
- add_action('wp_ajax_tnpc_regenerate_email', array($this, 'hook_wp_ajax_tnpc_regenerate_email'));
47
- }
48
- // Thank you to plugins which add the WP editor on other admin plugin pages...
49
- if (isset($_GET['page']) && $_GET['page'] == 'newsletter_emails_edit') {
50
- global $wp_actions;
51
- $wp_actions['wp_enqueue_editor'] = 1;
52
- }
53
- }
54
- }
55
-
56
- function options_decode($options) {
57
-
58
- // Start compatibility
59
- if (is_string($options) && strpos($options, 'options[') !== false) {
60
- $opts = array();
61
- parse_str($options, $opts);
62
- $options = $opts['options'];
63
- }
64
- // End compatibility
65
-
66
- if (is_array($options)) {
67
- return $options;
68
- }
69
-
70
- $tmp = json_decode($options, true);
71
- if (is_null($tmp)) {
72
- return json_decode(base64_decode($options), true);
73
- } else {
74
- return $tmp;
75
- }
76
- }
77
-
78
- /**
79
- *
80
- * @param array $options Options array
81
- */
82
- function options_encode($options) {
83
- return base64_encode(json_encode($options, JSON_HEX_TAG | JSON_HEX_AMP));
84
- }
85
-
86
- function hook_wp_ajax_tnpc_options() {
87
- global $wpdb;
88
-
89
- // TODO: Uniform to use id everywhere
90
- // if (!isset($_REQUEST['id'])) {
91
- // $_REQUEST['id'] = $_REQUEST['b'];
92
- // }
93
-
94
- $block = $this->get_block($_REQUEST['id']);
95
- if (!$block) {
96
- die('Block not found with id ' . esc_html($_REQUEST['id']));
97
- }
98
-
99
- if (!class_exists('NewsletterControls')) {
100
- include NEWSLETTER_INCLUDES_DIR . '/controls.php';
101
- }
102
-
103
- $options = $this->options_decode(stripslashes_deep($_REQUEST['options']));
104
- $composer = isset($_POST['composer']) ? $_POST['composer'] : [];
105
-
106
- $context = array('type' => '');
107
- if (isset($_REQUEST['context_type'])) {
108
- $context['type'] = $_REQUEST['context_type'];
109
- }
110
-
111
- $controls = new NewsletterControls($options);
112
- $fields = new NewsletterFields($controls);
113
-
114
- $controls->init();
115
- echo '<input type="hidden" name="action" value="tnpc_render">';
116
- echo '<input type="hidden" name="id" value="' . esc_attr($_REQUEST['id']) . '">';
117
- echo '<input type="hidden" name="context_type" value="' . esc_attr($context['type']) . '">';
118
- $inline_edits = '';
119
- if (isset($controls->data['inline_edits'])) {
120
- $inline_edits = $controls->data['inline_edits'];
121
- }
122
- echo '<input type="hidden" name="options[inline_edits]" value="', esc_attr($this->options_encode($inline_edits)), '">';
123
-
124
- ob_start();
125
- include $block['dir'] . '/options.php';
126
- $content = ob_get_clean();
127
- echo "<h2>", esc_html($block["name"]), "</h2>";
128
- echo $content;
129
- wp_die();
130
- }
131
-
132
- /**
133
- * Retrieves the presets list (no id in GET) or a specific preset id in GET)
134
- */
135
- public function ajax_get_all_presets() {
136
- wp_send_json_success($this->get_all_preset());
137
- }
138
-
139
- public function ajax_get_preset() {
140
-
141
- if (empty($_REQUEST['id'])) {
142
- wp_send_json_error([
143
- 'msg' => __('Invalid preset ID')
144
- ]);
145
- }
146
-
147
- $preset_id = $_REQUEST['id'];
148
- $preset_content = $this->get_preset_content($preset_id);
149
- $global_options = $this->get_preset_global_options($preset_id);
150
-
151
- wp_send_json_success([
152
- 'content' => $preset_content,
153
- 'globalOptions' => $global_options,
154
- ]);
155
- }
156
-
157
- private function get_preset_content($preset_id) {
158
-
159
- $content = '';
160
-
161
- if ($this->is_a_tnp_default_preset($preset_id)) {
162
-
163
- // Get preset from file
164
- $preset = $this->get_preset_from_file($preset_id);
165
-
166
- foreach ($preset->blocks as $item) {
167
- ob_start();
168
- $this->render_block($item->block, true, (array) $item->options);
169
- $content .= trim(ob_get_clean());
170
- }
171
- } else {
172
-
173
- // Get preset from db
174
- $preset_email = $this->get_email(intval($preset_id));
175
- $global_options = $this->extract_global_options_from($preset_email);
176
- $content = $this->regenerate_email_blocks($preset_email->message, $global_options);
177
- }
178
-
179
- return $content;
180
- }
181
-
182
- private function get_preset_global_options($preset_id) {
183
-
184
- if ($this->is_a_tnp_default_preset($preset_id)) {
185
- return [];
186
- }
187
-
188
- // Get preset from db
189
- $preset_email = $this->get_email(intval($preset_id));
190
- $global_options = $this->extract_global_options_from($preset_email);
191
-
192
- return $global_options;
193
- }
194
-
195
- private function extract_global_options_from($email) {
196
- $global_options = [];
197
- foreach ($email->options as $global_option_name => $global_option) {
198
- if (strpos($global_option_name, 'composer_') === 0) {
199
- $global_options[str_replace('composer_', '', $global_option_name)] = $global_option;
200
- }
201
- }
202
-
203
- return $global_options;
204
- }
205
-
206
- private function is_a_tnp_default_preset($preset_id) {
207
- return in_array($preset_id, self::$PRESETS_LIST);
208
- }
209
-
210
- private function get_all_preset() {
211
-
212
- $content = "<div class='tnpc-preset-container'>";
213
-
214
- if ($this->is_normal_context_request()) {
215
- $content .= "<div class='tnpc-preset-legacy-themes'><a href='" . $this->get_admin_page_url('theme') . "'>" . __('Looking for legacy themes?', 'newsletter') . "</a></div>";
216
- }
217
-
218
- // LOAD USER PRESETS
219
- $user_preset_list = $this->get_emails(self::PRESET_EMAIL_TYPE);
220
-
221
- foreach ($user_preset_list as $user_preset) {
222
-
223
- $default_icon_url = NEWSLETTER_URL . "/emails/presets/default-icon.png?ver=2";
224
- $preset_name = $user_preset->subject;
225
- $delete_preset_text = __('Delete', 'newsletter');
226
- $edit_preset_text = __('Edit', 'newsletter');
227
-
228
- // esc_js() assumes the string will be in single quote (arghhh!!!)
229
- $onclick_edit = 'tnpc_edit_preset(' . ((int) $user_preset->id) . ', \'' . esc_js($preset_name) . '\', event)';
230
- $onclick_delete = 'tnpc_delete_preset(' . ((int) $user_preset->id) . ', \'' . esc_js($preset_name) . '\', event)';
231
- $onclick_load = 'tnpc_load_preset(' . ((int) $user_preset->id) . ', \'' . esc_js($preset_name) . '\', event)';
232
-
233
- $content .= "<div class='tnpc-preset' onclick='" . esc_attr($onclick_load) . "'>\n";
234
- $content .= "<img src='$default_icon_url' title='" . esc_attr($preset_name) . "' alt='" . esc_attr($preset_name) . "'>\n";
235
- $content .= "<span class='tnpc-preset-label'>" . esc_html($user_preset->subject) . "</span>\n";
236
- $content .= "<span class='tnpc-delete-preset' onclick='" . esc_attr($onclick_delete) . "' title='" . esc_attr($delete_preset_text) . "'><i class='fas fa-times'></i></span>\n";
237
- $content .= "<span class='tnpc-edit-preset' onclick='" . esc_attr($onclick_edit) . "' title='" . esc_attr($edit_preset_text) . "'><i class='fas fa-pencil-alt'></i></span>\n";
238
- $content .= "</div>";
239
- }
240
-
241
- // LOAD TNP PRESETS
242
- foreach (self::$PRESETS_LIST as $id) {
243
- $preset = $this->get_preset_from_file($id);
244
- $preset_name = esc_html($preset->name);
245
- $content .= "<div class='tnpc-preset' onclick='tnpc_load_preset(\"$id\")'>";
246
- $content .= "<img src='$preset->icon' title='$preset_name' alt='$preset_name'/>";
247
- $content .= "<span class='tnpc-preset-label'>$preset_name</span>";
248
- $content .= "</div>";
249
- }
250
-
251
- if ($this->is_normal_context_request()) {
252
- $content .= $this->get_automated_spot_element();
253
- $content .= $this->get_autoresponder_spot_element();
254
- $content .= $this->get_raw_html_preset_element();
255
- }
256
-
257
- return $content;
258
- }
259
-
260
- private function is_normal_context_request() {
261
- return empty($_REQUEST['context_type']);
262
- }
263
-
264
- private function is_automated_context_request() {
265
- return isset($_REQUEST['context_type']) && $_REQUEST['context_type'] === 'automated';
266
- }
267
-
268
- private function is_autoresponder_context_request() {
269
- return isset($_REQUEST['context_type']) && $_REQUEST['context_type'] === 'autoresponder';
270
- }
271
-
272
- private function get_automated_spot_element() {
273
- $result = "<div class='tnpc-preset'>";
274
- if (class_exists('NewsletterAutomated')) {
275
- $result .= "<a href='?page=newsletter_automated_index'>";
276
- } else {
277
- $result .= "<a href='https://www.thenewsletterplugin.com/automated?utm_source=plugin&utm_campaign=automated&utm_medium=composer'>";
278
- }
279
- $result .= "<img src='" . plugins_url('newsletter') . "/emails/images/automated.png' title='Automated addon' alt='Automated'/>";
280
- $result .= "<span class='tnpc-preset-label'>Daily, weekly and monthly newsletters</span></a>";
281
- $result .= "</div>";
282
-
283
- return $result;
284
- }
285
-
286
- private function get_autoresponder_spot_element() {
287
- $result = "<div class='tnpc-preset'>";
288
- if (class_exists('NewsletterAutoresponder')) {
289
- $result .= "<a href='?page=newsletter_autoresponder_index'>";
290
- } else {
291
- $result .= "<a href='https://www.thenewsletterplugin.com/autoresponder?utm_source=plugin&utm_campaign=autoresponder&utm_medium=composer' target='_blank'>";
292
- }
293
- $result .= "<img src='" . plugins_url('newsletter') . "/emails/images/autoresponder.png' title='Autoresponder addon' alt='Autoresponder'/>";
294
- $result .= "<span class='tnpc-preset-label'>Autoresponders</span></a>";
295
- $result .= "</div>";
296
-
297
- return $result;
298
- }
299
-
300
- private function get_raw_html_preset_element() {
301
-
302
- $result = "<div class='tnpc-preset tnpc-preset-html' onclick='location.href=\"" . wp_nonce_url('admin.php?page=newsletter_emails_new&id=rawhtml', 'newsletter-new') . "\"'>";
303
- $result .= "<img src='" . plugins_url('newsletter') . "/emails/images/rawhtml.png' title='RAW HTML' alt='RAW'/>";
304
- $result .= "<span class='tnpc-preset-label'>Raw HTML</span>";
305
- $result .= "</div>";
306
-
307
- $result .= "<div class='clear'></div>";
308
- $result .= "</div>";
309
-
310
- return $result;
311
- }
312
-
313
- /**
314
- * Check if the preset name exists and adds an incremental suffix if the name exists.
315
- *
316
- * @param string $name
317
- *
318
- * @return string
319
- */
320
- public function sanitize_preset_name($name) {
321
- global $wpdb;
322
-
323
- $name = empty($name) ? __('Empty name preset', 'newsletter') : $name;
324
- $name = sanitize_text_field($name);
325
- $type = self::PRESET_EMAIL_TYPE;
326
- $count = (int) $wpdb->get_var("SELECT COUNT(*) FROM " . NEWSLETTER_EMAILS_TABLE . " WHERE type='$type' and subject='$name'");
327
-
328
- $name = $count > 0 ? $name . " - " . ($count + 1) : $name;
329
-
330
- return $name;
331
- }
332
-
333
- function has_dynamic_blocks($theme) {
334
- preg_match_all('/data-json="(.*?)"/m', $theme, $matches, PREG_PATTERN_ORDER);
335
- foreach ($matches[1] as $match) {
336
- $a = html_entity_decode($match, ENT_QUOTES, 'UTF-8');
337
- $options = $this->options_decode($a);
338
-
339
- $block = $this->get_block($options['block_id']);
340
- if (!$block) {
341
- continue;
342
- }
343
- if ($block['type'] == 'dynamic') {
344
- return true;
345
- }
346
- }
347
- return false;
348
- }
349
-
350
- /**
351
- * Regenerates a saved composed email rendering each block. Regeneration is
352
- * conditioned (possibly) by the context. The context is usually passed to blocks
353
- * so they can act in the right manner.
354
- *
355
- * $context contains a type and, for automated, the last_run.
356
- *
357
- * $email can actually be even a string containing the full newsletter HTML code.
358
- *
359
- * @param TNP_Email $email (Rinominare)
360
- * @return string
361
- */
362
- function regenerate($email, $context = []) {
363
-
364
- // Cannot be removed due to compatibility issues with old Automated versions
365
- if (is_object($email)) {
366
- $theme = $email->message;
367
- } else {
368
- $theme = $email;
369
- }
370
-
371
- //$this->logger->debug('Starting email regeneration');
372
- //$this->logger->debug($context);
373
-
374
- if (empty($theme)) {
375
- $this->logger->debug('The email was empty');
376
- return array('body' => '', 'subject' => '');
377
- }
378
-
379
- $context = array_merge(['last_run' => 0, 'type' => ''], $context);
380
-
381
- preg_match_all('/data-json="(.*?)"/m', $theme, $matches, PREG_PATTERN_ORDER);
382
-
383
- $result = '';
384
- $subject = '';
385
-
386
- foreach ($matches[1] as $match) {
387
- $a = html_entity_decode($match, ENT_QUOTES, 'UTF-8');
388
- $options = $this->options_decode($a);
389
-
390
- $block = $this->get_block($options['block_id']);
391
- if (!$block) {
392
- $this->logger->debug('Unable to load the block ' . $options['block_id']);
393
- //continue;
394
- }
395
-
396
- ob_start();
397
- $out = $this->render_block($options['block_id'], true, $options, $context);
398
- if (is_array($out)) {
399
- if ($out['return_empty_message'] || $out['stop']) {
400
- if (is_object($email)) {
401
- return false;
402
- }
403
- return array();
404
- }
405
- if ($out['skip']) {
406
- if (NEWSLETTER_DEBUG) {
407
- $result .= 'Block removed by request';
408
- }
409
- continue;
410
- }
411
- if (empty($subject) && !empty($out['subject'])) {
412
- $subject = $out['subject'];
413
- }
414
- }
415
- $block_html = ob_get_clean();
416
- $result .= $block_html;
417
- }
418
-
419
- // We need to keep the CSS/HEAD part, the regenearion is only about blocks
420
-
421
- if (is_object($email)) {
422
- $result = TNP_Composer::get_main_wrapper_open($email) . $result . TNP_Composer::get_main_wrapper_close($email);
423
- }
424
-
425
- $x = strpos($theme, '<body');
426
- if ($x !== false) {
427
- $x = strpos($theme, '>', $x);
428
- $result = substr($theme, 0, $x + 1) . $result . '</body></html>';
429
- } else {
430
-
431
- }
432
-
433
- if (is_object($email)) {
434
- $email->message = $result;
435
- $email->subject = $subject;
436
- return true;
437
- }
438
-
439
- // Kept for compatibility
440
- return array('body' => $result, 'subject' => $subject);
441
- }
442
-
443
- function remove_block_data($text) {
444
- // TODO: Lavorare!
445
- return $text;
446
- }
447
-
448
- static function get_outlook_wrapper_open($width = 600) {
449
- return '<!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" align="center" cellspacing="0" width="' . $width . '"><tr><td width="' . $width . '" style="vertical-align:top;width:' . $width . 'px;"><![endif]-->';
450
- }
451
-
452
- static function get_outlook_wrapper_close() {
453
- return "<!--[if mso | IE]></td></tr></table><![endif]-->";
454
- }
455
-
456
- function hook_safe_style_css($rules) {
457
- $rules[] = 'display';
458
- return $rules;
459
- }
460
-
461
- /**
462
- * Renders a block identified by its id, using the block options and adding a wrapper
463
- * if required (for the first block rendering).
464
- *
465
- * @param string $block_id
466
- * @param boolean $wrapper
467
- * @param array $options
468
- * @param array $context
469
- * @param array $composer
470
- */
471
- function render_block($block_id = null, $wrapper = false, $options = [], $context = [], $composer = []) {
472
- static $kses_style_filter = false;
473
- include_once NEWSLETTER_INCLUDES_DIR . '/helper.php';
474
-
475
- //Remove 'options_composer_' prefix
476
- $composer_defaults = [];
477
- foreach (TNP_Composer::get_global_style_defaults() as $global_option_name => $global_option) {
478
- $composer_defaults[str_replace('options_composer_', '', $global_option_name)] = $global_option;
479
- }
480
- $composer = array_merge($composer_defaults, $composer);
481
-
482
- $width = 600;
483
- $font_family = 'Helvetica, Arial, sans-serif';
484
-
485
- $global_title_font_family = $composer['title_font_family'];
486
- $global_title_font_size = $composer['title_font_size'];
487
- $global_title_font_color = $composer['title_font_color'];
488
- $global_title_font_weight = $composer['title_font_weight'];
489
-
490
- $global_text_font_family = $composer['text_font_family'];
491
- $global_text_font_size = $composer['text_font_size'];
492
- $global_text_font_color = $composer['text_font_color'];
493
- $global_text_font_weight = $composer['text_font_weight'];
494
-
495
- $global_button_font_family = $composer['button_font_family'];
496
- $global_button_font_size = $composer['button_font_size'];
497
- $global_button_font_color = $composer['button_font_color'];
498
- $global_button_font_weight = $composer['button_font_weight'];
499
- $global_button_background_color = $composer['button_background_color'];
500
-
501
- $global_block_background = $composer['block_background'];
502
-
503
- $info = Newsletter::instance()->get_options('info');
504
-
505
- // Just in case...
506
- if (!is_array($options)) {
507
- $options = array();
508
- }
509
-
510
- // This code filters the HTML to remove javascript and unsecure attributes and enable the
511
- // "display" rule for CSS which is needed in blocks to force specific "block" or "inline" or "table".
512
- add_filter('safe_style_css', [$this, 'hook_safe_style_css'], 9999);
513
- $options = wp_kses_post_deep($options);
514
- remove_filter('safe_style_css', [$this, 'hook_safe_style_css']);
515
-
516
- $block_options = get_option('newsletter_main');
517
-
518
- $block = $this->get_block($block_id);
519
-
520
- if (!isset($context['type']))
521
- $context['type'] = '';
522
-
523
- // Block not found
524
- if (!$block) {
525
- if ($wrapper) {
526
- echo '<table border="0" cellpadding="0" cellspacing="0" align="center" width="100%" style="border-collapse: collapse; width: 100%;" class="tnpc-row tnpc-row-block" data-id="', esc_attr($block_id), '">';
527
- echo '<tr>';
528
- echo '<td data-options="" bgcolor="#ffffff" align="center" style="padding: 0; font-family: Helvetica, Arial, sans-serif;" class="edit-block">';
529
- }
530
- echo $this->get_outlook_wrapper_open($width);
531
-
532
- echo '<p>Ops, this block type is no more registered!</p>';
533
-
534
- echo $this->get_outlook_wrapper_close();
535
-
536
- if ($wrapper) {
537
- echo '</td></tr></table>';
538
- }
539
- return;
540
- }
541
-
542
- $out = ['subject' => '', 'return_empty_message' => false, 'stop' => false, 'skip' => false];
543
-
544
- $dir = is_rtl() ? 'rtl' : 'ltr';
545
- $align_left = is_rtl() ? 'right' : 'left';
546
- $align_right = is_rtl() ? 'left' : 'right';
547
-
548
- ob_start();
549
- $logger = $this->logger;
550
- include $block['dir'] . '/block.php';
551
- $content = trim(ob_get_clean());
552
-
553
- if (empty($content)) {
554
- return $out;
555
- }
556
-
557
- $common_defaults = array(
558
- 'block_padding_top' => 0,
559
- 'block_padding_bottom' => 0,
560
- 'block_padding_right' => 0,
561
- 'block_padding_left' => 0,
562
- 'block_background' => '',
563
- 'block_background_2' => '',
564
- 'block_width' => 600,
565
- 'block_align' => 'center'
566
- );
567
-
568
- $options = array_merge($common_defaults, $options);
569
-
570
- // Obsolete
571
- $content = str_replace('{width}', $width, $content);
572
-
573
- $content = $this->inline_css($content, true);
574
-
575
- // CSS driven by the block
576
- // Requited for the server side parsing and rendering
577
- $options['block_id'] = $block_id;
578
-
579
- $options['block_padding_top'] = (int) str_replace('px', '', $options['block_padding_top']);
580
- $options['block_padding_bottom'] = (int) str_replace('px', '', $options['block_padding_bottom']);
581
- $options['block_padding_right'] = (int) str_replace('px', '', $options['block_padding_right']);
582
- $options['block_padding_left'] = (int) str_replace('px', '', $options['block_padding_left']);
583
-
584
- $block_background = empty($options['block_background']) ? $global_block_background : $options['block_background'];
585
-
586
- // Internal TD wrapper
587
- $style = 'text-align: center; ';
588
- $style .= 'width: 100% !important; ';
589
- $style .= 'line-height: normal !important; ';
590
- $style .= 'letter-spacing: normal; ';
591
- $style .= 'padding-top: ' . $options['block_padding_top'] . 'px; ';
592
- $style .= 'padding-left: ' . $options['block_padding_left'] . 'px; ';
593
- $style .= 'padding-right: ' . $options['block_padding_right'] . 'px; ';
594
- $style .= 'padding-bottom: ' . $options['block_padding_bottom'] . 'px; ';
595
- $style .= 'background-color: ' . $block_background . ';';
596
-
597
- if (isset($options['block_background_gradient'])) {
598
- $style .= 'background: linear-gradient(180deg, ' . $block_background . ' 0%, ' . $options['block_background_2'] . ' 100%);';
599
- }
600
-
601
- $data = $this->options_encode($options);
602
- // First time block creation wrapper
603
- if ($wrapper) {
604
- echo '<table border="0" cellpadding="0" cellspacing="0" align="center" width="100%" style="border-collapse: collapse; width: 100%;" class="tnpc-row tnpc-row-block" data-id="', esc_attr($block_id), '">', "\n";
605
- echo "<tr>";
606
- echo '<td align="center" style="padding: 0;" class="edit-block">', "\n";
607
- }
608
-
609
- // Container that fixes the width and makes the block responsive
610
- echo $this->get_outlook_wrapper_open($options['block_width']);
611
-
612
- echo '<table type="options" data-json="', esc_attr($data), '" class="tnpc-block-content" border="0" cellpadding="0" align="center" cellspacing="0" width="100%" style="width: 100%!important; max-width: ', $options['block_width'], 'px!important">', "\n";
613
- echo "<tr>";
614
- echo '<td align="', esc_attr($options['block_align']), '" style="', $style, '" bgcolor="', $block_background, '" width="100%">';
615
-
616
- //echo "<!-- block generated content -->\n";
617
- echo trim($content);
618
- //echo "\n<!-- /block generated content -->\n";
619
-
620
- echo "</td></tr></table>";
621
- echo $this->get_outlook_wrapper_close();
622
-
623
- // First time block creation wrapper
624
- if ($wrapper) {
625
- echo "</td></tr></table>";
626
- }
627
-
628
- return $out;
629
- }
630
-
631
- /**
632
- * Ajax call to render a block with a new set of options after the settings popup
633
- * has been saved.
634
- *
635
- * @param type $block_id
636
- * @param type $wrapper
637
- */
638
- function tnpc_render_callback() {
639
- if (!check_ajax_referer('save')) {
640
- $this->dienow('Expired request');
641
- }
642
-
643
- $block_id = $_POST['id'];
644
- $wrapper = isset($_POST['full']);
645
- $options = $this->restore_options_from_request();
646
-
647
- $this->render_block($block_id, $wrapper, $options, [], $_POST['composer']);
648
- wp_die();
649
- }
650
-
651
- function hook_wp_ajax_tnpc_regenerate_email() {
652
-
653
- $content = stripslashes($_POST['content']);
654
- $global_options = $_POST['composer'];
655
-
656
- $regenerated_content = $this->regenerate_email_blocks($content, $global_options);
657
-
658
- wp_send_json_success([
659
- 'content' => $regenerated_content,
660
- 'message' => __('Successfully updated', 'newsletter')
661
- ]);
662
- }
663
-
664
- private function regenerate_email_blocks($content, $global_options) {
665
-
666
- $raw_block_options = $this->extract_encoded_blocks_options($content);
667
-
668
- $regenerated_content = '';
669
-
670
- foreach ($raw_block_options as $raw_block_option) {
671
-
672
- /* $a = html_entity_decode( $raw_block_option, ENT_QUOTES, 'UTF-8' );
673
- $block_options = $this->options_decode( $a ); */
674
-
675
- $block_options = $this->options_decode($raw_block_option);
676
-
677
- $block = $this->get_block($block_options['block_id']);
678
- if (!$block) {
679
- $this->logger->debug('Unable to load the block ' . $block_options['block_id']);
680
- }
681
-
682
- ob_start();
683
- $this->render_block($block_options['block_id'], true, $block_options, [], $global_options);
684
- $block_html = ob_get_clean();
685
-
686
- $regenerated_content .= $block_html;
687
- }
688
-
689
- return $regenerated_content;
690
- }
691
-
692
- /**
693
- * @param string $html_email_content Email html content
694
- *
695
- * @return string[] Encoded options of email blocks
696
- */
697
- private function extract_encoded_blocks_options($html_email_content) {
698
-
699
- preg_match_all('/data-json="(.*?)"/m', $html_email_content, $raw_block_options, PREG_PATTERN_ORDER);
700
-
701
- return $raw_block_options[1];
702
- }
703
-
704
- function tnpc_preview_callback() {
705
- $email = Newsletter::instance()->get_email($_REQUEST['id'], ARRAY_A);
706
-
707
- if (empty($email)) {
708
- echo 'Wrong email identifier';
709
- return;
710
- }
711
-
712
- echo $email['message'];
713
-
714
- wp_die();
715
- }
716
-
717
- function tnpc_css_callback() {
718
- include NEWSLETTER_DIR . '/emails/tnp-composer/css/newsletter.css';
719
- wp_die();
720
- }
721
-
722
- /** Returns the correct admin page to edit the newsletter with the correct editor. */
723
- function get_editor_url($email_id, $editor_type) {
724
- switch ($editor_type) {
725
- case NewsletterEmails::EDITOR_COMPOSER:
726
- return admin_url("admin.php") . '?page=newsletter_emails_composer&id=' . $email_id;
727
- case NewsletterEmails::EDITOR_HTML:
728
- return admin_url("admin.php") . '?page=newsletter_emails_editorhtml&id=' . $email_id;
729
- case NewsletterEmails::EDITOR_TINYMCE:
730
- return admin_url("admin.php") . '?page=newsletter_emails_editortinymce&id=' . $email_id;
731
- }
732
- }
733
-
734
- /**
735
- * Returns the button linked to the correct "edit" page for the passed newsletter. The edit page can be an editor
736
- * or the targeting page (it depends on newsletter status).
737
- *
738
- * @param TNP_Email $email
739
- */
740
- function get_edit_button($email, $only_icon = false) {
741
-
742
- $editor_type = $this->get_editor_type($email);
743
- if ($email->status == 'new') {
744
- $edit_url = $this->get_editor_url($email->id, $editor_type);
745
- } else {
746
- $edit_url = 'admin.php?page=newsletter_emails_edit&id=' . $email->id;
747
- }
748
- switch ($editor_type) {
749
- case NewsletterEmails::EDITOR_COMPOSER:
750
- $icon_class = 'th-large';
751
- break;
752
- case NewsletterEmails::EDITOR_HTML:
753
- $icon_class = 'code';
754
- break;
755
- default:
756
- $icon_class = 'edit';
757
- break;
758
- }
759
- if ($only_icon) {
760
- return '<a class="button-primary" href="' . $edit_url . '" title="' . esc_attr__('Edit', 'newsletter') . '">' .
761
- '<i class="fas fa-' . $icon_class . '"></i></a>';
762
- } else {
763
- return '<a class="button-primary" href="' . $edit_url . '" title="' . esc_attr__('Edit', 'newsletter') . '">' .
764
- '<i class="fas fa-' . $icon_class . '"></i> ' . __('Edit', 'newsletter') . '</a>';
765
- }
766
- }
767
-
768
- /** Returns the correct editor type for the provided newsletter. Contains backward compatibility code. */
769
- function get_editor_type($email) {
770
- $email = (object) $email;
771
- $editor_type = $email->editor;
772
-
773
- // Backward compatibility
774
- $email_options = maybe_unserialize($email->options);
775
- if (isset($email_options['composer'])) {
776
- $editor_type = NewsletterEmails::EDITOR_COMPOSER;
777
- }
778
- // End backward compatibility
779
-
780
- return $editor_type;
781
- }
782
-
783
- /**
784
- *
785
- * @param type $action
786
- * @param type $user
787
- * @param type $email
788
- * @return type
789
- * @global wpdb $wpdb
790
- */
791
- function hook_newsletter_action($action, $user, $email) {
792
- global $wpdb;
793
-
794
- switch ($action) {
795
- case 'v':
796
- case 'view':
797
- $id = $_GET['id'];
798
- if ($id == 'last') {
799
- $email = $wpdb->get_row("select * from " . NEWSLETTER_EMAILS_TABLE . " where private=0 and type='message' and status='sent' order by send_on desc limit 1");
800
- } else {
801
- $email = $this->get_email($_GET['id']);
802
- }
803
- if (empty($email)) {
804
- header("HTTP/1.0 404 Not Found");
805
- die('Email not found');
806
- }
807
-
808
- if (!Newsletter::instance()->is_allowed()) {
809
-
810
- if ($email->status == 'new') {
811
- header("HTTP/1.0 404 Not Found");
812
- die('Not sent yet');
813
- }
814
-
815
- if ($email->private == 1) {
816
- if (!$user) {
817
- header("HTTP/1.0 404 Not Found");
818
- die('No available for online view');
819
- }
820
- $sent = $wpdb->get_row($wpdb->prepare("select * from " . NEWSLETTER_SENT_TABLE . " where email_id=%d and user_id=%d limit 1", $email->id, $user->id));
821
- if (!$sent) {
822
- header("HTTP/1.0 404 Not Found");
823
- die('No available for online view');
824
- }
825
- }
826
- }
827
-
828
-
829
- header('Content-Type: text/html;charset=UTF-8');
830
- header('X-Robots-Tag: noindex,nofollow,noarchive');
831
- header('Cache-Control: no-cache,no-store,private');
832
-
833
- echo $this->replace($email->message, $user, $email);
834
-
835
- die();
836
- break;
837
-
838
- case 'emails-css':
839
- $email_id = (int) $_GET['id'];
840
-
841
- $body = Newsletter::instance()->get_email_field($email_id, 'message');
842
-
843
- $x = strpos($body, '<style');
844
- if ($x === false)
845
- return;
846
-
847
- $x = strpos($body, '>', $x);
848
- $y = strpos($body, '</style>');
849
-
850
- header('Content-Type: text/css;charset=UTF-8');
851
-
852
- echo substr($body, $x + 1, $y - $x - 1);
853
-
854
- die();
855
- break;
856
-
857
- case 'emails-composer-css':
858
- header('Cache: no-cache');
859
- header('Content-Type: text/css');
860
- echo $this->get_composer_css();
861
- die();
862
- break;
863
-
864
- case 'emails-preview':
865
- if (!Newsletter::instance()->is_allowed()) {
866
- die('Not enough privileges');
867
- }
868
-
869
- if (!check_admin_referer('view')) {
870
- die();
871
- }
872
-
873
- $theme_id = $_GET['id'];
874
- $theme = $this->themes->get_theme($theme_id);
875
-
876
- // Used by theme code
877
- $theme_options = $this->themes->get_options($theme_id);
878
-
879
- $theme_url = $theme['url'];
880
-
881
- header('Content-Type: text/html;charset=UTF-8');
882
-
883
- include $theme['dir'] . '/theme.php';
884
-
885
- die();
886
- break;
887
-
888
- case 'emails-preview-text':
889
- header('Content-Type: text/plain;charset=UTF-8');
890
- if (!Newsletter::instance()->is_allowed()) {
891
- die('Not enough privileges');
892
- }
893
-
894
- if (!check_admin_referer('view')) {
895
- die();
896
- }
897
-
898
- // Used by theme code
899
- $theme_options = $this->get_current_theme_options();
900
-
901
- $file = include $theme['dir'] . '/theme-text.php';
902
-
903
- if (is_file($file)) {
904
- include $file;
905
- }
906
-
907
- die();
908
- break;
909
-
910
-
911
- case 'emails-create':
912
- // Newsletter from themes are created on frontend context because sometime WP themes change the way the content,
913
- // excerpt, thumbnail are extracted.
914
- if (!Newsletter::instance()->is_allowed()) {
915
- die('Not enough privileges');
916
- }
917
-
918
- require_once NEWSLETTER_INCLUDES_DIR . '/controls.php';
919
- $controls = new NewsletterControls();
920
-
921
- if (!$controls->is_action('create')) {
922
- die('Wrong call');
923
- }
924
-
925
- $theme_id = $controls->data['id'];
926
- $theme = $this->themes->get_theme($theme_id);
927
-
928
- if (!$theme) {
929
- die('invalid theme');
930
- }
931
-
932
- $this->themes->save_options($theme_id, $controls->data);
933
-
934
- $email = array();
935
- $email['status'] = 'new';
936
- $email['subject'] = ''; //__('Here the email subject', 'newsletter');
937
- $email['track'] = 1;
938
- $email['send_on'] = time();
939
- $email['editor'] = NewsletterEmails::EDITOR_TINYMCE;
940
- $email['type'] = 'message';
941
-
942
- $theme_options = $this->themes->get_options($theme_id);
943
-
944
- $theme_url = $theme['url'];
945
- $theme_subject = '';
946
-
947
- ob_start();
948
- include $theme['dir'] . '/theme.php';
949
- $email['message'] = ob_get_clean();
950
-
951
-
952
- if (!empty($theme_subject)) {
953
- $email['subject'] = $theme_subject;
954
- }
955
-
956
- if (file_exists($theme['dir'] . '/theme-text.php')) {
957
- ob_start();
958
- include $theme['dir'] . '/theme-text.php';
959
- $email['message_text'] = ob_get_clean();
960
- } else {
961
- $email['message_text'] = 'You need a modern email client to read this email. Read it online: {email_url}.';
962
- }
963
-
964
- $email = $this->save_email($email);
965
-
966
- $edit_url = $this->get_editor_url($email->id, $email->editor);
967
-
968
- header('Location: ' . $edit_url);
969
-
970
- die();
971
- break;
972
- }
973
- }
974
-
975
- function admin_menu() {
976
- $this->add_menu_page('index', 'Newsletters');
977
- $this->add_admin_page('list', 'Email List');
978
- $this->add_admin_page('new', 'Email New');
979
- $this->add_admin_page('edit', 'Email Edit');
980
- $this->add_admin_page('theme', 'Email Themes');
981
- $this->add_admin_page('composer', 'The Composer');
982
- $this->add_admin_page('editorhtml', 'HTML Editor');
983
- $this->add_admin_page('editortinymce', 'TinyMCE Editor');
984
- }
985
-
986
- /**
987
- * Builds a block data structure starting from the folder containing the block
988
- * files.
989
- *
990
- * @param string $dir
991
- * @return array | WP_Error
992
- */
993
- function build_block($dir) {
994
- $dir = realpath($dir);
995
- $dir = wp_normalize_path($dir);
996
- $full_file = $dir . '/block.php';
997
- if (!is_file($full_file)) {
998
- return new WP_Error('1', 'Missing block.php file in ' . $dir);
999
- }
1000
-
1001
- $relative_dir = substr($dir, strlen(WP_CONTENT_DIR));
1002
- $file = basename($dir);
1003
-
1004
- $data = get_file_data($full_file, ['name' => 'Name', 'section' => 'Section', 'description' => 'Description', 'type' => 'Type']);
1005
- $defaults = ['section' => 'content', 'name' => ucfirst($file), 'descritpion' => '', 'icon' => NEWSLETTER_URL . '/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=plugin&utm_campaign=automated&utm_medium=composer'>";
278
+ }
279
+ $result .= "<img src='" . plugins_url('newsletter') . "/emails/images/automated.png' title='Automated addon' alt='Automated'/>";
280
+ $result .= "<span class='tnpc-preset-label'>Daily, weekly and monthly newsletters</span></a>";
281
+ $result .= "</div>";
282
+
283
+ return $result;
284
+ }
285
+
286
+ private function get_autoresponder_spot_element() {
287
+ $result = "<div class='tnpc-preset'>";
288
+ if (class_exists('NewsletterAutoresponder')) {
289
+ $result .= "<a href='?page=newsletter_autoresponder_index'>";
290
+ } else {
291
+ $result .= "<a href='https://www.thenewsletterplugin.com/autoresponder?utm_source=plugin&utm_campaign=autoresponder&utm_medium=composer' target='_blank'>";
292
+ }
293
+ $result .= "<img src='" . plugins_url('newsletter') . "/emails/images/autoresponder.png' title='Autoresponder addon' alt='Autoresponder'/>";
294
+ $result .= "<span class='tnpc-preset-label'>Autoresponders</span></a>";
295
+ $result .= "</div>";
296
+
297
+ return $result;
298
+ }
299
+
300
+ private function get_raw_html_preset_element() {
301
+
302
+ $result = "<div class='tnpc-preset tnpc-preset-html' onclick='location.href=\"" . wp_nonce_url('admin.php?page=newsletter_emails_new&id=rawhtml', 'newsletter-new') . "\"'>";
303
+ $result .= "<img src='" . plugins_url('newsletter') . "/emails/images/rawhtml.png' title='RAW HTML' alt='RAW'/>";
304
+ $result .= "<span class='tnpc-preset-label'>Raw HTML</span>";
305
+ $result .= "</div>";
306
+
307
+ $result .= "<div class='clear'></div>";
308
+ $result .= "</div>";
309
+
310
+ return $result;
311
+ }
312
+
313
+ /**
314
+ * Check if the preset name exists and adds an incremental suffix if the name exists.
315
+ *
316
+ * @param string $name
317
+ *
318
+ * @return string
319
+ */
320
+ public function sanitize_preset_name($name) {
321
+ global $wpdb;
322
+
323
+ $name = empty($name) ? __('Empty name preset', 'newsletter') : $name;
324
+ $name = sanitize_text_field($name);
325
+ $type = self::PRESET_EMAIL_TYPE;
326
+ $count = (int) $wpdb->get_var("SELECT COUNT(*) FROM " . NEWSLETTER_EMAILS_TABLE . " WHERE type='$type' and subject='$name'");
327
+
328
+ $name = $count > 0 ? $name . " - " . ($count + 1) : $name;
329
+
330
+ return $name;
331
+ }
332
+
333
+ function has_dynamic_blocks($theme) {
334
+ preg_match_all('/data-json="(.*?)"/m', $theme, $matches, PREG_PATTERN_ORDER);
335
+ foreach ($matches[1] as $match) {
336
+ $a = html_entity_decode($match, ENT_QUOTES, 'UTF-8');
337
+ $options = $this->options_decode($a);
338
+
339
+ $block = $this->get_block($options['block_id']);
340
+ if (!$block) {
341
+ continue;
342
+ }
343
+ if ($block['type'] == 'dynamic') {
344
+ return true;
345
+ }
346
+ }
347
+ return false;
348
+ }
349
+
350
+ /**
351
+ * Regenerates a saved composed email rendering each block. Regeneration is
352
+ * conditioned (possibly) by the context. The context is usually passed to blocks
353
+ * so they can act in the right manner.
354
+ *
355
+ * $context contains a type and, for automated, the last_run.
356
+ *
357
+ * $email can actually be even a string containing the full newsletter HTML code.
358
+ *
359
+ * @param TNP_Email $email (Rinominare)
360
+ * @return string
361
+ */
362
+ function regenerate($email, $context = []) {
363
+
364
+ // Cannot be removed due to compatibility issues with old Automated versions
365
+ if (is_object($email)) {
366
+ $theme = $email->message;
367
+ } else {
368
+ $theme = $email;
369
+ }
370
+
371
+ //$this->logger->debug('Starting email regeneration');
372
+ //$this->logger->debug($context);
373
+
374
+ if (empty($theme)) {
375
+ $this->logger->debug('The email was empty');
376
+ return array('body' => '', 'subject' => '');
377
+ }
378
+
379
+ $context = array_merge(['last_run' => 0, 'type' => ''], $context);
380
+
381
+ preg_match_all('/data-json="(.*?)"/m', $theme, $matches, PREG_PATTERN_ORDER);
382
+
383
+ $result = '';
384
+ $subject = '';
385
+
386
+ foreach ($matches[1] as $match) {
387
+ $a = html_entity_decode($match, ENT_QUOTES, 'UTF-8');
388
+ $options = $this->options_decode($a);
389
+
390
+ $block = $this->get_block($options['block_id']);
391
+ if (!$block) {
392
+ $this->logger->debug('Unable to load the block ' . $options['block_id']);
393
+ //continue;
394
+ }
395
+
396
+ ob_start();
397
+ $out = $this->render_block($options['block_id'], true, $options, $context);
398
+ if (is_array($out)) {
399
+ if ($out['return_empty_message'] || $out['stop']) {
400
+ if (is_object($email)) {
401
+ return false;
402
+ }
403
+ return array();
404
+ }
405
+ if ($out['skip']) {
406
+ if (NEWSLETTER_DEBUG) {
407
+ $result .= 'Block removed by request';
408
+ }
409
+ continue;
410
+ }
411
+ if (empty($subject) && !empty($out['subject'])) {
412
+ $subject = $out['subject'];
413
+ }
414
+ }
415
+ $block_html = ob_get_clean();
416
+ $result .= $block_html;
417
+ }
418
+
419
+ // We need to keep the CSS/HEAD part, the regenearion is only about blocks
420
+
421
+ if (is_object($email)) {
422
+ $result = TNP_Composer::get_main_wrapper_open($email) . $result . TNP_Composer::get_main_wrapper_close($email);
423
+ }
424
+
425
+ $x = strpos($theme, '<body');
426
+ if ($x !== false) {
427
+ $x = strpos($theme, '>', $x);
428
+ $result = substr($theme, 0, $x + 1) . $result . '</body></html>';
429
+ } else {
430
+
431
+ }
432
+
433
+ if (is_object($email)) {
434
+ $email->message = $result;
435
+ $email->subject = $subject;
436
+ return true;
437
+ }
438
+
439
+ // Kept for compatibility
440
+ return array('body' => $result, 'subject' => $subject);
441
+ }
442
+
443
+ function remove_block_data($text) {
444
+ // TODO: Lavorare!
445
+ return $text;
446
+ }
447
+
448
+ static function get_outlook_wrapper_open($width = 600) {
449
+ return '<!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" align="center" cellspacing="0" width="' . $width . '"><tr><td width="' . $width . '" style="vertical-align:top;width:' . $width . 'px;"><![endif]-->';
450
+ }
451
+
452
+ static function get_outlook_wrapper_close() {
453
+ return "<!--[if mso | IE]></td></tr></table><![endif]-->";
454
+ }
455
+
456
+ function hook_safe_style_css($rules) {
457
+ $rules[] = 'display';
458
+ return $rules;
459
+ }
460
+
461
+ /**
462
+ * Renders a block identified by its id, using the block options and adding a wrapper
463
+ * if required (for the first block rendering).
464
+ *
465
+ * @param string $block_id
466
+ * @param boolean $wrapper
467
+ * @param array $options
468
+ * @param array $context
469
+ * @param array $composer
470
+ */
471
+ function render_block($block_id = null, $wrapper = false, $options = [], $context = [], $composer = []) {
472
+ static $kses_style_filter = false;
473
+ include_once NEWSLETTER_INCLUDES_DIR . '/helper.php';
474
+
475
+ //Remove 'options_composer_' prefix
476
+ $composer_defaults = [];
477
+ foreach (TNP_Composer::get_global_style_defaults() as $global_option_name => $global_option) {
478
+ $composer_defaults[str_replace('options_composer_', '', $global_option_name)] = $global_option;
479
+ }
480
+ $composer = array_merge($composer_defaults, $composer);
481
+
482
+ $width = 600;
483
+ $font_family = 'Helvetica, Arial, sans-serif';
484
+
485
+ $global_title_font_family = $composer['title_font_family'];
486
+ $global_title_font_size = $composer['title_font_size'];
487
+ $global_title_font_color = $composer['title_font_color'];
488
+ $global_title_font_weight = $composer['title_font_weight'];
489
+
490
+ $global_text_font_family = $composer['text_font_family'];
491
+ $global_text_font_size = $composer['text_font_size'];
492
+ $global_text_font_color = $composer['text_font_color'];
493
+ $global_text_font_weight = $composer['text_font_weight'];
494
+
495
+ $global_button_font_family = $composer['button_font_family'];
496
+ $global_button_font_size = $composer['button_font_size'];
497
+ $global_button_font_color = $composer['button_font_color'];
498
+ $global_button_font_weight = $composer['button_font_weight'];
499
+ $global_button_background_color = $composer['button_background_color'];
500
+
501
+ $global_block_background = $composer['block_background'];
502
+
503
+ $info = Newsletter::instance()->get_options('info');
504
+
505
+ // Just in case...
506
+ if (!is_array($options)) {
507
+ $options = array();
508
+ }
509
+
510
+ // This code filters the HTML to remove javascript and unsecure attributes and enable the
511
+ // "display" rule for CSS which is needed in blocks to force specific "block" or "inline" or "table".
512
+ add_filter('safe_style_css', [$this, 'hook_safe_style_css'], 9999);
513
+ $options = wp_kses_post_deep($options);
514
+ remove_filter('safe_style_css', [$this, 'hook_safe_style_css']);
515
+
516
+ $block_options = get_option('newsletter_main');
517
+
518
+ $block = $this->get_block($block_id);
519
+
520
+ if (!isset($context['type']))
521
+ $context['type'] = '';
522
+
523
+ // Block not found
524
+ if (!$block) {
525
+ if ($wrapper) {
526
+ echo '<table border="0" cellpadding="0" cellspacing="0" align="center" width="100%" style="border-collapse: collapse; width: 100%;" class="tnpc-row tnpc-row-block" data-id="', esc_attr($block_id), '">';
527
+ echo '<tr>';
528
+ echo '<td data-options="" bgcolor="#ffffff" align="center" style="padding: 0; font-family: Helvetica, Arial, sans-serif;" class="edit-block">';
529
+ }
530
+ echo $this->get_outlook_wrapper_open($width);
531
+
532
+ echo '<p>Ops, this block type is no more registered!</p>';
533
+
534
+ echo $this->get_outlook_wrapper_close();
535
+
536
+ if ($wrapper) {
537
+ echo '</td></tr></table>';
538
+ }
539
+ return;
540
+ }
541
+
542
+ $out = ['subject' => '', 'return_empty_message' => false, 'stop' => false, 'skip' => false];
543
+
544
+ $dir = is_rtl() ? 'rtl' : 'ltr';
545
+ $align_left = is_rtl() ? 'right' : 'left';
546
+ $align_right = is_rtl() ? 'left' : 'right';
547
+
548
+ ob_start();
549
+ $logger = $this->logger;
550
+ include $block['dir'] . '/block.php';
551
+ $content = trim(ob_get_clean());
552
+
553
+ if (empty($content)) {
554
+ return $out;
555
+ }
556
+
557
+ $common_defaults = array(
558
+ 'block_padding_top' => 0,
559
+ 'block_padding_bottom' => 0,
560
+ 'block_padding_right' => 0,
561
+ 'block_padding_left' => 0,
562
+ 'block_background' => '',
563
+ 'block_background_2' => '',
564
+ 'block_width' => 600,
565
+ 'block_align' => 'center'
566
+ );
567
+
568
+ $options = array_merge($common_defaults, $options);
569
+
570
+ // Obsolete
571
+ $content = str_replace('{width}', $width, $content);
572
+
573
+ $content = $this->inline_css($content, true);
574
+
575
+ // CSS driven by the block
576
+ // Requited for the server side parsing and rendering
577
+ $options['block_id'] = $block_id;
578
+
579
+ $options['block_padding_top'] = (int) str_replace('px', '', $options['block_padding_top']);
580
+ $options['block_padding_bottom'] = (int) str_replace('px', '', $options['block_padding_bottom']);
581
+ $options['block_padding_right'] = (int) str_replace('px', '', $options['block_padding_right']);
582
+ $options['block_padding_left'] = (int) str_replace('px', '', $options['block_padding_left']);
583
+
584
+ $block_background = empty($options['block_background']) ? $global_block_background : $options['block_background'];
585
+
586
+ // Internal TD wrapper
587
+ $style = 'text-align: center; ';
588
+ $style .= 'width: 100% !important; ';
589
+ $style .= 'line-height: normal !important; ';
590
+ $style .= 'letter-spacing: normal; ';
591
+ $style .= 'padding-top: ' . $options['block_padding_top'] . 'px; ';
592
+ $style .= 'padding-left: ' . $options['block_padding_left'] . 'px; ';
593
+ $style .= 'padding-right: ' . $options['block_padding_right'] . 'px; ';
594
+ $style .= 'padding-bottom: ' . $options['block_padding_bottom'] . 'px; ';
595
+ $style .= 'background-color: ' . $block_background . ';';
596
+
597
+ if (isset($options['block_background_gradient'])) {
598
+ $style .= 'background: linear-gradient(180deg, ' . $block_background . ' 0%, ' . $options['block_background_2'] . ' 100%);';
599
+ }
600
+
601
+ $data = $this->options_encode($options);
602
+ // First time block creation wrapper
603
+ if ($wrapper) {
604
+ echo '<table border="0" cellpadding="0" cellspacing="0" align="center" width="100%" style="border-collapse: collapse; width: 100%;" class="tnpc-row tnpc-row-block" data-id="', esc_attr($block_id), '">', "\n";
605
+ echo "<tr>";
606
+ echo '<td align="center" style="padding: 0;" class="edit-block">', "\n";
607
+ }
608
+
609
+ // Container that fixes the width and makes the block responsive
610
+ echo $this->get_outlook_wrapper_open($options['block_width']);
611
+
612
+ echo '<table type="options" data-json="', esc_attr($data), '" class="tnpc-block-content" border="0" cellpadding="0" align="center" cellspacing="0" width="100%" style="width: 100%!important; max-width: ', $options['block_width'], 'px!important">', "\n";
613
+ echo "<tr>";
614
+ echo '<td align="', esc_attr($options['block_align']), '" style="', $style, '" bgcolor="', $block_background, '" width="100%">';
615
+
616
+ //echo "<!-- block generated content -->\n";
617
+ echo trim($content);
618
+ //echo "\n<!-- /block generated content -->\n";
619
+
620
+ echo "</td></tr></table>";
621
+ echo $this->get_outlook_wrapper_close();
622
+
623
+ // First time block creation wrapper
624
+ if ($wrapper) {
625
+ echo "</td></tr></table>";
626
+ }
627
+
628
+ return $out;
629
+ }
630
+
631
+ /**
632
+ * Ajax call to render a block with a new set of options after the settings popup
633
+ * has been saved.
634
+ *
635
+ * @param type $block_id
636
+ * @param type $wrapper
637
+ */
638
+ function tnpc_render_callback() {
639
+ if (!check_ajax_referer('save')) {
640
+ $this->dienow('Expired request');
641
+ }
642
+
643
+ $block_id = $_POST['id'];
644
+ $wrapper = isset($_POST['full']);
645
+ $options = $this->restore_options_from_request();
646
+
647
+ $this->render_block($block_id, $wrapper, $options, [], $_POST['composer']);
648
+ wp_die();
649
+ }
650
+
651
+ function hook_wp_ajax_tnpc_regenerate_email() {
652
+
653
+ $content = stripslashes($_POST['content']);
654
+ $global_options = $_POST['composer'];
655
+
656
+ $regenerated_content = $this->regenerate_email_blocks($content, $global_options);
657
+
658
+ wp_send_json_success([
659
+ 'content' => $regenerated_content,
660
+ 'message' => __('Successfully updated', 'newsletter')
661
+ ]);
662
+ }
663
+
664
+ private function regenerate_email_blocks($content, $global_options) {
665
+
666
+ $raw_block_options = $this->extract_encoded_blocks_options($content);
667
+
668
+ $regenerated_content = '';
669
+
670
+ foreach ($raw_block_options as $raw_block_option) {
671
+
672
+ /* $a = html_entity_decode( $raw_block_option, ENT_QUOTES, 'UTF-8' );
673
+ $block_options = $this->options_decode( $a ); */
674
+
675
+ $block_options = $this->options_decode($raw_block_option);
676
+
677
+ $block = $this->get_block($block_options['block_id']);
678
+ if (!$block) {
679
+ $this->logger->debug('Unable to load the block ' . $block_options['block_id']);
680
+ }
681
+
682
+ ob_start();
683
+ $this->render_block($block_options['block_id'], true, $block_options, [], $global_options);
684
+ $block_html = ob_get_clean();
685
+
686
+ $regenerated_content .= $block_html;
687
+ }
688
+
689
+ return $regenerated_content;
690
+ }
691
+
692
+ /**
693
+ * @param string $html_email_content Email html content
694
+ *
695
+ * @return string[] Encoded options of email blocks
696
+ */
697
+ private function extract_encoded_blocks_options($html_email_content) {
698
+
699
+ preg_match_all('/data-json="(.*?)"/m', $html_email_content, $raw_block_options, PREG_PATTERN_ORDER);
700
+
701
+ return $raw_block_options[1];
702
+ }
703
+
704
+ function tnpc_preview_callback() {
705
+ $email = Newsletter::instance()->get_email($_REQUEST['id'], ARRAY_A);
706
+
707
+ if (empty($email)) {
708
+ echo 'Wrong email identifier';
709
+ return;
710
+ }
711
+
712
+ echo $email['message'];
713
+
714
+ wp_die();
715
+ }
716
+
717
+ function tnpc_css_callback() {
718
+ include NEWSLETTER_DIR . '/emails/tnp-composer/css/newsletter.css';
719
+ wp_die();
720
+ }
721
+
722
+ /** Returns the correct admin page to edit the newsletter with the correct editor. */
723
+ function get_editor_url($email_id, $editor_type) {
724
+ switch ($editor_type) {
725
+ case NewsletterEmails::EDITOR_COMPOSER:
726
+ return admin_url("admin.php") . '?page=newsletter_emails_composer&id=' . $email_id;
727
+ case NewsletterEmails::EDITOR_HTML:
728
+ return admin_url("admin.php") . '?page=newsletter_emails_editorhtml&id=' . $email_id;
729
+ case NewsletterEmails::EDITOR_TINYMCE:
730
+ return admin_url("admin.php") . '?page=newsletter_emails_editortinymce&id=' . $email_id;
731
+ }
732
+ }
733
+
734
+ /**
735
+ * Returns the button linked to the correct "edit" page for the passed newsletter. The edit page can be an editor
736
+ * or the targeting page (it depends on newsletter status).
737
+ *
738
+ * @param TNP_Email $email
739
+ */
740
+ function get_edit_button($email, $only_icon = false) {
741
+
742
+ $editor_type = $this->get_editor_type($email);
743
+ if ($email->status == 'new') {
744
+ $edit_url = $this->get_editor_url($email->id, $editor_type);
745
+ } else {
746
+ $edit_url = 'admin.php?page=newsletter_emails_edit&id=' . $email->id;
747
+ }
748
+ switch ($editor_type) {
749
+ case NewsletterEmails::EDITOR_COMPOSER:
750
+ $icon_class = 'th-large';
751
+ break;
752
+ case NewsletterEmails::EDITOR_HTML:
753
+ $icon_class = 'code';
754
+ break;
755
+ default:
756
+ $icon_class = 'edit';
757
+ break;
758
+ }
759
+ if ($only_icon) {
760
+ return '<a class="button-primary" href="' . $edit_url . '" title="' . esc_attr__('Edit', 'newsletter') . '">' .
761
+ '<i class="fas fa-' . $icon_class . '"></i></a>';
762
+ } else {
763
+ return '<a class="button-primary" href="' . $edit_url . '" title="' . esc_attr__('Edit', 'newsletter') . '">' .
764
+ '<i class="fas fa-' . $icon_class . '"></i> ' . __('Edit', 'newsletter') . '</a>';
765
+ }
766
+ }
767
+
768
+ /** Returns the correct editor type for the provided newsletter. Contains backward compatibility code. */
769
+ function get_editor_type($email) {
770
+ $email = (object) $email;
771
+ $editor_type = $email->editor;
772
+
773
+ // Backward compatibility
774
+ $email_options = maybe_unserialize($email->options);
775
+ if (isset($email_options['composer'])) {
776
+ $editor_type = NewsletterEmails::EDITOR_COMPOSER;
777
+ }
778
+ // End backward compatibility
779
+
780
+ return $editor_type;
781
+ }
782
+
783
+ /**
784
+ *
785
+ * @param type $action
786
+ * @param type $user
787
+ * @param type $email
788
+ * @return type
789
+ * @global wpdb $wpdb
790
+ */
791
+ function hook_newsletter_action($action, $user, $email) {
792
+ global $wpdb;
793
+
794
+ switch ($action) {
795
+ case 'v':
796
+ case 'view':
797
+ $id = $_GET['id'];
798
+ if ($id == 'last') {
799
+ $email = $wpdb->get_row("select * from " . NEWSLETTER_EMAILS_TABLE . " where private=0 and type='message' and status='sent' order by send_on desc limit 1");
800
+ } else {
801
+ $email = $this->get_email($_GET['id']);
802
+ }
803
+ if (empty($email)) {
804
+ header("HTTP/1.0 404 Not Found");
805
+ die('Email not found');
806
+ }
807
+
808
+ if (!Newsletter::instance()->is_allowed()) {
809
+
810
+ if ($email->status == 'new') {
811
+ header("HTTP/1.0 404 Not Found");
812
+ die('Not sent yet');
813
+ }
814
+
815
+ if ($email->private == 1) {
816
+ if (!$user) {
817
+ header("HTTP/1.0 404 Not Found");
818
+ die('No available for online view');
819
+ }
820
+ $sent = $wpdb->get_row($wpdb->prepare("select * from " . NEWSLETTER_SENT_TABLE . " where email_id=%d and user_id=%d limit 1", $email->id, $user->id));
821
+ if (!$sent) {
822
+ header("HTTP/1.0 404 Not Found");
823
+ die('No available for online view');
824
+ }
825
+ }
826
+ }
827
+
828
+
829
+ header('Content-Type: text/html;charset=UTF-8');
830
+ header('X-Robots-Tag: noindex,nofollow,noarchive');
831
+ header('Cache-Control: no-cache,no-store,private');
832
+
833
+ echo $this->replace($email->message, $user, $email);
834
+
835
+ die();
836
+ break;
837
+
838
+ case 'emails-css':
839
+ $email_id = (int) $_GET['id'];
840
+
841
+ $body = Newsletter::instance()->get_email_field($email_id, 'message');
842
+
843
+ $x = strpos($body, '<style');
844
+ if ($x === false)
845
+ return;
846
+
847
+ $x = strpos($body, '>', $x);
848
+ $y = strpos($body, '</style>');
849
+
850
+ header('Content-Type: text/css;charset=UTF-8');
851
+
852
+ echo substr($body, $x + 1, $y - $x - 1);
853
+
854
+ die();
855
+ break;
856
+
857
+ case 'emails-composer-css':
858
+ header('Cache: no-cache');
859
+ header('Content-Type: text/css');
860
+ echo $this->get_composer_css();
861
+ die();
862
+ break;
863
+
864
+ case 'emails-preview':
865
+ if (!Newsletter::instance()->is_allowed()) {
866
+ die('Not enough privileges');
867
+ }
868
+
869
+ if (!check_admin_referer('view')) {
870
+ die();
871
+ }
872
+
873
+ $theme_id = $_GET['id'];
874
+ $theme = $this->themes->get_theme($theme_id);
875
+
876
+ // Used by theme code
877
+ $theme_options = $this->themes->get_options($theme_id);
878
+
879
+ $theme_url = $theme['url'];
880
+
881
+ header('Content-Type: text/html;charset=UTF-8');
882
+
883
+ include $theme['dir'] . '/theme.php';
884
+
885
+ die();
886
+ break;
887
+
888
+ case 'emails-preview-text':
889
+ header('Content-Type: text/plain;charset=UTF-8');
890
+ if (!Newsletter::instance()->is_allowed()) {
891
+ die('Not enough privileges');
892
+ }
893
+
894
+ if (!check_admin_referer('view')) {
895
+ die();
896
+ }
897
+
898
+ // Used by theme code
899
+ $theme_options = $this->get_current_theme_options();
900
+
901
+ $file = include $theme['dir'] . '/theme-text.php';
902
+
903
+ if (is_file($file)) {
904
+ include $file;
905
+ }
906
+
907
+ die();
908
+ break;
909
+
910
+
911
+ case 'emails-create':
912
+ // Newsletter from themes are created on frontend context because sometime WP themes change the way the content,
913
+ // excerpt, thumbnail are extracted.
914
+ if (!Newsletter::instance()->is_allowed()) {
915
+ die('Not enough privileges');
916
+ }
917
+
918
+ require_once NEWSLETTER_INCLUDES_DIR . '/controls.php';
919
+ $controls = new NewsletterControls();
920
+
921
+ if (!$controls->is_action('create')) {
922
+ die('Wrong call');
923
+ }
924
+
925
+ $theme_id = $controls->data['id'];
926
+ $theme = $this->themes->get_theme($theme_id);
927
+
928
+ if (!$theme) {
929
+ die('invalid theme');
930
+ }
931
+
932
+ $this->themes->save_options($theme_id, $controls->data);
933
+
934
+ $email = array();
935
+ $email['status'] = 'new';
936
+ $email['subject'] = ''; //__('Here the email subject', 'newsletter');
937
+ $email['track'] = 1;
938
+ $email['send_on'] = time();
939
+ $email['editor'] = NewsletterEmails::EDITOR_TINYMCE;
940
+ $email['type'] = 'message';
941
+
942
+ $theme_options = $this->themes->get_options($theme_id);
943
+
944
+ $theme_url = $theme['url'];
945
+ $theme_subject = '';
946
+
947
+ ob_start();
948
+ include $theme['dir'] . '/theme.php';
949
+ $email['message'] = ob_get_clean();
950
+
951
+
952
+ if (!empty($theme_subject)) {
953
+ $email['subject'] = $theme_subject;
954
+ }
955
+
956
+ if (file_exists($theme['dir'] . '/theme-text.php')) {
957
+ ob_start();
958
+ include $theme['dir'] . '/theme-text.php';
959
+ $email['message_text'] = ob_get_clean();
960
+ } else {
961
+ $email['message_text'] = 'You need a modern email client to read this email. Read it online: {email_url}.';
962
+ }
963
+
964
+ $email = $this->save_email($email);
965
+
966
+ $edit_url = $this->get_editor_url($email->id, $email->editor);
967
+
968
+ header('Location: ' . $edit_url);
969
+
970
+ die();
971
+ break;
972
+ }
973
+ }
974
+
975
+ function admin_menu() {
976
+ $this->add_menu_page('index', 'Newsletters');
977
+ $this->add_admin_page('list', 'Email List');
978
+ $this->add_admin_page('new', 'Email New');
979
+ $this->add_admin_page('edit', 'Email Edit');
980
+ $this->add_admin_page('theme', 'Email Themes');
981
+ $this->add_admin_page('composer', 'The Composer');
982
+ $this->add_admin_page('editorhtml', 'HTML Editor');
983
+ $this->add_admin_page('editortinymce', 'TinyMCE Editor');
984
+ }
985
+
986
+ /**
987
+ * Builds a block data structure starting from the folder containing the block
988
+ * files.
989
+ *
990
+ * @param string $dir
991
+ * @return array | WP_Error
992
+ */
993
+ function build_block($dir) {
994
+ $dir = realpath($dir);
995
+ $dir = wp_normalize_path($dir);
996
+ $full_file = $dir . '/block.php';
997
+ if (!is_file($full_file)) {
998
+ return new WP_Error('1', 'Missing block.php file in ' . $dir);
999
+ }
1000
+
1001
+ $relative_dir = substr($dir, strlen(WP_CONTENT_DIR));
1002
+ $file = basename($dir);
1003
+
1004
+ $data = get_file_data($full_file, ['name' => 'Name', 'section' => 'Section', 'description' => 'Description', 'type' => 'Type']);
1005
+ $defaults = ['section' => 'content', 'name' => ucfirst($file), 'descritpion' => '', 'icon' => plugins_url('newsletter') . '/admin/images/block-icon.png'];
1006
+ $data = array_merge($defaults, $data);
1007
+
1008
+ if (is_file($dir . '/icon.png')) {
1009
+ $data['icon'] = content_url($relative_dir . '/icon.png');
1010
+ }
1011
+
1012
+ $data['id'] = sanitize_key($file);
1013
+
1014
+ // Absolute path of the block files
1015
+ $data['dir'] = $dir;
1016
+ $data['url'] = content_url($relative_dir);
1017
+
1018
+ return $data;
1019
+ }
1020
+
1021
+ /**
1022
+ *
1023
+ * @param type $dir
1024
+ * @return type
1025
+ */
1026
+ function scan_blocks_dir($dir) {
1027
+ $dir = realpath($dir);
1028
+ if (!$dir) {
1029
+ return [];
1030
+ }
1031
+ $dir = wp_normalize_path($dir);
1032
+
1033
+ $list = [];
1034
+ $handle = opendir($dir);
1035
+ while ($file = readdir($handle)) {
1036
+
1037
+ $data = $this->build_block($dir . '/' . $file);
1038
+
1039
+ if (is_wp_error($data)) {
1040
+ $this->logger->error($data);
1041
+ continue;
1042
+ }
1043
+ $list[$data['id']] = $data;
1044
+ }
1045
+ closedir($handle);
1046
+ return $list;
1047
+ }
1048
+
1049
+ /**
1050
+ * Array of arrays with every registered block and legacy block converted to the new
1051
+ * format.
1052
+ *
1053
+ * @return array
1054
+ */
1055
+ function get_blocks() {
1056
+
1057
+ if (!is_null($this->blocks)) {
1058
+ return $this->blocks;
1059
+ }
1060
+
1061
+ $this->blocks = $this->scan_blocks_dir(__DIR__ . '/blocks');
1062
+
1063
+ $extended = $this->scan_blocks_dir(WP_CONTENT_DIR . '/extensions/newsletter/blocks');
1064
+
1065
+ $this->blocks = array_merge($extended, $this->blocks);
1066
+
1067
+ $dirs = apply_filters('newsletter_blocks_dir', array());
1068
+
1069
+ //$this->logger->debug('Block dirs:');
1070
+ //$this->logger->debug($dirs);
1071
+
1072
+ foreach ($dirs as $dir) {
1073
+ $list = $this->scan_blocks_dir($dir);
1074
+ $this->blocks = array_merge($list, $this->blocks);
1075
+ }
1076
+
1077
+ do_action('newsletter_register_blocks');
1078
+
1079
+ foreach (TNP_Composer::$block_dirs as $dir) {
1080
+ $block = $this->build_block($dir);
1081
+ if (is_wp_error($block)) {
1082
+ $this->logger->error($block);
1083
+ continue;
1084
+ }
1085
+ if (!isset($this->blocks[$block['id']])) {
1086
+ $this->blocks[$block['id']] = $block;
1087
+ } else {
1088
+ $this->logger->error('The block "' . $block['id'] . '" has already been registered');
1089
+ }
1090
+ }
1091
+
1092
+ $this->blocks = array_reverse($this->blocks);
1093
+ return $this->blocks;
1094
+ }
1095
+
1096
+ /**
1097
+ * Return a single block (associative array) checking for legacy ID as well.
1098
+ *
1099
+ * @param string $id
1100
+ * @return array
1101
+ */
1102
+ function get_block($id) {
1103
+ switch ($id) {
1104
+ case 'content-03-text.block':
1105
+ $id = 'text';
1106
+ break;
1107
+ case 'footer-03-social.block':
1108
+ $id = 'social';
1109
+ break;
1110
+ case 'footer-02-canspam.block':
1111
+ $id = 'canspam';
1112
+ break;
1113
+ case 'content-05-image.block':
1114
+ $id = 'image';
1115
+ break;
1116
+ case 'header-01-header.block':
1117
+ $id = 'header';
1118
+ break;
1119
+ case 'footer-01-footer.block':
1120
+ $id = 'footer';
1121
+ break;
1122
+ case 'content-02-heading.block':
1123
+ $id = 'heading';
1124
+ break;
1125
+ case 'content-07-twocols.block':
1126
+ case 'content-06-posts.block':
1127
+ $id = 'posts';
1128
+ break;
1129
+ case 'content-04-cta.block':
1130
+ $id = 'cta';
1131
+ break;
1132
+ case 'content-01-hero.block':
1133
+ $id = 'hero';
1134
+ break;
1135
+ // case 'content-02-heading.block': $id = '/plugins/newsletter/emails/blocks/heading';
1136
+ // break;
1137
+ }
1138
+
1139
+ // Conversion for old full path ID
1140
+ $id = sanitize_key(basename($id));
1141
+
1142
+ // TODO: Correct id for compatibility
1143
+ $blocks = $this->get_blocks();
1144
+ if (!isset($blocks[$id])) {
1145
+ return null;
1146
+ }
1147
+ return $blocks[$id];
1148
+ }
1149
+
1150
+ function scan_presets_dir($dir = null) {
1151
+
1152
+ if (is_null($dir)) {
1153
+ $dir = __DIR__ . '/presets';
1154
+ }
1155
+
1156
+ if (!is_dir($dir)) {
1157
+ return array();
1158
+ }
1159
+
1160
+ $handle = opendir($dir);
1161
+ $list = array();
1162
+ $relative_dir = substr($dir, strlen(WP_CONTENT_DIR));
1163
+ while ($file = readdir($handle)) {
1164
+
1165
+ if ($file == '.' || $file == '..')
1166
+ continue;
1167
+
1168
+ // The block unique key, we should find out how to build it, maybe an hash of the (relative) dir?
1169
+ $preset_id = sanitize_key($file);
1170
+
1171
+ $full_file = $dir . '/' . $file . '/preset.json';
1172
+
1173
+ if (!is_file($full_file)) {
1174
+ continue;
1175
+ }
1176
+
1177
+ $icon = content_url($relative_dir . '/' . $file . '/icon.png');
1178
+
1179
+ $list[$preset_id] = $icon;
1180
+ }
1181
+ closedir($handle);
1182
+ return $list;
1183
+ }
1184
+
1185
+ function get_preset_from_file($id, $dir = null) {
1186
+
1187
+ if (is_null($dir)) {
1188
+ $dir = __DIR__ . '/presets';
1189
+ }
1190
+
1191
+ $id = $this->sanitize_file_name($id);
1192
+
1193
+ if (!is_dir($dir . '/' . $id) || !in_array($id, self::$PRESETS_LIST)) {
1194
+ return array();
1195
+ }
1196
+
1197
+ $json_content = file_get_contents("$dir/$id/preset.json");
1198
+ $json_content = str_replace("{placeholder_base_url}", plugins_url('newsletter') . '/emails/presets', $json_content);
1199
+ $json = json_decode($json_content);
1200
+ $json->icon = NEWSLETTER_URL . "/emails/presets/$id/icon.png?ver=2";
1201
+
1202
+ return $json;
1203
+ }
1204
+
1205
+ function get_composer_css() {
1206
+ $css = file_get_contents(__DIR__ . '/tnp-composer/css/newsletter.css');
1207
+ $css .= "\n\n";
1208
+ $css .= file_get_contents(__DIR__ . '/tnp-composer/css/backend.css');
1209
+ $blocks = $this->get_blocks();
1210
+ foreach ($blocks as $block) {
1211
+ if (!file_exists($block['dir'] . '/style.css')) {
1212
+ continue;
1213
+ }
1214
+ $css .= "\n\n";
1215
+ $css .= "/* " . $block['name'] . " */\n";
1216
+ $css .= file_get_contents($block['dir'] . '/style.css');
1217
+ }
1218
+ return $css;
1219
+ }
1220
+
1221
+ /**
1222
+ * Send an email to the test subscribers.
1223
+ *
1224
+ * @param TNP_Email $email Could be any object with the TNP_Email attributes
1225
+ * @param NewsletterControls $controls
1226
+ */
1227
+ function send_test_email($email, $controls) {
1228
+ if (!$email) {
1229
+ $controls->errors = __('Newsletter should be saved before send a test', 'newsletter');
1230
+ return;
1231
+ }
1232
+
1233
+ $original_subject = $email->subject;
1234
+ $this->set_test_subject_to($email);
1235
+
1236
+ $users = NewsletterUsers::instance()->get_test_users();
1237
+ if (count($users) == 0) {
1238
+ $controls->errors = '' . __('There are no test subscribers to send to', 'newsletter') .
1239
+ '. <a href="https://www.thenewsletterplugin.com/plugins/newsletter/subscribers-module#test" target="_blank"><strong>' .
1240
+ __('Read more', 'newsletter') . '</strong></a>.';
1241
+ } else {
1242
+ $r = Newsletter::instance()->send($email, $users, true);
1243
+ $emails = array();
1244
+ foreach ($users as $user) {
1245
+ $emails[] = '<a href="admin.php?page=newsletter_users_edit&id=' . $user->id . '" target="_blank">' . $user->email . '</a>';
1246
+ }
1247
+ if (is_wp_error($r)) {
1248
+ $controls->errors = 'Something went wrong. Check the error logs on status page.<br>';
1249
+ $controls->errors .= __('Test subscribers:', 'newsletter');
1250
+ $controls->errors .= ' ' . implode(', ', $emails);
1251
+ $controls->errors .= '<br>';
1252
+ $controls->errors .= '<strong>' . esc_html($r->get_error_message()) . '</strong><br>';
1253
+ $controls->errors .= '<a href="https://www.thenewsletterplugin.com/documentation/email-sending-issues" target="_blank"><strong>' . __('Read more about delivery issues', 'newsletter') . '</strong></a>.';
1254
+ } else {
1255
+ $controls->messages = __('Test subscribers:', 'newsletter');
1256
+
1257
+ $controls->messages .= ' ' . implode(', ', $emails);
1258
+ $controls->messages .= '.<br>';
1259
+ $controls->messages .= '<a href="https://www.thenewsletterplugin.com/documentation/subscribers#test" target="_blank"><strong>' .
1260
+ __('Read more about test subscribers', 'newsletter') . '</strong></a>.<br>';
1261
+ $controls->messages .= '<a href="https://www.thenewsletterplugin.com/documentation/email-sending-issues" target="_blank"><strong>' . __('Read more about delivery issues', 'newsletter') . '</strong></a>.';
1262
+ }
1263
+ }
1264
+ $email->subject = $original_subject;
1265
+ }
1266
+
1267
+ /**
1268
+ * Send an email to the test subscribers.
1269
+ *
1270
+ * @param TNP_Email $email Could be any object with the TNP_Email attributes
1271
+ * @param string $email_address
1272
+ *
1273
+ * @throws Exception
1274
+ */
1275
+ function send_test_newsletter_to_email_address( $email, $email_address ) {
1276
+
1277
+ if ( ! $email ) {
1278
+ throw new Exception( __( 'Newsletter should be saved before send a test', 'newsletter' ) );
1279
+ }
1280
+
1281
+ $this->set_test_subject_to( $email );
1282
+
1283
+ $dummy_subscriber = $this->make_dummy_subscriber();
1284
+ $dummy_subscriber->email = $email_address;
1285
+
1286
+ $result = Newsletter::instance()->send( $email, [ $dummy_subscriber ], true );
1287
+
1288
+ $email = '<a href="admin.php?page=newsletter_users_edit&id=' . $dummy_subscriber->id . '" target="_blank">' . $dummy_subscriber->email . '</a>';
1289
+
1290
+ if ( is_wp_error( $result ) ) {
1291
+ $error_message = 'Something went wrong. Check the error logs on status page.<br>';
1292
+ $error_message .= __( 'Test subscribers:', 'newsletter' );
1293
+ $error_message .= ' ' . $email;
1294
+ $error_message .= '<br>';
1295
+ $error_message .= '<strong>' . esc_html( $result->get_error_message() ) . '</strong><br>';
1296
+ $error_message .= '<a href="https://www.thenewsletterplugin.com/documentation/email-sending-issues" target="_blank"><strong>' . __( 'Read more about delivery issues', 'newsletter' ) . '</strong></a>.';
1297
+ throw new Exception( $error_message );
1298
+ }
1299
+
1300
+ $messages = __( 'Test subscribers:', 'newsletter' );
1301
+
1302
+ $messages .= ' ' . $email;
1303
+ $messages .= '.<br>';
1304
+ $messages .= '<a href="https://www.thenewsletterplugin.com/documentation/subscribers#test" target="_blank"><strong>' .
1305
+ __( 'Read more about test subscribers', 'newsletter' ) . '</strong></a>.<br>';
1306
+ $messages .= '<a href="https://www.thenewsletterplugin.com/documentation/email-sending-issues" target="_blank"><strong>' . __( 'Read more about delivery issues', 'newsletter' ) . '</strong></a>.';
1307
+
1308
+ return $messages;
1309
+ }
1310
+
1311
+ private function set_test_subject_to($email) {
1312
+ if ( $email->subject == '' ) {
1313
+ $email->subject = '[TEST] Dummy subject, it was empty (remember to set it)';
1314
+ } else {
1315
+ $email->subject = $email->subject . ' (TEST)';
1316
+ }
1317
+ }
1318
+
1319
+ private function make_dummy_subscriber() {
1320
+ $dummy_user = new TNP_User();
1321
+ $dummy_user->id = 0;
1322
+ $dummy_user->email = 'john.doe@example.org';
1323
+ $dummy_user->name = 'John';
1324
+ $dummy_user->surname = 'Doe';
1325
+ $dummy_user->sex = 'n';
1326
+ $dummy_user->language = '';
1327
+ $dummy_user->ip = '';
1328
+
1329
+ for ( $i = 1; $i <= NEWSLETTER_PROFILE_MAX; $i ++ ) {
1330
+ $profile_key = "profile_$i";
1331
+ $dummy_user->$profile_key = '';
1332
+ }
1333
+
1334
+ return $dummy_user;
1335
+ }
1336
+
1337
+ function restore_options_from_request() {
1338
+
1339
+ require_once NEWSLETTER_INCLUDES_DIR . '/controls.php';
1340
+ $controls = new NewsletterControls();
1341
+ $options = $controls->data;
1342
+
1343
+ if (isset($_POST['options']) && is_array($_POST['options'])) {
1344
+ // Get all block options
1345
+ //$options = stripslashes_deep($_POST['options']);
1346
+
1347
+ // Deserialize inline edits when
1348
+ // render is preformed on saving block options
1349
+ if (isset($options['inline_edits']) && !is_array($options['inline_edits'])) {
1350
+ $options['inline_edits'] = $this->options_decode($options['inline_edits']);
1351
+ }
1352
+
1353
+ // Restore inline edits from data-json
1354
+ // coming from inline editing
1355
+ // and merge with current inline edit
1356
+ if (isset($_POST['encoded_options'])) {
1357
+ $decoded_options = $this->options_decode($_POST['encoded_options']);
1358
+
1359
+ $to_merge_inline_edits = [];
1360
+
1361
+ if (isset($decoded_options['inline_edits'])) {
1362
+ foreach ($decoded_options['inline_edits'] as $decoded_inline_edit) {
1363
+ $to_merge_inline_edits[$decoded_inline_edit['post_id'] . $decoded_inline_edit['type']] = $decoded_inline_edit;
1364
+ }
1365
+ }
1366
+
1367
+ //Overwrite with new edited content
1368
+ if (isset($options['inline_edits'])) {
1369
+ foreach ($options['inline_edits'] as $inline_edit) {
1370
+ $to_merge_inline_edits[$inline_edit['post_id'] . $inline_edit['type']] = $inline_edit;
1371
+ }
1372
+ }
1373
+
1374
+ $options['inline_edits'] = array_values($to_merge_inline_edits);
1375
+ $options = array_merge($decoded_options, $options);
1376
+ }
1377
+
1378
+ return $options;
1379
+ }
1380
+
1381
+ return array();
1382
+ }
1383
+
1384
+ public function hook_wp_ajax_tnpc_delete_preset() {
1385
+
1386
+ if (!wp_verify_nonce($_POST['_wpnonce'], 'preset')) {
1387
+ wp_send_json_error('Expired request');
1388
+ }
1389
+
1390
+ $preset_id = (int) $_REQUEST['presetId'];
1391
+
1392
+ $newsletter = Newsletter::instance();
1393
+
1394
+ if ($preset_id > 0) {
1395
+ $preset = $newsletter->get_email($preset_id);
1396
+
1397
+ if ($preset && $preset->type === self::PRESET_EMAIL_TYPE) {
1398
+ Newsletter::instance()->delete_email($preset_id);
1399
+ wp_send_json_success();
1400
+ } else {
1401
+ wp_send_json_error(__('Is not a preset!', 'newsletter'));
1402
+ }
1403
+ } else {
1404
+ wp_send_json_error();
1405
+ }
1406
+ }
1407
+
1408
+ }
1409
+
1410
+ NewsletterEmails::instance();
{images → emails/images}/arrow.png RENAMED
File without changes
{images → emails/images}/theme-screenshot.png RENAMED
File without changes
emails/new.php CHANGED
@@ -1,175 +1,175 @@
1
- <?php
2
- /* @var $this NewsletterEmails */
3
- require_once NEWSLETTER_INCLUDES_DIR . '/controls.php';
4
-
5
- $controls = new NewsletterControls();
6
-
7
- $theme_id = $_GET['id'];
8
-
9
- if ($theme_id === 'rawhtml' && check_admin_referer('newsletter-new')) {
10
- $email = array();
11
- $email['status'] = 'new';
12
- $email['subject'] = __('Here the email subject', 'newsletter');
13
- $email['track'] = Newsletter::instance()->options['track'];
14
- $email['token'] = $this->get_token();
15
- $email['type'] = 'message';
16
- $email['send_on'] = time();
17
- $email['editor'] = NewsletterEmails::EDITOR_HTML;
18
- $email['message'] = "<!DOCTYPE html>\n<html>\n<head>\n<title>Your email title</title>\n</head>\n<body>\n</body>\n</html>";
19
- $email = Newsletter::instance()->save_email($email);
20
-
21
- $controls->js_redirect($this->get_editor_url($email->id, $email->editor));
22
- return;
23
- }
24
-
25
-
26
- $theme = $this->themes->get_theme($theme_id);
27
-
28
- // Should never happen
29
- if (!$theme) {
30
- die('Invalid theme');
31
- }
32
-
33
- if (!file_exists($theme['dir'] . '/theme-options.php') && check_admin_referer('newsletter-new')) {
34
- $email = array();
35
- $email['status'] = 'new';
36
- $email['subject'] = __('Here the email subject', 'newsletter');
37
- $email['track'] = Newsletter::instance()->options['track'];
38
- $email['token'] = $this->get_token();
39
- $email['type'] = 'message';
40
- $email['send_on'] = time();
41
- $email['editor'] = NewsletterEmails::EDITOR_TINYMCE;
42
-
43
- $theme_options = $this->themes->get_options($controls->data['theme']);
44
- $theme_url = $theme['url'];
45
- $theme_subject = '';
46
-
47
- ob_start();
48
- include $theme['dir'] . '/theme.php';
49
- $email['message'] = ob_get_clean();
50
-
51
- if (!empty($theme_subject)) {
52
- $email['subject'] = $theme_subject;
53
- }
54
-
55
- if (file_exists($theme['dir'] . '/theme-text.php')) {
56
- ob_start();
57
- include $theme['dir'] . '/theme-text.php';
58
- $email['message_text'] = ob_get_clean();
59
- } else {
60
- $email['message_text'] = 'You need a modern email client to read this email. Read it online: {email_url}.';
61
- }
62
- $email = Newsletter::instance()->save_email($email);
63
-
64
- $controls->js_redirect($this->get_editor_url($email->id, $email->editor));
65
- return;
66
- }
67
-
68
- if ($controls->is_action('refresh')) {
69
- $this->themes->save_options($theme_id, $controls->data);
70
- }
71
-
72
- if ($controls->is_action('create')) {
73
-
74
- $this->themes->save_options($theme_id, $controls->data);
75
-
76
- $email = array();
77
- $email['status'] = 'new';
78
- $email['subject'] = __('Here the email subject', 'newsletter');
79
- $email['track'] = Newsletter::instance()->options['track'];
80
- $email['message_text'] = '';
81
- $email['type'] = 'message';
82
- $email['send_on'] = time();
83
- $email['editor'] = NewsletterEmails::EDITOR_TINYMCE;
84
-
85
- $theme_options = $this->themes->get_options($theme_id);
86
-
87
- $theme_url = $theme['url'];
88
- $theme_subject = '';
89
-
90
- ob_start();
91
- include $theme['dir'] . '/theme.php';
92
- $email['message'] = ob_get_clean();
93
-
94
- if (!empty($theme_subject)) {
95
- $email['subject'] = $theme_subject;
96
- }
97
-
98
- if (is_file($theme['dir'] . '/theme-text.php')) {
99
- ob_start();
100
- include $theme['dir'] . '/theme-text.php';
101
- $email['message_text'] = ob_get_clean();
102
- }
103
-
104
- $email = $this->save_email($email);
105
- $controls->js_redirect($this->get_editor_url($email->id, $email->editor));
106
- return;
107
- } else {
108
- $controls->data = $this->themes->get_options($theme_id);
109
- $controls->data['id'] = $theme_id;
110
- }
111
- ?>
112
- <style>
113
- #tnp-body .tnp-emails-theme-options {
114
- background-color: #fff;
115
- padding: 10px;
116
- margin-top: 14px;
117
- }
118
-
119
- #tnp-body .tnp-emails-theme-options table.form-table {
120
- margin: 0;
121
- }
122
-
123
- #tnp-body .tnp-emails-theme-options h3 {
124
- color: #000;
125
- }
126
- </style>
127
-
128
- <div class="wrap tnp-emails tnp-emails-new" id="tnp-wrap">
129
-
130
- <?php include NEWSLETTER_DIR . '/tnp-header.php'; ?>
131
-
132
- <div id="tnp-heading">
133
-
134
- <h2><?php _e('Create a newsletter', 'newsletter') ?>
135
- <a class="tnp-btn-h1" href="<?php echo NewsletterEmails::instance()->get_admin_page_url('theme'); ?>"><?php _e('Back to newsletter themes', 'newsletter') ?></a>
136
- </h2>
137
- <br>
138
- <p>Theme options are saved for next time you'll use this theme.</p>
139
-
140
- </div>
141
-
142
- <div id="tnp-body" class="tnp-body-lite">
143
-
144
- <form method="post" action="">
145
- <?php $controls->init(); ?>
146
- <?php $controls->hidden('id'); ?>
147
- <table style="width: 100%; border-collapse: collapse">
148
- <tr>
149
- <td style="text-align: left; vertical-align: top; border-bottom: 1px solid #ddd; padding-bottom: 10px">
150
- <div style="float: right; margin-left: 15px;"><?php $controls->button_primary('refresh', __('Refresh the preview', 'newsletter')); ?></div>
151
-
152
- </td>
153
- <td style="text-align: left; vertical-align: top; border-bottom: 1px solid #ddd; padding-bottom: 10px">
154
- <div style="float: right"><?php $controls->button_primary('create', 'Proceed to edit &raquo;', 'this.form.action=\'' . home_url('/', is_ssl() ? 'https' : 'http') . '?na=emails-create\';this.form.submit()'); ?></div>
155
- <img style="position: relative; left: 5px; top: 10px;"src="<?php echo plugins_url('newsletter') ?>/images/arrow.png" height="35">
156
- </td>
157
- </tr>
158
- <tr>
159
- <td style="width: 500px; vertical-align: top;">
160
- <div class="tnp-emails-theme-options">
161
- <?php @include $theme['dir'] . '/theme-options.php'; ?>
162
- </div>
163
- </td>
164
- <td style="vertical-align: top; padding-top: 15px; padding-left: 15px">
165
- <iframe src="<?php echo wp_nonce_url(home_url('/', is_ssl() ? 'https' : 'http') . '?na=emails-preview&id=' . urlencode($theme_id) . '&ts=' . time(), 'view'); ?>" height="700" style="width: 100%; border: 1px solid #ccc"></iframe>
166
- </td>
167
- </tr>
168
- </table>
169
-
170
- </form>
171
- </div>
172
-
173
- <?php include NEWSLETTER_DIR . '/tnp-footer.php'; ?>
174
-
175
  </div>
1
+ <?php
2
+ /* @var $this NewsletterEmails */
3
+ require_once NEWSLETTER_INCLUDES_DIR . '/controls.php';
4
+
5
+ $controls = new NewsletterControls();
6
+
7
+ $theme_id = $_GET['id'];
8
+
9
+ if ($theme_id === 'rawhtml' && check_admin_referer('newsletter-new')) {
10
+ $email = array();
11
+ $email['status'] = 'new';
12
+ $email['subject'] = __('Here the email subject', 'newsletter');
13
+ $email['track'] = Newsletter::instance()->options['track'];
14
+ $email['token'] = $this->get_token();
15
+ $email['type'] = 'message';
16
+ $email['send_on'] = time();
17
+ $email['editor'] = NewsletterEmails::EDITOR_HTML;
18
+ $email['message'] = "<!DOCTYPE html>\n<html>\n<head>\n<title>Your email title</title>\n</head>\n<body>\n</body>\n</html>";
19
+ $email = Newsletter::instance()->save_email($email);
20
+
21
+ $controls->js_redirect($this->get_editor_url($email->id, $email->editor));
22
+ return;
23
+ }
24
+
25
+
26
+ $theme = $this->themes->get_theme($theme_id);
27
+
28
+ // Should never happen
29
+ if (!$theme) {
30
+ die('Invalid theme');
31
+ }
32
+
33
+ if (!file_exists($theme['dir'] . '/theme-options.php') && check_admin_referer('newsletter-new')) {
34
+ $email = array();
35
+ $email['status'] = 'new';
36
+ $email['subject'] = __('Here the email subject', 'newsletter');
37
+ $email['track'] = Newsletter::instance()->options['track'];
38
+ $email['token'] = $this->get_token();
39
+ $email['type'] = 'message';
40
+ $email['send_on'] = time();
41
+ $email['editor'] = NewsletterEmails::EDITOR_TINYMCE;
42
+
43
+ $theme_options = $this->themes->get_options($controls->data['theme']);
44
+ $theme_url = $theme['url'];
45
+ $theme_subject = '';
46
+
47
+ ob_start();
48
+ include $theme['dir'] . '/theme.php';
49
+ $email['message'] = ob_get_clean();
50
+
51
+ if (!empty($theme_subject)) {
52
+ $email['subject'] = $theme_subject;
53
+ }
54
+
55
+ if (file_exists($theme['dir'] . '/theme-text.php')) {
56
+ ob_start();
57
+ include $theme['dir'] . '/theme-text.php';
58
+ $email['message_text'] = ob_get_clean();
59
+ } else {
60
+ $email['message_text'] = 'You need a modern email client to read this email. Read it online: {email_url}.';
61
+ }
62
+ $email = Newsletter::instance()->save_email($email);
63
+
64
+ $controls->js_redirect($this->get_editor_url($email->id, $email->editor));
65
+ return;
66
+ }
67
+
68
+ if ($controls->is_action('refresh')) {
69
+ $this->themes->save_options($theme_id, $controls->data);
70
+ }
71
+
72
+ if ($controls->is_action('create')) {
73
+
74
+ $this->themes->save_options($theme_id, $controls->data);
75
+
76
+ $email = array();
77
+ $email['status'] = 'new';
78
+ $email['subject'] = __('Here the email subject', 'newsletter');
79
+ $email['track'] = Newsletter::instance()->options['track'];
80
+ $email['message_text'] = '';
81
+ $email['type'] = 'message';
82
+ $email['send_on'] = time();
83
+ $email['editor'] = NewsletterEmails::EDITOR_TINYMCE;
84
+
85
+ $theme_options = $this->themes->get_options($theme_id);
86
+
87
+ $theme_url = $theme['url'];
88
+ $theme_subject = '';
89
+
90
+ ob_start();
91
+ include $theme['dir'] . '/theme.php';
92
+ $email['message'] = ob_get_clean();
93
+
94
+ if (!empty($theme_subject)) {
95
+ $email['subject'] = $theme_subject;
96
+ }
97
+
98
+ if (is_file($theme['dir'] . '/theme-text.php')) {
99
+ ob_start();
100
+ include $theme['dir'] . '/theme-text.php';
101
+ $email['message_text'] = ob_get_clean();
102
+ }
103
+
104
+ $email = $this->save_email($email);
105
+ $controls->js_redirect($this->get_editor_url($email->id, $email->editor));
106
+ return;
107
+ } else {
108
+ $controls->data = $this->themes->get_options($theme_id);
109
+ $controls->data['id'] = $theme_id;
110
+ }
111
+ ?>
112
+ <style>
113
+ #tnp-body .tnp-emails-theme-options {
114
+ background-color: #fff;
115
+ padding: 10px;
116
+ margin-top: 14px;
117
+ }
118
+
119
+ #tnp-body .tnp-emails-theme-options table.form-table {
120
+ margin: 0;
121
+ }
122
+
123
+ #tnp-body .tnp-emails-theme-options h3 {
124
+ color: #000;
125
+ }
126
+ </style>
127
+
128
+ <div class="wrap tnp-emails tnp-emails-new" id="tnp-wrap">
129
+
130
+ <?php include NEWSLETTER_DIR . '/tnp-header.php'; ?>
131
+
132
+ <div id="tnp-heading">
133
+
134
+ <h2><?php _e('Create a newsletter', 'newsletter') ?>
135
+ <a class="tnp-btn-h1" href="<?php echo NewsletterEmails::instance()->get_admin_page_url('theme'); ?>"><?php _e('Back to newsletter themes', 'newsletter') ?></a>
136
+ </h2>
137
+ <br>
138
+ <p>Theme options are saved for next time you'll use this theme.</p>
139
+
140
+ </div>
141
+
142
+ <div id="tnp-body" class="tnp-body-lite">
143
+
144
+ <form method="post" action="">
145
+ <?php $controls->init(); ?>
146
+ <?php $controls->hidden('id'); ?>
147
+ <table style="width: 100%; border-collapse: collapse">
148
+ <tr>
149
+ <td style="text-align: left; vertical-align: top; border-bottom: 1px solid #ddd; padding-bottom: 10px">
150
+ <div style="float: right; margin-left: 15px;"><?php $controls->button_primary('refresh', __('Refresh the preview', 'newsletter')); ?></div>
151
+
152
+ </td>
153
+ <td style="text-align: left; vertical-align: top; border-bottom: 1px solid #ddd; padding-bottom: 10px">
154
+ <div style="float: right"><?php $controls->button_primary('create', 'Proceed to edit &raquo;', 'this.form.action=\'' . home_url('/', is_ssl() ? 'https' : 'http') . '?na=emails-create\';this.form.submit()'); ?></div>
155
+ <img style="position: relative; left: 5px; top: 10px;"src="<?php echo plugins_url('newsletter') ?>/emails/images/arrow.png" height="35">
156
+ </td>
157
+ </tr>
158
+ <tr>
159
+ <td style="width: 500px; vertical-align: top;">
160
+ <div class="tnp-emails-theme-options">
161
+ <?php @include $theme['dir'] . '/theme-options.php'; ?>
162
+ </div>
163
+ </td>
164
+ <td style="vertical-align: top; padding-top: 15px; padding-left: 15px">
165
+ <iframe src="<?php echo wp_nonce_url(home_url('/', is_ssl() ? 'https' : 'http') . '?na=emails-preview&id=' . urlencode($theme_id) . '&ts=' . time(), 'view'); ?>" height="700" style="width: 100%; border: 1px solid #ccc"></iframe>
166
+ </td>
167
+ </tr>
168
+ </table>
169
+
170
+ </form>
171
+ </div>
172
+
173
+ <?php include NEWSLETTER_DIR . '/tnp-footer.php'; ?>
174
+
175
  </div>
emails/tnp-composer/_css/newsletter-builder-v2.css CHANGED
@@ -1,1173 +1,1173 @@
1
- #wpbody-content {
2
- padding-bottom: 15px;
3
- }
4
-
5
- .tnp-emails-composer {
6
- margin: 15px 15px 0 0;
7
- }
8
-
9
-
10
- /* Contains the newsletter editing area and the tools */
11
- #newsletter-builder {
12
- height: calc(100vh - 120px);
13
- display: flex;
14
- flex-flow: row;
15
- position: relative;
16
- overflow: hidden;
17
- box-sizing: border-box;
18
- max-width: 1380px;
19
- margin: 0 auto;
20
- }
21
-
22
- /* Contains the newsletter editing area */
23
- #newsletter-builder-area {
24
- display: flex;
25
- flex-flow: column;
26
- flex-shrink: 0;
27
- background-color: #fff;
28
- width: 800px;
29
- box-sizing: border-box;
30
- border-radius: 3px;
31
- overflow: hidden;
32
- float: left;
33
-
34
- position: relative;
35
- }
36
-
37
- /* Contains the tools */
38
- #newsletter-builder-sidebar {
39
- display: flex;
40
- flex-flow: column;
41
- /* flex-grow: 1;*/
42
- max-width: 510px;
43
- min-width: 410px;
44
- background-color: #ECF0F1;
45
- margin-left: 20px;
46
- overflow-y: scroll;
47
- /* Needed for block options form */
48
- position: relative;
49
- border-radius: 3px;
50
- overflow: hidden;
51
- }
52
-
53
- #tnpc-subject-wrap {
54
- border-bottom: 1px solid #ccc;
55
- padding-bottom: 20px;
56
- padding-top: 20px;
57
- padding-left: 20px;
58
- position:relative;
59
- }
60
-
61
- #tnpc-subject-wrap th {
62
- color: #999;
63
- font-size: 14px;
64
- font-weight: normal!important;
65
- text-align: right;
66
- padding-right: 10px;
67
- padding-bottom: 10px;
68
- vertical-align: middle;
69
- }
70
-
71
- #tnpc-subject-wrap td {
72
- color: #000;
73
- font-size: 14px;
74
- font-weight: normal!important;
75
- text-align: left;
76
- padding-right: 10px;
77
- padding-bottom: 10px;
78
- vertical-align: middle;
79
- }
80
-
81
- #tnpc-subject-wrap th[dir=rtl] {
82
- text-align: left;
83
- }
84
- #tnpc-subject-wrap td[dir=rtl] {
85
- text-align: right;
86
- }
87
- #tnpc-subject-wrap td[dir=rtl] #options-title {
88
- margin-right: 0;
89
- }
90
-
91
- #tnpc-subject-wrap .composer-actions {
92
- position: absolute;
93
- right: 10px;
94
- bottom: 5px;
95
- display: flex;
96
- align-items: center;
97
- }
98
-
99
- #tnpc-subject-wrap .composer-view-mode {
100
- display: flex;
101
- }
102
-
103
- #tnpc-subject-wrap .composer-view-mode .composer-view-mode__item {
104
- display: block;
105
- width: 30px;
106
- height: 30px;
107
- line-height: 30px;
108
- box-sizing: content-box;
109
- text-align: center;
110
- margin-left: 3px;
111
- border-radius: 3px;
112
- border: solid 1px #3c434a;
113
- background-color: #FFF;
114
- color: #3c434a;
115
- font-size: 12px;
116
- cursor: pointer;
117
- }
118
-
119
- #tnpc-subject-wrap .composer-view-mode .composer-view-mode__item--active {
120
- background-color: #2b2f3a;
121
- border-color: #2b2f3a;
122
- color: #FFF;
123
- }
124
-
125
- #tnpc-subject-wrap .composer-view-mode .composer-view-mode__item:hover{
126
- background-color: #3c434a;
127
- border-color: #3c434a;
128
- color: #FFF;
129
- }
130
-
131
- .composer-actions .button-primary {
132
- background-color: #2b2f3a;
133
- }
134
-
135
- .composer-actions .button-primary:hover {
136
- background-color: #3c434a;
137
- }
138
-
139
- #test-newsletter-button {
140
- background-color: #2b2f3a;
141
- }
142
-
143
- #test-newsletter-button:hover {
144
- background-color: #3c434a;
145
- }
146
-
147
- #tnpc-subject #options-subject,
148
- #options-preheader
149
- {
150
- width: 550px;
151
- margin-right: 15px;
152
- border: 1px solid #ddd;
153
- font-size: 14px;
154
- font-family: monospace !important;
155
- }
156
-
157
- #options-preheader + .tnp-description
158
- {
159
- margin-top: 0;
160
- }
161
-
162
- #tnpc-subject i {
163
- font-size: 18px;
164
- }
165
-
166
- .tnp-composer-heading {
167
- background-color: #0073aa;
168
- border-radius: 3px !important;
169
- position: fixed;
170
- bottom: 0;
171
- right: 0;
172
- left: 56px;
173
- z-index: 1000;
174
- margin: 0 15px 10px 0;
175
- }
176
-
177
- .tnp-composer-heading h2 {
178
- display: inline-block;
179
- margin-left: 30px !important;
180
- text-transform: none !important;
181
- font-weight: 400 !important;
182
- }
183
-
184
- .tnp-composer-heading a {
185
- display: inline-block;
186
- margin-left: 30px;
187
- }
188
-
189
- .tnp-composer-heading form {
190
- display: inline-block;
191
- margin-left: 30px;
192
- }
193
-
194
- .tnp-composer-heading img {
195
- width: 50px;
196
- vertical-align: middle;
197
- }
198
-
199
- #newsletter-builder-sidebar h4 {
200
- /* margin: 5px; */
201
- /* text-align: center; */
202
- color: #868686;
203
- /* margin-bottom: 20px; */
204
- font-family: Montserrat, sans-serif;
205
- font-weight: 300;
206
- border-bottom: 1px solid #fff;
207
- padding-bottom: 10px;
208
- }
209
-
210
- /*#newsletter-builder-sidebar h4 span {
211
- background-color: #27AE60;
212
- padding: 2px 5px;
213
- border-radius: 3px;
214
- }*/
215
-
216
-
217
- #newsletter-builder-sidebar .newsletter-sidebar-add-buttons img {
218
- width: 150px;
219
- height: auto;
220
- }
221
-
222
- .newsletter-sidebar-add-buttons {
223
- border-radius: 4px;
224
- padding: 5px;
225
- margin: 5px;
226
- }
227
-
228
- .tnp-body-lite {
229
- background-color: #FFFFFF !important;
230
- }
231
-
232
- .newsletter-sidebar-buttons-content-tab {
233
- margin: 1px;
234
- position: relative;
235
- display: inline-block;
236
- }
237
- .newsletter-sidebar-buttons-content-tab:hover {
238
- cursor: move;
239
- }
240
- .newsletter-sidebar-buttons-content-tab:hover img{
241
- opacity: 0.8;
242
- }
243
- .newsletter-sidebar-buttons-content-tab:hover .newsletter-sidebar-buttons-content-tab-add{
244
- opacity: 0.5;
245
- }
246
- .newsletter-sidebar-buttons-content-tab-add {
247
- height: 100%;
248
- width: 100%;
249
- background-color: rgba(70,70,70,1);
250
- position: absolute;
251
- left: 0;
252
- top: 0;
253
- /*float: left;*/
254
- /*margin-top: -15px;*/
255
- /*margin-left: -50px;*/
256
- -webkit-transition: all 0.5s;
257
- -moz-transition: all 0.5s;
258
- -o-transition: all 0.5s;
259
- transition: all 0.5s;
260
- z-index: 2;
261
- opacity: 0;
262
- text-align: center;
263
- line-height: inherit;
264
- color: white;
265
- }
266
- .newsletter-sidebar-buttons-content-tab-add:hover {
267
- background-color: rgba(0,0,0,1);
268
- /* cursor: pointer;*/
269
- }
270
-
271
- #newsletter-builder-area-center-frame-content {
272
- /*float: left;*/
273
- /*width: 730px;*/
274
- /*background-color: rgba(153,153,153,1);*/
275
- min-height: 300px!important;
276
- padding-bottom: 75px;
277
- /*background-color: #ECF0F1;*/
278
- padding-top: 30px;
279
- /*border: 1px dashed #eee;*/
280
- overflow-y: scroll;
281
- /*max-height: 600px;*/
282
- }
283
-
284
- /* https://www.codegrepper.com/code-examples/css/how+ot+hide+scroll+bar+buy+allow+mouse+scrolling+css */
285
- #newsletter-builder-area-center-frame-content.tnp-view-mobile {
286
- margin: 20px auto 0px auto;
287
- width: 350px;
288
- height: 500px;
289
- -ms-overflow-style: none;
290
- scrollbar-width: none;
291
- border: 1px solid #ddd;
292
- border-radius: 10px;
293
- padding-top: 0;
294
- padding-bottom: 0;
295
- }
296
-
297
- #newsletter-builder-area-center-frame-content.tnp-view-mobile::-webkit-scrollbar {
298
- display: none;
299
- }
300
-
301
- #newsletter-builder-area-center-frame-content p {
302
- font-size: inherit;
303
- font-weight: inherit;
304
- font-family: inherit;
305
- color: inherit;
306
- }
307
-
308
- #newsletter-mobile-preview-area {
309
- margin-left: 30px;
310
- box-sizing: border-box;
311
- margin-top: 30px;
312
- text-align: center;
313
- border: 1px solid #ddd;
314
- border-radius: 10px;
315
- padding-left: 10px;
316
- padding-right: 10px;
317
- padding-top: 10px;
318
- }
319
- #newsletter-mobile-preview-area input {
320
- width: 100px;
321
- }
322
-
323
- iframe#tnpc-mobile-preview {
324
- height: 550px;
325
- box-sizing: border-box;
326
- width: 320px;
327
- border-radius: 10px;
328
- margin-top: 20px;
329
- margin-left: 20px;
330
- background-color: #F6F8FD;
331
- }
332
-
333
-
334
- .tnpc-row {
335
- -webkit-transition: box-shadow 0.5s;
336
- -moz-transition: box-shadow 0.5s;
337
- -o-transition: box-shadow 0.5s;
338
- transition: box-shadow 0.5s;
339
- position: relative;
340
- }
341
- .tnpc-row:hover {
342
- cursor: move;
343
- /*border: 2px dashed #999;*/
344
- -webkit-box-shadow: 0px 0px 20px 0px rgba(0,0,0,0.2);
345
- -moz-box-shadow: 0px 0px 20px 0px rgba(0,0,0,0.2);
346
- box-shadow: 0px 0px 20px 0px rgba(0,0,0,0.2);
347
- }
348
- .tnpc-row.ui-sortable-helper {
349
- max-width: 700px!important;
350
- }
351
- .tnpc-row-delete, .tnpc-row-edit-block, .tnpc-row-clone {
352
- height: 30px;
353
- width: 30px;
354
- top: 0px;
355
- background-color: rgba(255,255,255,0.5);
356
- z-index: 5;
357
- position: absolute;
358
- color: rgba(102,102,102,1);
359
- -webkit-transition: all 0.5s;
360
- -moz-transition: all 0.5s;
361
- -o-transition: all 0.5s;
362
- transition: all 0.5s;
363
- opacity: 0;
364
- text-align: center;
365
- font-size: 18px;
366
- }
367
- .tnpc-row-delete i, .tnpc-row-edit-block i, .tnpc-row-clone i {
368
- line-height: 30px;
369
- }
370
- .tnpc-row-delete:hover {
371
- background-color: #E74C3C;
372
- cursor: pointer;
373
- color: rgba(255,255,255,1);
374
- }
375
- .tnpc-row:hover .tnpc-row-delete, .tnpc-row:hover .tnpc-row-edit-block, .tnpc-row:hover .tnpc-row-clone {
376
- opacity: 1;
377
- }
378
- .tnpc-row-delete {
379
- right: 0px;
380
- z-index: 5;
381
- }
382
- .tnpc-row-edit-block {
383
- right: 60px;
384
- }
385
- .tnpc-row-edit-block:hover {
386
- background-color: #e0e0e0;
387
- cursor: pointer;
388
- color: rgba(0, 0, 0, 1);
389
- }
390
-
391
- .tnpc-row-clone {
392
- right: 30px;
393
- }
394
- .tnpc-row-clone:hover {
395
- background-color: #e0e0e0;;
396
- cursor: pointer;
397
- color: rgba(0,0,0,1);
398
- }
399
-
400
- .tnpc-row-edit {
401
- position: relative;
402
- }
403
- .tnpc-row-edit-hover {
404
- height: 100%;
405
- width: 100%;
406
- background-color: rgba(63,141,191,0.8);
407
- position: absolute;
408
- left: 0px;
409
- top: 0px;
410
- cursor: default;
411
- -webkit-transition: all 0.5s;
412
- -moz-transition: all 0.5s;
413
- -o-transition: all 0.5s;
414
- transition: all 0.5s;
415
- }
416
- .tnpc-row-edit-hover i {
417
- position: absolute;
418
- height: 30px;
419
- width: 30px;
420
- line-height: 30px;
421
- left: 50%;
422
- top: 50%;
423
- text-align: center;
424
- margin-top: -15px;
425
- margin-left: -15px;
426
- color: rgba(255,255,255,1);
427
- -webkit-transition: all 0.5s;
428
- -moz-transition: all 0.5s;
429
- -o-transition: all 0.5s;
430
- transition: all 0.5s;
431
- -webkit-border-radius: 50%;
432
- -moz-border-radius: 50%;
433
- border-radius: 50%;
434
- font-size: 16px;
435
- }
436
- .tnpc-row-edit-hover i:hover {
437
- background-color: rgba(0,0,0,0.5);
438
- cursor: pointer;
439
- }
440
-
441
- .tnpc-drop-here {
442
- padding:10px;
443
- text-align: center;
444
- }
445
-
446
- .tnpc-edit {
447
- height: 100vh;
448
- width: 100vw;
449
- z-index: 10;
450
- display: none;
451
- position: absolute;
452
- top: 0px;
453
- left: 0px;
454
- }
455
-
456
- .tnpc-edit-box-title {
457
- /*float: left;*/
458
- width: 100%;
459
- font-size: 29px;
460
- color: #666666;
461
- font-weight: 300;
462
- margin-bottom: 40px;
463
- }
464
-
465
- .tnpc-edit-box-content {
466
- /*float: left;*/
467
- width: 100%;
468
- margin-top: 10px;
469
- }
470
- .tnpc-edit-box-content-text {
471
- /*float: left;*/
472
- width: 100%;
473
- font-size: 15px;
474
- color: #666666;
475
- font-weight: 600;
476
- margin: 15px 0px 10px;
477
- text-transform: uppercase;
478
- }
479
-
480
- .tnpc-edit-box-content-text span {
481
- font-size: 11px;
482
- color: #95A5A6;
483
- background-color: #D3EADC;
484
- padding: 2px 5px;
485
- text-transform: none;
486
- border-radius: 5px;
487
- }
488
-
489
- .tnpc-edit-box-content-field {
490
- /*float: left;*/
491
- width: 100%;
492
- }
493
-
494
- .tnpc-edit-box-content-field-input {
495
- height: 33px;
496
- width: 380px;
497
- border: none !important;
498
- outline: none;
499
- font-family: inherit;
500
- padding-right: 10px;
501
- font-size: 15px;
502
- -webkit-transition: all 0.5s;
503
- -moz-transition: all 0.5s;
504
- -o-transition: all 0.5s;
505
- transition: all 0.5s;
506
- color: rgba(102,102,102,1);
507
- margin-bottom: 10px;
508
- }
509
- .tnpc-edit-box-content-field-input:focus {
510
- -webkit-box-shadow: inset 0px 0px 10px 0px rgba(0,0,0,0.2);
511
- -moz-box-shadow: inset 0px 0px 10px 0px rgba(0,0,0,0.2);
512
- box-shadow: inset 0px 0px 10px 0px rgba(0,0,0,0.2);
513
- }
514
- .tnpc-edit-box-content-field-textarea {
515
- /*float: left;*/
516
- height: 180px;
517
- width: 380px;
518
- border: 1px solid rgba(204,204,204,1);
519
- outline: none;
520
- font-family: inherit;
521
- font-size: 15px;
522
- -webkit-transition: all 0.5s;
523
- -moz-transition: all 0.5s;
524
- -o-transition: all 0.5s;
525
- transition: all 0.5s;
526
- color: rgba(102,102,102,1);
527
- resize: none;
528
- padding: 10px;
529
- }
530
- .tnpc-edit-box-content-field-textarea:focus {
531
- -webkit-box-shadow: inset 0px 0px 10px 0px rgba(0,0,0,0.2);
532
- -moz-box-shadow: inset 0px 0px 10px 0px rgba(0,0,0,0.2);
533
- box-shadow: inset 0px 0px 10px 0px rgba(0,0,0,0.2);
534
- }
535
- .tnpc-edit-box-content-icons {
536
- /*float: left;*/
537
- height: 388px;
538
- width: 388px;
539
- border: 1px solid rgba(204,204,204,1);
540
- margin-top: 15px;
541
- overflow-y: scroll;
542
- }
543
- .tnpc-edit-box-content-icons i {
544
- line-height: 50px;
545
- background-color: rgba(225,225,225,1);
546
- /*float: left;*/
547
- height: 50px;
548
- width: 50px;
549
- margin-top: 10px;
550
- margin-left: 10px;
551
- text-align: center;
552
- -webkit-transition: all 0.5s;
553
- -moz-transition: all 0.5s;
554
- -o-transition: all 0.5s;
555
- transition: all 0.5s;
556
- font-size: 28px;
557
- color: rgba(51,51,51,1);
558
- }
559
- .tnpc-edit-box-content-icons i:hover {
560
- cursor: pointer;
561
- background-color: rgba(153,153,153,1);
562
- -webkit-border-radius: 50%;
563
- -moz-border-radius: 50%;
564
- border-radius: 50%;
565
- color: rgba(0,0,0,1);
566
- }
567
-
568
- /* Save and cancel buttons container on block options popup */
569
- .tnpc-edit-box-buttons{
570
- margin-top: 10px;
571
- text-align: right;
572
- margin-right: 10px;
573
- }
574
-
575
- .tnpc-edit-box-buttons-save{
576
- height: 35px;
577
- text-align: right;
578
- line-height: 35px;
579
- color: #27AE60;
580
- font-weight: 600;
581
- font-size: 15px;
582
- -webkit-border-radius: 3px;
583
- -moz-border-radius: 3px;
584
- border-radius: 3px;
585
- -webkit-transition: background 0.5s;
586
- -moz-transition: background 0.5s;
587
- -o-transition: background 0.5s;
588
- transition: background 0.5s;
589
- cursor: pointer;
590
- /*float: left;*/
591
- /*padding-right: 25px;
592
- padding-left: 25px;
593
- width: 33%;*/
594
- display: inline-block;
595
- }
596
- .tnpc-edit-box-buttons-save:hover {
597
- color: #2ECC71;
598
- }
599
- .tnpc-edit-box-buttons-cancel{
600
- height: 35px;
601
- text-align: right;
602
- line-height: 35px;
603
- color: #666;
604
- font-weight: normal;
605
- font-size: 15px;
606
- -webkit-border-radius: 3px;
607
- -moz-border-radius: 3px;
608
- border-radius: 3px;
609
- -webkit-transition: background 0.5s;
610
- -moz-transition: background 0.5s;
611
- -o-transition: background 0.5s;
612
- transition: background 0.5s;
613
- cursor: pointer;
614
- /*float: left;*/
615
- /*padding-right: 25px;
616
- padding-left: 25px;
617
- width: 33%;*/
618
- display: inline-block;
619
- margin-right: 25px;
620
- }
621
- .tnpc-edit-box-buttons-cancel:hover {
622
- color: #E74C3C;
623
- }
624
-
625
-
626
- /* Tnp Composer Preview */
627
-
628
-
629
- .tnpc-subject a {
630
- font-family: Source Sans Pro;
631
- font-weight: 700;
632
- text-transform: uppercase;
633
- text-decoration: none;
634
- background-color: #3498DB;
635
- color: white;
636
- padding: 2px 10px;
637
- border-radius: 10px;
638
- font-size: 13px;
639
- letter-spacing: 0.1em;
640
- }
641
-
642
-
643
- .tnpc-preview {
644
- margin-top: 10px;
645
- }
646
-
647
- .tnpc-preview .fake-browser-ui iframe {
648
- width: 700px;
649
- }
650
-
651
- .tnpc-preview .fake-mobile-browser-ui iframe {
652
- width: 320px;
653
- }
654
-
655
- .fake-browser-ui {
656
- padding: 30px 0 0;
657
- border-radius: 3px;
658
- border-bottom: 10px solid #ccc;
659
- background: #ddd;
660
- display: inline-block;
661
- position: relative;
662
- line-height: 0;
663
- vertical-align: top;
664
- margin-left: 20px;
665
- }
666
-
667
- .fake-mobile-browser-ui {
668
- padding: 30px 10px 37px;
669
- border-radius: 10px;
670
- border-bottom: 10px solid #ccc;
671
- background: #ddd;
672
- display: inline-block;
673
- position: relative;
674
- line-height: 0;
675
- margin-left: 30px;
676
- }
677
-
678
- .fake-browser-ui .frame {
679
- display: block;
680
- height: 25px;
681
- position: absolute;
682
- top: 12px;
683
- left: 8px;
684
- }
685
-
686
- .fake-mobile-browser-ui .frame {
687
- display: block;
688
- height: 25px;
689
- margin-top: 10px;
690
- }
691
-
692
- .fake-browser-ui span {
693
- height: 12px;
694
- width: 12px;
695
- border-radius: 8px;
696
- background-color: #eee;
697
- border: 1px solid #dadada;
698
- float: left;
699
- margin: 0 0 0 4px;
700
- }
701
-
702
- .fake-mobile-browser-ui span {
703
- height: 50px;
704
- width: 50px;
705
- border-radius: 60px;
706
- background-color: #eee;
707
- border: 2px solid #ccc;
708
- display: block;
709
- margin: auto;
710
- }
711
-
712
- .fake-browser-ui .bt-1 {
713
- background-color: #ED594A;
714
- }
715
-
716
- .fake-browser-ui .bt-2 {
717
- background-color: #FDD800;
718
- }
719
-
720
- .fake-browser-ui .bt-3 {
721
- background-color: #5AC05A;
722
- }
723
-
724
-
725
- /* Tnp Html Editor */
726
-
727
- #tnpc-html-editor {
728
- height: 600px;
729
- border-top: 20px solid #323232;
730
- border-radius: 8px;
731
- }
732
-
733
- /* List Block Styles */ *
734
-
735
- .tnp-select2-option {
736
- /* background-color: red; */
737
- }
738
-
739
- .tnp-select2-option img {
740
- height: 15px;
741
- margin-right: 5px;
742
- vertical-align: middle;
743
- background-color: rgba(234, 234, 234, 0.25);
744
- padding: 10px;
745
- border-radius: 5px;
746
- }
747
-
748
- /* ************************************************************************** */
749
- /* Block options layer over the sidebar */
750
-
751
- /* Main container */
752
- #tnpc-block-options {
753
- /*height: 100vh;*/
754
- z-index: 10;
755
- flex-flow: column;
756
- display: none;
757
- position: absolute;
758
- top: 0px;
759
- left: 0px;
760
- bottom: 0;
761
- right: 0;
762
- background-color: #ECF0F1;
763
- padding: 0;
764
- }
765
-
766
- /* Save and cancel button container */
767
- #tnpc-block-options-buttons {
768
- padding: 20px;
769
- text-align: right;
770
- background-color: #ecf0f1;
771
- height: 70px;
772
- border-bottom: 1px solid #ffffff;
773
- }
774
-
775
-
776
- /* Form */
777
- #tnpc-block-options-form {
778
- background-color: #fff;
779
- padding: 15px;
780
- margin-top: 0;
781
- color: #444;
782
- background-color: #ECF0F1 !important;
783
- flex: 1 1 auto;
784
- overflow-y: auto;
785
- }
786
-
787
-
788
- #tnpc-block-options-form h3 {
789
- color: #000;
790
- }
791
-
792
- #tnpc-block-options-form table.form-table th {
793
- /*background-color: #f4f4f4;*/
794
- width: 100%;
795
- vertical-align: top;
796
- float: left;
797
- font-weight: normal;
798
- text-transform: uppercase;
799
- font-size: 13px;
800
- padding-top: 10px;
801
- padding-bottom: 5px;
802
- padding-left: 0;
803
- padding-right: 0;
804
-
805
- }
806
-
807
- #tnpc-block-options-form table.form-table td {
808
- float: left;
809
- padding: 0;
810
- margin: 0;
811
- border: 0;
812
- width: 100%;
813
- }
814
-
815
- #tnpc-block-options-form table.form-table {
816
- margin: 0px;
817
- border-collapse: separate!important;
818
- border-spacing: 1px!important;
819
- }
820
-
821
- #tnpc-block-options-form table.form-table table.tnp-button-colors {
822
- border: 0;
823
- border-collapse: collapse;
824
- }
825
- #tnpc-block-options-form table.form-table table.tnp-button-colors td {
826
- border: 0;
827
- padding-top: 0;
828
- }
829
-
830
- /* Style the tab */
831
- .tnpc-tabs {
832
- /*overflow: hidden;*/
833
- background-color: #fff;
834
- font-family: Montserrat, sans-serif;
835
- }
836
-
837
- /* Style the buttons that are used to open the tab content */
838
- .tnpc-tabs button {
839
- background-color: inherit;
840
- float: left;
841
- border: none;
842
- outline: none;
843
- cursor: pointer;
844
- padding: 14px 16px;
845
- transition: 0.3s;
846
- color: #6a8ba0;
847
- }
848
-
849
- /* Change background color of buttons on hover */
850
- .tnpc-tabs button:hover {
851
- background-color: #ddd;
852
- }
853
-
854
- /* Create an active/current tablink class */
855
- .tnpc-tabs button.active {
856
- background-color: #ECF0F1;
857
- color: #3498DB;
858
- }
859
-
860
- /* Style the tab content */
861
- .tabcontent {
862
- display: none;
863
- padding: 6px 12px;
864
- border-top: none;
865
- flex: 1 1 auto;
866
- overflow-y: auto;
867
- }
868
-
869
- /* Style Reset, Save, Save & Preview footer */
870
-
871
- .tnpc-controls {
872
- text-align: right;
873
- }
874
-
875
- .tnpc-logo {
876
- float: left;
877
- }
878
-
879
- .tnpc-logo p {
880
- font-family: Montserrat, Sans-serif;
881
- color: #fff !important;
882
- font-size: 16px;
883
- margin-left: 20px !important;
884
- margin-top: 5px !important;
885
- }
886
-
887
- #newsletter-builder-area ul {
888
- display: block;
889
- list-style-type: disc;
890
- margin-block-start: 1em;
891
- margin-block-end: 1em;
892
- margin-inline-start: 0;
893
- margin-inline-end: 0;
894
- padding-inline-start: 40px;
895
- }
896
-
897
- #newsletter-builder-area ul li {
898
- margin-bottom: 0;
899
- }
900
-
901
- /* Global Options elements style */
902
-
903
- #tnpc-general-options select {
904
- box-shadow: none;
905
- border-radius: 0px;
906
- border: none;
907
- margin-right: 5px;
908
- }
909
-
910
- /* Blocks options global styles */
911
-
912
- .tnpc-block-options-warning {
913
- background-color: #def9e9;
914
- padding: 10px 15px;
915
- }
916
-
917
- /* Presets */
918
- .tnpc-preset-container {
919
- max-width: 720px;
920
- margin: 0 auto;
921
- position: relative;
922
- }
923
-
924
- .tnpc-preset-legacy-themes {
925
- position: absolute;
926
- top: -15px;
927
- left: 15px;
928
- }
929
-
930
-
931
- .tnpc-preset {
932
- float: left;
933
- margin: 15px;
934
- cursor: pointer;
935
- width: 150px;
936
- height: 200px;
937
- background: white;
938
- border-radius: 8px;
939
- box-shadow: 0 0 2px 0 #dee3e4;
940
- position: relative;
941
- text-align: center;
942
- }
943
-
944
- .tnpc-preset-html {
945
- background-color: #5397d5;
946
- }
947
-
948
- .tnpc-preset-html span {
949
- color: #fff;
950
- }
951
-
952
- .tnpc-preset:hover {
953
- box-shadow: 0 0 8px 8px #dee3e4;
954
- }
955
-
956
- .tnpc-preset img {
957
- width: 50px;
958
- height: 50px;
959
- margin-top: 50px;
960
- }
961
-
962
- .tnpc-preset-label {
963
- position: absolute;
964
- top: 150px;
965
- left: 50%;
966
- transform: translate(-50%, -50%);
967
- font-size: 14px;
968
- width: 80%;
969
- font-family: soleil, sans-serif;
970
- color: #2f3241;
971
- font-weight: 200;
972
- }
973
-
974
- .tnpc-delete-preset {
975
- position: absolute;
976
- top: 0;
977
- right: 0;
978
- display: flex;
979
- align-items: center;
980
- justify-content: center;
981
- width: 20px;
982
- height: 20px;
983
- background-color: #8B0000;
984
- color: #FFF;
985
- font-weight: bold;
986
- border-radius: 0 8px 0 8px;
987
- z-index: 100;
988
- }
989
-
990
- .tnpc-edit-preset {
991
- position: absolute;
992
- top: 0;
993
- left: 0;
994
- display: flex;
995
- align-items: center;
996
- justify-content: center;
997
- width: 20px;
998
- height: 20px;
999
- background-color: #3498DB;
1000
- color: #FFF;
1001
- font-weight: bold;
1002
- border-radius: 8px 0 8px 0;
1003
- z-index: 100;
1004
- }
1005
-
1006
- .tnpc-inline-editable {
1007
- cursor: text;
1008
- }
1009
-
1010
- .tnpc-inline-editable {
1011
- cursor: text;
1012
- position: relative;
1013
- }
1014
-
1015
- .tnpc-inline-editable:hover {
1016
- color: #EEE !important;
1017
- }
1018
-
1019
- .tnpc-inline-editable:hover:after {
1020
- content: '';
1021
- cursor: pointer;
1022
- position: absolute;
1023
- top: 0;
1024
- left: 0;
1025
- width: 100%;
1026
- height: 100%;
1027
- background-color: rgba(0, 0, 0, 0.2);
1028
- opacity: 1;
1029
- border-radius: 2px;
1030
- }
1031
-
1032
- .tnpc-inline-editable:hover:before {
1033
- content: '\f464';
1034
- font-family: dashicons;
1035
- position: absolute;
1036
- top: 0;
1037
- bottom: 0;
1038
- left: 0;
1039
- right: 0;
1040
- margin: auto;
1041
- width: 32px;
1042
- height: 32px;
1043
- font-size: 32px;
1044
- line-height: 32px;
1045
- color: #333;
1046
- }
1047
-
1048
- .tnpc-inline-editable-form {
1049
- position: relative;
1050
- margin-top: 25px;
1051
- }
1052
-
1053
- .tnpc-inline-editable-form textarea,
1054
- .tnpc-inline-editable-form input {
1055
- width: 95%;
1056
- margin-left: 20px;
1057
- }
1058
-
1059
- .two-columns .tnpc-inline-editable-form-actions {
1060
- right: 0;
1061
- }
1062
-
1063
- .tnpc-inline-editable-form input {
1064
- padding: 5px;
1065
- font-size: 25px;
1066
- font-family: Helvetica, Arial, sans-serif;
1067
- font-weight: normal;
1068
- color: rgb(51, 51, 51);
1069
- line-height: normal;
1070
- }
1071
-
1072
- .tnpc-inline-editable-form-actions {
1073
- position: absolute;
1074
- top: -25px;
1075
- right: 5px;
1076
- display: flex;
1077
- align-items: center;
1078
- flex-wrap: wrap;
1079
- }
1080
-
1081
- .tnpc-inline-editable-form-actions button {
1082
- background: none;
1083
- padding: 0;
1084
- border: none;
1085
- }
1086
-
1087
- .tnpc-inline-editable-form-actions span {
1088
- display: block;
1089
- font-size: 25px;
1090
- color: #333;
1091
- cursor: pointer;
1092
- }
1093
-
1094
- .tnpc-inline-editable-form-actions span:first-child {
1095
- margin-right: 5px;
1096
- }
1097
-
1098
- #update-preset-button {
1099
- display: none;
1100
- }
1101
-
1102
- .separator {
1103
- display: flex;
1104
- align-items: center;
1105
- text-align: center;
1106
- padding: 1em 0;
1107
- }
1108
-
1109
- .separator::before,
1110
- .separator::after {
1111
- content: '';
1112
- flex: 1;
1113
- border-bottom: 1px solid #3c434a;
1114
- }
1115
-
1116
- .separator:not(:empty)::before {
1117
- margin-right: 1em;
1118
- }
1119
-
1120
- .separator:not(:empty)::after {
1121
- margin-left: 1em;
1122
- }
1123
-
1124
- #test-newsletter-modal .test-subscribers p {
1125
- margin: 0;
1126
- }
1127
-
1128
- #test-newsletter-modal h4 {
1129
- margin: 0 0 0.5rem 0;
1130
- }
1131
-
1132
- #test-newsletter-modal .separator {
1133
- padding: 1.25rem 0;
1134
- }
1135
-
1136
-
1137
- .tnp-suggest-subject {
1138
- cursor: pointer;
1139
- }
1140
-
1141
- /* Drag and drop placeholder */
1142
- .placeholder {
1143
- border: 1px dashed #bbb!important;
1144
- background-color: #fafafa!important;
1145
- height: 75px;
1146
- margin: 0 auto;
1147
- max-width: 600px;
1148
- box-sizing: border-box!important;
1149
- }
1150
-
1151
- #draggable-helper {
1152
- width: 600px;
1153
- border: 1px dashed #ddd;
1154
- opacity: 80%!important;
1155
- background-color: #fff;
1156
- text-align: center;
1157
- text-transform: uppercase;
1158
- font-size: 14px;
1159
- color: #666 !important;
1160
- padding: 20px;
1161
- }
1162
-
1163
- #sortable-helper {
1164
- width: 700px;
1165
- height: 75px;
1166
- border: 3px dashed #ddd;
1167
- opacity: .7;
1168
- background-color: #fff;
1169
- text-align: center;
1170
- text-transform: uppercase;
1171
- font-size: 14px; color: #aaa;
1172
- padding: 20px;
1173
- }
1
+ #wpbody-content {
2
+ padding-bottom: 15px;
3
+ }
4
+
5
+ .tnp-emails-composer {
6
+ margin: 15px 15px 0 0;
7
+ }
8
+
9
+
10
+ /* Contains the newsletter editing area and the tools */
11
+ #newsletter-builder {
12
+ height: calc(100vh - 120px);
13
+ display: flex;
14
+ flex-flow: row;
15
+ position: relative;
16
+ overflow: hidden;
17
+ box-sizing: border-box;
18
+ max-width: 1380px;
19
+ margin: 0 auto;
20
+ }
21
+
22
+ /* Contains the newsletter editing area */
23
+ #newsletter-builder-area {
24
+ display: flex;
25
+ flex-flow: column;
26
+ flex-shrink: 0;
27
+ background-color: #fff;
28
+ width: 800px;
29
+ box-sizing: border-box;
30
+ border-radius: 3px;
31
+ overflow: hidden;
32
+ float: left;
33
+
34
+ position: relative;
35
+ }
36
+
37
+ /* Contains the tools */
38
+ #newsletter-builder-sidebar {
39
+ display: flex;
40
+ flex-flow: column;
41
+ /* flex-grow: 1;*/
42
+ max-width: 510px;
43
+ min-width: 410px;
44
+ background-color: #ECF0F1;
45
+ margin-left: 20px;
46
+ overflow-y: scroll;
47
+ /* Needed for block options form */
48
+ position: relative;
49
+ border-radius: 3px;
50
+ overflow: hidden;
51
+ }
52
+
53
+ #tnpc-subject-wrap {
54
+ border-bottom: 1px solid #ccc;
55
+ padding-bottom: 20px;
56
+ padding-top: 20px;
57
+ padding-left: 20px;
58
+ position:relative;
59
+ }
60
+
61
+ #tnpc-subject-wrap th {
62
+ color: #999;
63
+ font-size: 14px;
64
+ font-weight: normal!important;
65
+ text-align: right;
66
+ padding-right: 10px;
67
+ padding-bottom: 10px;
68
+ vertical-align: middle;
69
+ }
70
+
71
+ #tnpc-subject-wrap td {
72
+ color: #000;
73
+ font-size: 14px;
74
+ font-weight: normal!important;
75
+ text-align: left;
76
+ padding-right: 10px;
77
+ padding-bottom: 10px;
78
+ vertical-align: middle;
79
+ }
80
+
81
+ #tnpc-subject-wrap th[dir=rtl] {
82
+ text-align: left;
83
+ }
84
+ #tnpc-subject-wrap td[dir=rtl] {
85
+ text-align: right;
86
+ }
87
+ #tnpc-subject-wrap td[dir=rtl] #options-title {
88
+ margin-right: 0;
89
+ }
90
+
91
+ #tnpc-subject-wrap .composer-actions {
92
+ position: absolute;
93
+ right: 10px;
94
+ bottom: 5px;
95
+ display: flex;
96
+ align-items: center;
97
+ }
98
+
99
+ #tnpc-subject-wrap .composer-view-mode {
100
+ display: flex;
101
+ }
102
+
103
+ #tnpc-subject-wrap .composer-view-mode .composer-view-mode__item {
104
+ display: block;
105
+ width: 30px;
106
+ height: 30px;
107
+ line-height: 30px;
108
+ box-sizing: content-box;
109
+ text-align: center;
110
+ margin-left: 3px;
111
+ border-radius: 3px;
112
+ border: solid 1px #3c434a;
113
+ background-color: #FFF;
114
+ color: #3c434a;
115
+ font-size: 12px;
116
+ cursor: pointer;
117
+ }
118
+
119
+ #tnpc-subject-wrap .composer-view-mode .composer-view-mode__item--active {
120
+ background-color: #2b2f3a;
121
+ border-color: #2b2f3a;
122
+ color: #FFF;
123
+ }
124
+
125
+ #tnpc-subject-wrap .composer-view-mode .composer-view-mode__item:hover{
126
+ background-color: #3c434a;
127
+ border-color: #3c434a;
128
+ color: #FFF;
129
+ }
130
+
131
+ .composer-actions .button-primary {
132
+ background-color: #2b2f3a;
133
+ }
134
+
135
+ .composer-actions .button-primary:hover {
136
+ background-color: #3c434a;
137
+ }
138
+
139
+ #test-newsletter-button {
140
+ background-color: #2b2f3a;
141
+ }
142
+
143
+ #test-newsletter-button:hover {
144
+ background-color: #3c434a;
145
+ }
146
+
147
+ #tnpc-subject #options-subject-subject,
148
+ #options-preheader
149
+ {
150
+ width: 600px;
151
+ margin-right: 15px;
152
+ border: 1px solid #ddd;
153
+ font-size: 14px;
154
+ font-family: monospace !important;
155
+ }
156
+
157
+ #options-preheader + .tnp-description
158
+ {
159
+ margin-top: 0;
160
+ }
161
+
162
+ #tnpc-subject i {
163
+ font-size: 18px;
164
+ }
165
+
166
+ .tnp-composer-heading {
167
+ background-color: #0073aa;
168
+ border-radius: 3px !important;
169
+ position: fixed;
170
+ bottom: 0;
171
+ right: 0;
172
+ left: 56px;
173
+ z-index: 1000;
174
+ margin: 0 15px 10px 0;
175
+ }
176
+
177
+ .tnp-composer-heading h2 {
178
+ display: inline-block;
179
+ margin-left: 30px !important;
180
+ text-transform: none !important;
181
+ font-weight: 400 !important;
182
+ }
183
+
184
+ .tnp-composer-heading a {
185
+ display: inline-block;
186
+ margin-left: 30px;
187
+ }
188
+
189
+ .tnp-composer-heading form {
190
+ display: inline-block;
191
+ margin-left: 30px;
192
+ }
193
+
194
+ .tnp-composer-heading img {
195
+ width: 50px;
196
+ vertical-align: middle;
197
+ }
198
+
199
+ #newsletter-builder-sidebar h4 {
200
+ /* margin: 5px; */
201
+ /* text-align: center; */
202
+ color: #868686;
203
+ /* margin-bottom: 20px; */
204
+ font-family: Montserrat, sans-serif;
205
+ font-weight: 300;
206
+ border-bottom: 1px solid #fff;
207
+ padding-bottom: 10px;
208
+ }
209
+
210
+ /*#newsletter-builder-sidebar h4 span {
211
+ background-color: #27AE60;
212
+ padding: 2px 5px;
213
+ border-radius: 3px;
214
+ }*/
215
+
216
+
217
+ #newsletter-builder-sidebar .newsletter-sidebar-add-buttons img {
218
+ width: 150px;
219
+ height: auto;
220
+ }
221
+
222
+ .newsletter-sidebar-add-buttons {
223
+ border-radius: 4px;
224
+ padding: 5px;
225
+ margin: 5px;
226
+ }
227
+
228
+ .tnp-body-lite {
229
+ background-color: #FFFFFF !important;
230
+ }
231
+
232
+ .newsletter-sidebar-buttons-content-tab {
233
+ margin: 1px;
234
+ position: relative;
235
+ display: inline-block;
236
+ }
237
+ .newsletter-sidebar-buttons-content-tab:hover {
238
+ cursor: move;
239
+ }
240
+ .newsletter-sidebar-buttons-content-tab:hover img{
241
+ opacity: 0.8;
242
+ }
243
+ .newsletter-sidebar-buttons-content-tab:hover .newsletter-sidebar-buttons-content-tab-add{
244
+ opacity: 0.5;
245
+ }
246
+ .newsletter-sidebar-buttons-content-tab-add {
247
+ height: 100%;
248
+ width: 100%;
249
+ background-color: rgba(70,70,70,1);
250
+ position: absolute;
251
+ left: 0;
252
+ top: 0;
253
+ /*float: left;*/
254
+ /*margin-top: -15px;*/
255
+ /*margin-left: -50px;*/
256
+ -webkit-transition: all 0.5s;
257
+ -moz-transition: all 0.5s;
258
+ -o-transition: all 0.5s;
259
+ transition: all 0.5s;
260
+ z-index: 2;
261
+ opacity: 0;
262
+ text-align: center;
263
+ line-height: inherit;
264
+ color: white;
265
+ }
266
+ .newsletter-sidebar-buttons-content-tab-add:hover {
267
+ background-color: rgba(0,0,0,1);
268
+ /* cursor: pointer;*/
269
+ }
270
+
271
+ #newsletter-builder-area-center-frame-content {
272
+ /*float: left;*/
273
+ /*width: 730px;*/
274
+ /*background-color: rgba(153,153,153,1);*/
275
+ min-height: 300px!important;
276
+ padding-bottom: 75px;
277
+ /*background-color: #ECF0F1;*/
278
+ padding-top: 30px;
279
+ /*border: 1px dashed #eee;*/
280
+ overflow-y: scroll;
281
+ /*max-height: 600px;*/
282
+ }
283
+
284
+ /* https://www.codegrepper.com/code-examples/css/how+ot+hide+scroll+bar+buy+allow+mouse+scrolling+css */
285
+ #newsletter-builder-area-center-frame-content.tnp-view-mobile {
286
+ margin: 20px auto 0px auto;
287
+ width: 350px;
288
+ height: 500px;
289
+ -ms-overflow-style: none;
290
+ scrollbar-width: none;
291
+ border: 1px solid #ddd;
292
+ border-radius: 10px;
293
+ padding-top: 0;
294
+ padding-bottom: 0;
295
+ }
296
+
297
+ #newsletter-builder-area-center-frame-content.tnp-view-mobile::-webkit-scrollbar {
298
+ display: none;
299
+ }
300
+
301
+ #newsletter-builder-area-center-frame-content p {
302
+ font-size: inherit;
303
+ font-weight: inherit;
304
+ font-family: inherit;
305
+ color: inherit;
306
+ }
307
+
308
+ #newsletter-mobile-preview-area {
309
+ margin-left: 30px;
310
+ box-sizing: border-box;
311
+ margin-top: 30px;
312
+ text-align: center;
313
+ border: 1px solid #ddd;
314
+ border-radius: 10px;
315
+ padding-left: 10px;
316
+ padding-right: 10px;
317
+ padding-top: 10px;
318
+ }
319
+ #newsletter-mobile-preview-area input {
320
+ width: 100px;
321
+ }
322
+
323
+ iframe#tnpc-mobile-preview {
324
+ height: 550px;
325
+ box-sizing: border-box;
326
+ width: 320px;
327
+ border-radius: 10px;
328
+ margin-top: 20px;
329
+ margin-left: 20px;
330
+ background-color: #F6F8FD;
331
+ }
332
+
333
+
334
+ .tnpc-row {
335
+ -webkit-transition: box-shadow 0.5s;
336
+ -moz-transition: box-shadow 0.5s;
337
+ -o-transition: box-shadow 0.5s;
338
+ transition: box-shadow 0.5s;
339
+ position: relative;
340
+ }
341
+ .tnpc-row:hover {
342
+ cursor: move;
343
+ /*border: 2px dashed #999;*/
344
+ -webkit-box-shadow: 0px 0px 20px 0px rgba(0,0,0,0.2);
345
+ -moz-box-shadow: 0px 0px 20px 0px rgba(0,0,0,0.2);
346
+ box-shadow: 0px 0px 20px 0px rgba(0,0,0,0.2);
347
+ }
348
+ .tnpc-row.ui-sortable-helper {
349
+ max-width: 700px!important;
350
+ }
351
+ .tnpc-row-delete, .tnpc-row-edit-block, .tnpc-row-clone {
352
+ height: 30px;
353
+ width: 30px;
354
+ top: 0px;
355
+ background-color: rgba(255,255,255,0.5);
356
+ z-index: 5;
357
+ position: absolute;
358
+ color: rgba(102,102,102,1);
359
+ -webkit-transition: all 0.5s;
360
+ -moz-transition: all 0.5s;
361
+ -o-transition: all 0.5s;
362
+ transition: all 0.5s;
363
+ opacity: 0;
364
+ text-align: center;
365
+ font-size: 18px;
366
+ }
367
+ .tnpc-row-delete i, .tnpc-row-edit-block i, .tnpc-row-clone i {
368
+ line-height: 30px;
369
+ }
370
+ .tnpc-row-delete:hover {
371
+ background-color: #E74C3C;
372
+ cursor: pointer;
373
+ color: rgba(255,255,255,1);
374
+ }
375
+ .tnpc-row:hover .tnpc-row-delete, .tnpc-row:hover .tnpc-row-edit-block, .tnpc-row:hover .tnpc-row-clone {
376
+ opacity: 1;
377
+ }
378
+ .tnpc-row-delete {
379
+ right: 0px;
380
+ z-index: 5;
381
+ }
382
+ .tnpc-row-edit-block {
383
+ right: 60px;
384
+ }
385
+ .tnpc-row-edit-block:hover {
386
+ background-color: #e0e0e0;
387
+ cursor: pointer;
388
+ color: rgba(0, 0, 0, 1);
389
+ }
390
+
391
+ .tnpc-row-clone {
392
+ right: 30px;
393
+ }
394
+ .tnpc-row-clone:hover {
395
+ background-color: #e0e0e0;;
396
+ cursor: pointer;
397
+ color: rgba(0,0,0,1);
398
+ }
399
+
400
+ .tnpc-row-edit {
401
+ position: relative;
402
+ }
403
+ .tnpc-row-edit-hover {
404
+ height: 100%;
405
+ width: 100%;
406
+ background-color: rgba(63,141,191,0.8);
407
+ position: absolute;
408
+ left: 0px;
409
+ top: 0px;
410
+ cursor: default;
411
+ -webkit-transition: all 0.5s;
412
+ -moz-transition: all 0.5s;
413
+ -o-transition: all 0.5s;
414
+ transition: all 0.5s;
415
+ }
416
+ .tnpc-row-edit-hover i {
417
+ position: absolute;
418
+ height: 30px;
419
+ width: 30px;
420
+ line-height: 30px;
421
+ left: 50%;
422
+ top: 50%;
423
+ text-align: center;
424
+ margin-top: -15px;
425
+ margin-left: -15px;
426
+ color: rgba(255,255,255,1);
427
+ -webkit-transition: all 0.5s;
428
+ -moz-transition: all 0.5s;
429
+ -o-transition: all 0.5s;
430
+ transition: all 0.5s;
431
+ -webkit-border-radius: 50%;
432
+ -moz-border-radius: 50%;
433
+ border-radius: 50%;
434
+ font-size: 16px;
435
+ }
436
+ .tnpc-row-edit-hover i:hover {
437
+ background-color: rgba(0,0,0,0.5);
438
+ cursor: pointer;
439
+ }
440
+
441
+ .tnpc-drop-here {
442
+ padding:10px;
443
+ text-align: center;
444
+ }
445
+
446
+ .tnpc-edit {
447
+ height: 100vh;
448
+ width: 100vw;
449
+ z-index: 10;
450
+ display: none;
451
+ position: absolute;
452
+ top: 0px;
453
+ left: 0px;
454
+ }
455
+
456
+ .tnpc-edit-box-title {
457
+ /*float: left;*/
458
+ width: 100%;
459
+ font-size: 29px;
460
+ color: #666666;
461
+ font-weight: 300;
462
+ margin-bottom: 40px;
463
+ }
464
+
465
+ .tnpc-edit-box-content {
466
+ /*float: left;*/
467
+ width: 100%;
468
+ margin-top: 10px;
469
+ }
470
+ .tnpc-edit-box-content-text {
471
+ /*float: left;*/
472
+ width: 100%;
473
+ font-size: 15px;
474
+ color: #666666;
475
+ font-weight: 600;
476
+ margin: 15px 0px 10px;
477
+ text-transform: uppercase;
478
+ }
479
+
480
+ .tnpc-edit-box-content-text span {
481
+ font-size: 11px;
482
+ color: #95A5A6;
483
+ background-color: #D3EADC;
484
+ padding: 2px 5px;
485
+ text-transform: none;
486
+ border-radius: 5px;
487
+ }
488
+
489
+ .tnpc-edit-box-content-field {
490
+ /*float: left;*/
491
+ width: 100%;
492
+ }
493
+
494
+ .tnpc-edit-box-content-field-input {
495
+ height: 33px;
496
+ width: 380px;
497
+ border: none !important;
498
+ outline: none;
499
+ font-family: inherit;
500
+ padding-right: 10px;
501
+ font-size: 15px;
502
+ -webkit-transition: all 0.5s;
503
+ -moz-transition: all 0.5s;
504
+ -o-transition: all 0.5s;
505
+ transition: all 0.5s;
506
+ color: rgba(102,102,102,1);
507
+ margin-bottom: 10px;
508
+ }
509
+ .tnpc-edit-box-content-field-input:focus {
510
+ -webkit-box-shadow: inset 0px 0px 10px 0px rgba(0,0,0,0.2);
511
+ -moz-box-shadow: inset 0px 0px 10px 0px rgba(0,0,0,0.2);
512
+ box-shadow: inset 0px 0px 10px 0px rgba(0,0,0,0.2);
513
+ }
514
+ .tnpc-edit-box-content-field-textarea {
515
+ /*float: left;*/
516
+ height: 180px;
517
+ width: 380px;
518
+ border: 1px solid rgba(204,204,204,1);
519
+ outline: none;
520
+ font-family: inherit;
521
+ font-size: 15px;
522
+ -webkit-transition: all 0.5s;
523
+ -moz-transition: all 0.5s;
524
+ -o-transition: all 0.5s;
525
+ transition: all 0.5s;
526
+ color: rgba(102,102,102,1);
527
+ resize: none;
528
+ padding: 10px;
529
+ }
530
+ .tnpc-edit-box-content-field-textarea:focus {
531
+ -webkit-box-shadow: inset 0px 0px 10px 0px rgba(0,0,0,0.2);
532
+ -moz-box-shadow: inset 0px 0px 10px 0px rgba(0,0,0,0.2);
533
+ box-shadow: inset 0px 0px 10px 0px rgba(0,0,0,0.2);
534
+ }
535
+ .tnpc-edit-box-content-icons {
536
+ /*float: left;*/
537
+ height: 388px;
538
+ width: 388px;
539
+ border: 1px solid rgba(204,204,204,1);
540
+ margin-top: 15px;
541
+ overflow-y: scroll;
542
+ }
543
+ .tnpc-edit-box-content-icons i {
544
+ line-height: 50px;
545
+ background-color: rgba(225,225,225,1);
546
+ /*float: left;*/
547
+ height: 50px;
548
+ width: 50px;
549
+ margin-top: 10px;
550
+ margin-left: 10px;
551
+ text-align: center;
552
+ -webkit-transition: all 0.5s;
553
+ -moz-transition: all 0.5s;
554
+ -o-transition: all 0.5s;
555
+ transition: all 0.5s;
556
+ font-size: 28px;
557
+ color: rgba(51,51,51,1);
558
+ }
559
+ .tnpc-edit-box-content-icons i:hover {
560
+ cursor: pointer;
561
+ background-color: rgba(153,153,153,1);
562
+ -webkit-border-radius: 50%;
563
+ -moz-border-radius: 50%;
564
+ border-radius: 50%;
565
+ color: rgba(0,0,0,1);
566
+ }
567
+
568
+ /* Save and cancel buttons container on block options popup */
569
+ .tnpc-edit-box-buttons{
570
+ margin-top: 10px;
571
+ text-align: right;
572
+ margin-right: 10px;
573
+ }
574
+
575
+ .tnpc-edit-box-buttons-save{
576
+ height: 35px;
577
+ text-align: right;
578
+ line-height: 35px;
579
+ color: #27AE60;
580
+ font-weight: 600;
581
+ font-size: 15px;
582
+ -webkit-border-radius: 3px;
583
+ -moz-border-radius: 3px;
584
+ border-radius: 3px;
585
+ -webkit-transition: background 0.5s;
586
+ -moz-transition: background 0.5s;
587
+ -o-transition: background 0.5s;
588
+ transition: background 0.5s;
589
+ cursor: pointer;
590
+ /*float: left;*/
591
+ /*padding-right: 25px;
592
+ padding-left: 25px;
593
+ width: 33%;*/
594
+ display: inline-block;
595
+ }
596
+ .tnpc-edit-box-buttons-save:hover {
597
+ color: #2ECC71;
598
+ }
599
+ .tnpc-edit-box-buttons-cancel{
600
+ height: 35px;
601
+ text-align: right;
602
+ line-height: 35px;
603
+ color: #666;
604
+ font-weight: normal;
605
+ font-size: 15px;
606
+ -webkit-border-radius: 3px;
607
+ -moz-border-radius: 3px;
608
+ border-radius: 3px;
609
+ -webkit-transition: background 0.5s;
610
+ -moz-transition: background 0.5s;
611
+ -o-transition: background 0.5s;
612
+ transition: background 0.5s;
613
+ cursor: pointer;
614
+ /*float: left;*/
615
+ /*padding-right: 25px;
616
+ padding-left: 25px;
617
+ width: 33%;*/
618
+ display: inline-block;
619
+ margin-right: 25px;
620
+ }
621
+ .tnpc-edit-box-buttons-cancel:hover {
622
+ color: #E74C3C;
623
+ }
624
+
625
+
626
+ /* Tnp Composer Preview */
627
+
628
+
629
+ .tnpc-subject a {
630
+ font-family: Source Sans Pro;
631
+ font-weight: 700;
632
+ text-transform: uppercase;
633
+ text-decoration: none;
634
+ background-color: #3498DB;
635
+ color: white;
636
+ padding: 2px 10px;
637
+ border-radius: 10px;
638
+ font-size: 13px;
639
+ letter-spacing: 0.1em;
640
+ }
641
+
642
+
643
+ .tnpc-preview {
644
+ margin-top: 10px;
645
+ }
646
+
647
+ .tnpc-preview .fake-browser-ui iframe {
648
+ width: 700px;
649
+ }
650
+
651
+ .tnpc-preview .fake-mobile-browser-ui iframe {
652
+ width: 320px;
653
+ }
654
+
655
+ .fake-browser-ui {
656
+ padding: 30px 0 0;
657
+ border-radius: 3px;
658
+ border-bottom: 10px solid #ccc;
659
+ background: #ddd;
660
+ display: inline-block;
661
+ position: relative;
662
+ line-height: 0;
663
+ vertical-align: top;
664
+ margin-left: 20px;
665
+ }
666
+
667
+ .fake-mobile-browser-ui {
668
+ padding: 30px 10px 37px;
669
+ border-radius: 10px;
670
+ border-bottom: 10px solid #ccc;
671
+ background: #ddd;
672
+ display: inline-block;
673
+ position: relative;
674
+ line-height: 0;
675
+ margin-left: 30px;
676
+ }
677
+
678
+ .fake-browser-ui .frame {
679
+ display: block;
680
+ height: 25px;
681
+ position: absolute;
682
+ top: 12px;
683
+ left: 8px;
684
+ }
685
+
686
+ .fake-mobile-browser-ui .frame {
687
+ display: block;
688
+ height: 25px;
689
+ margin-top: 10px;
690
+ }
691
+
692
+ .fake-browser-ui span {
693
+ height: 12px;
694
+ width: 12px;
695
+ border-radius: 8px;
696
+ background-color: #eee;
697
+ border: 1px solid #dadada;
698
+ float: left;
699
+ margin: 0 0 0 4px;
700
+ }
701
+
702
+ .fake-mobile-browser-ui span {
703
+ height: 50px;
704
+ width: 50px;
705
+ border-radius: 60px;
706
+ background-color: #eee;
707
+ border: 2px solid #ccc;
708
+ display: block;
709
+ margin: auto;
710
+ }
711
+
712
+ .fake-browser-ui .bt-1 {
713
+ background-color: #ED594A;
714
+ }
715
+
716
+ .fake-browser-ui .bt-2 {
717
+ background-color: #FDD800;
718
+ }
719
+
720
+ .fake-browser-ui .bt-3 {
721
+ background-color: #5AC05A;
722
+ }
723
+
724
+
725
+ /* Tnp Html Editor */
726
+
727
+ #tnpc-html-editor {
728
+ height: 600px;
729
+ border-top: 20px solid #323232;
730
+ border-radius: 8px;
731
+ }
732
+
733
+ /* List Block Styles */ *
734
+
735
+ .tnp-select2-option {
736
+ /* background-color: red; */
737
+ }
738
+
739
+ .tnp-select2-option img {
740
+ height: 15px;
741
+ margin-right: 5px;
742
+ vertical-align: middle;
743
+ background-color: rgba(234, 234, 234, 0.25);
744
+ padding: 10px;
745
+ border-radius: 5px;
746
+ }
747
+
748
+ /* ************************************************************************** */
749
+ /* Block options layer over the sidebar */
750
+
751
+ /* Main container */
752
+ #tnpc-block-options {
753
+ /*height: 100vh;*/
754
+ z-index: 10;
755
+ flex-flow: column;
756
+ display: none;
757
+ position: absolute;
758
+ top: 0px;
759
+ left: 0px;
760
+ bottom: 0;
761
+ right: 0;
762
+ background-color: #ECF0F1;
763
+ padding: 0;
764
+ }
765
+
766
+ /* Save and cancel button container */
767
+ #tnpc-block-options-buttons {
768
+ padding: 20px;
769
+ text-align: right;
770
+ background-color: #ecf0f1;
771
+ height: 70px;
772
+ border-bottom: 1px solid #ffffff;
773
+ }
774
+
775
+
776
+ /* Form */
777
+ #tnpc-block-options-form {
778
+ background-color: #fff;
779
+ padding: 15px;
780
+ margin-top: 0;
781
+ color: #444;
782
+ background-color: #ECF0F1 !important;
783
+ flex: 1 1 auto;
784
+ overflow-y: auto;
785
+ }
786
+
787
+
788
+ #tnpc-block-options-form h3 {
789
+ color: #000;
790
+ }
791
+
792
+ #tnpc-block-options-form table.form-table th {
793
+ /*background-color: #f4f4f4;*/
794
+ width: 100%;
795
+ vertical-align: top;
796
+ float: left;
797
+ font-weight: normal;
798
+ text-transform: uppercase;
799
+ font-size: 13px;
800
+ padding-top: 10px;
801
+ padding-bottom: 5px;
802
+ padding-left: 0;
803
+ padding-right: 0;
804
+
805
+ }
806
+
807
+ #tnpc-block-options-form table.form-table td {
808
+ float: left;
809
+ padding: 0;
810
+ margin: 0;
811
+ border: 0;
812
+ width: 100%;
813
+ }
814
+
815
+ #tnpc-block-options-form table.form-table {
816
+ margin: 0px;
817
+ border-collapse: separate!important;
818
+ border-spacing: 1px!important;
819
+ }
820
+
821
+ #tnpc-block-options-form table.form-table table.tnp-button-colors {
822
+ border: 0;
823
+ border-collapse: collapse;
824
+ }
825
+ #tnpc-block-options-form table.form-table table.tnp-button-colors td {
826
+ border: 0;
827
+ padding-top: 0;
828
+ }
829
+
830
+ /* Style the tab */
831
+ .tnpc-tabs {
832
+ /*overflow: hidden;*/
833
+ background-color: #fff;
834
+ font-family: Montserrat, sans-serif;
835
+ }
836
+
837
+ /* Style the buttons that are used to open the tab content */
838
+ .tnpc-tabs button {
839
+ background-color: inherit;
840
+ float: left;
841
+ border: none;
842
+ outline: none;
843
+ cursor: pointer;
844
+ padding: 14px 16px;
845
+ transition: 0.3s;
846
+ color: #6a8ba0;
847
+ }
848
+
849
+ /* Change background color of buttons on hover */
850
+ .tnpc-tabs button:hover {
851
+ background-color: #ddd;
852
+ }
853
+
854
+ /* Create an active/current tablink class */
855
+ .tnpc-tabs button.active {
856
+ background-color: #ECF0F1;
857
+ color: #3498DB;
858
+ }
859
+
860
+ /* Style the tab content */
861
+ .tabcontent {
862
+ display: none;
863
+ padding: 6px 12px;
864
+ border-top: none;
865
+ flex: 1 1 auto;
866
+ overflow-y: auto;
867
+ }
868
+
869
+ /* Style Reset, Save, Save & Preview footer */
870
+
871
+ .tnpc-controls {
872
+ text-align: right;
873
+ }
874
+
875
+ .tnpc-logo {
876
+ float: left;
877
+ }
878
+
879
+ .tnpc-logo p {
880
+ font-family: Montserrat, Sans-serif;
881
+ color: #fff !important;
882
+ font-size: 16px;
883
+ margin-left: 20px !important;
884
+ margin-top: 5px !important;
885
+ }
886
+
887
+ #newsletter-builder-area ul {
888
+ display: block;
889
+ list-style-type: disc;
890
+ margin-block-start: 1em;
891
+ margin-block-end: 1em;
892
+ margin-inline-start: 0;
893
+ margin-inline-end: 0;
894
+ padding-inline-start: 40px;
895
+ }
896
+
897
+ #newsletter-builder-area ul li {
898
+ margin-bottom: 0;
899
+ }
900
+
901
+ /* Global Options elements style */
902
+
903
+ #tnpc-general-options select {
904
+ box-shadow: none;
905
+ border-radius: 0px;
906
+ border: none;
907
+ margin-right: 5px;
908
+ }
909
+
910
+ /* Blocks options global styles */
911
+
912
+ .tnpc-block-options-warning {
913
+ background-color: #def9e9;
914
+ padding: 10px 15px;
915
+ }
916
+
917
+ /* Presets */
918
+ .tnpc-preset-container {
919
+ max-width: 720px;
920
+ margin: 0 auto;
921
+ position: relative;
922
+ }
923
+
924
+ .tnpc-preset-legacy-themes {
925
+ position: absolute;
926
+ top: -15px;
927
+ left: 15px;
928
+ }
929
+
930
+
931
+ .tnpc-preset {
932
+ float: left;
933
+ margin: 15px;
934
+ cursor: pointer;
935
+ width: 150px;
936
+ height: 200px;
937
+ background: white;
938
+ border-radius: 8px;
939
+ box-shadow: 0 0 2px 0 #dee3e4;
940
+ position: relative;
941
+ text-align: center;
942
+ }
943
+
944
+ .tnpc-preset-html {
945
+ background-color: #5397d5;
946
+ }
947
+
948
+ .tnpc-preset-html span {
949
+ color: #fff;
950
+ }
951
+
952
+ .tnpc-preset:hover {
953
+ box-shadow: 0 0 8px 8px #dee3e4;
954
+ }
955
+
956
+ .tnpc-preset img {
957
+ width: 50px;
958
+ height: 50px;
959
+ margin-top: 50px;
960
+ }
961
+
962
+ .tnpc-preset-label {
963
+ position: absolute;
964
+ top: 150px;
965
+ left: 50%;
966
+ transform: translate(-50%, -50%);
967
+ font-size: 14px;
968
+ width: 80%;
969
+ font-family: soleil, sans-serif;
970
+ color: #2f3241;
971
+ font-weight: 200;
972
+ }
973
+
974
+ .tnpc-delete-preset {
975
+ position: absolute;
976
+ top: 0;
977
+ right: 0;
978
+ display: flex;
979
+ align-items: center;
980
+ justify-content: center;
981
+ width: 20px;
982
+ height: 20px;
983
+ background-color: #8B0000;
984
+ color: #FFF;
985
+ font-weight: bold;
986
+ border-radius: 0 8px 0 8px;
987
+ z-index: 100;
988
+ }
989
+
990
+ .tnpc-edit-preset {
991
+ position: absolute;
992
+ top: 0;
993
+ left: 0;
994
+ display: flex;
995
+ align-items: center;
996
+ justify-content: center;
997
+ width: 20px;
998
+ height: 20px;
999
+ background-color: #3498DB;
1000
+ color: #FFF;
1001
+ font-weight: bold;
1002
+ border-radius: 8px 0 8px 0;
1003
+ z-index: 100;
1004
+ }
1005
+
1006
+ .tnpc-inline-editable {
1007
+ cursor: text;
1008
+ }
1009
+
1010
+ .tnpc-inline-editable {
1011
+ cursor: text;
1012
+ position: relative;
1013
+ }
1014
+
1015
+ .tnpc-inline-editable:hover {
1016
+ color: #EEE !important;
1017
+ }
1018
+
1019
+ .tnpc-inline-editable:hover:after {
1020
+ content: '';
1021
+ cursor: pointer;
1022
+ position: absolute;
1023
+ top: 0;
1024
+ left: 0;
1025
+ width: 100%;
1026
+ height: 100%;
1027
+ background-color: rgba(0, 0, 0, 0.2);
1028
+ opacity: 1;
1029
+ border-radius: 2px;
1030
+ }
1031
+
1032
+ .tnpc-inline-editable:hover:before {
1033
+ content: '\f464';
1034
+ font-family: dashicons;
1035
+ position: absolute;
1036
+ top: 0;
1037
+ bottom: 0;
1038
+ left: 0;
1039
+ right: 0;
1040
+ margin: auto;
1041
+ width: 32px;
1042
+ height: 32px;
1043
+ font-size: 32px;
1044
+ line-height: 32px;
1045
+ color: #333;
1046
+ }
1047
+
1048
+ .tnpc-inline-editable-form {
1049
+ position: relative;
1050
+ margin-top: 25px;
1051
+ }
1052
+
1053
+ .tnpc-inline-editable-form textarea,
1054
+ .tnpc-inline-editable-form input {
1055
+ width: 95%;
1056
+ margin-left: 20px;
1057
+ }
1058
+
1059
+ .two-columns .tnpc-inline-editable-form-actions {
1060
+ right: 0;
1061
+ }
1062
+
1063
+ .tnpc-inline-editable-form input {
1064
+ padding: 5px;
1065
+ font-size: 25px;
1066
+ font-family: Helvetica, Arial, sans-serif;
1067
+ font-weight: normal;
1068
+ color: rgb(51, 51, 51);
1069
+ line-height: normal;
1070
+ }
1071
+
1072
+ .tnpc-inline-editable-form-actions {
1073
+ position: absolute;
1074
+ top: -25px;
1075
+ right: 5px;
1076
+ display: flex;
1077
+ align-items: center;
1078
+ flex-wrap: wrap;
1079
+ }
1080
+
1081
+ .tnpc-inline-editable-form-actions button {
1082
+ background: none;
1083
+ padding: 0;
1084
+ border: none;
1085
+ }
1086
+
1087
+ .tnpc-inline-editable-form-actions span {
1088
+ display: block;
1089
+ font-size: 25px;
1090
+ color: #333;
1091
+ cursor: pointer;
1092
+ }
1093
+
1094
+ .tnpc-inline-editable-form-actions span:first-child {
1095
+ margin-right: 5px;
1096
+ }
1097
+
1098
+ #update-preset-button {
1099
+ display: none;
1100
+ }
1101
+
1102
+ .separator {
1103
+ display: flex;
1104
+ align-items: center;
1105
+ text-align: center;
1106
+ padding: 1em 0;
1107
+ }
1108
+
1109
+ .separator::before,
1110
+ .separator::after {
1111
+ content: '';
1112
+ flex: 1;
1113
+ border-bottom: 1px solid #3c434a;
1114
+ }
1115
+
1116
+ .separator:not(:empty)::before {
1117
+ margin-right: 1em;
1118
+ }
1119
+
1120
+ .separator:not(:empty)::after {
1121
+ margin-left: 1em;
1122
+ }
1123
+
1124
+ #test-newsletter-modal .test-subscribers p {
1125
+ margin: 0;
1126
+ }
1127
+
1128
+ #test-newsletter-modal h4 {
1129
+ margin: 0 0 0.5rem 0;
1130
+ }
1131
+
1132
+ #test-newsletter-modal .separator {
1133
+ padding: 1.25rem 0;
1134
+ }
1135
+
1136
+
1137
+ .tnp-suggest-subject {
1138
+ cursor: pointer;
1139
+ }
1140
+
1141
+ /* Drag and drop placeholder */
1142
+ .placeholder {
1143
+ border: 1px dashed #bbb!important;
1144
+ background-color: #fafafa!important;
1145
+ height: 75px;
1146
+ margin: 0 auto;
1147
+ max-width: 600px;
1148
+ box-sizing: border-box!important;
1149
+ }
1150
+
1151
+ #draggable-helper {
1152
+ width: 600px;
1153
+ border: 1px dashed #ddd;
1154
+ opacity: 80%!important;
1155
+ background-color: #fff;
1156
+ text-align: center;
1157
+ text-transform: uppercase;
1158
+ font-size: 14px;
1159
+ color: #666 !important;
1160
+ padding: 20px;
1161
+ }
1162
+
1163
+ #sortable-helper {
1164
+ width: 700px;
1165
+ height: 75px;
1166
+ border: 3px dashed #ddd;
1167
+ opacity: .7;
1168
+ background-color: #fff;
1169
+ text-align: center;
1170
+ text-transform: uppercase;
1171
+ font-size: 14px; color: #aaa;
1172
+ padding: 20px;
1173
+ }
emails/tnp-composer/_scripts/newsletter-builder-v2.js CHANGED
@@ -1,980 +1,980 @@
1
- // add delete buttons
2
- jQuery.fn.add_delete = function () {
3
- this.append('<div class="tnpc-row-delete" title="Delete"><img src="' + TNP_PLUGIN_URL + '/emails/tnp-composer/_assets/delete.png" width="32"></div>');
4
- this.find('.tnpc-row-delete').perform_delete();
5
- };
6
-
7
- // delete row
8
- jQuery.fn.perform_delete = function () {
9
- this.click(function () {
10
- tnpc_hide_block_options();
11
- // remove block
12
- jQuery(this).parent().remove();
13
- tnpc_mobile_preview();
14
- });
15
- }
16
-
17
- // add edit button
18
- jQuery.fn.add_block_edit = function () {
19
- this.append('<div class="tnpc-row-edit-block" title="Edit"><img src="' + TNP_PLUGIN_URL + '/emails/tnp-composer/_assets/edit.png" width="32"></div>');
20
- this.find('.tnpc-row-edit-block').perform_block_edit();
21
- }
22
-
23
- // edit block
24
- jQuery.fn.perform_block_edit = function () {
25
-
26
- jQuery(".tnpc-row-edit-block").click(function (e) {
27
- e.preventDefault()
28
- });
29
-
30
- this.click(function (e) {
31
-
32
- e.preventDefault();
33
-
34
- target = jQuery(this).parent().find('.edit-block');
35
-
36
- // The row container which is a global variable and used later after the options save
37
- container = jQuery(this).closest("table");
38
-
39
- if (container.hasClass('tnpc-row-block')) {
40
-
41
- tnpc_show_block_options();
42
-
43
- var options = container.find(".tnpc-block-content").attr("data-json");
44
-
45
- // Compatibility
46
- if (!options) {
47
- options = target.attr("data-options");
48
- }
49
-
50
- var data = {
51
- action: "tnpc_options",
52
- id: container.data("id"),
53
- context_type: tnp_context_type,
54
- options: options
55
- };
56
-
57
- tnpc_add_global_options(data);
58
-
59
- builderAreaHelper.lock();
60
- jQuery("#tnpc-block-options-form").load(ajaxurl, data, function () {
61
- console.log('Block form options loaded');
62
- start_options = jQuery("#tnpc-block-options-form").serializeArray();
63
- tnpc_add_global_options(start_options);
64
- builderAreaHelper.unlock();
65
- });
66
-
67
- } else {
68
- alert("This is deprecated block version and cannot be edited. Please replace it with a new one.");
69
- }
70
-
71
- });
72
-
73
- };
74
-
75
- // add clone button
76
- jQuery.fn.add_block_clone = function () {
77
- this.append('<div class="tnpc-row-clone" title="Clone"><img src="' + TNP_PLUGIN_URL + '/emails/tnp-composer/_assets/copy.png" width="32"></div>');
78
- this.find('.tnpc-row-clone').perform_clone();
79
- }
80
-
81
- // clone block
82
- jQuery.fn.perform_clone = function () {
83
-
84
- jQuery(".tnpc-row-clone").click(function (e) {
85
- e.preventDefault()
86
- });
87
-
88
- this.click(function (e) {
89
-
90
- e.preventDefault();
91
-
92
- // hide block edit form
93
- tnpc_hide_block_options();
94
-
95
- // find the row
96
- let row = jQuery(this).closest('.tnpc-row');
97
-
98
- // clone the block
99
- let new_row = row.clone();
100
- new_row.find(".tnpc-row-delete").remove();
101
- new_row.find(".tnpc-row-edit-block").remove();
102
- new_row.find(".tnpc-row-clone").remove();
103
-
104
- new_row.add_delete();
105
- new_row.add_block_edit();
106
- new_row.add_block_clone();
107
- // if (new_row.hasClass('tnpc-row-block')) {
108
- // new_row.find(".tnpc-row-edit-block i").click();
109
- // }
110
- new_row.insertAfter(row);
111
- tnpc_mobile_preview();
112
- });
113
- };
114
-
115
- let start_options = null;
116
- let container = null;
117
-
118
- jQuery(function () {
119
-
120
- // open blocks tab
121
- document.getElementById("defaultOpen").click();
122
-
123
- // preload content from a body named input
124
- var preloadedContent = jQuery('input[name="message"]').val();
125
- if (!preloadedContent) {
126
- preloadedContent = jQuery('input[name="options[message]"]').val();
127
- }
128
-
129
- if (!preloadedContent) {
130
- tnpc_show_presets_modal();
131
- } else {
132
- jQuery('#newsletter-builder-area-center-frame-content').html(preloadedContent);
133
- start_composer();
134
- }
135
-
136
- // subject management
137
- jQuery('#options-subject').val(jQuery('#tnpc-form input[name="options[subject]"]').val());
138
-
139
- // preheader management
140
- jQuery('#options-preheader').val(jQuery('#tnpc-form input[name="options[options_preheader]"]').val());
141
-
142
- // ======================== //
143
- // == BACKGROUND COLOR == //
144
- // ======================== //
145
- _setBuilderAreaBackgroundColor(document.getElementById('options-options_composer_background').value);
146
-
147
- function _setBuilderAreaBackgroundColor(color) {
148
- jQuery('#newsletter-builder-area-center-frame-content').css('background-color', color);
149
- }
150
-
151
- window._setBuilderAreaBackgroundColor = _setBuilderAreaBackgroundColor; //BAD STUFF!!!
152
-
153
- // ======================== //
154
- // == BACKGROUND COLOR == //
155
- // ======================== //
156
-
157
- });
158
-
159
- function BuilderAreaHelper() {
160
-
161
- var _builderAreaEl = document.querySelector('#newsletter-builder-area');
162
- var _overlayEl = document.createElement('div');
163
- _overlayEl.style.zIndex = 99999;
164
- _overlayEl.style.position = 'absolute';
165
- _overlayEl.style.top = 0;
166
- _overlayEl.style.left = 0;
167
- _overlayEl.style.width = '100%';
168
- _overlayEl.style.height = '100%';
169
-
170
- this.lock = function () {
171
- console.log('Lock builder area');
172
- _builderAreaEl.appendChild(_overlayEl);
173
- }
174
-
175
- this.unlock = function () {
176
- console.log('Unlock builder area');
177
- _builderAreaEl.removeChild(_overlayEl);
178
- }
179
-
180
- }
181
-
182
- let builderAreaHelper = new BuilderAreaHelper();
183
-
184
- function init_builder_area() {
185
-
186
- //Drag & Drop
187
- jQuery("#newsletter-builder-area-center-frame-content").sortable({
188
- revert: false,
189
- placeholder: "placeholder",
190
- forcePlaceholderSize: true,
191
- opacity: 0.6,
192
- tolerance: "pointer",
193
- helper: function (e) {
194
- var helper = jQuery(document.getElementById("sortable-helper")).clone();
195
- return helper;
196
- },
197
- update: function (event, ui) {
198
- if (ui.item.attr("id") == "draggable-helper") {
199
- loading_row = jQuery('<div style="text-align: center; padding: 20px; background-color: #d4d5d6; color: #52BE7F;"><i class="fa fa-cog fa-2x fa-spin" /></div>');
200
- ui.item.before(loading_row);
201
- ui.item.remove();
202
- var data = new Array(
203
- {"name": 'action', "value": 'tnpc_render'},
204
- {"name": 'id', "value": ui.item.data("id")},
205
- {"name": 'b', "value": ui.item.data("id")},
206
- {"name": 'full', "value": 1},
207
- {"name": '_wpnonce', "value": tnp_nonce}
208
- );
209
-
210
- tnpc_add_global_options(data);
211
-
212
- jQuery.post(ajaxurl, data, function (response) {
213
-
214
- var new_row = jQuery(response);
215
- // ui.item.before(new_row);
216
- // ui.item.remove();
217
- loading_row.before(new_row);
218
- loading_row.remove();
219
- new_row.add_delete();
220
- new_row.add_block_edit();
221
- new_row.add_block_clone();
222
- // new_row.find(".tnpc-row-edit").hover_edit();
223
- if (new_row.hasClass('tnpc-row-block')) {
224
- new_row.find(".tnpc-row-edit-block").click();
225
- }
226
- tnpc_mobile_preview();
227
- }).fail(function () {
228
- alert("Block rendering failed.");
229
- loading_row.remove();
230
- });
231
- } else {
232
- tnpc_mobile_preview();
233
- }
234
- }
235
- });
236
-
237
- jQuery(".newsletter-sidebar-buttons-content-tab").draggable({
238
- connectToSortable: "#newsletter-builder-area-center-frame-content",
239
-
240
- // Build the helper for dragging
241
- helper: function (e) {
242
- var helper = jQuery(document.getElementById("draggable-helper")).clone();
243
- // Do not uset .data() with jQuery
244
- helper.attr("data-id", e.currentTarget.dataset.id);
245
- helper.html(e.currentTarget.dataset.name);
246
- return helper;
247
- },
248
- revert: false,
249
- start: function () {
250
- if (jQuery('.tnpc-row').length) {
251
- } else {
252
- jQuery('#newsletter-builder-area-center-frame-content').append('<div class="tnpc-drop-here">Drag&Drop blocks here!</div>');
253
- }
254
- },
255
- stop: function (event, ui) {
256
- jQuery('.tnpc-drop-here').remove();
257
- }
258
- });
259
-
260
- jQuery(".tnpc-row").add_delete();
261
- jQuery(".tnpc-row").add_block_edit();
262
- jQuery(".tnpc-row").add_block_clone();
263
-
264
- }
265
-
266
- function start_composer() {
267
-
268
- init_builder_area();
269
-
270
- // Closes the block options layer (without saving)
271
- jQuery("#tnpc-block-options-cancel").click(function () {
272
-
273
- tnpc_hide_block_options();
274
-
275
- var _target = target;
276
-
277
- jQuery.post(ajaxurl, start_options, function (response) {
278
- _target.html(response);
279
- jQuery("#tnpc-block-options-form").html("");
280
- });
281
- });
282
-
283
- // Fires the save event for block options
284
- jQuery("#tnpc-block-options-save").click(function (e) {
285
- e.preventDefault();
286
-
287
- var _target = target;
288
-
289
- // fix for Codemirror
290
- if (typeof templateEditor !== 'undefined') {
291
- templateEditor.save();
292
- }
293
-
294
- if (window.tinymce)
295
- window.tinymce.triggerSave();
296
-
297
- var data = jQuery("#tnpc-block-options-form").serializeArray();
298
-
299
- tnpc_add_global_options(data);
300
-
301
- tnpc_hide_block_options();
302
-
303
- jQuery.post(ajaxurl, data, function (response) {
304
- _target.html(response);
305
- tnpc_mobile_preview();
306
- jQuery("#tnpc-block-options-form").html("");
307
- });
308
- });
309
-
310
- // live preview from block options *** EXPERIMENTAL ***
311
- jQuery('#tnpc-block-options-form').change(function (event) {
312
- var data = jQuery("#tnpc-block-options-form").serializeArray();
313
-
314
- var _container = container;
315
- var _target = target;
316
-
317
- tnpc_add_global_options(data);
318
-
319
- jQuery.post(ajaxurl, data, function (response) {
320
- _target.html(response);
321
- if (event.target.dataset.afterRendering === 'reload') {
322
- _container.find(".tnpc-row-edit-block").click();
323
- }
324
- }).fail(function () {
325
- alert("Block rendering failed");
326
- });
327
-
328
- });
329
-
330
- tnpc_mobile_preview();
331
-
332
- }
333
-
334
- function tnpc_show_block_options() {
335
-
336
- const animationDuration = 500;
337
-
338
- //jQuery("#tnpc-blocks").fadeOut(animationDuration);
339
- //jQuery("#tnpc-global-styles").fadeOut(animationDuration);
340
- //jQuery("#tnpc-mobile-tab").fadeOut(animationDuration);
341
- //jQuery("#tnpc-test-tab").fadeOut(animationDuration);
342
-
343
- jQuery("#tnpc-block-options").fadeIn(animationDuration);
344
- jQuery("#tnpc-block-options").css('display', 'flex');
345
-
346
- }
347
-
348
- function tnpc_hide_block_options() {
349
-
350
- const animationDuration = 500;
351
-
352
- jQuery("#tnpc-block-options").fadeOut(animationDuration);
353
-
354
- //var $activeTab = jQuery(".tnpc-tabs .tablinks.active");
355
- //jQuery('#' + $activeTab.data('tabId')).fadeIn(animationDuration);
356
-
357
- jQuery("#tnpc-block-options-form").html('');
358
-
359
- }
360
-
361
- function tnpc_mobile_preview() {
362
-
363
- return;
364
-
365
- }
366
-
367
- function tnpc_save(form) {
368
-
369
- form.elements["options[message]"].value = tnpc_get_email_content_from_builder_area();
370
-
371
- // When the composer is not showing the subject field (for example in Automated)
372
- if (document.getElementById("options-preheader")) {
373
- form.elements["options[options_preheader]"].value = jQuery('#options-preheader').val();
374
- } else {
375
- form.elements["options[options_preheader]"].value = "";
376
- }
377
- if (document.getElementById("options-subject")) {
378
- form.elements["options[subject]"].value = jQuery('#options-subject-subject').val();
379
- } else {
380
- form.elements["options[subject]"].value = "";
381
- }
382
-
383
- var global_form = document.getElementById("tnpc-global-styles-form");
384
- //Copy "Global styles" form inputs into main form
385
- tnpc_copy_form(global_form, form);
386
-
387
- }
388
-
389
- function tnpc_get_email_content_from_builder_area() {
390
-
391
- var $elMessage = jQuery("#newsletter-builder-area-center-frame-content").clone();
392
-
393
- $elMessage.find('.tnpc-row-delete').remove();
394
- $elMessage.find('.tnpc-row-edit-block').remove();
395
- $elMessage.find('.tnpc-row-clone').remove();
396
- $elMessage.find('.tnpc-row').removeClass('ui-draggable');
397
- $elMessage.find('#sortable-helper').remove();
398
-
399
- return $elMessage.html();
400
-
401
- }
402
-
403
- function tnpc_copy_form(source, dest) {
404
- for (var i = 0; i < source.elements.length; i++) {
405
- var field = document.createElement("input");
406
- field.type = "hidden";
407
- field.name = source.elements[i].name;
408
- field.value = source.elements[i].value;
409
-
410
- // Non clona le select!
411
- //var clonedEl = source.elements[i].cloneNode();
412
- //clonedEl.style.display = 'none';
413
- dest.appendChild(field);
414
- }
415
- }
416
-
417
- function tnpc_test() {
418
- let form = document.getElementById("tnpc-form");
419
- tnpc_save(form);
420
- form.act.value = "test";
421
- form.submit();
422
- }
423
-
424
- function openTab(evt, tabName) {
425
- evt.preventDefault();
426
- // Declare all variables
427
- var i, tabcontent, tablinks;
428
-
429
- // Get all elements with class="tabcontent" and hide them
430
- tabcontent = document.getElementsByClassName("tabcontent");
431
- for (i = 0; i < tabcontent.length; i++) {
432
- tabcontent[i].style.display = "none";
433
- }
434
-
435
- // Get all elements with class="tablinks" and remove the class "active"
436
- tablinks = document.getElementsByClassName("tablinks");
437
- for (i = 0; i < tablinks.length; i++) {
438
- tablinks[i].className = tablinks[i].className.replace(" active", "");
439
- }
440
-
441
- // Show the current tab, and add an "active" class to the button that opened the tab
442
- document.getElementById(tabName).style.display = "block";
443
- evt.currentTarget.className += " active";
444
- }
445
-
446
- function tnpc_scratch() {
447
-
448
- jQuery('#newsletter-builder-area-center-frame-content').html(" ");
449
- init_builder_area();
450
-
451
- }
452
-
453
- function tnpc_reload_options(e) {
454
- e.preventDefault();
455
- let options = jQuery("#tnpc-block-options-form").serializeArray();
456
- for (let i = 0; i < options.length; i++) {
457
- if (options[i].name === 'action') {
458
- options[i].value = 'tnpc_options';
459
- }
460
- }
461
-
462
- jQuery("#tnpc-block-options-form").load(ajaxurl, options);
463
- }
464
-
465
- function tnpc_add_global_options(data) {
466
- let globalOptions = jQuery("#tnpc-global-styles-form").serializeArray();
467
- for (let i = 0; i < globalOptions.length; i++) {
468
- globalOptions[i].name = globalOptions[i].name.replace("[options_", "[").replace("options[", "composer[").replace("composer_", "");
469
- if (Array.isArray(data)) {
470
- data.push(globalOptions[i]);
471
- } else {
472
- //Inline edit data format is object not array
473
- data[globalOptions[i].name] = globalOptions[i].value;
474
- }
475
- }
476
- }
477
-
478
- // ==================================================== //
479
- // ================= PRESET ===================== //
480
- // ==================================================== //
481
-
482
- //TODO non va bene tenere nel global space variabili che altri potrebbero accidentalmente modificare/usare
483
- // ma questo è un test
484
- const toastBottom = new TnpToast({duration: 5000, position: 'bottom right', wrapperPadding: '70px 20px'});
485
-
486
- //TODO - spostare gestione dei preset in contesto privato ma aggiungendo comunque a window le funzioni triggerate da html (load_preset, delete_preset,...) per mantenere compatibilità?
487
- const presetListModal = new TNPModal({
488
- closeWhenClickOutside: true,
489
- showClose: true,
490
- style: {
491
- backgroundColor: '#ECF0F1',
492
- height: '400px',
493
- width: '740px',
494
- },
495
- onClose: function () {
496
- start_composer();
497
- //Enable buttons
498
- jQuery('.tnpc-controls input[type=button]').attr('disabled', false);
499
- }
500
- });
501
-
502
- function tnpc_show_presets_modal() {
503
-
504
- jQuery('.tnpc-controls input[type=button]').attr('disabled', true);
505
-
506
- const elModalContent = presetListModal.open();
507
-
508
- jQuery.ajax({
509
- type: "POST",
510
- url: ajaxurl,
511
- data: {
512
- action: "tnpc_get_all_presets",
513
- context_type: tnp_context_type,
514
- },
515
- success: function (res) {
516
- jQuery(elModalContent).html(res.data);
517
- },
518
- });
519
-
520
- }
521
-
522
- function tnpc_load_preset(id, subject, isEditMode) {
523
-
524
- presetListModal.close();
525
-
526
- jQuery.ajax({
527
- type: "POST",
528
- url: ajaxurl,
529
- data: {
530
- action: "tnpc_get_preset",
531
- id: id
532
- },
533
- success: function (res) {
534
- jQuery('#newsletter-builder-area-center-frame-content').html(res.data.content);
535
- _restore_global_options(res.data.globalOptions);
536
-
537
- start_composer();
538
-
539
- if (!isEditMode) {
540
- //Enable buttons
541
- jQuery('.tnpc-controls input[type=button]').attr('disabled', false);
542
- }
543
-
544
- if (subject && subject.length > 0) {
545
- jQuery('#options-subject').val(tnpc_remove_double_quotes_escape_from(subject));
546
- }
547
- },
548
- });
549
-
550
- function _restore_global_options(options) {
551
- jQuery.each(options, function (name, value) {
552
- var el = jQuery(`#tnpc-global-styles-form #options-options_composer_${name}`);
553
- if (el.length) {
554
- el.val(value);
555
- }
556
- });
557
-
558
- tnp_controls_init();
559
- _setBuilderAreaBackgroundColor(document.getElementById('options-options_composer_background').value);
560
- }
561
-
562
- }
563
-
564
- function tnpc_save_preset(form) {
565
-
566
- const presetName = tnpc_remove_double_quotes_from(document.querySelector('#options-subject').value);
567
-
568
- const presetNameModal = new TNPModal({
569
- title: 'Choose a preset name',
570
- content: '<input type="text" id="preset_name" style="width: 100%" placeholder="Preset name" value="' + presetName + '"/>',
571
- showConfirm: true,
572
- clickConfirmOnPressEnter: true,
573
- onConfirm: function () {
574
- const inputEl = document.querySelector('#preset_name');
575
- document.querySelector('#options-subject').value = inputEl.value;
576
- tnpc_save(form);
577
- form.submit();
578
- }
579
- });
580
-
581
- presetNameModal.open();
582
-
583
- }
584
-
585
- function tnpc_delete_preset(presetId, name, event) {
586
- event.stopPropagation();
587
-
588
- const presetDeleteModal = new TNPModal({
589
- title: `Are you sure to delete "${name}" preset?`,
590
- confirmText: 'DELETE PRESET',
591
- confirmClassName: 'button-secondary button-danger',
592
- showConfirm: true,
593
- onConfirm: function () {
594
-
595
- const wrapperPresetEl = event.target.closest(".tnpc-preset");
596
-
597
- jQuery.ajax({
598
- type: 'POST',
599
- dataType: 'json',
600
- url: ajaxurl,
601
- data: {
602
- action: 'tnpc_delete_preset',
603
- _wpnonce: tnp_preset_nonce,
604
- presetId: presetId
605
- },
606
- success: function (response) {
607
- if (response.success) {
608
- wrapperPresetEl.parentNode.removeChild(wrapperPresetEl);
609
- toastBottom.success('Preset successfully deleted!');
610
- }
611
- }
612
- });
613
-
614
- }
615
- });
616
-
617
- presetDeleteModal.open();
618
-
619
- }
620
-
621
- function tnpc_edit_preset(presetId, name, event) {
622
- event.stopPropagation();
623
- tnpc_load_preset(presetId, name, true);
624
-
625
- //DISABLE BUTTON AND SHOW UPDATE BUTTON
626
- const composerForm = document.querySelector('#tnpc-form');
627
- const buttons = composerForm.querySelectorAll('input[type=button]');
628
- const updatePresetButton = composerForm.querySelector('#update-preset-button');
629
-
630
- for (btn of buttons) {
631
- if (btn.id && btn.id === 'save-preset-button') {
632
- btn.style.display = 'none';
633
- updatePresetButton.style.display = 'inline';
634
- updatePresetButton.disabled = false;
635
- } else {
636
- btn.disabled = true;
637
- }
638
- }
639
-
640
- //Add preset id hidden field
641
- const presetIdfield = document.createElement("input");
642
- presetIdfield.type = "hidden";
643
- presetIdfield.name = "preset_id";
644
- presetIdfield.value = presetId;
645
- composerForm.appendChild(presetIdfield);
646
-
647
- }
648
-
649
- function tnpc_remove_double_quotes_escape_from(str) {
650
- return str.replace(/\\"/g, '"');
651
- }
652
-
653
- function tnpc_remove_double_quotes_from(str) {
654
- return str.replace(/['"]+/g, '');
655
- }
656
-
657
- function tnpc_update_preset(form) {
658
-
659
- const presetName = tnpc_remove_double_quotes_from(document.querySelector('#options-subject').value);
660
-
661
- const presetNameModal = new TNPModal({
662
- title: 'Choose a preset name',
663
- content: '<input type="text" id="preset_name" style="width: 100%" placeholder="Preset name" value="' + presetName + '"/>',
664
- showConfirm: true,
665
- clickConfirmOnPressEnter: true,
666
- onConfirm: function () {
667
- const inputEl = document.querySelector('#preset_name');
668
- document.querySelector('#options-subject').value = inputEl.value;
669
- tnpc_save(form);
670
- form.submit();
671
- }
672
- });
673
-
674
- presetNameModal.open();
675
-
676
- }
677
-
678
- // ========================================================= //
679
- // ================= PRESET FINE ===================== //
680
- // ========================================================= //
681
-
682
- jQuery(document).ready(function () {
683
- 'use strict'
684
-
685
- var TNPInlineEditor = (function () {
686
-
687
- var className = 'tnpc-inline-editable';
688
- var newInputName = 'new_name';
689
- var activeInlineElements = [];
690
-
691
- function init() {
692
- // find all inline editable elements
693
- jQuery('#newsletter-builder-area-center-frame-content').on('click', '.' + className, function (e) {
694
- e.preventDefault();
695
- removeAllActiveElements();
696
-
697
- var originalEl = jQuery(this).hide();
698
- var newEl = jQuery(getEditableComponent(this.innerText.trim(), this.dataset.id, this.dataset.type, originalEl)).insertAfter(this);
699
-
700
- activeInlineElements.push({'originalEl': originalEl, 'newEl': newEl});
701
-
702
- //Add submit event listener for newly created block
703
- jQuery('.tnpc-inline-editable-form-' + this.dataset.type + this.dataset.id).on('submit', function (e) {
704
- submit(e, newEl, jQuery(originalEl));
705
- });
706
-
707
- //Add close event listener for newly created block
708
- jQuery('.tnpc-inline-editable-form-actions .tnpc-dismiss-' + this.dataset.type + this.dataset.id).on('click', function (e) {
709
- removeAllActiveElements();
710
- });
711
-
712
- });
713
-
714
- // Close all created elements if clicked outside
715
- jQuery('#newsletter-builder-area-center-frame-content').on('click', function (e) {
716
- if (activeInlineElements.length > 0
717
- && !jQuery(e.target).hasClass(className)
718
- && jQuery(e.target).closest('.tnpc-inline-editable-container').length === 0) {
719
- removeAllActiveElements();
720
- }
721
- });
722
-
723
- }
724
-
725
- function removeAllActiveElements() {
726
- activeInlineElements.forEach(function (obj) {
727
- obj.originalEl.show();
728
-
729
- obj.newEl.off();
730
- obj.newEl.remove();
731
- });
732
-
733
- activeInlineElements = []
734
- }
735
-
736
- function getEditableComponent(value, id, type, originalEl) {
737
-
738
- var element = '';
739
-
740
- //COPY FONT STYLE FROM ORIGINAL ELEMENT
741
- var fontFamily = originalEl.css('font-family');
742
- var fontSize = originalEl.css('font-size');
743
- var styleAttr = "style='font-family:" + fontFamily + ";font-size:" + fontSize + ";'";
744
-
745
- switch (type) {
746
- case 'text':
747
- {
748
- element = "<textarea name='" + newInputName + "' class='" + className + "-textarea' rows='5' " + styleAttr + ">" + value + "</textarea>";
749
- break;
750
- }
751
- case 'title':
752
- {
753
- element = "<textarea name='" + newInputName + "' class='" + className + "-textarea' rows='2'" + styleAttr + ">" + value + "</textarea>";
754
- break;
755
- }
756
- }
757
-
758
- var component = "<td>";
759
- component += "<form class='tnpc-inline-editable-form tnpc-inline-editable-form-" + type + id + "'>";
760
- component += "<input type='hidden' name='id' value='" + id + "'>";
761
- component += "<input type='hidden' name='type' value='" + type + "'>";
762
- component += "<input type='hidden' name='old_value' value='" + value + "'>";
763
- component += "<div class='tnpc-inline-editable-container'>";
764
- component += element;
765
- component += "<div class='tnpc-inline-editable-form-actions'>";
766
- component += "<button type='submit'><span class='dashicons dashicons-yes-alt' title='save'></span></button>";
767
- component += "<span class='dashicons dashicons-dismiss tnpc-dismiss-" + type + id + "' title='close'></span>";
768
- component += "</div>";
769
- component += "</div>";
770
- component += "</form>";
771
- component += "</td>";
772
- return component;
773
- }
774
-
775
- function submit(e, elementToDeleteAfterSubmit, elementToShow) {
776
- e.preventDefault();
777
-
778
- var id = elementToDeleteAfterSubmit.find('form input[name=id]').val();
779
- var type = elementToDeleteAfterSubmit.find('form input[name=type]').val();
780
- var newValue = elementToDeleteAfterSubmit.find('form [name="' + newInputName + '"]').val();
781
-
782
- ajax_render_block(elementToShow, type, id, newValue);
783
-
784
- elementToDeleteAfterSubmit.remove();
785
- elementToShow.show();
786
-
787
- }
788
-
789
- function ajax_render_block(inlineElement, type, postId, newContent) {
790
-
791
- var target = inlineElement.closest('.edit-block');
792
- var container = target.closest('table');
793
- var blockContent = target.children('.tnpc-block-content');
794
-
795
- if (container.hasClass('tnpc-row-block')) {
796
- var data = {
797
- 'action': 'tnpc_render',
798
- 'id': container.data('id'),
799
- 'b': container.data('id'),
800
- 'full': 1,
801
- '_wpnonce': tnp_nonce,
802
- 'options': {
803
- 'inline_edits': [{
804
- 'type': type,
805
- 'post_id': postId,
806
- 'content': newContent
807
- }]
808
- },
809
- 'encoded_options': blockContent.data('json')
810
- };
811
-
812
- tnpc_add_global_options(data);
813
-
814
- jQuery.post(ajaxurl, data, function (response) {
815
- var new_row = jQuery(response);
816
-
817
- container.before(new_row);
818
- container.remove();
819
-
820
- new_row.add_delete();
821
- new_row.add_block_edit();
822
- new_row.add_block_clone();
823
-
824
- //Force reload options
825
- if (new_row.hasClass('tnpc-row-block')) {
826
- new_row.find(".tnpc-row-edit-block").click();
827
- }
828
-
829
- tnpc_mobile_preview();
830
-
831
- }).fail(function () {
832
- alert("Block rendering failed.");
833
- });
834
-
835
- }
836
-
837
- }
838
-
839
- return {init};
840
- })();
841
-
842
- TNPInlineEditor.init();
843
-
844
- });
845
-
846
- // =================================================== //
847
- // =============== GLOBAL STYLE ================== //
848
- // =================================================== //
849
-
850
- (function globalStyleIIFE() {
851
-
852
- var _elTrigger = document.querySelector('#tnpc-global-styles-form [name="apply"]');
853
-
854
- _elTrigger.addEventListener('click', function (e) {
855
- e.preventDefault();
856
-
857
- var data = {
858
- 'action': 'tnpc_regenerate_email',
859
- 'content': tnpc_get_email_content_from_builder_area(),
860
- '_wpnonce': tnp_nonce,
861
- };
862
-
863
- tnpc_add_global_options(data);
864
-
865
- jQuery.post(ajaxurl, data, function (response) {
866
- if (response && response.success) {
867
- jQuery('#newsletter-builder-area-center-frame-content').html(response.data.content);
868
- //Change background color of builder area
869
- _setBuilderAreaBackgroundColor(document.getElementById('options-options_composer_background').value);
870
- init_builder_area();
871
- tnpc_mobile_preview();
872
-
873
- toastBottom.success(response.data.message);
874
- } else {
875
- toastBottom.error(response.data.message);
876
- }
877
- });
878
-
879
- });
880
-
881
- })();
882
-
883
- // ========================================================= //
884
- // ================= SEND A TEST ===================== //
885
- // ========================================================= //
886
-
887
- (function sendATestIIFE($) {
888
-
889
- var testNewsletterWithEmailFormId = '#test-newsletter-form';
890
- var testNewsletterWithEmailForm = document.querySelector(testNewsletterWithEmailFormId);
891
- testNewsletterWithEmailForm.addEventListener('submit', function (e) {
892
- e.preventDefault();
893
- var testEmail = testNewsletterWithEmailForm.querySelector('input[name="email"]').value;
894
-
895
- let form = document.getElementById("tnpc-form");
896
- tnpc_save(form);
897
-
898
- form.act.value = "send-test-to-email-address";
899
- var input = document.createElement("input");
900
- input.setAttribute("type", "hidden");
901
- input.setAttribute("name", "test_address_email");
902
- input.setAttribute("value", testEmail);
903
- form.appendChild(input);
904
-
905
- form.submit();
906
- });
907
-
908
- })(jQuery);
909
-
910
- // ================================================================== //
911
- // ================= SUBJECT LENGTH ICONS ===================== //
912
- // ================================================================== //
913
-
914
- (function subjectLengthIconsIIFE($) {
915
- var $subjectContainer = $('#tnpc-subject');
916
- var $subjectInput = $('#tnpc-subject input');
917
- var subjectCharCounterEl = null;
918
-
919
- $subjectInput.on('focusin', function (e) {
920
- $subjectContainer.find('img').fadeTo(400, 1);
921
- });
922
-
923
- $subjectInput.on('keyup', function (e) {
924
- setSubjectCharactersLenght(this.value.length);
925
- });
926
-
927
- $subjectInput.on('focusout', function (e) {
928
- $subjectContainer.find('img').fadeTo(300, 0);
929
- });
930
-
931
- function setSubjectCharactersLenght(length = 0) {
932
-
933
- if (length === 0 && subjectCharCounterEl !== null) {
934
- subjectCharCounterEl.remove();
935
- subjectCharCounterEl = null;
936
- return;
937
- }
938
-
939
- if (!subjectCharCounterEl) {
940
- subjectCharCounterEl = document.createElement("span");
941
- subjectCharCounterEl.style.position = 'absolute';
942
- subjectCharCounterEl.style.top = '-18px';
943
- subjectCharCounterEl.style.right = $subjectContainer[0].getBoundingClientRect().width - $subjectInput[0].getBoundingClientRect().width + 'px';
944
- subjectCharCounterEl.style.color = '#999';
945
- subjectCharCounterEl.style.fontSize = '0.8rem';
946
- $subjectContainer.find('div')[0].appendChild(subjectCharCounterEl);
947
- }
948
-
949
- const word = length === 1 ? 'character' : 'characters';
950
- subjectCharCounterEl.innerHTML = `${length} ${word}`;
951
- }
952
-
953
- })(jQuery);
954
-
955
- // ======================================================================= //
956
- // ================= COMPOSER MODE VIEW SWITCH ===================== //
957
- // ======================================================================= //
958
-
959
- (function composerModeViewIIFE($) {
960
- const activeClass = 'composer-view-mode__item--active';
961
- var status = 'desktop';
962
-
963
- $('.composer-view-mode__item[data-view-mode="' + status + '"]').addClass(activeClass);
964
-
965
- $('.composer-view-mode__item').on('click', function () {
966
- var $el = $(this);
967
-
968
- if ($el.data('viewMode') === 'desktop') {
969
- status = 'desktop';
970
- $('.composer-view-mode__item[data-view-mode="desktop"]').addClass(activeClass);
971
- $('.composer-view-mode__item[data-view-mode="mobile"]').removeClass(activeClass);
972
- } else if ($el.data('viewMode') === 'mobile') {
973
- status = 'mobile';
974
- $('.composer-view-mode__item[data-view-mode="desktop"]').removeClass(activeClass);
975
- $('.composer-view-mode__item[data-view-mode="mobile"]').addClass(activeClass);
976
- }
977
-
978
- tnp_view(status);
979
- });
980
- })(jQuery);
1
+ // add delete buttons
2
+ jQuery.fn.add_delete = function () {
3
+ this.append('<div class="tnpc-row-delete" title="Delete"><img src="' + TNP_PLUGIN_URL + '/emails/tnp-composer/_assets/delete.png" width="32"></div>');
4
+ this.find('.tnpc-row-delete').perform_delete();
5
+ };
6
+
7
+ // delete row
8
+ jQuery.fn.perform_delete = function () {
9
+ this.click(function () {
10
+ tnpc_hide_block_options();
11
+ // remove block
12
+ jQuery(this).parent().remove();
13
+ tnpc_mobile_preview();
14
+ });
15
+ }
16
+
17
+ // add edit button
18
+ jQuery.fn.add_block_edit = function () {
19
+ this.append('<div class="tnpc-row-edit-block" title="Edit"><img src="' + TNP_PLUGIN_URL + '/emails/tnp-composer/_assets/edit.png" width="32"></div>');
20
+ this.find('.tnpc-row-edit-block').perform_block_edit();
21
+ }
22
+
23
+ // edit block
24
+ jQuery.fn.perform_block_edit = function () {
25
+
26
+ jQuery(".tnpc-row-edit-block").click(function (e) {
27
+ e.preventDefault()
28
+ });
29
+
30
+ this.click(function (e) {
31
+
32
+ e.preventDefault();
33
+
34
+ target = jQuery(this).parent().find('.edit-block');
35
+
36
+ // The row container which is a global variable and used later after the options save
37
+ container = jQuery(this).closest("table");
38
+
39
+ if (container.hasClass('tnpc-row-block')) {
40
+
41
+ tnpc_show_block_options();
42
+
43
+ var options = container.find(".tnpc-block-content").attr("data-json");
44
+
45
+ // Compatibility
46
+ if (!options) {
47
+ options = target.attr("data-options");
48
+ }
49
+
50
+ var data = {
51
+ action: "tnpc_options",
52
+ id: container.data("id"),
53
+ context_type: tnp_context_type,
54
+ options: options
55
+ };
56
+
57
+ tnpc_add_global_options(data);
58
+
59
+ builderAreaHelper.lock();
60
+ jQuery("#tnpc-block-options-form").load(ajaxurl, data, function () {
61
+ console.log('Block form options loaded');
62
+ start_options = jQuery("#tnpc-block-options-form").serializeArray();
63
+ tnpc_add_global_options(start_options);
64
+ builderAreaHelper.unlock();
65
+ });
66
+
67
+ } else {
68
+ alert("This is deprecated block version and cannot be edited. Please replace it with a new one.");
69
+ }
70
+
71
+ });
72
+
73
+ };
74
+
75
+ // add clone button
76
+ jQuery.fn.add_block_clone = function () {
77
+ this.append('<div class="tnpc-row-clone" title="Clone"><img src="' + TNP_PLUGIN_URL + '/emails/tnp-composer/_assets/copy.png" width="32"></div>');
78
+ this.find('.tnpc-row-clone').perform_clone();
79
+ }
80
+
81
+ // clone block
82
+ jQuery.fn.perform_clone = function () {
83
+
84
+ jQuery(".tnpc-row-clone").click(function (e) {
85
+ e.preventDefault()
86
+ });
87
+
88
+ this.click(function (e) {
89
+
90
+ e.preventDefault();
91
+
92
+ // hide block edit form
93
+ tnpc_hide_block_options();
94
+
95
+ // find the row
96
+ let row = jQuery(this).closest('.tnpc-row');
97
+
98
+ // clone the block
99
+ let new_row = row.clone();
100
+ new_row.find(".tnpc-row-delete").remove();
101
+ new_row.find(".tnpc-row-edit-block").remove();
102
+ new_row.find(".tnpc-row-clone").remove();
103
+
104
+ new_row.add_delete();
105
+ new_row.add_block_edit();
106
+ new_row.add_block_clone();
107
+ // if (new_row.hasClass('tnpc-row-block')) {
108
+ // new_row.find(".tnpc-row-edit-block i").click();
109
+ // }
110
+ new_row.insertAfter(row);
111
+ tnpc_mobile_preview();
112
+ });
113
+ };
114
+
115
+ let start_options = null;
116
+ let container = null;
117
+
118
+ jQuery(function () {
119
+
120
+ // open blocks tab
121
+ document.getElementById("defaultOpen").click();
122
+
123
+ // preload content from a body named input
124
+ var preloadedContent = jQuery('input[name="message"]').val();
125
+ if (!preloadedContent) {
126
+ preloadedContent = jQuery('input[name="options[message]"]').val();
127
+ }
128
+
129
+ if (!preloadedContent) {
130
+ tnpc_show_presets_modal();
131
+ } else {
132
+ jQuery('#newsletter-builder-area-center-frame-content').html(preloadedContent);
133
+ start_composer();
134
+ }
135
+
136
+ // subject management
137
+ jQuery('#options-subject').val(jQuery('#tnpc-form input[name="options[subject]"]').val());
138
+
139
+ // preheader management
140
+ jQuery('#options-preheader').val(jQuery('#tnpc-form input[name="options[options_preheader]"]').val());
141
+
142
+ // ======================== //
143
+ // == BACKGROUND COLOR == //
144
+ // ======================== //
145
+ _setBuilderAreaBackgroundColor(document.getElementById('options-options_composer_background').value);
146
+
147
+ function _setBuilderAreaBackgroundColor(color) {
148
+ jQuery('#newsletter-builder-area-center-frame-content').css('background-color', color);
149
+ }
150
+
151
+ window._setBuilderAreaBackgroundColor = _setBuilderAreaBackgroundColor; //BAD STUFF!!!
152
+
153
+ // ======================== //
154
+ // == BACKGROUND COLOR == //
155
+ // ======================== //
156
+
157
+ });
158
+
159
+ function BuilderAreaHelper() {
160
+
161
+ var _builderAreaEl = document.querySelector('#newsletter-builder-area');
162
+ var _overlayEl = document.createElement('div');
163
+ _overlayEl.style.zIndex = 99999;
164
+ _overlayEl.style.position = 'absolute';
165
+ _overlayEl.style.top = 0;
166
+ _overlayEl.style.left = 0;
167
+ _overlayEl.style.width = '100%';
168
+ _overlayEl.style.height = '100%';
169
+
170
+ this.lock = function () {
171
+ console.log('Lock builder area');
172
+ _builderAreaEl.appendChild(_overlayEl);
173
+ }
174
+
175
+ this.unlock = function () {
176
+ console.log('Unlock builder area');
177
+ _builderAreaEl.removeChild(_overlayEl);
178
+ }
179
+
180
+ }
181
+
182
+ let builderAreaHelper = new BuilderAreaHelper();
183
+
184
+ function init_builder_area() {
185
+
186
+ //Drag & Drop
187
+ jQuery("#newsletter-builder-area-center-frame-content").sortable({
188
+ revert: false,
189
+ placeholder: "placeholder",
190
+ forcePlaceholderSize: true,
191
+ opacity: 0.6,
192
+ tolerance: "pointer",
193
+ helper: function (e) {
194
+ var helper = jQuery(document.getElementById("sortable-helper")).clone();
195
+ return helper;
196
+ },
197
+ update: function (event, ui) {
198
+ if (ui.item.attr("id") == "draggable-helper") {
199
+ loading_row = jQuery('<div style="text-align: center; padding: 20px; background-color: #d4d5d6; color: #52BE7F;"><i class="fa fa-cog fa-2x fa-spin" /></div>');
200
+ ui.item.before(loading_row);
201
+ ui.item.remove();
202
+ var data = new Array(
203
+ {"name": 'action', "value": 'tnpc_render'},
204
+ {"name": 'id', "value": ui.item.data("id")},
205
+ {"name": 'b', "value": ui.item.data("id")},
206
+ {"name": 'full', "value": 1},
207
+ {"name": '_wpnonce', "value": tnp_nonce}
208
+ );
209
+
210
+ tnpc_add_global_options(data);
211
+
212
+ jQuery.post(ajaxurl, data, function (response) {
213
+
214
+ var new_row = jQuery(response);
215
+ // ui.item.before(new_row);
216
+ // ui.item.remove();
217
+ loading_row.before(new_row);
218
+ loading_row.remove();
219
+ new_row.add_delete();
220
+ new_row.add_block_edit();
221
+ new_row.add_block_clone();
222
+ // new_row.find(".tnpc-row-edit").hover_edit();
223
+ if (new_row.hasClass('tnpc-row-block')) {
224
+ new_row.find(".tnpc-row-edit-block").click();
225
+ }
226
+ tnpc_mobile_preview();
227
+ }).fail(function () {
228
+ alert("Block rendering failed.");
229
+ loading_row.remove();
230
+ });
231
+ } else {
232
+ tnpc_mobile_preview();
233
+ }
234
+ }
235
+ });
236
+
237
+ jQuery(".newsletter-sidebar-buttons-content-tab").draggable({
238
+ connectToSortable: "#newsletter-builder-area-center-frame-content",
239
+
240
+ // Build the helper for dragging
241
+ helper: function (e) {
242
+ var helper = jQuery(document.getElementById("draggable-helper")).clone();
243
+ // Do not uset .data() with jQuery
244
+ helper.attr("data-id", e.currentTarget.dataset.id);
245
+ helper.html(e.currentTarget.dataset.name);
246
+ return helper;
247
+ },
248
+ revert: false,
249
+ start: function () {
250
+ if (jQuery('.tnpc-row').length) {
251
+ } else {
252
+ jQuery('#newsletter-builder-area-center-frame-content').append('<div class="tnpc-drop-here">Drag&Drop blocks here!</div>');
253
+ }
254
+ },
255
+ stop: function (event, ui) {
256
+ jQuery('.tnpc-drop-here').remove();
257
+ }
258
+ });
259
+
260
+ jQuery(".tnpc-row").add_delete();
261
+ jQuery(".tnpc-row").add_block_edit();
262
+ jQuery(".tnpc-row").add_block_clone();
263
+
264
+ }
265
+
266
+ function start_composer() {
267
+
268
+ init_builder_area();
269
+
270
+ // Closes the block options layer (without saving)
271
+ jQuery("#tnpc-block-options-cancel").click(function () {
272
+
273
+ tnpc_hide_block_options();
274
+
275
+ var _target = target;
276
+
277
+ jQuery.post(ajaxurl, start_options, function (response) {
278
+ _target.html(response);
279
+ jQuery("#tnpc-block-options-form").html("");
280
+ });
281
+ });
282
+
283
+ // Fires the save event for block options
284
+ jQuery("#tnpc-block-options-save").click(function (e) {
285
+ e.preventDefault();
286
+
287
+ var _target = target;
288
+
289
+ // fix for Codemirror
290
+ if (typeof templateEditor !== 'undefined') {
291
+ templateEditor.save();
292
+ }
293
+
294
+ if (window.tinymce)
295
+ window.tinymce.triggerSave();
296
+
297
+ var data = jQuery("#tnpc-block-options-form").serializeArray();
298
+
299
+ tnpc_add_global_options(data);
300
+
301
+ tnpc_hide_block_options();
302
+
303
+ jQuery.post(ajaxurl, data, function (response) {
304
+ _target.html(response);
305
+ tnpc_mobile_preview();
306
+ jQuery("#tnpc-block-options-form").html("");
307
+ });
308
+ });
309
+
310
+ // live preview from block options *** EXPERIMENTAL ***
311
+ jQuery('#tnpc-block-options-form').change(function (event) {
312
+ var data = jQuery("#tnpc-block-options-form").serializeArray();
313
+
314
+ var _container = container;
315
+ var _target = target;
316
+
317
+ tnpc_add_global_options(data);
318
+
319
+ jQuery.post(ajaxurl, data, function (response) {
320
+ _target.html(response);
321
+ if (event.target.dataset.afterRendering === 'reload') {
322
+ _container.find(".tnpc-row-edit-block").click();
323
+ }
324
+ }).fail(function () {
325
+ alert("Block rendering failed");
326
+ });
327
+
328
+ });
329
+
330
+ tnpc_mobile_preview();
331
+
332
+ }
333
+
334
+ function tnpc_show_block_options() {
335
+
336
+ const animationDuration = 500;
337
+
338
+ //jQuery("#tnpc-blocks").fadeOut(animationDuration);
339
+ //jQuery("#tnpc-global-styles").fadeOut(animationDuration);
340
+ //jQuery("#tnpc-mobile-tab").fadeOut(animationDuration);
341
+ //jQuery("#tnpc-test-tab").fadeOut(animationDuration);
342
+
343
+ jQuery("#tnpc-block-options").fadeIn(animationDuration);
344
+ jQuery("#tnpc-block-options").css('display', 'flex');
345
+
346
+ }
347
+
348
+ function tnpc_hide_block_options() {
349
+
350
+ const animationDuration = 500;
351
+
352
+ jQuery("#tnpc-block-options").fadeOut(animationDuration);
353
+
354
+ //var $activeTab = jQuery(".tnpc-tabs .tablinks.active");
355
+ //jQuery('#' + $activeTab.data('tabId')).fadeIn(animationDuration);
356
+
357
+ jQuery("#tnpc-block-options-form").html('');
358
+
359
+ }
360
+
361
+ function tnpc_mobile_preview() {
362
+
363
+ return;
364
+
365
+ }
366
+
367
+ function tnpc_save(form) {
368
+
369
+ form.elements["options[message]"].value = tnpc_get_email_content_from_builder_area();
370
+
371
+ // When the composer is not showing the subject field (for example in Automated)
372
+ if (document.getElementById("options-preheader")) {
373
+ form.elements["options[options_preheader]"].value = jQuery('#options-preheader').val();
374
+ } else {
375
+ form.elements["options[options_preheader]"].value = "";
376
+ }
377
+ if (document.getElementById("options-subject")) {
378
+ form.elements["options[subject]"].value = jQuery('#options-subject-subject').val();
379
+ } else {
380
+ form.elements["options[subject]"].value = "";
381
+ }
382
+
383
+ var global_form = document.getElementById("tnpc-global-styles-form");
384
+ //Copy "Global styles" form inputs into main form
385
+ tnpc_copy_form(global_form, form);
386
+
387
+ }
388
+
389
+ function tnpc_get_email_content_from_builder_area() {
390
+
391
+ var $elMessage = jQuery("#newsletter-builder-area-center-frame-content").clone();
392
+
393
+ $elMessage.find('.tnpc-row-delete').remove();
394
+ $elMessage.find('.tnpc-row-edit-block').remove();
395
+ $elMessage.find('.tnpc-row-clone').remove();
396
+ $elMessage.find('.tnpc-row').removeClass('ui-draggable');
397
+ $elMessage.find('#sortable-helper').remove();
398
+
399
+ return $elMessage.html();
400
+
401
+ }
402
+
403
+ function tnpc_copy_form(source, dest) {
404
+ for (var i = 0; i < source.elements.length; i++) {
405
+ var field = document.createElement("input");
406
+ field.type = "hidden";
407
+ field.name = source.elements[i].name;
408
+ field.value = source.elements[i].value;
409
+
410
+ // Non clona le select!
411
+ //var clonedEl = source.elements[i].cloneNode();
412
+ //clonedEl.style.display = 'none';
413
+ dest.appendChild(field);
414
+ }
415
+ }
416
+
417
+ function tnpc_test() {
418
+ let form = document.getElementById("tnpc-form");
419
+ tnpc_save(form);
420
+ form.act.value = "test";
421
+ form.submit();
422
+ }
423
+
424
+ function openTab(evt, tabName) {
425
+ evt.preventDefault();
426
+ // Declare all variables
427
+ var i, tabcontent, tablinks;
428
+
429
+ // Get all elements with class="tabcontent" and hide them
430
+ tabcontent = document.getElementsByClassName("tabcontent");
431
+ for (i = 0; i < tabcontent.length; i++) {
432
+ tabcontent[i].style.display = "none";
433
+ }
434
+
435
+ // Get all elements with class="tablinks" and remove the class "active"
436
+ tablinks = document.getElementsByClassName("tablinks");
437
+ for (i = 0; i < tablinks.length; i++) {
438
+ tablinks[i].className = tablinks[i].className.replace(" active", "");
439
+ }
440
+
441
+ // Show the current tab, and add an "active" class to the button that opened the tab
442
+ document.getElementById(tabName).style.display = "block";
443
+ evt.currentTarget.className += " active";
444
+ }
445
+
446
+ function tnpc_scratch() {
447
+
448
+ jQuery('#newsletter-builder-area-center-frame-content').html(" ");
449
+ init_builder_area();
450
+
451
+ }
452
+
453
+ function tnpc_reload_options(e) {
454
+ e.preventDefault();
455
+ let options = jQuery("#tnpc-block-options-form").serializeArray();
456
+ for (let i = 0; i < options.length; i++) {
457
+ if (options[i].name === 'action') {
458
+ options[i].value = 'tnpc_options';
459
+ }
460
+ }
461
+
462
+ jQuery("#tnpc-block-options-form").load(ajaxurl, options);
463
+ }
464
+
465
+ function tnpc_add_global_options(data) {
466
+ let globalOptions = jQuery("#tnpc-global-styles-form").serializeArray();
467
+ for (let i = 0; i < globalOptions.length; i++) {
468
+ globalOptions[i].name = globalOptions[i].name.replace("[options_", "[").replace("options[", "composer[").replace("composer_", "");
469
+ if (Array.isArray(data)) {
470
+ data.push(globalOptions[i]);
471
+ } else {
472
+ //Inline edit data format is object not array
473
+ data[globalOptions[i].name] = globalOptions[i].value;
474
+ }
475
+ }
476
+ }
477
+
478
+ // ==================================================== //
479
+ // ================= PRESET ===================== //
480
+ // ==================================================== //
481
+
482
+ //TODO non va bene tenere nel global space variabili che altri potrebbero accidentalmente modificare/usare
483
+ // ma questo è un test
484
+ const toastBottom = new TnpToast({duration: 5000, position: 'bottom right', wrapperPadding: '70px 20px'});
485
+
486
+ //TODO - spostare gestione dei preset in contesto privato ma aggiungendo comunque a window le funzioni triggerate da html (load_preset, delete_preset,...) per mantenere compatibilità?
487
+ const presetListModal = new TNPModal({
488
+ closeWhenClickOutside: true,
489
+ showClose: true,
490
+ style: {
491
+ backgroundColor: '#ECF0F1',
492
+ height: '400px',
493
+ width: '740px',
494
+ },
495
+ onClose: function () {
496
+ start_composer();
497
+ //Enable buttons
498
+ jQuery('.tnpc-controls input[type=button]').attr('disabled', false);
499
+ }
500
+ });
501
+
502
+ function tnpc_show_presets_modal() {
503
+
504
+ jQuery('.tnpc-controls input[type=button]').attr('disabled', true);
505
+
506
+ const elModalContent = presetListModal.open();
507
+
508
+ jQuery.ajax({
509
+ type: "POST",
510
+ url: ajaxurl,
511
+ data: {
512
+ action: "tnpc_get_all_presets",
513
+ context_type: tnp_context_type,
514
+ },
515
+ success: function (res) {
516
+ jQuery(elModalContent).html(res.data);
517
+ },
518
+ });
519
+
520
+ }
521
+
522
+ function tnpc_load_preset(id, subject, isEditMode) {
523
+
524
+ presetListModal.close();
525
+
526
+ jQuery.ajax({
527
+ type: "POST",
528
+ url: ajaxurl,
529
+ data: {
530
+ action: "tnpc_get_preset",
531
+ id: id
532
+ },
533
+ success: function (res) {
534
+ jQuery('#newsletter-builder-area-center-frame-content').html(res.data.content);
535
+ _restore_global_options(res.data.globalOptions);
536
+
537
+ start_composer();
538
+
539
+ if (!isEditMode) {
540
+ //Enable buttons
541
+ jQuery('.tnpc-controls input[type=button]').attr('disabled', false);
542
+ }
543
+
544
+ if (subject && subject.length > 0) {
545
+ jQuery('#options-subject').val(tnpc_remove_double_quotes_escape_from(subject));
546
+ }
547
+ },
548
+ });
549
+
550
+ function _restore_global_options(options) {
551
+ jQuery.each(options, function (name, value) {
552
+ var el = jQuery(`#tnpc-global-styles-form #options-options_composer_${name}`);
553
+ if (el.length) {
554
+ el.val(value);
555
+ }
556
+ });
557
+
558
+ tnp_controls_init();
559
+ _setBuilderAreaBackgroundColor(document.getElementById('options-options_composer_background').value);
560
+ }
561
+
562
+ }
563
+
564
+ function tnpc_save_preset(form) {
565
+
566
+ const presetName = tnpc_remove_double_quotes_from(document.querySelector('#options-subject').value);
567
+
568
+ const presetNameModal = new TNPModal({
569
+ title: 'Choose a preset name',
570
+ content: '<input type="text" id="preset_name" style="width: 100%" placeholder="Preset name" value="' + presetName + '"/>',
571
+ showConfirm: true,
572
+ clickConfirmOnPressEnter: true,
573
+ onConfirm: function () {
574
+ const inputEl = document.querySelector('#preset_name');
575
+ document.querySelector('#options-subject').value = inputEl.value;
576
+ tnpc_save(form);
577
+ form.submit();
578
+ }
579
+ });
580
+
581
+ presetNameModal.open();
582
+
583
+ }
584
+
585
+ function tnpc_delete_preset(presetId, name, event) {
586
+ event.stopPropagation();
587
+
588
+ const presetDeleteModal = new TNPModal({
589
+ title: `Are you sure to delete "${name}" preset?`,
590
+ confirmText: 'DELETE PRESET',
591
+ confirmClassName: 'button-secondary button-danger',
592
+ showConfirm: true,
593
+ onConfirm: function () {
594
+
595
+ const wrapperPresetEl = event.target.closest(".tnpc-preset");
596
+
597
+ jQuery.ajax({
598
+ type: 'POST',
599
+ dataType: 'json',
600
+ url: ajaxurl,
601
+ data: {
602
+ action: 'tnpc_delete_preset',
603
+ _wpnonce: tnp_preset_nonce,
604
+ presetId: presetId
605
+ },
606
+ success: function (response) {
607
+ if (response.success) {
608
+ wrapperPresetEl.parentNode.removeChild(wrapperPresetEl);
609
+ toastBottom.success('Preset successfully deleted!');
610
+ }
611
+ }
612
+ });
613
+
614
+ }
615
+ });
616
+
617
+ presetDeleteModal.open();
618
+
619
+ }
620
+
621
+ function tnpc_edit_preset(presetId, name, event) {
622
+ event.stopPropagation();
623
+ tnpc_load_preset(presetId, name, true);
624
+
625
+ //DISABLE BUTTON AND SHOW UPDATE BUTTON
626
+ const composerForm = document.querySelector('#tnpc-form');
627
+ const buttons = composerForm.querySelectorAll('input[type=button]');
628
+ const updatePresetButton = composerForm.querySelector('#update-preset-button');
629
+
630
+ for (btn of buttons) {
631
+ if (btn.id && btn.id === 'save-preset-button') {
632
+ btn.style.display = 'none';
633
+ updatePresetButton.style.display = 'inline';
634
+ updatePresetButton.disabled = false;
635
+ } else {
636
+ btn.disabled = true;
637
+ }
638
+ }
639
+
640
+ //Add preset id hidden field
641
+ const presetIdfield = document.createElement("input");
642
+ presetIdfield.type = "hidden";
643
+ presetIdfield.name = "preset_id";
644
+ presetIdfield.value = presetId;
645
+ composerForm.appendChild(presetIdfield);
646
+
647
+ }
648
+
649
+ function tnpc_remove_double_quotes_escape_from(str) {
650
+ return str.replace(/\\"/g, '"');
651
+ }
652
+
653
+ function tnpc_remove_double_quotes_from(str) {
654
+ return str.replace(/['"]+/g, '');
655
+ }
656
+
657
+ function tnpc_update_preset(form) {
658
+
659
+ const presetName = tnpc_remove_double_quotes_from(document.querySelector('#options-subject').value);
660
+
661
+ const presetNameModal = new TNPModal({
662
+ title: 'Choose a preset name',
663
+ content: '<input type="text" id="preset_name" style="width: 100%" placeholder="Preset name" value="' + presetName + '"/>',
664
+ showConfirm: true,
665
+ clickConfirmOnPressEnter: true,
666
+ onConfirm: function () {
667
+ const inputEl = document.querySelector('#preset_name');
668
+ document.querySelector('#options-subject').value = inputEl.value;
669
+ tnpc_save(form);
670
+ form.submit();
671
+ }
672
+ });
673
+
674
+ presetNameModal.open();
675
+
676
+ }
677
+
678
+ // ========================================================= //
679
+ // ================= PRESET FINE ===================== //
680
+ // ========================================================= //
681
+
682
+ jQuery(document).ready(function () {
683
+ 'use strict'
684
+
685
+ var TNPInlineEditor = (function () {
686
+
687
+ var className = 'tnpc-inline-editable';
688
+ var newInputName = 'new_name';
689
+ var activeInlineElements = [];
690
+
691
+ function init() {
692
+ // find all inline editable elements
693
+ jQuery('#newsletter-builder-area-center-frame-content').on('click', '.' + className, function (e) {
694
+ e.preventDefault();
695
+ removeAllActiveElements();
696
+
697
+ var originalEl = jQuery(this).hide();
698
+ var newEl = jQuery(getEditableComponent(this.innerText.trim(), this.dataset.id, this.dataset.type, originalEl)).insertAfter(this);
699
+
700
+ activeInlineElements.push({'originalEl': originalEl, 'newEl': newEl});
701
+
702
+ //Add submit event listener for newly created block
703
+ jQuery('.tnpc-inline-editable-form-' + this.dataset.type + this.dataset.id).on('submit', function (e) {
704
+ submit(e, newEl, jQuery(originalEl));
705
+ });
706
+
707
+ //Add close event listener for newly created block
708
+ jQuery('.tnpc-inline-editable-form-actions .tnpc-dismiss-' + this.dataset.type + this.dataset.id).on('click', function (e) {
709
+ removeAllActiveElements();
710
+ });
711
+
712
+ });
713
+
714
+ // Close all created elements if clicked outside
715
+ jQuery('#newsletter-builder-area-center-frame-content').on('click', function (e) {
716
+ if (activeInlineElements.length > 0
717
+ && !jQuery(e.target).hasClass(className)
718
+ && jQuery(e.target).closest('.tnpc-inline-editable-container').length === 0) {
719
+ removeAllActiveElements();
720
+ }
721
+ });
722
+
723
+ }
724
+
725
+ function removeAllActiveElements() {
726
+ activeInlineElements.forEach(function (obj) {
727
+ obj.originalEl.show();
728
+
729
+ obj.newEl.off();
730
+ obj.newEl.remove();
731
+ });
732
+
733
+ activeInlineElements = []
734
+ }
735
+
736
+ function getEditableComponent(value, id, type, originalEl) {
737
+
738
+ var element = '';
739
+
740
+ //COPY FONT STYLE FROM ORIGINAL ELEMENT
741
+ var fontFamily = originalEl.css('font-family');
742
+ var fontSize = originalEl.css('font-size');
743
+ var styleAttr = "style='font-family:" + fontFamily + ";font-size:" + fontSize + ";'";
744
+
745
+ switch (type) {
746
+ case 'text':
747
+ {
748
+ element = "<textarea name='" + newInputName + "' class='" + className + "-textarea' rows='5' " + styleAttr + ">" + value + "</textarea>";
749
+ break;
750
+ }
751
+ case 'title':
752
+ {
753
+ element = "<textarea name='" + newInputName + "' class='" + className + "-textarea' rows='2'" + styleAttr + ">" + value + "</textarea>";
754
+ break;
755
+ }
756
+ }
757
+
758
+ var component = "<td>";
759
+ component += "<form class='tnpc-inline-editable-form tnpc-inline-editable-form-" + type + id + "'>";
760
+ component += "<input type='hidden' name='id' value='" + id + "'>";
761
+ component += "<input type='hidden' name='type' value='" + type + "'>";
762
+ component += "<input type='hidden' name='old_value' value='" + value + "'>";
763
+ component += "<div class='tnpc-inline-editable-container'>";
764
+ component += element;
765
+ component += "<div class='tnpc-inline-editable-form-actions'>";
766
+ component += "<button type='submit'><span class='dashicons dashicons-yes-alt' title='save'></span></button>";
767
+ component += "<span class='dashicons dashicons-dismiss tnpc-dismiss-" + type + id + "' title='close'></span>";
768
+ component += "</div>";
769
+ component += "</div>";
770
+ component += "</form>";
771
+ component += "</td>";
772
+ return component;
773
+ }
774
+
775
+ function submit(e, elementToDeleteAfterSubmit, elementToShow) {
776
+ e.preventDefault();
777
+
778
+ var id = elementToDeleteAfterSubmit.find('form input[name=id]').val();
779
+ var type = elementToDeleteAfterSubmit.find('form input[name=type]').val();
780
+ var newValue = elementToDeleteAfterSubmit.find('form [name="' + newInputName + '"]').val();
781
+
782
+ ajax_render_block(elementToShow, type, id, newValue);
783
+
784
+ elementToDeleteAfterSubmit.remove();
785
+ elementToShow.show();
786
+
787
+ }
788
+
789
+ function ajax_render_block(inlineElement, type, postId, newContent) {
790
+
791
+ var target = inlineElement.closest('.edit-block');
792
+ var container = target.closest('table');
793
+ var blockContent = target.children('.tnpc-block-content');
794
+
795
+ if (container.hasClass('tnpc-row-block')) {
796
+ var data = {
797
+ 'action': 'tnpc_render',
798
+ 'id': container.data('id'),
799
+ 'b': container.data('id'),
800
+ 'full': 1,
801
+ '_wpnonce': tnp_nonce,
802
+ 'options': {
803
+ 'inline_edits': [{
804
+ 'type': type,
805
+ 'post_id': postId,
806
+ 'content': newContent
807
+ }]
808
+ },
809
+ 'encoded_options': blockContent.data('json')
810
+ };
811
+
812
+ tnpc_add_global_options(data);
813
+
814
+ jQuery.post(ajaxurl, data, function (response) {
815
+ var new_row = jQuery(response);
816
+
817
+ container.before(new_row);
818
+ container.remove();
819
+
820
+ new_row.add_delete();
821
+ new_row.add_block_edit();
822
+ new_row.add_block_clone();
823
+
824
+ //Force reload options
825
+ if (new_row.hasClass('tnpc-row-block')) {
826
+ new_row.find(".tnpc-row-edit-block").click();
827
+ }
828
+
829
+ tnpc_mobile_preview();
830
+
831
+ }).fail(function () {
832
+ alert("Block rendering failed.");
833
+ });
834
+
835
+ }
836
+
837
+ }
838
+
839
+ return {init};
840
+ })();
841
+
842
+ TNPInlineEditor.init();
843
+
844
+ });
845
+
846
+ // =================================================== //
847
+ // =============== GLOBAL STYLE ================== //
848
+ // =================================================== //
849
+
850
+ (function globalStyleIIFE() {
851
+
852
+ var _elTrigger = document.querySelector('#tnpc-global-styles-form [name="apply"]');
853
+
854
+ _elTrigger.addEventListener('click', function (e) {
855
+ e.preventDefault();
856
+
857
+ var data = {
858
+ 'action': 'tnpc_regenerate_email',
859
+ 'content': tnpc_get_email_content_from_builder_area(),
860
+ '_wpnonce': tnp_nonce,
861
+ };
862
+
863
+ tnpc_add_global_options(data);
864
+
865
+ jQuery.post(ajaxurl, data, function (response) {
866
+ if (response && response.success) {
867
+ jQuery('#newsletter-builder-area-center-frame-content').html(response.data.content);
868
+ //Change background color of builder area
869
+ _setBuilderAreaBackgroundColor(document.getElementById('options-options_composer_background').value);
870
+ init_builder_area();
871
+ tnpc_mobile_preview();
872
+
873
+ toastBottom.success(response.data.message);
874
+ } else {
875
+ toastBottom.error(response.data.message);
876
+ }
877
+ });
878
+
879
+ });
880
+
881
+ })();
882
+
883
+ // ========================================================= //
884
+ // ================= SEND A TEST ===================== //
885
+ // ========================================================= //
886
+
887
+ (function sendATestIIFE($) {
888
+
889
+ var testNewsletterWithEmailFormId = '#test-newsletter-form';
890
+ var testNewsletterWithEmailForm = document.querySelector(testNewsletterWithEmailFormId);
891
+ testNewsletterWithEmailForm.addEventListener('submit', function (e) {
892
+ e.preventDefault();
893
+ var testEmail = testNewsletterWithEmailForm.querySelector('input[name="email"]').value;
894
+
895
+ let form = document.getElementById("tnpc-form");
896
+ tnpc_save(form);
897
+
898
+ form.act.value = "send-test-to-email-address";
899
+ var input = document.createElement("input");
900
+ input.setAttribute("type", "hidden");
901
+ input.setAttribute("name", "test_address_email");
902
+ input.setAttribute("value", testEmail);
903
+ form.appendChild(input);
904
+
905
+ form.submit();
906
+ });
907
+
908
+ })(jQuery);
909
+
910
+ // ================================================================== //
911
+ // ================= SUBJECT LENGTH ICONS ===================== //
912
+ // ================================================================== //
913
+
914
+ (function subjectLengthIconsIIFE($) {
915
+ var $subjectContainer = $('#tnpc-subject');
916
+ var $subjectInput = $('#tnpc-subject input');
917
+ var subjectCharCounterEl = null;
918
+
919
+ $subjectInput.on('focusin', function (e) {
920
+ $subjectContainer.find('img').fadeTo(400, 1);
921
+ });
922
+
923
+ $subjectInput.on('keyup', function (e) {
924
+ setSubjectCharactersLenght(this.value.length);
925
+ });
926
+
927
+ $subjectInput.on('focusout', function (e) {
928
+ $subjectContainer.find('img').fadeTo(300, 0);
929
+ });
930
+
931
+ function setSubjectCharactersLenght(length = 0) {
932
+
933
+ if (length === 0 && subjectCharCounterEl !== null) {
934
+ subjectCharCounterEl.remove();
935
+ subjectCharCounterEl = null;
936
+ return;
937
+ }
938
+
939
+ if (!subjectCharCounterEl) {
940
+ subjectCharCounterEl = document.createElement("span");
941
+ subjectCharCounterEl.style.position = 'absolute';
942
+ subjectCharCounterEl.style.top = '-18px';
943
+ subjectCharCounterEl.style.right = $subjectContainer[0].getBoundingClientRect().width - $subjectInput[0].getBoundingClientRect().width + 'px';
944
+ subjectCharCounterEl.style.color = '#999';
945
+ subjectCharCounterEl.style.fontSize = '0.8rem';
946
+ $subjectContainer.find('div')[0].appendChild(subjectCharCounterEl);
947
+ }
948
+
949
+ const word = length === 1 ? 'character' : 'characters';
950
+ subjectCharCounterEl.innerHTML = `${length} ${word}`;
951
+ }
952
+
953
+ })(jQuery);
954
+
955
+ // ======================================================================= //
956
+ // ================= COMPOSER MODE VIEW SWITCH ===================== //
957
+ // ======================================================================= //
958
+
959
+ (function composerModeViewIIFE($) {
960
+ const activeClass = 'composer-view-mode__item--active';
961
+ var status = 'desktop';
962
+
963
+ $('.composer-view-mode__item[data-view-mode="' + status + '"]').addClass(activeClass);
964
+
965
+ $('.composer-view-mode__item').on('click', function () {
966
+ var $el = $(this);
967
+
968
+ if ($el.data('viewMode') === 'desktop') {
969
+ status = 'desktop';
970
+ $('.composer-view-mode__item[data-view-mode="desktop"]').addClass(activeClass);
971
+ $('.composer-view-mode__item[data-view-mode="mobile"]').removeClass(activeClass);
972
+ } else if ($el.data('viewMode') === 'mobile') {
973
+ status = 'mobile';
974
+ $('.composer-view-mode__item[data-view-mode="desktop"]').removeClass(activeClass);
975
+ $('.composer-view-mode__item[data-view-mode="mobile"]').addClass(activeClass);
976
+ }
977
+
978
+ tnp_view(status);
979
+ });
980
+ })(jQuery);
emails/tnp-composer/blocks/_/content-01-hero.block.php DELETED
@@ -1,48 +0,0 @@
1
-
2
-
3
- <table border="0" cellpadding="0" cellspacing="0" width="500" class="responsive-table" align="center">
4
- <tr>
5
- <td>
6
- <!-- HERO IMAGE -->
7
- <table width="100%" border="0" cellspacing="0" cellpadding="0">
8
- <tr>
9
- <td class="padding-copy tnpc-row-edit" data-type="image">
10
- <a href="#" target="_blank">
11
- <img src="https://unsplash.it/500/300?image=885" width="500" border="0" alt="Insert alt text here" style="max-width: 100%!important; width: 500px!important; height: auto!important; display: block;" class="img-max">
12
- </a>
13
- </td>
14
- </tr>
15
- <tr>
16
- <td>
17
- <!-- COPY -->
18
- <table width="100%" border="0" cellspacing="0" cellpadding="0">
19
- <tr>
20
- <td align="center" style="font-size: 25px; color: #333333; padding-top: 30px; font-family: Helvetica, arial, sans-serif; " class="padding-copy tnpc-row-edit" data-type="title">An Awesome Title</td>
21
- </tr>
22
- <tr>
23
- <td align="center" style="padding: 20px 0 0 0; font-size: 16px; line-height: 25px; color: #666666; font-family: Helvetica, arial, sans-serif; " class="padding-copy tnpc-row-edit" data-type="text">The judge, by the way, was the King; and as he wore his crown over the wig, (look at the frontispiece if you want to see how he did it,) he did not look at all comfortable, and it was certainly not becoming.</td>
24
- </tr>
25
- </table>
26
- </td>
27
- </tr>
28
- <tr>
29
- <td align="center">
30
- <!-- BULLETPROOF BUTTON -->
31
- <table width="100%" border="0" cellspacing="0" cellpadding="0" class="mobile-button-container">
32
- <tr>
33
- <td align="center" style="padding: 25px 0 0 0;" class="padding-copy">
34
- <table border="0" cellspacing="0" cellpadding="0" class="responsive-table">
35
- <tr>
36
- <td align="center">
37
- <a href="#" target="_blank" style="font-size: 16px; font-family: Helvetica, Arial, sans-serif; font-weight: normal; color: #ffffff; text-decoration: none; background-color: #256F9C; border-top: 15px solid #256F9C; border-bottom: 15px solid #256F9C; border-left: 25px solid #256F9C; border-right: 25px solid #256F9C; border-radius: 3px; -webkit-border-radius: 3px; -moz-border-radius: 3px; display: inline-block;" class="mobile-button tnpc-row-edit" data-type="button">Learn More &rarr;</a></td>
38
- </tr>
39
- </table>
40
- </td>
41
- </tr>
42
- </table>
43
- </td>
44
- </tr>
45
- </table>
46
- </td>
47
- </tr>
48
- </table>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
emails/tnp-composer/blocks/_/content-01-hero.block.png DELETED
Binary file
emails/tnp-composer/css/backend.css CHANGED
@@ -1,40 +1,43 @@
1
- /* Contains some rules to simulate device layout on backend preview */
2
-
3
- #newsletter-builder-area-center-frame-content.tnp-view-mobile table[class="responsive"] {
4
-
5
- width:100%!important;
6
- float: none;
7
- display: table;
8
- padding-left: 0;
9
- padding-right: 0;
10
- }
11
-
12
- #newsletter-builder-area-center-frame-content.tnp-view-mobile td[class="responsive"]{
13
- width:100%!important;
14
- max-width: 100%!important;
15
- display: block;
16
- padding-left: 0 !important;
17
- padding-right: 0 !important;
18
- float: none;
19
- }
20
-
21
- #newsletter-builder-area-center-frame-content.tnp-view-mobile img[class="responsive"]{
22
- max-width: 100%!important;
23
- }
24
-
25
- #newsletter-builder-area-center-frame-content.tnp-view-mobile img[class="fluid"]{
26
- width: 100%;
27
- max-width: 100%!important;
28
- }
29
-
30
- #newsletter-builder-area-center-frame-content.tnp-view-mobile .pt-1 {
31
- padding-top: 15px!important;
32
- }
33
-
34
- #newsletter-builder-area-center-frame-content.tnp-view-mobile .pb-1 {
35
- padding-bottom: 15px!important;
36
- }
37
-
38
- #newsletter-builder-area-center-frame-content.tnp-view-mobile .tnp-grid-column {
39
- max-width: 100%!important;
40
- }
 
 
 
1
+ /* Contains some rules to simulate device layout on backend preview */
2
+
3
+ #newsletter-builder-area-center-frame-content.tnp-view-mobile table[class="responsive"],
4
+ #newsletter-builder-area-center-frame-content.tnp-view-mobile table.responsive
5
+ {
6
+ width:100%!important;
7
+ float: none;
8
+ display: table;
9
+ padding-left: 0;
10
+ padding-right: 0;
11
+ }
12
+
13
+ #newsletter-builder-area-center-frame-content.tnp-view-mobile td[class="responsive"]{
14
+ width:100%!important;
15
+ max-width: 100%!important;
16
+ display: block;
17
+ padding-left: 0 !important;
18
+ padding-right: 0 !important;
19
+ float: none;
20
+ }
21
+
22
+ #newsletter-builder-area-center-frame-content.tnp-view-mobile img[class="responsive"],
23
+ #newsletter-builder-area-center-frame-content.tnp-view-mobile img.responsive
24
+ {
25
+ max-width: 100%!important;
26
+ }
27
+
28
+ #newsletter-builder-area-center-frame-content.tnp-view-mobile img[class="fluid"]{
29
+ width: 100%;
30
+ max-width: 100%!important;
31
+ }
32
+
33
+ #newsletter-builder-area-center-frame-content.tnp-view-mobile .pt-1 {
34
+ padding-top: 15px!important;
35
+ }
36
+
37
+ #newsletter-builder-area-center-frame-content.tnp-view-mobile .pb-1 {
38
+ padding-bottom: 15px!important;
39
+ }
40
+
41
+ #newsletter-builder-area-center-frame-content.tnp-view-mobile .tnp-grid-column {
42
+ max-width: 100%!important;
43
+ }
emails/tnp-composer/css/newsletter.css CHANGED
@@ -1,91 +1,38 @@
1
- #outlook a{padding:0;} /* Force Outlook to provide a "view in browser" message */
2
- .ReadMsgBody{width:100%;} .ExternalClass{width:100%;} /* Force Hotmail to display emails at full width */
3
- .ExternalClass, .ExternalClass p, .ExternalClass span, .ExternalClass font, .ExternalClass td, .ExternalClass div {line-height: 100%;} /* Force Hotmail to display normal line spacing */
4
- body, table, td, a{-webkit-text-size-adjust:100%; -ms-text-size-adjust:100%;} /* Prevent WebKit and Windows mobile changing default text sizes */
5
- table, td{mso-table-lspace:0pt; mso-table-rspace:0pt;} /* Remove spacing between tables in Outlook 2007 and up */
6
- img{-ms-interpolation-mode:bicubic;} /* Allow smoother rendering of resized image in Internet Explorer */
7
-
8
- body{margin:0; padding:0; height:100% !important; margin:0; padding:0; width:100% !important;}
9
- img{border:0; height:auto; line-height:100%; outline:none; text-decoration:none; max-width: 100%!important}
10
- table{border-collapse:collapse !important;}
11
- img.aligncenter{display:block;margin:0 auto;}
12
-
13
- @media screen and (max-width: 525px) {
14
-
15
- .pt-1 {
16
- padding-top: 15px!important;
17
- }
18
-
19
- .pb-1 {
20
- padding-bottom: 15px!important;
21
- }
22
-
23
- /* ALLOWS FOR FLUID TABLES */
24
- table[class="wrapper"]{
25
- width:100% !important;
26
- }
27
-
28
- table[class="responsive"]{
29
- width:100%!important;
30
- float: none;
31
- display: table;
32
- padding-left: 0;
33
- padding-right: 0;
34
- }
35
-
36
- img[class="responsive"] {
37
- max-width: 100%!important;
38
- }
39
-
40
- img[class="fluid"] {
41
- max-width: 100%!important;
42
- width: 100%;
43
- }
44
-
45
- .block {
46
- display: block;
47
- }
48
-
49
- td[class="responsive"]{
50
- width:100%!important;
51
- max-width: 100%!important;
52
- display: block;
53
- padding-left: 0 !important;
54
- padding-right: 0 !important;
55
- float: none;
56
- }
57
-
58
- td[class="section-padding"]{
59
- padding: 50px 15px 50px 15px !important;
60
- }
61
-
62
- td[class="section-padding-bottom-image"]{
63
- padding: 50px 15px 0 15px !important;
64
- }
65
-
66
- /* ADJUST BUTTONS ON MOBILE */
67
- td[class="mobile-wrapper"]{
68
- padding: 10px 5% 15px 5% !important;
69
- }
70
-
71
- td[class="responsive"]{
72
- display: block;
73
- width: 100% !important;
74
- }
75
-
76
- table[class="mobile-button-container"]{
77
- margin:0 auto;
78
- width:100% !important;
79
- }
80
-
81
- a[class="mobile-button"]{
82
- width:80% !important;
83
- padding: 15px !important;
84
- border: 0 !important;
85
- font-size: 16px !important;
86
- }
87
-
88
- .tnp-grid-column {
89
- max-width: 100%!important;
90
- }
91
- }
1
+ #outlook a{padding:0;}
2
+ .ReadMsgBody{width:100%;} .ExternalClass{width:100%;}
3
+ .ExternalClass, .ExternalClass p, .ExternalClass span, .ExternalClass font, .ExternalClass td, .ExternalClass div {line-height: 100%;}
4
+
5
+ body { margin: 0; padding: 0; height: 100%!important; width: 100%!important; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%;}
6
+ table,td { border-collapse: collapse !important; mso-table-lspace: 0pt; mso-table-rspace: 0pt;}
7
+ img { border: 0; height: auto; line-height: 100%; outline: none; text-decoration: none; max-width: 100%!important; -ms-interpolation-mode: bicubic;}
8
+ img.aligncenter { display: block; margin: 0 auto;}
9
+
10
+ @media screen and (max-width: 525px) {
11
+ .pt-1 { padding-top: 15px!important; }
12
+ .pb-1 { padding-bottom: 15px!important; }
13
+ .responsive { width:100%!important; }
14
+ table.responsive { width:100%!important; float: none; display: table; padding-left: 0; padding-right: 0; }
15
+ table[class="responsive"] { width:100%!important; float: none; display: table; padding-left: 0; padding-right: 0; }
16
+ img[class="responsive"] { max-width: 100%!important; }
17
+ .fluid { max-width: 100%!important; width: auto; }
18
+ img[class="fluid"] { max-width: 100%!important; width: auto; }
19
+
20
+ .block { display: block; }
21
+
22
+ td[class="responsive"]{
23
+ width:100%!important;
24
+ max-width: 100%!important;
25
+ display: block;
26
+ padding-left: 0 !important;
27
+ padding-right: 0 !important;
28
+ float: none;
29
+ }
30
+
31
+ td[class="section-padding-bottom-image"]{
32
+ padding: 50px 15px 0 15px !important;
33
+ }
34
+
35
+ .tnp-grid-column {
36
+ max-width: 100%!important;
37
+ }
38
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
emails/tnp-composer/css/newsletter.min.css DELETED
@@ -1 +0,0 @@
1
- #outlook a{padding:0}.ReadMsgBody{width:100%}.ExternalClass{width:100%}.ExternalClass,.ExternalClass p,.ExternalClass span,.ExternalClass font,.ExternalClass td,.ExternalClass div{line-height:100%}body,table,td,a{-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}table,td{mso-table-lspace:0;mso-table-rspace:0}img{-ms-interpolation-mode:bicubic}body{margin:0;padding:0;height:100%!important;margin:0;padding:0;width:100%!important}img{border:0;height:auto;line-height:100%;outline:0;text-decoration:none;max-width:100%!important}table{border-collapse:collapse!important}img.aligncenter{display:block;margin:0 auto}@media screen and (max-width:525px){table[class="wrapper"]{width:100%!important}table[class="mobile-full-width"]{width:100%!important}img[class="mobile-full-width"]{width:100%!important;display:block}td[class="logo"]{text-align:left;padding:20px 0 20px 0!important}td[class="logo"] img{margin:0 auto!important}td[class="mobile-hide"]{display:none}img[class="mobile-hide"]{display:none!important}img[class="img-max"]{max-width:100%!important;height:auto!important}table[class="responsive-table"]{width:100%!important;max-width:100%!important}.responsive{width:100%!important;max-width:100%!important;float:none;display:block;padding-left:0;padding-right:0}table[class="responsive"]{width:100%!important;max-width:100%!important;float:none;display:block}img.responsive{width:100%!important;max-width:100%!important}.block{display:block}td[class="responsive"]{width:100%!important;max-width:100%!important;display:block;padding-left:0;padding-right:0;float:none}td[class="padding"]{padding:10px 5% 15px 5%!important}td[class="padding-copy"]{padding:10px 5% 10px 5%!important;text-align:center}td[class="padding-meta"]{padding:30px 5% 0 5%!important;text-align:center}td[class="no-pad"]{padding:0 0 20px 0!important}td[class="no-padding"]{padding:0!important}td[class="section-padding"]{padding:50px 15px 50px 15px!important}td[class="section-padding-bottom-image"]{padding:50px 15px 0 15px!important}td[class="mobile-wrapper"]{padding:10px 5% 15px 5%!important}td[class="responsive"]{display:block;width:100%!important}table[class="mobile-button-container"]{margin:0 auto;width:100%!important}a[class="mobile-button"]{width:80%!important;padding:15px!important;border:0!important;font-size:16px!important}}
 
emails/tnp-composer/index-v2.php CHANGED
@@ -1,198 +1,198 @@
1
- <?php
2
- /**
3
- * This file is included by NewsletterControls to create the composer.
4
- */
5
- /* @var $this NewsletterControls */
6
-
7
- defined('ABSPATH') || exit;
8
-
9
- $list = NewsletterEmails::instance()->get_blocks();
10
-
11
- $blocks = array();
12
- foreach ($list as $key => $data) {
13
- if (!isset($blocks[$data['section']])) {
14
- $blocks[$data['section']] = array();
15
- }
16
- $blocks[$data['section']][$key]['name'] = $data['name'];
17
- $blocks[$data['section']][$key]['filename'] = $key;
18
- $blocks[$data['section']][$key]['icon'] = $data['icon'];
19
- }
20
-
21
- // order the sections
22
- $blocks = array_merge(array_flip(array('header', 'content', 'footer')), $blocks);
23
-
24
- // prepare the options for the default blocks
25
- $block_options = get_option('newsletter_main');
26
-
27
- $fields = new NewsletterFields($controls);
28
-
29
- $dir = is_rtl() ? 'rtl' : 'ltr';
30
- $rev_dir = is_rtl() ? 'ltr' : 'rlt';
31
-
32
- ?>
33
- <script type="text/javascript">
34
- // collapse wp menu
35
- document.body.classList.add('folded');
36
-
37
- function tnp_view(type) {
38
- if (type === 'mobile') {
39
- jQuery('#newsletter-builder-area-center-frame-content').addClass('tnp-view-mobile');
40
- } else {
41
- jQuery('#newsletter-builder-area-center-frame-content').removeClass('tnp-view-mobile');
42
- }
43
- }
44
- </script>
45
-
46
-
47
- <style>
48
- <?php echo NewsletterEmails::instance()->get_composer_css(); ?>
49
- </style>
50
- <div id="newsletter-builder" dir="ltr">
51
-
52
- <div id="newsletter-builder-area" class="tnp-builder-column">
53
-
54
- <?php if ( $tnpc_show_subject ) { ?>
55
- <div id="tnpc-subject-wrap" dir="<?php echo $dir ?>">
56
- <table role="presentation" style="width: 100%">
57
- <tr>
58
- <th dir="<?php echo $dir ?>"><?php _e('From', 'newsletter') ?></th>
59
- <td dir="<?php echo $dir ?>"><?php echo esc_html( $controls->data['sender_email'] ) ?></td>
60
- </tr>
61
- <tr>
62
- <th dir="<?php echo $dir ?>"><?php _e('Subject', 'newsletter') ?></th>
63
- <td dir="<?php echo $dir ?>">
64
- <div id="tnpc-subject">
65
- <?php $this->subject( 'subject' ); ?>
66
- </div>
67
- </td>
68
- </tr>
69
- <tr>
70
- <th dir="<?php echo $dir ?>"><span title="<?php esc_attr_e('Shown by some email clients as excerpt', 'newsletter')?>"><?php _e('Snippet', 'newsletter') ?></span>
71
- <?php $this->field_help('https://www.thenewsletterplugin.com/documentation/newsletters/composer/#subject') ?>
72
- </th>
73
- <td dir="<?php echo $dir ?>"><?php $this->text('preheader') ?></td>
74
- </tr>
75
- </table>
76
-
77
- <div style="text-align: left; margin-left: 1em;">
78
- <a href="https://www.thenewsletterplugin.com/documentation/newsletters/newsletter-tags/"
79
- target="_blank">You can use tags to inject subscriber fields</a>. Even on subject.
80
-
81
-
82
- </div>
83
-
84
- <div class="composer-actions">
85
-
86
- <div id="attachment-newsletter-button" class="button-primary" data-tnp-modal-target="#attachment-modal">
87
- <i class="fas fa-paperclip"></i>
88
- </div>
89
-
90
- <div id="test-newsletter-button" class="button-primary" data-tnp-modal-target="#test-newsletter-modal">
91
- <i class="fas fa-paper-plane"></i> <?php _e( 'Test', 'newsletter' ) ?>
92
- </div>
93
-
94
- <div class="composer-view-mode">
95
- <span class="composer-view-mode__item" data-view-mode="desktop">
96
- <i class="fas fa-desktop"></i>
97
- </span>
98
-
99
- <span class="composer-view-mode__item" data-view-mode="mobile">
100
- <i class="fas fa-mobile"></i>
101
- <span>
102
- </div>
103
-
104
- </div>
105
-
106
- <?php include NEWSLETTER_DIR . '/emails/tnp-composer/modal/test-newsletter.php' ?>
107
- <?php include NEWSLETTER_DIR . '/emails/tnp-composer/modal/attachment.php' ?>
108
-
109
- </div>
110
- <?php } ?>
111
-
112
-
113
- <div id="newsletter-builder-area-center-frame-content" dir="<?php echo $dir ?>">
114
-
115
- <!-- Composer content -->
116
-
117
- </div>
118
- </div>
119
-
120
- <div id="newsletter-builder-sidebar" dir="<?php echo is_rtl() ? 'rtl' : 'ltr' ?>">
121
-
122
- <div class="tnpc-tabs">
123
- <button class="tablinks" onclick="openTab(event, 'tnpc-blocks')" data-tab-id='tnpc-blocks' id="defaultOpen"><?php _e('Blocks', 'newsletter') ?></button>
124
- <button class="tablinks" onclick="openTab(event, 'tnpc-global-styles')" data-tab-id='tnpc-global-styles'><?php _e('Settings', 'newsletter') ?></button>
125
- </div>
126
-
127
- <div id="tnpc-blocks" class="tabcontent">
128
- <?php foreach ($blocks as $k => $section) { ?>
129
- <div class="newsletter-sidebar-add-buttons" id="sidebar-add-<?php echo $k ?>">
130
- <!--<h4><span><?php echo ucfirst($k) ?></span></h4>-->
131
- <?php foreach ($section AS $key => $block) { ?>
132
- <div class="newsletter-sidebar-buttons-content-tab" data-id="<?php echo $key ?>" data-name="<?php echo esc_attr($block['name']) ?>">
133
- <img src="<?php echo $block['icon'] ?>" title="<?php echo esc_attr($block['name']) ?>">
134
- </div>
135
- <?php } ?>
136
- </div>
137
- <?php } ?>
138
- </div>
139
-
140
- <div id="tnpc-global-styles" class="tabcontent">
141
-
142
- <form id="tnpc-global-styles-form">
143
-
144
- <div class="tnp-field-row">
145
- <div class="tnp-field-col-2">
146
- <?php $fields->color('options_composer_background', __('Main background', 'newsletter')) ?>
147
- </div>
148
- <div class="tnp-field-col-2">
149
- <?php $fields->color('options_composer_block_background', 'Blocks background') ?>
150
- </div>
151
- </div>
152
-
153
- <?php $fields->font('options_composer_title_font', __('Titles font', 'newsletter')) ?>
154
- <?php $fields->font('options_composer_text_font', __('Text font', 'newsletter')) ?>
155
- <?php $fields->button_style('options_composer_button', __('Button style', 'newsletter')); ?>
156
-
157
- <button class="button-secondary" name="apply"><?php _e("Apply", 'newsletter') ?></button>
158
-
159
- </form>
160
-
161
- </div>
162
-
163
- <!-- Block options container (dynamically loaded -->
164
- <div id="tnpc-block-options">
165
- <div id="tnpc-block-options-buttons">
166
- <span id="tnpc-block-options-cancel" class="button-secondary"><?php _e("Cancel", "newsletter") ?></span>
167
- <span id="tnpc-block-options-save" class="button-primary"><?php _e("Apply", "newsletter") ?></span>
168
- </div>
169
- <form id="tnpc-block-options-form" onsubmit="return false;"></form>
170
- </div>
171
-
172
- </div>
173
-
174
- <div style="clear: both"></div>
175
-
176
- </div>
177
-
178
- <div style="display: none">
179
- <div id="newsletter-preloaded-export"></div>
180
- <!-- Block placeholder used by jQuery UI -->
181
- <div id="draggable-helper"></div>
182
- <div id="sortable-helper"></div>
183
- </div>
184
-
185
- <script type="text/javascript">
186
- TNP_PLUGIN_URL = "<?php echo esc_js(NEWSLETTER_URL) ?>";
187
- TNP_HOME_URL = "<?php echo esc_js(home_url('/', is_ssl() ? 'https' : 'http')) ?>";
188
- tnp_context_type = "<?php echo esc_js($context_type) ?>";
189
- tnp_nonce = '<?php echo esc_js(wp_create_nonce('save')) ?>';
190
- tnp_preset_nonce = '<?php echo esc_js(wp_create_nonce('preset')) ?>';
191
- </script>
192
- <?php
193
- wp_enqueue_script('tnp-composer', plugins_url('newsletter') . '/emails/tnp-composer/_scripts/newsletter-builder-v2.js', ['tnp-modal', 'tnp-toast'], NEWSLETTER_VERSION);
194
- ?>
195
-
196
- <?php include NEWSLETTER_DIR . '/emails/subjects.php'; ?>
197
-
198
- <?php if (function_exists('wp_enqueue_editor')) wp_enqueue_editor(); ?>
1
+ <?php
2
+ /**
3
+ * This file is included by NewsletterControls to create the composer.
4
+ */
5
+ /* @var $this NewsletterControls */
6
+
7
+ defined('ABSPATH') || exit;
8
+
9
+ $list = NewsletterEmails::instance()->get_blocks();
10
+
11
+ $blocks = array();
12
+ foreach ($list as $key => $data) {
13
+ if (!isset($blocks[$data['section']])) {
14
+ $blocks[$data['section']] = array();
15
+ }
16
+ $blocks[$data['section']][$key]['name'] = $data['name'];
17
+ $blocks[$data['section']][$key]['filename'] = $key;
18
+ $blocks[$data['section']][$key]['icon'] = $data['icon'];
19
+ }
20
+
21
+ // order the sections
22
+ $blocks = array_merge(array_flip(array('header', 'content', 'footer')), $blocks);
23
+
24
+ // prepare the options for the default blocks
25
+ $block_options = get_option('newsletter_main');
26
+
27
+ $fields = new NewsletterFields($controls);
28
+
29
+ $dir = is_rtl() ? 'rtl' : 'ltr';
30
+ $rev_dir = is_rtl() ? 'ltr' : 'rlt';
31
+
32
+ ?>
33
+ <script type="text/javascript">
34
+ // collapse wp menu
35
+ document.body.classList.add('folded');
36
+
37
+ function tnp_view(type) {
38
+ if (type === 'mobile') {
39
+ jQuery('#newsletter-builder-area-center-frame-content').addClass('tnp-view-mobile');
40
+ } else {
41
+ jQuery('#newsletter-builder-area-center-frame-content').removeClass('tnp-view-mobile');
42
+ }
43
+ }
44
+ </script>
45
+
46
+
47
+ <style>
48
+ <?php echo NewsletterEmails::instance()->get_composer_css(); ?>
49
+ </style>
50
+ <div id="newsletter-builder" dir="ltr">
51
+
52
+ <div id="newsletter-builder-area" class="tnp-builder-column">
53
+
54
+ <?php if ( $tnpc_show_subject ) { ?>
55
+ <div id="tnpc-subject-wrap" dir="<?php echo $dir ?>">
56
+ <table role="presentation" style="width: 100%">
57
+ <tr>
58
+ <th dir="<?php echo $dir ?>"><?php _e('From', 'newsletter') ?></th>
59
+ <td dir="<?php echo $dir ?>"><?php echo esc_html( $controls->data['sender_email'] ) ?></td>
60
+ </tr>
61
+ <tr>
62
+ <th dir="<?php echo $dir ?>"><?php _e('Subject', 'newsletter') ?></th>
63
+ <td dir="<?php echo $dir ?>">
64
+ <div id="tnpc-subject">
65
+ <?php $this->subject( 'subject' ); ?>
66
+ </div>
67
+ </td>
68
+ </tr>
69
+ <tr>
70
+ <th dir="<?php echo $dir ?>"><span title="<?php esc_attr_e('Shown by some email clients as excerpt', 'newsletter')?>"><?php _e('Snippet', 'newsletter') ?></span>
71
+ <?php $this->field_help('https://www.thenewsletterplugin.com/documentation/newsletters/composer/#subject') ?>
72
+ </th>
73
+ <td dir="<?php echo $dir ?>"><?php $this->text('preheader') ?></td>
74
+ </tr>
75
+ </table>
76
+
77
+ <div style="text-align: left; margin-left: 1em;">
78
+ <a href="https://www.thenewsletterplugin.com/documentation/newsletters/newsletter-tags/"
79
+ target="_blank">You can use tags to inject subscriber fields</a>. Even on subject.
80
+
81
+
82
+ </div>
83
+
84
+ <div class="composer-actions">
85
+
86
+ <div id="attachment-newsletter-button" class="button-primary" data-tnp-modal-target="#attachment-modal">
87
+ <i class="fas fa-paperclip"></i>
88
+ </div>
89
+
90
+ <div id="test-newsletter-button" class="button-primary" data-tnp-modal-target="#test-newsletter-modal">
91
+ <i class="fas fa-paper-plane"></i> <?php _e( 'Test', 'newsletter' ) ?>
92
+ </div>
93
+
94
+ <div class="composer-view-mode">
95
+ <span class="composer-view-mode__item" data-view-mode="desktop">
96
+ <i class="fas fa-desktop"></i>
97
+ </span>
98
+
99
+ <span class="composer-view-mode__item" data-view-mode="mobile">
100
+ <i class="fas fa-mobile"></i>
101
+ <span>
102
+ </div>
103
+
104
+ </div>
105
+
106
+ <?php include NEWSLETTER_DIR . '/emails/tnp-composer/modal/test-newsletter.php' ?>
107
+ <?php include NEWSLETTER_DIR . '/emails/tnp-composer/modal/attachment.php' ?>
108
+
109
+ </div>
110
+ <?php } ?>
111
+
112
+
113
+ <div id="newsletter-builder-area-center-frame-content" dir="<?php echo $dir ?>">
114
+
115
+ <!-- Composer content -->
116
+
117
+ </div>
118
+ </div>
119
+
120
+ <div id="newsletter-builder-sidebar" dir="<?php echo is_rtl() ? 'rtl' : 'ltr' ?>">
121
+
122
+ <div class="tnpc-tabs">
123
+ <button class="tablinks" onclick="openTab(event, 'tnpc-blocks')" data-tab-id='tnpc-blocks' id="defaultOpen"><?php _e('Blocks', 'newsletter') ?></button>
124
+ <button class="tablinks" onclick="openTab(event, 'tnpc-global-styles')" data-tab-id='tnpc-global-styles'><?php _e('Settings', 'newsletter') ?></button>
125
+ </div>
126
+
127
+ <div id="tnpc-blocks" class="tabcontent">
128
+ <?php foreach ($blocks as $k => $section) { ?>
129
+ <div class="newsletter-sidebar-add-buttons" id="sidebar-add-<?php echo $k ?>">
130
+ <!--<h4><span><?php echo ucfirst($k) ?></span></h4>-->
131
+ <?php foreach ($section AS $key => $block) { ?>
132
+ <div class="newsletter-sidebar-buttons-content-tab" data-id="<?php echo $key ?>" data-name="<?php echo esc_attr($block['name']) ?>">
133
+ <img src="<?php echo $block['icon'] ?>" title="<?php echo esc_attr($block['name']) ?>">
134
+ </div>
135
+ <?php } ?>
136
+ </div>
137
+ <?php } ?>
138
+ </div>
139
+
140
+ <div id="tnpc-global-styles" class="tabcontent">
141
+
142
+ <form id="tnpc-global-styles-form">
143
+
144
+ <div class="tnp-field-row">
145
+ <div class="tnp-field-col-2">
146
+ <?php $fields->color('options_composer_background', __('Main background', 'newsletter')) ?>
147
+ </div>
148
+ <div class="tnp-field-col-2">
149
+ <?php $fields->color('options_composer_block_background', 'Blocks background') ?>
150
+ </div>
151
+ </div>
152
+
153
+ <?php $fields->font('options_composer_title_font', __('Titles font', 'newsletter')) ?>
154
+ <?php $fields->font('options_composer_text_font', __('Text font', 'newsletter')) ?>
155
+ <?php $fields->button_style('options_composer_button', __('Button style', 'newsletter')); ?>
156
+
157
+ <button class="button-secondary" name="apply"><?php _e("Apply", 'newsletter') ?></button>
158
+
159
+ </form>
160
+
161
+ </div>
162
+
163
+ <!-- Block options container (dynamically loaded -->
164
+ <div id="tnpc-block-options">
165
+ <div id="tnpc-block-options-buttons">
166
+ <span id="tnpc-block-options-cancel" class="button-secondary"><?php _e("Cancel", "newsletter") ?></span>
167
+ <span id="tnpc-block-options-save" class="button-primary"><?php _e("Apply", "newsletter") ?></span>
168
+ </div>
169
+ <form id="tnpc-block-options-form" onsubmit="return false;"></form>
170
+ </div>
171
+
172
+ </div>
173
+
174
+ <div style="clear: both"></div>
175
+
176
+ </div>
177
+
178
+ <div style="display: none">
179
+ <div id="newsletter-preloaded-export"></div>
180
+ <!-- Block placeholder used by jQuery UI -->
181
+ <div id="draggable-helper"></div>
182
+ <div id="sortable-helper"></div>
183
+ </div>
184
+
185
+ <script type="text/javascript">
186
+ TNP_PLUGIN_URL = "<?php echo esc_js(NEWSLETTER_URL) ?>";
187
+ TNP_HOME_URL = "<?php echo esc_js(home_url('/', is_ssl() ? 'https' : 'http')) ?>";
188
+ tnp_context_type = "<?php echo esc_js($context_type) ?>";
189
+ tnp_nonce = '<?php echo esc_js(wp_create_nonce('save')) ?>';
190
+ tnp_preset_nonce = '<?php echo esc_js(wp_create_nonce('preset')) ?>';
191
+ </script>
192
+ <?php
193
+ wp_enqueue_script('tnp-composer', plugins_url('newsletter') . '/emails/tnp-composer/_scripts/newsletter-builder-v2.js', ['tnp-modal', 'tnp-toast'], NEWSLETTER_VERSION);
194
+ ?>
195
+
196
+ <?php include NEWSLETTER_DIR . '/emails/subjects.php'; ?>
197
+
198
+ <?php if (function_exists('wp_enqueue_editor')) wp_enqueue_editor(); ?>
emails/tnp-composer/modal/attachment.php CHANGED
@@ -1,19 +1,19 @@
1
- <div class="tnp-modal2" id="attachment-modal" aria-hidden="true">
2
- <div class="tnp-modal2__content" role="dialog" style="width: 600px">
3
- <header class="tnp-modal2__header">
4
-
5
- <h2><?php _e("Attachments", 'newsletter') ?></h2>
6
- <span class="tnp-modal2__close" data-tnp-modal-close aria-label="Close modal"></span>
7
- </header>
8
-
9
- <div class="tnp-modal2__body">
10
-
11
- <p>
12
- The Newsletter plugin does not support attachments.
13
- <a href="https://www.thenewsletterplugin.com/documentation/adding-attachments-to-newsletters/" target="_blank">Please read more in our documentation</a>.
14
- </p>
15
-
16
- </div>
17
-
18
- </div>
19
- </div>
1
+ <div class="tnp-modal2" id="attachment-modal" aria-hidden="true">
2
+ <div class="tnp-modal2__content" role="dialog" style="width: 600px">
3
+ <header class="tnp-modal2__header">
4
+
5
+ <h2><?php _e("Attachments", 'newsletter') ?></h2>
6
+ <span class="tnp-modal2__close" data-tnp-modal-close aria-label="Close modal"></span>
7
+ </header>
8
+
9
+ <div class="tnp-modal2__body">
10
+
11
+ <p>
12
+ The Newsletter plugin does not support attachments.
13
+ <a href="https://www.thenewsletterplugin.com/documentation/adding-attachments-to-newsletters/" target="_blank">Please read more in our documentation</a>.
14
+ </p>
15
+
16
+ </div>
17
+
18
+ </div>
19
+ </div>
images/animated-overlay.gif DELETED
Binary file
images/cd-icon-navigation.svg DELETED
@@ -1,50 +0,0 @@
1
- <?xml version="1.0" encoding="utf-8"?>
2
- <!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
3
- <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
4
- <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
5
- width="240px" height="120px" viewBox="0 0 240 120" enable-background="new 0 0 240 120" xml:space="preserve">
6
- <g transform="translate(22, 22)">
7
- <path fill="#7D7160" d="M195,4h-4V1c0-0.6-0.4-1-1-1h-7c-1.7,0-3,1.3-3,3v10c0,1.7,1.3,3,3,3h12c0.6,0,1-0.4,1-1V5
8
- C196,4.4,195.6,4,195,4z M182,3c0-0.6,0.4-1,1-1h6v2h-7V3z M194,14h-11c-0.6,0-1-0.4-1-1V6h8h4V14z"/>
9
- <rect x="191" y="9" fill="#D2C9C0" width="2" height="2"/>
10
- </g>
11
- <g transform="translate(82, 22)">
12
- <path fill="#7D7160" d="M15,4h-3V1c0-0.6-0.4-1-1-1H5C4.4,0,4,0.4,4,1v3H1C0.4,4,0,4.4,0,5v10c0,0.6,0.4,1,1,1h14c0.6,0,1-0.4,1-1
13
- V5C16,4.4,15.6,4,15,4z M6,2h4v2H6V2z M14,14H2V6h12V14z"/>
14
- <rect x="5" y="7" fill="#D2C9C0" width="6" height="3"/>
15
- </g>
16
- <g transform="translate(142, 22)">
17
- <circle fill="#D2C9C0" cx="6" cy="8" r="2"/>
18
- <path fill="#7D7160" d="M10,2H6C2.7,2,0,4.7,0,8s2.7,6,6,6h4c3.3,0,6-2.7,6-6S13.3,2,10,2z M10,12H6c-2.2,0-4-1.8-4-4s1.8-4,4-4h4
19
- c2.2,0,4,1.8,4,4S12.2,12,10,12z"/>
20
- </g>
21
- <g transform="translate(202, 22)">
22
- <path fill="#7D7160" d="M-177,16h10c0.6,0,1-0.4,1-1V7.5l0.3,0.3c0.2,0.1,0.5,0.2,0.7,0.2c0.3,0,0.6-0.1,0.8-0.3
23
- c0.4-0.4,0.3-1-0.1-1.4l-7-6c-0.4-0.3-0.9-0.3-1.3,0l-3.4,2.8V2c0-0.6-0.4-1-1-1c-0.6,0-1,0.4-1,1v2.8l-1.7,1.4
24
- c-0.4,0.4-0.5,1-0.1,1.4c0.4,0.4,1,0.5,1.4,0.1l0.4-0.2V15C-178,15.6-177.6,16-177,16z M-176,5.7l4-3.4l4,3.5c0,0.1,0,0.1,0,0.2v8
25
- h-2v-3c0-0.6-0.4-1-1-1h-2c-0.6,0-1,0.4-1,1v3h-2V5.7z"/>
26
- <rect x="-174" y="6" fill="#D2C9C0" width="4" height="2"/>
27
- </g>
28
- <g transform="translate(22, 82)">
29
- <path fill="#FF6155" d="M195,4h-4V1c0-0.6-0.4-1-1-1h-7c-1.7,0-3,1.3-3,3v10c0,1.7,1.3,3,3,3h12c0.6,0,1-0.4,1-1V5
30
- C196,4.4,195.6,4,195,4z M182,3c0-0.6,0.4-1,1-1h6v2h-7V3z M194,14h-11c-0.6,0-1-0.4-1-1V6h8h4V14z"/>
31
- <rect x="191" y="9" fill="#FF6155" width="2" height="2"/>
32
- </g>
33
- <g transform="translate(82, 82)">
34
- <path fill="#FF6155" d="M15,4h-3V1c0-0.6-0.4-1-1-1H5C4.4,0,4,0.4,4,1v3H1C0.4,4,0,4.4,0,5v10c0,0.6,0.4,1,1,1h14c0.6,0,1-0.4,1-1
35
- V5C16,4.4,15.6,4,15,4z M6,2h4v2H6V2z M14,14H2V6h12V14z"/>
36
- <rect x="5" y="7" fill="#FF6155" width="6" height="3"/>
37
- </g>
38
- <g transform="translate(142, 82)">
39
- <circle fill="#FF6155" cx="6" cy="8" r="2"/>
40
- <path fill="#FF6155" d="M10,2H6C2.7,2,0,4.7,0,8s2.7,6,6,6h4c3.3,0,6-2.7,6-6S13.3,2,10,2z M10,12H6c-2.2,0-4-1.8-4-4s1.8-4,4-4h4
41
- c2.2,0,4,1.8,4,4S12.2,12,10,12z"/>
42
- </g>
43
- <g transform="translate(202, 82)">
44
- <path fill="#FF6155" d="M-177,16h10c0.6,0,1-0.4,1-1V7.5l0.3,0.3c0.2,0.1,0.5,0.2,0.7,0.2c0.3,0,0.6-0.1,0.8-0.3
45
- c0.4-0.4,0.3-1-0.1-1.4l-7-6c-0.4-0.3-0.9-0.3-1.3,0l-3.4,2.8V2c0-0.6-0.4-1-1-1c-0.6,0-1,0.4-1,1v2.8l-1.7,1.4
46
- c-0.4,0.4-0.5,1-0.1,1.4c0.4,0.4,1,0.5,1.4,0.1l0.4-0.2V15C-178,15.6-177.6,16-177,16z M-176,5.7l4-3.4l4,3.5c0,0.1,0,0.1,0,0.2v8
47
- h-2v-3c0-0.6-0.4-1-1-1h-2c-0.6,0-1,0.4-1,1v3h-2V5.7z"/>
48
- <rect x="-174" y="6" fill="#FF6155" width="4" height="2"/>
49
- </g>
50
- </svg>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
images/errors.png DELETED
Binary file
images/facebook.png DELETED
Binary file
images/header/debug.png DELETED
Binary file
images/header/documentation.png DELETED
Binary file
images/header/facebook.png DELETED
Binary file
images/header/forum.png DELETED
Binary file
images/header/logo.png DELETED
Binary file
images/header/tnp-logo-white-header.png DELETED
Binary file
images/idea.svg DELETED
@@ -1 +0,0 @@
1
- <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 48 48" xml:space="preserve" width="48" height="48"><g class="nc-icon-wrapper"><path fill="#43A6DD" d="M6,25H2c-0.55225,0-1-0.44727-1-1s0.44775-1,1-1h4c0.55225,0,1,0.44727,1,1S6.55225,25,6,25z"></path> <path fill="#43A6DD" d="M11.27197,12.27246c-0.25586,0-0.51172-0.09766-0.70703-0.29297l-2.82812-2.8291 c-0.39062-0.39062-0.39062-1.02344,0-1.41406s1.02344-0.39062,1.41406,0l2.82812,2.8291c0.39062,0.39062,0.39062,1.02344,0,1.41406 C11.78369,12.1748,11.52783,12.27246,11.27197,12.27246z"></path> <path fill="#43A6DD" d="M24,7c-0.55225,0-1-0.44727-1-1V2c0-0.55273,0.44775-1,1-1s1,0.44727,1,1v4C25,6.55273,24.55225,7,24,7z"></path> <path fill="#43A6DD" d="M36.72803,12.27246c-0.25586,0-0.51172-0.09766-0.70703-0.29297c-0.39062-0.39062-0.39062-1.02344,0-1.41406 l2.82812-2.8291c0.38965-0.39062,1.02344-0.39062,1.41406,0s0.39062,1.02344,0,1.41406l-2.82812,2.8291 C37.24023,12.1748,36.98389,12.27246,36.72803,12.27246z"></path> <path fill="#43A6DD" d="M46,25h-4c-0.55225,0-1-0.44727-1-1s0.44775-1,1-1h4c0.55225,0,1,0.44727,1,1S46.55225,25,46,25z"></path> <path fill="#EFD358" d="M29,41H19v-5.01172c-5.33776-2.22651-8.67485-7.80107-7.88457-13.71942 c0.85052-6.36943,6.45305-11.26616,12.87902-11.26886C31.16545,10.997,37,16.83019,37,24c0,5.25-3.18652,9.98047-8,11.98828V41z"></path> <path fill="#E6E6E6" d="M24.00001,47h-0.00001C21.23857,47,19,44.76143,19,42v-3h10v3C29,44.76143,26.76143,47,24.00001,47z"></path> <rect x="19" y="39" fill="#B3B3B3" width="10" height="2"></rect></g></svg>
 
images/img-1.jpg DELETED
Binary file
images/img-2.jpg DELETED
Binary file
images/img-3.jpg DELETED
Binary file
images/img-4.jpg DELETED
Binary file
images/messages.png DELETED
Binary file
images/modal-background.png DELETED
Binary file
images/popup/bg.png DELETED
Binary file
images/popup/button.png DELETED
Binary file
images/preamble.png DELETED
Binary file
images/social-1/discord.png ADDED
Binary file
images/social-1/facebook.png ADDED
Binary file
images/social-1/instagram.png ADDED
Binary file
images/social-1/linkedin.png ADDED
Binary file
images/social-1/pinterest.png ADDED
Binary file
images/social-1/soundcloud.png ADDED
Binary file
images/social-1/telegram.png ADDED
Binary file
images/social-1/tiktok.png ADDED
Binary file
images/social-1/tumblr.png ADDED
Binary file
images/social-1/twitch.png ADDED
Binary file
images/social-1/twitter.png ADDED
Binary file
images/social-1/vimeo.png ADDED
Binary file
images/social-1/vk.png ADDED
Binary file
images/social-1/youtube.png ADDED
Binary file
images/social-2/discord.png ADDED
Binary file
images/social-2/facebook.png ADDED
Binary file
images/social-2/instagram.png ADDED
Binary file
images/social-2/linkedin.png ADDED
Binary file
images/social-2/pinterest.png ADDED
Binary file
images/social-2/soundcloud.png ADDED
Binary file
images/social-2/telegram.png ADDED
Binary file
images/social-2/tiktok.png ADDED
Binary file
images/social-2/tumblr.png ADDED
Binary file
images/social-2/twitch.png ADDED
Binary file
images/social-2/twitter.png ADDED
Binary file
images/social-2/vimeo.png ADDED
Binary file
images/social-2/vk.png ADDED
Binary file
images/social-2/youtube.png ADDED
Binary file
images/tnp-logo-colore-text-white@2x.png DELETED
Binary file
includes/controls.php CHANGED
@@ -1,2074 +1,2107 @@
1
- <?php
2
-
3
- defined('ABSPATH') || exit;
4
-
5
- include_once __DIR__ . '/fields.php';
6
-
7
- class NewsletterControls {
8
-
9
- var $data = [];
10
- var $action = false;
11
- var $button_data = '';
12
- var $errors = '';
13
-
14
- /**
15
- * @var string
16
- */
17
- var $messages = '';
18
-
19
- /**
20
- * @var array
21
- */
22
- var $warnings = array();
23
- var $countries = array(
24
- 'AF' => 'Afghanistan',
25
- 'AX' => 'Aland Islands',
26
- 'AL' => 'Albania',
27
- 'DZ' => 'Algeria',
28
- 'AS' => 'American Samoa',
29
- 'AD' => 'Andorra',
30
- 'AO' => 'Angola',
31
- 'AI' => 'Anguilla',
32
- 'AQ' => 'Antarctica',
33
- 'AG' => 'Antigua And Barbuda',
34
- 'AR' => 'Argentina',
35
- 'AM' => 'Armenia',
36
- 'AW' => 'Aruba',
37
- 'AU' => 'Australia',
38
- 'AT' => 'Austria',
39
- 'AZ' => 'Azerbaijan',
40
- 'BS' => 'Bahamas',
41
- 'BH' => 'Bahrain',
42
- 'BD' => 'Bangladesh',
43
- 'BB' => 'Barbados',
44
- 'BY' => 'Belarus',
45
- 'BE' => 'Belgium',
46
- 'BZ' => 'Belize',
47
- 'BJ' => 'Benin',
48
- 'BM' => 'Bermuda',
49
- 'BT' => 'Bhutan',
50
- 'BO' => 'Bolivia',
51
- 'BA' => 'Bosnia And Herzegovina',
52
- 'BW' => 'Botswana',
53
- 'BV' => 'Bouvet Island',
54
- 'BR' => 'Brazil',
55
- 'IO' => 'British Indian Ocean Territory',
56
- 'BN' => 'Brunei Darussalam',
57
- 'BG' => 'Bulgaria',
58
- 'BF' => 'Burkina Faso',
59
- 'BI' => 'Burundi',
60
- 'KH' => 'Cambodia',
61
- 'CM' => 'Cameroon',
62
- 'CA' => 'Canada',
63
- 'CV' => 'Cape Verde',
64
- 'KY' => 'Cayman Islands',
65
- 'CF' => 'Central African Republic',
66
- 'TD' => 'Chad',
67
- 'CL' => 'Chile',
68
- 'CN' => 'China',
69
- 'CX' => 'Christmas Island',
70
- 'CC' => 'Cocos (Keeling) Islands',
71
- 'CO' => 'Colombia',
72
- 'KM' => 'Comoros',
73
- 'CG' => 'Congo',
74
- 'CD' => 'Congo, Democratic Republic',
75
- 'CK' => 'Cook Islands',
76
- 'CR' => 'Costa Rica',
77
- 'CI' => 'Cote D\'Ivoire',
78
- 'HR' => 'Croatia',
79
- 'CU' => 'Cuba',
80
- 'CY' => 'Cyprus',
81
- 'CZ' => 'Czech Republic',
82
- 'DK' => 'Denmark',
83
- 'DJ' => 'Djibouti',
84
- 'DM' => 'Dominica',
85
- 'DO' => 'Dominican Republic',
86
- 'EC' => 'Ecuador',
87
- 'EG' => 'Egypt',
88
- 'SV' => 'El Salvador',
89
- 'GQ' => 'Equatorial Guinea',
90
- 'ER' => 'Eritrea',
91
- 'EE' => 'Estonia',
92
- 'ET' => 'Ethiopia',
93
- 'FK' => 'Falkland Islands (Malvinas)',
94
- 'FO' => 'Faroe Islands',
95
- 'FJ' => 'Fiji',
96
- 'FI' => 'Finland',
97
- 'FR' => 'France',
98
- 'GF' => 'French Guiana',
99
- 'PF' => 'French Polynesia',
100
- 'TF' => 'French Southern Territories',
101
- 'GA' => 'Gabon',
102
- 'GM' => 'Gambia',
103
- 'GE' => 'Georgia',
104
- 'DE' => 'Germany',
105
- 'GH' => 'Ghana',
106
- 'GI' => 'Gibraltar',
107
- 'GR' => 'Greece',
108
- 'GL' => 'Greenland',
109
- 'GD' => 'Grenada',
110
- 'GP' => 'Guadeloupe',
111
- 'GU' => 'Guam',
112
- 'GT' => 'Guatemala',
113
- 'GG' => 'Guernsey',
114
- 'GN' => 'Guinea',
115
- 'GW' => 'Guinea-Bissau',
116
- 'GY' => 'Guyana',
117
- 'HT' => 'Haiti',
118
- 'HM' => 'Heard Island & Mcdonald Islands',
119
- 'VA' => 'Holy See (Vatican City State)',
120
- 'HN' => 'Honduras',
121
- 'HK' => 'Hong Kong',
122
- 'HU' => 'Hungary',
123
- 'IS' => 'Iceland',
124
- 'IN' => 'India',
125
- 'ID' => 'Indonesia',
126
- 'IR' => 'Iran, Islamic Republic Of',
127
- 'IQ' => 'Iraq',
128
- 'IE' => 'Ireland',
129
- 'IM' => 'Isle Of Man',
130
- 'IL' => 'Israel',
131
- 'IT' => 'Italy',
132
- 'JM' => 'Jamaica',
133
- 'JP' => 'Japan',
134
- 'JE' => 'Jersey',
135
- 'JO' => 'Jordan',
136
- 'KZ' => 'Kazakhstan',
137
- 'KE' => 'Kenya',
138
- 'KI' => 'Kiribati',
139
- 'KR' => 'Korea',
140
- 'KW' => 'Kuwait',
141
- 'KG' => 'Kyrgyzstan',
142
- 'LA' => 'Lao People\'s Democratic Republic',
143
- 'LV' => 'Latvia',
144
- 'LB' => 'Lebanon',
145
- 'LS' => 'Lesotho',
146
- 'LR' => 'Liberia',
147
- 'LY' => 'Libyan Arab Jamahiriya',
148
- 'LI' => 'Liechtenstein',
149
- 'LT' => 'Lithuania',
150
- 'LU' => 'Luxembourg',
151
- 'MO' => 'Macao',
152
- 'MK' => 'Macedonia',
153
- 'MG' => 'Madagascar',
154
- 'MW' => 'Malawi',
155
- 'MY' => 'Malaysia',
156
- 'MV' => 'Maldives',
157
- 'ML' => 'Mali',
158
- 'MT' => 'Malta',
159
- 'MH' => 'Marshall Islands',
160
- 'MQ' => 'Martinique',
161
- 'MR' => 'Mauritania',
162
- 'MU' => 'Mauritius',
163
- 'YT' => 'Mayotte',
164
- 'MX' => 'Mexico',
165
- 'FM' => 'Micronesia, Federated States Of',
166
- 'MD' => 'Moldova',
167
- 'MC' => 'Monaco',
168
- 'MN' => 'Mongolia',
169
- 'ME' => 'Montenegro',
170
- 'MS' => 'Montserrat',
171
- 'MA' => 'Morocco',
172
- 'MZ' => 'Mozambique',
173
- 'MM' => 'Myanmar',
174
- 'NA' => 'Namibia',
175
- 'NR' => 'Nauru',
176
- 'NP' => 'Nepal',
177
- 'NL' => 'Netherlands',
178
- 'AN' => 'Netherlands Antilles',
179
- 'NC' => 'New Caledonia',
180
- 'NZ' => 'New Zealand',
181
- 'NI' => 'Nicaragua',
182
- 'NE' => 'Niger',
183
- 'NG' => 'Nigeria',
184
- 'NU' => 'Niue',
185
- 'NF' => 'Norfolk Island',
186
- 'MP' => 'Northern Mariana Islands',
187
- 'NO' => 'Norway',
188
- 'OM' => 'Oman',
189
- 'PK' => 'Pakistan',
190
- 'PW' => 'Palau',
191
- 'PS' => 'Palestinian Territory, Occupied',
192
- 'PA' => 'Panama',
193
- 'PG' => 'Papua New Guinea',
194
- 'PY' => 'Paraguay',
195
- 'PE' => 'Peru',
196
- 'PH' => 'Philippines',
197
- 'PN' => 'Pitcairn',
198
- 'PL' => 'Poland',
199
- 'PT' => 'Portugal',
200
- 'PR' => 'Puerto Rico',
201
- 'QA' => 'Qatar',
202
- 'RE' => 'Reunion',
203
- 'RO' => 'Romania',
204
- 'RU' => 'Russian Federation',
205
- 'RW' => 'Rwanda',
206
- 'BL' => 'Saint Barthelemy',
207
- 'SH' => 'Saint Helena',
208
- 'KN' => 'Saint Kitts And Nevis',
209
- 'LC' => 'Saint Lucia',
210
- 'MF' => 'Saint Martin',
211
- 'PM' => 'Saint Pierre And Miquelon',
212
- 'VC' => 'Saint Vincent And Grenadines',
213
- 'WS' => 'Samoa',
214
- 'SM' => 'San Marino',
215
- 'ST' => 'Sao Tome And Principe',
216
- 'SA' => 'Saudi Arabia',
217
- 'SN' => 'Senegal',
218
- 'RS' => 'Serbia',
219
- 'SC' => 'Seychelles',
220
- 'SL' => 'Sierra Leone',
221
- 'SG' => 'Singapore',
222
- 'SK' => 'Slovakia',
223
- 'SI' => 'Slovenia',
224
- 'SB' => 'Solomon Islands',
225
- 'SO' => 'Somalia',
226
- 'ZA' => 'South Africa',
227
- 'GS' => 'South Georgia And Sandwich Isl.',
228
- 'ES' => 'Spain',
229
- 'LK' => 'Sri Lanka',
230
- 'SD' => 'Sudan',
231
- 'SR' => 'Suriname',
232
- 'SJ' => 'Svalbard And Jan Mayen',
233
- 'SZ' => 'Swaziland',
234
- 'SE' => 'Sweden',
235
- 'CH' => 'Switzerland',
236
- 'SY' => 'Syrian Arab Republic',
237
- 'TW' => 'Taiwan',
238
- 'TJ' => 'Tajikistan',
239
- 'TZ' => 'Tanzania',
240
- 'TH' => 'Thailand',
241
- 'TL' => 'Timor-Leste',
242
- 'TG' => 'Togo',
243
- 'TK' => 'Tokelau',
244
- 'TO' => 'Tonga',
245
- 'TT' => 'Trinidad And Tobago',
246
- 'TN' => 'Tunisia',
247
- 'TR' => 'Turkey',
248
- 'TM' => 'Turkmenistan',
249
- 'TC' => 'Turks And Caicos Islands',
250
- 'TV' => 'Tuvalu',
251
- 'UG' => 'Uganda',
252
- 'UA' => 'Ukraine',
253
- 'AE' => 'United Arab Emirates',
254
- 'GB' => 'United Kingdom',
255
- 'US' => 'United States',
256
- 'UM' => 'United States Outlying Islands',
257
- 'UY' => 'Uruguay',
258
- 'UZ' => 'Uzbekistan',
259
- 'VU' => 'Vanuatu',
260
- 'VE' => 'Venezuela',
261
- 'VN' => 'Viet Nam',
262
- 'VG' => 'Virgin Islands, British',
263
- 'VI' => 'Virgin Islands, U.S.',
264
- 'WF' => 'Wallis And Futuna',
265
- 'EH' => 'Western Sahara',
266
- 'YE' => 'Yemen',
267
- 'ZM' => 'Zambia',
268
- 'ZW' => 'Zimbabwe',
269
- 'XX' => 'Undefined',
270
- 'CW' => 'Curaçao',
271
- 'SS' => 'South Sudan',
272
- 'EU' => 'Europe (generic)',
273
- 'A1' => 'Anonymous IP',
274
- 'A2' => 'Satellite IP'
275
- );
276
-
277
- /**
278
- *
279
- * @param array $options
280
- */
281
- function __construct($options = null) {
282
- if ($options === null) {
283
- if (isset($_POST['options'])) {
284
- $this->data = stripslashes_deep($_POST['options']);
285
- }
286
- } else {
287
- $this->data = (array) $options;
288
- }
289
-
290
- if (isset($_REQUEST['act'])) {
291
- $this->action = $_REQUEST['act'];
292
- }
293
-
294
- if (isset($_REQUEST['btn'])) {
295
- $this->button_data = $_REQUEST['btn'];
296
- }
297
- // Fields analysis
298
- if (isset($_REQUEST['tnp_fields'])) {
299
- $fields = $_REQUEST['tnp_fields'];
300
- if (is_array($fields)) {
301
- foreach ($fields as $name => $type) {
302
- if ($type == 'datetime') {
303
- // Ex. The user insert 01/07/2012 14:30 and it set the time zone to +2. We cannot use the
304
- // mktime, since it uses the time zone of the machine. We create the time as if we are on
305
- // GMT 0 and then we subtract the GMT offset (the example date and time on GMT+2 happens
306
- // "before").
307
-
308
- $time = gmmktime((int) $_REQUEST[$name . '_hour'], 0, 0, (int) $_REQUEST[$name . '_month'], (int) $_REQUEST[$name . '_day'], (int) $_REQUEST[$name . '_year']);
309
- $time -= get_option('gmt_offset') * 3600;
310
- $this->data[$name] = $time;
311
- continue;
312
- }
313
- if ($type === 'array') {
314
- if (!isset($this->data[$name]))
315
- $this->data[$name] = [];
316
- }
317
- if ($type === 'checkbox') {
318
- if (!isset($this->data[$name])) {
319
- $this->data[$name] = 0;
320
- }
321
- }
322
- }
323
- }
324
- }
325
- }
326
-
327
- function set_data($data) {
328
- if (is_array($data)) {
329
- $this->data = $data;
330
- } else if (is_object($data)) {
331
- $this->data = (array) $data;
332
- } else {
333
- $this->data = [];
334
- }
335
- }
336
-
337
- function merge($options) {
338
- if (!is_array($options))
339
- return;
340
- if ($this->data == null)
341
- $this->data = array();
342
- $this->data = array_merge($this->data, $options);
343
- }
344
-
345
- function merge_defaults($defaults) {
346
- if ($this->data == null)
347
- $this->data = $defaults;
348
- else
349
- $this->data = array_merge($defaults, $this->data);
350
- }
351
-
352
- /**
353
- * Return true is there in an asked action is no action name is specified or
354
- * true is the requested action matches the passed action.
355
- * Dies if it is not a safe call.
356
- */
357
- function is_action($action = null) {
358
- if ($action == null)
359
- return $this->action != null;
360
- if ($this->action == null)
361
- return false;
362
- if ($this->action != $action)
363
- return false;
364
- if (check_admin_referer('save'))
365
- return true;
366
- die('Invalid call');
367
- }
368
-
369
- function get_value($name, $def = null) {
370
- if (!isset($this->data[$name])) {
371
- return $def;
372
- }
373
- return $this->data[$name];
374
- }
375
-
376
- function get_value_array($name) {
377
- if (!isset($this->data[$name]) || !is_array($this->data[$name]))
378
- return array();
379
- return $this->data[$name];
380
- }
381
-
382
- function show_error($text) {
383
- echo '<div class="tnp-error">', $text, '</div>';
384
- }
385
-
386
- function show_warning($text) {
387
- echo '<div class="tnp-warning">', $text, '</div>';
388
- }
389
-
390
- function show_message($text) {
391
- echo '<div class="tnpc-message">', $text, '</div>';
392
- }
393
-
394
- /**
395
- * Show the errors and messages.
396
- */
397
- function show() {
398
- static $shown = false;
399
-
400
- if ($shown) {
401
- return;
402
- }
403
- $shown = true;
404
-
405
- if (!empty($this->errors)) {
406
- echo '<div class="tnpc-error">';
407
- echo $this->errors;
408
- echo '</div>';
409
- }
410
- if (!empty($this->warnings)) {
411
- foreach ((array) $this->warnings as $warning) {
412
- echo '<div class="tnpc-warning">';
413
- echo $warning;
414
- echo '</div>';
415
- }
416
- }
417
- if (!empty($this->messages)) {
418
- echo '<div class="tnpc-message">';
419
- echo $this->messages;
420
- echo '</div>';
421
- }
422
- }
423
-
424
- function add_message($text) {
425
- if (!empty($this->messages)) {
426
- $this->messages .= '<br><br>';
427
- }
428
- $this->messages .= $text;
429
- }
430
-
431
- function add_message_saved() {
432
- if (!empty($this->messages)) {
433
- $this->messages .= '<br><br>';
434
- }
435
- $this->messages .= __('Saved.', 'newsletter');
436
- }
437
-
438
- function add_message_deleted() {
439
- if (!empty($this->messages)) {
440
- $this->messages .= '<br><br>';
441
- }
442
- $this->messages .= __('Deleted.', 'newsletter');
443
- }
444
-
445
- function add_message_reset() {
446
- if (!empty($this->messages)) {
447
- $this->messages .= '<br><br>';
448
- }
449
- $this->messages .= __('Options reset.', 'newsletter');
450
- }
451
-
452
- function add_message_done() {
453
- if (!empty($this->messages)) {
454
- $this->messages .= '<br><br>';
455
- }
456
- $this->messages .= __('Done.', 'newsletter');
457
- }
458
-
459
- function add_language_warning() {
460
- $newsletter = Newsletter::instance();
461
- $current_language = $newsletter->get_current_language();
462
-
463
- if (!$current_language) {
464
- return;
465
- }
466
- $this->warnings[] = 'You are configuring the language <strong>' . $newsletter->get_language_label($current_language) . '</strong>. Switch to "all languages" to see all options.';
467
- }
468
-
469
- function switch_to_all_languages_notice() {
470
- echo '<div class="tnpc-languages-notice">';
471
- _e('Switch the administration side to "all languages" to set these options', 'newsletter');
472
- echo '</div>';
473
- }
474
-
475
- function hint($text, $url = '') {
476
- echo '<div class="tnpc-hint">';
477
- // Do not escape that, it can be formatted
478
- echo $text;
479
- if (!empty($url)) {
480
- echo ' <a href="' . esc_attr($url) . '" target="_blank">Read more</a>.';
481
- }
482
- echo '</div>';
483
- }
484
-
485
- function yesno($name) {
486
- $value = isset($this->data[$name]) ? (int) $this->data[$name] : 0;
487
-
488
- echo '<select style="width: 60px" name="options[', esc_attr($name), ']">';
489
- echo '<option value="0"';
490
- if ($value == 0) {
491
- echo ' selected';
492
- }
493
- echo '>', __('No', 'newsletter'), '</option>';
494
- echo '<option value="1"';
495
- if ($value == 1) {
496
- echo ' selected';
497
- }
498
- echo '>', __('Yes', 'newsletter'), '</option>';
499
- echo '</select>&nbsp;&nbsp;&nbsp;';
500
- }
501
-
502
- function enabled($name = 'enabled', $attrs = []) {
503
- $value = isset($this->data[$name]) ? (int) $this->data[$name] : 0;
504
- $name = esc_attr($name);
505
-
506
- echo '<select style="width: 100px" name="options[', $name, ']" id="options-', $name, '"';
507
- if (isset($attrs['bind_to'])) {
508
- echo ' onchange="tnp_select_toggle(this, \'', $attrs['bind_to'], '\')"';
509
- }
510
- echo '>';
511
- echo '<option value="0"';
512
- if ($value == 0) {
513
- echo ' selected';
514
- }
515
- echo '>', __('Disabled', 'newsletter'), '</option>';
516
- echo '<option value="1"';
517
- if ($value == 1) {
518
- echo ' selected';
519
- }
520
- echo '>', __('Enabled', 'newsletter'), '</option>';
521
- echo '</select>';
522
- if (isset($attrs['bind_to'])) {
523
- if ($value) {
524
- echo '<script>jQuery(function ($) {$("#options-', $attrs['bind_to'], '").show()})</script>';
525
- } else {
526
- echo '<script>jQuery(function ($) {$("#options-', $attrs['bind_to'], '").hide()})</script>';
527
- }
528
- }
529
- }
530
-
531
- function disabled($name) {
532
- $value = isset($this->data[$name]) ? (int) $this->data[$name] : 0;
533
-
534
- echo '<select style="width: 100px" name="options[' . esc_attr($name) . ']">';
535
- echo '<option value="0"';
536
- if ($value == 0) {
537
- echo ' selected';
538
- }
539
- echo '>Enabled</option>';
540
- echo '<option value="1"';
541
- if ($value == 1) {
542
- echo ' selected';
543
- }
544
- echo '>Disabled</option>';
545
- echo '</select>';
546
- }
547
-
548
- /**
549
- * Creates a set of checkbox all named as $name with values and labels extracted from
550
- * $values_labels. A checkbox will be checked if internal data under key $name is an array
551
- * and contains the value of the current (echoing) checkbox.
552
- *
553
- * On submit it produces an array under the name $name IF at least one checkbox has
554
- * been checked. Otherwise the key won't be present.
555
- *
556
- * @param array $values
557
- * @param string $name
558
- * @param array $values_labels
559
- */
560
- function checkboxes_group($name, $values_labels) {
561
- $value_array = $this->get_value_array($name);
562
-
563
- echo '<div class="tnpc-checkboxes">';
564
- foreach ($values_labels as $value => $label) {
565
- echo '<label><input type="checkbox" id="' . esc_attr($name) . '" name="options[' . esc_attr($name) . '][]" value="' . esc_attr($value) . '"';
566
- if (array_search($value, $value_array) !== false) {
567
- echo ' checked';
568
- }
569
- echo '>';
570
- if ($label != '') {
571
- echo '&nbsp;' . esc_html($label);
572
- }
573
- echo "</label>";
574
- }
575
- echo "<div style='clear: both'></div>";
576
- }
577
-
578
- /** Creates a checkbox group with all public post types.
579
- */
580
- function post_types($name = 'post_types') {
581
- $list = array();
582
- $post_types = get_post_types(array('public' => true), 'objects', 'and');
583
- foreach ($post_types as $post_type) {
584
- $list[$post_type->name] = $post_type->labels->name;
585
- }
586
-
587
- $this->checkboxes_group($name, $list);
588
- }
589
-
590
- function posts_select($name, $max = 20, $args = array()) {
591
- $args = array_merge(array(
592
- 'posts_per_page' => 5,
593
- 'offset' => 0,
594
- 'category' => '',
595
- 'category_name' => '',
596
- 'orderby' => 'date',
597
- 'order' => 'DESC',
598
- 'include' => '',
599
- 'exclude' => '',
600
- 'meta_key' => '',
601
- 'meta_value' => '',
602
- 'post_type' => 'post',
603
- 'post_mime_type' => '',
604
- 'post_parent' => '',
605
- 'author' => '',
606
- 'author_name' => '',
607
- 'post_status' => 'publish',
608
- 'suppress_filters' => true
609
- ), $args);
610
- $args['posts_per_page'] = $max;
611
-
612
- $posts = get_posts($args);
613
- $options = array();
614
- foreach ($posts as $post) {
615
- $options['' . $post->ID] = $post->post_title;
616
- }
617
-
618
- $this->select($name, $options);
619
- }
620
-
621
- function select_number($name, $min, $max) {
622
- $options = array();
623
- for ($i = $min; $i <= $max; $i++) {
624
- $options['' . $i] = $i;
625
- }
626
- $this->select($name, $options);
627
- }
628
-
629
- function page($name = 'page', $first = null, $language = '', $show_id = false) {
630
- $args = array(
631
- 'post_type' => 'page',
632
- 'posts_per_page' => 1000,
633
- 'offset' => 0,
634
- 'orderby' => 'post_title',
635
- 'post_status' => 'any',
636
- 'suppress_filters' => true
637
- );
638
-
639
- $pages = get_posts($args);
640
- //$pages = get_pages();
641
- $options = array();
642
- foreach ($pages as $page) {
643
- /* @var $page WP_Post */
644
- $label = $page->post_title;
645
- if ($page->post_status != 'publish') {
646
- $label .= ' (' . $page->post_status . ')';
647
- }
648
- if ($show_id) {
649
- $label .= ' [' . $page->ID . ']';
650
- }
651
- $options[$page->ID] = $label;
652
- }
653
- $this->select($name, $options, $first);
654
- }
655
-
656
- /** Used to create a select which is part of a group of controls identified by $name that will
657
- * produce an array of values as $_REQUEST['name'].
658
- * @param string $name
659
- * @param array $options Associative array
660
- */
661
- function select_group($name, $options) {
662
- $value_array = $this->get_value_array($name);
663
-
664
- echo '<select name="options[' . esc_attr($name) . '][]">';
665
-
666
- foreach ($options as $key => $label) {
667
- echo '<option value="' . esc_attr($key) . '"';
668
- if (array_search($key, $value_array) !== false) {
669
- echo ' selected';
670
- }
671
- echo '>' . esc_html($label) . '</option>';
672
- }
673
-
674
- echo '</select>';
675
- }
676
-
677
- function select($name, $options, $first = null, $attrs = []) {
678
- echo '<select id="options-' . esc_attr($name) . '" name="options[' . esc_attr($name) . ']"';
679
- if ($attrs) {
680
- foreach ($attrs as $key => $value) {
681
- echo ' ', $key, '="' . esc_attr($value), '"';
682
- }
683
- }
684
- echo '>';
685
- if (!empty($first)) {
686
- echo '<option value="">' . esc_html($first) . '</option>';
687
- }
688
- $value = $this->get_value($name);
689
- foreach ($options as $key => $label) {
690
- echo '<option value="' . esc_attr($key) . '"';
691
- if ($value == $key) {
692
- echo ' selected';
693
- }
694
- echo '>' . esc_html($label) . '</option>';
695
- }
696
- echo '</select>';
697
- }
698
-
699
- function select_images($name, $options, $first = null) {
700
- $value = $this->get_value($name);
701
-
702
- echo '<select id="options-' . esc_attr($name) . '" name="options[' . esc_attr($name) . ']" style="min-width: 200px">';
703
- if (!empty($first)) {
704
- echo '<option value="">' . esc_html($first) . '</option>';
705
- } else {
706
- // if (empty($value)) {
707
- // $keys = array_keys($options);
708
- // $value = $keys[0];
709
- // }
710
- }
711
- foreach ($options as $key => $data) {
712
- echo '<option value="' . esc_attr($key) . '" image="' . esc_attr($data['image']) . '"';
713
- if ($value == $key)
714
- echo ' selected';
715
- echo '>' . esc_html($data['label']) . '</option>';
716
- }
717
- echo '</select>';
718
- echo '<script>jQuery("#options-' . esc_attr($name) . '").select2({templateResult: tnp_select_images, templateSelection: tnp_select_images_selection});</script>';
719
- }
720
-
721
- function select2($name, $options, $first = null, $multiple = false, $style = null, $placeholder = '') {
722
-
723
- if ($multiple) {
724
- $option_name = "options[" . esc_attr($name) . "][]";
725
- } else {
726
- $option_name = "options[" . esc_attr($name) . "]";
727
- }
728
-
729
- if (is_null($style)) {
730
- $style = 'width: 100%';
731
- }
732
-
733
- $value = $this->get_value($name);
734
-
735
- echo '<select id="options-', esc_attr($name), '" name="', $option_name, '" style="', $style, '"',
736
- ($multiple ? ' multiple' : ''), '>';
737
- if (!empty($first)) {
738
- echo '<option value="">' . esc_html($first) . '</option>';
739
- }
740
-
741
- foreach ($options as $key => $data) {
742
- echo '<option value="' . esc_attr($key) . '"';
743
- if (is_array($value) && in_array($key, $value) || (!is_null($value) && $value == $key )) {
744
- echo ' selected';
745
- }
746
- echo '>' . esc_html($data) . '</option>';
747
- }
748
-
749
- echo '</select>';
750
- echo '<script>jQuery("#options-' . esc_attr($name) . '").select2({placeholder: "', esc_js($placeholder), '"});</script>';
751
- }
752
-
753
- function select_grouped($name, $groups) {
754
- $value = $this->get_value($name);
755
- $name = esc_attr($name);
756
- echo '<select name="options[', $name, ']">';
757
-
758
- foreach ($groups as $group) {
759
- echo '<optgroup label="' . esc_attr($group['']) . '">';
760
- if (!empty($group)) {
761
- foreach ($group as $key => $label) {
762
- if ($key == '') {
763
- continue;
764
- }
765
- echo '<option value="' . esc_attr($key) . '"';
766
- if ($value == $key) {
767
- echo ' selected';
768
- }
769
- echo '>' . esc_html($label) . '</option>';
770
- }
771
- }
772
- echo '</optgroup>';
773
- }
774
- echo '</select>';
775
- }
776
-
777
- /**
778
- * Generated a select control with all available templates. From version 3 there are
779
- * only on kind of templates, they are no more separated by type.
780
- */
781
- function themes($name, $themes, $submit_on_click = true) {
782
- foreach ($themes as $key => $data) {
783
- echo '<label style="display: block; float: left; text-align: center; margin-right: 10px;">';
784
- echo esc_html($key) . '<br>';
785
- echo '<img src="' . esc_attr($data['screenshot']) . '" width="100" height="100" style="border: 1px solid #666; padding: 5px"><br>';
786
- echo '<input style="position: relative; top: -40px" type="radio" onchange="this.form.act.value=\'theme\';this.form.submit()" name="options[' . esc_attr($name) . ']" value="' . esc_attr($key) . '"';
787
- if ($this->data[$name] == $key) {
788
- echo ' checked';
789
- }
790
- echo '>';
791
- echo '</label>';
792
- }
793
- echo '<div style="clear: both"></div>';
794
- }
795
-
796
- function value($name) {
797
- echo esc_html($this->data[$name]);
798
- }
799
-
800
- function value_date($name, $show_remaining = true) {
801
- $time = $this->get_value($name);
802
-
803
- echo gmdate(get_option('date_format') . ' ' . get_option('time_format'), $time + get_option('gmt_offset') * 3600);
804
- $delta = $time - time();
805
- if ($show_remaining && $delta > 0) {
806
- echo 'Remaining: ';
807
- $delta = $time - time();
808
- $days = floor($delta / (24 * 3600));
809
- $delta = $delta - $days * 24 * 3600;
810
- $hours = floor($delta / 3600);
811
- $delta = $delta - $hours * 3600;
812
- $minutes = floor($delta / 60);
813
-
814
- if ($days > 0) {
815
- echo $days . ' days ';
816
- }
817
- echo $hours . ' hours ';
818
- echo $minutes . ' minutes ';
819
- }
820
- }
821
-
822
- function password($name, $size = 20, $placeholder = '') {
823
- $value = $this->get_value($name);
824
- $name = esc_attr($name);
825
- echo '<input id="options-', $name, '" placeholder="' . esc_attr($placeholder) . '" name="options[', $name, ']" type="password" autocomplete="off" ';
826
- if (!empty($size)) {
827
- echo 'size="', $size, '" ';
828
- }
829
- echo 'value="', esc_attr($value), '">';
830
- }
831
-
832
- function text($name, $size = 20, $placeholder = '') {
833
- $value = $this->get_value($name);
834
- $name = esc_attr($name);
835
- echo '<input id="options-', $name, '" placeholder="' . esc_attr($placeholder) . '" title="' . esc_attr($placeholder) . '" name="options[', $name, ']" type="text" ';
836
- if (!empty($size)) {
837
- echo 'size="', esc_attr($size), '" ';
838
- }
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) {
852
- $value = $this->get_value($name);
853
- echo '<input name="options[' . esc_attr($name) . ']" type="url" placeholder="http://..." size="' . esc_attr($size) . '" value="';
854
- echo esc_attr($value);
855
- echo '"/>';
856
- }
857
-
858
- function hidden($name) {
859
- $value = $this->get_value($name);
860
- echo '<input name="options[', esc_attr($name), ']" id="options-', esc_attr($name), '" type="hidden" value="', esc_attr($value), '">';
861
- }
862
-
863
- /**
864
- * General button.
865
- *
866
- * @param type $action
867
- * @param type $label
868
- * @param type $attrs
869
- */
870
- function btn($action, $label, $attrs = []) {
871
- echo '<button class="button-primary tnpc-button"';
872
- if (isset($attrs['id'])) {
873
- echo ' id="', esc_attrs($attrs['id']), '"';
874
- }
875
- $onclick = "this.form.act.value='" . esc_attr(esc_js($action)) . "';";
876
- if (!empty($attrs['data'])) {
877
- $onclick .= "this.form.btn.value='" . esc_attr(esc_js($attrs['data'])) . "';";
878
- }
879
- if (isset($attrs['confirm'])) {
880
- if (is_string($attrs['confirm'])) {
881
- $onclick .= "if (!confirm('" . esc_attr(esc_js($attrs['confirm'])) . "')) return false;";
882
- } else if ($attrs['confirm'] === true) {
883
- $onclick .= "if (!confirm('" . esc_attr(esc_js(__('Proceed?', 'newsletter'))) . "')) return false;";
884
- }
885
- }
886
- echo 'onclick="', $onclick, '"';
887
- if (!empty($attrs['title'])) {
888
- echo ' title="', esc_attr($attrs['title']), '"';
889
- }
890
- if (!empty($attrs['style'])) {
891
- echo ' style="', esc_attr($attrs['style']), '"';
892
- }
893
- echo '>';
894
- if (!empty($attrs['icon'])) {
895
- echo '<i class="fas ', esc_attr($attrs['icon']), '"></i>';
896
- if (!empty($label)) {
897
- echo '&nbsp;', esc_html($label);
898
- }
899
- } else {
900
- echo esc_html($label);
901
- }
902
- echo '</button>';
903
- }
904
-
905
- function btn_link($url, $label, $attrs = []) {
906
- echo '<a href="', esc_attr($url), '" class="button-primary tnpc-button"';
907
- if (!empty($attrs['style'])) {
908
- echo ' style="', esc_attr($attrs['style']), '"';
909
- }
910
- if (!empty($attrs['title'])) {
911
- echo ' title="', esc_attr($attrs['title']), '"';
912
- }
913
- if (!empty($attrs['target'])) {
914
- echo ' target="', esc_attr($attrs['target']), '"';
915
- }
916
- echo '>';
917
- if (!empty($attrs['icon'])) {
918
- echo '<i class="fas ', esc_attr($attrs['icon']), '"></i>';
919
- if (!empty($label)) {
920
- echo '&nbsp;', esc_html($label);
921
- }
922
- } else {
923
- echo esc_html($label);
924
- }
925
- echo '</a>';
926
- }
927
-
928
- function button($action, $label, $function = '', $id = '') {
929
- $id = !empty($id) ? " id=\"$id\" " : '';
930
- if ($function != null) {
931
- echo '<input ' . $id . ' class="button-primary tnpc-button" type="button" value="' . esc_attr($label) . '" onclick="this.form.act.value=\'' . esc_attr($action) . '\';' . esc_html($function) . '"/>';
932
- } else {
933
- echo '<input ' . $id . ' class="button-primary tnpc-button" type="submit" value="' . esc_attr($label) . '" onclick="this.form.act.value=\'' . esc_attr($action) . '\';return true;"/>';
934
- }
935
- }
936
-
937
- function action_link($action, $label, $function = null) {
938
- if ($function != null) {
939
- echo '<input class="button-link tnpc-button" type="button" value="' . esc_attr($label) . '" onclick="this.form.act.value=\'' . esc_attr($action) . '\';' . esc_html($function) . '"/>';
940
- } else {
941
- echo '<input class="button-link tnpc-button" type="submit" value="' . esc_attr($label) . '" onclick="this.form.act.value=\'' . esc_attr($action) . '\';return true;"/>';
942
- }
943
- }
944
-
945
- function button_save() {
946
- $this->btn('save', __('Save', 'newsletter'), ['icon' => 'fa-save']);
947
- }
948
-
949
- function button_reset($action = 'reset') {
950
- $this->btn($action, __('Reset', 'newsletter'), ['icon' => 'fa-reply', 'confirm' => true]);
951
- }
952
-
953
- function button_copy($data = '') {
954
- $this->btn('copy', __('Duplicate', 'newsletter'), ['data' => $data, 'icon' => 'fa-copy', 'confirm' => true]);
955
- }
956
-
957
- function button_icon_copy($data = '') {
958
- $this->btn('copy', '', ['data' => $data, 'icon' => 'fa-copy', 'confirm' => true, 'title' => __('Duplicate', 'newsletter')]);
959
- }
960
-
961
- /**
962
- * Creates a button with "delete" action.
963
- * @param type $data
964
- */
965
- function button_delete($data = '') {
966
- $this->btn('delete', __('Delete', 'newsletter'), ['data' => $data, 'icon' => 'fa-times', 'confirm' => true, 'style' => 'background-color: darkred; color: #ffffff']);
967
- }
968
-
969
- function button_icon_delete($data = '') {
970
- $this->btn('delete', '', ['data' => $data, 'icon' => 'fa-times', 'confirm' => true, 'title' => __('Delete', 'newsletter'), 'style' => 'background-color: darkred; color: #ffffff']);
971
- }
972
-
973
- function button_icon_configure($url) {
974
- $this->btn_link($url, '', ['icon' => 'fa-cog', 'title' => __('Configure', 'newsletter')]);
975
- }
976
-
977
- function button_icon_subscribers($url) {
978
- $this->btn_link($url, '', ['icon' => 'fa-users', 'title' => __('Subscribers', 'newsletter')]);
979
- }
980
-
981
- function button_statistics($url) {
982
- $this->btn_link($url, __('Statistics', 'newsletter'), ['icon' => 'fa-chart-bar']);
983
- }
984
-
985
- function button_icon_statistics($url) {
986
- $this->btn_link($url, '', ['icon' => 'fa-chart-bar', 'title' => __('Statistics', 'newsletter')]);
987
- }
988
-
989
- function button_icon_view($url) {
990
- $this->btn_link($url, '', ['icon' => 'fa-eye', 'title' => __('View', 'newsletter'), 'target' => '_blank']);
991
- }
992
-
993
- function button_icon_newsletters($url) {
994
- $this->btn_link($url, '', ['icon' => 'fa-file-alt', 'title' => __('Newsletters', 'newsletter')]);
995
- }
996
-
997
- function button_icon_design($url) {
998
- $this->btn_link($url, '', ['icon' => 'fa-paint-brush', 'title' => __('Design', 'newsletter')]);
999
- }
1000
-
1001
- function button_icon_edit($url) {
1002
- $this->btn_link($url, '', ['icon' => 'fa-edit', 'title' => __('Edit', 'newsletter')]);
1003
- }
1004
-
1005
- function button_icon_back($url) {
1006
- $this->btn_link($url, '', ['icon' => 'fa-chevron-left', 'title' => __('Back', 'newsletter')]);
1007
- }
1008
-
1009
- function button_icon($action, $icon, $title = '', $data = '', $confirm = false) {
1010
- $this->btn($action, '', ['data' => $data, 'icon' => $icon, 'title' => $title, 'confirm' => $confirm]);
1011
- }
1012
-
1013
- function button_back($url) {
1014
- $this->btn_link($url, __('Back', 'newsletter'), ['icon' => 'fa-chevron-left']);
1015
- }
1016
-
1017
- function button_test($action = 'test') {
1018
- $this->btn($action, __('Test', 'newsletter'), ['icon' => 'fa-vial']);
1019
- }
1020
-
1021
- function button_primary($action, $label, $function = null) {
1022
- if ($function != null) {
1023
- echo '<button class="button-primary" onclick="this.form.act.value=\'' . esc_attr($action) . '\';' . esc_attr($function) . '">', $label, '</button>';
1024
- } else {
1025
- echo '<button class="button-primary" onclick="this.form.act.value=\'' . esc_attr($action) . '\'; return true;"/>', $label, '</button>';
1026
- }
1027
- }
1028
-
1029
- function button_confirm($action, $label, $message = '', $data = '') {
1030
- $this->btn($action, $label, ['data' => $data, 'confirm' => $message]);
1031
- }
1032
-
1033
- /**
1034
- * @deprecated
1035
- * @param string $url
1036
- * @param string $label Not escaped.
1037
- */
1038
- function button_link($url, $label = '') {
1039
- echo '<a href="', esc_attr($url), '" class="button-primary">', $label, '</a>';
1040
- }
1041
-
1042
- function editor($name, $rows = 5, $cols = 75) {
1043
- echo '<textarea class="visual" name="options[' . esc_attr($name) . ']" style="width: 100%" wrap="off" rows="' . esc_attr($rows) . '">';
1044
- echo esc_html($this->get_value($name));
1045
- echo '</textarea>';
1046
- }
1047
-
1048
- function wp_editor($name, $settings = array()) {
1049
-
1050
- add_filter('mce_buttons', function ($mce_buttons) {
1051
- $mce_buttons[] = 'wp_add_media';
1052
- //$mce_buttons[] = 'wp_code';
1053
- return $mce_buttons;
1054
- });
1055
-
1056
- $settings = array_merge(['media_buttons' => false], $settings);
1057
-
1058
- $value = $this->get_value($name);
1059
- wp_editor($value, $name, array_merge(array(
1060
- 'tinymce' => array('content_css' => plugins_url('newsletter') . '/css/wp-editor.css?ver=' . filemtime(NEWSLETTER_DIR . '/css/wp-editor.css')),
1061
- 'textarea_name' => 'options[' . esc_attr($name) . ']',
1062
- 'wpautop' => false
1063
- ), $settings));
1064
- //echo '<p class="description">You can install <a href="https://wordpress.org/plugins/tinymce-advanced/" target="_blank">TinyMCE Advanced</a> for advanced editing features</p>';
1065
- }
1066
-
1067
- function textarea($name, $width = '100%', $height = '50') {
1068
- $value = $this->get_value($name);
1069
- if (is_array($value)) {
1070
- $value = implode("\n", $value);
1071
- }
1072
- echo '<textarea id="options-' . esc_attr($name) . '" class="dynamic" name="options[' . esc_attr($name) . ']" wrap="off" style="width:' . esc_attr($width) . ';height:' . esc_attr($height) . '">';
1073
- echo esc_html($value);
1074
- echo '</textarea>';
1075
- }
1076
-
1077
- function textarea_fixed($name, $width = '100%', $height = '200') {
1078
- $value = $this->get_value($name);
1079
- $name = esc_attr($name);
1080
- echo '<textarea id="options-', $name, '" name="options[', $name, ']" wrap="off" style="width:', esc_attr($width), ';height:', esc_attr($height), 'px">';
1081
- echo esc_html($value);
1082
- echo '</textarea>';
1083
- }
1084
-
1085
- function textarea_preview($name, $width = '100%', $height = '200', $header = '', $footer = '', $switch_button = true) {
1086
- $value = $this->get_value($name);
1087
- $name = esc_attr($name);
1088
- if ($switch_button) {
1089
- echo '<input class="button-primary" type="button" onclick="newsletter_textarea_preview(\'options-', $name, '\', \'\', \'\')" value="Switch editor/preview">';
1090
- echo '<br><br>';
1091
- }
1092
- echo '<div style="box-sizing: border-box; position: relative; margin: 0; padding: 0; width:' . esc_attr($width) . '; height:' . esc_attr($height) . '">';
1093
- echo '<textarea id="options-', $name, '" name="options[', $name, ']" wrap="off" style="width:' . esc_attr($width) . ';height:' . esc_attr($height) . 'px">';
1094
- echo esc_html($value);
1095
- echo '</textarea>';
1096
- echo '<div id="options-', $name, '-preview" style="box-sizing: border-box; background-color: #eee; border: 1px solid #bbb; padding: 15px; width: auto; position: absolute; top: 20px; left: 20px; box-shadow: 0 0 20px #777; z-index: 10000; display: none">';
1097
- echo '<iframe id="options-', $name, '-iframe" class="tnp-editor-preview-desktop"></iframe>';
1098
- echo '<iframe id="options-', $name, '-iframe-phone" class="tnp-editor-preview-mobile"></iframe>';
1099
- echo '</div>';
1100
- echo '</div>';
1101
- }
1102
-
1103
- function email($prefix, $editor = null, $disable_option = false, $settings = array()) {
1104
- if ($disable_option) {
1105
- $this->disabled($prefix . '_disabled');
1106
- echo '<br>';
1107
- }
1108
-
1109
- $this->text($prefix . '_subject', 90, 'Subject');
1110
- echo '<br><br>';
1111
-
1112
- if ($editor == 'wordpress') {
1113
- $this->wp_editor($prefix . '_message', $settings);
1114
- } else if ($editor == 'textarea') {
1115
- $this->textarea($prefix . '_message');
1116
- } else {
1117
- $this->editor($prefix . '_message');
1118
- }
1119
- }
1120
-
1121
- function checkbox($name, $label = '') {
1122
- if ($label != '') {
1123
- echo '<label>';
1124
- }
1125
- echo '<input type="checkbox" id="options-' . esc_attr($name) . '" name="options[' . esc_attr($name) . ']" value="1"';
1126
- if (!empty($this->data[$name])) {
1127
- echo ' checked';
1128
- }
1129
- echo '>';
1130
- if ($label != '') {
1131
- echo '&nbsp;' . esc_html($label) . '</label>';
1132
- }
1133
- }
1134
-
1135
- function checkbox2($name, $label = '') {
1136
- if ($label != '') {
1137
- echo '<label>';
1138
- }
1139
- echo '<input type="checkbox" id="' . esc_attr($name) . '" onchange="document.getElementById(\'' . esc_attr($name) . '_hidden\').value=this.checked?\'1\':\'0\'"';
1140
- if (!empty($this->data[$name])) {
1141
- echo ' checked="checked"';
1142
- }
1143
- echo '>';
1144
- if ($label != '') {
1145
- echo '&nbsp;' . esc_html($label) . '</label>';
1146
- }
1147
- echo '<input type="hidden" id="' . esc_attr($name) . '_hidden" name="options[' . esc_attr($name) . ']" value="';
1148
-
1149
- echo empty($this->data[$name]) ? '0' : '1';
1150
- echo '">';
1151
- }
1152
-
1153
- function radio($name, $value, $label = '') {
1154
- if ($label != '') {
1155
- echo '<label>';
1156
- }
1157
- echo '<input type="radio" id="' . esc_attr($name) . '" name="options[' . esc_attr($name) . ']" value="' . esc_attr($value) . '"';
1158
- $v = $this->get_value($name);
1159
- if ($v == $value) {
1160
- echo ' checked';
1161
- }
1162
- echo '>';
1163
- if ($label != '') {
1164
- echo '&nbsp;' . esc_html($label) . '</label>';
1165
- }
1166
- }
1167
-
1168
- /**
1169
- * Creates a checkbox named $name and checked if the internal data contains under
1170
- * the key $name an array containig the passed value.
1171
- */
1172
- function checkbox_group($name, $value, $label = '', $attrs = []) {
1173
- $attrs = wp_parse_args($attrs, ['label_escape' => true]);
1174
- echo '<label><input type="checkbox" id="' . esc_attr($name) . '" name="options[' . esc_attr($name) . '][]" value="' . esc_attr($value) . '"';
1175
- if (isset($this->data[$name]) && is_array($this->data[$name]) && array_search($value, $this->data[$name]) !== false) {
1176
- echo ' checked';
1177
- }
1178
- echo '>';
1179
- if ($label != '') {
1180
- if ($attrs['label_escape']) {
1181
- echo esc_html($label);
1182
- } else {
1183
- echo $label;
1184
- }
1185
- }
1186
- echo '</label>';
1187
- }
1188
-
1189
- function checkboxes($name, $options) {
1190
- echo '<div class="tnpc-checkboxes">';
1191
- foreach ($options as $value => $label) {
1192
- $this->checkbox_group($name, $value, $label);
1193
- }
1194
- echo '<div style="clear: both"></div>';
1195
- echo '</div>';
1196
- }
1197
-
1198
- function color($name, $default = '') {
1199
- $value = esc_attr($this->get_value($name, $default));
1200
- $name = esc_attr($name);
1201
- echo '<input class="tnpc-color" id="options-', $name, '" name="options[', $name, ']" type="text" value="', $value, '">';
1202
- }
1203
-
1204
- /** Creates a set of checkbox named $name_[category id] (so they are posted with distinct names).
1205
- */
1206
- function categories($name = 'category') {
1207
- $categories = get_categories();
1208
- echo '<div class="tnpc-checkboxes">';
1209
- foreach ($categories as $c) {
1210
- $this->checkbox($name . '_' . $c->cat_ID, esc_html($c->cat_name));
1211
- }
1212
- echo '<div style="clear: both"></div>';
1213
- }
1214
-
1215
- /**
1216
- * Creates a set of checkbox to activate the profile preferences. Every checkbox has a DIV around to
1217
- * be formatted.
1218
- */
1219
- function categories_group($name, $show_mode = false) {
1220
- $categories = get_categories();
1221
- if ($show_mode) {
1222
- $this->select($name . '_mode', array('include' => 'To be included', 'exclude' => 'To be excluded'));
1223
- }
1224
- echo '<div class="tnpc-checkboxes">';
1225
- foreach ($categories as &$c) {
1226
- $this->checkbox_group($name, $c->cat_ID, esc_html($c->cat_name));
1227
- }
1228
- echo '<div style="clear: both"></div>';
1229
- }
1230
-
1231
- /**
1232
- * Creates a set of checkboxes named $name_[preference number] (so they are
1233
- * distinct fields).
1234
- * Empty preferences are skipped.
1235
- */
1236
- function preferences($name = 'preferences') {
1237
- $lists = Newsletter::instance()->get_lists();
1238
-
1239
- echo '<div class="tnpc-checkboxes">';
1240
- foreach ($lists as $list) {
1241
- $this->checkbox2($name . '_' . $list->id, esc_html($list->name));
1242
- }
1243
- echo '<div style="clear: both"></div>';
1244
- }
1245
-
1246
- /** A list of all lists defined each one with a checkbox to select it. An array
1247
- * of ID of all checked lists is submitted.
1248
- *
1249
- * @param string $name
1250
- */
1251
- function lists($name = 'lists') {
1252
- echo '<input type="hidden" name="tnp_fields[' . esc_attr($name) . ']" value="array">';
1253
- $this->preferences_group($name);
1254
- }
1255
-
1256
- function lists_checkboxes($name = 'lists') {
1257
- $this->preferences_group($name);
1258
- }
1259
-
1260
- /**
1261
- * Creates a set of checkboxes all names $name[] and the preference number as value
1262
- * so the selected checkboxes are retrieved as an array of values ($REQUEST[$name]
1263
- * will be an array if at east one preference is checked).
1264
- */
1265
- function preferences_group($name = 'preferences') {
1266
-
1267
- $lists = Newsletter::instance()->get_lists();
1268
-
1269
- echo '<div class="tnpc-lists">';
1270
- foreach ($lists as $list) {
1271
- $this->checkbox_group($name, $list->id, '<span>' . $list->id . '</span> ' . esc_html($list->name), ['label_escape' => false]);
1272
- }
1273
- echo '<a href="https://www.thenewsletterplugin.com/documentation/newsletter-lists" target="_blank">'
1274
- . 'Click here to read more about lists.'
1275
- . '</a>';
1276
- echo '</div>';
1277
- }
1278
-
1279
- /** Creates as many selects as the active preferences with the three values
1280
- * 'any', 'yes', 'no' corresponding to the values 0, 1, 2.
1281
- */
1282
- function preferences_selects($name = 'preferences', $skip_empty = false) {
1283
- $lists = Newsletter::instance()->get_lists();
1284
-
1285
- echo '<div class="newsletter-preferences-group">';
1286
- foreach ($lists as $list) {
1287
-
1288
- echo '<div class="newsletter-preferences-item">';
1289
-
1290
- $this->select($name . '_' . $list->id, array(0 => 'Any', 1 => 'Yes', 2 => 'No'));
1291
- echo '(' . $list->id . ') ' . esc_html($list->name);
1292
-
1293
- echo '</div>';
1294
- }
1295
- echo '<div style="clear: both"></div>';
1296
- echo '<a href="https://www.thenewsletterplugin.com/plugins/newsletter/newsletter-preferences" target="_blank">Click here know more about preferences.</a> They can be configured on Subscription/Form field panel.';
1297
- echo '</div>';
1298
- }
1299
-
1300
- /**
1301
- * Creates a single select with the active preferences.
1302
- */
1303
- function preferences_select($name = 'preference', $empty_label = null) {
1304
- $lists = $this->get_list_options($empty_label);
1305
- $this->select($name, $lists);
1306
- echo ' <a href="admin.php?page=newsletter_subscription_lists" target="_blank"><i class="fas fa-edit"></i></a>';
1307
- }
1308
-
1309
- function lists_select($name = 'list', $empty_label = null) {
1310
- $lists = $this->get_list_options($empty_label);
1311
- $this->select($name, $lists);
1312
- }
1313
-
1314
- function lists_select_with_notes($name = 'list', $empty_label = null) {
1315
-
1316
- $value = $this->get_value($name);
1317
-
1318
- $lists = Newsletter::instance()->get_lists();
1319
- $options = [];
1320
- if ($empty_label) {
1321
- $options[''] = $empty_label;
1322
- }
1323
-
1324
- foreach ($lists as $list) {
1325
- $options['' . $list->id] = '(' . $list->id . ') ' . esc_html($list->name);
1326
- }
1327
-
1328
- $this->select($name, $options, null, ['onchange' => 'tnp_lists_toggle(this); return true;']);
1329
- echo '<div id="options-', esc_attr($name), '-notes" class="tnpc_lists_notes">';
1330
- foreach ($lists as $list) {
1331
- $id = $list->id;
1332
- $notes = apply_filters('newsletter_lists_notes', [], $id);
1333
-
1334
- echo '<div class="list_', $id, '" style="display: ', ($value == $id ? 'block' : 'none'), '">';
1335
- if ($list->forced) {
1336
- echo 'Enforced on subscription<br>';
1337
- }
1338
- echo implode('<br>', $notes);
1339
- echo '</div>';
1340
- }
1341
- echo '</div>';
1342
- }
1343
-
1344
- function public_lists_select($name = 'list', $empty_label = null) {
1345
- $lists = $this->get_public_list_options($empty_label);
1346
- $this->select($name, $lists);
1347
- }
1348
-
1349
- /**
1350
- * Generates an associative array with the active lists to be used in a select.
1351
- * @param string $empty_label
1352
- * @return array
1353
- */
1354
- function get_list_options($empty_label = null) {
1355
- $objs = Newsletter::instance()->get_lists();
1356
- $lists = array();
1357
- if ($empty_label) {
1358
- $lists[''] = $empty_label;
1359
- }
1360
- foreach ($objs as $list) {
1361
- $lists['' . $list->id] = '(' . $list->id . ') ' . esc_html($list->name);
1362
- }
1363
- return $lists;
1364
- }
1365
-
1366
- function get_public_list_options($empty_label = null) {
1367
- $objs = Newsletter::instance()->get_lists_public();
1368
- $lists = array();
1369
- if ($empty_label) {
1370
- $lists[''] = $empty_label;
1371
- }
1372
- foreach ($objs as $list) {
1373
- $lists['' . $list->id] = '(' . $list->id . ') ' . esc_html($list->name);
1374
- }
1375
- return $lists;
1376
- }
1377
-
1378
- function date($name) {
1379
- $this->hidden($name);
1380
- $year = date('Y', $this->data[$name]);
1381
- $day = date('j', $this->data[$name]);
1382
- $month = date('m', $this->data[$name]);
1383
- $onchange = "this.form.elements['options[" . esc_attr($name) . "]'].value = new Date(document.getElementById('" . esc_attr($name) . "_year').value, document.getElementById('" . esc_attr($name) . "_month').value, document.getElementById('" . esc_attr($name) . "_day').value, 12, 0, 0).getTime()/1000";
1384
- echo '<select id="' . $name . '_month" onchange="' . esc_attr($onchange) . '">';
1385
- for ($i = 0; $i < 12; $i++) {
1386
- echo '<option value="' . $i . '"';
1387
- if ($month - 1 == $i) {
1388
- echo ' selected';
1389
- }
1390
- echo '>' . date('F', mktime(0, 0, 0, $i + 1, 1, 2000)) . '</option>';
1391
- }
1392
- echo '</select>';
1393
-
1394
- echo '<select id="' . esc_attr($name) . '_day" onchange="' . esc_attr($onchange) . '">';
1395
- for ($i = 1; $i <= 31; $i++) {
1396
- echo '<option value="' . $i . '"';
1397
- if ($day == $i) {
1398
- echo ' selected';
1399
- }
1400
- echo '>' . $i . '</option>';
1401
- }
1402
- echo '</select>';
1403
-
1404
- echo '<select id="' . esc_attr($name) . '_year" onchange="' . esc_attr($onchange) . '">';
1405
- for ($i = 2011; $i <= 2021; $i++) {
1406
- echo '<option value="' . $i . '"';
1407
- if ($year == $i) {
1408
- echo ' selected';
1409
- }
1410
- echo '>' . $i . '</option>';
1411
- }
1412
- echo '</select>';
1413
- }
1414
-
1415
- /**
1416
- * Creates a set of fields to collect a date and sends back the triplet year, month and day.
1417
- *
1418
- * @param string $name
1419
- */
1420
- function date2($name) {
1421
- $year = $this->get_value($name . '_year');
1422
- $day = $this->get_value($name . '_day');
1423
- $month = $this->get_value($name . '_month');
1424
-
1425
- echo '<select name="options[' . $name . '_month]">';
1426
- echo '<option value="">-</option>';
1427
- for ($i = 1; $i <= 12; $i++) {
1428
- echo '<option value="' . $i . '"';
1429
- if ($month == $i) {
1430
- echo ' selected';
1431
- }
1432
- echo '>' . date_i18n('F', mktime(0, 0, 0, $i, 1, 2000)) . '</option>';
1433
- }
1434
- echo '</select>';
1435
-
1436
- echo '<select name="options[' . esc_attr($name) . '_day]">';
1437
- echo '<option value="">-</option>';
1438
- for ($i = 1; $i <= 31; $i++) {
1439
- echo '<option value="' . $i . '"';
1440
- if ($day == $i) {
1441
- echo ' selected';
1442
- }
1443
- echo '>' . $i . '</option>';
1444
- }
1445
- echo '</select>';
1446
-
1447
- echo '<select name="options[' . esc_attr($name) . '_year]">';
1448
- echo '<option value="">-</option>';
1449
- for ($i = 2011; $i <= 2021; $i++) {
1450
- echo '<option value="' . $i . '"';
1451
- if ($year == $i) {
1452
- echo ' selected';
1453
- }
1454
- echo '>' . $i . '</option>';
1455
- }
1456
- echo '</select>';
1457
- }
1458
-
1459
- /**
1460
- * Date and time (hour) selector. Timestamp stored.
1461
- */
1462
- function datetime($name) {
1463
- echo '<input type="hidden" name="tnp_fields[' . esc_attr($name) . ']" value="datetime">';
1464
- $value = (int) $this->get_value($name);
1465
- if (empty($value)) {
1466
- $value = time();
1467
- }
1468
-
1469
- $time = $value + get_option('gmt_offset') * 3600;
1470
- $year = gmdate('Y', $time);
1471
- $day = gmdate('j', $time);
1472
- $month = gmdate('m', $time);
1473
- $hour = gmdate('H', $time);
1474
-
1475
- echo '<select name="' . esc_attr($name) . '_month">';
1476
- for ($i = 1; $i <= 12; $i++) {
1477
- echo '<option value="' . $i . '"';
1478
- if ($month == $i) {
1479
- echo ' selected';
1480
- }
1481
- echo '>' . date('F', mktime(0, 0, 0, $i, 1, 2000)) . '</option>';
1482
- }
1483
- echo '</select>';
1484
-
1485
- echo '<select name="' . esc_attr($name) . '_day">';
1486
- for ($i = 1; $i <= 31; $i++) {
1487
- echo '<option value="' . $i . '"';
1488
- if ($day == $i) {
1489
- echo ' selected';
1490
- }
1491
- echo '>' . $i . '</option>';
1492
- }
1493
- echo '</select>';
1494
-
1495
- $last_year = date('Y') + 2;
1496
- echo '<select name="' . esc_attr($name) . '_year">';
1497
- for ($i = 2011; $i <= $last_year; $i++) {
1498
- echo '<option value="' . $i . '"';
1499
- if ($year == $i) {
1500
- echo ' selected';
1501
- }
1502
- echo '>' . $i . '</option>';
1503
- }
1504
- echo '</select>';
1505
-
1506
- echo '<select name="' . esc_attr($name) . '_hour">';
1507
- for ($i = 0; $i <= 23; $i++) {
1508
- echo '<option value="' . $i . '"';
1509
- if ($hour == $i) {
1510
- echo ' selected';
1511
- }
1512
- echo '>' . $i . ':00</option>';
1513
- }
1514
- echo '</select>';
1515
- }
1516
-
1517
- function hours($name) {
1518
- $hours = array();
1519
- for ($i = 0; $i < 24; $i++) {
1520
- $hours['' . $i] = sprintf('%02d', $i) . ':00';
1521
- }
1522
- $this->select($name, $hours);
1523
- }
1524
-
1525
- function days($name) {
1526
- $days = array(0 => 'Every day', 1 => 'Monday', 2 => 'Tuesday', 3 => 'Wednesday', 4 => 'Thursday', 5 => 'Friday', 6 => 'Saturday', 7 => 'Sunday');
1527
- $this->select($name, $days);
1528
- }
1529
-
1530
- function init($options = array()) {
1531
- $cookie_name = 'newsletter_tab';
1532
- if (isset($options['cookie_name'])) {
1533
- $cookie_name = $options['cookie_name'];
1534
- }
1535
- echo '<script type="text/javascript">
1536
- jQuery(document).ready(function(){
1537
-
1538
- tnp_controls_init();
1539
-
1540
- jQuery("textarea.dynamic").focus(function() {
1541
- jQuery("textarea.dynamic").css("height", "50px");
1542
- jQuery(this).css("height", "400px");
1543
- });
1544
- tabs = jQuery("#tabs").tabs({
1545
- active : jQuery.cookie("' . $cookie_name . '"),
1546
- activate : function( event, ui ){
1547
- jQuery.cookie("' . $cookie_name . '", ui.newTab.index(),{expires: 1});
1548
- }
1549
- });
1550
- jQuery(".tnp-tabs").tabs({});
1551
-
1552
- });
1553
- function newsletter_media(name) {
1554
- var tnp_uploader = wp.media({
1555
- title: "Select an image",
1556
- button: {
1557
- text: "Select"
1558
- },
1559
- multiple: false
1560
- }).on("select", function() {
1561
- var media = tnp_uploader.state().get("selection").first();
1562
- document.getElementById(name + "_id").value = media.id;
1563
- jQuery("#" + name + "_id").trigger("change");
1564
- //alert(media.attributes.url);
1565
- if (media.attributes.url.substring(0, 0) == "/") {
1566
- media.attributes.url = "' . site_url('/') . '" + media.attributes.url;
1567
- }
1568
- document.getElementById(name + "_url").value = media.attributes.url;
1569
-
1570
- var img_url = media.attributes.url;
1571
- if (typeof media.attributes.sizes.medium !== "undefined") img_url = media.attributes.sizes.medium.url;
1572
- if (img_url.substring(0, 0) == "/") {
1573
- img_url = "' . site_url('/') . '" + img_url;
1574
- }
1575
- document.getElementById(name + "_img").src = img_url;
1576
- }).open();
1577
- }
1578
- function newsletter_media_remove(name) {
1579
- if (confirm("Are you sure?")) {
1580
- document.getElementById(name + "_id").value = "";
1581
- document.getElementById(name + "_url").value = "";
1582
- document.getElementById(name + "_img").src = "' . plugins_url('newsletter') . '/images/nomedia.png";
1583
- }
1584
- }
1585
- function newsletter_textarea_preview(id, header, footer) {
1586
- var d = document.getElementById(id + "-iframe").contentWindow.document;
1587
- d.open();
1588
- if (templateEditor) {
1589
- d.write(templateEditor.getValue());
1590
- } else {
1591
- d.write(header + document.getElementById(id).value + footer);
1592
- }
1593
- d.close();
1594
-
1595
- var d = document.getElementById(id + "-iframe-phone").contentWindow.document;
1596
- d.open();
1597
- if (templateEditor) {
1598
- d.write(templateEditor.getValue());
1599
- } else {
1600
- d.write(header + document.getElementById(id).value + footer);
1601
- }
1602
- d.close();
1603
- //jQuery("#" + id + "-iframe-phone").toggle();
1604
- jQuery("#" + id + "-preview").toggle();
1605
- }
1606
- function tnp_select_images(state) {
1607
- if (!state.id) { return state.text; }
1608
- var $state = jQuery("<span class=\"tnp-select2-option\"><img style=\"height: 20px!important; position: relative; top: 5px\" src=\"" + state.element.getAttribute("image") + "\"> " + state.text + "</span>");
1609
- return $state;
1610
- }
1611
- function tnp_select_images_selection(state) {
1612
- if (!state.id) { return state.text; }
1613
- var $state = jQuery("<span class=\"tnp-select2-option\"><img style=\"height: 20px!important; position: relative; top: 5px\" src=\"" + state.element.getAttribute("image") + "\"> " + state.text + "</span>");
1614
- return $state;
1615
- }
1616
-
1617
-
1618
- </script>
1619
- ';
1620
- echo '<input name="act" type="hidden" value=""/>';
1621
- echo '<input name="btn" type="hidden" value=""/>';
1622
- wp_nonce_field('save');
1623
- }
1624
-
1625
- function log_level($name = 'log_level') {
1626
- $this->select($name, array(0 => 'None', 2 => 'Error', 3 => 'Normal', 4 => 'Debug'));
1627
- }
1628
-
1629
- function update_option($name, $data = null) {
1630
- if ($data == null) {
1631
- $data = $this->data;
1632
- }
1633
- update_option($name, $data);
1634
- if (isset($data['log_level'])) {
1635
- update_option($name . '_log_level', $data['log_level']);
1636
- }
1637
- }
1638
-
1639
- function js_redirect($url) {
1640
- echo '<script>';
1641
- echo 'location.href="' . $url . '"';
1642
- echo '</script>';
1643
- die();
1644
- }
1645
-
1646
- /**
1647
- * @deprecated
1648
- */
1649
- function get_test_subscribers() {
1650
- return NewsletterUsers::instance()->get_test_users();
1651
- }
1652
-
1653
- /**
1654
- * Attributes:
1655
- * weight: [true|false]
1656
- * color: [true|false]
1657
- *
1658
- * @param string $name
1659
- * @param array $attrs
1660
- */
1661
- function css_font($name = 'font', $attrs = array()) {
1662
- $default = [
1663
- 'color' => true,
1664
- 'weight' => true,
1665
- 'hide_size' => false,
1666
- 'hide_weight' => false,
1667
- 'hide_color' => false,
1668
- ];
1669
- $attrs = array_merge($default, $attrs);
1670
- $this->css_font_family($name . '_family', !empty($attrs['family_default']));
1671
- if (!$attrs['hide_size']) {
1672
- $this->css_font_size($name . '_size', !empty($attrs['size_default']));
1673
- }
1674
- if ($attrs['weight'] && !$attrs['hide_weight']) {
1675
- $this->css_font_weight($name . '_weight', !empty($attrs['weight_default']));
1676
- }
1677
- if ($attrs['color'] && !$attrs['hide_color']) {
1678
- $this->color($name . '_color');
1679
- }
1680
- }
1681
-
1682
- function css_font_size($name = 'font_size', $show_empty_option = false) {
1683
- $value = $this->get_value($name);
1684
-
1685
- echo '<select class="tnpf-font-size" id="options-', esc_attr($name), '" name="options[', esc_attr($name), ']">';
1686
- if ($show_empty_option) {
1687
- echo "<option value=''>-</option>";
1688
- }
1689
- for ($i = 8; $i <= 50; $i++) {
1690
- echo '<option value="' . $i . '"';
1691
- if ($value == $i) {
1692
- echo ' selected';
1693
- }
1694
- echo '>' . $i . '</option>';
1695
- }
1696
- echo '</select>';
1697
- }
1698
-
1699
- function css_font_weight($name = 'font_weight', $show_empty_option = false) {
1700
- $value = $this->get_value($name);
1701
-
1702
- $fonts = array('normal' => 'Normal', 'bold' => 'Bold');
1703
-
1704
- echo '<select class="tnpf-font-weight" id="options-' . esc_attr($name) . '" name="options[' . esc_attr($name) . ']">';
1705
- if ($show_empty_option) {
1706
- echo "<option value=''>-</option>";
1707
- }
1708
- foreach ($fonts as $key => $font) {
1709
- echo '<option value="', esc_attr($key), '"';
1710
- if ($value == $key) {
1711
- echo ' selected';
1712
- }
1713
- echo '>', esc_html($font), '</option>';
1714
- }
1715
- echo '</select>';
1716
- }
1717
-
1718
- function css_font_family($name = 'font_family', $show_empty_option = false) {
1719
- $value = $this->get_value($name);
1720
-
1721
- $fonts = [];
1722
- if ($show_empty_option) {
1723
- $fonts[''] = 'Default';
1724
- }
1725
-
1726
- $fonts = array_merge($fonts, ['Helvetica, Arial, sans-serif' => 'Helvetica, Arial',
1727
- 'Arial Black, Gadget, sans-serif' => 'Arial Black, Gadget',
1728
- 'Garamond, serif' => 'Garamond',
1729
- 'Courier, monospace' => 'Courier',
1730
- 'Comic Sans MS, cursive' => 'Comic Sans MS',
1731
- 'Impact, Charcoal, sans-serif' => 'Impact, Charcoal',
1732
- 'Tahoma, Geneva, sans-serif' => 'Tahoma, Geneva',
1733
- 'Times New Roman, Times, serif' => 'Times New Roman',
1734
- 'Verdana, Geneva, sans-serif' => 'Verdana, Geneva']);
1735
-
1736
- echo '<select class="tnpf-font-family" id="options-', esc_attr($name), '" name="options[', esc_attr($name), ']">';
1737
- foreach ($fonts as $font => $label) {
1738
- echo '<option value="', esc_attr($font), '"';
1739
- if ($value == $font) {
1740
- echo ' selected';
1741
- }
1742
- echo '>', esc_html($label), '</option>';
1743
- }
1744
- echo '</select>';
1745
- }
1746
-
1747
- function css_text_align($name) {
1748
- $options = array('left' => __('Left', 'newsletter'), 'right' => __('Right', 'newsletter'),
1749
- 'center' => __('Center', 'newsletter'));
1750
- $this->select($name, $options);
1751
- }
1752
-
1753
- function css_border($name) {
1754
- $value = $this->get_value($name . '_width');
1755
-
1756
- echo 'width&nbsp;<select id="options-' . esc_attr($name) . '-width" name="options[' . esc_attr($name) . '_width]">';
1757
- for ($i = 0; $i < 10; $i++) {
1758
- echo '<option value="' . $i . '"';
1759
- if ($value == $i) {
1760
- echo ' selected';
1761
- }
1762
- echo '>' . $i . '</option>';
1763
- }
1764
- echo '</select>&nbsp;px&nbsp;&nbsp;';
1765
-
1766
- $this->select($name . '_type', array('solid' => 'Solid', 'dashed' => 'Dashed'));
1767
-
1768
- $this->color($name . '_color');
1769
-
1770
- $value = $this->get_value($name . '_radius');
1771
-
1772
- echo '&nbsp;&nbsp;radius&nbsp;<select id="options-' . esc_attr($name) . '-radius" name="options[' . esc_attr($name) . '_radius]">';
1773
- for ($i = 0; $i < 10; $i++) {
1774
- echo '<option value="' . $i . '"';
1775
- if ($value == $i) {
1776
- echo ' selected';
1777
- }
1778
- echo '>' . $i . '</option>';
1779
- }
1780
- echo '</select>&nbsp;px';
1781
- }
1782
-
1783
- /**
1784
- * Media selector using the media library of WP. Produces a field which values is an array containing 'id' and 'url'.
1785
- *
1786
- * @param string $name
1787
- */
1788
- function media($name) {
1789
- if (isset($this->data[$name]['id'])) {
1790
- $media_id = (int) $this->data[$name]['id'];
1791
- $media = wp_get_attachment_image_src($media_id, 'medium');
1792
- $media_full = wp_get_attachment_image_src($media_id, 'full');
1793
- } else {
1794
- $media = false;
1795
- }
1796
- echo '<div style="position: relative">';
1797
- echo '<a style="position: absolute; top: 5px; right: 5px; background-color: none; color: #000; padding: 0px 5px 6px 5px; font-size: 24px; display: block; text-decoration: none" href="#" onclick="newsletter_media_remove(\'' . esc_attr($name) . '\'); return false">&times;</a>';
1798
- if ($media === false) {
1799
- $media = array('', '', '');
1800
- $media_full = array('', '', '');
1801
- $media_id = 0;
1802
- echo '<img style="max-width: 200px; max-height: 150px; width: 100px;" id="' . esc_attr($name) . '_img" src="' . plugins_url('newsletter') . '/images/nomedia.png" onclick="newsletter_media(\'' . esc_attr($name) . '\')">';
1803
- } else {
1804
- echo '<img style="max-width: 200px; max-height: 150px;" id="' . esc_attr($name) . '_img" src="' . esc_attr($media[0]) . '" onclick="newsletter_media(\'' . esc_attr($name) . '\')">';
1805
- }
1806
-
1807
- echo '</div>';
1808
- echo '<input type="hidden" id="' . esc_attr($name) . '_id" name="options[' . esc_attr($name) . '][id]" value="' . esc_attr($media_id) . '" size="5">';
1809
- echo '<input type="hidden" id="' . esc_attr($name) . '_url" name="options[' . esc_attr($name) . '][url]" value="' . esc_attr($media_full[0]) . '" size="50">';
1810
- }
1811
-
1812
- function media_input($option, $name, $label) {
1813
-
1814
- if (!empty($label)) {
1815
- $output = '<label class="select" for="tnp_' . esc_attr($name) . '">' . esc_html($label) . ':</label>';
1816
- }
1817
- $output .= '<input id="tnp_' . esc_attr($name) . '" type="text" size="36" name="' . esc_attr($option) . '[' . esc_attr($name) . ']" value="' . esc_attr($val) . '" />';
1818
- $output .= '<input id="tnp_' . esc_attr($name) . '_button" class="button-primary" type="button" value="Select Image" />';
1819
- $output .= '<br class="clear"/>';
1820
-
1821
- echo $output;
1822
- }
1823
-
1824
- function language($name = 'language', $empty_label = 'All') {
1825
- if (!class_exists('SitePress') && !function_exists('pll_default_language') && !class_exists('TRP_Translate_Press')) {
1826
- echo __('Install a multilanguage plugin.', 'newsletter');
1827
- echo ' <a href="https://www.thenewsletterplugin.com/documentation/multilanguage" target="_blank">', __('Read more', 'newsletter'), '</a>';
1828
- return;
1829
- }
1830
-
1831
- $languages = Newsletter::instance()->get_languages();
1832
- if (!empty($empty_label)) {
1833
- $languages = array_merge(array('' => $empty_label), $languages);
1834
- }
1835
- $this->select($name, $languages);
1836
- }
1837
-
1838
- function is_multilanguage() {
1839
- return Newsletter::instance()->is_multilanguage();
1840
- }
1841
-
1842
- /**
1843
- * Creates a checkbox group with all active languages. Each checkbox is named
1844
- * $name[] and values with the relative language code.
1845
- *
1846
- * @param string $name
1847
- */
1848
- function languages($name = 'languages') {
1849
- if (!$this->is_multilanguage()) {
1850
- echo __('Install WPML or Polylang for multilanguage support', 'newsletter');
1851
- return;
1852
- }
1853
-
1854
- $language_options = Newsletter::instance()->get_languages();
1855
-
1856
- if (empty($language_options)) {
1857
- echo __('Your multilanguage plugin is not supported or there are no languages defined', 'newsletter');
1858
- return;
1859
- }
1860
-
1861
- $this->checkboxes_group($name, $language_options);
1862
- }
1863
-
1864
- /**
1865
- * Prints a formatted date using the formats and timezone of WP, including the current date and time and the
1866
- * time left to the passed time.
1867
- *
1868
- * @param int $time
1869
- * @param int $now
1870
- * @param bool $left
1871
- * @return string
1872
- */
1873
- static function print_date($time = null, $now = false, $left = false) {
1874
- if (is_null($time)) {
1875
- $time = time();
1876
- }
1877
- if ($time == false) {
1878
- $buffer = 'none';
1879
- } else {
1880
- $buffer = date_i18n(get_option('date_format') . ' ' . get_option('time_format'), $time + get_option('gmt_offset') * 3600);
1881
- }
1882
- if ($now) {
1883
- $buffer .= ' (now: ' . gmdate(get_option('date_format') . ' ' .
1884
- get_option('time_format'), time() + get_option('gmt_offset') * 3600);
1885
- $buffer .= ')';
1886
- }
1887
- if ($left) {
1888
- if ($time - time() < 0) {
1889
- $buffer .= ', ' . (time() - $time) . ' seconds late';
1890
- } else {
1891
- $buffer .= ', ' . gmdate('H:i:s', $time - time()) . ' left';
1892
- }
1893
- }
1894
- return $buffer;
1895
- }
1896
-
1897
- static function delta_time($delta = 0) {
1898
- $seconds = $delta % 60;
1899
- $minutes = floor(($delta / 60) % 60);
1900
- $hours = floor(($delta / (60 * 60)) % 24);
1901
- $days = floor($delta / (24 * 60 * 60));
1902
-
1903
- return $days . ' day(s), ' . $hours . ' hour(s), ' . $minutes . ' minute(s)';
1904
- }
1905
-
1906
- /**
1907
- * Prints the help button near a form field. The label is used as icon title.
1908
- *
1909
- * @param string $url
1910
- * @param string $label
1911
- */
1912
- static function help($url, $label = '') {
1913
- echo '<a href="', $url, '" target="_blank" title="', esc_attr($label), '"><i class="fas fa-question-circle"></i></a>';
1914
- }
1915
-
1916
- static function idea($url, $label = '') {
1917
- echo '<a href="', $url, '" target="_blank" title="', esc_attr($label), '"><i class="fas fa-lightbulb-o"></i></a>';
1918
- }
1919
-
1920
- static function field_help($url, $text = '') {
1921
- if (strpos($url, 'http') !== 0) {
1922
- $url = 'https://www.thenewsletterplugin.com' . $url;
1923
- }
1924
- echo '<a href="', $url, '" target="_blank" style="text-decoration: none" title="' . esc_attr(__('Read more', 'newsletter')) . '"><i class="fas fa-question-circle"></i>';
1925
- if ($text)
1926
- echo '&nbsp;', $text;
1927
- echo '</a>';
1928
- }
1929
-
1930
- static function field_label($label, $help_url = false) {
1931
- echo $label;
1932
- if ($help_url) {
1933
- echo '&nbsp';
1934
- self::field_help($help_url);
1935
- }
1936
- }
1937
-
1938
- /**
1939
- * Prints a panel link to the documentation.
1940
- *
1941
- * @param type $url
1942
- * @param type $text
1943
- */
1944
- static function panel_help($url, $text = '') {
1945
- if (empty($text))
1946
- $text = __('Need help?', 'newsletter');
1947
- echo '<span class="tnp-panel-help"><a href="', $url, '" target="_blank">', $text, '</a></span>';
1948
- }
1949
-
1950
- /**
1951
- * Prints an administration page link to the documentation (just under the administration page title.
1952
- * @param type $url
1953
- * @param type $text
1954
- */
1955
- static function page_help($url, $text = '') {
1956
- if (empty($text))
1957
- $text = __('Need help?', 'newsletter');
1958
- echo '<div class="tnp-page-help"><a href="', $url, '" target="_blank">', $text, '</a></div>';
1959
- }
1960
-
1961
- static function print_truncated($text, $size = 50) {
1962
- if (mb_strlen($text) < $size)
1963
- return esc_html($text);
1964
- $sub = mb_substr($text, 0, $size);
1965
- echo '<span title="', esc_attr($text), '">', esc_html($sub), '...</span>';
1966
- }
1967
-
1968
- function block_background($name = 'block_background') {
1969
- $this->color($name);
1970
- }
1971
-
1972
- function block_padding($name = 'block_padding', $options = array()) {
1973
- echo '<div style="text-align: center; width: 250px;">';
1974
- $this->text($name . '_top', 5);
1975
- echo '<br>';
1976
- $this->text($name . '_left', 5);
1977
- echo "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
1978
- $this->text($name . '_right', 5);
1979
- echo '<br>';
1980
- $this->text($name . '_bottom', 5);
1981
- echo '</div>';
1982
- }
1983
-
1984
- /**
1985
- * Adds the fields used by the composer (version 1) in the page form.
1986
- *
1987
- * @param type $name
1988
- */
1989
- function composer_fields($name = 'body') {
1990
-
1991
- // body
1992
- $value = $this->get_value($name);
1993
-
1994
- // Extracts only the body part
1995
- $x = strpos($value, '<body');
1996
- if ($x) {
1997
- $x = strpos($value, '>', $x);
1998
- $y = strpos($value, '</body>');
1999
- $value = substr($value, $x + 1, $y - $x - 1);
2000
- }
2001
-
2002
- /* Cleans up uncorrectly stored newsletter bodies */
2003
- $value = preg_replace('/<style\s+.*?>.*?<\\/style>/is', '', $value);
2004
- $value = preg_replace('/<meta.*?>/', '', $value);
2005
- $value = preg_replace('/<title\s+.*?>.*?<\\/title>/i', '', $value);
2006
- $value = trim($value);
2007
-
2008
- // Required since esc_html DOES NOT escape the HTML entities (apparently)
2009
- $value = str_replace('&', '&amp;', $value);
2010
- $value = str_replace('"', '&quot;', $value);
2011
- $value = str_replace('<', '&lt;', $value);
2012
- $value = str_replace('>', '&gt;', $value);
2013
- echo '<input type="hidden" name="options[', esc_attr($name), ']" id="options-', esc_attr($name), '" value="', esc_attr($value), '">';
2014
-
2015
- // Used by composer to rebuild the full HTML
2016
- $css = NewsletterEmails::instance()->get_composer_css();
2017
- echo '<input type="hidden" name="options[css]" id="options-css" value="', esc_attr($css), '">';
2018
-
2019
- // subject
2020
- $value = $this->get_value('subject');
2021
- echo '<input type="hidden" name="options[subject]" id="options-subject" value="', esc_attr($value), '">';
2022
- }
2023
-
2024
- function composer_load($name = 'body', $show_subject = false, $show_test = true, $context_type = '') {
2025
-
2026
- global $controls;
2027
- global $tnpc_show_subject;
2028
- $tnpc_show_subject = $show_subject;
2029
-
2030
- wp_enqueue_style('tnpc-style', plugins_url('newsletter') . '/emails/tnp-composer/_css/newsletter-builder.css', array(), time());
2031
-
2032
- include NEWSLETTER_DIR . '/emails/tnp-composer/index.php';
2033
- }
2034
-
2035
- /**
2036
- * Adds the fields used by the composer (version 2) in the page form.
2037
- */
2038
- function composer_fields_v2($name = 'message') {
2039
-
2040
- // The composer, on saving, fills in those fields
2041
- $this->hidden('subject');
2042
- $this->hidden('message');
2043
- $this->hidden('options_preheader');
2044
-
2045
- //$preheader_value = $this->get_value('options_preheader');
2046
- // echo '<input name="options[preheader]" id="options-preheader" type="hidden" value="', esc_attr($preheader_value), '">';
2047
- }
2048
-
2049
- function composer_load_v2($show_subject = false, $show_test = true, $context_type = '') {
2050
-
2051
- global $tnpc_show_subject;
2052
- $tnpc_show_subject = $show_subject;
2053
-
2054
- echo "<link href='", plugins_url('newsletter'), "/emails/tnp-composer/_css/newsletter-builder-v2.css?ver=" . NEWSLETTER_VERSION . "' rel='stylesheet' type='text/css'>";
2055
-
2056
- $controls = $this;
2057
- include NEWSLETTER_DIR . '/emails/tnp-composer/index-v2.php';
2058
- }
2059
-
2060
- function subject($name) {
2061
- $value = $this->get_value($name);
2062
- // Leave the ID with this prefix!
2063
- echo '<div style="position: relative"><input size="80" id="options-subject-', esc_attr($name), '" name="options[' . esc_attr($name) . ']" type="text" placeholder="" value="';
2064
- echo esc_attr($value);
2065
- echo '">';
2066
- echo '&nbsp;<i class="far fa-lightbulb tnp-suggest-subject" data-tnp-modal-target="#subject-ideas-modal"></i>';
2067
-
2068
- echo '<img src="', plugins_url('newsletter'), '/images/subject/android.png" style="position: absolute; width: 16px; left: 330px; top: 25px; display: block; opacity: 0">';
2069
- echo '<img src="', plugins_url('newsletter'), '/images/subject/iphone.png" style="position: absolute; width: 16px; left: 380px; top: 25px; display: block; opacity: 0">';
2070
- //echo '<img src="', NEWSLETTER_URL, '/images/subject/gmail.png" style="position: absolute; width: 16px; left: 400px; top: 25px; display: block; opacity: 0">';
2071
- echo '</div>';
2072
- }
2073
-
2074
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ defined('ABSPATH') || exit;
4
+
5
+ include_once __DIR__ . '/fields.php';
6
+
7
+ class NewsletterControls {
8
+
9
+ var $data = [];
10
+ var $action = false;
11
+ var $button_data = '';
12
+ var $errors = '';
13
+
14
+ /**
15
+ * @var string
16
+ */
17
+ var $messages = '';
18
+
19
+ /**
20
+ * @var array
21
+ */
22
+ var $warnings = array();
23
+ var $countries = array(
24
+ 'AF' => 'Afghanistan',
25
+ 'AX' => 'Aland Islands',
26
+ 'AL' => 'Albania',
27
+ 'DZ' => 'Algeria',
28
+ 'AS' => 'American Samoa',
29
+ 'AD' => 'Andorra',
30
+ 'AO' => 'Angola',
31
+ 'AI' => 'Anguilla',
32
+ 'AQ' => 'Antarctica',
33
+ 'AG' => 'Antigua And Barbuda',
34
+ 'AR' => 'Argentina',
35
+ 'AM' => 'Armenia',
36
+ 'AW' => 'Aruba',
37
+ 'AU' => 'Australia',
38
+ 'AT' => 'Austria',
39
+ 'AZ' => 'Azerbaijan',
40
+ 'BS' => 'Bahamas',
41
+ 'BH' => 'Bahrain',
42
+ 'BD' => 'Bangladesh',
43
+ 'BB' => 'Barbados',
44
+ 'BY' => 'Belarus',
45
+ 'BE' => 'Belgium',
46
+ 'BZ' => 'Belize',
47
+ 'BJ' => 'Benin',
48
+ 'BM' => 'Bermuda',
49
+ 'BT' => 'Bhutan',
50
+ 'BO' => 'Bolivia',
51
+ 'BA' => 'Bosnia And Herzegovina',
52
+ 'BW' => 'Botswana',
53
+ 'BV' => 'Bouvet Island',
54
+ 'BR' => 'Brazil',
55
+ 'IO' => 'British Indian Ocean Territory',
56
+ 'BN' => 'Brunei Darussalam',
57
+ 'BG' => 'Bulgaria',
58
+ 'BF' => 'Burkina Faso',
59
+ 'BI' => 'Burundi',
60
+ 'KH' => 'Cambodia',
61
+ 'CM' => 'Cameroon',
62
+ 'CA' => 'Canada',
63
+ 'CV' => 'Cape Verde',
64
+ 'KY' => 'Cayman Islands',
65
+ 'CF' => 'Central African Republic',
66
+ 'TD' => 'Chad',
67
+ 'CL' => 'Chile',
68
+ 'CN' => 'China',
69
+ 'CX' => 'Christmas Island',
70
+ 'CC' => 'Cocos (Keeling) Islands',
71
+ 'CO' => 'Colombia',
72
+ 'KM' => 'Comoros',
73
+ 'CG' => 'Congo',
74
+ 'CD' => 'Congo, Democratic Republic',
75
+ 'CK' => 'Cook Islands',
76
+ 'CR' => 'Costa Rica',
77
+ 'CI' => 'Cote D\'Ivoire',
78
+ 'HR' => 'Croatia',
79
+ 'CU' => 'Cuba',
80
+ 'CY' => 'Cyprus',
81
+ 'CZ' => 'Czech Republic',
82
+ 'DK' => 'Denmark',
83
+ 'DJ' => 'Djibouti',
84
+ 'DM' => 'Dominica',
85
+ 'DO' => 'Dominican Republic',
86
+ 'EC' => 'Ecuador',
87
+ 'EG' => 'Egypt',
88
+ 'SV' => 'El Salvador',
89
+ 'GQ' => 'Equatorial Guinea',
90
+ 'ER' => 'Eritrea',
91
+ 'EE' => 'Estonia',
92
+ 'ET' => 'Ethiopia',
93
+ 'FK' => 'Falkland Islands (Malvinas)',
94
+ 'FO' => 'Faroe Islands',
95
+ 'FJ' => 'Fiji',
96
+ 'FI' => 'Finland',
97
+ 'FR' => 'France',
98
+ 'GF' => 'French Guiana',
99
+ 'PF' => 'French Polynesia',
100
+ 'TF' => 'French Southern Territories',
101
+ 'GA' => 'Gabon',
102
+ 'GM' => 'Gambia',
103
+ 'GE' => 'Georgia',
104
+ 'DE' => 'Germany',
105
+ 'GH' => 'Ghana',
106
+ 'GI' => 'Gibraltar',
107
+ 'GR' => 'Greece',
108
+ 'GL' => 'Greenland',
109
+ 'GD' => 'Grenada',
110
+ 'GP' => 'Guadeloupe',
111
+ 'GU' => 'Guam',
112
+ 'GT' => 'Guatemala',
113
+ 'GG' => 'Guernsey',
114
+ 'GN' => 'Guinea',
115
+ 'GW' => 'Guinea-Bissau',
116
+ 'GY' => 'Guyana',
117
+ 'HT' => 'Haiti',
118
+ 'HM' => 'Heard Island & Mcdonald Islands',
119
+ 'VA' => 'Holy See (Vatican City State)',
120
+ 'HN' => 'Honduras',
121
+ 'HK' => 'Hong Kong',
122
+ 'HU' => 'Hungary',
123
+ 'IS' => 'Iceland',
124
+ 'IN' => 'India',
125
+ 'ID' => 'Indonesia',
126
+ 'IR' => 'Iran, Islamic Republic Of',
127
+ 'IQ' => 'Iraq',
128
+ 'IE' => 'Ireland',
129
+ 'IM' => 'Isle Of Man',
130
+ 'IL' => 'Israel',
131
+ 'IT' => 'Italy',
132
+ 'JM' => 'Jamaica',
133
+ 'JP' => 'Japan',
134
+ 'JE' => 'Jersey',
135
+ 'JO' => 'Jordan',
136
+ 'KZ' => 'Kazakhstan',
137
+ 'KE' => 'Kenya',
138
+ 'KI' => 'Kiribati',
139
+ 'KR' => 'Korea',
140
+ 'KW' => 'Kuwait',
141
+ 'KG' => 'Kyrgyzstan',
142
+ 'LA' => 'Lao People\'s Democratic Republic',
143
+ 'LV' => 'Latvia',
144
+ 'LB' => 'Lebanon',
145
+ 'LS' => 'Lesotho',
146
+ 'LR' => 'Liberia',
147
+ 'LY' => 'Libyan Arab Jamahiriya',
148
+ 'LI' => 'Liechtenstein',
149
+ 'LT' => 'Lithuania',
150
+ 'LU' => 'Luxembourg',
151
+ 'MO' => 'Macao',
152
+ 'MK' => 'Macedonia',
153
+ 'MG' => 'Madagascar',
154
+ 'MW' => 'Malawi',
155
+ 'MY' => 'Malaysia',
156
+ 'MV' => 'Maldives',
157
+ 'ML' => 'Mali',
158
+ 'MT' => 'Malta',
159
+ 'MH' => 'Marshall Islands',
160
+ 'MQ' => 'Martinique',
161
+ 'MR' => 'Mauritania',
162
+ 'MU' => 'Mauritius',
163
+ 'YT' => 'Mayotte',
164
+ 'MX' => 'Mexico',
165
+ 'FM' => 'Micronesia, Federated States Of',
166
+ 'MD' => 'Moldova',
167
+ 'MC' => 'Monaco',
168
+ 'MN' => 'Mongolia',
169
+ 'ME' => 'Montenegro',
170
+ 'MS' => 'Montserrat',
171
+ 'MA' => 'Morocco',
172
+ 'MZ' => 'Mozambique',
173
+ 'MM' => 'Myanmar',
174
+ 'NA' => 'Namibia',
175
+ 'NR' => 'Nauru',
176
+ 'NP' => 'Nepal',
177
+ 'NL' => 'Netherlands',
178
+ 'AN' => 'Netherlands Antilles',
179
+ 'NC' => 'New Caledonia',
180
+ 'NZ' => 'New Zealand',
181
+ 'NI' => 'Nicaragua',
182
+ 'NE' => 'Niger',
183
+ 'NG' => 'Nigeria',
184
+ 'NU' => 'Niue',
185
+ 'NF' => 'Norfolk Island',
186
+ 'MP' => 'Northern Mariana Islands',
187
+ 'NO' => 'Norway',
188
+ 'OM' => 'Oman',
189
+ 'PK' => 'Pakistan',
190
+ 'PW' => 'Palau',
191
+ 'PS' => 'Palestinian Territory, Occupied',
192
+ 'PA' => 'Panama',
193
+ 'PG' => 'Papua New Guinea',
194
+ 'PY' => 'Paraguay',
195
+ 'PE' => 'Peru',
196
+ 'PH' => 'Philippines',
197
+ 'PN' => 'Pitcairn',
198
+ 'PL' => 'Poland',
199
+ 'PT' => 'Portugal',
200
+ 'PR' => 'Puerto Rico',
201
+ 'QA' => 'Qatar',
202
+ 'RE' => 'Reunion',
203
+ 'RO' => 'Romania',
204
+ 'RU' => 'Russian Federation',
205
+ 'RW' => 'Rwanda',
206
+ 'BL' => 'Saint Barthelemy',
207
+ 'SH' => 'Saint Helena',
208
+ 'KN' => 'Saint Kitts And Nevis',
209
+ 'LC' => 'Saint Lucia',
210
+ 'MF' => 'Saint Martin',
211
+ 'PM' => 'Saint Pierre And Miquelon',
212
+ 'VC' => 'Saint Vincent And Grenadines',
213
+ 'WS' => 'Samoa',
214
+ 'SM' => 'San Marino',
215
+ 'ST' => 'Sao Tome And Principe',
216
+ 'SA' => 'Saudi Arabia',
217
+ 'SN' => 'Senegal',
218
+ 'RS' => 'Serbia',
219
+ 'SC' => 'Seychelles',
220
+ 'SL' => 'Sierra Leone',
221
+ 'SG' => 'Singapore',
222
+ 'SK' => 'Slovakia',
223
+ 'SI' => 'Slovenia',
224
+ 'SB' => 'Solomon Islands',
225
+ 'SO' => 'Somalia',
226
+ 'ZA' => 'South Africa',
227
+ 'GS' => 'South Georgia And Sandwich Isl.',
228
+ 'ES' => 'Spain',
229
+ 'LK' => 'Sri Lanka',
230
+ 'SD' => 'Sudan',
231
+ 'SR' => 'Suriname',
232
+ 'SJ' => 'Svalbard And Jan Mayen',
233
+ 'SZ' => 'Swaziland',
234
+ 'SE' => 'Sweden',
235
+ 'CH' => 'Switzerland',
236
+ 'SY' => 'Syrian Arab Republic',
237
+ 'TW' => 'Taiwan',
238
+ 'TJ' => 'Tajikistan',
239
+ 'TZ' => 'Tanzania',
240
+ 'TH' => 'Thailand',
241
+ 'TL' => 'Timor-Leste',
242
+ 'TG' => 'Togo',
243
+ 'TK' => 'Tokelau',
244
+ 'TO' => 'Tonga',
245
+ 'TT' => 'Trinidad And Tobago',
246
+ 'TN' => 'Tunisia',
247
+ 'TR' => 'Turkey',
248
+ 'TM' => 'Turkmenistan',
249
+ 'TC' => 'Turks And Caicos Islands',
250
+ 'TV' => 'Tuvalu',
251
+ 'UG' => 'Uganda',
252
+ 'UA' => 'Ukraine',
253
+ 'AE' => 'United Arab Emirates',
254
+ 'GB' => 'United Kingdom',
255
+ 'US' => 'United States',
256
+ 'UM' => 'United States Outlying Islands',
257
+ 'UY' => 'Uruguay',
258
+ 'UZ' => 'Uzbekistan',
259
+ 'VU' => 'Vanuatu',
260
+ 'VE' => 'Venezuela',
261
+ 'VN' => 'Viet Nam',
262
+ 'VG' => 'Virgin Islands, British',
263
+ 'VI' => 'Virgin Islands, U.S.',
264
+ 'WF' => 'Wallis And Futuna',
265
+ 'EH' => 'Western Sahara',
266
+ 'YE' => 'Yemen',
267
+ 'ZM' => 'Zambia',
268
+ 'ZW' => 'Zimbabwe',
269
+ 'XX' => 'Undefined',
270
+ 'CW' => 'Curaçao',
271
+ 'SS' => 'South Sudan',
272
+ 'EU' => 'Europe (generic)',
273
+ 'A1' => 'Anonymous IP',
274
+ 'A2' => 'Satellite IP'
275
+ );
276
+
277
+ /**
278
+ *
279
+ * @param array $options
280
+ */
281
+ function __construct($options = null) {
282
+ if ($options === null) {
283
+ if (isset($_POST['options'])) {
284
+ $this->data = stripslashes_deep($_POST['options']);
285
+ }
286
+ } else {
287
+ $this->data = (array) $options;
288
+ }
289
+
290
+ if (isset($_REQUEST['act'])) {
291
+ $this->action = $_REQUEST['act'];
292
+ }
293
+
294
+ if (isset($_REQUEST['btn'])) {
295
+ $this->button_data = $_REQUEST['btn'];
296
+ }
297
+ // Fields analysis
298
+ if (isset($_REQUEST['tnp_fields'])) {
299
+ $fields = $_REQUEST['tnp_fields'];
300
+ if (is_array($fields)) {
301
+ foreach ($fields as $name => $type) {
302
+ if ($type == 'datetime') {
303
+ // Ex. The user insert 01/07/2012 14:30 and it set the time zone to +2. We cannot use the
304
+ // mktime, since it uses the time zone of the machine. We create the time as if we are on
305
+ // GMT 0 and then we subtract the GMT offset (the example date and time on GMT+2 happens
306
+ // "before").
307
+
308
+ $time = gmmktime((int) $_REQUEST[$name . '_hour'], 0, 0, (int) $_REQUEST[$name . '_month'], (int) $_REQUEST[$name . '_day'], (int) $_REQUEST[$name . '_year']);
309
+ $time -= get_option('gmt_offset') * 3600;
310
+ $this->data[$name] = $time;
311
+ continue;
312
+ }
313
+ if ($type === 'array') {
314
+ if (!isset($this->data[$name]))
315
+ $this->data[$name] = [];
316
+ }
317
+ if ($type === 'checkbox') {
318
+ if (!isset($this->data[$name])) {
319
+ $this->data[$name] = 0;
320
+ }
321
+ }
322
+ }
323
+ }
324
+ }
325
+ }
326
+
327
+ function set_data($data) {
328
+ if (is_array($data)) {
329
+ $this->data = $data;
330
+ } else if (is_object($data)) {
331
+ $this->data = (array) $data;
332
+ } else {
333
+ $this->data = [];
334
+ }
335
+ }
336
+
337
+ function merge($options) {
338
+ if (!is_array($options))
339
+ return;
340
+ if ($this->data == null)
341
+ $this->data = array();
342
+ $this->data = array_merge($this->data, $options);
343
+ }
344
+
345
+ function merge_defaults($defaults) {
346
+ if ($this->data == null)
347
+ $this->data = $defaults;
348
+ else
349
+ $this->data = array_merge($defaults, $this->data);
350
+ }
351
+
352
+ /**
353
+ * Return true is there in an asked action is no action name is specified or
354
+ * true is the requested action matches the passed action.
355
+ * Dies if it is not a safe call.
356
+ */
357
+ function is_action($action = null) {
358
+ if ($action == null)
359
+ return $this->action != null;
360
+ if ($this->action == null)
361
+ return false;
362
+ if ($this->action != $action)
363
+ return false;
364
+ if (check_admin_referer('save'))
365
+ return true;
366
+ die('Invalid call');
367
+ }
368
+
369
+ function get_value($name, $def = null) {
370
+ if (!isset($this->data[$name])) {
371
+ return $def;
372
+ }
373
+ return $this->data[$name];
374
+ }
375
+
376
+ function get_value_array($name) {
377
+ if (!isset($this->data[$name]) || !is_array($this->data[$name]))
378
+ return array();
379
+ return $this->data[$name];
380
+ }
381
+
382
+ function show_error($text) {
383
+ echo '<div class="tnp-error">', $text, '</div>';
384
+ }
385
+
386
+ function show_warning($text) {
387
+ echo '<div class="tnp-warning">', $text, '</div>';
388
+ }
389
+
390
+ function show_message($text) {
391
+ echo '<div class="tnpc-message">', $text, '</div>';
392
+ }
393
+
394
+ /**
395
+ * Show the errors and messages.
396
+ */
397
+ function show() {
398
+ static $shown = false;
399
+
400
+ if ($shown) {
401
+ return;
402
+ }
403
+ $shown = true;
404
+
405
+ if (!empty($this->errors)) {
406
+ echo '<div class="tnpc-error">';
407
+ echo $this->errors;
408
+ echo '</div>';
409
+ }
410
+ if (!empty($this->warnings)) {
411
+ foreach ((array) $this->warnings as $warning) {
412
+ echo '<div class="tnpc-warning">';
413
+ echo $warning;
414
+ echo '</div>';
415
+ }
416
+ }
417
+ if (!empty($this->messages)) {
418
+ echo '<div class="tnpc-message">';
419
+ echo $this->messages;
420
+ echo '</div>';
421
+ }
422
+ }
423
+
424
+ function add_message($text) {
425
+ if (!empty($this->messages)) {
426
+ $this->messages .= '<br><br>';
427
+ }
428
+ $this->messages .= $text;
429
+ }
430
+
431
+ function add_message_saved() {
432
+ if (!empty($this->messages)) {
433
+ $this->messages .= '<br><br>';
434
+ }
435
+ $this->messages .= __('Saved.', 'newsletter');
436
+ }
437
+
438
+ function add_message_deleted() {
439
+ if (!empty($this->messages)) {
440
+ $this->messages .= '<br><br>';
441
+ }
442
+ $this->messages .= __('Deleted.', 'newsletter');
443
+ }
444
+
445
+ function add_message_reset() {
446
+ if (!empty($this->messages)) {
447
+ $this->messages .= '<br><br>';
448
+ }
449
+ $this->messages .= __('Options reset.', 'newsletter');
450
+ }
451
+
452
+ function add_message_done() {
453
+ if (!empty($this->messages)) {
454
+ $this->messages .= '<br><br>';
455
+ }
456
+ $this->messages .= __('Done.', 'newsletter');
457
+ }
458
+
459
+ function add_language_warning() {
460
+ $newsletter = Newsletter::instance();
461
+ $current_language = $newsletter->get_current_language();
462
+
463
+ if (!$current_language) {
464
+ return;
465
+ }
466
+ $this->warnings[] = 'You are configuring the language <strong>' . $newsletter->get_language_label($current_language) . '</strong>. Switch to "all languages" to see all options.';
467
+ }
468
+
469
+ function switch_to_all_languages_notice() {
470
+ echo '<div class="tnpc-languages-notice">';
471
+ _e('Switch the administration side to "all languages" to set these options', 'newsletter');
472
+ echo '</div>';
473
+ }
474
+
475
+ function hint($text, $url = '') {
476
+ echo '<div class="tnpc-hint">';
477
+ // Do not escape that, it can be formatted
478
+ echo $text;
479
+ if (!empty($url)) {
480
+ echo ' <a href="' . esc_attr($url) . '" target="_blank">Read more</a>.';
481
+ }
482
+ echo '</div>';
483
+ }
484
+
485
+ function yesno($name) {
486
+ $value = isset($this->data[$name]) ? (int) $this->data[$name] : 0;
487
+
488
+ echo '<select style="width: 60px" name="options[', esc_attr($name), ']">';
489
+ echo '<option value="0"';
490
+ if ($value == 0) {
491
+ echo ' selected';
492
+ }
493
+ echo '>', __('No', 'newsletter'), '</option>';
494
+ echo '<option value="1"';
495
+ if ($value == 1) {
496
+ echo ' selected';
497
+ }
498
+ echo '>', __('Yes', 'newsletter'), '</option>';
499
+ echo '</select>&nbsp;&nbsp;&nbsp;';
500
+ }
501
+
502
+ function enabled($name = 'enabled', $attrs = []) {
503
+ $value = isset($this->data[$name]) ? (int) $this->data[$name] : 0;
504
+ $name = esc_attr($name);
505
+
506
+ echo '<select style="width: 100px" name="options[', $name, ']" id="options-', $name, '"';
507
+ if (isset($attrs['bind_to'])) {
508
+ echo ' onchange="tnp_select_toggle(this, \'', $attrs['bind_to'], '\')"';
509
+ }
510
+ echo '>';
511
+ echo '<option value="0"';
512
+ if ($value == 0) {
513
+ echo ' selected';
514
+ }
515
+ echo '>', __('Disabled', 'newsletter'), '</option>';
516
+ echo '<option value="1"';
517
+ if ($value == 1) {
518
+ echo ' selected';
519
+ }
520
+ echo '>', __('Enabled', 'newsletter'), '</option>';
521
+ echo '</select>';
522
+ if (isset($attrs['bind_to'])) {
523
+ if ($value) {
524
+ echo '<script>jQuery(function ($) {$("#options-', $attrs['bind_to'], '").show()})</script>';
525
+ } else {
526
+ echo '<script>jQuery(function ($) {$("#options-', $attrs['bind_to'], '").hide()})</script>';
527
+ }
528
+ }
529
+ }
530
+
531
+ function disabled($name) {
532
+ $value = isset($this->data[$name]) ? (int) $this->data[$name] : 0;
533
+
534
+ echo '<select style="width: 100px" name="options[' . esc_attr($name) . ']">';
535
+ echo '<option value="0"';
536
+ if ($value == 0) {
537
+ echo ' selected';
538
+ }
539
+ echo '>Enabled</option>';
540
+ echo '<option value="1"';
541
+ if ($value == 1) {
542
+ echo ' selected';
543
+ }
544
+ echo '>Disabled</option>';
545
+ echo '</select>';
546
+ }
547
+
548
+ /**
549
+ * Creates a set of checkbox all named as $name with values and labels extracted from
550
+ * $values_labels. A checkbox will be checked if internal data under key $name is an array
551
+ * and contains the value of the current (echoing) checkbox.
552
+ *
553
+ * On submit it produces an array under the name $name IF at least one checkbox has
554
+ * been checked. Otherwise the key won't be present.
555
+ *
556
+ * @param array $values
557
+ * @param string $name
558
+ * @param array $values_labels
559
+ */
560
+ function checkboxes_group($name, $values_labels) {
561
+ $value_array = $this->get_value_array($name);
562
+
563
+ echo '<div class="tnpc-checkboxes">';
564
+ foreach ($values_labels as $value => $label) {
565
+ echo '<label><input type="checkbox" id="' . esc_attr($name) . '" name="options[' . esc_attr($name) . '][]" value="' . esc_attr($value) . '"';
566
+ if (array_search($value, $value_array) !== false) {
567
+ echo ' checked';
568
+ }
569
+ echo '>';
570
+ if ($label != '') {
571
+ echo '&nbsp;' . esc_html($label);
572
+ }
573
+ echo "</label>";
574
+ }
575
+ echo "<div style='clear: both'></div>";
576
+ }
577
+
578
+ /** Creates a checkbox group with all public post types.
579
+ */
580
+ function post_types($name = 'post_types') {
581
+ $list = array();
582
+ $post_types = get_post_types(array('public' => true), 'objects', 'and');
583
+ foreach ($post_types as $post_type) {
584
+ $list[$post_type->name] = $post_type->labels->name;
585
+ }
586
+
587
+ $this->checkboxes_group($name, $list);
588
+ }
589
+
590
+ function posts_select($name, $max = 20, $args = array()) {
591
+ $args = array_merge(array(
592
+ 'posts_per_page' => 5,
593
+ 'offset' => 0,
594
+ 'category' => '',
595
+ 'category_name' => '',
596
+ 'orderby' => 'date',
597
+ 'order' => 'DESC',
598
+ 'include' => '',
599
+ 'exclude' => '',
600
+ 'meta_key' => '',
601
+ 'meta_value' => '',
602
+ 'post_type' => 'post',
603
+ 'post_mime_type' => '',
604
+ 'post_parent' => '',
605
+ 'author' => '',
606
+ 'author_name' => '',
607
+ 'post_status' => 'publish',
608
+ 'suppress_filters' => true
609
+ ), $args);
610
+ $args['posts_per_page'] = $max;
611
+
612
+ $posts = get_posts($args);
613
+ $options = array();
614
+ foreach ($posts as $post) {
615
+ $options['' . $post->ID] = $post->post_title;
616
+ }
617
+
618
+ $this->select($name, $options);
619
+ }
620
+
621
+ function select_number($name, $min, $max) {
622
+ $options = array();
623
+ for ($i = $min; $i <= $max; $i++) {
624
+ $options['' . $i] = $i;
625
+ }
626
+ $this->select($name, $options);
627
+ }
628
+
629
+ function page($name = 'page', $first = null, $language = '', $show_id = false) {
630
+ $args = array(
631
+ 'post_type' => 'page',
632
+ 'posts_per_page' => 1000,
633
+ 'offset' => 0,
634
+ 'orderby' => 'post_title',
635
+ 'post_status' => 'any',
636
+ 'suppress_filters' => true
637
+ );
638
+
639
+ $pages = get_posts($args);
640
+ //$pages = get_pages();
641
+ $options = array();
642
+ foreach ($pages as $page) {
643
+ /* @var $page WP_Post */
644
+ $label = $page->post_title;
645
+ if ($page->post_status != 'publish') {
646
+ $label .= ' (' . $page->post_status . ')';
647
+ }
648
+ if ($show_id) {
649
+ $label .= ' [' . $page->ID . ']';
650
+ }
651
+ $options[$page->ID] = $label;
652
+ }
653
+ $this->select($name, $options, $first);
654
+ }
655
+
656
+ /** Used to create a select which is part of a group of controls identified by $name that will
657
+ * produce an array of values as $_REQUEST['name'].
658
+ * @param string $name
659
+ * @param array $options Associative array
660
+ */
661
+ function select_group($name, $options) {
662
+ $value_array = $this->get_value_array($name);
663
+
664
+ echo '<select name="options[' . esc_attr($name) . '][]">';
665
+
666
+ foreach ($options as $key => $label) {
667
+ echo '<option value="' . esc_attr($key) . '"';
668
+ if (array_search($key, $value_array) !== false) {
669
+ echo ' selected';
670
+ }
671
+ echo '>' . esc_html($label) . '</option>';
672
+ }
673
+
674
+ echo '</select>';
675
+ }
676
+
677
+ function select($name, $options, $first = null, $attrs = []) {
678
+ echo '<select id="options-' . esc_attr($name) . '" name="options[' . esc_attr($name) . ']"';
679
+ if ($attrs) {
680
+ foreach ($attrs as $key => $value) {
681
+ echo ' ', $key, '="' . esc_attr($value), '"';
682
+ }
683
+ }
684
+ echo '>';
685
+ if (!empty($first)) {
686
+ echo '<option value="">' . esc_html($first) . '</option>';
687
+ }
688
+ $value = $this->get_value($name);
689
+ foreach ($options as $key => $label) {
690
+ echo '<option value="' . esc_attr($key) . '"';
691
+ if ($value == $key) {
692
+ echo ' selected';
693
+ }
694
+ echo '>' . esc_html($label) . '</option>';
695
+ }
696
+ echo '</select>';
697
+ }
698
+
699
+ function select_images($name, $options, $first = null) {
700
+ $value = $this->get_value($name);
701
+
702
+ echo '<select id="options-' . esc_attr($name) . '" name="options[' . esc_attr($name) . ']" style="min-width: 200px">';
703
+ if (!empty($first)) {
704
+ echo '<option value="">' . esc_html($first) . '</option>';
705
+ } else {
706
+ // if (empty($value)) {
707
+ // $keys = array_keys($options);
708
+ // $value = $keys[0];
709
+ // }
710
+ }
711
+ foreach ($options as $key => $data) {
712
+ echo '<option value="' . esc_attr($key) . '" image="' . esc_attr($data['image']) . '"';
713
+ if ($value == $key)
714
+ echo ' selected';
715
+ echo '>' . esc_html($data['label']) . '</option>';
716
+ }
717
+ echo '</select>';
718
+ echo '<script>jQuery("#options-' . esc_attr($name) . '").select2({templateResult: tnp_select_images, templateSelection: tnp_select_images_selection});</script>';
719
+ }
720
+
721
+ function select2($name, $options, $first = null, $multiple = false, $style = null, $placeholder = '') {
722
+
723
+ if ($multiple) {
724
+ $option_name = "options[" . esc_attr($name) . "][]";
725
+ } else {
726
+ $option_name = "options[" . esc_attr($name) . "]";
727
+ }
728
+
729
+ if (is_null($style)) {
730
+ $style = 'width: 100%';
731
+ }
732
+
733
+ $value = $this->get_value($name);
734
+
735
+ echo '<select id="options-', esc_attr($name), '" name="', $option_name, '" style="', $style, '"',
736
+ ($multiple ? ' multiple' : ''), '>';
737
+ if (!empty($first)) {
738
+ echo '<option value="">' . esc_html($first) . '</option>';
739
+ }
740
+
741
+ foreach ($options as $key => $data) {
742
+ echo '<option value="' . esc_attr($key) . '"';
743
+ if (is_array($value) && in_array($key, $value) || (!is_null($value) && $value == $key )) {
744
+ echo ' selected';
745
+ }
746
+ echo '>' . esc_html($data) . '</option>';
747
+ }
748
+
749
+ echo '</select>';
750
+ echo '<script>jQuery("#options-' . esc_attr($name) . '").select2({placeholder: "', esc_js($placeholder), '"});</script>';
751
+ }
752
+
753
+ function select_grouped($name, $groups) {
754
+ $value = $this->get_value($name);
755
+ $name = esc_attr($name);
756
+ echo '<select name="options[', $name, ']">';
757
+
758
+ foreach ($groups as $group) {
759
+ echo '<optgroup label="' . esc_attr($group['']) . '">';
760
+ if (!empty($group)) {
761
+ foreach ($group as $key => $label) {
762
+ if ($key == '') {
763
+ continue;
764
+ }
765
+ echo '<option value="' . esc_attr($key) . '"';
766
+ if ($value == $key) {
767
+ echo ' selected';
768
+ }
769
+ echo '>' . esc_html($label) . '</option>';
770
+ }
771
+ }
772
+ echo '</optgroup>';
773
+ }
774
+ echo '</select>';
775
+ }
776
+
777
+ /**
778
+ * Generated a select control with all available templates. From version 3 there are
779
+ * only on kind of templates, they are no more separated by type.
780
+ */
781
+ function themes($name, $themes, $submit_on_click = true) {
782
+ foreach ($themes as $key => $data) {
783
+ echo '<label style="display: block; float: left; text-align: center; margin-right: 10px;">';
784
+ echo esc_html($key) . '<br>';
785
+ echo '<img src="' . esc_attr($data['screenshot']) . '" width="100" height="100" style="border: 1px solid #666; padding: 5px"><br>';
786
+ echo '<input style="position: relative; top: -40px" type="radio" onchange="this.form.act.value=\'theme\';this.form.submit()" name="options[' . esc_attr($name) . ']" value="' . esc_attr($key) . '"';
787
+ if ($this->data[$name] == $key) {
788
+ echo ' checked';
789
+ }
790
+ echo '>';
791
+ echo '</label>';
792
+ }
793
+ echo '<div style="clear: both"></div>';
794
+ }
795
+
796
+ function value($name) {
797
+ echo esc_html($this->data[$name]);
798
+ }
799
+
800
+ function value_date($name, $show_remaining = true) {
801
+ $time = $this->get_value($name);
802
+
803
+ echo gmdate(get_option('date_format') . ' ' . get_option('time_format'), $time + get_option('gmt_offset') * 3600);
804
+ $delta = $time - time();
805
+ if ($show_remaining && $delta > 0) {
806
+ echo 'Remaining: ';
807
+ $delta = $time - time();
808
+ $days = floor($delta / (24 * 3600));
809
+ $delta = $delta - $days * 24 * 3600;
810
+ $hours = floor($delta / 3600);
811
+ $delta = $delta - $hours * 3600;
812
+ $minutes = floor($delta / 60);
813
+
814
+ if ($days > 0) {
815
+ echo $days . ' days ';
816
+ }
817
+ echo $hours . ' hours ';
818
+ echo $minutes . ' minutes ';
819
+ }
820
+ }
821
+
822
+ function password($name, $size = 20, $placeholder = '') {
823
+ $value = $this->get_value($name);
824
+ $name = esc_attr($name);
825
+ echo '<input id="options-', $name, '" placeholder="' . esc_attr($placeholder) . '" name="options[', $name, ']" type="password" autocomplete="off" ';
826
+ if (!empty($size)) {
827
+ echo 'size="', $size, '" ';
828
+ }
829
+ echo 'value="', esc_attr($value), '">';
830
+ }
831
+
832
+ function text($name, $size = 20, $placeholder = '') {
833
+ $value = $this->get_value($name);
834
+ $name = esc_attr($name);
835
+ echo '<input id="options-', $name, '" placeholder="' . esc_attr($placeholder) . '" title="' . esc_attr($placeholder) . '" name="options[', $name, ']" type="text" ';
836
+ if (!empty($size)) {
837
+ echo 'size="', esc_attr($size), '" ';
838
+ }
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) {
852
+ $value = $this->get_value($name);
853
+ echo '<input name="options[' . esc_attr($name) . ']" type="url" placeholder="http://..." size="' . esc_attr($size) . '" value="';
854
+ echo esc_attr($value);
855
+ echo '"/>';
856
+ }
857
+
858
+ function hidden($name) {
859
+ $value = $this->get_value($name);
860
+ echo '<input name="options[', esc_attr($name), ']" id="options-', esc_attr($name), '" type="hidden" value="', esc_attr($value), '">';
861
+ }
862
+
863
+ /**
864
+ * General button. Attributes:
865
+ * - id: the element HTML id
866
+ * - confirm: if string the text is shown in a confirmation message, if true shows a standard confirm message
867
+ * - icon: the font awesome icon name (fa-xxx)
868
+ * - style: the CSS style
869
+ * - data: free data associated to the button click ($controls->button_data) for example to pass the element ID from a list of elements
870
+ *
871
+ * @param string $action
872
+ * @param string $label
873
+ * @param array $attrs
874
+ */
875
+ function btn($action, $label, $attrs = []) {
876
+ echo '<button class="button-primary tnpc-button"';
877
+ if (isset($attrs['id'])) {
878
+ echo ' id="', esc_attrs($attrs['id']), '"';
879
+ }
880
+ $onclick = "this.form.act.value='" . esc_attr(esc_js(trim($action))) . "';";
881
+ if (!empty($attrs['data'])) {
882
+ $onclick .= "this.form.btn.value='" . esc_attr(esc_js($attrs['data'])) . "';";
883
+ }
884
+ if (isset($attrs['confirm'])) {
885
+ if (is_string($attrs['confirm'])) {
886
+ $onclick .= "if (!confirm('" . esc_attr(esc_js($attrs['confirm'])) . "')) return false;";
887
+ } else if ($attrs['confirm'] === true) {
888
+ $onclick .= "if (!confirm('" . esc_attr(esc_js(__('Proceed?', 'newsletter'))) . "')) return false;";
889
+ }
890
+ }
891
+ echo 'onclick="', $onclick, '"';
892
+ if (!empty($attrs['title'])) {
893
+ echo ' title="', esc_attr($attrs['title']), '"';
894
+ }
895
+ if (!empty($attrs['style'])) {
896
+ echo ' style="', esc_attr($attrs['style']), '"';
897
+ }
898
+ echo '>';
899
+ if (!empty($attrs['icon'])) {
900
+ echo '<i class="fas ', esc_attr($attrs['icon']), '"></i>';
901
+ if (!empty($label)) {
902
+ echo '&nbsp;', esc_html($label);
903
+ }
904
+ } else {
905
+ echo esc_html($label);
906
+ }
907
+ echo '</button>';
908
+ }
909
+
910
+ /**
911
+ * Creates a link looking lie a standard button. Attributes:
912
+ * - title: the link "title" HTML attribute
913
+ * - target: the link "target" HTML attribute
914
+ * - icon: the font awesome icon name (fa-xxx)
915
+ * - style: the CSS style
916
+ *
917
+ * @param string $url
918
+ * @param string $label
919
+ * @param array $attrs
920
+ */
921
+ function btn_link($url, $label, $attrs = []) {
922
+ echo '<a href="', esc_attr($url), '" class="button-primary tnpc-button"';
923
+ if (!empty($attrs['style'])) {
924
+ echo ' style="', esc_attr($attrs['style']), '"';
925
+ }
926
+ if (!empty($attrs['title'])) {
927
+ echo ' title="', esc_attr($attrs['title']), '"';
928
+ }
929
+ if (!empty($attrs['target'])) {
930
+ echo ' target="', esc_attr($attrs['target']), '"';
931
+ }
932
+ echo '>';
933
+ if (!empty($attrs['icon'])) {
934
+ echo '<i class="fas ', esc_attr($attrs['icon']), '"></i>';
935
+ if (!empty($label)) {
936
+ echo '&nbsp;', esc_html($label);
937
+ }
938
+ } else {
939
+ echo esc_html($label);
940
+ }
941
+ echo '</a>';
942
+ }
943
+
944
+ function button($action, $label, $function = '', $id = '') {
945
+ $id = !empty($id) ? " id=\"$id\" " : '';
946
+ if ($function != null) {
947
+ echo '<input ' . $id . ' class="button-primary tnpc-button" type="button" value="' . esc_attr($label) . '" onclick="this.form.act.value=\'' . esc_attr($action) . '\';' . esc_html($function) . '"/>';
948
+ } else {
949
+ echo '<input ' . $id . ' class="button-primary tnpc-button" type="submit" value="' . esc_attr($label) . '" onclick="this.form.act.value=\'' . esc_attr($action) . '\';return true;"/>';
950
+ }
951
+ }
952
+
953
+ function action_link($action, $label, $function = null) {
954
+ if ($function != null) {
955
+ echo '<input class="button-link tnpc-button" type="button" value="' . esc_attr($label) . '" onclick="this.form.act.value=\'' . esc_attr($action) . '\';' . esc_html($function) . '"/>';
956
+ } else {
957
+ echo '<input class="button-link tnpc-button" type="submit" value="' . esc_attr($label) . '" onclick="this.form.act.value=\'' . esc_attr($action) . '\';return true;"/>';
958
+ }
959
+ }
960
+
961
+ function button_save() {
962
+ $this->btn('save', __('Save', 'newsletter'), ['icon' => 'fa-save']);
963
+ }
964
+
965
+ function button_reset($action = 'reset') {
966
+ $this->btn($action, __('Reset', 'newsletter'), ['icon' => 'fa-reply', 'confirm' => true]);
967
+ }
968
+
969
+ function button_copy($data = '') {
970
+ $this->btn('copy', __('Duplicate', 'newsletter'), ['data' => $data, 'icon' => 'fa-copy', 'confirm' => true]);
971
+ }
972
+
973
+ function button_icon_copy($data = '') {
974
+ $this->btn('copy', '', ['data' => $data, 'icon' => 'fa-copy', 'confirm' => true, 'title' => __('Duplicate', 'newsletter')]);
975
+ }
976
+
977
+ /**
978
+ * Creates a button with "delete" action.
979
+ * @param type $data
980
+ */
981
+ function button_delete($data = '') {
982
+ $this->btn('delete', __('Delete', 'newsletter'), ['data' => $data, 'icon' => 'fa-times', 'confirm' => true, 'style' => 'background-color: darkred; color: #ffffff']);
983
+ }
984
+
985
+ function button_icon_delete($data = '') {
986
+ $this->btn('delete', '', ['data' => $data, 'icon' => 'fa-times', 'confirm' => true, 'title' => __('Delete', 'newsletter'), 'style' => 'background-color: darkred; color: #ffffff']);
987
+ }
988
+
989
+ function button_icon_configure($url) {
990
+ $this->btn_link($url, '', ['icon' => 'fa-cog', 'title' => __('Configure', 'newsletter')]);
991
+ }
992
+
993
+ function button_icon_subscribers($url) {
994
+ $this->btn_link($url, '', ['icon' => 'fa-users', 'title' => __('Subscribers', 'newsletter')]);
995
+ }
996
+
997
+ function button_statistics($url) {
998
+ $this->btn_link($url, __('Statistics', 'newsletter'), ['icon' => 'fa-chart-bar']);
999
+ }
1000
+
1001
+ function button_icon_statistics($url) {
1002
+ $this->btn_link($url, '', ['icon' => 'fa-chart-bar', 'title' => __('Statistics', 'newsletter')]);
1003
+ }
1004
+
1005
+ function button_icon_view($url) {
1006
+ $this->btn_link($url, '', ['icon' => 'fa-eye', 'title' => __('View', 'newsletter'), 'target' => '_blank']);
1007
+ }
1008
+
1009
+ function button_icon_newsletters($url) {
1010
+ $this->btn_link($url, '', ['icon' => 'fa-file-alt', 'title' => __('Newsletters', 'newsletter')]);
1011
+ }
1012
+
1013
+ function button_icon_design($url) {
1014
+ $this->btn_link($url, '', ['icon' => 'fa-paint-brush', 'title' => __('Design', 'newsletter')]);
1015
+ }
1016
+
1017
+ function button_icon_edit($url) {
1018
+ $this->btn_link($url, '', ['icon' => 'fa-edit', 'title' => __('Edit', 'newsletter')]);
1019
+ }
1020
+
1021
+ function button_icon_back($url) {
1022
+ $this->btn_link($url, '', ['icon' => 'fa-chevron-left', 'title' => __('Back', 'newsletter')]);
1023
+ }
1024
+
1025
+ function button_icon($action, $icon, $title = '', $data = '', $confirm = false) {
1026
+ $this->btn($action, '', ['data' => $data, 'icon' => $icon, 'title' => $title, 'confirm' => $confirm]);
1027
+ }
1028
+
1029
+ function button_back($url) {
1030
+ $this->btn_link($url, __('Back', 'newsletter'), ['icon' => 'fa-chevron-left']);
1031
+ }
1032
+
1033
+ function button_test($action = 'test') {
1034
+ $this->btn($action, __('Test', 'newsletter'), ['icon' => 'fa-vial']);
1035
+ }
1036
+
1037
+ /**
1038
+ * @deprecated
1039
+ */
1040
+ function button_primary($action, $label, $function = null) {
1041
+ if ($function != null) {
1042
+ echo '<button class="button-primary" onclick="this.form.act.value=\'' . esc_attr($action) . '\';' . esc_attr($function) . '">', $label, '</button>';
1043
+ } else {
1044
+ echo '<button class="button-primary" onclick="this.form.act.value=\'' . esc_attr($action) . '\'; return true;"/>', $label, '</button>';
1045
+ }
1046
+ }
1047
+
1048
+ /**
1049
+ * @deprecated
1050
+ */
1051
+ function button_confirm($action, $label, $message = '', $data = '') {
1052
+ $this->btn($action, $label, ['data' => $data, 'confirm' => $message]);
1053
+ }
1054
+
1055
+ /**
1056
+ * @deprecated
1057
+ * @param string $url
1058
+ * @param string $label Not escaped.
1059
+ */
1060
+ function button_link($url, $label = '') {
1061
+ echo '<a href="', esc_attr($url), '" class="button-primary">', $label, '</a>';
1062
+ }
1063
+
1064
+ function editor($name, $rows = 5, $cols = 75) {
1065
+ echo '<textarea class="visual" name="options[' . esc_attr($name) . ']" style="width: 100%" wrap="off" rows="' . esc_attr($rows) . '">';
1066
+ echo esc_html($this->get_value($name));
1067
+ echo '</textarea>';
1068
+ }
1069
+
1070
+ function wp_editor($name, $settings = []) {
1071
+
1072
+ add_filter('mce_buttons', function ($mce_buttons) {
1073
+ $mce_buttons[] = 'wp_add_media';
1074
+ //$mce_buttons[] = 'wp_code';
1075
+ return $mce_buttons;
1076
+ });
1077
+
1078
+ $settings = array_merge(['media_buttons' => false], $settings);
1079
+
1080
+ $value = $this->get_value($name);
1081
+ wp_editor($value, $name, array_merge(array(
1082
+ 'tinymce' => array('content_css' => plugins_url('newsletter') . '/admin/wp-editor.css?ver=' . NEWSLETTER_VERSION),
1083
+ 'textarea_name' => 'options[' . esc_attr($name) . ']',
1084
+ 'wpautop' => false
1085
+ ), $settings));
1086
+ }
1087
+
1088
+ function textarea($name, $width = '100%', $height = '50') {
1089
+ $value = $this->get_value($name);
1090
+ if (is_array($value)) {
1091
+ $value = implode("\n", $value);
1092
+ }
1093
+ echo '<textarea id="options-' . esc_attr($name) . '" class="dynamic" name="options[' . esc_attr($name) . ']" wrap="off" style="width:' . esc_attr($width) . ';height:' . esc_attr($height) . '">';
1094
+ echo esc_html($value);
1095
+ echo '</textarea>';
1096
+ }
1097
+
1098
+ function textarea_fixed($name, $width = '100%', $height = '200') {
1099
+ $value = $this->get_value($name);
1100
+ $name = esc_attr($name);
1101
+ echo '<textarea id="options-', $name, '" name="options[', $name, ']" wrap="off" style="width:', esc_attr($width), ';height:', esc_attr($height), 'px">';
1102
+ echo esc_html($value);
1103
+ echo '</textarea>';
1104
+ }
1105
+
1106
+ function textarea_preview($name, $width = '100%', $height = '200', $header = '', $footer = '', $switch_button = true) {
1107
+ $value = $this->get_value($name);
1108
+ $name = esc_attr($name);
1109
+ if ($switch_button) {
1110
+ echo '<input class="button-primary" type="button" onclick="newsletter_textarea_preview(\'options-', $name, '\', \'\', \'\')" value="Switch editor/preview">';
1111
+ echo '<br><br>';
1112
+ }
1113
+ echo '<div style="box-sizing: border-box; position: relative; margin: 0; padding: 0; width:' . esc_attr($width) . '; height:' . esc_attr($height) . '">';
1114
+ echo '<textarea id="options-', $name, '" name="options[', $name, ']" wrap="off" style="width:' . esc_attr($width) . ';height:' . esc_attr($height) . 'px">';
1115
+ echo esc_html($value);
1116
+ echo '</textarea>';
1117
+ echo '<div id="options-', $name, '-preview" style="box-sizing: border-box; background-color: #eee; border: 1px solid #bbb; padding: 15px; width: auto; position: absolute; top: 20px; left: 20px; box-shadow: 0 0 20px #777; z-index: 10000; display: none">';
1118
+ echo '<iframe id="options-', $name, '-iframe" class="tnp-editor-preview-desktop"></iframe>';
1119
+ echo '<iframe id="options-', $name, '-iframe-phone" class="tnp-editor-preview-mobile"></iframe>';
1120
+ echo '</div>';
1121
+ echo '</div>';
1122
+ }
1123
+
1124
+ function email($prefix, $editor = null, $disable_option = false, $settings = array()) {
1125
+ if ($disable_option) {
1126
+ $this->disabled($prefix . '_disabled');
1127
+ echo '<br>';
1128
+ }
1129
+
1130
+ $this->text($prefix . '_subject', 90, 'Subject');
1131
+ echo '<br><br>';
1132
+
1133
+ if ($editor == 'wordpress') {
1134
+ $this->wp_editor($prefix . '_message', $settings);
1135
+ } else if ($editor == 'textarea') {
1136
+ $this->textarea($prefix . '_message');
1137
+ } else {
1138
+ $this->editor($prefix . '_message');
1139
+ }
1140
+ }
1141
+
1142
+ /**
1143
+ * Standard checkbox, when not checked no value is transmitted (checkbox2).
1144
+ *
1145
+ * @param string $name
1146
+ * @param string $label
1147
+ */
1148
+ function checkbox($name, $label = '') {
1149
+ if ($label != '') {
1150
+ echo '<label>';
1151
+ }
1152
+ echo '<input type="checkbox" id="options-' . esc_attr($name) . '" name="options[' . esc_attr($name) . ']" value="1"';
1153
+ if (!empty($this->data[$name])) {
1154
+ echo ' checked';
1155
+ }
1156
+ echo '>';
1157
+ if ($label != '') {
1158
+ echo '&nbsp;' . esc_html($label) . '</label>';
1159
+ }
1160
+ }
1161
+
1162
+ /**
1163
+ * Checkbox with a hidden field to transmit 1 or 0 even when the checkbox is not checked.
1164
+ *
1165
+ * @param string $name
1166
+ * @param string $label
1167
+ */
1168
+ function checkbox2($name, $label = '') {
1169
+ if ($label != '') {
1170
+ echo '<label>';
1171
+ }
1172
+ echo '<input type="checkbox" id="' . esc_attr($name) . '" onchange="document.getElementById(\'' . esc_attr($name) . '_hidden\').value=this.checked?\'1\':\'0\'"';
1173
+ if (!empty($this->data[$name])) {
1174
+ echo ' checked';
1175
+ }
1176
+ echo '>';
1177
+ if ($label != '') {
1178
+ echo '&nbsp;' . esc_html($label) . '</label>';
1179
+ }
1180
+ echo '<input type="hidden" id="' . esc_attr($name) . '_hidden" name="options[' . esc_attr($name) . ']" value="';
1181
+
1182
+ echo empty($this->data[$name]) ? '0' : '1';
1183
+ echo '">';
1184
+ }
1185
+
1186
+ function radio($name, $value, $label = '') {
1187
+ if ($label != '') {
1188
+ echo '<label>';
1189
+ }
1190
+ echo '<input type="radio" id="' . esc_attr($name) . '" name="options[' . esc_attr($name) . ']" value="' . esc_attr($value) . '"';
1191
+ $v = $this->get_value($name);
1192
+ if ($v == $value) {
1193
+ echo ' checked';
1194
+ }
1195
+ echo '>';
1196
+ if ($label != '') {
1197
+ echo '&nbsp;' . esc_html($label) . '</label>';
1198
+ }
1199
+ }
1200
+
1201
+ /**
1202
+ * Creates a checkbox named $name and checked if the internal data contains under
1203
+ * the key $name an array containig the passed value.
1204
+ */
1205
+ function checkbox_group($name, $value, $label = '', $attrs = []) {
1206
+ $attrs = wp_parse_args($attrs, ['label_escape' => true]);
1207
+ echo '<label><input type="checkbox" id="' . esc_attr($name) . '" name="options[' . esc_attr($name) . '][]" value="' . esc_attr($value) . '"';
1208
+ if (isset($this->data[$name]) && is_array($this->data[$name]) && array_search($value, $this->data[$name]) !== false) {
1209
+ echo ' checked';
1210
+ }
1211
+ echo '>';
1212
+ if ($label != '') {
1213
+ if ($attrs['label_escape']) {
1214
+ echo esc_html($label);
1215
+ } else {
1216
+ echo $label;
1217
+ }
1218
+ }
1219
+ echo '</label>';
1220
+ }
1221
+
1222
+ function checkboxes($name, $options) {
1223
+ echo '<div class="tnpc-checkboxes">';
1224
+ foreach ($options as $value => $label) {
1225
+ $this->checkbox_group($name, $value, $label);
1226
+ }
1227
+ echo '<div style="clear: both"></div>';
1228
+ echo '</div>';
1229
+ }
1230
+
1231
+ function color($name, $default = '') {
1232
+ $value = esc_attr($this->get_value($name, $default));
1233
+ $name = esc_attr($name);
1234
+ echo '<input class="tnpc-color" id="options-', $name, '" name="options[', $name, ']" type="text" value="', $value, '">';
1235
+ }
1236
+
1237
+ /** Creates a set of checkbox named $name_[category id] (so they are posted with distinct names).
1238
+ */
1239
+ function categories($name = 'category') {
1240
+ $categories = get_categories();
1241
+ echo '<div class="tnpc-checkboxes">';
1242
+ foreach ($categories as $c) {
1243
+ $this->checkbox($name . '_' . $c->cat_ID, esc_html($c->cat_name));
1244
+ }
1245
+ echo '<div style="clear: both"></div>';
1246
+ }
1247
+
1248
+ /**
1249
+ * Creates a set of checkbox to activate the profile preferences. Every checkbox has a DIV around to
1250
+ * be formatted.
1251
+ */
1252
+ function categories_group($name, $show_mode = false) {
1253
+ $categories = get_categories();
1254
+ if ($show_mode) {
1255
+ $this->select($name . '_mode', array('include' => 'To be included', 'exclude' => 'To be excluded'));
1256
+ }
1257
+ echo '<div class="tnpc-checkboxes">';
1258
+ foreach ($categories as &$c) {
1259
+ $this->checkbox_group($name, $c->cat_ID, esc_html($c->cat_name));
1260
+ }
1261
+ echo '<div style="clear: both"></div>';
1262
+ }
1263
+
1264
+ /**
1265
+ * Creates a set of checkboxes named $name_[preference number] (so they are
1266
+ * distinct fields).
1267
+ * Empty preferences are skipped.
1268
+ */
1269
+ function preferences($name = 'preferences') {
1270
+ $lists = Newsletter::instance()->get_lists();
1271
+
1272
+ echo '<div class="tnpc-checkboxes">';
1273
+ foreach ($lists as $list) {
1274
+ $this->checkbox2($name . '_' . $list->id, esc_html($list->name));
1275
+ }
1276
+ echo '<div style="clear: both"></div>';
1277
+ }
1278
+
1279
+ /** A list of all lists defined each one with a checkbox to select it. An array
1280
+ * of ID of all checked lists is submitted.
1281
+ *
1282
+ * @param string $name
1283
+ */
1284
+ function lists($name = 'lists') {
1285
+ echo '<input type="hidden" name="tnp_fields[' . esc_attr($name) . ']" value="array">';
1286
+ $this->preferences_group($name);
1287
+ }
1288
+
1289
+ function lists_checkboxes($name = 'lists') {
1290
+ $this->preferences_group($name);
1291
+ }
1292
+
1293
+ /**
1294
+ * Creates a set of checkboxes all names $name[] and the preference number as value
1295
+ * so the selected checkboxes are retrieved as an array of values ($REQUEST[$name]
1296
+ * will be an array if at east one preference is checked).
1297
+ */
1298
+ function preferences_group($name = 'preferences') {
1299
+
1300
+ $lists = Newsletter::instance()->get_lists();
1301
+
1302
+ echo '<div class="tnpc-lists">';
1303
+ foreach ($lists as $list) {
1304
+ $this->checkbox_group($name, $list->id, '<span>' . $list->id . '</span> ' . esc_html($list->name), ['label_escape' => false]);
1305
+ }
1306
+ echo '<a href="https://www.thenewsletterplugin.com/documentation/newsletter-lists" target="_blank">'
1307
+ . 'Click here to read more about lists.'
1308
+ . '</a>';
1309
+ echo '</div>';
1310
+ }
1311
+
1312
+ /** Creates as many selects as the active preferences with the three values
1313
+ * 'any', 'yes', 'no' corresponding to the values 0, 1, 2.
1314
+ */
1315
+ function preferences_selects($name = 'preferences', $skip_empty = false) {
1316
+ $lists = Newsletter::instance()->get_lists();
1317
+
1318
+ echo '<div class="newsletter-preferences-group">';
1319
+ foreach ($lists as $list) {
1320
+
1321
+ echo '<div class="newsletter-preferences-item">';
1322
+
1323
+ $this->select($name . '_' . $list->id, array(0 => 'Any', 1 => 'Yes', 2 => 'No'));
1324
+ echo '(' . $list->id . ') ' . esc_html($list->name);
1325
+
1326
+ echo '</div>';
1327
+ }
1328
+ echo '<div style="clear: both"></div>';
1329
+ echo '<a href="https://www.thenewsletterplugin.com/plugins/newsletter/newsletter-preferences" target="_blank">Click here know more about preferences.</a> They can be configured on Subscription/Form field panel.';
1330
+ echo '</div>';
1331
+ }
1332
+
1333
+ /**
1334
+ * Creates a single select with the active preferences.
1335
+ */
1336
+ function preferences_select($name = 'preference', $empty_label = null) {
1337
+ $lists = $this->get_list_options($empty_label);
1338
+ $this->select($name, $lists);
1339
+ echo ' <a href="admin.php?page=newsletter_subscription_lists" target="_blank"><i class="fas fa-edit"></i></a>';
1340
+ }
1341
+
1342
+ function lists_select($name = 'list', $empty_label = null) {
1343
+ $lists = $this->get_list_options($empty_label);
1344
+ $this->select($name, $lists);
1345
+ }
1346
+
1347
+ function lists_select_with_notes($name = 'list', $empty_label = null) {
1348
+
1349
+ $value = $this->get_value($name);
1350
+
1351
+ $lists = Newsletter::instance()->get_lists();
1352
+ $options = [];
1353
+ if ($empty_label) {
1354
+ $options[''] = $empty_label;
1355
+ }
1356
+
1357
+ foreach ($lists as $list) {
1358
+ $options['' . $list->id] = '(' . $list->id . ') ' . esc_html($list->name);
1359
+ }
1360
+
1361
+ $this->select($name, $options, null, ['onchange' => 'tnp_lists_toggle(this); return true;']);
1362
+ echo '<div id="options-', esc_attr($name), '-notes" class="tnpc_lists_notes">';
1363
+ foreach ($lists as $list) {
1364
+ $id = $list->id;
1365
+ $notes = apply_filters('newsletter_lists_notes', [], $id);
1366
+
1367
+ echo '<div class="list_', $id, '" style="display: ', ($value == $id ? 'block' : 'none'), '">';
1368
+ if ($list->forced) {
1369
+ echo 'Enforced on subscription<br>';
1370
+ }
1371
+ echo implode('<br>', $notes);
1372
+ echo '</div>';
1373
+ }
1374
+ echo '</div>';
1375
+ }
1376
+
1377
+ function public_lists_select($name = 'list', $empty_label = null) {
1378
+ $lists = $this->get_public_list_options($empty_label);
1379
+ $this->select($name, $lists);
1380
+ }
1381
+
1382
+ /**
1383
+ * Generates an associative array with the active lists to be used in a select.
1384
+ * @param string $empty_label
1385
+ * @return array
1386
+ */
1387
+ function get_list_options($empty_label = null) {
1388
+ $objs = Newsletter::instance()->get_lists();
1389
+ $lists = array();
1390
+ if ($empty_label) {
1391
+ $lists[''] = $empty_label;
1392
+ }
1393
+ foreach ($objs as $list) {
1394
+ $lists['' . $list->id] = '(' . $list->id . ') ' . esc_html($list->name);
1395
+ }
1396
+ return $lists;
1397
+ }
1398
+
1399
+ function get_public_list_options($empty_label = null) {
1400
+ $objs = Newsletter::instance()->get_lists_public();
1401
+ $lists = array();
1402
+ if ($empty_label) {
1403
+ $lists[''] = $empty_label;
1404
+ }
1405
+ foreach ($objs as $list) {
1406
+ $lists['' . $list->id] = '(' . $list->id . ') ' . esc_html($list->name);
1407
+ }
1408
+ return $lists;
1409
+ }
1410
+
1411
+ function date($name) {
1412
+ $this->hidden($name);
1413
+ $year = date('Y', $this->data[$name]);
1414
+ $day = date('j', $this->data[$name]);
1415
+ $month = date('m', $this->data[$name]);
1416
+ $onchange = "this.form.elements['options[" . esc_attr($name) . "]'].value = new Date(document.getElementById('" . esc_attr($name) . "_year').value, document.getElementById('" . esc_attr($name) . "_month').value, document.getElementById('" . esc_attr($name) . "_day').value, 12, 0, 0).getTime()/1000";
1417
+ echo '<select id="' . $name . '_month" onchange="' . esc_attr($onchange) . '">';
1418
+ for ($i = 0; $i < 12; $i++) {
1419
+ echo '<option value="' . $i . '"';
1420
+ if ($month - 1 == $i) {
1421
+ echo ' selected';
1422
+ }
1423
+ echo '>' . date('F', mktime(0, 0, 0, $i + 1, 1, 2000)) . '</option>';
1424
+ }
1425
+ echo '</select>';
1426
+
1427
+ echo '<select id="' . esc_attr($name) . '_day" onchange="' . esc_attr($onchange) . '">';
1428
+ for ($i = 1; $i <= 31; $i++) {
1429
+ echo '<option value="' . $i . '"';
1430
+ if ($day == $i) {
1431
+ echo ' selected';
1432
+ }
1433
+ echo '>' . $i . '</option>';
1434
+ }
1435
+ echo '</select>';
1436
+
1437
+ echo '<select id="' . esc_attr($name) . '_year" onchange="' . esc_attr($onchange) . '">';
1438
+ for ($i = 2011; $i <= 2021; $i++) {
1439
+ echo '<option value="' . $i . '"';
1440
+ if ($year == $i) {
1441
+ echo ' selected';
1442
+ }
1443
+ echo '>' . $i . '</option>';
1444
+ }
1445
+ echo '</select>';
1446
+ }
1447
+
1448
+ /**
1449
+ * Creates a set of fields to collect a date and sends back the triplet year, month and day.
1450
+ *
1451
+ * @param string $name
1452
+ */
1453
+ function date2($name) {
1454
+ $year = $this->get_value($name . '_year');
1455
+ $day = $this->get_value($name . '_day');
1456
+ $month = $this->get_value($name . '_month');
1457
+
1458
+ echo '<select name="options[' . $name . '_month]">';
1459
+ echo '<option value="">-</option>';
1460
+ for ($i = 1; $i <= 12; $i++) {
1461
+ echo '<option value="' . $i . '"';
1462
+ if ($month == $i) {
1463
+ echo ' selected';
1464
+ }
1465
+ echo '>' . date_i18n('F', mktime(0, 0, 0, $i, 1, 2000)) . '</option>';
1466
+ }
1467
+ echo '</select>';
1468
+
1469
+ echo '<select name="options[' . esc_attr($name) . '_day]">';
1470
+ echo '<option value="">-</option>';
1471
+ for ($i = 1; $i <= 31; $i++) {
1472
+ echo '<option value="' . $i . '"';
1473
+ if ($day == $i) {
1474
+ echo ' selected';
1475
+ }
1476
+ echo '>' . $i . '</option>';
1477
+ }
1478
+ echo '</select>';
1479
+
1480
+ echo '<select name="options[' . esc_attr($name) . '_year]">';
1481
+ echo '<option value="">-</option>';
1482
+ for ($i = 2011; $i <= 2021; $i++) {
1483
+ echo '<option value="' . $i . '"';
1484
+ if ($year == $i) {
1485
+ echo ' selected';
1486
+ }
1487
+ echo '>' . $i . '</option>';
1488
+ }
1489
+ echo '</select>';
1490
+ }
1491
+
1492
+ /**
1493
+ * Date and time (hour) selector. Timestamp stored.
1494
+ */
1495
+ function datetime($name) {
1496
+ echo '<input type="hidden" name="tnp_fields[' . esc_attr($name) . ']" value="datetime">';
1497
+ $value = (int) $this->get_value($name);
1498
+ if (empty($value)) {
1499
+ $value = time();
1500
+ }
1501
+
1502
+ $time = $value + get_option('gmt_offset') * 3600;
1503
+ $year = gmdate('Y', $time);
1504
+ $day = gmdate('j', $time);
1505
+ $month = gmdate('m', $time);
1506
+ $hour = gmdate('H', $time);
1507
+
1508
+ echo '<select name="' . esc_attr($name) . '_month">';
1509
+ for ($i = 1; $i <= 12; $i++) {
1510
+ echo '<option value="' . $i . '"';
1511
+ if ($month == $i) {
1512
+ echo ' selected';
1513
+ }
1514
+ echo '>' . date('F', mktime(0, 0, 0, $i, 1, 2000)) . '</option>';
1515
+ }
1516
+ echo '</select>';
1517
+
1518
+ echo '<select name="' . esc_attr($name) . '_day">';
1519
+ for ($i = 1; $i <= 31; $i++) {
1520
+ echo '<option value="' . $i . '"';
1521
+ if ($day == $i) {
1522
+ echo ' selected';
1523
+ }
1524
+ echo '>' . $i . '</option>';
1525
+ }
1526
+ echo '</select>';
1527
+
1528
+ $last_year = date('Y') + 2;
1529
+ echo '<select name="' . esc_attr($name) . '_year">';
1530
+ for ($i = 2011; $i <= $last_year; $i++) {
1531
+ echo '<option value="' . $i . '"';
1532
+ if ($year == $i) {
1533
+ echo ' selected';
1534
+ }
1535
+ echo '>' . $i . '</option>';
1536
+ }
1537
+ echo '</select>';
1538
+
1539
+ echo '<select name="' . esc_attr($name) . '_hour">';
1540
+ for ($i = 0; $i <= 23; $i++) {
1541
+ echo '<option value="' . $i . '"';
1542
+ if ($hour == $i) {
1543
+ echo ' selected';
1544
+ }
1545
+ echo '>' . $i . ':00</option>';
1546
+ }
1547
+ echo '</select>';
1548
+ }
1549
+
1550
+ function hours($name) {
1551
+ $hours = array();
1552
+ for ($i = 0; $i < 24; $i++) {
1553
+ $hours['' . $i] = sprintf('%02d', $i) . ':00';
1554
+ }
1555
+ $this->select($name, $hours);
1556
+ }
1557
+
1558
+ function days($name) {
1559
+ $days = array(0 => 'Every day', 1 => 'Monday', 2 => 'Tuesday', 3 => 'Wednesday', 4 => 'Thursday', 5 => 'Friday', 6 => 'Saturday', 7 => 'Sunday');
1560
+ $this->select($name, $days);
1561
+ }
1562
+
1563
+ function init($options = array()) {
1564
+ $cookie_name = 'newsletter_tab';
1565
+ if (isset($options['cookie_name'])) {
1566
+ $cookie_name = $options['cookie_name'];
1567
+ }
1568
+ echo '<script type="text/javascript">
1569
+ jQuery(document).ready(function(){
1570
+
1571
+ tnp_controls_init();
1572
+
1573
+ jQuery("textarea.dynamic").focus(function() {
1574
+ jQuery("textarea.dynamic").css("height", "50px");
1575
+ jQuery(this).css("height", "400px");
1576
+ });
1577
+ tabs = jQuery("#tabs").tabs({
1578
+ active : jQuery.cookie("' . $cookie_name . '"),
1579
+ activate : function( event, ui ){
1580
+ jQuery.cookie("' . $cookie_name . '", ui.newTab.index(),{expires: 1});
1581
+ }
1582
+ });
1583
+ jQuery(".tnp-tabs").tabs({});
1584
+
1585
+ });
1586
+ function newsletter_media(name) {
1587
+ var tnp_uploader = wp.media({
1588
+ title: "Select an image",
1589
+ button: {
1590
+ text: "Select"
1591
+ },
1592
+ multiple: false
1593
+ }).on("select", function() {
1594
+ var media = tnp_uploader.state().get("selection").first();
1595
+ document.getElementById(name + "_id").value = media.id;
1596
+ jQuery("#" + name + "_id").trigger("change");
1597
+ //alert(media.attributes.url);
1598
+ if (media.attributes.url.substring(0, 0) == "/") {
1599
+ media.attributes.url = "' . site_url('/') . '" + media.attributes.url;
1600
+ }
1601
+ document.getElementById(name + "_url").value = media.attributes.url;
1602
+
1603
+ var img_url = media.attributes.url;
1604
+ if (typeof media.attributes.sizes.medium !== "undefined") img_url = media.attributes.sizes.medium.url;
1605
+ if (img_url.substring(0, 0) == "/") {
1606
+ img_url = "' . site_url('/') . '" + img_url;
1607
+ }
1608
+ document.getElementById(name + "_img").src = img_url;
1609
+ }).open();
1610
+ }
1611
+ function newsletter_media_remove(name) {
1612
+ if (confirm("Are you sure?")) {
1613
+ document.getElementById(name + "_id").value = "";
1614
+ document.getElementById(name + "_url").value = "";
1615
+ document.getElementById(name + "_img").src = "' . plugins_url('newsletter') . '/images/nomedia.png";
1616
+ }
1617
+ }
1618
+ function newsletter_textarea_preview(id, header, footer) {
1619
+ var d = document.getElementById(id + "-iframe").contentWindow.document;
1620
+ d.open();
1621
+ if (templateEditor) {
1622
+ d.write(templateEditor.getValue());
1623
+ } else {
1624
+ d.write(header + document.getElementById(id).value + footer);
1625
+ }
1626
+ d.close();
1627
+
1628
+ var d = document.getElementById(id + "-iframe-phone").contentWindow.document;
1629
+ d.open();
1630
+ if (templateEditor) {
1631
+ d.write(templateEditor.getValue());
1632
+ } else {
1633
+ d.write(header + document.getElementById(id).value + footer);
1634
+ }
1635
+ d.close();
1636
+ //jQuery("#" + id + "-iframe-phone").toggle();
1637
+ jQuery("#" + id + "-preview").toggle();
1638
+ }
1639
+ function tnp_select_images(state) {
1640
+ if (!state.id) { return state.text; }
1641
+ var $state = jQuery("<span class=\"tnp-select2-option\"><img style=\"height: 20px!important; position: relative; top: 5px\" src=\"" + state.element.getAttribute("image") + "\"> " + state.text + "</span>");
1642
+ return $state;
1643
+ }
1644
+ function tnp_select_images_selection(state) {
1645
+ if (!state.id) { return state.text; }
1646
+ var $state = jQuery("<span class=\"tnp-select2-option\"><img style=\"height: 20px!important; position: relative; top: 5px\" src=\"" + state.element.getAttribute("image") + "\"> " + state.text + "</span>");
1647
+ return $state;
1648
+ }
1649
+
1650
+
1651
+ </script>
1652
+ ';
1653
+ echo '<input name="act" type="hidden" value=""/>';
1654
+ echo '<input name="btn" type="hidden" value=""/>';
1655
+ wp_nonce_field('save');
1656
+ }
1657
+
1658
+ function log_level($name = 'log_level') {
1659
+ $this->select($name, array(0 => 'None', 2 => 'Error', 3 => 'Normal', 4 => 'Debug'));
1660
+ }
1661
+
1662
+ function update_option($name, $data = null) {
1663
+ if ($data == null) {
1664
+ $data = $this->data;
1665
+ }
1666
+ update_option($name, $data);
1667
+ if (isset($data['log_level'])) {
1668
+ update_option($name . '_log_level', $data['log_level']);
1669
+ }
1670
+ }
1671
+
1672
+ function js_redirect($url) {
1673
+ echo '<script>';
1674
+ echo 'location.href="' . $url . '"';
1675
+ echo '</script>';
1676
+ die();
1677
+ }
1678
+
1679
+ /**
1680
+ * @deprecated
1681
+ */
1682
+ function get_test_subscribers() {
1683
+ return NewsletterUsers::instance()->get_test_users();
1684
+ }
1685
+
1686
+ /**
1687
+ * Attributes:
1688
+ * weight: [true|false]
1689
+ * color: [true|false]
1690
+ *
1691
+ * @param string $name
1692
+ * @param array $attrs
1693
+ */
1694
+ function css_font($name = 'font', $attrs = array()) {
1695
+ $default = [
1696
+ 'color' => true,
1697
+ 'weight' => true,
1698
+ 'hide_size' => false,
1699
+ 'hide_weight' => false,
1700
+ 'hide_color' => false,
1701
+ ];
1702
+ $attrs = array_merge($default, $attrs);
1703
+ $this->css_font_family($name . '_family', !empty($attrs['family_default']));
1704
+ if (!$attrs['hide_size']) {
1705
+ $this->css_font_size($name . '_size', !empty($attrs['size_default']));
1706
+ }
1707
+ if ($attrs['weight'] && !$attrs['hide_weight']) {
1708
+ $this->css_font_weight($name . '_weight', !empty($attrs['weight_default']));
1709
+ }
1710
+ if ($attrs['color'] && !$attrs['hide_color']) {
1711
+ $this->color($name . '_color');
1712
+ }
1713
+ }
1714
+
1715
+ function css_font_size($name = 'font_size', $show_empty_option = false) {
1716
+ $value = $this->get_value($name);
1717
+
1718
+ echo '<select class="tnpf-font-size" id="options-', esc_attr($name), '" name="options[', esc_attr($name), ']">';
1719
+ if ($show_empty_option) {
1720
+ echo "<option value=''>-</option>";
1721
+ }
1722
+ for ($i = 8; $i <= 50; $i++) {
1723
+ echo '<option value="' . $i . '"';
1724
+ if ($value == $i) {
1725
+ echo ' selected';
1726
+ }
1727
+ echo '>' . $i . '</option>';
1728
+ }
1729
+ echo '</select>';
1730
+ }
1731
+
1732
+ function css_font_weight($name = 'font_weight', $show_empty_option = false) {
1733
+ $value = $this->get_value($name);
1734
+
1735
+ $fonts = array('normal' => 'Normal', 'bold' => 'Bold');
1736
+
1737
+ echo '<select class="tnpf-font-weight" id="options-' . esc_attr($name) . '" name="options[' . esc_attr($name) . ']">';
1738
+ if ($show_empty_option) {
1739
+ echo "<option value=''>-</option>";
1740
+ }
1741
+ foreach ($fonts as $key => $font) {
1742
+ echo '<option value="', esc_attr($key), '"';
1743
+ if ($value == $key) {
1744
+ echo ' selected';
1745
+ }
1746
+ echo '>', esc_html($font), '</option>';
1747
+ }
1748
+ echo '</select>';
1749
+ }
1750
+
1751
+ function css_font_family($name = 'font_family', $show_empty_option = false) {
1752
+ $value = $this->get_value($name);
1753
+
1754
+ $fonts = [];
1755
+ if ($show_empty_option) {
1756
+ $fonts[''] = 'Default';
1757
+ }
1758
+
1759
+ $fonts = array_merge($fonts, ['Helvetica, Arial, sans-serif' => 'Helvetica, Arial',
1760
+ 'Arial Black, Gadget, sans-serif' => 'Arial Black, Gadget',
1761
+ 'Garamond, serif' => 'Garamond',
1762
+ 'Courier, monospace' => 'Courier',
1763
+ 'Comic Sans MS, cursive' => 'Comic Sans MS',
1764
+ 'Impact, Charcoal, sans-serif' => 'Impact, Charcoal',
1765
+ 'Tahoma, Geneva, sans-serif' => 'Tahoma, Geneva',
1766
+ 'Times New Roman, Times, serif' => 'Times New Roman',
1767
+ 'Verdana, Geneva, sans-serif' => 'Verdana, Geneva']);
1768
+
1769
+ echo '<select class="tnpf-font-family" id="options-', esc_attr($name), '" name="options[', esc_attr($name), ']">';
1770
+ foreach ($fonts as $font => $label) {
1771
+ echo '<option value="', esc_attr($font), '"';
1772
+ if ($value == $font) {
1773
+ echo ' selected';
1774
+ }
1775
+ echo '>', esc_html($label), '</option>';
1776
+ }
1777
+ echo '</select>';
1778
+ }
1779
+
1780
+ function css_text_align($name) {
1781
+ $options = array('left' => __('Left', 'newsletter'), 'right' => __('Right', 'newsletter'),
1782
+ 'center' => __('Center', 'newsletter'));
1783
+ $this->select($name, $options);
1784
+ }
1785
+
1786
+ function css_border($name) {
1787
+ $value = $this->get_value($name . '_width');
1788
+
1789
+ echo 'width&nbsp;<select id="options-' . esc_attr($name) . '-width" name="options[' . esc_attr($name) . '_width]">';
1790
+ for ($i = 0; $i < 10; $i++) {
1791
+ echo '<option value="' . $i . '"';
1792
+ if ($value == $i) {
1793
+ echo ' selected';
1794
+ }
1795
+ echo '>' . $i . '</option>';
1796
+ }
1797
+ echo '</select>&nbsp;px&nbsp;&nbsp;';
1798
+
1799
+ $this->select($name . '_type', array('solid' => 'Solid', 'dashed' => 'Dashed'));
1800
+
1801
+ $this->color($name . '_color');
1802
+
1803
+ $value = $this->get_value($name . '_radius');
1804
+
1805
+ echo '&nbsp;&nbsp;radius&nbsp;<select id="options-' . esc_attr($name) . '-radius" name="options[' . esc_attr($name) . '_radius]">';
1806
+ for ($i = 0; $i < 10; $i++) {
1807
+ echo '<option value="' . $i . '"';
1808
+ if ($value == $i) {
1809
+ echo ' selected';
1810
+ }
1811
+ echo '>' . $i . '</option>';
1812
+ }
1813
+ echo '</select>&nbsp;px';
1814
+ }
1815
+
1816
+ /**
1817
+ * Media selector using the media library of WP. Produces a field which values is an array containing 'id' and 'url'.
1818
+ *
1819
+ * @param string $name
1820
+ */
1821
+ function media($name) {
1822
+ if (isset($this->data[$name]['id'])) {
1823
+ $media_id = (int) $this->data[$name]['id'];
1824
+ $media = wp_get_attachment_image_src($media_id, 'medium');
1825
+ $media_full = wp_get_attachment_image_src($media_id, 'full');
1826
+ } else {
1827
+ $media = false;
1828
+ }
1829
+ echo '<div style="position: relative">';
1830
+ echo '<a style="position: absolute; top: 5px; right: 5px; background-color: none; color: #000; padding: 0px 5px 6px 5px; font-size: 24px; display: block; text-decoration: none" href="#" onclick="newsletter_media_remove(\'' . esc_attr($name) . '\'); return false">&times;</a>';
1831
+ if ($media === false) {
1832
+ $media = array('', '', '');
1833
+ $media_full = array('', '', '');
1834
+ $media_id = 0;
1835
+ echo '<img style="max-width: 200px; max-height: 150px; width: 100px;" id="' . esc_attr($name) . '_img" src="' . plugins_url('newsletter') . '/images/nomedia.png" onclick="newsletter_media(\'' . esc_attr($name) . '\')">';
1836
+ } else {
1837
+ echo '<img style="max-width: 200px; max-height: 150px;" id="' . esc_attr($name) . '_img" src="' . esc_attr($media[0]) . '" onclick="newsletter_media(\'' . esc_attr($name) . '\')">';
1838
+ }
1839
+
1840
+ echo '</div>';
1841
+ echo '<input type="hidden" id="' . esc_attr($name) . '_id" name="options[' . esc_attr($name) . '][id]" value="' . esc_attr($media_id) . '" size="5">';
1842
+ echo '<input type="hidden" id="' . esc_attr($name) . '_url" name="options[' . esc_attr($name) . '][url]" value="' . esc_attr($media_full[0]) . '" size="50">';
1843
+ }
1844
+
1845
+ function media_input($option, $name, $label) {
1846
+
1847
+ if (!empty($label)) {
1848
+ $output = '<label class="select" for="tnp_' . esc_attr($name) . '">' . esc_html($label) . ':</label>';
1849
+ }
1850
+ $output .= '<input id="tnp_' . esc_attr($name) . '" type="text" size="36" name="' . esc_attr($option) . '[' . esc_attr($name) . ']" value="' . esc_attr($val) . '" />';
1851
+ $output .= '<input id="tnp_' . esc_attr($name) . '_button" class="button-primary" type="button" value="Select Image" />';
1852
+ $output .= '<br class="clear"/>';
1853
+
1854
+ echo $output;
1855
+ }
1856
+
1857
+ function language($name = 'language', $empty_label = 'All') {
1858
+ if (!class_exists('SitePress') && !function_exists('pll_default_language') && !class_exists('TRP_Translate_Press')) {
1859
+ echo __('Install a multilanguage plugin.', 'newsletter');
1860
+ echo ' <a href="https://www.thenewsletterplugin.com/documentation/multilanguage" target="_blank">', __('Read more', 'newsletter'), '</a>';
1861
+ return;
1862
+ }
1863
+
1864
+ $languages = Newsletter::instance()->get_languages();
1865
+ if (!empty($empty_label)) {
1866
+ $languages = array_merge(array('' => $empty_label), $languages);
1867
+ }
1868
+ $this->select($name, $languages);
1869
+ }
1870
+
1871
+ function is_multilanguage() {
1872
+ return Newsletter::instance()->is_multilanguage();
1873
+ }
1874
+
1875
+ /**
1876
+ * Creates a checkbox group with all active languages. Each checkbox is named
1877
+ * $name[] and values with the relative language code.
1878
+ *
1879
+ * @param string $name
1880
+ */
1881
+ function languages($name = 'languages') {
1882
+ if (!$this->is_multilanguage()) {
1883
+ echo __('Install WPML or Polylang for multilanguage support', 'newsletter');
1884
+ return;
1885
+ }
1886
+
1887
+ $language_options = Newsletter::instance()->get_languages();
1888
+
1889
+ if (empty($language_options)) {
1890
+ echo __('Your multilanguage plugin is not supported or there are no languages defined', 'newsletter');
1891
+ return;
1892
+ }
1893
+
1894
+ $this->checkboxes_group($name, $language_options);
1895
+ }
1896
+
1897
+ /**
1898
+ * Prints a formatted date using the formats and timezone of WP, including the current date and time and the
1899
+ * time left to the passed time.
1900
+ *
1901
+ * @param int $time
1902
+ * @param int $now
1903
+ * @param bool $left
1904
+ * @return string
1905
+ */
1906
+ static function print_date($time = null, $now = false, $left = false) {
1907
+ if (is_null($time)) {
1908
+ $time = time();
1909
+ }
1910
+ if ($time == false) {
1911
+ $buffer = 'none';
1912
+ } else {
1913
+ $buffer = date_i18n(get_option('date_format') . ' ' . get_option('time_format'), $time + get_option('gmt_offset') * 3600);
1914
+
1915
+ if ($now) {
1916
+ $buffer .= ' (now: ' . gmdate(get_option('date_format') . ' ' .
1917
+ get_option('time_format'), time() + get_option('gmt_offset') * 3600);
1918
+ $buffer .= ')';
1919
+ }
1920
+ if ($left) {
1921
+ if ($time - time() < 0) {
1922
+ $buffer .= ', ' . (time() - $time) . ' seconds late';
1923
+ } else {
1924
+ $buffer .= ', ' . gmdate('H:i:s', $time - time()) . ' left';
1925
+ }
1926
+ }
1927
+ }
1928
+ return $buffer;
1929
+ }
1930
+
1931
+ static function delta_time($delta = 0) {
1932
+ $seconds = $delta % 60;
1933
+ $minutes = floor(($delta / 60) % 60);
1934
+ $hours = floor(($delta / (60 * 60)) % 24);
1935
+ $days = floor($delta / (24 * 60 * 60));
1936
+
1937
+ return $days . ' day(s), ' . $hours . ' hour(s), ' . $minutes . ' minute(s)';
1938
+ }
1939
+
1940
+ /**
1941
+ * Prints the help button near a form field. The label is used as icon title.
1942
+ *
1943
+ * @param string $url
1944
+ * @param string $label
1945
+ */
1946
+ static function help($url, $label = '') {
1947
+ echo '<a href="', $url, '" target="_blank" title="', esc_attr($label), '"><i class="fas fa-question-circle"></i></a>';
1948
+ }
1949
+
1950
+ static function idea($url, $label = '') {
1951
+ echo '<a href="', $url, '" target="_blank" title="', esc_attr($label), '"><i class="fas fa-lightbulb-o"></i></a>';
1952
+ }
1953
+
1954
+ static function field_help($url, $text = '') {
1955
+ if (strpos($url, 'http') !== 0) {
1956
+ $url = 'https://www.thenewsletterplugin.com' . $url;
1957
+ }
1958
+ echo '<a href="', $url, '" target="_blank" style="text-decoration: none" title="' . esc_attr(__('Read more', 'newsletter')) . '"><i class="fas fa-question-circle"></i>';
1959
+ if ($text)
1960
+ echo '&nbsp;', $text;
1961
+ echo '</a>';
1962
+ }
1963
+
1964
+ static function field_label($label, $help_url = false) {
1965
+ echo $label;
1966
+ if ($help_url) {
1967
+ echo '&nbsp';
1968
+ self::field_help($help_url);
1969
+ }
1970
+ }
1971
+
1972
+ /**
1973
+ * Prints a panel link to the documentation.
1974
+ *
1975
+ * @param type $url
1976
+ * @param type $text
1977
+ */
1978
+ static function panel_help($url, $text = '') {
1979
+ if (empty($text))
1980
+ $text = __('Need help?', 'newsletter');
1981
+ echo '<span class="tnp-panel-help"><a href="', $url, '" target="_blank">', $text, '</a></span>';
1982
+ }
1983
+
1984
+ /**
1985
+ * Prints an administration page link to the documentation (just under the administration page title.
1986
+ * @param type $url
1987
+ * @param type $text
1988
+ */
1989
+ static function page_help($url, $text = '') {
1990
+ if (empty($text))
1991
+ $text = __('Need help?', 'newsletter');
1992
+ echo '<div class="tnp-page-help"><a href="', $url, '" target="_blank">', $text, '</a></div>';
1993
+ }
1994
+
1995
+ static function print_truncated($text, $size = 50) {
1996
+ if (mb_strlen($text) < $size)
1997
+ return esc_html($text);
1998
+ $sub = mb_substr($text, 0, $size);
1999
+ echo '<span title="', esc_attr($text), '">', esc_html($sub), '...</span>';
2000
+ }
2001
+
2002
+ function block_background($name = 'block_background') {
2003
+ $this->color($name);
2004
+ }
2005
+
2006
+ function block_padding($name = 'block_padding', $options = array()) {
2007
+ echo '<div style="text-align: center; width: 250px;">';
2008
+ $this->text($name . '_top', 5);
2009
+ echo '<br>';
2010
+ $this->text($name . '_left', 5);
2011
+ echo "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
2012
+ $this->text($name . '_right', 5);
2013
+ echo '<br>';
2014
+ $this->text($name . '_bottom', 5);
2015
+ echo '</div>';
2016
+ }
2017
+
2018
+ /**
2019
+ * Adds the fields used by the composer (version 1) in the page form.
2020
+ *
2021
+ * @param type $name
2022
+ */
2023
+ function composer_fields($name = 'body') {
2024
+
2025
+ // body
2026
+ $value = $this->get_value($name);
2027
+
2028
+ // Extracts only the body part
2029
+ $x = strpos($value, '<body');
2030
+ if ($x) {
2031
+ $x = strpos($value, '>', $x);
2032
+ $y = strpos($value, '</body>');
2033
+ $value = substr($value, $x + 1, $y - $x - 1);
2034
+ }
2035
+
2036
+ /* Cleans up uncorrectly stored newsletter bodies */
2037
+ $value = preg_replace('/<style\s+.*?>.*?<\\/style>/is', '', $value);
2038
+ $value = preg_replace('/<meta.*?>/', '', $value);
2039
+ $value = preg_replace('/<title\s+.*?>.*?<\\/title>/i', '', $value);
2040
+ $value = trim($value);
2041
+
2042
+ // Required since esc_html DOES NOT escape the HTML entities (apparently)
2043
+ $value = str_replace('&', '&amp;', $value);
2044
+ $value = str_replace('"', '&quot;', $value);
2045
+ $value = str_replace('<', '&lt;', $value);
2046
+ $value = str_replace('>', '&gt;', $value);
2047
+ echo '<input type="hidden" name="options[', esc_attr($name), ']" id="options-', esc_attr($name), '" value="', esc_attr($value), '">';
2048
+
2049
+ // Used by composer to rebuild the full HTML
2050
+ $css = NewsletterEmails::instance()->get_composer_css();
2051
+ echo '<input type="hidden" name="options[css]" id="options-css" value="', esc_attr($css), '">';
2052
+
2053
+ // subject
2054
+ $value = $this->get_value('subject');
2055
+ echo '<input type="hidden" name="options[subject]" id="options-subject" value="', esc_attr($value), '">';
2056
+ }
2057
+
2058
+ function composer_load($name = 'body', $show_subject = false, $show_test = true, $context_type = '') {
2059
+
2060
+ global $controls;
2061
+ global $tnpc_show_subject;
2062
+ $tnpc_show_subject = $show_subject;
2063
+
2064
+ wp_enqueue_style('tnpc-style', plugins_url('newsletter') . '/emails/tnp-composer/_css/newsletter-builder.css', array(), time());
2065
+
2066
+ include NEWSLETTER_DIR . '/emails/tnp-composer/index.php';
2067
+ }
2068
+
2069
+ /**
2070
+ * Adds the fields used by the composer (version 2) in the page form.
2071
+ */
2072
+ function composer_fields_v2($name = 'message') {
2073
+
2074
+ // The composer, on saving, fills in those fields
2075
+ $this->hidden('subject');
2076
+ $this->hidden('message');
2077
+ $this->hidden('options_preheader');
2078
+
2079
+ //$preheader_value = $this->get_value('options_preheader');
2080
+ // echo '<input name="options[preheader]" id="options-preheader" type="hidden" value="', esc_attr($preheader_value), '">';
2081
+ }
2082
+
2083
+ function composer_load_v2($show_subject = false, $show_test = true, $context_type = '') {
2084
+
2085
+ global $tnpc_show_subject;
2086
+ $tnpc_show_subject = $show_subject;
2087
+
2088
+ echo "<link href='", plugins_url('newsletter'), "/emails/tnp-composer/_css/newsletter-builder-v2.css?ver=" . NEWSLETTER_VERSION . "' rel='stylesheet' type='text/css'>";
2089
+
2090
+ $controls = $this;
2091
+ include NEWSLETTER_DIR . '/emails/tnp-composer/index-v2.php';
2092
+ }
2093
+
2094
+ function subject($name) {
2095
+ $value = $this->get_value($name);
2096
+ // Leave the ID with this prefix!
2097
+ echo '<div style="position: relative"><input size="80" id="options-subject-', esc_attr($name), '" name="options[' . esc_attr($name) . ']" type="text" placeholder="" value="';
2098
+ echo esc_attr($value);
2099
+ echo '">';
2100
+ echo '&nbsp;<i class="far fa-lightbulb tnp-suggest-subject" data-tnp-modal-target="#subject-ideas-modal"></i>';
2101
+
2102
+ echo '<img src="', plugins_url('newsletter'), '/admin/images/subject/android.png" style="position: absolute; width: 16px; left: 330px; top: 25px; display: block; opacity: 0">';
2103
+ echo '<img src="', plugins_url('newsletter'), '/admin/images/subject/iphone.png" style="position: absolute; width: 16px; left: 380px; top: 25px; display: block; opacity: 0">';
2104
+ echo '</div>';
2105
+ }
2106
+
2107
+ }
includes/cron.php ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Set up and log the cron system. To be loaded at the plugin start.
4
+ */
5
+ defined('ABSPATH') || exit;
6
+
7
+ // Can be redefined in wp-config.php (not recommended)
8
+ if (!defined('NEWSLETTER_CRON_INTERVAL')) {
9
+ define('NEWSLETTER_CRON_INTERVAL', 300);
10
+ }
11
+
12
+ // Logging of the cron calls to debug the so many situations where the cron is not triggered at all (grrr...).
13
+ if (defined('DOING_CRON') && DOING_CRON) {
14
+ $calls = get_option('newsletter_diagnostic_cron_calls', []);
15
+ // Protection against scrambled options or bad written database caching plugin (yes, it happened, grrr...).
16
+ if (!is_array($calls)) {
17
+ $calls = [];
18
+ }
19
+ $calls[] = time();
20
+ // TODO: create a constant for samples limit
21
+ if (count($calls) > 100) {
22
+ // TODO: optimize using array_slice() and call every ten records
23
+ array_shift($calls);
24
+ }
25
+ update_option('newsletter_diagnostic_cron_calls', $calls, false);
26
+ }
27
+
28
+ // As soon as possible but with low priority so it is ecxecutes as last filter to avoid bad witten
29
+ // filters which remove other's schedules (yes, it happened, grrr...).
30
+ add_filter('cron_schedules', function ($schedules) {
31
+ $schedules['newsletter'] = [
32
+ 'interval' => NEWSLETTER_CRON_INTERVAL,
33
+ 'display' => 'Every ' . NEWSLETTER_CRON_INTERVAL . ' seconds'
34
+ ];
35
+ return $schedules;
36
+ }, 1000);
includes/fields.php CHANGED
@@ -1,745 +1,745 @@
1
- <?php
2
-
3
- class NewsletterFields {
4
- /* @var NewsletterControls */
5
-
6
- var $controls;
7
-
8
- public function __construct(NewsletterControls $controls) {
9
- $this->controls = $controls;
10
- }
11
-
12
- public function _open($subclass = '') {
13
- echo '<div class="tnp-field ', $subclass, '">';
14
- }
15
-
16
- public function _close() {
17
- echo '</div>';
18
- }
19
-
20
- public function _label($text, $for = '') {
21
- if (empty($text)) {
22
- return;
23
- }
24
- // Do not escape, HTML allowed
25
- echo '<label class="tnp-label">', $text, '</label>';
26
- }
27
-
28
- public function _description($attrs) {
29
- if (empty($attrs['description'])) {
30
- return;
31
- }
32
- // Do not escape, HTML allowed
33
- echo '<div class="tnp-description">', $attrs['description'], '</div>';
34
- }
35
-
36
- public function _id($name) {
37
- return 'options-' . esc_attr($name);
38
- }
39
-
40
- public function _name($name) {
41
- return 'options[' . esc_attr($name) . ']';
42
- }
43
-
44
- /**
45
- * Adds some empty basic atributes to avoid the isset() checking.
46
- *
47
- * @param array $attrs
48
- * @return array
49
- */
50
- public function _merge_base_attrs($attrs) {
51
- return array_merge(['description' => '', 'label' => '', 'help_url' => ''], $attrs);
52
- }
53
-
54
- /** Adds some basic attributes and the provided default ones.
55
- *
56
- * @param array $attrs
57
- * @param array $defaults
58
- * @return array
59
- */
60
- public function _merge_attrs($attrs, $defaults = []) {
61
- return array_merge(['description' => '', 'label' => '', 'help_url' => ''], $defaults, $attrs);
62
- }
63
-
64
- /**
65
- * A form section title.
66
- *
67
- * @param string $title
68
- */
69
- public function section($title = '') {
70
- // Do not escape, HTML allowed
71
- echo '<h3 class="tnp-section">', $title, '</h3>';
72
- }
73
-
74
- public function separator() {
75
- echo '<div class="tnp-field tnp-separator"></div>';
76
- }
77
-
78
- public function checkbox($name, $label = '', $attrs = []) {
79
- $attrs = $this->_merge_base_attrs($attrs);
80
- $this->_open('tnp-checkbox');
81
- $this->controls->checkbox($name, $label);
82
- $this->_description($attrs);
83
- $this->_close();
84
- }
85
-
86
- /** General Input field with default type = text
87
- *
88
- * Attributes:
89
- * - label_after (default: none): small text ti be displayed after the text field
90
- * - min (default: none): minimum number of characters
91
- * - max (default: none): maximum number of characters
92
- * - size (default: none): size in pixels
93
- */
94
- public function input($name, $label = '', $attrs = []) {
95
- $attrs = $this->_merge_attrs($attrs, ['placeholder' => '', 'size' => 0, 'label_after' => '', 'type' => 'text']);
96
- $this->_open();
97
- $this->_label($label);
98
- $value = $this->controls->get_value($name);
99
-
100
- echo '<input id="', $this->_id($name), '" placeholder="', esc_attr($attrs['placeholder']), '" name="', $this->_name($name), '" type="', esc_attr($attrs['type']), '"';
101
-
102
- if (!empty($attrs['size'])) {
103
- echo ' style="width: ', $attrs['size'], 'px"';
104
- }
105
-
106
- if (isset($attrs['min'])) {
107
- echo ' min="' . (int) $attrs['min'] . '"';
108
- }
109
-
110
- if (isset($attrs['max'])) {
111
- echo ' max="' . (int) $attrs['max'] . '"';
112
- }
113
-
114
- echo ' value="', esc_attr($value), '">';
115
-
116
- if (!empty($attrs['label_after'])) {
117
- echo $attrs['label_after'];
118
- }
119
-
120
- $this->_description($attrs);
121
- $this->_close();
122
- }
123
-
124
- public function text($name, $label = '', $attrs = []) {
125
- $attrs['type'] = 'text';
126
- $this->input($name, $label, $attrs);
127
- }
128
-
129
- public function text_on_off($name, $label = '', $attrs = []) {
130
- $attrs = $this->_merge_attrs($attrs, ['placeholder' => '', 'size' => 0, 'label_after' => '', 'type' => 'text']);
131
- $this->_open();
132
- $this->_label($label);
133
- $value = $this->controls->get_value($name);
134
-
135
- echo '<input type="hidden" name="tnp_fields[' . esc_attr($name . '_enabled') . ']" value="checkbox">';
136
- echo '<input id="', $this->_id($name . '_enabled'), '" name="', $this->_name($name . '_enabled'), '" type="checkbox" value="1"';
137
- if (!empty($this->controls->get_value($name . '_enabled'))) {
138
- echo ' checked';
139
- }
140
- echo '>&nbsp;';
141
-
142
- echo '<input id="', $this->_id($name), '" placeholder="', esc_attr($attrs['placeholder']), '" name="', $this->_name($name), '" type="text"';
143
-
144
- echo ' style="width: 90%;"';
145
-
146
- if (isset($attrs['min'])) {
147
- echo ' min="' . (int) $attrs['min'] . '"';
148
- }
149
-
150
- if (isset($attrs['max'])) {
151
- echo ' max="' . (int) $attrs['max'] . '"';
152
- }
153
-
154
- echo ' value="', esc_attr($value), '">';
155
-
156
- if (!empty($attrs['label_after'])) {
157
- echo $attrs['label_after'];
158
- }
159
-
160
- $this->_description($attrs);
161
- $this->_close();
162
- }
163
-
164
- public function number($name, $label = '', $attrs = []) {
165
- $attrs = array_merge(['type' => 'number'], $attrs);
166
- $this->input($name, $label, $attrs);
167
- }
168
-
169
- /**
170
- * A set of text fields, named $name_1, $name_2, ...
171
- *
172
- * Attributes:
173
- * - label_after: a label to show after the field column
174
- *
175
- * @param type $name
176
- * @param type $label
177
- * @param type $count
178
- * @param type $attrs
179
- */
180
- public function multitext($name, $label = '', $count = 10, $attrs = []) {
181
- $attrs = $this->_merge_attrs($attrs, ['description' => '', 'placeholder' => '', 'size' => 0, 'label_after' => '']);
182
- $this->_open();
183
- $this->_label($label);
184
-
185
- for ($i = 1; $i <= $count; $i++) {
186
- $value = $this->controls->get_value($name . '_' . $i);
187
- echo '<input id="', $this->_id($name . '_' . $i), '" placeholder="', esc_attr($attrs['placeholder']), '" name="options[', $name, '_', $i, ']" type="text"';
188
- if (!empty($attrs['size'])) {
189
- echo ' style="width: ', $attrs['size'], 'px"';
190
- }
191
- echo ' value="', esc_attr($value), '">';
192
- }
193
- if (!empty($attrs['label_after'])) {
194
- echo $attrs['label_after'];
195
- }
196
- $this->_description($attrs);
197
- $this->_close();
198
- }
199
-
200
- public function textarea($name, $label = '', $attrs = []) {
201
- $attrs = $this->_merge_attrs($attrs, ['width' => '100%', 'height' => '150']);
202
- $this->_open();
203
- $this->_label($label);
204
- $this->controls->textarea_fixed($name, $attrs['width'], $attrs['height']);
205
- $this->_description($attrs);
206
- $this->_close();
207
- }
208
-
209
- public function wp_editor($name, $label = '', $attrs = []) {
210
- global $wp_version;
211
-
212
- $attrs = $this->_merge_attrs($attrs);
213
- $this->_open();
214
- $this->_label($label);
215
- $value = $this->controls->get_value($name);
216
- $name = esc_attr($name);
217
-
218
- // Uhm...
219
- if (is_array($value)) {
220
- $value = implode("\n", $value);
221
- }
222
- if (version_compare($wp_version, '4.8', '<')) {
223
- echo '<p><strong>Rich editor available only with WP 4.8+</strong></p>';
224
- }
225
- echo '<textarea class="tnpf-wp-editor" id="options-', $name, '" name="options[', $name, ']" style="width: 100%;height:250px">';
226
- echo esc_html($value);
227
- echo '</textarea>';
228
-
229
- if (version_compare($wp_version, '4.8', '>=')) {
230
-
231
- $paragraph_style = " p { font-family: ${attrs['text_font_family']}; font-size: ${attrs['text_font_size']}px; font-weight: ${attrs['text_font_weight']}; color: ${attrs['text_font_color']}; line-height: 1.5em; }";
232
- $content_style = $paragraph_style;
233
-
234
- echo '<script>';
235
- echo 'wp.editor.remove("options-', $name, '");';
236
- echo 'wp.editor.initialize("options-', $name, '", { tinymce: {content_style: "' . $content_style . '", toolbar1: "undo redo | formatselect fontselect fontsizeselect | bold italic forecolor backcolor | link unlink | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | removeformat | wp_add_media | charmap | rtl ltr", fontsize_formats: "11px 12px 14px 16px 18px 24px 36px 48px", plugins: "link textcolor colorpicker lists wordpress charmap directionality", default_link_target: "_blank", relative_urls : false, convert_urls: false, keep_styles: true }});';
237
- echo '</script>';
238
- }
239
- $this->_description($attrs);
240
- $this->_close();
241
- }
242
-
243
- /**
244
- * Attributes:
245
- * - realod: when true is forces a submit of the form (used to change the form fields or values for example when changing layout or color scheme)
246
- *
247
- * @param type $name
248
- * @param type $label
249
- * @param type $options
250
- * @param type $attrs
251
- */
252
- public function select($name, $label = '', $options = [], $attrs = []) {
253
- $attrs = $this->_merge_attrs($attrs, ['reload' => false, 'after-rendering' => '', 'class' => '']);
254
- $this->_open();
255
- $this->_label($label);
256
- $value = $this->controls->get_value($name);
257
-
258
- echo '<select id="', $this->_id($name), '" name="', $this->_name($name), '"';
259
- if ($attrs['class']) {
260
- echo ' class="', esc_attr($attrs['class']), '"';
261
- }
262
- if ($attrs['reload']) {
263
- echo ' onchange="tnpc_reload_options(event)"';
264
- }
265
- if (!empty($attrs['after-rendering'])) {
266
- echo ' data-after-rendering="', $attrs['after-rendering'], '"';
267
- }
268
- echo '>';
269
- // if (!empty($first)) {
270
- // echo '<option value="">', esc_html($first), '</option>';
271
- // }
272
-
273
- foreach ($options as $key => $text) {
274
- echo '<option value="', esc_attr($key), '"';
275
- if ($value == $key) {
276
- echo ' selected';
277
- }
278
- echo '>', esc_html($text), '</option>';
279
- }
280
- echo '</select>';
281
-
282
- $this->_description($attrs);
283
- $this->_close();
284
- }
285
-
286
- public function align($name = 'align') {
287
- $this->select($name,
288
- __('Align', 'newsletter'),
289
- ['center' => __('Center', 'newsletter'), 'left' => __('Left', 'newsletter'), 'right' => __('Right')]
290
- );
291
- }
292
-
293
- public function yesno($name, $label = '', $attrs = []) {
294
- $attrs = $this->_merge_attrs($attrs);
295
- $this->_open();
296
- $this->_label($label);
297
- $this->controls->yesno($name);
298
- $this->_description($attrs);
299
- $this->_close();
300
- }
301
-
302
- public function select_number($name, $label = '', $min, $max, $attrs = []) {
303
- $attrs = $this->_merge_attrs($attrs);
304
- $this->_open();
305
- $this->_label($label);
306
- $this->controls->select_number($name, $min, $max);
307
- $this->_description($attrs);
308
- $this->_close();
309
- }
310
-
311
- /**
312
- * General field to collect an element dimension in pixels
313
- *
314
- * Attributes:
315
- * - size: field width in pixels
316
- */
317
- public function size($name, $label = '', $attrs = []) {
318
- $attrs = $this->_merge_attrs($attrs, ['description' => '', 'placeholder' => '', 'size' => 0, 'label_after' => 'px']);
319
- $this->_open('tnp-size');
320
- $this->_label($label);
321
- $value = $this->controls->get_value($name);
322
- echo '<input id="', $this->_id($name), '" placeholder="', esc_attr($attrs['placeholder']), '" name="', $this->_name($name), '" type="text"';
323
- if (!empty($attrs['size'])) {
324
- echo ' style="width: ', $attrs['size'], 'px"';
325
- }
326
- echo ' value="', esc_attr($value), '">', $attrs['label_after'];
327
- $this->_description($attrs);
328
- $this->_close();
329
- }
330
-
331
- /**
332
- * Collects a color in HEX format with a picker.
333
- */
334
- public function color($name, $label, $attrs = []) {
335
- $this->_open('tnp-color');
336
- $this->_label($label);
337
- $this->controls->color($name);
338
- $this->_description($attrs);
339
- $this->_close();
340
- }
341
-
342
- /**
343
- * Configuration for a simple button with label and color
344
- *
345
- * Attributes:
346
- * - weight: if true (default) shows the font weight selector
347
- * - url_paceholder: the placeholder for the URL field
348
- * - url: if true (default) shows the URL field (sometime the URL is produced elsewhere, for example on post list)
349
- */
350
- public function button($name, $label = '', $attrs = []) {
351
- $attrs = $this->_merge_attrs($attrs,
352
- [
353
- 'placeholder' => 'Label...',
354
- 'url_placeholder' => 'https://...',
355
- 'url' => true,
356
- 'weight' => true,
357
- 'family_default' => false,
358
- 'size_default' => false,
359
- 'weight_default' => false,
360
- ]);
361
-
362
- $this->_open('tnpf-button');
363
- $this->_label($label);
364
- $value = $this->controls->get_value($name . '_label');
365
- $name_esc = esc_attr($name);
366
- echo '<div class="tnp-field-row">';
367
- echo '<div class="tnp-field-col-2">';
368
- echo '<input id="', $this->_id($name . '_label'), '" placeholder="', esc_attr($attrs['placeholder']), '" name="options[', $name_esc, '_label]" type="text"';
369
- echo ' style="width: 100%"';
370
- echo ' value="', esc_attr($value), '">';
371
- echo '</div>';
372
-
373
- if ($attrs['url']) {
374
- $value = $this->controls->get_value($name . '_url');
375
- echo '<div class="tnp-field-col-2">';
376
- $width = isset($attrs['media']) ? '90%' : '100%';
377
- echo '<input id="', $this->_id($name . '_url'), '" placeholder="', esc_attr($attrs['url_placeholder']), '" name="options[',
378
- $name_esc, '_url]" type="url" style="width: ', $width, '" value="', esc_attr($value), '">';
379
- if (isset($attrs['media'])) {
380
- echo '&nbsp;<i class="far fa-folder-open" data-field="', $this->_id($name . '_url'), '" onclick="tnp_fields_url_select(this)"></i>';
381
- }
382
- echo '</div>';
383
- }
384
- echo '<div style="clear: both"></div>';
385
- echo '</div>';
386
- $this->controls->css_font($name . '_font', [
387
- 'weight' => $attrs['weight'],
388
- 'family_default' => $attrs['family_default'],
389
- 'size_default' => $attrs['size_default'],
390
- 'weight_default' => $attrs['weight_default']
391
- ]);
392
- $this->controls->color($name . '_background');
393
- $this->_close();
394
- }
395
-
396
- public function button_style($name, $label = '') {
397
-
398
- $this->_open('tnp-font');
399
- $this->_label($label);
400
- $this->controls->css_font($name . '_font');
401
- $this->controls->color($name . '_background_color');
402
- $this->_close();
403
- }
404
-
405
- /**
406
- * URL input field
407
- *
408
- * @param string $name
409
- * @param string $label
410
- * @param array $attrs
411
- */
412
- public function url($name, $label = '', $attrs = []) {
413
- $attrs = $this->_merge_attrs($attrs, ['placeholder' => 'https://...']);
414
- $this->_open('tnp-url');
415
- $this->_label($label);
416
- $this->controls->text_url($name);
417
- if (isset($attrs['media'])) {
418
- echo '<i class="far fa-folder-open" onclick="tnp_fields_url_select(\'options_', $name, '\')"></i>';
419
- }
420
- $this->_description($attrs);
421
- $this->_close();
422
- }
423
-
424
- /**
425
- * Provides a list of custom post types.
426
- *
427
- * @param string $name
428
- * @param string $label
429
- * @param array $attrs
430
- */
431
- public function post_type($name = 'post_type', $label = '', $attrs = []) {
432
-
433
- $post_types = get_post_types(['public' => true], 'objects', 'and');
434
-
435
- $attrs = array_merge(['description' => ''], $attrs);
436
- $this->_open('tnp-post-type');
437
- $this->_label($label);
438
-
439
- $options = ['post' => 'Standard posts', 'page' => 'Pages'];
440
-
441
- foreach ($post_types as $post_type) {
442
- if ($post_type->name == 'post' || $post_type->name == 'page' || $post_type->name == 'attachment') {
443
- continue;
444
- }
445
- $options[$post_type->name] = $post_type->labels->name;
446
- }
447
-
448
- $value = $this->controls->get_value($name);
449
-
450
- echo '<select id="', $this->_id($name), '" name="options[' . esc_attr($name) . ']" onchange="tnpc_reload_options(event); return false;">';
451
- // if (!empty($first)) {
452
- // echo '<option value="">' . esc_html($first) . '</option>';
453
- // }
454
- $label = esc_html($label);
455
- foreach ($options as $key => $label) {
456
- echo '<option value="' . esc_attr($key) . '"';
457
- if ($value == $key)
458
- echo ' selected';
459
- echo '>', $label, '</option>';
460
- }
461
- echo '</select>';
462
-
463
- $this->_description($attrs);
464
- $this->_close();
465
- }
466
-
467
- function posts($name, $label, $count = 20, $args = []) {
468
- $args = array_merge(array('filters' => array(
469
- 'posts_per_page' => 5,
470
- 'offset' => 0,
471
- 'category' => '',
472
- 'category_name' => '',
473
- 'orderby' => 'date',
474
- 'order' => 'DESC',
475
- 'include' => '',
476
- 'exclude' => '',
477
- 'meta_key' => '',
478
- 'meta_value' => '',
479
- 'post_type' => 'post',
480
- 'post_mime_type' => '',
481
- 'post_parent' => '',
482
- 'author' => '',
483
- 'author_name' => '',
484
- 'post_status' => 'publish',
485
- 'suppress_filters' => true),
486
- 'last_post_option' => false
487
- ), $args);
488
- $args['filters']['posts_per_page'] = $count;
489
-
490
- $posts = get_posts($args['filters']);
491
- $options = array();
492
- if ($args['last_post_option']) {
493
- $options['last'] = 'Most recent post';
494
- }
495
- foreach ($posts as $post) {
496
- $options['' . $post->ID] = $post->post_title;
497
- }
498
-
499
- $this->select($name, $label, $options);
500
- }
501
-
502
- function lists($name, $label, $attrs = []) {
503
- $attrs = $this->_merge_attrs($attrs, ['empty_label' => null]);
504
- $this->_open();
505
- $this->_label($label);
506
- $lists = $this->controls->get_list_options($attrs['empty_label']);
507
- $this->controls->select($name, $lists);
508
- $this->_description($attrs);
509
- $this->_close();
510
- }
511
-
512
- /**
513
- * Media selector using the WP media library (for images and files.
514
- * The field to use it the {$name}_id which contains the media id.
515
- *
516
- * Attributes:
517
- * - alt: if true shows the alternate text field for the "alt" attribute
518
- * - layout: if set to "mini" the controls is shown as a mini selector, no labels
519
- *
520
- * @param string $name
521
- * @param string $label
522
- * @param array $attrs
523
- */
524
- public function media($name, $label = '', $attrs = []) {
525
- $attrs = $this->_merge_attrs($attrs, ['alt' => false, 'layout' => '']);
526
-
527
- if (empty($attrs['layout'])) {
528
- $this->_open('tnp-media');
529
- $this->_label($label);
530
- $this->controls->media($name);
531
- if ($attrs['alt']) {
532
- $this->controls->text($name . '_alt', 20, 'Alternative text');
533
- }
534
- $this->_description($attrs);
535
- $this->_close();
536
- } else {
537
- if (isset($this->controls->data[$name]['id'])) {
538
- $media_id = (int) $this->controls->data[$name]['id'];
539
- $media = wp_get_attachment_image_src($media_id, 'thumbnail');
540
- } else {
541
- $media = false;
542
- $media_id = 0;
543
- }
544
- echo '<div class="tnpf-media-mini-select" data-name="' . esc_attr($name) . '" style="width: 100px; height: 100px; overflow: hidden; border: 1px dashed #999; position: relative" onclick="tnp_fields_media_mini_select(this)">';
545
- echo '<a style="position: absolute; top: 5px; right: 5px; background-color: #000; color: #fff; padding: 0px 5px 6px 5px; font-size: 24px; display: block; text-decoration: none" href="#" onclick="tnp_fields_media_mini_remove(\'' . esc_attr($name) . '\'); return false;">&times;</a>';
546
- if ($media) {
547
- echo '<img style="max-width: 100%; height: auto; display: block" id="' . esc_attr($name) . '_img" src="' . esc_attr($media[0]) . '">';
548
- } else {
549
- echo '<img style="max-width: 100%; height: auto; display: block" id="' . esc_attr($name) . '_img" src="">';
550
- }
551
-
552
- echo '</div>';
553
- echo '<input type="hidden" id="' . esc_attr($name) . '_id" name="options[' . esc_attr($name) . '][id]" value="' . esc_attr($media_id) . '">';
554
- }
555
- }
556
-
557
- public function categories($name = 'categories', $label = '', $attrs = []) {
558
- if (empty($label)) {
559
- $label = __('Categories', 'newsletter');
560
- }
561
- $attrs = $this->_merge_attrs($attrs);
562
- $this->_open('tnp-categories');
563
- $this->_label($label);
564
- $this->controls->categories_group($name);
565
- $this->_description($attrs);
566
- $this->_close();
567
- }
568
-
569
- /**
570
- * The field name is preset to tax_$taxonomy. A different name can be specified
571
- * with the attribute 'name'.
572
- * @param type $taxonomy
573
- * @param type $label
574
- * @param type $attrs
575
- */
576
- public function terms($taxonomy, $label = '', $attrs = []) {
577
- if (isset($attrs['name'])) {
578
- $name = $attrs['name'];
579
- } else {
580
- $name = 'tax_' . $taxonomy;
581
- }
582
- if (empty($label)) {
583
- $label = __('Terms', 'newsletter');
584
- }
585
- $attrs = $this->_merge_attrs($attrs);
586
- $this->_open('tnp-categories');
587
- $this->_label($label);
588
- $terms = get_terms($taxonomy);
589
-
590
- if (empty($terms)) {
591
- echo 'No terms in use';
592
- } else {
593
-
594
- echo '<div class="newsletter-checkboxes-group">';
595
- foreach ($terms as $term) {
596
- /* @var $term WP_Term */
597
- echo '<div class="newsletter-checkboxes-item">';
598
- $this->controls->checkbox_group($name, $term->term_id, esc_html($term->name));
599
- echo '</div>';
600
- }
601
- echo '<div style="clear: both"></div>';
602
- echo '</div>';
603
- }
604
-
605
- $this->_description($attrs);
606
- $this->_close();
607
- }
608
-
609
- /**
610
- * Shows a language selector only if the blog is multilanguage.
611
- *
612
- * @param string $name
613
- * @param string $label
614
- * @param array $attrs
615
- */
616
- public function language($name = 'language', $label = '', $attrs = []) {
617
- if (!Newsletter::instance()->is_multilanguage()) {
618
- return;
619
- }
620
- if (empty($label)) {
621
- $label = __('Language', 'newsletter');
622
- }
623
- $attrs = $this->_merge_attrs($attrs);
624
- $this->_open('tnp-language');
625
- $this->_label($label);
626
- $this->controls->language($name);
627
- $this->_description($attrs);
628
- $this->_close();
629
- }
630
-
631
- /**
632
- * Collects font details for a text: family, color, size and weight to be used
633
- * directly on CSS rules. Size is a pure number.
634
- *
635
- * Attributes:
636
- * - family: true|false enable or not the font family field
637
- * - family_default: true|false enables the default entry with an empty key value
638
- * - color: true|false enable or not the color field
639
- * - weight: true|false enable or not the weight field
640
- * - size: true|false enable or not the size selection
641
- *
642
- * @param type $name
643
- * @param type $label
644
- * @param array $attrs
645
- */
646
- public function font($name = 'font', $label = 'Font', $attrs = []) {
647
- $attrs = $this->_merge_base_attrs($attrs);
648
- $attrs = array_merge([
649
- 'hide_family' => false,
650
- 'family' => true,
651
- 'color' => true,
652
- 'size' => true,
653
- 'weight' => true,
654
- 'family_default' => false,
655
- 'size_default' => false,
656
- 'weight_default' => false,
657
- ], $attrs);
658
-
659
- $this->_open('tnp-font');
660
- $this->_label($label);
661
-
662
- $this->controls->css_font_family($name . '_family', !empty($attrs['family_default']));
663
-
664
- if ($attrs['size']) {
665
- $this->controls->css_font_size($name . '_size', !empty($attrs['size_default']));
666
- }
667
- if ($attrs['weight']) {
668
- $this->controls->css_font_weight($name . '_weight', !empty($attrs['weight_default']));
669
- }
670
- if ($attrs['color']) {
671
- $this->controls->color($name . '_color');
672
- }
673
-
674
- $this->_description($attrs);
675
- $this->_close();
676
- }
677
-
678
- /**
679
- * Collects fout number values representing the padding of a box. The values can
680
- * be found as {$name}_top, {$name}_bottom, {$name}_left, {$name}_right.
681
- *
682
- * @param type $name
683
- * @param type $label
684
- * @param type $attrs
685
- */
686
- public function padding($name = 'block_padding', $label = 'Padding', $attrs = []) {
687
- $attrs = $this->_merge_base_attrs($attrs);
688
- $attrs = array_merge(['padding_top' => 0, 'padding_left' => 0, 'padding_right' => 0, 'padding_bottom' => 0], $attrs);
689
- $field_only = !empty($attrs['field_only']);
690
-
691
- if (!$field_only) {
692
- $this->_open('tnp-padding');
693
- $this->_label($label);
694
- }
695
- echo '<div class="tnp-padding-fields">';
696
- echo '&larr;';
697
- $this->controls->text($name . '_left', 5);
698
- echo '&nbsp;&nbsp;&nbsp;';
699
- echo '&uarr;';
700
- $this->controls->text($name . '_top', 5);
701
- echo '&nbsp;&nbsp;&nbsp;';
702
-
703
- $this->controls->text($name . '_bottom', 5);
704
- echo '&darr;';
705
- echo '&nbsp;&nbsp;&nbsp;';
706
- $this->controls->text($name . '_right', 5);
707
- echo '&rarr;';
708
- echo '</div>';
709
- if (!$field_only) {
710
- $this->_description($attrs);
711
- $this->_close();
712
- }
713
- }
714
-
715
- /**
716
- * Background color selector for a block.
717
- */
718
- public function block_background() {
719
- $this->color('block_background', __('Block Background', 'newsletter'));
720
- }
721
-
722
- /**
723
- * Padding selector for a block.
724
- */
725
- public function block_padding() {
726
- $this->padding('block_padding', __('Padding', 'newsletter'));
727
- }
728
-
729
- public function block_commons() {
730
-
731
- $this->_open('tnp-block-commons');
732
- $this->_label('Padding and background');
733
- $this->controls->color('block_background');
734
-
735
- echo '&nbsp;&rarr;&nbsp;';
736
- $this->controls->checkbox('block_background_gradient');
737
- $this->controls->color('block_background_2');
738
-
739
- echo '&nbsp;&nbsp;&nbsp;';
740
- $this->padding('block_padding', '', ['field_only' => true]);
741
- echo '<div class="tnp-description">Gradients are displayed only by few clients</div>';
742
- $this->_close();
743
- }
744
-
745
- }
1
+ <?php
2
+
3
+ class NewsletterFields {
4
+ /* @var NewsletterControls */
5
+
6
+ var $controls;
7
+
8
+ public function __construct(NewsletterControls $controls) {
9
+ $this->controls = $controls;
10
+ }
11
+
12
+ public function _open($subclass = '') {
13
+ echo '<div class="tnp-field ', $subclass, '">';
14
+ }
15
+
16
+ public function _close() {
17
+ echo '</div>';
18
+ }
19
+
20
+ public function _label($text, $for = '') {
21
+ if (empty($text)) {
22
+ return;
23
+ }
24
+ // Do not escape, HTML allowed
25
+ echo '<label class="tnp-label">', $text, '</label>';
26
+ }
27
+
28
+ public function _description($attrs) {
29
+ if (empty($attrs['description'])) {
30
+ return;
31
+ }
32
+ // Do not escape, HTML allowed
33
+ echo '<div class="tnp-description">', $attrs['description'], '</div>';
34
+ }
35
+
36
+ public function _id($name) {
37
+ return 'options-' . esc_attr($name);
38
+ }
39
+
40
+ public function _name($name) {
41
+ return 'options[' . esc_attr($name) . ']';
42
+ }
43
+
44
+ /**
45
+ * Adds some empty basic atributes to avoid the isset() checking.
46
+ *
47
+ * @param array $attrs
48
+ * @return array
49
+ */
50
+ public function _merge_base_attrs($attrs) {
51
+ return array_merge(['description' => '', 'label' => '', 'help_url' => ''], $attrs);
52
+ }
53
+
54
+ /** Adds some basic attributes and the provided default ones.
55
+ *
56
+ * @param array $attrs
57
+ * @param array $defaults
58
+ * @return array
59
+ */
60
+ public function _merge_attrs($attrs, $defaults = []) {
61
+ return array_merge(['description' => '', 'label' => '', 'help_url' => ''], $defaults, $attrs);
62
+ }
63
+
64
+ /**
65
+ * A form section title.
66
+ *
67
+ * @param string $title
68
+ */
69
+ public function section($title = '') {
70
+ // Do not escape, HTML allowed
71
+ echo '<h3 class="tnp-section">', $title, '</h3>';
72
+ }
73
+
74
+ public function separator() {
75
+ echo '<div class="tnp-field tnp-separator"></div>';
76
+ }
77
+
78
+ public function checkbox($name, $label = '', $attrs = []) {
79
+ $attrs = $this->_merge_base_attrs($attrs);
80
+ $this->_open('tnp-checkbox');
81
+ $this->controls->checkbox($name, $label);
82
+ $this->_description($attrs);
83
+ $this->_close();
84
+ }
85
+
86
+ /** General Input field with default type = text
87
+ *
88
+ * Attributes:
89
+ * - label_after (default: none): small text ti be displayed after the text field
90
+ * - min (default: none): minimum number of characters
91
+ * - max (default: none): maximum number of characters
92
+ * - size (default: none): size in pixels
93
+ */
94
+ public function input($name, $label = '', $attrs = []) {
95
+ $attrs = $this->_merge_attrs($attrs, ['placeholder' => '', 'size' => 0, 'label_after' => '', 'type' => 'text']);
96
+ $this->_open();
97
+ $this->_label($label);
98
+ $value = $this->controls->get_value($name);
99
+
100
+ echo '<input id="', $this->_id($name), '" placeholder="', esc_attr($attrs['placeholder']), '" name="', $this->_name($name), '" type="', esc_attr($attrs['type']), '"';
101
+
102
+ if (!empty($attrs['size'])) {
103
+ echo ' style="width: ', $attrs['size'], 'px"';
104
+ }
105
+
106
+ if (isset($attrs['min'])) {
107
+ echo ' min="' . (int) $attrs['min'] . '"';
108
+ }
109
+
110
+ if (isset($attrs['max'])) {
111
+ echo ' max="' . (int) $attrs['max'] . '"';
112
+ }
113
+
114
+ echo ' value="', esc_attr($value), '">';
115
+
116
+ if (!empty($attrs['label_after'])) {
117
+ echo $attrs['label_after'];
118
+ }
119
+
120
+ $this->_description($attrs);
121
+ $this->_close();
122
+ }
123
+
124
+ public function text($name, $label = '', $attrs = []) {
125
+ $attrs['type'] = 'text';
126
+ $this->input($name, $label, $attrs);
127
+ }
128
+
129
+ public function text_on_off($name, $label = '', $attrs = []) {
130
+ $attrs = $this->_merge_attrs($attrs, ['placeholder' => '', 'size' => 0, 'label_after' => '', 'type' => 'text']);
131
+ $this->_open();
132
+ $this->_label($label);
133
+ $value = $this->controls->get_value($name);
134
+
135
+ echo '<input type="hidden" name="tnp_fields[' . esc_attr($name . '_enabled') . ']" value="checkbox">';
136
+ echo '<input id="', $this->_id($name . '_enabled'), '" name="', $this->_name($name . '_enabled'), '" type="checkbox" value="1"';
137
+ if (!empty($this->controls->get_value($name . '_enabled'))) {
138
+ echo ' checked';
139
+ }
140
+ echo '>&nbsp;';
141
+
142
+ echo '<input id="', $this->_id($name), '" placeholder="', esc_attr($attrs['placeholder']), '" name="', $this->_name($name), '" type="text"';
143
+
144
+ echo ' style="width: 90%;"';
145
+
146
+ if (isset($attrs['min'])) {
147
+ echo ' min="' . (int) $attrs['min'] . '"';
148
+ }
149
+
150
+ if (isset($attrs['max'])) {
151
+ echo ' max="' . (int) $attrs['max'] . '"';
152
+ }
153
+
154
+ echo ' value="', esc_attr($value), '">';
155
+
156
+ if (!empty($attrs['label_after'])) {
157
+ echo $attrs['label_after'];
158
+ }
159
+
160
+ $this->_description($attrs);
161
+ $this->_close();
162
+ }
163
+
164
+ public function number($name, $label = '', $attrs = []) {
165
+ $attrs = array_merge(['type' => 'number'], $attrs);
166
+ $this->input($name, $label, $attrs);
167
+ }
168
+
169
+ /**
170
+ * A set of text fields, named $name_1, $name_2, ...
171
+ *
172
+ * Attributes:
173
+ * - label_after: a label to show after the field column
174
+ *
175
+ * @param type $name
176
+ * @param type $label
177
+ * @param type $count
178
+ * @param type $attrs
179
+ */
180
+ public function multitext($name, $label = '', $count = 10, $attrs = []) {
181
+ $attrs = $this->_merge_attrs($attrs, ['description' => '', 'placeholder' => '', 'size' => 0, 'label_after' => '']);
182
+ $this->_open();
183
+ $this->_label($label);
184
+
185
+ for ($i = 1; $i <= $count; $i++) {
186
+ $value = $this->controls->get_value($name . '_' . $i);
187
+ echo '<input id="', $this->_id($name . '_' . $i), '" placeholder="', esc_attr($attrs['placeholder']), '" name="options[', $name, '_', $i, ']" type="text"';
188
+ if (!empty($attrs['size'])) {
189
+ echo ' style="width: ', $attrs['size'], 'px"';
190
+ }
191
+ echo ' value="', esc_attr($value), '">';
192
+ }
193
+ if (!empty($attrs['label_after'])) {
194
+ echo $attrs['label_after'];
195
+ }
196
+ $this->_description($attrs);
197
+ $this->_close();
198
+ }
199
+
200
+ public function textarea($name, $label = '', $attrs = []) {
201
+ $attrs = $this->_merge_attrs($attrs, ['width' => '100%', 'height' => '150']);
202
+ $this->_open();
203
+ $this->_label($label);
204
+ $this->controls->textarea_fixed($name, $attrs['width'], $attrs['height']);
205
+ $this->_description($attrs);
206
+ $this->_close();
207
+ }
208
+
209
+ public function wp_editor($name, $label = '', $attrs = []) {
210
+ global $wp_version;
211
+
212
+ $attrs = $this->_merge_attrs($attrs);
213
+ $this->_open();
214
+ $this->_label($label);
215
+ $value = $this->controls->get_value($name);
216
+ $name = esc_attr($name);
217
+
218
+ // Uhm...
219
+ if (is_array($value)) {
220
+ $value = implode("\n", $value);
221
+ }
222
+ if (version_compare($wp_version, '4.8', '<')) {
223
+ echo '<p><strong>Rich editor available only with WP 4.8+</strong></p>';
224
+ }
225
+ echo '<textarea class="tnpf-wp-editor" id="options-', $name, '" name="options[', $name, ']" style="width: 100%;height:250px">';
226
+ echo esc_html($value);
227
+ echo '</textarea>';
228
+
229
+ if (version_compare($wp_version, '4.8', '>=')) {
230
+
231
+ $paragraph_style = " p { font-family: ${attrs['text_font_family']}; font-size: ${attrs['text_font_size']}px; font-weight: ${attrs['text_font_weight']}; color: ${attrs['text_font_color']}; line-height: 1.5em; }";
232
+ $content_style = $paragraph_style;
233
+
234
+ echo '<script>';
235
+ echo 'wp.editor.remove("options-', $name, '");';
236
+ echo 'wp.editor.initialize("options-', $name, '", { tinymce: {content_style: "' . $content_style . '", toolbar1: "undo redo | formatselect fontselect fontsizeselect | bold italic forecolor backcolor | link unlink | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | removeformat | wp_add_media | charmap | rtl ltr", fontsize_formats: "11px 12px 14px 16px 18px 24px 36px 48px", plugins: "link textcolor colorpicker lists wordpress charmap directionality", default_link_target: "_blank", relative_urls : false, convert_urls: false, keep_styles: true }});';
237
+ echo '</script>';
238
+ }
239
+ $this->_description($attrs);
240
+ $this->_close();
241
+ }
242
+
243
+ /**
244
+ * Attributes:
245
+ * - realod: when true is forces a submit of the form (used to change the form fields or values for example when changing layout or color scheme)
246
+ *
247
+ * @param type $name
248
+ * @param type $label
249
+ * @param type $options
250
+ * @param type $attrs
251
+ */
252
+ public function select($name, $label = '', $options = [], $attrs = []) {
253
+ $attrs = $this->_merge_attrs($attrs, ['reload' => false, 'after-rendering' => '', 'class' => '']);
254
+ $this->_open();
255
+ $this->_label($label);
256
+ $value = $this->controls->get_value($name);
257
+
258
+ echo '<select id="', $this->_id($name), '" name="', $this->_name($name), '"';
259
+ if ($attrs['class']) {
260
+ echo ' class="', esc_attr($attrs['class']), '"';
261
+ }
262
+ if ($attrs['reload']) {
263
+ echo ' onchange="tnpc_reload_options(event)"';
264
+ }
265
+ if (!empty($attrs['after-rendering'])) {
266
+ echo ' data-after-rendering="', $attrs['after-rendering'], '"';
267
+ }
268
+ echo '>';
269
+ // if (!empty($first)) {
270
+ // echo '<option value="">', esc_html($first), '</option>';
271
+ // }
272
+
273
+ foreach ($options as $key => $text) {
274
+ echo '<option value="', esc_attr($key), '"';
275
+ if ($value == $key) {
276
+ echo ' selected';
277
+ }
278
+ echo '>', esc_html($text), '</option>';
279
+ }
280
+ echo '</select>';
281
+
282
+ $this->_description($attrs);
283
+ $this->_close();
284
+ }
285
+
286
+ public function align($name = 'align') {
287
+ $this->select($name,
288
+ __('Align', 'newsletter'),
289
+ ['center' => __('Center', 'newsletter'), 'left' => __('Left', 'newsletter'), 'right' => __('Right')]
290
+ );
291
+ }
292
+
293
+ public function yesno($name, $label = '', $attrs = []) {
294
+ $attrs = $this->_merge_attrs($attrs);
295
+ $this->_open();
296
+ $this->_label($label);
297
+ $this->controls->yesno($name);
298
+ $this->_description($attrs);
299
+ $this->_close();
300
+ }
301
+
302
+ public function select_number($name, $label = '', $min = 0, $max = 10, $attrs = []) {
303
+ $attrs = $this->_merge_attrs($attrs);
304
+ $this->_open();
305
+ $this->_label($label);
306
+ $this->controls->select_number($name, $min, $max);
307
+ $this->_description($attrs);
308
+ $this->_close();
309
+ }
310
+
311
+ /**
312
+ * General field to collect an element dimension in pixels
313
+ *
314
+ * Attributes:
315
+ * - size: field width in pixels
316
+ */
317
+ public function size($name, $label = '', $attrs = []) {
318
+ $attrs = $this->_merge_attrs($attrs, ['description' => '', 'placeholder' => '', 'size' => 0, 'label_after' => 'px']);
319
+ $this->_open('tnp-size');
320
+ $this->_label($label);
321
+ $value = $this->controls->get_value($name);
322
+ echo '<input id="', $this->_id($name), '" placeholder="', esc_attr($attrs['placeholder']), '" name="', $this->_name($name), '" type="text"';
323
+ if (!empty($attrs['size'])) {
324
+ echo ' style="width: ', $attrs['size'], 'px"';
325
+ }
326
+ echo ' value="', esc_attr($value), '">', $attrs['label_after'];
327
+ $this->_description($attrs);
328
+ $this->_close();
329
+ }
330
+
331
+ /**
332
+ * Collects a color in HEX format with a picker.
333
+ */
334
+ public function color($name, $label, $attrs = []) {
335
+ $this->_open('tnp-color');
336
+ $this->_label($label);
337
+ $this->controls->color($name);
338
+ $this->_description($attrs);
339
+ $this->_close();
340
+ }
341
+
342
+ /**
343
+ * Configuration for a simple button with label and color
344
+ *
345
+ * Attributes:
346
+ * - weight: if true (default) shows the font weight selector
347
+ * - url_paceholder: the placeholder for the URL field
348
+ * - url: if true (default) shows the URL field (sometime the URL is produced elsewhere, for example on post list)
349
+ */
350
+ public function button($name, $label = '', $attrs = []) {
351
+ $attrs = $this->_merge_attrs($attrs,
352
+ [
353
+ 'placeholder' => 'Label...',
354
+ 'url_placeholder' => 'https://...',
355
+ 'url' => true,
356
+ 'weight' => true,
357
+ 'family_default' => false,
358
+ 'size_default' => false,
359
+ 'weight_default' => false,
360
+ ]);
361
+
362
+ $this->_open('tnpf-button');
363
+ $this->_label($label);
364
+ $value = $this->controls->get_value($name . '_label');
365
+ $name_esc = esc_attr($name);
366
+ echo '<div class="tnp-field-row">';
367
+ echo '<div class="tnp-field-col-2">';
368
+ echo '<input id="', $this->_id($name . '_label'), '" placeholder="', esc_attr($attrs['placeholder']), '" name="options[', $name_esc, '_label]" type="text"';
369
+ echo ' style="width: 100%"';
370
+ echo ' value="', esc_attr($value), '">';
371
+ echo '</div>';
372
+
373
+ if ($attrs['url']) {
374
+ $value = $this->controls->get_value($name . '_url');
375
+ echo '<div class="tnp-field-col-2">';
376
+ $width = isset($attrs['media']) ? '90%' : '100%';
377
+ echo '<input id="', $this->_id($name . '_url'), '" placeholder="', esc_attr($attrs['url_placeholder']), '" name="options[',
378
+ $name_esc, '_url]" type="url" style="width: ', $width, '" value="', esc_attr($value), '">';
379
+ if (isset($attrs['media'])) {
380
+ echo '&nbsp;<i class="far fa-folder-open" data-field="', $this->_id($name . '_url'), '" onclick="tnp_fields_url_select(this)"></i>';
381
+ }
382
+ echo '</div>';
383
+ }
384
+ echo '<div style="clear: both"></div>';
385
+ echo '</div>';
386
+ $this->controls->css_font($name . '_font', [
387
+ 'weight' => $attrs['weight'],
388
+ 'family_default' => $attrs['family_default'],
389
+ 'size_default' => $attrs['size_default'],
390
+ 'weight_default' => $attrs['weight_default']
391
+ ]);
392
+ $this->controls->color($name . '_background');
393
+ $this->_close();
394
+ }
395
+
396
+ public function button_style($name, $label = '') {
397
+
398
+ $this->_open('tnp-font');
399
+ $this->_label($label);
400
+ $this->controls->css_font($name . '_font');
401
+ $this->controls->color($name . '_background_color');
402
+ $this->_close();
403
+ }
404
+
405
+ /**
406
+ * URL input field
407
+ *
408
+ * @param string $name
409
+ * @param string $label
410
+ * @param array $attrs
411
+ */
412
+ public function url($name, $label = '', $attrs = []) {
413
+ $attrs = $this->_merge_attrs($attrs, ['placeholder' => 'https://...']);
414
+ $this->_open('tnp-url');
415
+ $this->_label($label);
416
+ $this->controls->text_url($name);
417
+ if (isset($attrs['media'])) {
418
+ echo '<i class="far fa-folder-open" onclick="tnp_fields_url_select(\'options_', $name, '\')"></i>';
419
+ }
420
+ $this->_description($attrs);
421
+ $this->_close();
422
+ }
423
+
424
+ /**
425
+ * Provides a list of custom post types.
426
+ *
427
+ * @param string $name
428
+ * @param string $label
429
+ * @param array $attrs
430
+ */
431
+ public function post_type($name = 'post_type', $label = '', $attrs = []) {
432
+
433
+ $post_types = get_post_types(['public' => true], 'objects', 'and');
434
+
435
+ $attrs = array_merge(['description' => ''], $attrs);
436
+ $this->_open('tnp-post-type');
437
+ $this->_label($label);
438
+
439
+ $options = ['post' => 'Standard posts', 'page' => 'Pages'];
440
+
441
+ foreach ($post_types as $post_type) {
442
+ if ($post_type->name == 'post' || $post_type->name == 'page' || $post_type->name == 'attachment') {
443
+ continue;
444
+ }
445
+ $options[$post_type->name] = $post_type->labels->name;
446
+ }
447
+
448
+ $value = $this->controls->get_value($name);
449
+
450
+ echo '<select id="', $this->_id($name), '" name="options[' . esc_attr($name) . ']" onchange="tnpc_reload_options(event); return false;">';
451
+ // if (!empty($first)) {
452
+ // echo '<option value="">' . esc_html($first) . '</option>';
453
+ // }
454
+ $label = esc_html($label);
455
+ foreach ($options as $key => $label) {
456
+ echo '<option value="' . esc_attr($key) . '"';
457
+ if ($value == $key)
458
+ echo ' selected';
459
+ echo '>', $label, '</option>';
460
+ }
461
+ echo '</select>';
462
+
463
+ $this->_description($attrs);
464
+ $this->_close();
465
+ }
466
+
467
+ function posts($name, $label, $count = 20, $args = []) {
468
+ $args = array_merge(array('filters' => array(
469
+ 'posts_per_page' => 5,
470
+ 'offset' => 0,
471
+ 'category' => '',
472
+ 'category_name' => '',
473
+ 'orderby' => 'date',
474
+ 'order' => 'DESC',
475
+ 'include' => '',
476
+ 'exclude' => '',
477
+ 'meta_key' => '',
478
+ 'meta_value' => '',
479
+ 'post_type' => 'post',
480
+ 'post_mime_type' => '',
481
+ 'post_parent' => '',
482
+ 'author' => '',
483
+ 'author_name' => '',
484
+ 'post_status' => 'publish',
485
+ 'suppress_filters' => true),
486
+ 'last_post_option' => false
487
+ ), $args);
488
+ $args['filters']['posts_per_page'] = $count;
489
+
490
+ $posts = get_posts($args['filters']);
491
+ $options = array();
492
+ if ($args['last_post_option']) {
493
+ $options['last'] = 'Most recent post';
494
+ }
495
+ foreach ($posts as $post) {
496
+ $options['' . $post->ID] = $post->post_title;
497
+ }
498
+
499
+ $this->select($name, $label, $options);
500
+ }
501
+
502
+ function lists($name, $label, $attrs = []) {
503
+ $attrs = $this->_merge_attrs($attrs, ['empty_label' => null]);
504
+ $this->_open();
505
+ $this->_label($label);
506
+ $lists = $this->controls->get_list_options($attrs['empty_label']);
507
+ $this->controls->select($name, $lists);
508
+ $this->_description($attrs);
509
+ $this->_close();
510
+ }
511
+
512
+ /**
513
+ * Media selector using the WP media library (for images and files.
514
+ * The field to use it the {$name}_id which contains the media id.
515
+ *
516
+ * Attributes:
517
+ * - alt: if true shows the alternate text field for the "alt" attribute
518
+ * - layout: if set to "mini" the controls is shown as a mini selector, no labels
519
+ *
520
+ * @param string $name
521
+ * @param string $label
522
+ * @param array $attrs
523
+ */
524
+ public function media($name, $label = '', $attrs = []) {
525
+ $attrs = $this->_merge_attrs($attrs, ['alt' => false, 'layout' => '']);
526
+
527
+ if (empty($attrs['layout'])) {
528
+ $this->_open('tnp-media');
529
+ $this->_label($label);
530
+ $this->controls->media($name);
531
+ if ($attrs['alt']) {
532
+ $this->controls->text($name . '_alt', 20, 'Alternative text');
533
+ }
534
+ $this->_description($attrs);
535
+ $this->_close();
536
+ } else {
537
+ if (isset($this->controls->data[$name]['id'])) {
538
+ $media_id = (int) $this->controls->data[$name]['id'];
539
+ $media = wp_get_attachment_image_src($media_id, 'thumbnail');
540
+ } else {
541
+ $media = false;
542
+ $media_id = 0;
543
+ }
544
+ echo '<div class="tnpf-media-mini-select" data-name="' . esc_attr($name) . '" style="width: 100px; height: 100px; overflow: hidden; border: 1px dashed #999; position: relative" onclick="tnp_fields_media_mini_select(this)">';
545
+ echo '<a style="position: absolute; top: 5px; right: 5px; background-color: #000; color: #fff; padding: 0px 5px 6px 5px; font-size: 24px; display: block; text-decoration: none" href="#" onclick="tnp_fields_media_mini_remove(\'' . esc_attr($name) . '\'); return false;">&times;</a>';
546
+ if ($media) {
547
+ echo '<img style="max-width: 100%; height: auto; display: block" id="' . esc_attr($name) . '_img" src="' . esc_attr($media[0]) . '">';
548
+ } else {
549
+ echo '<img style="max-width: 100%; height: auto; display: block" id="' . esc_attr($name) . '_img" src="">';
550
+ }
551
+
552
+ echo '</div>';
553
+ echo '<input type="hidden" id="' . esc_attr($name) . '_id" name="options[' . esc_attr($name) . '][id]" value="' . esc_attr($media_id) . '">';
554
+ }
555
+ }
556
+
557
+ public function categories($name = 'categories', $label = '', $attrs = []) {
558
+ if (empty($label)) {
559
+ $label = __('Categories', 'newsletter');
560
+ }
561
+ $attrs = $this->_merge_attrs($attrs);
562
+ $this->_open('tnp-categories');
563
+ $this->_label($label);
564
+ $this->controls->categories_group($name);
565
+ $this->_description($attrs);
566
+ $this->_close();
567
+ }
568
+
569
+ /**
570
+ * The field name is preset to tax_$taxonomy. A different name can be specified
571
+ * with the attribute 'name'.
572
+ * @param type $taxonomy
573
+ * @param type $label
574
+ * @param type $attrs
575
+ */
576
+ public function terms($taxonomy, $label = '', $attrs = []) {
577
+ if (isset($attrs['name'])) {
578
+ $name = $attrs['name'];
579
+ } else {
580
+ $name = 'tax_' . $taxonomy;
581
+ }
582
+ if (empty($label)) {
583
+ $label = __('Terms', 'newsletter');
584
+ }
585
+ $attrs = $this->_merge_attrs($attrs);
586
+ $this->_open('tnp-categories');
587
+ $this->_label($label);
588
+ $terms = get_terms($taxonomy);
589
+
590
+ if (empty($terms)) {
591
+ echo 'No terms in use';
592
+ } else {
593
+
594
+ echo '<div class="newsletter-checkboxes-group">';
595
+ foreach ($terms as $term) {
596
+ /* @var $term WP_Term */
597
+ echo '<div class="newsletter-checkboxes-item">';
598
+ $this->controls->checkbox_group($name, $term->term_id, esc_html($term->name));
599
+ echo '</div>';
600
+ }
601
+ echo '<div style="clear: both"></div>';
602
+ echo '</div>';
603
+ }
604
+
605
+ $this->_description($attrs);
606
+ $this->_close();
607
+ }
608
+
609
+ /**
610
+ * Shows a language selector only if the blog is multilanguage.
611
+ *
612
+ * @param string $name
613
+ * @param string $label
614
+ * @param array $attrs
615
+ */
616
+ public function language($name = 'language', $label = '', $attrs = []) {
617
+ if (!Newsletter::instance()->is_multilanguage()) {
618
+ return;
619
+ }
620
+ if (empty($label)) {
621
+ $label = __('Language', 'newsletter');
622
+ }
623
+ $attrs = $this->_merge_attrs($attrs);
624
+ $this->_open('tnp-language');
625
+ $this->_label($label);
626
+ $this->controls->language($name);
627
+ $this->_description($attrs);
628
+ $this->_close();
629
+ }
630
+
631
+ /**
632
+ * Collects font details for a text: family, color, size and weight to be used
633
+ * directly on CSS rules. Size is a pure number.
634
+ *
635
+ * Attributes:
636
+ * - family: true|false enable or not the font family field
637
+ * - family_default: true|false enables the default entry with an empty key value
638
+ * - color: true|false enable or not the color field
639
+ * - weight: true|false enable or not the weight field
640
+ * - size: true|false enable or not the size selection
641
+ *
642
+ * @param type $name
643
+ * @param type $label
644
+ * @param array $attrs
645
+ */
646
+ public function font($name = 'font', $label = 'Font', $attrs = []) {
647
+ $attrs = $this->_merge_base_attrs($attrs);
648
+ $attrs = array_merge([
649
+ 'hide_family' => false,
650
+ 'family' => true,
651
+ 'color' => true,
652
+ 'size' => true,
653
+ 'weight' => true,
654
+ 'family_default' => false,
655
+ 'size_default' => false,
656
+ 'weight_default' => false,
657
+ ], $attrs);
658
+
659
+ $this->_open('tnp-font');
660
+ $this->_label($label);
661
+
662
+ $this->controls->css_font_family($name . '_family', !empty($attrs['family_default']));
663
+
664
+ if ($attrs['size']) {
665
+ $this->controls->css_font_size($name . '_size', !empty($attrs['size_default']));
666
+ }
667
+ if ($attrs['weight']) {
668
+ $this->controls->css_font_weight($name . '_weight', !empty($attrs['weight_default']));
669
+ }
670
+ if ($attrs['color']) {
671
+ $this->controls->color($name . '_color');
672
+ }
673
+
674
+ $this->_description($attrs);
675
+ $this->_close();
676
+ }
677
+
678
+ /**
679
+ * Collects fout number values representing the padding of a box. The values can
680
+ * be found as {$name}_top, {$name}_bottom, {$name}_left, {$name}_right.
681
+ *
682
+ * @param type $name
683
+ * @param type $label
684
+ * @param type $attrs
685
+ */
686
+ public function padding($name = 'block_padding', $label = 'Padding', $attrs = []) {
687
+ $attrs = $this->_merge_base_attrs($attrs);
688
+ $attrs = array_merge(['padding_top' => 0, 'padding_left' => 0, 'padding_right' => 0, 'padding_bottom' => 0], $attrs);
689
+ $field_only = !empty($attrs['field_only']);
690
+
691
+ if (!$field_only) {
692
+ $this->_open('tnp-padding');
693
+ $this->_label($label);
694
+ }
695
+ echo '<div class="tnp-padding-fields">';
696
+ echo '&larr;';
697
+ $this->controls->text($name . '_left', 5);
698
+ echo '&nbsp;&nbsp;&nbsp;';
699
+ echo '&uarr;';
700
+ $this->controls->text($name . '_top', 5);
701
+ echo '&nbsp;&nbsp;&nbsp;';
702
+
703
+ $this->controls->text($name . '_bottom', 5);
704
+ echo '&darr;';
705
+ echo '&nbsp;&nbsp;&nbsp;';
706
+ $this->controls->text($name . '_right', 5);
707
+ echo '&rarr;';
708
+ echo '</div>';
709
+ if (!$field_only) {
710
+ $this->_description($attrs);
711
+ $this->_close();
712
+ }
713
+ }
714
+
715
+ /**
716
+ * Background color selector for a block.
717
+ */
718
+ public function block_background() {
719
+ $this->color('block_background', __('Block Background', 'newsletter'));
720
+ }
721
+
722
+ /**
723
+ * Padding selector for a block.
724
+ */
725
+ public function block_padding() {
726
+ $this->padding('block_padding', __('Padding', 'newsletter'));
727
+ }
728
+
729
+ public function block_commons() {
730
+
731
+ $this->_open('tnp-block-commons');
732
+ $this->_label('Padding and background');
733
+ $this->controls->color('block_background');
734
+
735
+ echo '&nbsp;&rarr;&nbsp;';
736
+ $this->controls->checkbox('block_background_gradient');
737
+ $this->controls->color('block_background_2');
738
+
739
+ echo '&nbsp;&nbsp;&nbsp;';
740
+ $this->padding('block_padding', '', ['field_only' => true]);
741
+ echo '<div class="tnp-description">Gradients are displayed only by few clients</div>';
742
+ $this->_close();
743
+ }
744
+
745
+ }
includes/system.php ADDED
@@ -0,0 +1,263 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ defined('ABSPATH') || exit;
4
+
5
+ class NewsletterSystem {
6
+
7
+ static $instance;
8
+
9
+ const JOB_OK = 0;
10
+ const JOB_MISSING = 2;
11
+ const JOB_LATE = 1;
12
+ const JOB_SKIPPED = 3;
13
+
14
+ /**
15
+ * @return NewsletterSystem
16
+ */
17
+ static function instance() {
18
+ if (self::$instance == null) {
19
+ self::$instance = new NewsletterSystem();
20
+ }
21
+ return self::$instance;
22
+ }
23
+
24
+ function __construct() {
25
+
26
+ }
27
+
28
+ function reset_cron_stats() {
29
+ update_option('newsletter_diagnostic_cron_calls', [], false);
30
+ }
31
+
32
+ /**
33
+ *
34
+ * @param type $calls
35
+ * @return \TNP_Cron_Stats
36
+ */
37
+ function get_cron_stats() {
38
+ $calls = get_option('newsletter_diagnostic_cron_calls', []);
39
+
40
+ // Not enough data
41
+ if (count($calls) < 12) {
42
+ return null;
43
+ }
44
+
45
+ $stats = new TNP_Cron_Stats();
46
+
47
+ for ($i = 1; $i < count($calls); $i++) {
48
+ $delta = $calls[$i] - $calls[$i - 1];
49
+ $stats->deltas[] = $delta;
50
+ if ($stats->min > $delta) {
51
+ $stats->min = $delta;
52
+ }
53
+ if ($stats->max < $delta) {
54
+ $stats->max = $delta;
55
+ }
56
+ }
57
+ $stats->avg = round(array_sum($stats->deltas) / count($stats->deltas));
58
+ $stats->good = $stats->avg < NEWSLETTER_CRON_INTERVAL * 1.1; // 10% error
59
+ return $stats;
60
+ }
61
+
62
+ function get_last_cron_call() {
63
+ $calls = get_option('newsletter_diagnostic_cron_calls', []);
64
+ if (empty($calls))
65
+ return 0;
66
+ return end($calls);
67
+ }
68
+
69
+ function has_newsletter_schedule() {
70
+ $schedules = wp_get_schedules();
71
+ if (!empty($schedules)) {
72
+ foreach ($schedules as $key => $data) {
73
+ if ($key == 'newsletter') {
74
+ return true;
75
+ }
76
+ }
77
+ }
78
+ return false;
79
+ }
80
+
81
+ /**
82
+ *
83
+ * @return boolean|int
84
+ */
85
+ function get_job_delay() {
86
+ $x = wp_next_scheduled('newsletter');
87
+ return $x === false ? false : time() - $x;
88
+ }
89
+
90
+ function get_job_schedule() {
91
+ return wp_next_scheduled('newsletter');
92
+ }
93
+
94
+ function get_cache_job_status($refresh = false) {
95
+ $status = get_transient('newsletter_job_status');
96
+ if ($status === false || $refresh) {
97
+ $status = $this->get_job_status();
98
+ set_transient('newsletter_job_status', $status, DAY_IN_SECONDS);
99
+ }
100
+ return $status;
101
+ }
102
+
103
+ function get_job_status() {
104
+
105
+ $x = wp_next_scheduled('newsletter');
106
+ if ($x === false) {
107
+ return self::JOB_MISSING;
108
+ }
109
+
110
+ // Special case: the scheduler has been triggered but the job not executed
111
+ $calls = get_option('newsletter_diagnostic_cron_calls', []);
112
+ if (!empty($calls)) {
113
+ $last = end($calls);
114
+ if ($last > $x) {
115
+ return self::JOB_SKIPPED;
116
+ }
117
+ }
118
+
119
+ if (time() - $x > 900) {
120
+ return self::JOB_LATE;
121
+ }
122
+ return self::JOB_OK;
123
+ }
124
+
125
+ function reset_send_stats() {
126
+ update_option('newsletter_diagnostic_send_calls', [], false);
127
+ }
128
+
129
+ function get_send_stats() {
130
+ // Send calls stats
131
+ $send_calls = get_option('newsletter_diagnostic_send_calls', []);
132
+ if (!$send_calls) {
133
+ return null;
134
+ }
135
+ $send_max = 0;
136
+ $send_min = PHP_INT_MAX;
137
+ $send_total_time = 0;
138
+ $send_total_emails = 0;
139
+ $send_completed = 0;
140
+ $stats = new TNP_Send_stats();
141
+
142
+ // Batches
143
+ for ($i = 0; $i < count($send_calls); $i++) {
144
+ // 0 - batch start time
145
+ // 1 - batch end time
146
+ // 2 - number of sent email in this batch
147
+ // 3 - 0: prematurely stopped, 1: completed
148
+ if (empty($send_calls[$i][2])) {
149
+ continue;
150
+ }
151
+ if ($send_calls[$i][2] <= 1) {
152
+ continue;
153
+ }
154
+
155
+ $delta = $send_calls[$i][1] - $send_calls[$i][0];
156
+ $send_total_time += $delta;
157
+ $send_total_emails += $send_calls[$i][2];
158
+
159
+ $send_mean = $delta / $send_calls[$i][2];
160
+ $stats->means[] = $send_mean;
161
+ $stats->sizes[] = $send_calls[$i][2];
162
+ if ($send_min > $send_mean) {
163
+ $send_min = $send_mean;
164
+ }
165
+ if ($send_max < $send_mean) {
166
+ $send_max = $send_mean;
167
+ }
168
+ if (isset($send_calls[$i][3]) && $send_calls[$i][3]) {
169
+ $send_completed++;
170
+ }
171
+ }
172
+
173
+ if (empty($stats->means)) {
174
+ return null;
175
+ }
176
+ $send_mean = $send_total_time / $send_total_emails;
177
+
178
+ $stats->min = round($send_min, 2);
179
+ $stats->max = round($send_max, 2);
180
+ $stats->mean = round($send_mean, 2);
181
+ $stats->total_time = round($send_total_time);
182
+ $stats->total_emails = $send_total_emails;
183
+ $stats->total_runs = count($stats->means);
184
+
185
+ $stats->completed = $send_completed;
186
+ $stats->interrupted = $stats->total_runs - $stats->completed;
187
+ return $stats;
188
+ }
189
+
190
+ /**
191
+ * Returns a list of functions attached to the prvoded filter or action name.
192
+ *
193
+ * @global array $wp_filter
194
+ * @param string $tag
195
+ * @return string
196
+ */
197
+ function get_hook_functions($tag) {
198
+ global $wp_filter;
199
+ if (isset($wp_filter[$tag])) {
200
+ $b = '';
201
+ foreach ($wp_filter[$tag]->callbacks as $priority => $functions) {
202
+
203
+ foreach ($functions as $function) {
204
+ $b .= '[' . $priority . '] ';
205
+ if (is_array($function['function'])) {
206
+ if (is_object($function['function'][0])) {
207
+ $b .= get_class($function['function'][0]) . '::' . $function['function'][1];
208
+ } else {
209
+ $b .= $function['function'][0] . '::' . $function['function'][1];
210
+ }
211
+ } else {
212
+ if (is_object($function['function'])) {
213
+ $fn = new ReflectionFunction($function['function']);
214
+ $b .= get_class($fn->getClosureThis()) . '(closure)';
215
+ } else {
216
+ $b .= $function['function'];
217
+ }
218
+ }
219
+ $b .= "<br>";
220
+ }
221
+ }
222
+ }
223
+ return $b;
224
+ }
225
+
226
+ }
227
+
228
+ class TNP_Cron_Stats {
229
+
230
+ var $min = PHP_INT_MAX;
231
+ var $max = 0;
232
+ var $avg = 0;
233
+ /* List of intervals between cron calls */
234
+ var $deltas = [];
235
+ /* If the cron is triggered enough often */
236
+ var $good = true;
237
+
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;
246
+ /* Total sending time of the collected batches */
247
+ var $total_time;
248
+ /* Batch completed without timeout or max emails limit */
249
+ var $completed;
250
+ /* Batches interrupted due to reached limits */
251
+ var $interrupted;
252
+ /* Min time to send an email */
253
+ var $min = PHP_INT_MAX;
254
+ /* Max time to send an email */
255
+ var $max = 0;
256
+ /* Average time to send all collected emails */
257
+ var $mean = 0;
258
+ /* List of single batches average per email sending time */
259
+ var $means = [];
260
+ /* Number of emails in the single batches */
261
+ var $sizes = [];
262
+
263
+ }
includes/themes.php CHANGED
@@ -1,204 +1,204 @@
1
- <?php
2
-
3
- defined('ABSPATH') || exit;
4
-
5
- /**
6
- * @property string $id Theme identifier
7
- * @property string $dir Absolute path to the theme folder
8
- * @property string $name Theme name
9
- */
10
- class TNP_Theme {
11
-
12
- var $dir;
13
- var $name;
14
-
15
- public function get_defaults() {
16
- @include $this->dir . '/theme-defaults.php';
17
- if (!isset($theme_defaults) || !is_array($theme_defaults)) {
18
- return array();
19
- }
20
- return $theme_defaults;
21
- }
22
- }
23
-
24
- /**
25
- * Registers a Newsletter theme to be shown as option on standard newsletter creation
26
- * Designers love functions...
27
- * @param string $dir The absolute path of a folder containing a Newsletter theme
28
- */
29
- function tnp_register_theme($dir) {
30
- NewsletterThemes::register_theme($dir);
31
- }
32
-
33
- class NewsletterThemes {
34
-
35
- var $module;
36
- var $is_extension = false;
37
- static $registered_theme_dirs = array();
38
-
39
- static function register_theme($dir) {
40
- if (!file_exists($dir . '/theme.php')) {
41
- $error = new WP_Error('1', 'theme.php missing on folder ' . $dir);
42
- return $error;
43
- }
44
- self::$registered_theme_dirs[] = $dir;
45
- return true;
46
- }
47
-
48
- function __construct($module, $is_extension = false) {
49
- $this->module = $module;
50
- $this->is_extension = $is_extension;
51
- }
52
-
53
- /**
54
- * Build an associative array which represent a theme starting from the theme
55
- * parsing the files in the theme folder.<br>
56
- * dir - the full path to the theme folder<br>
57
- * url - the full url to the theme folder (to reference assets like images)<br>
58
- * id - the folder name, used as unique identifier<br>
59
- * screenshot - url to an image representing the theme (400x400)<br>
60
- * name - the readable theme name extracted from the theme.php<br>
61
- *
62
- * description - not used
63
- * type - not used
64
- *
65
- * @param string $dir
66
- * @return array
67
- */
68
- function build_theme($dir) {
69
-
70
- if (!is_dir($dir)) {
71
- return null;
72
- }
73
-
74
- if (!is_file($dir . '/theme.php')) {
75
- return null;
76
- }
77
-
78
- $data = get_file_data($dir . '/theme.php', array('name' => 'Name', 'type' => 'Type', 'description' => 'Description'));
79
- $data['id'] = basename($dir);
80
- $data['dir'] = $dir;
81
- if (empty($data['name'])) {
82
- $data['name'] = $data['id'];
83
- }
84
-
85
- if (empty($data['type'])) {
86
- $data['type'] = 'standard';
87
- }
88
- $relative_dir = substr($dir, strlen(WP_CONTENT_DIR));
89
- $data['url'] = content_url($relative_dir);
90
- $screenshot = $dir . '/screenshot.png';
91
- if (is_file($screenshot)) {
92
- $relative_dir = substr($dir, strlen(WP_CONTENT_DIR));
93
- $data['screenshot'] = $data['url'] . '/screenshot.png';
94
- } else {
95
- $data['screenshot'] = plugins_url('newsletter') . '/images/theme-screenshot.png';
96
- }
97
- return $data;
98
- }
99
-
100
- function get_all_with_data() {
101
- $list = array();
102
-
103
- // Packaged themes
104
- $list['default'] = $this->build_theme(NEWSLETTER_DIR . '/emails/themes/default');
105
- $list['blank'] = $this->build_theme(NEWSLETTER_DIR . '/emails/themes/blank');
106
- $list['cta-2015'] = $this->build_theme(NEWSLETTER_DIR . '/emails/themes/cta-2015');
107
- $list['vimeo-like'] = $this->build_theme(NEWSLETTER_DIR . '/emails/themes/vimeo-like');
108
- $list['pint'] = $this->build_theme(NEWSLETTER_DIR . '/emails/themes/pint');
109
-
110
- // Extensions folder scan
111
- $dir = WP_CONTENT_DIR . '/extensions/newsletter/' . $this->module . '/themes';
112
- $handle = @opendir($dir);
113
-
114
- if ($handle !== false) {
115
- while ($file = readdir($handle)) {
116
-
117
- $data = $this->build_theme($dir . '/' . $file);
118
-
119
- if (!$data || isset($list[$data['id']])) {
120
- continue;
121
- }
122
-
123
- $list[$data['id']] = $data;
124
- }
125
- closedir($handle);
126
- }
127
-
128
- // Registered themes
129
- do_action('newsletter_register_themes');
130
-
131
- foreach (self::$registered_theme_dirs as $dir) {
132
- $data = $this->build_theme($dir);
133
- if (!$data || isset($list[$data['id']])) {
134
- continue;
135
- }
136
-
137
- $list[$data['id']] = $data;
138
- }
139
-
140
- return $list;
141
- }
142
-
143
- /**
144
- * Returns a data structure containing the theme details.
145
- *
146
- * @param string $id
147
- * @return array
148
- */
149
- function get_theme($id) {
150
- $themes = $this->get_all_with_data();
151
- if (isset($themes[$id])) {
152
- return $themes[$id];
153
- }
154
- return null;
155
- }
156
-
157
- /**
158
- *
159
- * @param type $theme
160
- * @param type $options
161
- * @param type $module
162
- */
163
- function save_options($theme_id, &$options) {
164
- $theme_options = array();
165
- foreach ($options as $key => &$value) {
166
- if (substr($key, 0, 6) != 'theme_')
167
- continue;
168
- $theme_options[$key] = $value;
169
- }
170
- update_option('newsletter_' . $this->module . '_theme_' . $theme_id, $theme_options, false);
171
- }
172
-
173
- function get_options($theme_id) {
174
- $options = get_option('newsletter_' . $this->module . '_theme_' . $theme_id);
175
- // To avoid merge problems.
176
- if (!is_array($options)) {
177
- $options = array();
178
- }
179
-
180
- $theme = $this->get_theme($theme_id);
181
-
182
- $file = $theme['dir'] . '/theme-defaults.php';
183
- if (is_file($file)) {
184
- @include $file;
185
- }
186
- if (isset($theme_defaults) && is_array($theme_defaults)) {
187
- $options = array_merge($theme_defaults, $options);
188
- }
189
-
190
- // main options merge
191
- $main_options = Newsletter::instance()->options;
192
- foreach ($main_options as $key => $value) {
193
- $options['main_' . $key] = $value;
194
- }
195
-
196
- $info_options = Newsletter::instance()->get_options('info');
197
- foreach ($info_options as $key => $value) {
198
- $options['main_' . $key] = $value;
199
- }
200
-
201
- return $options;
202
- }
203
- }
204
-
1
+ <?php
2
+
3
+ defined('ABSPATH') || exit;
4
+
5
+ /**
6
+ * @property string $id Theme identifier
7
+ * @property string $dir Absolute path to the theme folder
8
+ * @property string $name Theme name
9
+ */
10
+ class TNP_Theme {
11
+
12
+ var $dir;
13
+ var $name;
14
+
15
+ public function get_defaults() {
16
+ @include $this->dir . '/theme-defaults.php';
17
+ if (!isset($theme_defaults) || !is_array($theme_defaults)) {
18
+ return array();
19
+ }
20
+ return $theme_defaults;
21
+ }
22
+ }
23
+
24
+ /**
25
+ * Registers a Newsletter theme to be shown as option on standard newsletter creation
26
+ * Designers love functions...
27
+ * @param string $dir The absolute path of a folder containing a Newsletter theme
28
+ */
29
+ function tnp_register_theme($dir) {
30
+ NewsletterThemes::register_theme($dir);
31
+ }
32
+
33
+ class NewsletterThemes {
34
+
35
+ var $module;
36
+ var $is_extension = false;
37
+ static $registered_theme_dirs = array();
38
+
39
+ static function register_theme($dir) {
40
+ if (!file_exists($dir . '/theme.php')) {
41
+ $error = new WP_Error('1', 'theme.php missing on folder ' . $dir);
42
+ return $error;
43
+ }
44
+ self::$registered_theme_dirs[] = $dir;
45
+ return true;
46
+ }
47
+
48
+ function __construct($module, $is_extension = false) {
49
+ $this->module = $module;
50
+ $this->is_extension = $is_extension;
51
+ }
52
+
53
+ /**
54
+ * Build an associative array which represent a theme starting from the theme
55
+ * parsing the files in the theme folder.<br>
56
+ * dir - the full path to the theme folder<br>
57
+ * url - the full url to the theme folder (to reference assets like images)<br>
58
+ * id - the folder name, used as unique identifier<br>
59
+ * screenshot - url to an image representing the theme (400x400)<br>
60
+ * name - the readable theme name extracted from the theme.php<br>
61
+ *
62
+ * description - not used
63
+ * type - not used
64
+ *
65
+ * @param string $dir
66
+ * @return array
67
+ */
68
+ function build_theme($dir) {
69
+
70
+ if (!is_dir($dir)) {
71
+ return null;
72
+ }
73
+
74
+ if (!is_file($dir . '/theme.php')) {
75
+ return null;
76
+ }
77
+
78
+ $data = get_file_data($dir . '/theme.php', array('name' => 'Name', 'type' => 'Type', 'description' => 'Description'));
79
+ $data['id'] = basename($dir);
80
+ $data['dir'] = $dir;
81
+ if (empty($data['name'])) {
82
+ $data['name'] = $data['id'];
83
+ }
84
+
85
+ if (empty($data['type'])) {
86
+ $data['type'] = 'standard';
87
+ }
88
+ $relative_dir = substr($dir, strlen(WP_CONTENT_DIR));
89
+ $data['url'] = content_url($relative_dir);
90
+ $screenshot = $dir . '/screenshot.png';
91
+ if (is_file($screenshot)) {
92
+ $relative_dir = substr($dir, strlen(WP_CONTENT_DIR));
93
+ $data['screenshot'] = $data['url'] . '/screenshot.png';
94
+ } else {
95
+ $data['screenshot'] = plugins_url('newsletter') . '/emails/images/theme-screenshot.png';
96
+ }
97
+ return $data;
98
+ }
99
+
100
+ function get_all_with_data() {
101
+ $list = array();
102
+
103
+ // Packaged themes
104
+ $list['default'] = $this->build_theme(NEWSLETTER_DIR . '/emails/themes/default');
105
+ $list['blank'] = $this->build_theme(NEWSLETTER_DIR . '/emails/themes/blank');
106
+ $list['cta-2015'] = $this->build_theme(NEWSLETTER_DIR . '/emails/themes/cta-2015');
107
+ $list['vimeo-like'] = $this->build_theme(NEWSLETTER_DIR . '/emails/themes/vimeo-like');
108
+ $list['pint'] = $this->build_theme(NEWSLETTER_DIR . '/emails/themes/pint');
109
+
110
+ // Extensions folder scan
111
+ $dir = WP_CONTENT_DIR . '/extensions/newsletter/' . $this->module . '/themes';
112
+ $handle = @opendir($dir);
113
+
114
+ if ($handle !== false) {
115
+ while ($file = readdir($handle)) {
116
+
117
+ $data = $this->build_theme($dir . '/' . $file);
118
+
119
+ if (!$data || isset($list[$data['id']])) {
120
+ continue;
121
+ }
122
+
123
+ $list[$data['id']] = $data;
124
+ }
125
+ closedir($handle);
126
+ }
127
+
128
+ // Registered themes
129
+ do_action('newsletter_register_themes');
130
+
131
+ foreach (self::$registered_theme_dirs as $dir) {
132
+ $data = $this->build_theme($dir);
133
+ if (!$data || isset($list[$data['id']])) {
134
+ continue;
135
+ }
136
+
137
+ $list[$data['id']] = $data;
138
+ }
139
+
140
+ return $list;
141
+ }
142
+
143
+ /**
144
+ * Returns a data structure containing the theme details.
145
+ *
146
+ * @param string $id
147
+ * @return array
148
+ */
149
+ function get_theme($id) {
150
+ $themes = $this->get_all_with_data();
151
+ if (isset($themes[$id])) {
152
+ return $themes[$id];
153
+ }
154
+ return null;
155
+ }
156
+
157
+ /**
158
+ *
159
+ * @param type $theme
160
+ * @param type $options
161
+ * @param type $module
162
+ */
163
+ function save_options($theme_id, &$options) {
164
+ $theme_options = array();
165
+ foreach ($options as $key => &$value) {
166
+ if (substr($key, 0, 6) != 'theme_')
167
+ continue;
168
+ $theme_options[$key] = $value;
169
+ }
170
+ update_option('newsletter_' . $this->module . '_theme_' . $theme_id, $theme_options, false);
171
+ }
172
+
173
+ function get_options($theme_id) {
174
+ $options = get_option('newsletter_' . $this->module . '_theme_' . $theme_id);
175
+ // To avoid merge problems.
176
+ if (!is_array($options)) {
177
+ $options = array();
178
+ }
179
+
180
+ $theme = $this->get_theme($theme_id);
181
+
182
+ $file = $theme['dir'] . '/theme-defaults.php';
183
+ if (is_file($file)) {
184
+ @include $file;
185
+ }
186
+ if (isset($theme_defaults) && is_array($theme_defaults)) {
187
+ $options = array_merge($theme_defaults, $options);
188
+ }
189
+
190
+ // main options merge
191
+ $main_options = Newsletter::instance()->options;
192
+ foreach ($main_options as $key => $value) {
193
+ $options['main_' . $key] = $value;
194
+ }
195
+
196
+ $info_options = Newsletter::instance()->get_options('info');
197
+ foreach ($info_options as $key => $value) {
198
+ $options['main_' . $key] = $value;
199
+ }
200
+
201
+ return $options;
202
+ }
203
+ }
204
+
includes/tnp-blocks.js CHANGED
@@ -1,252 +1,252 @@
1
- (function (blocks, editor, element, components) {
2
-
3
- const el = element.createElement;
4
- const {registerBlockType} = blocks;
5
- const { RichText, InspectorControls, withColors, PanelColorSettings, getColorClassName, AlignmentToolbar, BlockControls } = editor;
6
- const { Fragment } = element;
7
- const { TextControl, RadioControl, Panel, PanelBody, PanelRow, SelectControl, RangeControl } = components;
8
- const colorSamples = [
9
- {
10
- name: 'GREEN SEA',
11
- slug: 'GREENSEA',
12
- color: '#16A085'
13
- },
14
- {
15
- name: 'NEPHRITIS',
16
- slug: 'NEPHRITIS',
17
- color: '#27AE60'
18
- },
19
- {
20
- name: 'BELIZE HOLE',
21
- slug: 'BELIZEHOLE',
22
- color: '#2980B9'
23
- },
24
- {
25
- name: 'WISTERIA',
26
- slug: 'WISTERIA',
27
- color: '#8E44AD'
28
- },
29
- {
30
- name: 'MIDNIGHT BLUE',
31
- slug: 'MIDNIGHTBLUE',
32
- color: '#2C3E50'
33
- },
34
- {
35
- name: 'ORANGE',
36
- slug: 'ORANGE',
37
- color: '#F39C12'
38
- },
39
- {
40
- name: 'ALIZARIN',
41
- slug: 'ALIZARIN',
42
- color: '#E74C3C'
43
- },
44
- {
45
- name: 'WHITE',
46
- slug: 'WHITE',
47
- color: '#FFFFFF'
48
- },
49
- {
50
- name: 'CLOUDS',
51
- slug: 'CLOUDS',
52
- color: '#ECF0F1'
53
- },
54
- {
55
- name: 'ASBESTOS',
56
- slug: 'ASBESTOS',
57
- color: '#7F8C8D'
58
- }
59
- ];
60
-
61
- registerBlockType('tnp/minimal', {
62
- title: 'Newsletter subscription form',
63
- icon: 'email',
64
- category: 'common',
65
- keywords: ['newsletter', 'subscription', 'form'],
66
- attributes: {
67
- formtype: {type: 'string', default: 'minimal'},
68
- content: { type: 'array', source: 'children', selector: 'p', default: 'Subscribe to our newsletter!'},
69
- list_ids: { type: 'string' },
70
- rowColor: { type: 'string'},
71
- customRowColor: { type: 'string'},
72
- textColor: { type: 'string'},
73
- customTextColor: { type: 'string'},
74
- buttonColor: { type: 'string'},
75
- customButtonColor: { type: 'string'},
76
- padding: {type: 'integer', default: 20},
77
- alignment: { type: 'string'}
78
- },
79
-
80
- edit: withColors('rowColor', 'textColor', 'buttonColor')(function (props) {
81
-
82
- function onChangeContent( newContent ) {
83
- props.setAttributes( { content: newContent } );
84
- }
85
-
86
- function onChangeAlignment( newAlignment ) {
87
- props.setAttributes( { alignment: newAlignment } );
88
- }
89
-
90
- return el( Fragment, {},
91
- el( InspectorControls, {},
92
-
93
- // 1st Panel - Form Settings
94
- el( PanelBody, { title: 'Form Settings', initialOpen: true },
95
-
96
- /* Form type */
97
- el( RadioControl,
98
- {
99
- label: 'Form type',
100
- options : [
101
- { label: 'Minimal', value: 'minimal' },
102
- { label: 'Full', value: 'full' },
103
- ],
104
- onChange: ( value ) => {
105
- props.setAttributes( { formtype: value } );
106
- },
107
- selected: props.attributes.formtype
108
- }
109
- ),
110
-
111
- /* Lists field */
112
- el( PanelRow, {},
113
- el( TextControl,
114
- {
115
- label: 'Lists IDs (comma separated)',
116
- onChange: ( value ) => {
117
- props.setAttributes( { list_ids: value } );
118
- },
119
- value: props.attributes.list_ids
120
- }
121
- )
122
- )
123
- ),
124
-
125
- /* Style */
126
- el( PanelColorSettings, {
127
- title: 'Style',
128
- colorSettings: [
129
- {
130
- colors: colorSamples, // here you can pass custom colors
131
- value: props.rowColor.color,
132
- label: 'Background color',
133
- onChange: props.setRowColor,
134
- },
135
- {
136
- colors: colorSamples, // here you can pass custom colors
137
- value: props.textColor.color,
138
- label: 'Text color',
139
- onChange: props.setTextColor,
140
- },
141
- {
142
- colors: colorSamples, // here you can pass custom colors
143
- value: props.buttonColor.color,
144
- label: 'Button color',
145
- onChange: props.setButtonColor,
146
- }
147
- ]
148
- }),
149
-
150
- el( RangeControl,
151
- {
152
- label: 'Padding',
153
- min: 0,
154
- max: 100,
155
- onChange: ( value ) => {
156
- props.setAttributes( { padding: value } );
157
- },
158
- value: props.attributes.padding
159
- }
160
- )
161
-
162
- ),
163
-
164
- el(
165
- "div",
166
- {style: {backgroundColor: props.rowColor.color, color: props.textColor.color, padding: props.attributes.padding, textAlign: props.attributes.alignment}},
167
- el(
168
- BlockControls,
169
- { key: 'controls' },
170
- el(
171
- AlignmentToolbar,
172
- {
173
- value: props.attributes.alignment,
174
- onChange: onChangeAlignment
175
- }
176
- )
177
- ),
178
- el(RichText,
179
- {
180
- tagName: 'p',
181
- format: 'string',
182
- onChange: onChangeContent,
183
- value: props.attributes.content,
184
- // formattingControls: [ 'bold' ]
185
- }),
186
- el('div',
187
- {style: {backgroundColor: 'lightGrey', margin: '20px', padding: '5px',
188
- fontFamily: '-apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen-Sans, Ubuntu, Cantarell, Helvetica Neue, sans-serif'}},
189
- el('svg',
190
- {
191
- width: 20,
192
- height: 20
193
- },
194
- wp.element.createElement( 'path',
195
- {
196
- d: "M6 14H4V6h2V4H2v12h4M7.1 17h2.1l3.7-14h-2.1M14 4v2h2v8h-2v2h4V4"
197
- }
198
- )
199
- ),
200
- ' Newsletter Form'
201
- ),
202
- ))
203
- }),
204
- save: function (props) {
205
-
206
- var rowClass = getColorClassName( 'row-color', props.attributes.rowColor );
207
- var textClass = getColorClassName( 'text-color', props.attributes.textColor );
208
- var buttonClass = getColorClassName( 'button-color', props.attributes.buttonColor );
209
-
210
- formtype_attr = "";
211
- if (props.attributes.formtype != "full") {
212
- formtype_attr = " type=\"minimal\"";
213
- }
214
-
215
- lists_attr = "";
216
- if (props.attributes.list_ids) {
217
- lists_attr = " lists=\"" + props.attributes.list_ids + "\"";
218
- }
219
-
220
- button_color_attr = "";
221
- button_color = buttonClass ? undefined : props.attributes.customButtonColor;
222
- if (button_color) {
223
- button_color_attr = " button_color=\"" + button_color + "\"";
224
- }
225
-
226
- var formStyles = {
227
- backgroundColor: rowClass ? undefined : props.attributes.customRowColor,
228
- color: textClass ? undefined : props.attributes.customTextColor,
229
- padding: props.attributes.padding,
230
- textAlign: props.attributes.alignment
231
- };
232
-
233
- return (
234
- el('div', {style: formStyles},
235
- el( RichText.Content, {
236
- tagName: 'p',
237
- value: props.attributes.content
238
- }),
239
- el(
240
- "div",
241
- {},
242
- "[newsletter_form" + formtype_attr + lists_attr + button_color_attr + "]"
243
- )));
244
- }
245
- });
246
-
247
- })(
248
- window.wp.blocks,
249
- window.wp.editor,
250
- window.wp.element,
251
- window.wp.components,
252
- );
1
+ (function (blocks, editor, element, components) {
2
+
3
+ const el = element.createElement;
4
+ const {registerBlockType} = blocks;
5
+ const { RichText, InspectorControls, withColors, PanelColorSettings, getColorClassName, AlignmentToolbar, BlockControls } = editor;
6
+ const { Fragment } = element;
7
+ const { TextControl, RadioControl, Panel, PanelBody, PanelRow, SelectControl, RangeControl } = components;
8
+ const colorSamples = [
9
+ {
10
+ name: 'GREEN SEA',
11
+ slug: 'GREENSEA',
12
+ color: '#16A085'
13
+ },
14
+ {
15
+ name: 'NEPHRITIS',
16
+ slug: 'NEPHRITIS',
17
+ color: '#27AE60'
18
+ },
19
+ {
20
+ name: 'BELIZE HOLE',
21
+ slug: 'BELIZEHOLE',
22
+ color: '#2980B9'
23
+ },
24
+ {
25
+ name: 'WISTERIA',
26
+ slug: 'WISTERIA',
27
+ color: '#8E44AD'
28
+ },
29
+ {
30
+ name: 'MIDNIGHT BLUE',
31
+ slug: 'MIDNIGHTBLUE',
32
+ color: '#2C3E50'
33
+ },
34
+ {
35
+ name: 'ORANGE',
36
+ slug: 'ORANGE',
37
+ color: '#F39C12'
38
+ },
39
+ {
40
+ name: 'ALIZARIN',
41
+ slug: 'ALIZARIN',
42
+ color: '#E74C3C'
43
+ },
44
+ {
45
+ name: 'WHITE',
46
+ slug: 'WHITE',
47
+ color: '#FFFFFF'
48
+ },
49
+ {
50
+ name: 'CLOUDS',
51
+ slug: 'CLOUDS',
52
+ color: '#ECF0F1'
53
+ },
54
+ {
55
+ name: 'ASBESTOS',
56
+ slug: 'ASBESTOS',
57
+ color: '#7F8C8D'
58
+ }
59
+ ];
60
+
61
+ registerBlockType('tnp/minimal', {
62
+ title: 'Newsletter subscription form',
63
+ icon: 'email',
64
+ category: 'common',
65
+ keywords: ['newsletter', 'subscription', 'form'],
66
+ attributes: {
67
+ formtype: {type: 'string', default: 'minimal'},
68
+ content: { type: 'array', source: 'children', selector: 'p', default: 'Subscribe to our newsletter!'},
69
+ list_ids: { type: 'string' },
70
+ rowColor: { type: 'string'},
71
+ customRowColor: { type: 'string'},
72
+ textColor: { type: 'string'},
73
+ customTextColor: { type: 'string'},
74
+ buttonColor: { type: 'string'},
75
+ customButtonColor: { type: 'string'},
76
+ padding: {type: 'integer', default: 20},
77
+ alignment: { type: 'string'}
78
+ },
79
+
80
+ edit: withColors('rowColor', 'textColor', 'buttonColor')(function (props) {
81
+
82
+ function onChangeContent( newContent ) {
83
+ props.setAttributes( { content: newContent } );
84
+ }
85
+
86
+ function onChangeAlignment( newAlignment ) {
87
+ props.setAttributes( { alignment: newAlignment } );
88
+ }
89
+
90
+ return el( Fragment, {},
91
+ el( InspectorControls, {},
92
+
93
+ // 1st Panel - Form Settings
94
+ el( PanelBody, { title: 'Form Settings', initialOpen: true },
95
+
96
+ /* Form type */
97
+ el( RadioControl,
98
+ {
99
+ label: 'Form type',
100
+ options : [
101
+ { label: 'Minimal', value: 'minimal' },
102
+ { label: 'Full', value: 'full' },
103
+ ],
104
+ onChange: ( value ) => {
105
+ props.setAttributes( { formtype: value } );
106
+ },
107
+ selected: props.attributes.formtype
108
+ }
109
+ ),
110
+
111
+ /* Lists field */
112
+ el( PanelRow, {},
113
+ el( TextControl,
114
+ {
115
+ label: 'Lists IDs (comma separated)',
116
+ onChange: ( value ) => {
117
+ props.setAttributes( { list_ids: value } );
118
+ },
119
+ value: props.attributes.list_ids
120
+ }
121
+ )
122
+ )
123
+ ),
124
+
125
+ /* Style */
126
+ el( PanelColorSettings, {
127
+ title: 'Style',
128
+ colorSettings: [
129
+ {
130
+ colors: colorSamples, // here you can pass custom colors
131
+ value: props.rowColor.color,
132
+ label: 'Background color',
133
+ onChange: props.setRowColor,
134
+ },
135
+ {
136
+ colors: colorSamples, // here you can pass custom colors
137
+ value: props.textColor.color,
138
+ label: 'Text color',
139
+ onChange: props.setTextColor,
140
+ },
141
+ {
142
+ colors: colorSamples, // here you can pass custom colors
143
+ value: props.buttonColor.color,
144
+ label: 'Button color',
145
+ onChange: props.setButtonColor,
146
+ }
147
+ ]
148
+ }),
149
+
150
+ el( RangeControl,
151
+ {
152
+ label: 'Padding',
153
+ min: 0,
154
+ max: 100,
155
+ onChange: ( value ) => {
156
+ props.setAttributes( { padding: value } );
157
+ },
158
+ value: props.attributes.padding
159
+ }
160
+ )
161
+
162
+ ),
163
+
164
+ el(
165
+ "div",
166
+ {style: {backgroundColor: props.rowColor.color, color: props.textColor.color, padding: props.attributes.padding, textAlign: props.attributes.alignment}},
167
+ el(
168
+ BlockControls,
169
+ { key: 'controls' },
170
+ el(
171
+ AlignmentToolbar,
172
+ {
173
+ value: props.attributes.alignment,
174
+ onChange: onChangeAlignment
175
+ }
176
+ )
177
+ ),
178
+ el(RichText,
179
+ {
180
+ tagName: 'p',
181
+ format: 'string',
182
+ onChange: onChangeContent,
183
+ value: props.attributes.content,
184
+ // formattingControls: [ 'bold' ]
185
+ }),
186
+ el('div',
187
+ {style: {backgroundColor: 'lightGrey', margin: '20px', padding: '5px',
188
+ fontFamily: '-apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen-Sans, Ubuntu, Cantarell, Helvetica Neue, sans-serif'}},
189
+ el('svg',
190
+ {
191
+ width: 20,
192
+ height: 20
193
+ },
194
+ wp.element.createElement( 'path',
195
+ {
196
+ d: "M6 14H4V6h2V4H2v12h4M7.1 17h2.1l3.7-14h-2.1M14 4v2h2v8h-2v2h4V4"
197
+ }
198
+ )
199
+ ),
200
+ ' Newsletter Form'
201
+ ),
202
+ ))
203
+ }),
204
+ save: function (props) {
205
+
206
+ var rowClass = getColorClassName( 'row-color', props.attributes.rowColor );
207
+ var textClass = getColorClassName( 'text-color', props.attributes.textColor );
208
+ var buttonClass = getColorClassName( 'button-color', props.attributes.buttonColor );
209
+
210
+ formtype_attr = "";
211
+ if (props.attributes.formtype != "full") {
212
+ formtype_attr = " type=\"minimal\"";
213
+ }
214
+
215
+ lists_attr = "";
216
+ if (props.attributes.list_ids) {
217
+ lists_attr = " lists=\"" + props.attributes.list_ids + "\"";
218
+ }
219
+
220
+ button_color_attr = "";
221
+ button_color = buttonClass ? undefined : props.attributes.customButtonColor;
222
+ if (button_color) {
223
+ button_color_attr = " button_color=\"" + button_color + "\"";
224
+ }
225
+
226
+ var formStyles = {
227
+ backgroundColor: rowClass ? undefined : props.attributes.customRowColor,
228
+ color: textClass ? undefined : props.attributes.customTextColor,
229
+ padding: props.attributes.padding,
230
+ textAlign: props.attributes.alignment
231
+ };
232
+
233
+ return (
234
+ el('div', {style: formStyles},
235
+ el( RichText.Content, {
236
+ tagName: 'p',
237
+ value: props.attributes.content
238
+ }),
239
+ el(
240
+ "div",
241
+ {},
242
+ "[newsletter_form" + formtype_attr + lists_attr + button_color_attr + "]"
243
+ )));
244
+ }
245
+ });
246
+
247
+ })(
248
+ window.wp.blocks,
249
+ window.wp.blockEditor,
250
+ window.wp.element,
251
+ window.wp.components,
252
+ );
{css → main/css}/dashboard.css RENAMED
@@ -1,166 +1,166 @@
1
- /* This is loaded inline in the main/index.php file */
2
-
3
- #wpfooter {
4
- position: relative;
5
- }
6
-
7
- .tnp-dashboard {
8
- background-color: #f2f5f9;
9
- color: #222222;
10
- }
11
-
12
- .tnp-dashboard .tnp-cards-container {
13
- background-color: #28313c;
14
- }
15
-
16
- /* Row Break Helper */
17
-
18
- .break {
19
- flex-basis: 100%;
20
- height: 0;
21
- }
22
-
23
- .tnp-dashboard .tnp-card {
24
- flex: 1 0;
25
- align-content: flex-start;
26
- margin: 15px;
27
- font-family: soleil, sans-serif;
28
- border-radius: 15px;
29
- background-color: #232D3B;
30
- -webkit-box-shadow: 1px 1px 7px 0px rgb(0 0 0 / 15%);
31
- -moz-box-shadow: 1px 1px 7px 0px rgba(0, 0, 0, 0.15);
32
- box-shadow: 1px 1px 7px 0px rgb(0 0 0 / 15%);
33
- padding: 15px;
34
- color: #FFF;
35
- display: flex;
36
- flex-wrap: wrap;
37
- position: relative;
38
- }
39
-
40
- .tnp-dashboard .tnp-card a, .tnp-dashboard .tnp-card a:active {
41
- text-decoration: none;
42
- color: inherit !important;
43
- }
44
-
45
-
46
- .tnp-dashboard .tnp-card .tnp-card-title {
47
- flex-basis: 80%;
48
- font-weight: 900;
49
- margin: 0px 0px 30px;
50
- font-size: 17px;
51
- }
52
-
53
- .tnp-dashboard .tnp-card .tnp-card-upper-buttons {
54
- flex-basis: 10%;
55
- font-weight: 900;
56
- margin: 0px 0px 10px;
57
- font-size: 15px;
58
- }
59
-
60
- .tnp-dashboard .tnp-card .tnp-card-description {
61
- font-size: 12px;
62
- line-height: 16px;
63
- font-weight: 500;
64
- margin: 0px 0px 20px;
65
- }
66
-
67
- .tnp-dashboard .tnp-card .tnp-card-content {
68
- width: 100%;
69
- }
70
-
71
- .tnp-dashboard .tnp-card .tnp-card-button-container {
72
- margin-top: 40px;
73
- }
74
-
75
- .tnp-dashboard .tnp-card .tnp-card-button-container a {
76
- position: absolute;
77
- padding: 7px 15px;
78
- font-size: 14px;
79
- font-weight: 500;
80
- color: #FFF !important;
81
- background-color: #2980b9;
82
- border-radius: 4px;
83
- text-decoration: none;
84
- bottom: 15px;
85
- left: auto;
86
- -webkit-transform: none;
87
- transform: none;
88
- }
89
-
90
- .tnp-dashboard .tnp-card .tnp-card-button-container a:hover {
91
- background-color: #2980b9e6;
92
- }
93
-
94
- .tnp-dashboard .tnp-card .tnp-card-newsletter-list {
95
- display: flex;
96
- margin: 10px 0px;
97
- align-items: center;
98
- background-color: #263240;
99
- border-radius: 5px;
100
- padding: 10px 10px;
101
- }
102
-
103
- .tnp-dashboard .tnp-card .tnp-card-newsletters-subject {
104
- flex-basis: 50%;
105
- margin-right: 10px;
106
- }
107
-
108
- .tnp-dashboard .tnp-card .tnp-card-newsletters-status {
109
- flex-basis: 10%;
110
- margin-right: 10px;
111
- }
112
-
113
- .tnp-dashboard .tnp-card .tnp-card-newsletters-progress {
114
- flex-basis: 30%;
115
- margin-right: 10px;
116
- }
117
-
118
- .tnp-dashboard .tnp-card .tnp-card-newsletters-action {
119
- flex-basis: 5%;
120
- margin-left: auto;
121
- }
122
-
123
- .tnp-dashboard .tnp-card .tnp-card-newsletters-subscriber-email {
124
- flex-basis: 40%;
125
- margin-right: 10px;
126
- }
127
-
128
- .tnp-dashboard .tnp-card .tnp-card-newsletters-subscriber-name {
129
- flex-basis: 30%;
130
- margin-right: 10px;
131
- }
132
-
133
- .tnp-dashboard .tnp-card .tnp-card-newsletters-subscriber-status {
134
- flex-basis: 20%;
135
- margin-right: 10px;
136
- }
137
-
138
- .tnp-dashboard .tnp-card .tnp-card-documentation-index {
139
- background-color: #2a3544;
140
- padding: 15px 20px 17px 5px;
141
- border-radius: 15px;
142
- font-size: 15px;
143
- margin: 10px;
144
- }
145
-
146
- .tnp-dashboard .tnp-card .tnp-card-documentation-index svg {
147
- margin: 0px 10px;
148
- vertical-align: text-bottom;
149
- fill: #fff;
150
- stroke: #fff;
151
- }
152
-
153
- .tnp-dashboard .tnp-card .tnp-card-documentation-index:hover {
154
- animation: ease-in 4s;
155
- background-color: #323e4e;
156
- }
157
-
158
- .tnp-card .tnp-canvas {
159
- width: 100%;
160
- }
161
-
162
- /* CSS Gradients */
163
-
164
- .tnp-mimosa {
165
- background-color: #fd7278;
166
  }
1
+ /* This is loaded inline in the main/index.php file */
2
+
3
+ #wpfooter {
4
+ position: relative;
5
+ }
6
+
7
+ .tnp-dashboard {
8
+ background-color: #f2f5f9;
9
+ color: #222222;
10
+ }
11
+
12
+ .tnp-dashboard .tnp-cards-container {
13
+ background-color: #28313c;
14
+ }
15
+
16
+ /* Row Break Helper */
17
+
18
+ .break {
19
+ flex-basis: 100%;
20
+ height: 0;
21
+ }
22
+
23
+ .tnp-dashboard .tnp-card {
24
+ flex: 1 0;
25
+ align-content: flex-start;
26
+ margin: 15px;
27
+ font-family: soleil, sans-serif;
28
+ border-radius: 15px;
29
+ background-color: #232D3B;
30
+ -webkit-box-shadow: 1px 1px 7px 0px rgb(0 0 0 / 15%);
31
+ -moz-box-shadow: 1px 1px 7px 0px rgba(0, 0, 0, 0.15);
32
+ box-shadow: 1px 1px 7px 0px rgb(0 0 0 / 15%);
33
+ padding: 15px;
34
+ color: #FFF;
35
+ display: flex;
36
+ flex-wrap: wrap;
37
+ position: relative;
38
+ }
39
+
40
+ .tnp-dashboard .tnp-card a, .tnp-dashboard .tnp-card a:active {
41
+ text-decoration: none;
42
+ color: inherit !important;
43
+ }
44
+
45
+
46
+ .tnp-dashboard .tnp-card .tnp-card-title {
47
+ flex-basis: 80%;
48
+ font-weight: 900;
49
+ margin: 0px 0px 30px;
50
+ font-size: 17px;
51
+ }
52
+
53
+ .tnp-dashboard .tnp-card .tnp-card-upper-buttons {
54
+ flex-basis: 10%;
55
+ font-weight: 900;
56
+ margin: 0px 0px 10px;
57
+ font-size: 15px;
58
+ }
59
+
60
+ .tnp-dashboard .tnp-card .tnp-card-description {
61
+ font-size: 12px;
62
+ line-height: 16px;
63
+ font-weight: 500;
64
+ margin: 0px 0px 20px;
65
+ }
66
+
67
+ .tnp-dashboard .tnp-card .tnp-card-content {
68
+ width: 100%;
69
+ }
70
+
71
+ .tnp-dashboard .tnp-card .tnp-card-button-container {
72
+ margin-top: 40px;
73
+ }
74
+
75
+ .tnp-dashboard .tnp-card .tnp-card-button-container a {
76
+ position: absolute;
77
+ padding: 7px 15px;
78
+ font-size: 14px;
79
+ font-weight: 500;
80
+ color: #FFF !important;
81
+ background-color: #2980b9;
82
+ border-radius: 4px;
83
+ text-decoration: none;
84
+ bottom: 15px;
85
+ left: auto;
86
+ -webkit-transform: none;
87
+ transform: none;
88
+ }
89
+
90
+ .tnp-dashboard .tnp-card .tnp-card-button-container a:hover {
91
+ background-color: #2980b9e6;
92
+ }
93
+
94
+ .tnp-dashboard .tnp-card .tnp-card-newsletter-list {
95
+ display: flex;
96
+ margin: 10px 0px;
97
+ align-items: center;
98
+ background-color: #263240;
99
+ border-radius: 5px;
100
+ padding: 10px 10px;
101
+ }
102
+
103
+ .tnp-dashboard .tnp-card .tnp-card-newsletters-subject {
104
+ flex-basis: 50%;
105
+ margin-right: 10px;
106
+ }
107
+
108
+ .tnp-dashboard .tnp-card .tnp-card-newsletters-status {
109
+ flex-basis: 10%;
110
+ margin-right: 10px;
111
+ }
112
+
113
+ .tnp-dashboard .tnp-card .tnp-card-newsletters-progress {
114
+ flex-basis: 30%;
115
+ margin-right: 10px;
116
+ }
117
+
118
+ .tnp-dashboard .tnp-card .tnp-card-newsletters-action {
119
+ flex-basis: 5%;
120
+ margin-left: auto;
121
+ }
122
+
123
+ .tnp-dashboard .tnp-card .tnp-card-newsletters-subscriber-email {
124
+ flex-basis: 40%;
125
+ margin-right: 10px;
126
+ }
127
+
128
+ .tnp-dashboard .tnp-card .tnp-card-newsletters-subscriber-name {
129
+ flex-basis: 30%;
130
+ margin-right: 10px;
131
+ }
132
+
133
+ .tnp-dashboard .tnp-card .tnp-card-newsletters-subscriber-status {
134
+ flex-basis: 20%;
135
+ margin-right: 10px;
136
+ }
137
+
138
+ .tnp-dashboard .tnp-card .tnp-card-documentation-index {
139
+ background-color: #2a3544;
140
+ padding: 15px 20px 17px 5px;
141
+ border-radius: 15px;
142
+ font-size: 15px;
143
+ margin: 10px;
144
+ }
145
+
146
+ .tnp-dashboard .tnp-card .tnp-card-documentation-index svg {
147
+ margin: 0px 10px;
148
+ vertical-align: text-bottom;
149
+ fill: #fff;
150
+ stroke: #fff;
151
+ }
152
+
153
+ .tnp-dashboard .tnp-card .tnp-card-documentation-index:hover {
154
+ animation: ease-in 4s;
155
+ background-color: #323e4e;
156
+ }
157
+
158
+ .tnp-card .tnp-canvas {
159
+ width: 100%;
160
+ }
161
+
162
+ /* CSS Gradients */
163
+
164
+ .tnp-mimosa {
165
+ background-color: #fd7278;
166
  }
{css → main/css}/welcome.css RENAMED
@@ -1,348 +1,348 @@
1
- /* This is loaded inline in the welcome.php page */
2
-
3
- .cd-slider-wrapper {
4
- position: relative;
5
- width: 100%;
6
- height: 90vh;
7
- /* hide horizontal scrollbar on IE11 */
8
- overflow: hidden;
9
- margin: 0 auto;
10
- }
11
- .cd-slider-wrapper .cd-slider, .cd-slider-wrapper .cd-slider > li {
12
- height: 100%;
13
- width: 100%;
14
- }
15
-
16
- .tnp-logo-big {
17
- width: 300px;
18
- }
19
-
20
- .tnp-row {
21
- display: table-row;
22
- }
23
-
24
- .tnp-third {
25
- width: 33%;
26
- float: left;
27
- }
28
-
29
- .tnp-welcome-confirm-button {
30
- color: #fff;
31
- padding: 10px 30px;
32
- background-color: #2ECC71;
33
- font-weight: 700;
34
- font-size: 15px;
35
- box-shadow: 0 20px 38px rgba(0, 0, 0, 0.16);
36
- text-decoration: none;
37
- display: inline-block;
38
- text-align: center;
39
- margin: 20px auto 0px;
40
- }
41
-
42
- .tnp-welcome-confirm-button:hover {
43
- box-shadow: 0 0 38px rgba(0, 0, 0, 0.16);
44
- color: #fff;
45
- }
46
-
47
- .tnp-welcome-confirm-button:visited {
48
- color: #fff;
49
- text-decoration: none;
50
- }
51
-
52
- .tnp-welcome-link-button {
53
- color: #fff;
54
- padding: 10px 30px;
55
- background-color: #3498DB;
56
- font-weight: 700;
57
- font-size: 15px;
58
- box-shadow: 0 20px 38px rgba(0, 0, 0, 0.16);
59
- text-decoration: none;
60
- }
61
-
62
- .tnp-welcome-link-button:hover {
63
- box-shadow: 0 0 38px rgba(0, 0, 0, 0.16);
64
- color: #fff;
65
- }
66
-
67
- .tnp-welcome-link-button:visited {
68
- color: #fff;
69
- text-decoration: none;
70
- }
71
-
72
- #tnp-welcome input[type="text"], #tnp-welcome input[type="email"] {
73
- max-width: 90%;
74
- }
75
-
76
- .tnp-welcome-next {
77
- background-color: #2ECC71;
78
- padding: 10px 20px;
79
- color: white;
80
- text-decoration: none;
81
- font-weight: 600;
82
- font-size: 16px;
83
- margin: 0px 10px;
84
- box-shadow: 0 10px 30px rgba(0, 0, 0, 0.16);
85
- width: -moz-fit-content;
86
- width: -webkit-fit-content;
87
- width: fit-content;
88
- display: flex;
89
- align-items: center;
90
- }
91
-
92
- .tnp-welcome-next:hover {
93
- box-shadow: 0 0 38px rgba(0, 0, 0, 0.16);
94
- color: #fff;
95
- }
96
-
97
- .tnp-welcome-next:visited {
98
- color: #fff;
99
- text-decoration: none;
100
- }
101
-
102
- .tnp-welcome-prev {
103
- background-color: #3498DB;
104
- padding: 10px 20px;
105
- color: white;
106
- text-decoration: none;
107
- font-weight: 600;
108
- font-size: 16px;
109
- margin: 0px 0px 0px 10px;
110
- box-shadow: 0 10px 30px rgba(0, 0, 0, 0.16);
111
- width: -moz-fit-content;
112
- width: -webkit-fit-content;
113
- width: fit-content;
114
- display: flex;
115
- align-items: center;
116
- }
117
-
118
- .tnp-welcome-prev:hover {
119
- box-shadow: 0 0 38px rgba(0, 0, 0, 0.16);
120
- color: #fff;
121
- }
122
-
123
- .tnp-welcome-prev:visited {
124
- color: #fff;
125
- text-decoration: none;
126
- }
127
-
128
- .tnp-welcome-next svg {
129
- margin-left: 10px;
130
- }
131
-
132
- .tnp-welcome-prev svg {
133
- margin-right: 10px;
134
- }
135
-
136
- .cd-slider input {
137
- width: 250px;
138
- height: 40px;
139
- border: 1px solid #6c7280;
140
- background: #454a56;
141
- color: white;
142
- color: white;
143
- padding: 0px 10px;
144
- }
145
-
146
- .cd-slider > li {
147
- position: absolute;
148
- top: 0;
149
- left: 0;
150
- opacity: 0;
151
- /* used to vertically center its content */
152
- display: table;
153
- background-position: center center;
154
- background-repeat: no-repeat;
155
- -webkit-font-smoothing: antialiased;
156
- -moz-osx-font-smoothing: grayscale;
157
- }
158
- .cd-slider > li.visible {
159
- /* selected slide */
160
- position: relative;
161
- z-index: 2;
162
- opacity: 1;
163
- }
164
- .cd-slider > li:first-of-type {
165
- background-color: #2B313A;
166
- }
167
- .cd-slider > li:nth-of-type(2) {
168
- background-color: #2B313A;
169
- }
170
- .cd-slider > li:nth-of-type(3) {
171
- background-color: #2B313A;
172
- }
173
- .cd-slider > li:nth-of-type(4) {
174
- background-color: #2B313A;
175
- }
176
- .cd-slider > li:first-of-type, .cd-slider > li:nth-of-type(2), .cd-slider > li:nth-of-type(3), .cd-slider > li:nth-of-type(4) {
177
- background-size: cover;
178
- }
179
- .cd-slider > li > div {
180
- /* vertically center the slider content */
181
- display: table-cell;
182
- vertical-align: middle;
183
- text-align: center;
184
- }
185
- .cd-slider > li h2, .cd-slider > li p {
186
- text-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
187
- line-height: 1.2;
188
- margin: 0 auto 14px;
189
- color: #ffffff;
190
- width: 90%;
191
- max-width: 320px;
192
- }
193
- .cd-slider > li h2 {
194
- font-size: 40px;
195
- }
196
- .cd-slider > li p {
197
- font-size: 18px;
198
- line-height: 26px;
199
- text-align: left;
200
- color: #B8C3C9;
201
- margin: 40px auto;
202
- }
203
-
204
- .cd-slider > li .cd-btn {
205
- display: inline-block;
206
- padding: 1.2em 1.4em;
207
- margin-top: .8em;
208
- background-color: rgba(0, 0, 0, 0.6);
209
- border-radius: .25em;
210
- font-size: 1.3rem;
211
- font-weight: 700;
212
- letter-spacing: 1px;
213
- color: #ffffff;
214
- text-transform: uppercase;
215
- -webkit-transition: background-color 0.2s;
216
- -moz-transition: background-color 0.2s;
217
- transition: background-color 0.2s;
218
- }
219
- .no-touch .cd-slider > li .cd-btn:hover {
220
- background-color: rgba(0, 0, 0, 0.8);
221
- }
222
- @media only screen and (min-width: 768px) {
223
- .cd-slider > li h2, .cd-slider > li p {
224
- max-width: 520px;
225
- }
226
- .cd-slider > li h2 {
227
- font-size: 40px;
228
- }
229
- .cd-slider > li p {
230
- font-size: 18px;
231
- line-height: 26px;
232
- text-align: left;
233
- color: #B8C3C9;
234
- margin: 40px auto;
235
-
236
- }
237
- }
238
- @media only screen and (min-width: 1170px) {
239
- .cd-slider > li h2, .cd-slider > li p {
240
- margin-bottom: 20px;
241
- }
242
- .cd-slider > li h2 {
243
- font-size: 40px;
244
- }
245
- .cd-slider > li p {
246
- font-size: 18px;
247
- line-height: 26px;
248
- text-align: center;
249
- color: #B8C3C9;
250
- margin: 30px auto;
251
- }
252
- }
253
-
254
- /* --------------------------------
255
-
256
- Tnp Welcome Slider Navigation
257
-
258
- -------------------------------- */
259
- .cd-slider-navigation {
260
- position: relative;
261
- bottom: 110px;
262
- z-index: 3;
263
- display: flex;
264
- justify-content: center;
265
- }
266
-
267
- /* svg cover layer */
268
-
269
- .cd-svg-cover {
270
- position: absolute;
271
- z-index: 1;
272
- left: 0;
273
- top: 0;
274
- height: 100%;
275
- width: 100%;
276
- opacity: 0;
277
- }
278
- .cd-svg-cover path {
279
- fill: #ED6A6A;
280
- }
281
- .cd-svg-cover.is-animating {
282
- z-index: 4;
283
- opacity: 1;
284
- -webkit-transition: opacity 0.6s;
285
- -moz-transition: opacity 0.6s;
286
- transition: opacity 0.6s;
287
- }
288
-
289
- /* Switch element style */
290
-
291
- /* The switch - the box around the slider */
292
- .switch {
293
- position: relative;
294
- display: inline-block;
295
- width: 60px;
296
- height: 34px;
297
- }
298
-
299
- /* Hide default HTML checkbox */
300
- .switch input {display:none;}
301
-
302
- /* The slider */
303
- .slider {
304
- position: absolute;
305
- cursor: pointer;
306
- top: 0;
307
- left: 0;
308
- right: 0;
309
- bottom: 0;
310
- background-color: #ccc;
311
- -webkit-transition: .4s;
312
- transition: .4s;
313
- }
314
-
315
- .slider:before {
316
- position: absolute;
317
- content: "";
318
- height: 26px;
319
- width: 26px;
320
- left: 4px;
321
- bottom: 4px;
322
- background-color: white;
323
- -webkit-transition: .4s;
324
- transition: .4s;
325
- }
326
-
327
- input:checked + .slider {
328
- background-color: #2196F3;
329
- }
330
-
331
- input:focus + .slider {
332
- box-shadow: 0 0 1px #2196F3;
333
- }
334
-
335
- input:checked + .slider:before {
336
- -webkit-transform: translateX(26px);
337
- -ms-transform: translateX(26px);
338
- transform: translateX(26px);
339
- }
340
-
341
- /* Rounded sliders */
342
- .slider.round {
343
- border-radius: 34px;
344
- }
345
-
346
- .slider.round:before {
347
- border-radius: 50%;
348
- }
1
+ /* This is loaded inline in the welcome.php page */
2
+
3
+ .cd-slider-wrapper {
4
+ position: relative;
5
+ width: 100%;
6
+ height: 90vh;
7
+ /* hide horizontal scrollbar on IE11 */
8
+ overflow: hidden;
9
+ margin: 0 auto;
10
+ }
11
+ .cd-slider-wrapper .cd-slider, .cd-slider-wrapper .cd-slider > li {
12
+ height: 100%;
13
+ width: 100%;
14
+ }
15
+
16
+ .tnp-logo-big {
17
+ width: 300px;
18
+ }
19
+
20
+ .tnp-row {
21
+ display: table-row;
22
+ }
23
+
24
+ .tnp-third {
25
+ width: 33%;
26
+ float: left;
27
+ }
28
+
29
+ .tnp-welcome-confirm-button {
30
+ color: #fff;
31
+ padding: 10px 30px;
32
+ background-color: #2ECC71;
33
+ font-weight: 700;
34
+ font-size: 15px;
35
+ box-shadow: 0 20px 38px rgba(0, 0, 0, 0.16);
36
+ text-decoration: none;
37
+ display: inline-block;
38
+ text-align: center;
39
+ margin: 20px auto 0px;
40
+ }
41
+
42
+ .tnp-welcome-confirm-button:hover {
43
+ box-shadow: 0 0 38px rgba(0, 0, 0, 0.16);
44
+ color: #fff;
45
+ }
46
+
47
+ .tnp-welcome-confirm-button:visited {
48
+ color: #fff;
49
+ text-decoration: none;
50
+ }
51
+
52
+ .tnp-welcome-link-button {
53
+ color: #fff;
54
+ padding: 10px 30px;
55
+ background-color: #3498DB;
56
+ font-weight: 700;
57
+ font-size: 15px;
58
+ box-shadow: 0 20px 38px rgba(0, 0, 0, 0.16);
59
+ text-decoration: none;
60
+ }
61
+
62
+ .tnp-welcome-link-button:hover {
63
+ box-shadow: 0 0 38px rgba(0, 0, 0, 0.16);
64
+ color: #fff;
65
+ }
66
+
67
+ .tnp-welcome-link-button:visited {
68
+ color: #fff;
69
+ text-decoration: none;
70
+ }
71
+
72
+ #tnp-welcome input[type="text"], #tnp-welcome input[type="email"] {
73
+ max-width: 90%;
74
+ }
75
+
76
+ .tnp-welcome-next {
77
+ background-color: #2ECC71;
78
+ padding: 10px 20px;
79
+ color: white;
80
+ text-decoration: none;
81
+ font-weight: 600;
82
+ font-size: 16px;
83
+ margin: 0px 10px;
84
+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.16);
85
+ width: -moz-fit-content;
86
+ width: -webkit-fit-content;
87
+ width: fit-content;
88
+ display: flex;
89
+ align-items: center;
90
+ }
91
+
92
+ .tnp-welcome-next:hover {
93
+ box-shadow: 0 0 38px rgba(0, 0, 0, 0.16);
94
+ color: #fff;
95
+ }
96
+
97
+ .tnp-welcome-next:visited {
98
+ color: #fff;
99
+ text-decoration: none;
100
+ }
101
+
102
+ .tnp-welcome-prev {
103
+ background-color: #3498DB;
104
+ padding: 10px 20px;
105
+ color: white;
106
+ text-decoration: none;
107
+ font-weight: 600;
108
+ font-size: 16px;
109
+ margin: 0px 0px 0px 10px;
110
+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.16);
111
+ width: -moz-fit-content;
112
+ width: -webkit-fit-content;
113
+ width: fit-content;
114
+ display: flex;
115
+ align-items: center;
116
+ }
117
+
118
+ .tnp-welcome-prev:hover {
119
+ box-shadow: 0 0 38px rgba(0, 0, 0, 0.16);
120
+ color: #fff;
121
+ }
122
+
123
+ .tnp-welcome-prev:visited {
124
+ color: #fff;
125
+ text-decoration: none;
126
+ }
127
+
128
+ .tnp-welcome-next svg {
129
+ margin-left: 10px;
130
+ }
131
+
132
+ .tnp-welcome-prev svg {
133
+ margin-right: 10px;
134
+ }
135
+
136
+ .cd-slider input {
137
+ width: 250px;
138
+ height: 40px;
139
+ border: 1px solid #6c7280;
140
+ background: #454a56;
141
+ color: white;
142
+ color: white;
143
+ padding: 0px 10px;
144
+ }
145
+
146
+ .cd-slider > li {
147
+ position: absolute;
148
+ top: 0;
149
+ left: 0;
150
+ opacity: 0;
151
+ /* used to vertically center its content */
152
+ display: table;
153
+ background-position: center center;
154
+ background-repeat: no-repeat;
155
+ -webkit-font-smoothing: antialiased;
156
+ -moz-osx-font-smoothing: grayscale;
157
+ }
158
+ .cd-slider > li.visible {
159
+ /* selected slide */
160
+ position: relative;
161
+ z-index: 2;
162
+ opacity: 1;
163
+ }
164
+ .cd-slider > li:first-of-type {
165
+ background-color: #2B313A;
166
+ }
167
+ .cd-slider > li:nth-of-type(2) {
168
+ background-color: #2B313A;
169
+ }
170
+ .cd-slider > li:nth-of-type(3) {
171
+ background-color: #2B313A;
172
+ }
173
+ .cd-slider > li:nth-of-type(4) {
174
+ background-color: #2B313A;
175
+ }
176
+ .cd-slider > li:first-of-type, .cd-slider > li:nth-of-type(2), .cd-slider > li:nth-of-type(3), .cd-slider > li:nth-of-type(4) {
177
+ background-size: cover;
178
+ }
179
+ .cd-slider > li > div {
180
+ /* vertically center the slider content */
181
+ display: table-cell;
182
+ vertical-align: middle;
183
+ text-align: center;
184
+ }
185
+ .cd-slider > li h2, .cd-slider > li p {
186
+ text-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
187
+ line-height: 1.2;
188
+ margin: 0 auto 14px;
189
+ color: #ffffff;
190
+ width: 90%;
191
+ max-width: 320px;
192
+ }
193
+ .cd-slider > li h2 {
194
+ font-size: 40px;
195
+ }
196
+ .cd-slider > li p {
197
+ font-size: 18px;
198
+ line-height: 26px;
199
+ text-align: left;
200
+ color: #B8C3C9;
201
+ margin: 40px auto;
202
+ }
203
+
204
+ .cd-slider > li .cd-btn {
205
+ display: inline-block;
206
+ padding: 1.2em 1.4em;
207
+ margin-top: .8em;
208
+ background-color: rgba(0, 0, 0, 0.6);
209
+ border-radius: .25em;
210
+ font-size: 1.3rem;
211
+ font-weight: 700;
212
+ letter-spacing: 1px;
213
+ color: #ffffff;
214
+ text-transform: uppercase;
215
+ -webkit-transition: background-color 0.2s;
216
+ -moz-transition: background-color 0.2s;
217
+ transition: background-color 0.2s;
218
+ }
219
+ .no-touch .cd-slider > li .cd-btn:hover {
220
+ background-color: rgba(0, 0, 0, 0.8);
221
+ }
222
+ @media only screen and (min-width: 768px) {
223
+ .cd-slider > li h2, .cd-slider > li p {
224
+ max-width: 520px;
225
+ }
226
+ .cd-slider > li h2 {
227
+ font-size: 40px;
228
+ }
229
+ .cd-slider > li p {
230
+ font-size: 18px;
231
+ line-height: 26px;
232
+ text-align: left;
233
+ color: #B8C3C9;
234
+ margin: 40px auto;
235
+
236
+ }
237
+ }
238
+ @media only screen and (min-width: 1170px) {
239
+ .cd-slider > li h2, .cd-slider > li p {
240
+ margin-bottom: 20px;
241
+ }
242
+ .cd-slider > li h2 {
243
+ font-size: 40px;
244
+ }
245
+ .cd-slider > li p {
246
+ font-size: 18px;
247
+ line-height: 26px;
248
+ text-align: center;
249
+ color: #B8C3C9;
250
+ margin: 30px auto;
251
+ }
252
+ }
253
+
254
+ /* --------------------------------
255
+
256
+ Tnp Welcome Slider Navigation
257
+
258
+ -------------------------------- */
259
+ .cd-slider-navigation {
260
+ position: relative;
261
+ bottom: 110px;
262
+ z-index: 3;
263
+ display: flex;
264
+ justify-content: center;
265
+ }
266
+
267
+ /* svg cover layer */
268
+
269
+ .cd-svg-cover {
270
+ position: absolute;
271
+ z-index: 1;
272
+ left: 0;
273
+ top: 0;
274
+ height: 100%;
275
+ width: 100%;
276
+ opacity: 0;
277
+ }
278
+ .cd-svg-cover path {
279
+ fill: #ED6A6A;
280
+ }
281
+ .cd-svg-cover.is-animating {
282
+ z-index: 4;
283
+ opacity: 1;
284
+ -webkit-transition: opacity 0.6s;
285
+ -moz-transition: opacity 0.6s;
286
+ transition: opacity 0.6s;
287
+ }
288
+
289
+ /* Switch element style */
290
+
291
+ /* The switch - the box around the slider */
292
+ .switch {
293
+ position: relative;
294
+ display: inline-block;
295
+ width: 60px;
296
+ height: 34px;
297
+ }
298
+
299
+ /* Hide default HTML checkbox */
300
+ .switch input {display:none;}
301
+
302
+ /* The slider */
303
+ .slider {
304
+ position: absolute;
305
+ cursor: pointer;
306
+ top: 0;
307
+ left: 0;
308
+ right: 0;
309
+ bottom: 0;
310
+ background-color: #ccc;
311
+ -webkit-transition: .4s;
312
+ transition: .4s;
313
+ }
314
+
315
+ .slider:before {
316
+ position: absolute;
317
+ content: "";
318
+ height: 26px;
319
+ width: 26px;
320
+ left: 4px;
321
+ bottom: 4px;
322
+ background-color: white;
323
+ -webkit-transition: .4s;
324
+ transition: .4s;
325
+ }
326
+
327
+ input:checked + .slider {
328
+ background-color: #2196F3;
329
+ }
330
+
331
+ input:focus + .slider {
332
+ box-shadow: 0 0 1px #2196F3;
333
+ }
334
+
335
+ input:checked + .slider:before {
336
+ -webkit-transform: translateX(26px);
337
+ -ms-transform: translateX(26px);
338
+ transform: translateX(26px);
339
+ }
340
+
341
+ /* Rounded sliders */
342
+ .slider.round {
343
+ border-radius: 34px;
344
+ }
345
+
346
+ .slider.round:before {
347
+ border-radius: 50%;
348
+ }
main/delivery.php CHANGED
@@ -92,7 +92,6 @@ function tnp_get_hook_functions($tag) {
92
  foreach ($wp_filter[$tag]->callbacks as $priority => $functions) {
93
 
94
  foreach ($functions as $function) {
95
- //var_dump($function);
96
  $b .= '[' . $priority . '] ';
97
  if (is_array($function['function'])) {
98
  if (is_object($function['function'][0])) {
92
  foreach ($wp_filter[$tag]->callbacks as $priority => $functions) {
93
 
94
  foreach ($functions as $function) {
 
95
  $b .= '[' . $priority . '] ';
96
  if (is_array($function['function'])) {
97
  if (is_object($function['function'][0])) {
main/diagnostic.php DELETED
@@ -1,84 +0,0 @@
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
- function tnp_get_hook_functions($tag) {
11
- global $wp_filter;
12
- if (isset($wp_filter)) {
13
- $b = '';
14
- foreach ($wp_filter[$tag]->callbacks as $priority => $functions) {
15
-
16
- foreach ($functions as $function) {
17
- //var_dump($function);
18
- $b .= '[' . $priority . '] ';
19
- if (is_array($function['function'])) {
20
- if (is_object($function['function'][0])) {
21
- $b .= get_class($function['function'][0]) . '::' . $function['function'][1];
22
- } else {
23
- $b .= $function['function'][0] . '::' . $function['function'][1];
24
- }
25
- } else {
26
- if (is_object($function['function'])) {
27
- $fn = new ReflectionFunction($function['function']);
28
- $b .= get_class($fn->getClosureThis()) . '(closure)';
29
- } else {
30
- $b .= $function['function'];
31
- }
32
- }
33
- $b .= "<br>";
34
- }
35
- }
36
- }
37
- return $b;
38
- }
39
- ?>
40
-
41
- <div class="wrap tnp-main-diagnostic" id="tnp-wrap">
42
-
43
- <?php include NEWSLETTER_DIR . '/tnp-header.php'; ?>
44
-
45
- <div id="tnp-heading">
46
-
47
- <h2><?php _e('Diagnostic', 'newsletter') ?></h2>
48
-
49
- </div>
50
-
51
- <div id="tnp-body">
52
-
53
- <form method="post" action="">
54
- <?php $controls->init(); ?>
55
-
56
- <h3>Hooks</h3>
57
- <table class="widefat" id="tnp-status-table">
58
-
59
- <thead>
60
- <tr>
61
- <th>Parameter</th>
62
- <th><?php _e('Status', 'newsletter') ?></th>
63
- <th>Action</th>
64
- </tr>
65
- </thead>
66
-
67
- <tbody>
68
- <tr>
69
- <td>Filter "newsletter_replace"</td>
70
- <td>
71
- </td>
72
- <td>
73
- <?php echo tnp_get_hook_functions('newsletter_replace') ?>
74
- </td>
75
-
76
- </tr>
77
- </tbody>
78
- </table>
79
- </form>
80
- </div>
81
-
82
- <?php include NEWSLETTER_DIR . '/tnp-footer.php'; ?>
83
-
84
- </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
main/index.php CHANGED
@@ -1,318 +1,318 @@
1
- <?php
2
- /* @var $this Newsletter */
3
-
4
- defined('ABSPATH') || exit;
5
-
6
- include_once NEWSLETTER_INCLUDES_DIR . '/controls.php';
7
- $controls = new NewsletterControls();
8
-
9
- wp_enqueue_script('tnp-chart');
10
-
11
- if ($controls->is_action('feed_enable')) {
12
- delete_option('newsletter_feed_demo_disable');
13
- $controls->messages = 'Feed by Mail demo panels enabled. On next page reload it will show up.';
14
- }
15
-
16
- if ($controls->is_action('feed_disable')) {
17
- update_option('newsletter_feed_demo_disable', 1);
18
- $controls->messages = 'Feed by Mail demo panel disabled. On next page reload it will disappear.';
19
- }
20
-
21
- $emails_module = NewsletterEmails::instance();
22
- $statistics_module = NewsletterStatistics::instance();
23
- $emails = $wpdb->get_results("select * from " . NEWSLETTER_EMAILS_TABLE . " where type='message' order by id desc limit 5");
24
-
25
- $users_module = NewsletterUsers::instance();
26
- $query = "select * from " . NEWSLETTER_USERS_TABLE . " order by id desc limit 5";
27
- $subscribers = $wpdb->get_results($query);
28
-
29
- // Retrieves the last standard newsletter
30
- $last_email = $wpdb->get_row(
31
- $wpdb->prepare("select * from " . NEWSLETTER_EMAILS_TABLE . " where type='message' and status in ('sent', 'sending') and send_on<%d order by id desc limit 1", time()));
32
-
33
- if ($last_email) {
34
- $report = $statistics_module->get_statistics($last_email);
35
- $last_email_sent = $report->total;
36
- $last_email_opened = $report->open_count;
37
- $last_email_notopened = $last_email_sent - $last_email_opened;
38
- $last_email_clicked = $report->click_count;
39
- $last_email_opened -= $last_email_clicked;
40
-
41
- $overall_sent = $wpdb->get_var("select sum(sent) from " . NEWSLETTER_EMAILS_TABLE . " where type='message' and status in ('sent', 'sending')");
42
-
43
- $overall_opened = $wpdb->get_var("select count(distinct user_id,email_id) from " . NEWSLETTER_STATS_TABLE);
44
- $overall_notopened = $overall_sent - $overall_opened;
45
- $overall_clicked = $wpdb->get_var("select count(distinct user_id,email_id) from " . NEWSLETTER_STATS_TABLE . " where url<>''");
46
- $overall_opened -= $overall_clicked;
47
- } else {
48
- $last_email_opened = 500;
49
- $last_email_notopened = 400;
50
- $last_email_clicked = 200;
51
-
52
- $overall_opened = 500;
53
- $overall_notopened = 400;
54
- $overall_clicked = 200;
55
- }
56
-
57
- $months = $wpdb->get_results("select count(*) as c, concat(year(created), '-', date_format(created, '%m')) as d "
58
- . "from " . NEWSLETTER_USERS_TABLE . " where status='C' "
59
- . "group by concat(year(created), '-', date_format(created, '%m')) order by d desc limit 12");
60
- $values = array();
61
- $labels = array();
62
- foreach ($months as $month) {
63
- $values[] = (int) $month->c;
64
- $labels[] = date("M y", date_create_from_format("Y-m", $month->d)->getTimestamp());
65
- }
66
- $values = array_reverse($values);
67
- $labels = array_reverse($labels);
68
-
69
- $lists = $this->get_lists();
70
- ?>
71
-
72
- <style>
73
- <?php include NEWSLETTER_DIR . '/css/dashboard.css' ?>
74
- </style>
75
-
76
- <div class="wrap" id="tnp-wrap">
77
-
78
- <?php include NEWSLETTER_DIR . '/tnp-header.php'; ?>
79
-
80
- <div id="tnp-body" class="tnp-main-index">
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">
118
- <?php foreach ($emails as $email) { ?>
119
- <div class="tnp-card-newsletter-list">
120
- <?php
121
- $subject = $email->subject ? $email->subject : "Newsletter #" . $email->id;
122
- ?>
123
- <div class="tnp-card-newsletters-subject">
124
- <?php echo esc_html($subject) ?>
125
- </div>
126
- <div class="tnp-card-newsletters-status">
127
- <?php $emails_module->show_email_status_label($email) ?>
128
- </div>
129
- <div class="tnp-card-newsletters-progress">
130
- <?php $emails_module->show_email_progress_bar($email, array('scheduled' => true)) ?>
131
- </div>
132
- <div class="tnp-card-newsletters-action">
133
- <?php
134
- if ($email->status === TNP_Email::STATUS_SENT || $email->status === TNP_Email::STATUS_SENDING) {
135
- echo '<a class="button-primary" href="' . $statistics_module->get_statistics_url($email->id) . '"><i class="fas fa-chart-bar"></i></a>';
136
- } else {
137
- echo $emails_module->get_edit_button($email, true);
138
- }
139
- ?>
140
- </div>
141
- </div>
142
- <?php } ?>
143
- </div>
144
- </div>
145
-
146
- <div class="tnp-card">
147
- <div class="tnp-card-title">Last Subscribers</div>
148
- <div class="tnp-card-upper-buttons"><a href="<?php echo $users_module->get_admin_page_url('new'); ?>"><?php _e('New', 'newsletter') ?></a></div>
149
- <div class="tnp-card-upper-buttons"><a href="<?php echo $users_module->get_admin_page_url('index'); ?>"><?php _e('List', 'newsletter') ?></a></div>
150
- <div class="tnp-card-content">
151
-
152
- <?php foreach ($subscribers as $s) { ?>
153
- <div class="tnp-card-newsletter-list">
154
- <div class="tnp-card-newsletters-subscriber-email">
155
- <?php echo esc_html($s->email) ?>
156
- </div>
157
-
158
- <div class="tnp-card-newsletters-subscriber-name">
159
- <?php echo esc_html($s->name) ?> <?php echo esc_html($s->surname) ?>
160
- </div>
161
- <div class="tnp-card-newsletters-subscriber-status">
162
- <?php echo $emails_module->get_user_status_label($s) ?>
163
- </div>
164
- <div class="tnp-card-newsletters-action">
165
- <a class="button-primary"
166
- title="<?php _e('Edit', 'newsletter') ?>"
167
- href="<?php echo $users_module->get_admin_page_url('edit'); ?>&amp;id=<?php echo $s->id; ?>"><i
168
- class="fas fa-edit"></i></a>
169
- <!--
170
- <a title="<?php _e('Profile', 'newsletter') ?>"
171
- href="<?php echo home_url('/') ?>?na=p&nk=<?php echo $s->id . '-' . $s->token; ?>"
172
- class="button-primary" target="_blank"><i
173
- class="fas fa-user"></i></a>-->
174
- </div>
175
- </div>
176
- <?php } ?>
177
-
178
-
179
-
180
- </div>
181
- </div>
182
- </div>
183
- <div class="tnp-cards-container">
184
- <div class="tnp-card">
185
- <div class="tnp-card-title"><?php _e('Subscriptions', 'newsletter') ?></div>
186
- <div class="tnp-canvas">
187
- <canvas id="tnp-events-chart-canvas" height="300"></canvas>
188
- </div>
189
-
190
- <script type="text/javascript">
191
- var events_data = {
192
- labels: <?php echo json_encode($labels) ?>,
193
- datasets: [
194
- {
195
- label: "<?php _e('Subscriptions', 'newsletter') ?>",
196
- fill: true,
197
- strokeColor: "#27AE60",
198
- backgroundColor: "#293544",
199
- borderColor: "#27AE60",
200
- pointBorderColor: "#27AE60",
201
- pointBackgroundColor: "#ECF0F1",
202
- data: <?php echo json_encode($values) ?>
203
- }
204
- ]
205
- };
206
-
207
- jQuery(document).ready(function ($) {
208
- ctxe = $('#tnp-events-chart-canvas').get(0).getContext("2d");
209
- eventsLineChart = new Chart(ctxe, {
210
- type: 'line', data: events_data,
211
- options: {
212
- maintainAspectRatio: false,
213
- xresponsive: true,
214
- scales: {
215
- xAxes: [{
216
- type: "category",
217
- "id": "x-axis-1",
218
- gridLines: {display: false},
219
- ticks: {fontFamily: "soleil"}
220
- }],
221
- yAxes: [
222
- {
223
- type: "linear",
224
- "id": "y-axis-1",
225
- gridLines: {display: false},
226
- ticks: {fontFamily: "soleil"}
227
- },
228
- ]
229
- },
230
- }
231
- });
232
- });
233
- </script>
234
- </div>
235
- <div class="tnp-card">
236
- <div class="tnp-card-title"><?php _e('Documentation', 'newsletter') ?></div>
237
- <div class="break"></div>
238
- <a href="https://www.thenewsletterplugin.com/documentation/installation/" target="_blank">
239
- <div class="tnp-card-documentation-index">
240
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="20" height="20"><title>saved items</title><g class="nc-icon-wrapper" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" ><path d="M37,4h3a4,4,0,0,1,4,4V40a4,4,0,0,1-4,4H8a4,4,0,0,1-4-4V8A4,4,0,0,1,8,4h3" fill="none" stroke-miterlimit="10"/> <polygon points="32 24 24 18 16 24 16 4 32 4 32 24" fill="none" stroke-miterlimit="10" data-color="color-2"/></g></svg>
241
- Installation
242
- </div>
243
- </a>
244
- <a href="https://www.thenewsletterplugin.com/documentation/subscription/" target="_blank">
245
- <div class="tnp-card-documentation-index">
246
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="20" height="20"><title>saved items</title><g class="nc-icon-wrapper" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" ><path d="M37,4h3a4,4,0,0,1,4,4V40a4,4,0,0,1-4,4H8a4,4,0,0,1-4-4V8A4,4,0,0,1,8,4h3" fill="none" stroke-miterlimit="10"/> <polygon points="32 24 24 18 16 24 16 4 32 4 32 24" fill="none" stroke-miterlimit="10" data-color="color-2"/></g></svg>
247
- Subscription
248
- </div>
249
- </a>
250
- <a href="https://www.thenewsletterplugin.com/category/tips" target="_blank">
251
- <div class="tnp-card-documentation-index">
252
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="20" height="20"><title>saved items</title><g class="nc-icon-wrapper" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" ><path d="M37,4h3a4,4,0,0,1,4,4V40a4,4,0,0,1-4,4H8a4,4,0,0,1-4-4V8A4,4,0,0,1,8,4h3" fill="none" stroke-miterlimit="10"/> <polygon points="32 24 24 18 16 24 16 4 32 4 32 24" fill="none" stroke-miterlimit="10" data-color="color-2"/></g></svg>
253
- Tips & Tricks
254
- </div>
255
- </a>
256
- <a href="https://www.thenewsletterplugin.com/documentation/subscribers-and-management/" target="_blank">
257
- <div class="tnp-card-documentation-index">
258
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="20" height="20"><title>saved items</title><g class="nc-icon-wrapper" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" ><path d="M37,4h3a4,4,0,0,1,4,4V40a4,4,0,0,1-4,4H8a4,4,0,0,1-4-4V8A4,4,0,0,1,8,4h3" fill="none" stroke-miterlimit="10"/> <polygon points="32 24 24 18 16 24 16 4 32 4 32 24" fill="none" stroke-miterlimit="10" data-color="color-2"/></g></svg>
259
- Subscribers and management
260
- </div>
261
- </a>
262
- <a href="https://www.thenewsletterplugin.com/documentation/newsletters/newsletters-module/" target="_blank">
263
- <div class="tnp-card-documentation-index">
264
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="20" height="20"><title>saved items</title><g class="nc-icon-wrapper" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" ><path d="M37,4h3a4,4,0,0,1,4,4V40a4,4,0,0,1-4,4H8a4,4,0,0,1-4-4V8A4,4,0,0,1,8,4h3" fill="none" stroke-miterlimit="10"/> <polygon points="32 24 24 18 16 24 16 4 32 4 32 24" fill="none" stroke-miterlimit="10" data-color="color-2"/></g></svg>
265
- Creating Newsletters
266
- </div>
267
- </a>
268
- <a href="https://www.thenewsletterplugin.com/documentation/addons/" target="_blank">
269
- <div class="tnp-card-documentation-index">
270
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="20" height="20"><title>saved items</title><g class="nc-icon-wrapper" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" ><path d="M37,4h3a4,4,0,0,1,4,4V40a4,4,0,0,1-4,4H8a4,4,0,0,1-4-4V8A4,4,0,0,1,8,4h3" fill="none" stroke-miterlimit="10"/> <polygon points="32 24 24 18 16 24 16 4 32 4 32 24" fill="none" stroke-miterlimit="10" data-color="color-2"/></g></svg>
271
- Premium Addons
272
- </div>
273
- </a>
274
- <a href="https://www.thenewsletterplugin.com/documentation/customization/" target="_blank">
275
- <div class="tnp-card-documentation-index">
276
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="20" height="20"><title>saved items</title><g class="nc-icon-wrapper" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" ><path d="M37,4h3a4,4,0,0,1,4,4V40a4,4,0,0,1-4,4H8a4,4,0,0,1-4-4V8A4,4,0,0,1,8,4h3" fill="none" stroke-miterlimit="10"/> <polygon points="32 24 24 18 16 24 16 4 32 4 32 24" fill="none" stroke-miterlimit="10" data-color="color-2"/></g></svg>
277
- Customization
278
- </div>
279
- </a>
280
- <a href="https://www.thenewsletterplugin.com/documentation/delivery-and-spam/" target="_blank">
281
- <div class="tnp-card-documentation-index">
282
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="20" height="20"><title>saved items</title><g class="nc-icon-wrapper" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" ><path d="M37,4h3a4,4,0,0,1,4,4V40a4,4,0,0,1-4,4H8a4,4,0,0,1-4-4V8A4,4,0,0,1,8,4h3" fill="none" stroke-miterlimit="10"/> <polygon points="32 24 24 18 16 24 16 4 32 4 32 24" fill="none" stroke-miterlimit="10" data-color="color-2"/></g></svg>
283
- Delivery and spam
284
- </div>
285
- </a>
286
- <a href="https://www.thenewsletterplugin.com/documentation/developers/" target="_blank">
287
- <div class="tnp-card-documentation-index">
288
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="20" height="20"><title>saved items</title><g class="nc-icon-wrapper" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" ><path d="M37,4h3a4,4,0,0,1,4,4V40a4,4,0,0,1-4,4H8a4,4,0,0,1-4-4V8A4,4,0,0,1,8,4h3" fill="none" stroke-miterlimit="10"/> <polygon points="32 24 24 18 16 24 16 4 32 4 32 24" fill="none" stroke-miterlimit="10" data-color="color-2"/></g></svg>
289
- Developers & Advanced Topics
290
- </div>
291
- </a>
292
- </div>
293
- </div>
294
- <div class="tnp-cards-container">
295
- <div class="tnp-card" style="align-self: flex-start">
296
- <div class="tnp-card-title"><?php _e('Developers', 'newsletter') ?></div>
297
- <div class="tnp-card-description">Extending Newsletter by yourself? There is something for you as well!</div>
298
- <div class="tnp-card-button-container">
299
- <a href="https://www.thenewsletterplugin.com/documentation/developers/" target="_blank">Developer's love 💛</a>
300
- </div>
301
- </div>
302
- <div class="tnp-card">
303
- <div class="tnp-card-title"><?php _e('Video Tutorials', 'newsletter') ?></div>
304
- <div class="tnp-card-description">We have some videos to help gest the most from Newsletter.</div>
305
- <div class="tnp-card-video">
306
- <iframe width="560" height="315" src="https://www.youtube.com/embed/zmVmW84Bw9A" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
307
- </div>
308
- <div class="tnp-card-button-container">
309
- <a href="https://www.thenewsletterplugin.com/video-tutorials" target="_blank">See the videos</a>
310
- </div>
311
- </div>
312
- </div>
313
-
314
- </div>
315
-
316
- <?php include NEWSLETTER_DIR . '/tnp-footer.php'; ?>
317
-
318
- </div>
1
+ <?php
2
+ /* @var $this Newsletter */
3
+
4
+ defined('ABSPATH') || exit;
5
+
6
+ include_once NEWSLETTER_INCLUDES_DIR . '/controls.php';
7
+ $controls = new NewsletterControls();
8
+
9
+ wp_enqueue_script('tnp-chart');
10
+
11
+ if ($controls->is_action('feed_enable')) {
12
+ delete_option('newsletter_feed_demo_disable');
13
+ $controls->messages = 'Feed by Mail demo panels enabled. On next page reload it will show up.';
14
+ }
15
+
16
+ if ($controls->is_action('feed_disable')) {
17
+ update_option('newsletter_feed_demo_disable', 1);
18
+ $controls->messages = 'Feed by Mail demo panel disabled. On next page reload it will disappear.';
19
+ }
20
+
21
+ $emails_module = NewsletterEmails::instance();
22
+ $statistics_module = NewsletterStatistics::instance();
23
+ $emails = $wpdb->get_results("select * from " . NEWSLETTER_EMAILS_TABLE . " where type='message' order by id desc limit 5");
24
+
25
+ $users_module = NewsletterUsers::instance();
26
+ $query = "select * from " . NEWSLETTER_USERS_TABLE . " order by id desc limit 5";
27
+ $subscribers = $wpdb->get_results($query);
28
+
29
+ // Retrieves the last standard newsletter
30
+ $last_email = $wpdb->get_row(
31
+ $wpdb->prepare("select * from " . NEWSLETTER_EMAILS_TABLE . " where type='message' and status in ('sent', 'sending') and send_on<%d order by id desc limit 1", time()));
32
+
33
+ if ($last_email) {
34
+ $report = $statistics_module->get_statistics($last_email);
35
+ $last_email_sent = $report->total;
36
+ $last_email_opened = $report->open_count;
37
+ $last_email_notopened = $last_email_sent - $last_email_opened;
38
+ $last_email_clicked = $report->click_count;
39
+ $last_email_opened -= $last_email_clicked;
40
+
41
+ $overall_sent = $wpdb->get_var("select sum(sent) from " . NEWSLETTER_EMAILS_TABLE . " where type='message' and status in ('sent', 'sending')");
42
+
43
+ $overall_opened = $wpdb->get_var("select count(distinct user_id,email_id) from " . NEWSLETTER_STATS_TABLE);
44
+ $overall_notopened = $overall_sent - $overall_opened;
45
+ $overall_clicked = $wpdb->get_var("select count(distinct user_id,email_id) from " . NEWSLETTER_STATS_TABLE . " where url<>''");
46
+ $overall_opened -= $overall_clicked;
47
+ } else {
48
+ $last_email_opened = 500;
49
+ $last_email_notopened = 400;
50
+ $last_email_clicked = 200;
51
+
52
+ $overall_opened = 500;
53
+ $overall_notopened = 400;
54
+ $overall_clicked = 200;
55
+ }
56
+
57
+ $months = $wpdb->get_results("select count(*) as c, concat(year(created), '-', date_format(created, '%m')) as d "
58
+ . "from " . NEWSLETTER_USERS_TABLE . " where status='C' "
59
+ . "group by concat(year(created), '-', date_format(created, '%m')) order by d desc limit 12");
60
+ $values = array();
61
+ $labels = array();
62
+ foreach ($months as $month) {
63
+ $values[] = (int) $month->c;
64
+ $labels[] = date("M y", date_create_from_format("Y-m", $month->d)->getTimestamp());
65
+ }
66
+ $values = array_reverse($values);
67
+ $labels = array_reverse($labels);
68
+
69
+ $lists = $this->get_lists();
70
+ ?>
71
+
72
+ <style>
73
+ <?php include __DIR__ . '/css/dashboard.css' ?>
74
+ </style>
75
+
76
+ <div class="wrap" id="tnp-wrap">
77
+
78
+ <?php include NEWSLETTER_DIR . '/tnp-header.php'; ?>
79
+
80
+ <div id="tnp-body" class="tnp-main-index">
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">
118
+ <?php foreach ($emails as $email) { ?>
119
+ <div class="tnp-card-newsletter-list">
120
+ <?php
121
+ $subject = $email->subject ? $email->subject : "Newsletter #" . $email->id;
122
+ ?>
123
+ <div class="tnp-card-newsletters-subject">
124
+ <?php echo esc_html($subject) ?>
125
+ </div>
126
+ <div class="tnp-card-newsletters-status">
127
+ <?php $emails_module->show_email_status_label($email) ?>
128
+ </div>
129
+ <div class="tnp-card-newsletters-progress">
130
+ <?php $emails_module->show_email_progress_bar($email, array('scheduled' => true)) ?>
131
+ </div>
132
+ <div class="tnp-card-newsletters-action">
133
+ <?php
134
+ if ($email->status === TNP_Email::STATUS_SENT || $email->status === TNP_Email::STATUS_SENDING) {
135
+ echo '<a class="button-primary" href="' . $statistics_module->get_statistics_url($email->id) . '"><i class="fas fa-chart-bar"></i></a>';
136
+ } else {
137
+ echo $emails_module->get_edit_button($email, true);
138
+ }
139
+ ?>
140
+ </div>
141
+ </div>
142
+ <?php } ?>
143
+ </div>
144
+ </div>
145
+
146
+ <div class="tnp-card">
147
+ <div class="tnp-card-title">Last Subscribers</div>
148
+ <div class="tnp-card-upper-buttons"><a href="<?php echo $users_module->get_admin_page_url('new'); ?>"><?php _e('New', 'newsletter') ?></a></div>
149
+ <div class="tnp-card-upper-buttons"><a href="<?php echo $users_module->get_admin_page_url('index'); ?>"><?php _e('List', 'newsletter') ?></a></div>
150
+ <div class="tnp-card-content">
151
+
152
+ <?php foreach ($subscribers as $s) { ?>
153
+ <div class="tnp-card-newsletter-list">
154
+ <div class="tnp-card-newsletters-subscriber-email">
155
+ <?php echo esc_html($s->email) ?>
156
+ </div>
157
+
158
+ <div class="tnp-card-newsletters-subscriber-name">
159
+ <?php echo esc_html($s->name) ?> <?php echo esc_html($s->surname) ?>
160
+ </div>
161
+ <div class="tnp-card-newsletters-subscriber-status">
162
+ <?php echo $emails_module->get_user_status_label($s) ?>
163
+ </div>
164
+ <div class="tnp-card-newsletters-action">
165
+ <a class="button-primary"
166
+ title="<?php _e('Edit', 'newsletter') ?>"
167
+ href="<?php echo $users_module->get_admin_page_url('edit'); ?>&amp;id=<?php echo $s->id; ?>"><i
168
+ class="fas fa-edit"></i></a>
169
+ <!--
170
+ <a title="<?php _e('Profile', 'newsletter') ?>"
171
+ href="<?php echo home_url('/') ?>?na=p&nk=<?php echo $s->id . '-' . $s->token; ?>"
172
+ class="button-primary" target="_blank"><i
173
+ class="fas fa-user"></i></a>-->
174
+ </div>
175
+ </div>
176
+ <?php } ?>
177
+
178
+
179
+
180
+ </div>
181
+ </div>
182
+ </div>
183
+ <div class="tnp-cards-container">
184
+ <div class="tnp-card">
185
+ <div class="tnp-card-title"><?php _e('Subscriptions', 'newsletter') ?></div>
186
+ <div class="tnp-canvas">
187
+ <canvas id="tnp-events-chart-canvas" height="300"></canvas>
188
+ </div>
189
+
190
+ <script type="text/javascript">
191
+ var events_data = {
192
+ labels: <?php echo json_encode($labels) ?>,
193
+ datasets: [
194
+ {
195
+ label: "<?php _e('Subscriptions', 'newsletter') ?>",
196
+ fill: true,
197
+ strokeColor: "#27AE60",
198
+ backgroundColor: "#293544",
199
+ borderColor: "#27AE60",
200
+ pointBorderColor: "#27AE60",
201
+ pointBackgroundColor: "#ECF0F1",
202
+ data: <?php echo json_encode($values) ?>
203
+ }
204
+ ]
205
+ };
206
+
207
+ jQuery(document).ready(function ($) {
208
+ ctxe = $('#tnp-events-chart-canvas').get(0).getContext("2d");
209
+ eventsLineChart = new Chart(ctxe, {
210
+ type: 'line', data: events_data,
211
+ options: {
212
+ maintainAspectRatio: false,
213
+ xresponsive: true,
214
+ scales: {
215
+ xAxes: [{
216
+ type: "category",
217
+ "id": "x-axis-1",
218
+ gridLines: {display: false},
219
+ ticks: {fontFamily: "soleil"}
220
+ }],
221
+ yAxes: [
222
+ {
223
+ type: "linear",
224
+ "id": "y-axis-1",
225
+ gridLines: {display: false},
226
+ ticks: {fontFamily: "soleil"}
227
+ },
228
+ ]
229
+ },
230
+ }
231
+ });
232
+ });
233
+ </script>
234
+ </div>
235
+ <div class="tnp-card">
236
+ <div class="tnp-card-title"><?php _e('Documentation', 'newsletter') ?></div>
237
+ <div class="break"></div>
238
+ <a href="https://www.thenewsletterplugin.com/documentation/installation/" target="_blank">
239
+ <div class="tnp-card-documentation-index">
240
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="20" height="20"><title>saved items</title><g class="nc-icon-wrapper" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" ><path d="M37,4h3a4,4,0,0,1,4,4V40a4,4,0,0,1-4,4H8a4,4,0,0,1-4-4V8A4,4,0,0,1,8,4h3" fill="none" stroke-miterlimit="10"/> <polygon points="32 24 24 18 16 24 16 4 32 4 32 24" fill="none" stroke-miterlimit="10" data-color="color-2"/></g></svg>
241
+ Installation
242
+ </div>
243
+ </a>
244
+ <a href="https://www.thenewsletterplugin.com/documentation/subscription/" target="_blank">
245
+ <div class="tnp-card-documentation-index">
246
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="20" height="20"><title>saved items</title><g class="nc-icon-wrapper" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" ><path d="M37,4h3a4,4,0,0,1,4,4V40a4,4,0,0,1-4,4H8a4,4,0,0,1-4-4V8A4,4,0,0,1,8,4h3" fill="none" stroke-miterlimit="10"/> <polygon points="32 24 24 18 16 24 16 4 32 4 32 24" fill="none" stroke-miterlimit="10" data-color="color-2"/></g></svg>
247
+ Subscription
248
+ </div>
249
+ </a>
250
+ <a href="https://www.thenewsletterplugin.com/category/tips" target="_blank">
251
+ <div class="tnp-card-documentation-index">
252
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="20" height="20"><title>saved items</title><g class="nc-icon-wrapper" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" ><path d="M37,4h3a4,4,0,0,1,4,4V40a4,4,0,0,1-4,4H8a4,4,0,0,1-4-4V8A4,4,0,0,1,8,4h3" fill="none" stroke-miterlimit="10"/> <polygon points="32 24 24 18 16 24 16 4 32 4 32 24" fill="none" stroke-miterlimit="10" data-color="color-2"/></g></svg>
253
+ Tips & Tricks
254
+ </div>
255
+ </a>
256
+ <a href="https://www.thenewsletterplugin.com/documentation/subscribers-and-management/" target="_blank">
257
+ <div class="tnp-card-documentation-index">
258
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="20" height="20"><title>saved items</title><g class="nc-icon-wrapper" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" ><path d="M37,4h3a4,4,0,0,1,4,4V40a4,4,0,0,1-4,4H8a4,4,0,0,1-4-4V8A4,4,0,0,1,8,4h3" fill="none" stroke-miterlimit="10"/> <polygon points="32 24 24 18 16 24 16 4 32 4 32 24" fill="none" stroke-miterlimit="10" data-color="color-2"/></g></svg>
259
+ Subscribers and management
260
+ </div>
261
+ </a>
262
+ <a href="https://www.thenewsletterplugin.com/documentation/newsletters/newsletters-module/" target="_blank">
263
+ <div class="tnp-card-documentation-index">
264
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="20" height="20"><title>saved items</title><g class="nc-icon-wrapper" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" ><path d="M37,4h3a4,4,0,0,1,4,4V40a4,4,0,0,1-4,4H8a4,4,0,0,1-4-4V8A4,4,0,0,1,8,4h3" fill="none" stroke-miterlimit="10"/> <polygon points="32 24 24 18 16 24 16 4 32 4 32 24" fill="none" stroke-miterlimit="10" data-color="color-2"/></g></svg>
265
+ Creating Newsletters
266
+ </div>
267
+ </a>
268
+ <a href="https://www.thenewsletterplugin.com/documentation/addons/" target="_blank">
269
+ <div class="tnp-card-documentation-index">
270
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="20" height="20"><title>saved items</title><g class="nc-icon-wrapper" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" ><path d="M37,4h3a4,4,0,0,1,4,4V40a4,4,0,0,1-4,4H8a4,4,0,0,1-4-4V8A4,4,0,0,1,8,4h3" fill="none" stroke-miterlimit="10"/> <polygon points="32 24 24 18 16 24 16 4 32 4 32 24" fill="none" stroke-miterlimit="10" data-color="color-2"/></g></svg>
271
+ Premium Addons
272
+ </div>
273
+ </a>
274
+ <a href="https://www.thenewsletterplugin.com/documentation/customization/" target="_blank">
275
+ <div class="tnp-card-documentation-index">
276
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="20" height="20"><title>saved items</title><g class="nc-icon-wrapper" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" ><path d="M37,4h3a4,4,0,0,1,4,4V40a4,4,0,0,1-4,4H8a4,4,0,0,1-4-4V8A4,4,0,0,1,8,4h3" fill="none" stroke-miterlimit="10"/> <polygon points="32 24 24 18 16 24 16 4 32 4 32 24" fill="none" stroke-miterlimit="10" data-color="color-2"/></g></svg>
277
+ Customization
278
+ </div>
279
+ </a>
280
+ <a href="https://www.thenewsletterplugin.com/documentation/delivery-and-spam/" target="_blank">
281
+ <div class="tnp-card-documentation-index">
282
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="20" height="20"><title>saved items</title><g class="nc-icon-wrapper" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" ><path d="M37,4h3a4,4,0,0,1,4,4V40a4,4,0,0,1-4,4H8a4,4,0,0,1-4-4V8A4,4,0,0,1,8,4h3" fill="none" stroke-miterlimit="10"/> <polygon points="32 24 24 18 16 24 16 4 32 4 32 24" fill="none" stroke-miterlimit="10" data-color="color-2"/></g></svg>
283
+ Delivery and spam
284
+ </div>
285
+ </a>
286
+ <a href="https://www.thenewsletterplugin.com/documentation/developers/" target="_blank">
287
+ <div class="tnp-card-documentation-index">
288
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="20" height="20"><title>saved items</title><g class="nc-icon-wrapper" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" ><path d="M37,4h3a4,4,0,0,1,4,4V40a4,4,0,0,1-4,4H8a4,4,0,0,1-4-4V8A4,4,0,0,1,8,4h3" fill="none" stroke-miterlimit="10"/> <polygon points="32 24 24 18 16 24 16 4 32 4 32 24" fill="none" stroke-miterlimit="10" data-color="color-2"/></g></svg>
289
+ Developers & Advanced Topics
290
+ </div>
291
+ </a>
292
+ </div>
293
+ </div>
294
+ <div class="tnp-cards-container">
295
+ <div class="tnp-card" style="align-self: flex-start">
296
+ <div class="tnp-card-title"><?php _e('Developers', 'newsletter') ?></div>
297
+ <div class="tnp-card-description">Extending Newsletter by yourself? There is something for you as well!</div>
298
+ <div class="tnp-card-button-container">
299
+ <a href="https://www.thenewsletterplugin.com/documentation/developers/" target="_blank">Developer's love 💛</a>
300
+ </div>
301
+ </div>
302
+ <div class="tnp-card">
303
+ <div class="tnp-card-title"><?php _e('Video Tutorials', 'newsletter') ?></div>
304
+ <div class="tnp-card-description">We have some videos to help gest the most from Newsletter.</div>
305
+ <div class="tnp-card-video">
306
+ <iframe width="560" height="315" src="https://www.youtube.com/embed/zmVmW84Bw9A" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
307
+ </div>
308
+ <div class="tnp-card-button-container">
309
+ <a href="https://www.thenewsletterplugin.com/video-tutorials" target="_blank">See the videos</a>
310
+ </div>
311
+ </div>
312
+ </div>
313
+
314
+ </div>
315
+
316
+ <?php include NEWSLETTER_DIR . '/tnp-footer.php'; ?>
317
+
318
+ </div>
main/js/main.js DELETED
@@ -1,178 +0,0 @@
1
-
2
- /*
3
- convert a cubic bezier value to a custom mina easing
4
- http://stackoverflow.com/questions/25265197/how-to-convert-a-cubic-bezier-value-to-a-custom-mina-easing-snap-svg
5
- */
6
- var duration = 300,
7
- delay = 300,
8
- epsilon = (1000 / 60 / duration) / 4,
9
- firstCustomMinaAnimation = bezier(.42, .03, .77, .63, epsilon),
10
- secondCustomMinaAnimation = bezier(.27, .5, .6, .99, epsilon);
11
-
12
- jQuery(document).ready(function () {
13
- //initialize the slider
14
- jQuery('.cd-slider-wrapper').each(function () {
15
- initSlider(jQuery(this));
16
- });
17
- });
18
-
19
- function initSlider(sliderWrapper) {
20
- //cache jQuery objects
21
- slider = sliderWrapper.find('.cd-slider'),
22
- sliderNavigation = sliderWrapper.find('.cd-slider-navigation').find('li'),
23
- svgCoverLayer = sliderWrapper.find('div.cd-svg-cover'),
24
- pathId = svgCoverLayer.find('path').attr('id'),
25
- svgPath = Snap('#' + pathId);
26
-
27
- //store path 'd' attribute values
28
- pathArray = [];
29
- pathArray[0] = svgCoverLayer.data('step1');
30
- pathArray[1] = svgCoverLayer.data('step6');
31
- pathArray[2] = svgCoverLayer.data('step2');
32
- pathArray[3] = svgCoverLayer.data('step7');
33
- pathArray[4] = svgCoverLayer.data('step3');
34
- pathArray[5] = svgCoverLayer.data('step8');
35
- pathArray[6] = svgCoverLayer.data('step4');
36
- pathArray[7] = svgCoverLayer.data('step9');
37
- pathArray[8] = svgCoverLayer.data('step5');
38
- pathArray[9] = svgCoverLayer.data('step10');
39
-
40
- //update visible slide when user clicks .cd-slider-navigation buttons
41
- sliderNavigation.on('click', function (event) {
42
- event.preventDefault();
43
- var selectedItem = jQuery(this);
44
- if (!selectedItem.hasClass('selected')) {
45
- // if it's not already selected
46
- var selectedSlidePosition = selectedItem.index(),
47
- selectedSlide = slider.children('li').eq(selectedSlidePosition),
48
- visibleSlide = slider.find('.visible'),
49
- visibleSlidePosition = visibleSlide.index(),
50
- direction = '';
51
- direction = (visibleSlidePosition < selectedSlidePosition) ? 'next' : 'prev';
52
- updateSlide(visibleSlide, selectedSlide, direction, svgCoverLayer, sliderNavigation, pathArray, svgPath);
53
- }
54
- });
55
- }
56
-
57
- function nextSlide() {
58
- var visibleSlide = slider.find('.visible');
59
- jQuery.post("?page=newsletter_main_welcome&noheader=1&action=save", jQuery("#tnp-welcome").serialize());
60
- var visibleSlidePosition = visibleSlide.index();
61
- var selectedSlide = slider.children('li').eq(visibleSlidePosition + 1);
62
- if (selectedSlide.hasClass("tnp-last-slide")) {
63
- jQuery(".cd-slider-navigation").hide();
64
- }
65
-
66
- updateSlide(visibleSlide, selectedSlide, "next", svgCoverLayer, sliderNavigation, pathArray, svgPath);
67
-
68
- }
69
-
70
- function prevSlide() {
71
- var visibleSlide = slider.find('.visible');
72
- var visibleSlidePosition = visibleSlide.index();
73
- var selectedSlide = slider.children('li').eq(visibleSlidePosition - 1);
74
-
75
- updateSlide(visibleSlide, selectedSlide, "prev", svgCoverLayer, sliderNavigation, pathArray, svgPath);
76
- if (selectedSlide.hasClass("tnp-first-slide")) {
77
- jQuery(".cd-slider-navigation a.tnp-welcome-prev").hide();
78
- }
79
- }
80
-
81
- function updateSlide(oldSlide, newSlide, direction, svgCoverLayer, sliderNavigation, paths, svgPath) {
82
- if (direction == 'next') {
83
- var path1 = paths[0],
84
- path2 = paths[2],
85
- path3 = paths[4];
86
- path4 = paths[6];
87
- path5 = paths[8];
88
- } else {
89
- var path1 = paths[1],
90
- path2 = paths[3],
91
- path3 = paths[5];
92
- path4 = paths[7];
93
- path5 = paths[9];
94
- }
95
-
96
- svgCoverLayer.addClass('is-animating');
97
- svgPath.attr('d', path1);
98
- svgPath.animate({'d': path2}, duration, firstCustomMinaAnimation, function () {
99
- svgPath.animate({'d': path3}, duration, secondCustomMinaAnimation, function () {
100
- oldSlide.removeClass('visible');
101
- newSlide.addClass('visible');
102
- updateNavSlide(newSlide, sliderNavigation);
103
- setTimeout(function () {
104
- svgPath.animate({'d': path4}, duration, firstCustomMinaAnimation, function () {
105
- svgPath.animate({'d': path5}, duration, secondCustomMinaAnimation, function () {
106
- svgCoverLayer.removeClass('is-animating');
107
- if (direction == "next") {
108
- jQuery(".cd-slider-navigation a.tnp-welcome-prev").show();
109
- }
110
- });
111
- });
112
- }, delay);
113
- });
114
- });
115
- }
116
-
117
- function updateNavSlide(actualSlide, sliderNavigation) {
118
- var position = actualSlide.index();
119
- sliderNavigation.removeClass('selected').eq(position).addClass('selected');
120
- }
121
-
122
- function bezier(x1, y1, x2, y2, epsilon) {
123
- //https://github.com/arian/cubic-bezier
124
- var curveX = function (t) {
125
- var v = 1 - t;
126
- return 3 * v * v * t * x1 + 3 * v * t * t * x2 + t * t * t;
127
- };
128
-
129
- var curveY = function (t) {
130
- var v = 1 - t;
131
- return 3 * v * v * t * y1 + 3 * v * t * t * y2 + t * t * t;
132
- };
133
-
134
- var derivativeCurveX = function (t) {
135
- var v = 1 - t;
136
- return 3 * (2 * (t - 1) * t + v * v) * x1 + 3 * (-t * t * t + 2 * v * t) * x2;
137
- };
138
-
139
- return function (t) {
140
-
141
- var x = t, t0, t1, t2, x2, d2, i;
142
-
143
- // First try a few iterations of Newton's method -- normally very fast.
144
- for (t2 = x, i = 0; i < 8; i++) {
145
- x2 = curveX(t2) - x;
146
- if (Math.abs(x2) < epsilon)
147
- return curveY(t2);
148
- d2 = derivativeCurveX(t2);
149
- if (Math.abs(d2) < 1e-6)
150
- break;
151
- t2 = t2 - x2 / d2;
152
- }
153
-
154
- t0 = 0, t1 = 1, t2 = x;
155
-
156
- if (t2 < t0)
157
- return curveY(t0);
158
- if (t2 > t1)
159
- return curveY(t1);
160
-
161
- // Fallback to the bisection method for reliability.
162
- while (t0 < t1) {
163
- x2 = curveX(t2);
164
- if (Math.abs(x2 - x) < epsilon)
165
- return curveY(t2);
166
- if (x > x2)
167
- t0 = t2;
168
- else
169
- t1 = t2;
170
- t2 = (t1 - t0) * .5 + t0;
171
- }
172
-
173
- // Failure
174
- return curveY(t2);
175
-
176
- };
177
- }
178
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
main/js/{snap.svg-min.js → welcome.js} RENAMED
@@ -1,21 +1,200 @@
1
- // Snap.svg 0.4.1
2
- //
3
- // Copyright (c) 2013 – 2015 Adobe Systems Incorporated. All rights reserved.
4
- //
5
- // Licensed under the Apache License, Version 2.0 (the "License");
6
- // you may not use this file except in compliance with the License.
7
- // You may obtain a copy of the License at
8
- //
9
- // http://www.apache.org/licenses/LICENSE-2.0
10
- //
11
- // Unless required by applicable law or agreed to in writing, software
12
- // distributed under the License is distributed on an "AS IS" BASIS,
13
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
- // See the License for the specific language governing permissions and
15
- // limitations under the License.
16
- //
17
- // build: 2015-04-13
18
-
19
- !function(a){var b,c,d="0.4.2",e="hasOwnProperty",f=/[\.\/]/,g=/\s*,\s*/,h="*",i=function(a,b){return a-b},j={n:{}},k=function(){for(var a=0,b=this.length;b>a;a++)if("undefined"!=typeof this[a])return this[a]},l=function(){for(var a=this.length;--a;)if("undefined"!=typeof this[a])return this[a]},m=function(a,d){a=String(a);var e,f=c,g=Array.prototype.slice.call(arguments,2),h=m.listeners(a),j=0,n=[],o={},p=[],q=b;p.firstDefined=k,p.lastDefined=l,b=a,c=0;for(var r=0,s=h.length;s>r;r++)"zIndex"in h[r]&&(n.push(h[r].zIndex),h[r].zIndex<0&&(o[h[r].zIndex]=h[r]));for(n.sort(i);n[j]<0;)if(e=o[n[j++]],p.push(e.apply(d,g)),c)return c=f,p;for(r=0;s>r;r++)if(e=h[r],"zIndex"in e)if(e.zIndex==n[j]){if(p.push(e.apply(d,g)),c)break;do if(j++,e=o[n[j]],e&&p.push(e.apply(d,g)),c)break;while(e)}else o[e.zIndex]=e;else if(p.push(e.apply(d,g)),c)break;return c=f,b=q,p};m._events=j,m.listeners=function(a){var b,c,d,e,g,i,k,l,m=a.split(f),n=j,o=[n],p=[];for(e=0,g=m.length;g>e;e++){for(l=[],i=0,k=o.length;k>i;i++)for(n=o[i].n,c=[n[m[e]],n[h]],d=2;d--;)b=c[d],b&&(l.push(b),p=p.concat(b.f||[]));o=l}return p},m.on=function(a,b){if(a=String(a),"function"!=typeof b)return function(){};for(var c=a.split(g),d=0,e=c.length;e>d;d++)!function(a){for(var c,d=a.split(f),e=j,g=0,h=d.length;h>g;g++)e=e.n,e=e.hasOwnProperty(d[g])&&e[d[g]]||(e[d[g]]={n:{}});for(e.f=e.f||[],g=0,h=e.f.length;h>g;g++)if(e.f[g]==b){c=!0;break}!c&&e.f.push(b)}(c[d]);return function(a){+a==+a&&(b.zIndex=+a)}},m.f=function(a){var b=[].slice.call(arguments,1);return function(){m.apply(null,[a,null].concat(b).concat([].slice.call(arguments,0)))}},m.stop=function(){c=1},m.nt=function(a){return a?new RegExp("(?:\\.|\\/|^)"+a+"(?:\\.|\\/|$)").test(b):b},m.nts=function(){return b.split(f)},m.off=m.unbind=function(a,b){if(!a)return void(m._events=j={n:{}});var c=a.split(g);if(c.length>1)for(var d=0,i=c.length;i>d;d++)m.off(c[d],b);else{c=a.split(f);var k,l,n,d,i,o,p,q=[j];for(d=0,i=c.length;i>d;d++)for(o=0;o<q.length;o+=n.length-2){if(n=[o,1],k=q[o].n,c[d]!=h)k[c[d]]&&n.push(k[c[d]]);else for(l in k)k[e](l)&&n.push(k[l]);q.splice.apply(q,n)}for(d=0,i=q.length;i>d;d++)for(k=q[d];k.n;){if(b){if(k.f){for(o=0,p=k.f.length;p>o;o++)if(k.f[o]==b){k.f.splice(o,1);break}!k.f.length&&delete k.f}for(l in k.n)if(k.n[e](l)&&k.n[l].f){var r=k.n[l].f;for(o=0,p=r.length;p>o;o++)if(r[o]==b){r.splice(o,1);break}!r.length&&delete k.n[l].f}}else{delete k.f;for(l in k.n)k.n[e](l)&&k.n[l].f&&delete k.n[l].f}k=k.n}}},m.once=function(a,b){var c=function(){return m.unbind(a,c),b.apply(this,arguments)};return m.on(a,c)},m.version=d,m.toString=function(){return"You are running Eve "+d},"undefined"!=typeof module&&module.exports?module.exports=m:"function"==typeof define&&define.amd?define("eve",[],function(){return m}):a.eve=m}(this),function(a,b){if("function"==typeof define&&define.amd)define(["eve"],function(c){return b(a,c)});else if("undefined"!=typeof exports){var c=require("eve");module.exports=b(a,c)}else b(a,a.eve)}(window||this,function(a,b){var c=function(b){var c={},d=a.requestAnimationFrame||a.webkitRequestAnimationFrame||a.mozRequestAnimationFrame||a.oRequestAnimationFrame||a.msRequestAnimationFrame||function(a){setTimeout(a,16)},e=Array.isArray||function(a){return a instanceof Array||"[object Array]"==Object.prototype.toString.call(a)},f=0,g="M"+(+new Date).toString(36),h=function(){return g+(f++).toString(36)},i=Date.now||function(){return+new Date},j=function(a){var b=this;if(null==a)return b.s;var c=b.s-a;b.b+=b.dur*c,b.B+=b.dur*c,b.s=a},k=function(a){var b=this;return null==a?b.spd:void(b.spd=a)},l=function(a){var b=this;return null==a?b.dur:(b.s=b.s*a/b.dur,void(b.dur=a))},m=function(){var a=this;delete c[a.id],a.update(),b("mina.stop."+a.id,a)},n=function(){var a=this;a.pdif||(delete c[a.id],a.update(),a.pdif=a.get()-a.b)},o=function(){var a=this;a.pdif&&(a.b=a.get()-a.pdif,delete a.pdif,c[a.id]=a)},p=function(){var a,b=this;if(e(b.start)){a=[];for(var c=0,d=b.start.length;d>c;c++)a[c]=+b.start[c]+(b.end[c]-b.start[c])*b.easing(b.s)}else a=+b.start+(b.end-b.start)*b.easing(b.s);b.set(a)},q=function(){var a=0;for(var e in c)if(c.hasOwnProperty(e)){var f=c[e],g=f.get();a++,f.s=(g-f.b)/(f.dur/f.spd),f.s>=1&&(delete c[e],f.s=1,a--,function(a){setTimeout(function(){b("mina.finish."+a.id,a)})}(f)),f.update()}a&&d(q)},r=function(a,b,e,f,g,i,s){var t={id:h(),start:a,end:b,b:e,s:0,dur:f-e,spd:1,get:g,set:i,easing:s||r.linear,status:j,speed:k,duration:l,stop:m,pause:n,resume:o,update:p};c[t.id]=t;var u,v=0;for(u in c)if(c.hasOwnProperty(u)&&(v++,2==v))break;return 1==v&&d(q),t};return r.time=i,r.getById=function(a){return c[a]||null},r.linear=function(a){return a},r.easeout=function(a){return Math.pow(a,1.7)},r.easein=function(a){return Math.pow(a,.48)},r.easeinout=function(a){if(1==a)return 1;if(0==a)return 0;var b=.48-a/1.04,c=Math.sqrt(.1734+b*b),d=c-b,e=Math.pow(Math.abs(d),1/3)*(0>d?-1:1),f=-c-b,g=Math.pow(Math.abs(f),1/3)*(0>f?-1:1),h=e+g+.5;return 3*(1-h)*h*h+h*h*h},r.backin=function(a){if(1==a)return 1;var b=1.70158;return a*a*((b+1)*a-b)},r.backout=function(a){if(0==a)return 0;a-=1;var b=1.70158;return a*a*((b+1)*a+b)+1},r.elastic=function(a){return a==!!a?a:Math.pow(2,-10*a)*Math.sin(2*(a-.075)*Math.PI/.3)+1},r.bounce=function(a){var b,c=7.5625,d=2.75;return 1/d>a?b=c*a*a:2/d>a?(a-=1.5/d,b=c*a*a+.75):2.5/d>a?(a-=2.25/d,b=c*a*a+.9375):(a-=2.625/d,b=c*a*a+.984375),b},a.mina=r,r}("undefined"==typeof b?function(){}:b),d=function(a){function c(a,b){if(a){if(a.nodeType)return w(a);if(e(a,"array")&&c.set)return c.set.apply(c,a);if(a instanceof s)return a;if(null==b)return a=y.doc.querySelector(String(a)),w(a)}return a=null==a?"100%":a,b=null==b?"100%":b,new v(a,b)}function d(a,b){if(b){if("#text"==a&&(a=y.doc.createTextNode(b.text||b["#text"]||"")),"#comment"==a&&(a=y.doc.createComment(b.text||b["#text"]||"")),"string"==typeof a&&(a=d(a)),"string"==typeof b)return 1==a.nodeType?"xlink:"==b.substring(0,6)?a.getAttributeNS(T,b.substring(6)):"xml:"==b.substring(0,4)?a.getAttributeNS(U,b.substring(4)):a.getAttribute(b):"text"==b?a.nodeValue:null;if(1==a.nodeType){for(var c in b)if(b[z](c)){var e=A(b[c]);e?"xlink:"==c.substring(0,6)?a.setAttributeNS(T,c.substring(6),e):"xml:"==c.substring(0,4)?a.setAttributeNS(U,c.substring(4),e):a.setAttribute(c,e):a.removeAttribute(c)}}else"text"in b&&(a.nodeValue=b.text)}else a=y.doc.createElementNS(U,a);return a}function e(a,b){return b=A.prototype.toLowerCase.call(b),"finite"==b?isFinite(a):"array"==b&&(a instanceof Array||Array.isArray&&Array.isArray(a))?!0:"null"==b&&null===a||b==typeof a&&null!==a||"object"==b&&a===Object(a)||J.call(a).slice(8,-1).toLowerCase()==b}function f(a){if("function"==typeof a||Object(a)!==a)return a;var b=new a.constructor;for(var c in a)a[z](c)&&(b[c]=f(a[c]));return b}function h(a,b){for(var c=0,d=a.length;d>c;c++)if(a[c]===b)return a.push(a.splice(c,1)[0])}function i(a,b,c){function d(){var e=Array.prototype.slice.call(arguments,0),f=e.join("␀"),g=d.cache=d.cache||{},i=d.count=d.count||[];return g[z](f)?(h(i,f),c?c(g[f]):g[f]):(i.length>=1e3&&delete g[i.shift()],i.push(f),g[f]=a.apply(b,e),c?c(g[f]):g[f])}return d}function j(a,b,c,d,e,f){if(null==e){var g=a-c,h=b-d;return g||h?(180+180*D.atan2(-h,-g)/H+360)%360:0}return j(a,b,e,f)-j(c,d,e,f)}function k(a){return a%360*H/180}function l(a){return 180*a/H%360}function m(a){var b=[];return a=a.replace(/(?:^|\s)(\w+)\(([^)]+)\)/g,function(a,c,d){return d=d.split(/\s*,\s*|\s+/),"rotate"==c&&1==d.length&&d.push(0,0),"scale"==c&&(d.length>2?d=d.slice(0,2):2==d.length&&d.push(0,0),1==d.length&&d.push(d[0],0,0)),b.push("skewX"==c?["m",1,0,D.tan(k(d[0])),1,0,0]:"skewY"==c?["m",1,D.tan(k(d[0])),0,1,0,0]:[c.charAt(0)].concat(d)),a}),b}function n(a,b){var d=ab(a),e=new c.Matrix;if(d)for(var f=0,g=d.length;g>f;f++){var h,i,j,k,l,m=d[f],n=m.length,o=A(m[0]).toLowerCase(),p=m[0]!=o,q=p?e.invert():0;"t"==o&&2==n?e.translate(m[1],0):"t"==o&&3==n?p?(h=q.x(0,0),i=q.y(0,0),j=q.x(m[1],m[2]),k=q.y(m[1],m[2]),e.translate(j-h,k-i)):e.translate(m[1],m[2]):"r"==o?2==n?(l=l||b,e.rotate(m[1],l.x+l.width/2,l.y+l.height/2)):4==n&&(p?(j=q.x(m[2],m[3]),k=q.y(m[2],m[3]),e.rotate(m[1],j,k)):e.rotate(m[1],m[2],m[3])):"s"==o?2==n||3==n?(l=l||b,e.scale(m[1],m[n-1],l.x+l.width/2,l.y+l.height/2)):4==n?p?(j=q.x(m[2],m[3]),k=q.y(m[2],m[3]),e.scale(m[1],m[1],j,k)):e.scale(m[1],m[1],m[2],m[3]):5==n&&(p?(j=q.x(m[3],m[4]),k=q.y(m[3],m[4]),e.scale(m[1],m[2],j,k)):e.scale(m[1],m[2],m[3],m[4])):"m"==o&&7==n&&e.add(m[1],m[2],m[3],m[4],m[5],m[6])}return e}function o(a){var b=a.node.ownerSVGElement&&w(a.node.ownerSVGElement)||a.node.parentNode&&w(a.node.parentNode)||c.select("svg")||c(0,0),d=b.select("defs"),e=null==d?!1:d.node;return e||(e=u("defs",b.node).node),e}function p(a){return a.node.ownerSVGElement&&w(a.node.ownerSVGElement)||c.select("svg")}function q(a,b,c){function e(a){if(null==a)return I;if(a==+a)return a;d(j,{width:a});try{return j.getBBox().width}catch(b){return 0}}function f(a){if(null==a)return I;if(a==+a)return a;d(j,{height:a});try{return j.getBBox().height}catch(b){return 0}}function g(d,e){null==b?i[d]=e(a.attr(d)||0):d==b&&(i=e(null==c?a.attr(d)||0:c))}var h=p(a).node,i={},j=h.querySelector(".svg---mgr");switch(j||(j=d("rect"),d(j,{x:-9e9,y:-9e9,width:10,height:10,"class":"svg---mgr",fill:"none"}),h.appendChild(j)),a.type){case"rect":g("rx",e),g("ry",f);case"image":g("width",e),g("height",f);case"text":g("x",e),g("y",f);break;case"circle":g("cx",e),g("cy",f),g("r",e);break;case"ellipse":g("cx",e),g("cy",f),g("rx",e),g("ry",f);break;case"line":g("x1",e),g("x2",e),g("y1",f),g("y2",f);break;case"marker":g("refX",e),g("markerWidth",e),g("refY",f),g("markerHeight",f);break;case"radialGradient":g("fx",e),g("fy",f);break;case"tspan":g("dx",e),g("dy",f);break;default:g(b,e)}return h.removeChild(j),i}function r(a){e(a,"array")||(a=Array.prototype.slice.call(arguments,0));for(var b=0,c=0,d=this.node;this[b];)delete this[b++];for(b=0;b<a.length;b++)"set"==a[b].type?a[b].forEach(function(a){d.appendChild(a.node)}):d.appendChild(a[b].node);var f=d.childNodes;for(b=0;b<f.length;b++)this[c++]=w(f[b]);return this}function s(a){if(a.snap in V)return V[a.snap];var b;try{b=a.ownerSVGElement}catch(c){}this.node=a,b&&(this.paper=new v(b)),this.type=a.tagName||a.nodeName;var d=this.id=S(this);if(this.anims={},this._={transform:[]},a.snap=d,V[d]=this,"g"==this.type&&(this.add=r),this.type in{g:1,mask:1,pattern:1,symbol:1})for(var e in v.prototype)v.prototype[z](e)&&(this[e]=v.prototype[e])}function t(a){this.node=a}function u(a,b){var c=d(a);b.appendChild(c);var e=w(c);return e}function v(a,b){var c,e,f,g=v.prototype;if(a&&"svg"==a.tagName){if(a.snap in V)return V[a.snap];var h=a.ownerDocument;c=new s(a),e=a.getElementsByTagName("desc")[0],f=a.getElementsByTagName("defs")[0],e||(e=d("desc"),e.appendChild(h.createTextNode("Created with Snap")),c.node.appendChild(e)),f||(f=d("defs"),c.node.appendChild(f)),c.defs=f;for(var i in g)g[z](i)&&(c[i]=g[i]);c.paper=c.root=c}else c=u("svg",y.doc.body),d(c.node,{height:b,version:1.1,width:a,xmlns:U});return c}function w(a){return a?a instanceof s||a instanceof t?a:a.tagName&&"svg"==a.tagName.toLowerCase()?new v(a):a.tagName&&"object"==a.tagName.toLowerCase()&&"image/svg+xml"==a.type?new v(a.contentDocument.getElementsByTagName("svg")[0]):new s(a):a}function x(a,b){for(var c=0,d=a.length;d>c;c++){var e={type:a[c].type,attr:a[c].attr()},f=a[c].children();b.push(e),f.length&&x(f,e.childNodes=[])}}c.version="0.4.0",c.toString=function(){return"Snap v"+this.version},c._={};var y={win:a.window,doc:a.window.document};c._.glob=y;{var z="hasOwnProperty",A=String,B=parseFloat,C=parseInt,D=Math,E=D.max,F=D.min,G=D.abs,H=(D.pow,D.PI),I=(D.round,""),J=Object.prototype.toString,K=/^\s*((#[a-f\d]{6})|(#[a-f\d]{3})|rgba?\(\s*([\d\.]+%?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+%?(?:\s*,\s*[\d\.]+%?)?)\s*\)|hsba?\(\s*([\d\.]+(?:deg|\xb0|%)?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+(?:%?\s*,\s*[\d\.]+)?%?)\s*\)|hsla?\(\s*([\d\.]+(?:deg|\xb0|%)?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+(?:%?\s*,\s*[\d\.]+)?%?)\s*\))\s*$/i,L=(c._.separator=/[,\s]+/,/[\s]*,[\s]*/),M={hs:1,rg:1},N=/([a-z])[\s,]*((-?\d*\.?\d*(?:e[\-+]?\d+)?[\s]*,?[\s]*)+)/gi,O=/([rstm])[\s,]*((-?\d*\.?\d*(?:e[\-+]?\d+)?[\s]*,?[\s]*)+)/gi,P=/(-?\d*\.?\d*(?:e[\-+]?\\d+)?)[\s]*,?[\s]*/gi,Q=0,R="S"+(+new Date).toString(36),S=function(a){return(a&&a.type?a.type:I)+R+(Q++).toString(36)},T="http://www.w3.org/1999/xlink",U="http://www.w3.org/2000/svg",V={};c.url=function(a){return"url('#"+a+"')"}}c._.$=d,c._.id=S,c.format=function(){var a=/\{([^\}]+)\}/g,b=/(?:(?:^|\.)(.+?)(?=\[|\.|$|\()|\[('|")(.+?)\2\])(\(\))?/g,c=function(a,c,d){var e=d;return c.replace(b,function(a,b,c,d,f){b=b||d,e&&(b in e&&(e=e[b]),"function"==typeof e&&f&&(e=e()))}),e=(null==e||e==d?a:e)+""};return function(b,d){return A(b).replace(a,function(a,b){return c(a,b,d)})}}(),c._.clone=f,c._.cacher=i,c.rad=k,c.deg=l,c.sin=function(a){return D.sin(c.rad(a))},c.tan=function(a){return D.tan(c.rad(a))},c.cos=function(a){return D.cos(c.rad(a))},c.asin=function(a){return c.deg(D.asin(a))},c.acos=function(a){return c.deg(D.acos(a))},c.atan=function(a){return c.deg(D.atan(a))},c.atan2=function(a){return c.deg(D.atan2(a))},c.angle=j,c.len=function(a,b,d,e){return Math.sqrt(c.len2(a,b,d,e))},c.len2=function(a,b,c,d){return(a-c)*(a-c)+(b-d)*(b-d)},c.closestPoint=function(a,b,c){function d(a){var d=a.x-b,e=a.y-c;return d*d+e*e}for(var e,f,g,h,i=a.node,j=i.getTotalLength(),k=j/i.pathSegList.numberOfItems*.125,l=1/0,m=0;j>=m;m+=k)(h=d(g=i.getPointAtLength(m)))<l&&(e=g,f=m,l=h);for(k*=.5;k>.5;){var n,o,p,q,r,s;(p=f-k)>=0&&(r=d(n=i.getPointAtLength(p)))<l?(e=n,f=p,l=r):(q=f+k)<=j&&(s=d(o=i.getPointAtLength(q)))<l?(e=o,f=q,l=s):k*=.5}return e={x:e.x,y:e.y,length:f,distance:Math.sqrt(l)}},c.is=e,c.snapTo=function(a,b,c){if(c=e(c,"finite")?c:10,e(a,"array")){for(var d=a.length;d--;)if(G(a[d]-b)<=c)return a[d]}else{a=+a;var f=b%a;if(c>f)return b-f;if(f>a-c)return b-f+a}return b},c.getRGB=i(function(a){if(!a||(a=A(a)).indexOf("-")+1)return{r:-1,g:-1,b:-1,hex:"none",error:1,toString:Z};if("none"==a)return{r:-1,g:-1,b:-1,hex:"none",toString:Z};if(!(M[z](a.toLowerCase().substring(0,2))||"#"==a.charAt())&&(a=W(a)),!a)return{r:-1,g:-1,b:-1,hex:"none",error:1,toString:Z};var b,d,f,g,h,i,j=a.match(K);return j?(j[2]&&(f=C(j[2].substring(5),16),d=C(j[2].substring(3,5),16),b=C(j[2].substring(1,3),16)),j[3]&&(f=C((h=j[3].charAt(3))+h,16),d=C((h=j[3].charAt(2))+h,16),b=C((h=j[3].charAt(1))+h,16)),j[4]&&(i=j[4].split(L),b=B(i[0]),"%"==i[0].slice(-1)&&(b*=2.55),d=B(i[1]),"%"==i[1].slice(-1)&&(d*=2.55),f=B(i[2]),"%"==i[2].slice(-1)&&(f*=2.55),"rgba"==j[1].toLowerCase().slice(0,4)&&(g=B(i[3])),i[3]&&"%"==i[3].slice(-1)&&(g/=100)),j[5]?(i=j[5].split(L),b=B(i[0]),"%"==i[0].slice(-1)&&(b/=100),d=B(i[1]),"%"==i[1].slice(-1)&&(d/=100),f=B(i[2]),"%"==i[2].slice(-1)&&(f/=100),("deg"==i[0].slice(-3)||"°"==i[0].slice(-1))&&(b/=360),"hsba"==j[1].toLowerCase().slice(0,4)&&(g=B(i[3])),i[3]&&"%"==i[3].slice(-1)&&(g/=100),c.hsb2rgb(b,d,f,g)):j[6]?(i=j[6].split(L),b=B(i[0]),"%"==i[0].slice(-1)&&(b/=100),d=B(i[1]),"%"==i[1].slice(-1)&&(d/=100),f=B(i[2]),"%"==i[2].slice(-1)&&(f/=100),("deg"==i[0].slice(-3)||"°"==i[0].slice(-1))&&(b/=360),"hsla"==j[1].toLowerCase().slice(0,4)&&(g=B(i[3])),i[3]&&"%"==i[3].slice(-1)&&(g/=100),c.hsl2rgb(b,d,f,g)):(b=F(D.round(b),255),d=F(D.round(d),255),f=F(D.round(f),255),g=F(E(g,0),1),j={r:b,g:d,b:f,toString:Z},j.hex="#"+(16777216|f|d<<8|b<<16).toString(16).slice(1),j.opacity=e(g,"finite")?g:1,j)):{r:-1,g:-1,b:-1,hex:"none",error:1,toString:Z}},c),c.hsb=i(function(a,b,d){return c.hsb2rgb(a,b,d).hex}),c.hsl=i(function(a,b,d){return c.hsl2rgb(a,b,d).hex}),c.rgb=i(function(a,b,c,d){if(e(d,"finite")){var f=D.round;return"rgba("+[f(a),f(b),f(c),+d.toFixed(2)]+")"}return"#"+(16777216|c|b<<8|a<<16).toString(16).slice(1)});var W=function(a){var b=y.doc.getElementsByTagName("head")[0]||y.doc.getElementsByTagName("svg")[0],c="rgb(255, 0, 0)";return(W=i(function(a){if("red"==a.toLowerCase())return c;b.style.color=c,b.style.color=a;var d=y.doc.defaultView.getComputedStyle(b,I).getPropertyValue("color");return d==c?null:d}))(a)},X=function(){return"hsb("+[this.h,this.s,this.b]+")"},Y=function(){return"hsl("+[this.h,this.s,this.l]+")"},Z=function(){return 1==this.opacity||null==this.opacity?this.hex:"rgba("+[this.r,this.g,this.b,this.opacity]+")"},$=function(a,b,d){if(null==b&&e(a,"object")&&"r"in a&&"g"in a&&"b"in a&&(d=a.b,b=a.g,a=a.r),null==b&&e(a,string)){var f=c.getRGB(a);a=f.r,b=f.g,d=f.b}return(a>1||b>1||d>1)&&(a/=255,b/=255,d/=255),[a,b,d]},_=function(a,b,d,f){a=D.round(255*a),b=D.round(255*b),d=D.round(255*d);var g={r:a,g:b,b:d,opacity:e(f,"finite")?f:1,hex:c.rgb(a,b,d),toString:Z};return e(f,"finite")&&(g.opacity=f),g};c.color=function(a){var b;return e(a,"object")&&"h"in a&&"s"in a&&"b"in a?(b=c.hsb2rgb(a),a.r=b.r,a.g=b.g,a.b=b.b,a.opacity=1,a.hex=b.hex):e(a,"object")&&"h"in a&&"s"in a&&"l"in a?(b=c.hsl2rgb(a),a.r=b.r,a.g=b.g,a.b=b.b,a.opacity=1,a.hex=b.hex):(e(a,"string")&&(a=c.getRGB(a)),e(a,"object")&&"r"in a&&"g"in a&&"b"in a&&!("error"in a)?(b=c.rgb2hsl(a),a.h=b.h,a.s=b.s,a.l=b.l,b=c.rgb2hsb(a),a.v=b.b):(a={hex:"none"},a.r=a.g=a.b=a.h=a.s=a.v=a.l=-1,a.error=1)),a.toString=Z,a},c.hsb2rgb=function(a,b,c,d){e(a,"object")&&"h"in a&&"s"in a&&"b"in a&&(c=a.b,b=a.s,d=a.o,a=a.h),a*=360;var f,g,h,i,j;return a=a%360/60,j=c*b,i=j*(1-G(a%2-1)),f=g=h=c-j,a=~~a,f+=[j,i,0,0,i,j][a],g+=[i,j,j,i,0,0][a],h+=[0,0,i,j,j,i][a],_(f,g,h,d)},c.hsl2rgb=function(a,b,c,d){e(a,"object")&&"h"in a&&"s"in a&&"l"in a&&(c=a.l,b=a.s,a=a.h),(a>1||b>1||c>1)&&(a/=360,b/=100,c/=100),a*=360;var f,g,h,i,j;return a=a%360/60,j=2*b*(.5>c?c:1-c),i=j*(1-G(a%2-1)),f=g=h=c-j/2,a=~~a,f+=[j,i,0,0,i,j][a],g+=[i,j,j,i,0,0][a],h+=[0,0,i,j,j,i][a],_(f,g,h,d)},c.rgb2hsb=function(a,b,c){c=$(a,b,c),a=c[0],b=c[1],c=c[2];var d,e,f,g;return f=E(a,b,c),g=f-F(a,b,c),d=0==g?null:f==a?(b-c)/g:f==b?(c-a)/g+2:(a-b)/g+4,d=(d+360)%6*60/360,e=0==g?0:g/f,{h:d,s:e,b:f,toString:X}},c.rgb2hsl=function(a,b,c){c=$(a,b,c),a=c[0],b=c[1],c=c[2];var d,e,f,g,h,i;return g=E(a,b,c),h=F(a,b,c),i=g-h,d=0==i?null:g==a?(b-c)/i:g==b?(c-a)/i+2:(a-b)/i+4,d=(d+360)%6*60/360,f=(g+h)/2,e=0==i?0:.5>f?i/(2*f):i/(2-2*f),{h:d,s:e,l:f,toString:Y}},c.parsePathString=function(a){if(!a)return null;var b=c.path(a);if(b.arr)return c.path.clone(b.arr);var d={a:7,c:6,o:2,h:1,l:2,m:2,r:4,q:4,s:4,t:2,v:1,u:3,z:0},f=[];return e(a,"array")&&e(a[0],"array")&&(f=c.path.clone(a)),f.length||A(a).replace(N,function(a,b,c){var e=[],g=b.toLowerCase();if(c.replace(P,function(a,b){b&&e.push(+b)}),"m"==g&&e.length>2&&(f.push([b].concat(e.splice(0,2))),g="l",b="m"==b?"l":"L"),"o"==g&&1==e.length&&f.push([b,e[0]]),"r"==g)f.push([b].concat(e));else for(;e.length>=d[g]&&(f.push([b].concat(e.splice(0,d[g]))),d[g]););}),f.toString=c.path.toString,b.arr=c.path.clone(f),f};var ab=c.parseTransformString=function(a){if(!a)return null;var b=[];return e(a,"array")&&e(a[0],"array")&&(b=c.path.clone(a)),b.length||A(a).replace(O,function(a,c,d){{var e=[];c.toLowerCase()}d.replace(P,function(a,b){b&&e.push(+b)}),b.push([c].concat(e))}),b.toString=c.path.toString,b};c._.svgTransform2string=m,c._.rgTransform=/^[a-z][\s]*-?\.?\d/i,c._.transform2matrix=n,c._unit2px=q;y.doc.contains||y.doc.compareDocumentPosition?function(a,b){var c=9==a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a==d||!(!d||1!=d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)for(;b;)if(b=b.parentNode,b==a)return!0;return!1};c._.getSomeDefs=o,c._.getSomeSVG=p,c.select=function(a){return a=A(a).replace(/([^\\]):/g,"$1\\:"),w(y.doc.querySelector(a))},c.selectAll=function(a){for(var b=y.doc.querySelectorAll(a),d=(c.set||Array)(),e=0;e<b.length;e++)d.push(w(b[e]));return d},setInterval(function(){for(var a in V)if(V[z](a)){var b=V[a],c=b.node;("svg"!=b.type&&!c.ownerSVGElement||"svg"==b.type&&(!c.parentNode||"ownerSVGElement"in c.parentNode&&!c.ownerSVGElement))&&delete V[a]}},1e4),s.prototype.attr=function(a,c){var d=this,f=d.node;if(!a){if(1!=f.nodeType)return{text:f.nodeValue};for(var g=f.attributes,h={},i=0,j=g.length;j>i;i++)h[g[i].nodeName]=g[i].nodeValue;return h}if(e(a,"string")){if(!(arguments.length>1))return b("snap.util.getattr."+a,d).firstDefined();var k={};k[a]=c,a=k}for(var l in a)a[z](l)&&b("snap.util.attr."+l,d,a[l]);return d},c.parse=function(a){var b=y.doc.createDocumentFragment(),c=!0,d=y.doc.createElement("div");if(a=A(a),a.match(/^\s*<\s*svg(?:\s|>)/)||(a="<svg>"+a+"</svg>",c=!1),d.innerHTML=a,a=d.getElementsByTagName("svg")[0])if(c)b=a;else for(;a.firstChild;)b.appendChild(a.firstChild);return new t(b)},c.fragment=function(){for(var a=Array.prototype.slice.call(arguments,0),b=y.doc.createDocumentFragment(),d=0,e=a.length;e>d;d++){var f=a[d];f.node&&f.node.nodeType&&b.appendChild(f.node),f.nodeType&&b.appendChild(f),"string"==typeof f&&b.appendChild(c.parse(f).node)}return new t(b)},c._.make=u,c._.wrap=w,v.prototype.el=function(a,b){var c=u(a,this.node);return b&&c.attr(b),c},s.prototype.children=function(){for(var a=[],b=this.node.childNodes,d=0,e=b.length;e>d;d++)a[d]=c(b[d]);return a},s.prototype.toJSON=function(){var a=[];return x([this],a),a[0]},b.on("snap.util.getattr",function(){var a=b.nt();a=a.substring(a.lastIndexOf(".")+1);var c=a.replace(/[A-Z]/g,function(a){return"-"+a.toLowerCase()});return bb[z](c)?this.node.ownerDocument.defaultView.getComputedStyle(this.node,null).getPropertyValue(c):d(this.node,a)});var bb={"alignment-baseline":0,"baseline-shift":0,clip:0,"clip-path":0,"clip-rule":0,color:0,"color-interpolation":0,"color-interpolation-filters":0,"color-profile":0,"color-rendering":0,cursor:0,direction:0,display:0,"dominant-baseline":0,"enable-background":0,fill:0,"fill-opacity":0,"fill-rule":0,filter:0,"flood-color":0,"flood-opacity":0,font:0,"font-family":0,"font-size":0,"font-size-adjust":0,"font-stretch":0,"font-style":0,"font-variant":0,"font-weight":0,"glyph-orientation-horizontal":0,"glyph-orientation-vertical":0,"image-rendering":0,kerning:0,"letter-spacing":0,"lighting-color":0,marker:0,"marker-end":0,"marker-mid":0,"marker-start":0,mask:0,opacity:0,overflow:0,"pointer-events":0,"shape-rendering":0,"stop-color":0,"stop-opacity":0,stroke:0,"stroke-dasharray":0,"stroke-dashoffset":0,"stroke-linecap":0,"stroke-linejoin":0,"stroke-miterlimit":0,"stroke-opacity":0,"stroke-width":0,"text-anchor":0,"text-decoration":0,"text-rendering":0,"unicode-bidi":0,visibility:0,"word-spacing":0,"writing-mode":0};b.on("snap.util.attr",function(a){var c=b.nt(),e={};c=c.substring(c.lastIndexOf(".")+1),e[c]=a;var f=c.replace(/-(\w)/gi,function(a,b){return b.toUpperCase()}),g=c.replace(/[A-Z]/g,function(a){return"-"+a.toLowerCase()});bb[z](g)?this.node.style[f]=null==a?I:a:d(this.node,e)}),function(){}(v.prototype),c.ajax=function(a,c,d,f){var g=new XMLHttpRequest,h=S();if(g){if(e(c,"function"))f=d,d=c,c=null;else if(e(c,"object")){var i=[];for(var j in c)c.hasOwnProperty(j)&&i.push(encodeURIComponent(j)+"="+encodeURIComponent(c[j]));c=i.join("&")}return g.open(c?"POST":"GET",a,!0),c&&(g.setRequestHeader("X-Requested-With","XMLHttpRequest"),g.setRequestHeader("Content-type","application/x-www-form-urlencoded")),d&&(b.once("snap.ajax."+h+".0",d),b.once("snap.ajax."+h+".200",d),b.once("snap.ajax."+h+".304",d)),g.onreadystatechange=function(){4==g.readyState&&b("snap.ajax."+h+"."+g.status,f,g)},4==g.readyState?g:(g.send(c),g)}},c.load=function(a,b,d){c.ajax(a,function(a){var e=c.parse(a.responseText);d?b.call(d,e):b(e)})};var cb=function(a){var b=a.getBoundingClientRect(),c=a.ownerDocument,d=c.body,e=c.documentElement,f=e.clientTop||d.clientTop||0,h=e.clientLeft||d.clientLeft||0,i=b.top+(g.win.pageYOffset||e.scrollTop||d.scrollTop)-f,j=b.left+(g.win.pageXOffset||e.scrollLeft||d.scrollLeft)-h;return{y:i,x:j}};return c.getElementByPoint=function(a,b){var c=this,d=(c.canvas,y.doc.elementFromPoint(a,b));if(y.win.opera&&"svg"==d.tagName){var e=cb(d),f=d.createSVGRect();f.x=a-e.x,f.y=b-e.y,f.width=f.height=1;var g=d.getIntersectionList(f,null);g.length&&(d=g[g.length-1])}return d?w(d):null},c.plugin=function(a){a(c,s,v,y,t)},y.win.Snap=c,c}(a||this);return d.plugin(function(d,e,f,g,h){function i(a,b){if(null==b){var c=!0;if(b=a.node.getAttribute("linearGradient"==a.type||"radialGradient"==a.type?"gradientTransform":"pattern"==a.type?"patternTransform":"transform"),!b)return new d.Matrix;b=d._.svgTransform2string(b)}else b=d._.rgTransform.test(b)?o(b).replace(/\.{3}|\u2026/g,a._.transform||""):d._.svgTransform2string(b),n(b,"array")&&(b=d.path?d.path.toString.call(b):o(b)),a._.transform=b;var e=d._.transform2matrix(b,a.getBBox(1));return c?e:void(a.matrix=e)}function j(a){function b(a,b){var c=q(a.node,b);c=c&&c.match(f),c=c&&c[2],c&&"#"==c.charAt()&&(c=c.substring(1),c&&(h[c]=(h[c]||[]).concat(function(c){var d={};d[b]=URL(c),q(a.node,d)})))}function c(a){var b=q(a.node,"xlink:href");b&&"#"==b.charAt()&&(b=b.substring(1),b&&(h[b]=(h[b]||[]).concat(function(b){a.attr("xlink:href","#"+b)})))}for(var d,e=a.selectAll("*"),f=/^\s*url\(("|'|)(.*)\1\)\s*$/,g=[],h={},i=0,j=e.length;j>i;i++){d=e[i],b(d,"fill"),b(d,"stroke"),b(d,"filter"),b(d,"mask"),b(d,"clip-path"),c(d);var k=q(d.node,"id");k&&(q(d.node,{id:d.id}),g.push({old:k,id:d.id}))}for(i=0,j=g.length;j>i;i++){var l=h[g[i].old];if(l)for(var m=0,n=l.length;n>m;m++)l[m](g[i].id)}}function k(a,b,c){return function(d){var e=d.slice(a,b);return 1==e.length&&(e=e[0]),c?c(e):e}}function l(a){return function(){var b=a?"<"+this.type:"",c=this.node.attributes,d=this.node.childNodes;if(a)for(var e=0,f=c.length;f>e;e++)b+=" "+c[e].name+'="'+c[e].value.replace(/"/g,'\\"')+'"';if(d.length){for(a&&(b+=">"),e=0,f=d.length;f>e;e++)3==d[e].nodeType?b+=d[e].nodeValue:1==d[e].nodeType&&(b+=u(d[e]).toString());a&&(b+="</"+this.type+">")}else a&&(b+="/>");return b}}var m=e.prototype,n=d.is,o=String,p=d._unit2px,q=d._.$,r=d._.make,s=d._.getSomeDefs,t="hasOwnProperty",u=d._.wrap;m.getBBox=function(a){if(!d.Matrix||!d.path)return this.node.getBBox();var b=this,c=new d.Matrix;if(b.removed)return d._.box();for(;"use"==b.type;)if(a||(c=c.add(b.transform().localMatrix.translate(b.attr("x")||0,b.attr("y")||0))),b.original)b=b.original;else{var e=b.attr("xlink:href");b=b.original=b.node.ownerDocument.getElementById(e.substring(e.indexOf("#")+1))}var f=b._,g=d.path.get[b.type]||d.path.get.deflt;try{return a?(f.bboxwt=g?d.path.getBBox(b.realPath=g(b)):d._.box(b.node.getBBox()),d._.box(f.bboxwt)):(b.realPath=g(b),b.matrix=b.transform().localMatrix,f.bbox=d.path.getBBox(d.path.map(b.realPath,c.add(b.matrix))),d._.box(f.bbox))}catch(h){return d._.box()}};var v=function(){return this.string};m.transform=function(a){var b=this._;if(null==a){for(var c,e=this,f=new d.Matrix(this.node.getCTM()),g=i(this),h=[g],j=new d.Matrix,k=g.toTransformString(),l=o(g)==o(this.matrix)?o(b.transform):k;"svg"!=e.type&&(e=e.parent());)h.push(i(e));for(c=h.length;c--;)j.add(h[c]);return{string:l,globalMatrix:f,totalMatrix:j,localMatrix:g,diffMatrix:f.clone().add(g.invert()),global:f.toTransformString(),total:j.toTransformString(),local:k,toString:v}}return a instanceof d.Matrix?(this.matrix=a,this._.transform=a.toTransformString()):i(this,a),this.node&&("linearGradient"==this.type||"radialGradient"==this.type?q(this.node,{gradientTransform:this.matrix}):"pattern"==this.type?q(this.node,{patternTransform:this.matrix}):q(this.node,{transform:this.matrix})),this},m.parent=function(){return u(this.node.parentNode)},m.append=m.add=function(a){if(a){if("set"==a.type){var b=this;return a.forEach(function(a){b.add(a)}),this}a=u(a),this.node.appendChild(a.node),a.paper=this.paper}return this},m.appendTo=function(a){return a&&(a=u(a),a.append(this)),this},m.prepend=function(a){if(a){if("set"==a.type){var b,c=this;return a.forEach(function(a){b?b.after(a):c.prepend(a),b=a}),this}a=u(a);var d=a.parent();this.node.insertBefore(a.node,this.node.firstChild),this.add&&this.add(),a.paper=this.paper,this.parent()&&this.parent().add(),d&&d.add()}return this},m.prependTo=function(a){return a=u(a),a.prepend(this),this},m.before=function(a){if("set"==a.type){var b=this;return a.forEach(function(a){var c=a.parent();b.node.parentNode.insertBefore(a.node,b.node),c&&c.add()}),this.parent().add(),this}a=u(a);var c=a.parent();return this.node.parentNode.insertBefore(a.node,this.node),this.parent()&&this.parent().add(),c&&c.add(),a.paper=this.paper,this},m.after=function(a){a=u(a);var b=a.parent();return this.node.nextSibling?this.node.parentNode.insertBefore(a.node,this.node.nextSibling):this.node.parentNode.appendChild(a.node),this.parent()&&this.parent().add(),b&&b.add(),a.paper=this.paper,this},m.insertBefore=function(a){a=u(a);var b=this.parent();return a.node.parentNode.insertBefore(this.node,a.node),this.paper=a.paper,b&&b.add(),a.parent()&&a.parent().add(),this},m.insertAfter=function(a){a=u(a);var b=this.parent();return a.node.parentNode.insertBefore(this.node,a.node.nextSibling),this.paper=a.paper,b&&b.add(),a.parent()&&a.parent().add(),this},m.remove=function(){var a=this.parent();return this.node.parentNode&&this.node.parentNode.removeChild(this.node),delete this.paper,this.removed=!0,a&&a.add(),this},m.select=function(a){return u(this.node.querySelector(a))},m.selectAll=function(a){for(var b=this.node.querySelectorAll(a),c=(d.set||Array)(),e=0;e<b.length;e++)c.push(u(b[e]));return c},m.asPX=function(a,b){return null==b&&(b=this.attr(a)),+p(this,a,b)},m.use=function(){var a,b=this.node.id;return b||(b=this.id,q(this.node,{id:b})),a="linearGradient"==this.type||"radialGradient"==this.type||"pattern"==this.type?r(this.type,this.node.parentNode):r("use",this.node.parentNode),q(a.node,{"xlink:href":"#"+b}),a.original=this,a},m.clone=function(){var a=u(this.node.cloneNode(!0));return q(a.node,"id")&&q(a.node,{id:a.id}),j(a),a.insertAfter(this),a},m.toDefs=function(){var a=s(this);return a.appendChild(this.node),this},m.pattern=m.toPattern=function(a,b,c,d){var e=r("pattern",s(this));return null==a&&(a=this.getBBox()),n(a,"object")&&"x"in a&&(b=a.y,c=a.width,d=a.height,a=a.x),q(e.node,{x:a,y:b,width:c,height:d,patternUnits:"userSpaceOnUse",id:e.id,viewBox:[a,b,c,d].join(" ")}),e.node.appendChild(this.node),e},m.marker=function(a,b,c,d,e,f){var g=r("marker",s(this));return null==a&&(a=this.getBBox()),n(a,"object")&&"x"in a&&(b=a.y,c=a.width,d=a.height,e=a.refX||a.cx,f=a.refY||a.cy,a=a.x),q(g.node,{viewBox:[a,b,c,d].join(" "),markerWidth:c,markerHeight:d,orient:"auto",refX:e||0,refY:f||0,id:g.id}),g.node.appendChild(this.node),g};var w=function(a,b,d,e){"function"!=typeof d||d.length||(e=d,d=c.linear),this.attr=a,this.dur=b,d&&(this.easing=d),e&&(this.callback=e)};d._.Animation=w,d.animation=function(a,b,c,d){return new w(a,b,c,d)},m.inAnim=function(){var a=this,b=[];for(var c in a.anims)a.anims[t](c)&&!function(a){b.push({anim:new w(a._attrs,a.dur,a.easing,a._callback),mina:a,curStatus:a.status(),status:function(b){return a.status(b)},stop:function(){a.stop()}})}(a.anims[c]);return b},d.animate=function(a,d,e,f,g,h){"function"!=typeof g||g.length||(h=g,g=c.linear);var i=c.time(),j=c(a,d,i,i+f,c.time,e,g);return h&&b.once("mina.finish."+j.id,h),j},m.stop=function(){for(var a=this.inAnim(),b=0,c=a.length;c>b;b++)a[b].stop();return this},m.animate=function(a,d,e,f){"function"!=typeof e||e.length||(f=e,e=c.linear),a instanceof w&&(f=a.callback,e=a.easing,d=a.dur,a=a.attr);var g,h,i,j,l=[],m=[],p={},q=this;for(var r in a)if(a[t](r)){q.equal?(j=q.equal(r,o(a[r])),g=j.from,h=j.to,i=j.f):(g=+q.attr(r),h=+a[r]);var s=n(g,"array")?g.length:1;p[r]=k(l.length,l.length+s,i),l=l.concat(g),m=m.concat(h)}var u=c.time(),v=c(l,m,u,u+d,c.time,function(a){var b={};for(var c in p)p[t](c)&&(b[c]=p[c](a));q.attr(b)},e);return q.anims[v.id]=v,v._attrs=a,v._callback=f,b("snap.animcreated."+q.id,v),b.once("mina.finish."+v.id,function(){delete q.anims[v.id],f&&f.call(q)}),b.once("mina.stop."+v.id,function(){delete q.anims[v.id]}),q};var x={};m.data=function(a,c){var e=x[this.id]=x[this.id]||{};if(0==arguments.length)return b("snap.data.get."+this.id,this,e,null),e;
20
- if(1==arguments.length){if(d.is(a,"object")){for(var f in a)a[t](f)&&this.data(f,a[f]);return this}return b("snap.data.get."+this.id,this,e[a],a),e[a]}return e[a]=c,b("snap.data.set."+this.id,this,c,a),this},m.removeData=function(a){return null==a?x[this.id]={}:x[this.id]&&delete x[this.id][a],this},m.outerSVG=m.toString=l(1),m.innerSVG=l(),m.toDataURL=function(){if(a&&a.btoa){var b=this.getBBox(),c=d.format('<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="{width}" height="{height}" viewBox="{x} {y} {width} {height}">{contents}</svg>',{x:+b.x.toFixed(3),y:+b.y.toFixed(3),width:+b.width.toFixed(3),height:+b.height.toFixed(3),contents:this.outerSVG()});return"data:image/svg+xml;base64,"+btoa(unescape(encodeURIComponent(c)))}},h.prototype.select=m.select,h.prototype.selectAll=m.selectAll}),d.plugin(function(a){function b(a,b,d,e,f,g){return null==b&&"[object SVGMatrix]"==c.call(a)?(this.a=a.a,this.b=a.b,this.c=a.c,this.d=a.d,this.e=a.e,void(this.f=a.f)):void(null!=a?(this.a=+a,this.b=+b,this.c=+d,this.d=+e,this.e=+f,this.f=+g):(this.a=1,this.b=0,this.c=0,this.d=1,this.e=0,this.f=0))}var c=Object.prototype.toString,d=String,e=Math,f="";!function(c){function g(a){return a[0]*a[0]+a[1]*a[1]}function h(a){var b=e.sqrt(g(a));a[0]&&(a[0]/=b),a[1]&&(a[1]/=b)}c.add=function(a,c,d,e,f,g){var h,i,j,k,l=[[],[],[]],m=[[this.a,this.c,this.e],[this.b,this.d,this.f],[0,0,1]],n=[[a,d,f],[c,e,g],[0,0,1]];for(a&&a instanceof b&&(n=[[a.a,a.c,a.e],[a.b,a.d,a.f],[0,0,1]]),h=0;3>h;h++)for(i=0;3>i;i++){for(k=0,j=0;3>j;j++)k+=m[h][j]*n[j][i];l[h][i]=k}return this.a=l[0][0],this.b=l[1][0],this.c=l[0][1],this.d=l[1][1],this.e=l[0][2],this.f=l[1][2],this},c.invert=function(){var a=this,c=a.a*a.d-a.b*a.c;return new b(a.d/c,-a.b/c,-a.c/c,a.a/c,(a.c*a.f-a.d*a.e)/c,(a.b*a.e-a.a*a.f)/c)},c.clone=function(){return new b(this.a,this.b,this.c,this.d,this.e,this.f)},c.translate=function(a,b){return this.add(1,0,0,1,a,b)},c.scale=function(a,b,c,d){return null==b&&(b=a),(c||d)&&this.add(1,0,0,1,c,d),this.add(a,0,0,b,0,0),(c||d)&&this.add(1,0,0,1,-c,-d),this},c.rotate=function(b,c,d){b=a.rad(b),c=c||0,d=d||0;var f=+e.cos(b).toFixed(9),g=+e.sin(b).toFixed(9);return this.add(f,g,-g,f,c,d),this.add(1,0,0,1,-c,-d)},c.x=function(a,b){return a*this.a+b*this.c+this.e},c.y=function(a,b){return a*this.b+b*this.d+this.f},c.get=function(a){return+this[d.fromCharCode(97+a)].toFixed(4)},c.toString=function(){return"matrix("+[this.get(0),this.get(1),this.get(2),this.get(3),this.get(4),this.get(5)].join()+")"},c.offset=function(){return[this.e.toFixed(4),this.f.toFixed(4)]},c.determinant=function(){return this.a*this.d-this.b*this.c},c.split=function(){var b={};b.dx=this.e,b.dy=this.f;var c=[[this.a,this.c],[this.b,this.d]];b.scalex=e.sqrt(g(c[0])),h(c[0]),b.shear=c[0][0]*c[1][0]+c[0][1]*c[1][1],c[1]=[c[1][0]-c[0][0]*b.shear,c[1][1]-c[0][1]*b.shear],b.scaley=e.sqrt(g(c[1])),h(c[1]),b.shear/=b.scaley,this.determinant()<0&&(b.scalex=-b.scalex);var d=-c[0][1],f=c[1][1];return 0>f?(b.rotate=a.deg(e.acos(f)),0>d&&(b.rotate=360-b.rotate)):b.rotate=a.deg(e.asin(d)),b.isSimple=!(+b.shear.toFixed(9)||b.scalex.toFixed(9)!=b.scaley.toFixed(9)&&b.rotate),b.isSuperSimple=!+b.shear.toFixed(9)&&b.scalex.toFixed(9)==b.scaley.toFixed(9)&&!b.rotate,b.noRotation=!+b.shear.toFixed(9)&&!b.rotate,b},c.toTransformString=function(a){var b=a||this.split();return+b.shear.toFixed(9)?"m"+[this.get(0),this.get(1),this.get(2),this.get(3),this.get(4),this.get(5)]:(b.scalex=+b.scalex.toFixed(4),b.scaley=+b.scaley.toFixed(4),b.rotate=+b.rotate.toFixed(4),(b.dx||b.dy?"t"+[+b.dx.toFixed(4),+b.dy.toFixed(4)]:f)+(1!=b.scalex||1!=b.scaley?"s"+[b.scalex,b.scaley,0,0]:f)+(b.rotate?"r"+[+b.rotate.toFixed(4),0,0]:f))}}(b.prototype),a.Matrix=b,a.matrix=function(a,c,d,e,f,g){return new b(a,c,d,e,f,g)}}),d.plugin(function(a,c,d,e,f){function g(d){return function(e){if(b.stop(),e instanceof f&&1==e.node.childNodes.length&&("radialGradient"==e.node.firstChild.tagName||"linearGradient"==e.node.firstChild.tagName||"pattern"==e.node.firstChild.tagName)&&(e=e.node.firstChild,n(this).appendChild(e),e=l(e)),e instanceof c)if("radialGradient"==e.type||"linearGradient"==e.type||"pattern"==e.type){e.node.id||p(e.node,{id:e.id});var g=q(e.node.id)}else g=e.attr(d);else if(g=a.color(e),g.error){var h=a(n(this).ownerSVGElement).gradient(e);h?(h.node.id||p(h.node,{id:h.id}),g=q(h.node.id)):g=e}else g=r(g);var i={};i[d]=g,p(this.node,i),this.node.style[d]=t}}function h(a){b.stop(),a==+a&&(a+="px"),this.node.style.fontSize=a}function i(a){for(var b=[],c=a.childNodes,d=0,e=c.length;e>d;d++){var f=c[d];3==f.nodeType&&b.push(f.nodeValue),"tspan"==f.tagName&&b.push(1==f.childNodes.length&&3==f.firstChild.nodeType?f.firstChild.nodeValue:i(f))}return b}function j(){return b.stop(),this.node.style.fontSize}var k=a._.make,l=a._.wrap,m=a.is,n=a._.getSomeDefs,o=/^url\(#?([^)]+)\)$/,p=a._.$,q=a.url,r=String,s=a._.separator,t="";b.on("snap.util.attr.mask",function(a){if(a instanceof c||a instanceof f){if(b.stop(),a instanceof f&&1==a.node.childNodes.length&&(a=a.node.firstChild,n(this).appendChild(a),a=l(a)),"mask"==a.type)var d=a;else d=k("mask",n(this)),d.node.appendChild(a.node);!d.node.id&&p(d.node,{id:d.id}),p(this.node,{mask:q(d.id)})}}),function(a){b.on("snap.util.attr.clip",a),b.on("snap.util.attr.clip-path",a),b.on("snap.util.attr.clipPath",a)}(function(a){if(a instanceof c||a instanceof f){if(b.stop(),"clipPath"==a.type)var d=a;else d=k("clipPath",n(this)),d.node.appendChild(a.node),!d.node.id&&p(d.node,{id:d.id});p(this.node,{"clip-path":q(d.node.id||d.id)})}}),b.on("snap.util.attr.fill",g("fill")),b.on("snap.util.attr.stroke",g("stroke"));var u=/^([lr])(?:\(([^)]*)\))?(.*)$/i;b.on("snap.util.grad.parse",function(a){a=r(a);var b=a.match(u);if(!b)return null;var c=b[1],d=b[2],e=b[3];return d=d.split(/\s*,\s*/).map(function(a){return+a==a?+a:a}),1==d.length&&0==d[0]&&(d=[]),e=e.split("-"),e=e.map(function(a){a=a.split(":");var b={color:a[0]};return a[1]&&(b.offset=parseFloat(a[1])),b}),{type:c,params:d,stops:e}}),b.on("snap.util.attr.d",function(c){b.stop(),m(c,"array")&&m(c[0],"array")&&(c=a.path.toString.call(c)),c=r(c),c.match(/[ruo]/i)&&(c=a.path.toAbsolute(c)),p(this.node,{d:c})})(-1),b.on("snap.util.attr.#text",function(a){b.stop(),a=r(a);for(var c=e.doc.createTextNode(a);this.node.firstChild;)this.node.removeChild(this.node.firstChild);this.node.appendChild(c)})(-1),b.on("snap.util.attr.path",function(a){b.stop(),this.attr({d:a})})(-1),b.on("snap.util.attr.class",function(a){b.stop(),this.node.className.baseVal=a})(-1),b.on("snap.util.attr.viewBox",function(a){var c;c=m(a,"object")&&"x"in a?[a.x,a.y,a.width,a.height].join(" "):m(a,"array")?a.join(" "):a,p(this.node,{viewBox:c}),b.stop()})(-1),b.on("snap.util.attr.transform",function(a){this.transform(a),b.stop()})(-1),b.on("snap.util.attr.r",function(a){"rect"==this.type&&(b.stop(),p(this.node,{rx:a,ry:a}))})(-1),b.on("snap.util.attr.textpath",function(a){if(b.stop(),"text"==this.type){var d,e,f;if(!a&&this.textPath){for(e=this.textPath;e.node.firstChild;)this.node.appendChild(e.node.firstChild);return e.remove(),void delete this.textPath}if(m(a,"string")){var g=n(this),h=l(g.parentNode).path(a);g.appendChild(h.node),d=h.id,h.attr({id:d})}else a=l(a),a instanceof c&&(d=a.attr("id"),d||(d=a.id,a.attr({id:d})));if(d)if(e=this.textPath,f=this.node,e)e.attr({"xlink:href":"#"+d});else{for(e=p("textPath",{"xlink:href":"#"+d});f.firstChild;)e.appendChild(f.firstChild);f.appendChild(e),this.textPath=l(e)}}})(-1),b.on("snap.util.attr.text",function(a){if("text"==this.type){for(var c=this.node,d=function(a){var b=p("tspan");if(m(a,"array"))for(var c=0;c<a.length;c++)b.appendChild(d(a[c]));else b.appendChild(e.doc.createTextNode(a));return b.normalize&&b.normalize(),b};c.firstChild;)c.removeChild(c.firstChild);for(var f=d(a);f.firstChild;)c.appendChild(f.firstChild)}b.stop()})(-1),b.on("snap.util.attr.fontSize",h)(-1),b.on("snap.util.attr.font-size",h)(-1),b.on("snap.util.getattr.transform",function(){return b.stop(),this.transform()})(-1),b.on("snap.util.getattr.textpath",function(){return b.stop(),this.textPath})(-1),function(){function c(c){return function(){b.stop();var d=e.doc.defaultView.getComputedStyle(this.node,null).getPropertyValue("marker-"+c);return"none"==d?d:a(e.doc.getElementById(d.match(o)[1]))}}function d(a){return function(c){b.stop();var d="marker"+a.charAt(0).toUpperCase()+a.substring(1);if(""==c||!c)return void(this.node.style[d]="none");if("marker"==c.type){var e=c.node.id;return e||p(c.node,{id:c.id}),void(this.node.style[d]=q(e))}}}b.on("snap.util.getattr.marker-end",c("end"))(-1),b.on("snap.util.getattr.markerEnd",c("end"))(-1),b.on("snap.util.getattr.marker-start",c("start"))(-1),b.on("snap.util.getattr.markerStart",c("start"))(-1),b.on("snap.util.getattr.marker-mid",c("mid"))(-1),b.on("snap.util.getattr.markerMid",c("mid"))(-1),b.on("snap.util.attr.marker-end",d("end"))(-1),b.on("snap.util.attr.markerEnd",d("end"))(-1),b.on("snap.util.attr.marker-start",d("start"))(-1),b.on("snap.util.attr.markerStart",d("start"))(-1),b.on("snap.util.attr.marker-mid",d("mid"))(-1),b.on("snap.util.attr.markerMid",d("mid"))(-1)}(),b.on("snap.util.getattr.r",function(){return"rect"==this.type&&p(this.node,"rx")==p(this.node,"ry")?(b.stop(),p(this.node,"rx")):void 0})(-1),b.on("snap.util.getattr.text",function(){if("text"==this.type||"tspan"==this.type){b.stop();var a=i(this.node);return 1==a.length?a[0]:a}})(-1),b.on("snap.util.getattr.#text",function(){return this.node.textContent})(-1),b.on("snap.util.getattr.viewBox",function(){b.stop();var c=p(this.node,"viewBox");return c?(c=c.split(s),a._.box(+c[0],+c[1],+c[2],+c[3])):void 0})(-1),b.on("snap.util.getattr.points",function(){var a=p(this.node,"points");return b.stop(),a?a.split(s):void 0})(-1),b.on("snap.util.getattr.path",function(){var a=p(this.node,"d");return b.stop(),a})(-1),b.on("snap.util.getattr.class",function(){return this.node.className.baseVal})(-1),b.on("snap.util.getattr.fontSize",j)(-1),b.on("snap.util.getattr.font-size",j)(-1)}),d.plugin(function(a,b){var c=/\S+/g,d=String,e=b.prototype;e.addClass=function(a){var b,e,f,g,h=d(a||"").match(c)||[],i=this.node,j=i.className.baseVal,k=j.match(c)||[];if(h.length){for(b=0;f=h[b++];)e=k.indexOf(f),~e||k.push(f);g=k.join(" "),j!=g&&(i.className.baseVal=g)}return this},e.removeClass=function(a){var b,e,f,g,h=d(a||"").match(c)||[],i=this.node,j=i.className.baseVal,k=j.match(c)||[];if(k.length){for(b=0;f=h[b++];)e=k.indexOf(f),~e&&k.splice(e,1);g=k.join(" "),j!=g&&(i.className.baseVal=g)}return this},e.hasClass=function(a){var b=this.node,d=b.className.baseVal,e=d.match(c)||[];return!!~e.indexOf(a)},e.toggleClass=function(a,b){if(null!=b)return b?this.addClass(a):this.removeClass(a);var d,e,f,g,h=(a||"").match(c)||[],i=this.node,j=i.className.baseVal,k=j.match(c)||[];for(d=0;f=h[d++];)e=k.indexOf(f),~e?k.splice(e,1):k.push(f);return g=k.join(" "),j!=g&&(i.className.baseVal=g),this}}),d.plugin(function(){function a(a){return a}function c(a){return function(b){return+b.toFixed(3)+a}}var d={"+":function(a,b){return a+b},"-":function(a,b){return a-b},"/":function(a,b){return a/b},"*":function(a,b){return a*b}},e=String,f=/[a-z]+$/i,g=/^\s*([+\-\/*])\s*=\s*([\d.eE+\-]+)\s*([^\d\s]+)?\s*$/;b.on("snap.util.attr",function(a){var c=e(a).match(g);if(c){var h=b.nt(),i=h.substring(h.lastIndexOf(".")+1),j=this.attr(i),k={};b.stop();var l=c[3]||"",m=j.match(f),n=d[c[1]];if(m&&m==l?a=n(parseFloat(j),+c[2]):(j=this.asPX(i),a=n(this.asPX(i),this.asPX(i,c[2]+l))),isNaN(j)||isNaN(a))return;k[i]=a,this.attr(k)}})(-10),b.on("snap.util.equal",function(h,i){var j=e(this.attr(h)||""),k=e(i).match(g);if(k){b.stop();var l=k[3]||"",m=j.match(f),n=d[k[1]];return m&&m==l?{from:parseFloat(j),to:n(parseFloat(j),+k[2]),f:c(m)}:(j=this.asPX(h),{from:j,to:n(j,this.asPX(h,k[2]+l)),f:a})}})(-10)}),d.plugin(function(c,d,e,f){var g=e.prototype,h=c.is;g.rect=function(a,b,c,d,e,f){var g;return null==f&&(f=e),h(a,"object")&&"[object Object]"==a?g=a:null!=a&&(g={x:a,y:b,width:c,height:d},null!=e&&(g.rx=e,g.ry=f)),this.el("rect",g)},g.circle=function(a,b,c){var d;return h(a,"object")&&"[object Object]"==a?d=a:null!=a&&(d={cx:a,cy:b,r:c}),this.el("circle",d)};var i=function(){function a(){this.parentNode.removeChild(this)}return function(b,c){var d=f.doc.createElement("img"),e=f.doc.body;d.style.cssText="position:absolute;left:-9999em;top:-9999em",d.onload=function(){c.call(d),d.onload=d.onerror=null,e.removeChild(d)},d.onerror=a,e.appendChild(d),d.src=b}}();g.image=function(a,b,d,e,f){var g=this.el("image");if(h(a,"object")&&"src"in a)g.attr(a);else if(null!=a){var j={"xlink:href":a,preserveAspectRatio:"none"};null!=b&&null!=d&&(j.x=b,j.y=d),null!=e&&null!=f?(j.width=e,j.height=f):i(a,function(){c._.$(g.node,{width:this.offsetWidth,height:this.offsetHeight})}),c._.$(g.node,j)}return g},g.ellipse=function(a,b,c,d){var e;return h(a,"object")&&"[object Object]"==a?e=a:null!=a&&(e={cx:a,cy:b,rx:c,ry:d}),this.el("ellipse",e)},g.path=function(a){var b;return h(a,"object")&&!h(a,"array")?b=a:a&&(b={d:a}),this.el("path",b)},g.group=g.g=function(a){var b=this.el("g");return 1==arguments.length&&a&&!a.type?b.attr(a):arguments.length&&b.add(Array.prototype.slice.call(arguments,0)),b},g.svg=function(a,b,c,d,e,f,g,i){var j={};return h(a,"object")&&null==b?j=a:(null!=a&&(j.x=a),null!=b&&(j.y=b),null!=c&&(j.width=c),null!=d&&(j.height=d),null!=e&&null!=f&&null!=g&&null!=i&&(j.viewBox=[e,f,g,i])),this.el("svg",j)},g.mask=function(a){var b=this.el("mask");return 1==arguments.length&&a&&!a.type?b.attr(a):arguments.length&&b.add(Array.prototype.slice.call(arguments,0)),b},g.ptrn=function(a,b,c,d,e,f,g,i){if(h(a,"object"))var j=a;else j={patternUnits:"userSpaceOnUse"},a&&(j.x=a),b&&(j.y=b),null!=c&&(j.width=c),null!=d&&(j.height=d),j.viewBox=null!=e&&null!=f&&null!=g&&null!=i?[e,f,g,i]:[a||0,b||0,c||0,d||0];return this.el("pattern",j)},g.use=function(a){return null!=a?(a instanceof d&&(a.attr("id")||a.attr({id:c._.id(a)}),a=a.attr("id")),"#"==String(a).charAt()&&(a=a.substring(1)),this.el("use",{"xlink:href":"#"+a})):d.prototype.use.call(this)},g.symbol=function(a,b,c,d){var e={};return null!=a&&null!=b&&null!=c&&null!=d&&(e.viewBox=[a,b,c,d]),this.el("symbol",e)},g.text=function(a,b,c){var d={};return h(a,"object")?d=a:null!=a&&(d={x:a,y:b,text:c||""}),this.el("text",d)},g.line=function(a,b,c,d){var e={};return h(a,"object")?e=a:null!=a&&(e={x1:a,x2:c,y1:b,y2:d}),this.el("line",e)},g.polyline=function(a){arguments.length>1&&(a=Array.prototype.slice.call(arguments,0));var b={};return h(a,"object")&&!h(a,"array")?b=a:null!=a&&(b={points:a}),this.el("polyline",b)},g.polygon=function(a){arguments.length>1&&(a=Array.prototype.slice.call(arguments,0));var b={};return h(a,"object")&&!h(a,"array")?b=a:null!=a&&(b={points:a}),this.el("polygon",b)},function(){function d(){return this.selectAll("stop")}function e(a,b){var d=k("stop"),e={offset:+b+"%"};return a=c.color(a),e["stop-color"]=a.hex,a.opacity<1&&(e["stop-opacity"]=a.opacity),k(d,e),this.node.appendChild(d),this}function f(){if("linearGradient"==this.type){var a=k(this.node,"x1")||0,b=k(this.node,"x2")||1,d=k(this.node,"y1")||0,e=k(this.node,"y2")||0;return c._.box(a,d,math.abs(b-a),math.abs(e-d))}var f=this.node.cx||.5,g=this.node.cy||.5,h=this.node.r||0;return c._.box(f-h,g-h,2*h,2*h)}function h(a,c){function d(a,b){for(var c=(b-l)/(a-m),d=m;a>d;d++)g[d].offset=+(+l+c*(d-m)).toFixed(2);m=a,l=b}var e,f=b("snap.util.grad.parse",null,c).firstDefined();if(!f)return null;f.params.unshift(a),e="l"==f.type.toLowerCase()?i.apply(0,f.params):j.apply(0,f.params),f.type!=f.type.toLowerCase()&&k(e.node,{gradientUnits:"userSpaceOnUse"});var g=f.stops,h=g.length,l=0,m=0;h--;for(var n=0;h>n;n++)"offset"in g[n]&&d(n,g[n].offset);for(g[h].offset=g[h].offset||100,d(h,g[h].offset),n=0;h>=n;n++){var o=g[n];e.addStop(o.color,o.offset)}return e}function i(a,b,g,h,i){var j=c._.make("linearGradient",a);return j.stops=d,j.addStop=e,j.getBBox=f,null!=b&&k(j.node,{x1:b,y1:g,x2:h,y2:i}),j}function j(a,b,g,h,i,j){var l=c._.make("radialGradient",a);return l.stops=d,l.addStop=e,l.getBBox=f,null!=b&&k(l.node,{cx:b,cy:g,r:h}),null!=i&&null!=j&&k(l.node,{fx:i,fy:j}),l}var k=c._.$;g.gradient=function(a){return h(this.defs,a)},g.gradientLinear=function(a,b,c,d){return i(this.defs,a,b,c,d)},g.gradientRadial=function(a,b,c,d,e){return j(this.defs,a,b,c,d,e)},g.toString=function(){var a,b=this.node.ownerDocument,d=b.createDocumentFragment(),e=b.createElement("div"),f=this.node.cloneNode(!0);return d.appendChild(e),e.appendChild(f),c._.$(f,{xmlns:"http://www.w3.org/2000/svg"}),a=e.innerHTML,d.removeChild(d.firstChild),a},g.toDataURL=function(){return a&&a.btoa?"data:image/svg+xml;base64,"+btoa(unescape(encodeURIComponent(this))):void 0},g.clear=function(){for(var a,b=this.node.firstChild;b;)a=b.nextSibling,"defs"!=b.tagName?b.parentNode.removeChild(b):g.clear.call({node:b}),b=a}}()}),d.plugin(function(a,b){function c(a){var b=c.ps=c.ps||{};return b[a]?b[a].sleep=100:b[a]={sleep:100},setTimeout(function(){for(var c in b)b[K](c)&&c!=a&&(b[c].sleep--,!b[c].sleep&&delete b[c])}),b[a]}function d(a,b,c,d){return null==a&&(a=b=c=d=0),null==b&&(b=a.y,c=a.width,d=a.height,a=a.x),{x:a,y:b,width:c,w:c,height:d,h:d,x2:a+c,y2:b+d,cx:a+c/2,cy:b+d/2,r1:N.min(c,d)/2,r2:N.max(c,d)/2,r0:N.sqrt(c*c+d*d)/2,path:w(a,b,c,d),vb:[a,b,c,d].join(" ")}}function e(){return this.join(",").replace(L,"$1")}function f(a){var b=J(a);return b.toString=e,b}function g(a,b,c,d,e,f,g,h,j){return null==j?n(a,b,c,d,e,f,g,h):i(a,b,c,d,e,f,g,h,o(a,b,c,d,e,f,g,h,j))}function h(c,d){function e(a){return+(+a).toFixed(3)}return a._.cacher(function(a,f,h){a instanceof b&&(a=a.attr("d")),a=E(a);for(var j,k,l,m,n,o="",p={},q=0,r=0,s=a.length;s>r;r++){if(l=a[r],"M"==l[0])j=+l[1],k=+l[2];else{if(m=g(j,k,l[1],l[2],l[3],l[4],l[5],l[6]),q+m>f){if(d&&!p.start){if(n=g(j,k,l[1],l[2],l[3],l[4],l[5],l[6],f-q),o+=["C"+e(n.start.x),e(n.start.y),e(n.m.x),e(n.m.y),e(n.x),e(n.y)],h)return o;p.start=o,o=["M"+e(n.x),e(n.y)+"C"+e(n.n.x),e(n.n.y),e(n.end.x),e(n.end.y),e(l[5]),e(l[6])].join(),q+=m,j=+l[5],k=+l[6];continue}if(!c&&!d)return n=g(j,k,l[1],l[2],l[3],l[4],l[5],l[6],f-q)}q+=m,j=+l[5],k=+l[6]}o+=l.shift()+l}return p.end=o,n=c?q:d?p:i(j,k,l[0],l[1],l[2],l[3],l[4],l[5],1)},null,a._.clone)}function i(a,b,c,d,e,f,g,h,i){var j=1-i,k=R(j,3),l=R(j,2),m=i*i,n=m*i,o=k*a+3*l*i*c+3*j*i*i*e+n*g,p=k*b+3*l*i*d+3*j*i*i*f+n*h,q=a+2*i*(c-a)+m*(e-2*c+a),r=b+2*i*(d-b)+m*(f-2*d+b),s=c+2*i*(e-c)+m*(g-2*e+c),t=d+2*i*(f-d)+m*(h-2*f+d),u=j*a+i*c,v=j*b+i*d,w=j*e+i*g,x=j*f+i*h,y=90-180*N.atan2(q-s,r-t)/O;return{x:o,y:p,m:{x:q,y:r},n:{x:s,y:t},start:{x:u,y:v},end:{x:w,y:x},alpha:y}}function j(b,c,e,f,g,h,i,j){a.is(b,"array")||(b=[b,c,e,f,g,h,i,j]);var k=D.apply(null,b);return d(k.min.x,k.min.y,k.max.x-k.min.x,k.max.y-k.min.y)}function k(a,b,c){return b>=a.x&&b<=a.x+a.width&&c>=a.y&&c<=a.y+a.height}function l(a,b){return a=d(a),b=d(b),k(b,a.x,a.y)||k(b,a.x2,a.y)||k(b,a.x,a.y2)||k(b,a.x2,a.y2)||k(a,b.x,b.y)||k(a,b.x2,b.y)||k(a,b.x,b.y2)||k(a,b.x2,b.y2)||(a.x<b.x2&&a.x>b.x||b.x<a.x2&&b.x>a.x)&&(a.y<b.y2&&a.y>b.y||b.y<a.y2&&b.y>a.y)}function m(a,b,c,d,e){var f=-3*b+9*c-9*d+3*e,g=a*f+6*b-12*c+6*d;return a*g-3*b+3*c}function n(a,b,c,d,e,f,g,h,i){null==i&&(i=1),i=i>1?1:0>i?0:i;for(var j=i/2,k=12,l=[-.1252,.1252,-.3678,.3678,-.5873,.5873,-.7699,.7699,-.9041,.9041,-.9816,.9816],n=[.2491,.2491,.2335,.2335,.2032,.2032,.1601,.1601,.1069,.1069,.0472,.0472],o=0,p=0;k>p;p++){var q=j*l[p]+j,r=m(q,a,c,e,g),s=m(q,b,d,f,h),t=r*r+s*s;o+=n[p]*N.sqrt(t)}return j*o}function o(a,b,c,d,e,f,g,h,i){if(!(0>i||n(a,b,c,d,e,f,g,h)<i)){var j,k=1,l=k/2,m=k-l,o=.01;for(j=n(a,b,c,d,e,f,g,h,m);S(j-i)>o;)l/=2,m+=(i>j?1:-1)*l,j=n(a,b,c,d,e,f,g,h,m);return m}}function p(a,b,c,d,e,f,g,h){if(!(Q(a,c)<P(e,g)||P(a,c)>Q(e,g)||Q(b,d)<P(f,h)||P(b,d)>Q(f,h))){var i=(a*d-b*c)*(e-g)-(a-c)*(e*h-f*g),j=(a*d-b*c)*(f-h)-(b-d)*(e*h-f*g),k=(a-c)*(f-h)-(b-d)*(e-g);if(k){var l=i/k,m=j/k,n=+l.toFixed(2),o=+m.toFixed(2);if(!(n<+P(a,c).toFixed(2)||n>+Q(a,c).toFixed(2)||n<+P(e,g).toFixed(2)||n>+Q(e,g).toFixed(2)||o<+P(b,d).toFixed(2)||o>+Q(b,d).toFixed(2)||o<+P(f,h).toFixed(2)||o>+Q(f,h).toFixed(2)))return{x:l,y:m}}}}function q(a,b,c){var d=j(a),e=j(b);if(!l(d,e))return c?0:[];for(var f=n.apply(0,a),g=n.apply(0,b),h=~~(f/8),k=~~(g/8),m=[],o=[],q={},r=c?0:[],s=0;h+1>s;s++){var t=i.apply(0,a.concat(s/h));m.push({x:t.x,y:t.y,t:s/h})}for(s=0;k+1>s;s++)t=i.apply(0,b.concat(s/k)),o.push({x:t.x,y:t.y,t:s/k});for(s=0;h>s;s++)for(var u=0;k>u;u++){var v=m[s],w=m[s+1],x=o[u],y=o[u+1],z=S(w.x-v.x)<.001?"y":"x",A=S(y.x-x.x)<.001?"y":"x",B=p(v.x,v.y,w.x,w.y,x.x,x.y,y.x,y.y);if(B){if(q[B.x.toFixed(4)]==B.y.toFixed(4))continue;q[B.x.toFixed(4)]=B.y.toFixed(4);var C=v.t+S((B[z]-v[z])/(w[z]-v[z]))*(w.t-v.t),D=x.t+S((B[A]-x[A])/(y[A]-x[A]))*(y.t-x.t);C>=0&&1>=C&&D>=0&&1>=D&&(c?r++:r.push({x:B.x,y:B.y,t1:C,t2:D}))}}return r}function r(a,b){return t(a,b)}function s(a,b){return t(a,b,1)}function t(a,b,c){a=E(a),b=E(b);for(var d,e,f,g,h,i,j,k,l,m,n=c?0:[],o=0,p=a.length;p>o;o++){var r=a[o];if("M"==r[0])d=h=r[1],e=i=r[2];else{"C"==r[0]?(l=[d,e].concat(r.slice(1)),d=l[6],e=l[7]):(l=[d,e,d,e,h,i,h,i],d=h,e=i);for(var s=0,t=b.length;t>s;s++){var u=b[s];if("M"==u[0])f=j=u[1],g=k=u[2];else{"C"==u[0]?(m=[f,g].concat(u.slice(1)),f=m[6],g=m[7]):(m=[f,g,f,g,j,k,j,k],f=j,g=k);var v=q(l,m,c);if(c)n+=v;else{for(var w=0,x=v.length;x>w;w++)v[w].segment1=o,v[w].segment2=s,v[w].bez1=l,v[w].bez2=m;n=n.concat(v)}}}}}return n}function u(a,b,c){var d=v(a);return k(d,b,c)&&t(a,[["M",b,c],["H",d.x2+10]],1)%2==1}function v(a){var b=c(a);if(b.bbox)return J(b.bbox);if(!a)return d();a=E(a);for(var e,f=0,g=0,h=[],i=[],j=0,k=a.length;k>j;j++)if(e=a[j],"M"==e[0])f=e[1],g=e[2],h.push(f),i.push(g);else{var l=D(f,g,e[1],e[2],e[3],e[4],e[5],e[6]);h=h.concat(l.min.x,l.max.x),i=i.concat(l.min.y,l.max.y),f=e[5],g=e[6]}var m=P.apply(0,h),n=P.apply(0,i),o=Q.apply(0,h),p=Q.apply(0,i),q=d(m,n,o-m,p-n);return b.bbox=J(q),q}function w(a,b,c,d,f){if(f)return[["M",+a+ +f,b],["l",c-2*f,0],["a",f,f,0,0,1,f,f],["l",0,d-2*f],["a",f,f,0,0,1,-f,f],["l",2*f-c,0],["a",f,f,0,0,1,-f,-f],["l",0,2*f-d],["a",f,f,0,0,1,f,-f],["z"]];var g=[["M",a,b],["l",c,0],["l",0,d],["l",-c,0],["z"]];return g.toString=e,g}function x(a,b,c,d,f){if(null==f&&null==d&&(d=c),a=+a,b=+b,c=+c,d=+d,null!=f)var g=Math.PI/180,h=a+c*Math.cos(-d*g),i=a+c*Math.cos(-f*g),j=b+c*Math.sin(-d*g),k=b+c*Math.sin(-f*g),l=[["M",h,j],["A",c,c,0,+(f-d>180),0,i,k]];else l=[["M",a,b],["m",0,-d],["a",c,d,0,1,1,0,2*d],["a",c,d,0,1,1,0,-2*d],["z"]];return l.toString=e,l}function y(b){var d=c(b),g=String.prototype.toLowerCase;if(d.rel)return f(d.rel);a.is(b,"array")&&a.is(b&&b[0],"array")||(b=a.parsePathString(b));var h=[],i=0,j=0,k=0,l=0,m=0;"M"==b[0][0]&&(i=b[0][1],j=b[0][2],k=i,l=j,m++,h.push(["M",i,j]));for(var n=m,o=b.length;o>n;n++){var p=h[n]=[],q=b[n];if(q[0]!=g.call(q[0]))switch(p[0]=g.call(q[0]),p[0]){case"a":p[1]=q[1],p[2]=q[2],p[3]=q[3],p[4]=q[4],p[5]=q[5],p[6]=+(q[6]-i).toFixed(3),p[7]=+(q[7]-j).toFixed(3);break;case"v":p[1]=+(q[1]-j).toFixed(3);break;case"m":k=q[1],l=q[2];default:for(var r=1,s=q.length;s>r;r++)p[r]=+(q[r]-(r%2?i:j)).toFixed(3)}else{p=h[n]=[],"m"==q[0]&&(k=q[1]+i,l=q[2]+j);for(var t=0,u=q.length;u>t;t++)h[n][t]=q[t]}var v=h[n].length;switch(h[n][0]){case"z":i=k,j=l;break;case"h":i+=+h[n][v-1];break;case"v":j+=+h[n][v-1];break;default:i+=+h[n][v-2],j+=+h[n][v-1]}}return h.toString=e,d.rel=f(h),h}function z(b){var d=c(b);if(d.abs)return f(d.abs);if(I(b,"array")&&I(b&&b[0],"array")||(b=a.parsePathString(b)),!b||!b.length)return[["M",0,0]];var g,h=[],i=0,j=0,k=0,l=0,m=0;"M"==b[0][0]&&(i=+b[0][1],j=+b[0][2],k=i,l=j,m++,h[0]=["M",i,j]);for(var n,o,p=3==b.length&&"M"==b[0][0]&&"R"==b[1][0].toUpperCase()&&"Z"==b[2][0].toUpperCase(),q=m,r=b.length;r>q;q++){if(h.push(n=[]),o=b[q],g=o[0],g!=g.toUpperCase())switch(n[0]=g.toUpperCase(),n[0]){case"A":n[1]=o[1],n[2]=o[2],n[3]=o[3],n[4]=o[4],n[5]=o[5],n[6]=+o[6]+i,n[7]=+o[7]+j;break;case"V":n[1]=+o[1]+j;break;case"H":n[1]=+o[1]+i;break;case"R":for(var s=[i,j].concat(o.slice(1)),t=2,u=s.length;u>t;t++)s[t]=+s[t]+i,s[++t]=+s[t]+j;h.pop(),h=h.concat(G(s,p));break;case"O":h.pop(),s=x(i,j,o[1],o[2]),s.push(s[0]),h=h.concat(s);break;case"U":h.pop(),h=h.concat(x(i,j,o[1],o[2],o[3])),n=["U"].concat(h[h.length-1].slice(-2));break;case"M":k=+o[1]+i,l=+o[2]+j;default:for(t=1,u=o.length;u>t;t++)n[t]=+o[t]+(t%2?i:j)}else if("R"==g)s=[i,j].concat(o.slice(1)),h.pop(),h=h.concat(G(s,p)),n=["R"].concat(o.slice(-2));else if("O"==g)h.pop(),s=x(i,j,o[1],o[2]),s.push(s[0]),h=h.concat(s);else if("U"==g)h.pop(),h=h.concat(x(i,j,o[1],o[2],o[3])),n=["U"].concat(h[h.length-1].slice(-2));else for(var v=0,w=o.length;w>v;v++)n[v]=o[v];if(g=g.toUpperCase(),"O"!=g)switch(n[0]){case"Z":i=+k,j=+l;break;case"H":i=n[1];break;case"V":j=n[1];break;case"M":k=n[n.length-2],l=n[n.length-1];default:i=n[n.length-2],j=n[n.length-1]}}return h.toString=e,d.abs=f(h),h}function A(a,b,c,d){return[a,b,c,d,c,d]}function B(a,b,c,d,e,f){var g=1/3,h=2/3;return[g*a+h*c,g*b+h*d,g*e+h*c,g*f+h*d,e,f]}function C(b,c,d,e,f,g,h,i,j,k){var l,m=120*O/180,n=O/180*(+f||0),o=[],p=a._.cacher(function(a,b,c){var d=a*N.cos(c)-b*N.sin(c),e=a*N.sin(c)+b*N.cos(c);return{x:d,y:e}});if(k)y=k[0],z=k[1],w=k[2],x=k[3];else{l=p(b,c,-n),b=l.x,c=l.y,l=p(i,j,-n),i=l.x,j=l.y;var q=(N.cos(O/180*f),N.sin(O/180*f),(b-i)/2),r=(c-j)/2,s=q*q/(d*d)+r*r/(e*e);s>1&&(s=N.sqrt(s),d=s*d,e=s*e);var t=d*d,u=e*e,v=(g==h?-1:1)*N.sqrt(S((t*u-t*r*r-u*q*q)/(t*r*r+u*q*q))),w=v*d*r/e+(b+i)/2,x=v*-e*q/d+(c+j)/2,y=N.asin(((c-x)/e).toFixed(9)),z=N.asin(((j-x)/e).toFixed(9));y=w>b?O-y:y,z=w>i?O-z:z,0>y&&(y=2*O+y),0>z&&(z=2*O+z),h&&y>z&&(y-=2*O),!h&&z>y&&(z-=2*O)}var A=z-y;if(S(A)>m){var B=z,D=i,E=j;z=y+m*(h&&z>y?1:-1),i=w+d*N.cos(z),j=x+e*N.sin(z),o=C(i,j,d,e,f,0,h,D,E,[z,B,w,x])}A=z-y;var F=N.cos(y),G=N.sin(y),H=N.cos(z),I=N.sin(z),J=N.tan(A/4),K=4/3*d*J,L=4/3*e*J,M=[b,c],P=[b+K*G,c-L*F],Q=[i+K*I,j-L*H],R=[i,j];if(P[0]=2*M[0]-P[0],P[1]=2*M[1]-P[1],k)return[P,Q,R].concat(o);o=[P,Q,R].concat(o).join().split(",");for(var T=[],U=0,V=o.length;V>U;U++)T[U]=U%2?p(o[U-1],o[U],n).y:p(o[U],o[U+1],n).x;return T}function D(a,b,c,d,e,f,g,h){for(var i,j,k,l,m,n,o,p,q=[],r=[[],[]],s=0;2>s;++s)if(0==s?(j=6*a-12*c+6*e,i=-3*a+9*c-9*e+3*g,k=3*c-3*a):(j=6*b-12*d+6*f,i=-3*b+9*d-9*f+3*h,k=3*d-3*b),S(i)<1e-12){if(S(j)<1e-12)continue;l=-k/j,l>0&&1>l&&q.push(l)}else o=j*j-4*k*i,p=N.sqrt(o),0>o||(m=(-j+p)/(2*i),m>0&&1>m&&q.push(m),n=(-j-p)/(2*i),n>0&&1>n&&q.push(n));for(var t,u=q.length,v=u;u--;)l=q[u],t=1-l,r[0][u]=t*t*t*a+3*t*t*l*c+3*t*l*l*e+l*l*l*g,r[1][u]=t*t*t*b+3*t*t*l*d+3*t*l*l*f+l*l*l*h;return r[0][v]=a,r[1][v]=b,r[0][v+1]=g,r[1][v+1]=h,r[0].length=r[1].length=v+2,{min:{x:P.apply(0,r[0]),y:P.apply(0,r[1])},max:{x:Q.apply(0,r[0]),y:Q.apply(0,r[1])}}}function E(a,b){var d=!b&&c(a);if(!b&&d.curve)return f(d.curve);for(var e=z(a),g=b&&z(b),h={x:0,y:0,bx:0,by:0,X:0,Y:0,qx:null,qy:null},i={x:0,y:0,bx:0,by:0,X:0,Y:0,qx:null,qy:null},j=(function(a,b,c){var d,e;if(!a)return["C",b.x,b.y,b.x,b.y,b.x,b.y];switch(!(a[0]in{T:1,Q:1})&&(b.qx=b.qy=null),a[0]){case"M":b.X=a[1],b.Y=a[2];break;case"A":a=["C"].concat(C.apply(0,[b.x,b.y].concat(a.slice(1))));break;case"S":"C"==c||"S"==c?(d=2*b.x-b.bx,e=2*b.y-b.by):(d=b.x,e=b.y),a=["C",d,e].concat(a.slice(1));break;case"T":"Q"==c||"T"==c?(b.qx=2*b.x-b.qx,b.qy=2*b.y-b.qy):(b.qx=b.x,b.qy=b.y),a=["C"].concat(B(b.x,b.y,b.qx,b.qy,a[1],a[2]));break;case"Q":b.qx=a[1],b.qy=a[2],a=["C"].concat(B(b.x,b.y,a[1],a[2],a[3],a[4]));break;case"L":a=["C"].concat(A(b.x,b.y,a[1],a[2]));break;case"H":a=["C"].concat(A(b.x,b.y,a[1],b.y));break;case"V":a=["C"].concat(A(b.x,b.y,b.x,a[1]));break;case"Z":a=["C"].concat(A(b.x,b.y,b.X,b.Y))}return a}),k=function(a,b){if(a[b].length>7){a[b].shift();for(var c=a[b];c.length;)m[b]="A",g&&(n[b]="A"),a.splice(b++,0,["C"].concat(c.splice(0,6)));a.splice(b,1),r=Q(e.length,g&&g.length||0)}},l=function(a,b,c,d,f){a&&b&&"M"==a[f][0]&&"M"!=b[f][0]&&(b.splice(f,0,["M",d.x,d.y]),c.bx=0,c.by=0,c.x=a[f][1],c.y=a[f][2],r=Q(e.length,g&&g.length||0))},m=[],n=[],o="",p="",q=0,r=Q(e.length,g&&g.length||0);r>q;q++){e[q]&&(o=e[q][0]),"C"!=o&&(m[q]=o,q&&(p=m[q-1])),e[q]=j(e[q],h,p),"A"!=m[q]&&"C"==o&&(m[q]="C"),k(e,q),g&&(g[q]&&(o=g[q][0]),"C"!=o&&(n[q]=o,q&&(p=n[q-1])),g[q]=j(g[q],i,p),"A"!=n[q]&&"C"==o&&(n[q]="C"),k(g,q)),l(e,g,h,i,q),l(g,e,i,h,q);var s=e[q],t=g&&g[q],u=s.length,v=g&&t.length;h.x=s[u-2],h.y=s[u-1],h.bx=M(s[u-4])||h.x,h.by=M(s[u-3])||h.y,i.bx=g&&(M(t[v-4])||i.x),i.by=g&&(M(t[v-3])||i.y),i.x=g&&t[v-2],i.y=g&&t[v-1]}return g||(d.curve=f(e)),g?[e,g]:e}function F(a,b){if(!b)return a;var c,d,e,f,g,h,i;for(a=E(a),e=0,g=a.length;g>e;e++)for(i=a[e],f=1,h=i.length;h>f;f+=2)c=b.x(i[f],i[f+1]),d=b.y(i[f],i[f+1]),i[f]=c,i[f+1]=d;return a}function G(a,b){for(var c=[],d=0,e=a.length;e-2*!b>d;d+=2){var f=[{x:+a[d-2],y:+a[d-1]},{x:+a[d],y:+a[d+1]},{x:+a[d+2],y:+a[d+3]},{x:+a[d+4],y:+a[d+5]}];b?d?e-4==d?f[3]={x:+a[0],y:+a[1]}:e-2==d&&(f[2]={x:+a[0],y:+a[1]},f[3]={x:+a[2],y:+a[3]}):f[0]={x:+a[e-2],y:+a[e-1]}:e-4==d?f[3]=f[2]:d||(f[0]={x:+a[d],y:+a[d+1]}),c.push(["C",(-f[0].x+6*f[1].x+f[2].x)/6,(-f[0].y+6*f[1].y+f[2].y)/6,(f[1].x+6*f[2].x-f[3].x)/6,(f[1].y+6*f[2].y-f[3].y)/6,f[2].x,f[2].y])}return c}var H=b.prototype,I=a.is,J=a._.clone,K="hasOwnProperty",L=/,?([a-z]),?/gi,M=parseFloat,N=Math,O=N.PI,P=N.min,Q=N.max,R=N.pow,S=N.abs,T=h(1),U=h(),V=h(0,1),W=a._unit2px,X={path:function(a){return a.attr("path")},circle:function(a){var b=W(a);return x(b.cx,b.cy,b.r)},ellipse:function(a){var b=W(a);return x(b.cx||0,b.cy||0,b.rx,b.ry)},rect:function(a){var b=W(a);return w(b.x||0,b.y||0,b.width,b.height,b.rx,b.ry)},image:function(a){var b=W(a);return w(b.x||0,b.y||0,b.width,b.height)},line:function(a){return"M"+[a.attr("x1")||0,a.attr("y1")||0,a.attr("x2"),a.attr("y2")]},polyline:function(a){return"M"+a.attr("points")},polygon:function(a){return"M"+a.attr("points")+"z"},deflt:function(a){var b=a.node.getBBox();return w(b.x,b.y,b.width,b.height)}};a.path=c,a.path.getTotalLength=T,a.path.getPointAtLength=U,a.path.getSubpath=function(a,b,c){if(this.getTotalLength(a)-c<1e-6)return V(a,b).end;var d=V(a,c,1);return b?V(d,b).end:d},H.getTotalLength=function(){return this.node.getTotalLength?this.node.getTotalLength():void 0},H.getPointAtLength=function(a){return U(this.attr("d"),a)},H.getSubpath=function(b,c){return a.path.getSubpath(this.attr("d"),b,c)},a._.box=d,a.path.findDotsAtSegment=i,a.path.bezierBBox=j,a.path.isPointInsideBBox=k,a.closest=function(b,c,e,f){for(var g=100,h=d(b-g/2,c-g/2,g,g),i=[],j=e[0].hasOwnProperty("x")?function(a){return{x:e[a].x,y:e[a].y}}:function(a){return{x:e[a],y:f[a]}},l=0;1e6>=g&&!l;){for(var m=0,n=e.length;n>m;m++){var o=j(m);if(k(h,o.x,o.y)){l++,i.push(o);break}}l||(g*=2,h=d(b-g/2,c-g/2,g,g))}if(1e6!=g){var p,q=1/0;for(m=0,n=i.length;n>m;m++){var r=a.len(b,c,i[m].x,i[m].y);q>r&&(q=r,i[m].len=r,p=i[m])}return p}},a.path.isBBoxIntersect=l,a.path.intersection=r,a.path.intersectionNumber=s,a.path.isPointInside=u,a.path.getBBox=v,a.path.get=X,a.path.toRelative=y,a.path.toAbsolute=z,a.path.toCubic=E,a.path.map=F,a.path.toString=e,a.path.clone=f}),d.plugin(function(a){var d=Math.max,e=Math.min,f=function(a){if(this.items=[],this.bindings={},this.length=0,this.type="set",a)for(var b=0,c=a.length;c>b;b++)a[b]&&(this[this.items.length]=this.items[this.items.length]=a[b],this.length++)},g=f.prototype;g.push=function(){for(var a,b,c=0,d=arguments.length;d>c;c++)a=arguments[c],a&&(b=this.items.length,this[b]=this.items[b]=a,this.length++);return this},g.pop=function(){return this.length&&delete this[this.length--],this.items.pop()},g.forEach=function(a,b){for(var c=0,d=this.items.length;d>c;c++)if(a.call(b,this.items[c],c)===!1)return this;return this},g.animate=function(d,e,f,g){"function"!=typeof f||f.length||(g=f,f=c.linear),d instanceof a._.Animation&&(g=d.callback,f=d.easing,e=f.dur,d=d.attr);var h=arguments;if(a.is(d,"array")&&a.is(h[h.length-1],"array"))var i=!0;var j,k=function(){j?this.b=j:j=this.b},l=0,m=this,n=g&&function(){++l==m.length&&g.call(this)
21
- };return this.forEach(function(a,c){b.once("snap.animcreated."+a.id,k),i?h[c]&&a.animate.apply(a,h[c]):a.animate(d,e,f,n)})},g.remove=function(){for(;this.length;)this.pop().remove();return this},g.bind=function(a,b,c){var d={};if("function"==typeof b)this.bindings[a]=b;else{var e=c||a;this.bindings[a]=function(a){d[e]=a,b.attr(d)}}return this},g.attr=function(a){var b={};for(var c in a)this.bindings[c]?this.bindings[c](a[c]):b[c]=a[c];for(var d=0,e=this.items.length;e>d;d++)this.items[d].attr(b);return this},g.clear=function(){for(;this.length;)this.pop()},g.splice=function(a,b){a=0>a?d(this.length+a,0):a,b=d(0,e(this.length-a,b));var c,g=[],h=[],i=[];for(c=2;c<arguments.length;c++)i.push(arguments[c]);for(c=0;b>c;c++)h.push(this[a+c]);for(;c<this.length-a;c++)g.push(this[a+c]);var j=i.length;for(c=0;c<j+g.length;c++)this.items[a+c]=this[a+c]=j>c?i[c]:g[c-j];for(c=this.items.length=this.length-=b-j;this[c];)delete this[c++];return new f(h)},g.exclude=function(a){for(var b=0,c=this.length;c>b;b++)if(this[b]==a)return this.splice(b,1),!0;return!1},g.insertAfter=function(a){for(var b=this.items.length;b--;)this.items[b].insertAfter(a);return this},g.getBBox=function(){for(var a=[],b=[],c=[],f=[],g=this.items.length;g--;)if(!this.items[g].removed){var h=this.items[g].getBBox();a.push(h.x),b.push(h.y),c.push(h.x+h.width),f.push(h.y+h.height)}return a=e.apply(0,a),b=e.apply(0,b),c=d.apply(0,c),f=d.apply(0,f),{x:a,y:b,x2:c,y2:f,width:c-a,height:f-b,cx:a+(c-a)/2,cy:b+(f-b)/2}},g.clone=function(a){a=new f;for(var b=0,c=this.items.length;c>b;b++)a.push(this.items[b].clone());return a},g.toString=function(){return"Snap‘s set"},g.type="set",a.Set=f,a.set=function(){var a=new f;return arguments.length&&a.push.apply(a,Array.prototype.slice.call(arguments,0)),a}}),d.plugin(function(a,c){function d(a){var b=a[0];switch(b.toLowerCase()){case"t":return[b,0,0];case"m":return[b,1,0,0,1,0,0];case"r":return 4==a.length?[b,0,a[2],a[3]]:[b,0];case"s":return 5==a.length?[b,1,1,a[3],a[4]]:3==a.length?[b,1,1]:[b,1]}}function e(b,c,e){c=p(c).replace(/\.{3}|\u2026/g,b),b=a.parseTransformString(b)||[],c=a.parseTransformString(c)||[];for(var f,g,h,i,l=Math.max(b.length,c.length),m=[],n=[],o=0;l>o;o++){if(h=b[o]||d(c[o]),i=c[o]||d(h),h[0]!=i[0]||"r"==h[0].toLowerCase()&&(h[2]!=i[2]||h[3]!=i[3])||"s"==h[0].toLowerCase()&&(h[3]!=i[3]||h[4]!=i[4])){b=a._.transform2matrix(b,e()),c=a._.transform2matrix(c,e()),m=[["m",b.a,b.b,b.c,b.d,b.e,b.f]],n=[["m",c.a,c.b,c.c,c.d,c.e,c.f]];break}for(m[o]=[],n[o]=[],f=0,g=Math.max(h.length,i.length);g>f;f++)f in h&&(m[o][f]=h[f]),f in i&&(n[o][f]=i[f])}return{from:k(m),to:k(n),f:j(m)}}function f(a){return a}function g(a){return function(b){return+b.toFixed(3)+a}}function h(a){return a.join(" ")}function i(b){return a.rgb(b[0],b[1],b[2])}function j(a){var b,c,d,e,f,g,h=0,i=[];for(b=0,c=a.length;c>b;b++){for(f="[",g=['"'+a[b][0]+'"'],d=1,e=a[b].length;e>d;d++)g[d]="val["+h++ +"]";f+=g+"]",i[b]=f}return Function("val","return Snap.path.toString.call(["+i+"])")}function k(a){for(var b=[],c=0,d=a.length;d>c;c++)for(var e=1,f=a[c].length;f>e;e++)b.push(a[c][e]);return b}function l(a){return isFinite(parseFloat(a))}function m(b,c){return a.is(b,"array")&&a.is(c,"array")?b.toString()==c.toString():!1}var n={},o=/[a-z]+$/i,p=String;n.stroke=n.fill="colour",c.prototype.equal=function(a,c){return b("snap.util.equal",this,a,c).firstDefined()},b.on("snap.util.equal",function(b,c){var d,q,r=p(this.attr(b)||""),s=this;if(l(r)&&l(c))return{from:parseFloat(r),to:parseFloat(c),f:f};if("colour"==n[b])return d=a.color(r),q=a.color(c),{from:[d.r,d.g,d.b,d.opacity],to:[q.r,q.g,q.b,q.opacity],f:i};if("viewBox"==b)return d=this.attr(b).vb.split(" ").map(Number),q=c.split(" ").map(Number),{from:d,to:q,f:h};if("transform"==b||"gradientTransform"==b||"patternTransform"==b)return c instanceof a.Matrix&&(c=c.toTransformString()),a._.rgTransform.test(c)||(c=a._.svgTransform2string(c)),e(r,c,function(){return s.getBBox(1)});if("d"==b||"path"==b)return d=a.path.toCubic(r,c),{from:k(d[0]),to:k(d[1]),f:j(d[0])};if("points"==b)return d=p(r).split(a._.separator),q=p(c).split(a._.separator),{from:d,to:q,f:function(a){return a}};var t=r.match(o),u=p(c).match(o);return t&&m(t,u)?{from:parseFloat(r),to:parseFloat(c),f:g(t)}:{from:this.asPX(b),to:this.asPX(b,c),f:f}})}),d.plugin(function(a,c,d,e){for(var f=c.prototype,g="hasOwnProperty",h=("createTouch"in e.doc),i=["click","dblclick","mousedown","mousemove","mouseout","mouseover","mouseup","touchstart","touchmove","touchend","touchcancel"],j={mousedown:"touchstart",mousemove:"touchmove",mouseup:"touchend"},k=(function(a,b){var c="y"==a?"scrollTop":"scrollLeft",d=b&&b.node?b.node.ownerDocument:e.doc;return d[c in d.documentElement?"documentElement":"body"][c]}),l=function(){return this.originalEvent.preventDefault()},m=function(){return this.originalEvent.stopPropagation()},n=function(a,b,c,d){var e=h&&j[b]?j[b]:b,f=function(e){var f=k("y",d),i=k("x",d);if(h&&j[g](b))for(var n=0,o=e.targetTouches&&e.targetTouches.length;o>n;n++)if(e.targetTouches[n].target==a||a.contains(e.targetTouches[n].target)){var p=e;e=e.targetTouches[n],e.originalEvent=p,e.preventDefault=l,e.stopPropagation=m;break}var q=e.clientX+i,r=e.clientY+f;return c.call(d,e,q,r)};return b!==e&&a.addEventListener(b,f,!1),a.addEventListener(e,f,!1),function(){return b!==e&&a.removeEventListener(b,f,!1),a.removeEventListener(e,f,!1),!0}},o=[],p=function(a){for(var c,d=a.clientX,e=a.clientY,f=k("y"),g=k("x"),i=o.length;i--;){if(c=o[i],h){for(var j,l=a.touches&&a.touches.length;l--;)if(j=a.touches[l],j.identifier==c.el._drag.id||c.el.node.contains(j.target)){d=j.clientX,e=j.clientY,(a.originalEvent?a.originalEvent:a).preventDefault();break}}else a.preventDefault();{var m=c.el.node;m.nextSibling,m.parentNode,m.style.display}d+=g,e+=f,b("snap.drag.move."+c.el.id,c.move_scope||c.el,d-c.el._drag.x,e-c.el._drag.y,d,e,a)}},q=function(c){a.unmousemove(p).unmouseup(q);for(var d,e=o.length;e--;)d=o[e],d.el._drag={},b("snap.drag.end."+d.el.id,d.end_scope||d.start_scope||d.move_scope||d.el,c),b.off("snap.drag.*."+d.el.id);o=[]},r=i.length;r--;)!function(b){a[b]=f[b]=function(c,d){if(a.is(c,"function"))this.events=this.events||[],this.events.push({name:b,f:c,unbind:n(this.node||document,b,c,d||this)});else for(var e=0,f=this.events.length;f>e;e++)if(this.events[e].name==b)try{this.events[e].f.call(this)}catch(g){}return this},a["un"+b]=f["un"+b]=function(a){for(var c=this.events||[],d=c.length;d--;)if(c[d].name==b&&(c[d].f==a||!a))return c[d].unbind(),c.splice(d,1),!c.length&&delete this.events,this;return this}}(i[r]);f.hover=function(a,b,c,d){return this.mouseover(a,c).mouseout(b,d||c)},f.unhover=function(a,b){return this.unmouseover(a).unmouseout(b)};var s=[];f.drag=function(c,d,e,f,g,h){function i(i,j,l){(i.originalEvent||i).preventDefault(),k._drag.x=j,k._drag.y=l,k._drag.id=i.identifier,!o.length&&a.mousemove(p).mouseup(q),o.push({el:k,move_scope:f,start_scope:g,end_scope:h}),d&&b.on("snap.drag.start."+k.id,d),c&&b.on("snap.drag.move."+k.id,c),e&&b.on("snap.drag.end."+k.id,e),b("snap.drag.start."+k.id,g||f||k,j,l,i)}function j(a,c,d){b("snap.draginit."+k.id,k,a,c,d)}var k=this;if(!arguments.length){var l;return k.drag(function(a,b){this.attr({transform:l+(l?"T":"t")+[a,b]})},function(){l=this.transform().local})}return b.on("snap.draginit."+k.id,i),k._drag={},s.push({el:k,start:i,init:j}),k.mousedown(j),k},f.undrag=function(){for(var c=s.length;c--;)s[c].el==this&&(this.unmousedown(s[c].init),s.splice(c,1),b.unbind("snap.drag.*."+this.id),b.unbind("snap.draginit."+this.id));return!s.length&&a.unmousemove(p).unmouseup(q),this}}),d.plugin(function(a,c,d){var e=(c.prototype,d.prototype),f=/^\s*url\((.+)\)/,g=String,h=a._.$;a.filter={},e.filter=function(b){var d=this;"svg"!=d.type&&(d=d.paper);var e=a.parse(g(b)),f=a._.id(),i=(d.node.offsetWidth,d.node.offsetHeight,h("filter"));return h(i,{id:f,filterUnits:"userSpaceOnUse"}),i.appendChild(e.node),d.defs.appendChild(i),new c(i)},b.on("snap.util.getattr.filter",function(){b.stop();var c=h(this.node,"filter");if(c){var d=g(c).match(f);return d&&a.select(d[1])}}),b.on("snap.util.attr.filter",function(d){if(d instanceof c&&"filter"==d.type){b.stop();var e=d.node.id;e||(h(d.node,{id:d.id}),e=d.id),h(this.node,{filter:a.url(e)})}d&&"none"!=d||(b.stop(),this.node.removeAttribute("filter"))}),a.filter.blur=function(b,c){null==b&&(b=2);var d=null==c?b:[b,c];return a.format('<feGaussianBlur stdDeviation="{def}"/>',{def:d})},a.filter.blur.toString=function(){return this()},a.filter.shadow=function(b,c,d,e,f){return"string"==typeof d&&(e=d,f=e,d=4),"string"!=typeof e&&(f=e,e="#000"),e=e||"#000",null==d&&(d=4),null==f&&(f=1),null==b&&(b=0,c=2),null==c&&(c=b),e=a.color(e),a.format('<feGaussianBlur in="SourceAlpha" stdDeviation="{blur}"/><feOffset dx="{dx}" dy="{dy}" result="offsetblur"/><feFlood flood-color="{color}"/><feComposite in2="offsetblur" operator="in"/><feComponentTransfer><feFuncA type="linear" slope="{opacity}"/></feComponentTransfer><feMerge><feMergeNode/><feMergeNode in="SourceGraphic"/></feMerge>',{color:e,dx:b,dy:c,blur:d,opacity:f})},a.filter.shadow.toString=function(){return this()},a.filter.grayscale=function(b){return null==b&&(b=1),a.format('<feColorMatrix type="matrix" values="{a} {b} {c} 0 0 {d} {e} {f} 0 0 {g} {b} {h} 0 0 0 0 0 1 0"/>',{a:.2126+.7874*(1-b),b:.7152-.7152*(1-b),c:.0722-.0722*(1-b),d:.2126-.2126*(1-b),e:.7152+.2848*(1-b),f:.0722-.0722*(1-b),g:.2126-.2126*(1-b),h:.0722+.9278*(1-b)})},a.filter.grayscale.toString=function(){return this()},a.filter.sepia=function(b){return null==b&&(b=1),a.format('<feColorMatrix type="matrix" values="{a} {b} {c} 0 0 {d} {e} {f} 0 0 {g} {h} {i} 0 0 0 0 0 1 0"/>',{a:.393+.607*(1-b),b:.769-.769*(1-b),c:.189-.189*(1-b),d:.349-.349*(1-b),e:.686+.314*(1-b),f:.168-.168*(1-b),g:.272-.272*(1-b),h:.534-.534*(1-b),i:.131+.869*(1-b)})},a.filter.sepia.toString=function(){return this()},a.filter.saturate=function(b){return null==b&&(b=1),a.format('<feColorMatrix type="saturate" values="{amount}"/>',{amount:1-b})},a.filter.saturate.toString=function(){return this()},a.filter.hueRotate=function(b){return b=b||0,a.format('<feColorMatrix type="hueRotate" values="{angle}"/>',{angle:b})},a.filter.hueRotate.toString=function(){return this()},a.filter.invert=function(b){return null==b&&(b=1),a.format('<feComponentTransfer><feFuncR type="table" tableValues="{amount} {amount2}"/><feFuncG type="table" tableValues="{amount} {amount2}"/><feFuncB type="table" tableValues="{amount} {amount2}"/></feComponentTransfer>',{amount:b,amount2:1-b})},a.filter.invert.toString=function(){return this()},a.filter.brightness=function(b){return null==b&&(b=1),a.format('<feComponentTransfer><feFuncR type="linear" slope="{amount}"/><feFuncG type="linear" slope="{amount}"/><feFuncB type="linear" slope="{amount}"/></feComponentTransfer>',{amount:b})},a.filter.brightness.toString=function(){return this()},a.filter.contrast=function(b){return null==b&&(b=1),a.format('<feComponentTransfer><feFuncR type="linear" slope="{amount}" intercept="{amount2}"/><feFuncG type="linear" slope="{amount}" intercept="{amount2}"/><feFuncB type="linear" slope="{amount}" intercept="{amount2}"/></feComponentTransfer>',{amount:b,amount2:.5-b/2})},a.filter.contrast.toString=function(){return this()}}),d.plugin(function(a,b){var c=a._.box,d=a.is,e=/^[^a-z]*([tbmlrc])/i,f=function(){return"T"+this.dx+","+this.dy};b.prototype.getAlign=function(a,b){null==b&&d(a,"string")&&(b=a,a=null),a=a||this.paper;var g=a.getBBox?a.getBBox():c(a),h=this.getBBox(),i={};switch(b=b&&b.match(e),b=b?b[1].toLowerCase():"c"){case"t":i.dx=0,i.dy=g.y-h.y;break;case"b":i.dx=0,i.dy=g.y2-h.y2;break;case"m":i.dx=0,i.dy=g.cy-h.cy;break;case"l":i.dx=g.x-h.x,i.dy=0;break;case"r":i.dx=g.x2-h.x2,i.dy=0;break;default:i.dx=g.cx-h.cx,i.dy=0}return i.toString=f,i},b.prototype.align=function(a,b){return this.transform("..."+this.getAlign(a,b))}}),d});
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Snap.svg 0.4.1
2
+ //
3
+ // Copyright (c) 2013 – 2015 Adobe Systems Incorporated. All rights reserved.
4
+ //
5
+ // Licensed under the Apache License, Version 2.0 (the "License");
6
+ // you may not use this file except in compliance with the License.
7
+ // You may obtain a copy of the License at
8
+ //
9
+ // http://www.apache.org/licenses/LICENSE-2.0
10
+ //
11
+ // Unless required by applicable law or agreed to in writing, software
12
+ // distributed under the License is distributed on an "AS IS" BASIS,
13
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ // See the License for the specific language governing permissions and
15
+ // limitations under the License.
16
+ //
17
+ // build: 2015-04-13
18
+
19
+ !function(a){var b,c,d="0.4.2",e="hasOwnProperty",f=/[\.\/]/,g=/\s*,\s*/,h="*",i=function(a,b){return a-b},j={n:{}},k=function(){for(var a=0,b=this.length;b>a;a++)if("undefined"!=typeof this[a])return this[a]},l=function(){for(var a=this.length;--a;)if("undefined"!=typeof this[a])return this[a]},m=function(a,d){a=String(a);var e,f=c,g=Array.prototype.slice.call(arguments,2),h=m.listeners(a),j=0,n=[],o={},p=[],q=b;p.firstDefined=k,p.lastDefined=l,b=a,c=0;for(var r=0,s=h.length;s>r;r++)"zIndex"in h[r]&&(n.push(h[r].zIndex),h[r].zIndex<0&&(o[h[r].zIndex]=h[r]));for(n.sort(i);n[j]<0;)if(e=o[n[j++]],p.push(e.apply(d,g)),c)return c=f,p;for(r=0;s>r;r++)if(e=h[r],"zIndex"in e)if(e.zIndex==n[j]){if(p.push(e.apply(d,g)),c)break;do if(j++,e=o[n[j]],e&&p.push(e.apply(d,g)),c)break;while(e)}else o[e.zIndex]=e;else if(p.push(e.apply(d,g)),c)break;return c=f,b=q,p};m._events=j,m.listeners=function(a){var b,c,d,e,g,i,k,l,m=a.split(f),n=j,o=[n],p=[];for(e=0,g=m.length;g>e;e++){for(l=[],i=0,k=o.length;k>i;i++)for(n=o[i].n,c=[n[m[e]],n[h]],d=2;d--;)b=c[d],b&&(l.push(b),p=p.concat(b.f||[]));o=l}return p},m.on=function(a,b){if(a=String(a),"function"!=typeof b)return function(){};for(var c=a.split(g),d=0,e=c.length;e>d;d++)!function(a){for(var c,d=a.split(f),e=j,g=0,h=d.length;h>g;g++)e=e.n,e=e.hasOwnProperty(d[g])&&e[d[g]]||(e[d[g]]={n:{}});for(e.f=e.f||[],g=0,h=e.f.length;h>g;g++)if(e.f[g]==b){c=!0;break}!c&&e.f.push(b)}(c[d]);return function(a){+a==+a&&(b.zIndex=+a)}},m.f=function(a){var b=[].slice.call(arguments,1);return function(){m.apply(null,[a,null].concat(b).concat([].slice.call(arguments,0)))}},m.stop=function(){c=1},m.nt=function(a){return a?new RegExp("(?:\\.|\\/|^)"+a+"(?:\\.|\\/|$)").test(b):b},m.nts=function(){return b.split(f)},m.off=m.unbind=function(a,b){if(!a)return void(m._events=j={n:{}});var c=a.split(g);if(c.length>1)for(var d=0,i=c.length;i>d;d++)m.off(c[d],b);else{c=a.split(f);var k,l,n,d,i,o,p,q=[j];for(d=0,i=c.length;i>d;d++)for(o=0;o<q.length;o+=n.length-2){if(n=[o,1],k=q[o].n,c[d]!=h)k[c[d]]&&n.push(k[c[d]]);else for(l in k)k[e](l)&&n.push(k[l]);q.splice.apply(q,n)}for(d=0,i=q.length;i>d;d++)for(k=q[d];k.n;){if(b){if(k.f){for(o=0,p=k.f.length;p>o;o++)if(k.f[o]==b){k.f.splice(o,1);break}!k.f.length&&delete k.f}for(l in k.n)if(k.n[e](l)&&k.n[l].f){var r=k.n[l].f;for(o=0,p=r.length;p>o;o++)if(r[o]==b){r.splice(o,1);break}!r.length&&delete k.n[l].f}}else{delete k.f;for(l in k.n)k.n[e](l)&&k.n[l].f&&delete k.n[l].f}k=k.n}}},m.once=function(a,b){var c=function(){return m.unbind(a,c),b.apply(this,arguments)};return m.on(a,c)},m.version=d,m.toString=function(){return"You are running Eve "+d},"undefined"!=typeof module&&module.exports?module.exports=m:"function"==typeof define&&define.amd?define("eve",[],function(){return m}):a.eve=m}(this),function(a,b){if("function"==typeof define&&define.amd)define(["eve"],function(c){return b(a,c)});else if("undefined"!=typeof exports){var c=require("eve");module.exports=b(a,c)}else b(a,a.eve)}(window||this,function(a,b){var c=function(b){var c={},d=a.requestAnimationFrame||a.webkitRequestAnimationFrame||a.mozRequestAnimationFrame||a.oRequestAnimationFrame||a.msRequestAnimationFrame||function(a){setTimeout(a,16)},e=Array.isArray||function(a){return a instanceof Array||"[object Array]"==Object.prototype.toString.call(a)},f=0,g="M"+(+new Date).toString(36),h=function(){return g+(f++).toString(36)},i=Date.now||function(){return+new Date},j=function(a){var b=this;if(null==a)return b.s;var c=b.s-a;b.b+=b.dur*c,b.B+=b.dur*c,b.s=a},k=function(a){var b=this;return null==a?b.spd:void(b.spd=a)},l=function(a){var b=this;return null==a?b.dur:(b.s=b.s*a/b.dur,void(b.dur=a))},m=function(){var a=this;delete c[a.id],a.update(),b("mina.stop."+a.id,a)},n=function(){var a=this;a.pdif||(delete c[a.id],a.update(),a.pdif=a.get()-a.b)},o=function(){var a=this;a.pdif&&(a.b=a.get()-a.pdif,delete a.pdif,c[a.id]=a)},p=function(){var a,b=this;if(e(b.start)){a=[];for(var c=0,d=b.start.length;d>c;c++)a[c]=+b.start[c]+(b.end[c]-b.start[c])*b.easing(b.s)}else a=+b.start+(b.end-b.start)*b.easing(b.s);b.set(a)},q=function(){var a=0;for(var e in c)if(c.hasOwnProperty(e)){var f=c[e],g=f.get();a++,f.s=(g-f.b)/(f.dur/f.spd),f.s>=1&&(delete c[e],f.s=1,a--,function(a){setTimeout(function(){b("mina.finish."+a.id,a)})}(f)),f.update()}a&&d(q)},r=function(a,b,e,f,g,i,s){var t={id:h(),start:a,end:b,b:e,s:0,dur:f-e,spd:1,get:g,set:i,easing:s||r.linear,status:j,speed:k,duration:l,stop:m,pause:n,resume:o,update:p};c[t.id]=t;var u,v=0;for(u in c)if(c.hasOwnProperty(u)&&(v++,2==v))break;return 1==v&&d(q),t};return r.time=i,r.getById=function(a){return c[a]||null},r.linear=function(a){return a},r.easeout=function(a){return Math.pow(a,1.7)},r.easein=function(a){return Math.pow(a,.48)},r.easeinout=function(a){if(1==a)return 1;if(0==a)return 0;var b=.48-a/1.04,c=Math.sqrt(.1734+b*b),d=c-b,e=Math.pow(Math.abs(d),1/3)*(0>d?-1:1),f=-c-b,g=Math.pow(Math.abs(f),1/3)*(0>f?-1:1),h=e+g+.5;return 3*(1-h)*h*h+h*h*h},r.backin=function(a){if(1==a)return 1;var b=1.70158;return a*a*((b+1)*a-b)},r.backout=function(a){if(0==a)return 0;a-=1;var b=1.70158;return a*a*((b+1)*a+b)+1},r.elastic=function(a){return a==!!a?a:Math.pow(2,-10*a)*Math.sin(2*(a-.075)*Math.PI/.3)+1},r.bounce=function(a){var b,c=7.5625,d=2.75;return 1/d>a?b=c*a*a:2/d>a?(a-=1.5/d,b=c*a*a+.75):2.5/d>a?(a-=2.25/d,b=c*a*a+.9375):(a-=2.625/d,b=c*a*a+.984375),b},a.mina=r,r}("undefined"==typeof b?function(){}:b),d=function(a){function c(a,b){if(a){if(a.nodeType)return w(a);if(e(a,"array")&&c.set)return c.set.apply(c,a);if(a instanceof s)return a;if(null==b)return a=y.doc.querySelector(String(a)),w(a)}return a=null==a?"100%":a,b=null==b?"100%":b,new v(a,b)}function d(a,b){if(b){if("#text"==a&&(a=y.doc.createTextNode(b.text||b["#text"]||"")),"#comment"==a&&(a=y.doc.createComment(b.text||b["#text"]||"")),"string"==typeof a&&(a=d(a)),"string"==typeof b)return 1==a.nodeType?"xlink:"==b.substring(0,6)?a.getAttributeNS(T,b.substring(6)):"xml:"==b.substring(0,4)?a.getAttributeNS(U,b.substring(4)):a.getAttribute(b):"text"==b?a.nodeValue:null;if(1==a.nodeType){for(var c in b)if(b[z](c)){var e=A(b[c]);e?"xlink:"==c.substring(0,6)?a.setAttributeNS(T,c.substring(6),e):"xml:"==c.substring(0,4)?a.setAttributeNS(U,c.substring(4),e):a.setAttribute(c,e):a.removeAttribute(c)}}else"text"in b&&(a.nodeValue=b.text)}else a=y.doc.createElementNS(U,a);return a}function e(a,b){return b=A.prototype.toLowerCase.call(b),"finite"==b?isFinite(a):"array"==b&&(a instanceof Array||Array.isArray&&Array.isArray(a))?!0:"null"==b&&null===a||b==typeof a&&null!==a||"object"==b&&a===Object(a)||J.call(a).slice(8,-1).toLowerCase()==b}function f(a){if("function"==typeof a||Object(a)!==a)return a;var b=new a.constructor;for(var c in a)a[z](c)&&(b[c]=f(a[c]));return b}function h(a,b){for(var c=0,d=a.length;d>c;c++)if(a[c]===b)return a.push(a.splice(c,1)[0])}function i(a,b,c){function d(){var e=Array.prototype.slice.call(arguments,0),f=e.join("␀"),g=d.cache=d.cache||{},i=d.count=d.count||[];return g[z](f)?(h(i,f),c?c(g[f]):g[f]):(i.length>=1e3&&delete g[i.shift()],i.push(f),g[f]=a.apply(b,e),c?c(g[f]):g[f])}return d}function j(a,b,c,d,e,f){if(null==e){var g=a-c,h=b-d;return g||h?(180+180*D.atan2(-h,-g)/H+360)%360:0}return j(a,b,e,f)-j(c,d,e,f)}function k(a){return a%360*H/180}function l(a){return 180*a/H%360}function m(a){var b=[];return a=a.replace(/(?:^|\s)(\w+)\(([^)]+)\)/g,function(a,c,d){return d=d.split(/\s*,\s*|\s+/),"rotate"==c&&1==d.length&&d.push(0,0),"scale"==c&&(d.length>2?d=d.slice(0,2):2==d.length&&d.push(0,0),1==d.length&&d.push(d[0],0,0)),b.push("skewX"==c?["m",1,0,D.tan(k(d[0])),1,0,0]:"skewY"==c?["m",1,D.tan(k(d[0])),0,1,0,0]:[c.charAt(0)].concat(d)),a}),b}function n(a,b){var d=ab(a),e=new c.Matrix;if(d)for(var f=0,g=d.length;g>f;f++){var h,i,j,k,l,m=d[f],n=m.length,o=A(m[0]).toLowerCase(),p=m[0]!=o,q=p?e.invert():0;"t"==o&&2==n?e.translate(m[1],0):"t"==o&&3==n?p?(h=q.x(0,0),i=q.y(0,0),j=q.x(m[1],m[2]),k=q.y(m[1],m[2]),e.translate(j-h,k-i)):e.translate(m[1],m[2]):"r"==o?2==n?(l=l||b,e.rotate(m[1],l.x+l.width/2,l.y+l.height/2)):4==n&&(p?(j=q.x(m[2],m[3]),k=q.y(m[2],m[3]),e.rotate(m[1],j,k)):e.rotate(m[1],m[2],m[3])):"s"==o?2==n||3==n?(l=l||b,e.scale(m[1],m[n-1],l.x+l.width/2,l.y+l.height/2)):4==n?p?(j=q.x(m[2],m[3]),k=q.y(m[2],m[3]),e.scale(m[1],m[1],j,k)):e.scale(m[1],m[1],m[2],m[3]):5==n&&(p?(j=q.x(m[3],m[4]),k=q.y(m[3],m[4]),e.scale(m[1],m[2],j,k)):e.scale(m[1],m[2],m[3],m[4])):"m"==o&&7==n&&e.add(m[1],m[2],m[3],m[4],m[5],m[6])}return e}function o(a){var b=a.node.ownerSVGElement&&w(a.node.ownerSVGElement)||a.node.parentNode&&w(a.node.parentNode)||c.select("svg")||c(0,0),d=b.select("defs"),e=null==d?!1:d.node;return e||(e=u("defs",b.node).node),e}function p(a){return a.node.ownerSVGElement&&w(a.node.ownerSVGElement)||c.select("svg")}function q(a,b,c){function e(a){if(null==a)return I;if(a==+a)return a;d(j,{width:a});try{return j.getBBox().width}catch(b){return 0}}function f(a){if(null==a)return I;if(a==+a)return a;d(j,{height:a});try{return j.getBBox().height}catch(b){return 0}}function g(d,e){null==b?i[d]=e(a.attr(d)||0):d==b&&(i=e(null==c?a.attr(d)||0:c))}var h=p(a).node,i={},j=h.querySelector(".svg---mgr");switch(j||(j=d("rect"),d(j,{x:-9e9,y:-9e9,width:10,height:10,"class":"svg---mgr",fill:"none"}),h.appendChild(j)),a.type){case"rect":g("rx",e),g("ry",f);case"image":g("width",e),g("height",f);case"text":g("x",e),g("y",f);break;case"circle":g("cx",e),g("cy",f),g("r",e);break;case"ellipse":g("cx",e),g("cy",f),g("rx",e),g("ry",f);break;case"line":g("x1",e),g("x2",e),g("y1",f),g("y2",f);break;case"marker":g("refX",e),g("markerWidth",e),g("refY",f),g("markerHeight",f);break;case"radialGradient":g("fx",e),g("fy",f);break;case"tspan":g("dx",e),g("dy",f);break;default:g(b,e)}return h.removeChild(j),i}function r(a){e(a,"array")||(a=Array.prototype.slice.call(arguments,0));for(var b=0,c=0,d=this.node;this[b];)delete this[b++];for(b=0;b<a.length;b++)"set"==a[b].type?a[b].forEach(function(a){d.appendChild(a.node)}):d.appendChild(a[b].node);var f=d.childNodes;for(b=0;b<f.length;b++)this[c++]=w(f[b]);return this}function s(a){if(a.snap in V)return V[a.snap];var b;try{b=a.ownerSVGElement}catch(c){}this.node=a,b&&(this.paper=new v(b)),this.type=a.tagName||a.nodeName;var d=this.id=S(this);if(this.anims={},this._={transform:[]},a.snap=d,V[d]=this,"g"==this.type&&(this.add=r),this.type in{g:1,mask:1,pattern:1,symbol:1})for(var e in v.prototype)v.prototype[z](e)&&(this[e]=v.prototype[e])}function t(a){this.node=a}function u(a,b){var c=d(a);b.appendChild(c);var e=w(c);return e}function v(a,b){var c,e,f,g=v.prototype;if(a&&"svg"==a.tagName){if(a.snap in V)return V[a.snap];var h=a.ownerDocument;c=new s(a),e=a.getElementsByTagName("desc")[0],f=a.getElementsByTagName("defs")[0],e||(e=d("desc"),e.appendChild(h.createTextNode("Created with Snap")),c.node.appendChild(e)),f||(f=d("defs"),c.node.appendChild(f)),c.defs=f;for(var i in g)g[z](i)&&(c[i]=g[i]);c.paper=c.root=c}else c=u("svg",y.doc.body),d(c.node,{height:b,version:1.1,width:a,xmlns:U});return c}function w(a){return a?a instanceof s||a instanceof t?a:a.tagName&&"svg"==a.tagName.toLowerCase()?new v(a):a.tagName&&"object"==a.tagName.toLowerCase()&&"image/svg+xml"==a.type?new v(a.contentDocument.getElementsByTagName("svg")[0]):new s(a):a}function x(a,b){for(var c=0,d=a.length;d>c;c++){var e={type:a[c].type,attr:a[c].attr()},f=a[c].children();b.push(e),f.length&&x(f,e.childNodes=[])}}c.version="0.4.0",c.toString=function(){return"Snap v"+this.version},c._={};var y={win:a.window,doc:a.window.document};c._.glob=y;{var z="hasOwnProperty",A=String,B=parseFloat,C=parseInt,D=Math,E=D.max,F=D.min,G=D.abs,H=(D.pow,D.PI),I=(D.round,""),J=Object.prototype.toString,K=/^\s*((#[a-f\d]{6})|(#[a-f\d]{3})|rgba?\(\s*([\d\.]+%?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+%?(?:\s*,\s*[\d\.]+%?)?)\s*\)|hsba?\(\s*([\d\.]+(?:deg|\xb0|%)?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+(?:%?\s*,\s*[\d\.]+)?%?)\s*\)|hsla?\(\s*([\d\.]+(?:deg|\xb0|%)?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+(?:%?\s*,\s*[\d\.]+)?%?)\s*\))\s*$/i,L=(c._.separator=/[,\s]+/,/[\s]*,[\s]*/),M={hs:1,rg:1},N=/([a-z])[\s,]*((-?\d*\.?\d*(?:e[\-+]?\d+)?[\s]*,?[\s]*)+)/gi,O=/([rstm])[\s,]*((-?\d*\.?\d*(?:e[\-+]?\d+)?[\s]*,?[\s]*)+)/gi,P=/(-?\d*\.?\d*(?:e[\-+]?\\d+)?)[\s]*,?[\s]*/gi,Q=0,R="S"+(+new Date).toString(36),S=function(a){return(a&&a.type?a.type:I)+R+(Q++).toString(36)},T="http://www.w3.org/1999/xlink",U="http://www.w3.org/2000/svg",V={};c.url=function(a){return"url('#"+a+"')"}}c._.$=d,c._.id=S,c.format=function(){var a=/\{([^\}]+)\}/g,b=/(?:(?:^|\.)(.+?)(?=\[|\.|$|\()|\[('|")(.+?)\2\])(\(\))?/g,c=function(a,c,d){var e=d;return c.replace(b,function(a,b,c,d,f){b=b||d,e&&(b in e&&(e=e[b]),"function"==typeof e&&f&&(e=e()))}),e=(null==e||e==d?a:e)+""};return function(b,d){return A(b).replace(a,function(a,b){return c(a,b,d)})}}(),c._.clone=f,c._.cacher=i,c.rad=k,c.deg=l,c.sin=function(a){return D.sin(c.rad(a))},c.tan=function(a){return D.tan(c.rad(a))},c.cos=function(a){return D.cos(c.rad(a))},c.asin=function(a){return c.deg(D.asin(a))},c.acos=function(a){return c.deg(D.acos(a))},c.atan=function(a){return c.deg(D.atan(a))},c.atan2=function(a){return c.deg(D.atan2(a))},c.angle=j,c.len=function(a,b,d,e){return Math.sqrt(c.len2(a,b,d,e))},c.len2=function(a,b,c,d){return(a-c)*(a-c)+(b-d)*(b-d)},c.closestPoint=function(a,b,c){function d(a){var d=a.x-b,e=a.y-c;return d*d+e*e}for(var e,f,g,h,i=a.node,j=i.getTotalLength(),k=j/i.pathSegList.numberOfItems*.125,l=1/0,m=0;j>=m;m+=k)(h=d(g=i.getPointAtLength(m)))<l&&(e=g,f=m,l=h);for(k*=.5;k>.5;){var n,o,p,q,r,s;(p=f-k)>=0&&(r=d(n=i.getPointAtLength(p)))<l?(e=n,f=p,l=r):(q=f+k)<=j&&(s=d(o=i.getPointAtLength(q)))<l?(e=o,f=q,l=s):k*=.5}return e={x:e.x,y:e.y,length:f,distance:Math.sqrt(l)}},c.is=e,c.snapTo=function(a,b,c){if(c=e(c,"finite")?c:10,e(a,"array")){for(var d=a.length;d--;)if(G(a[d]-b)<=c)return a[d]}else{a=+a;var f=b%a;if(c>f)return b-f;if(f>a-c)return b-f+a}return b},c.getRGB=i(function(a){if(!a||(a=A(a)).indexOf("-")+1)return{r:-1,g:-1,b:-1,hex:"none",error:1,toString:Z};if("none"==a)return{r:-1,g:-1,b:-1,hex:"none",toString:Z};if(!(M[z](a.toLowerCase().substring(0,2))||"#"==a.charAt())&&(a=W(a)),!a)return{r:-1,g:-1,b:-1,hex:"none",error:1,toString:Z};var b,d,f,g,h,i,j=a.match(K);return j?(j[2]&&(f=C(j[2].substring(5),16),d=C(j[2].substring(3,5),16),b=C(j[2].substring(1,3),16)),j[3]&&(f=C((h=j[3].charAt(3))+h,16),d=C((h=j[3].charAt(2))+h,16),b=C((h=j[3].charAt(1))+h,16)),j[4]&&(i=j[4].split(L),b=B(i[0]),"%"==i[0].slice(-1)&&(b*=2.55),d=B(i[1]),"%"==i[1].slice(-1)&&(d*=2.55),f=B(i[2]),"%"==i[2].slice(-1)&&(f*=2.55),"rgba"==j[1].toLowerCase().slice(0,4)&&(g=B(i[3])),i[3]&&"%"==i[3].slice(-1)&&(g/=100)),j[5]?(i=j[5].split(L),b=B(i[0]),"%"==i[0].slice(-1)&&(b/=100),d=B(i[1]),"%"==i[1].slice(-1)&&(d/=100),f=B(i[2]),"%"==i[2].slice(-1)&&(f/=100),("deg"==i[0].slice(-3)||"°"==i[0].slice(-1))&&(b/=360),"hsba"==j[1].toLowerCase().slice(0,4)&&(g=B(i[3])),i[3]&&"%"==i[3].slice(-1)&&(g/=100),c.hsb2rgb(b,d,f,g)):j[6]?(i=j[6].split(L),b=B(i[0]),"%"==i[0].slice(-1)&&(b/=100),d=B(i[1]),"%"==i[1].slice(-1)&&(d/=100),f=B(i[2]),"%"==i[2].slice(-1)&&(f/=100),("deg"==i[0].slice(-3)||"°"==i[0].slice(-1))&&(b/=360),"hsla"==j[1].toLowerCase().slice(0,4)&&(g=B(i[3])),i[3]&&"%"==i[3].slice(-1)&&(g/=100),c.hsl2rgb(b,d,f,g)):(b=F(D.round(b),255),d=F(D.round(d),255),f=F(D.round(f),255),g=F(E(g,0),1),j={r:b,g:d,b:f,toString:Z},j.hex="#"+(16777216|f|d<<8|b<<16).toString(16).slice(1),j.opacity=e(g,"finite")?g:1,j)):{r:-1,g:-1,b:-1,hex:"none",error:1,toString:Z}},c),c.hsb=i(function(a,b,d){return c.hsb2rgb(a,b,d).hex}),c.hsl=i(function(a,b,d){return c.hsl2rgb(a,b,d).hex}),c.rgb=i(function(a,b,c,d){if(e(d,"finite")){var f=D.round;return"rgba("+[f(a),f(b),f(c),+d.toFixed(2)]+")"}return"#"+(16777216|c|b<<8|a<<16).toString(16).slice(1)});var W=function(a){var b=y.doc.getElementsByTagName("head")[0]||y.doc.getElementsByTagName("svg")[0],c="rgb(255, 0, 0)";return(W=i(function(a){if("red"==a.toLowerCase())return c;b.style.color=c,b.style.color=a;var d=y.doc.defaultView.getComputedStyle(b,I).getPropertyValue("color");return d==c?null:d}))(a)},X=function(){return"hsb("+[this.h,this.s,this.b]+")"},Y=function(){return"hsl("+[this.h,this.s,this.l]+")"},Z=function(){return 1==this.opacity||null==this.opacity?this.hex:"rgba("+[this.r,this.g,this.b,this.opacity]+")"},$=function(a,b,d){if(null==b&&e(a,"object")&&"r"in a&&"g"in a&&"b"in a&&(d=a.b,b=a.g,a=a.r),null==b&&e(a,string)){var f=c.getRGB(a);a=f.r,b=f.g,d=f.b}return(a>1||b>1||d>1)&&(a/=255,b/=255,d/=255),[a,b,d]},_=function(a,b,d,f){a=D.round(255*a),b=D.round(255*b),d=D.round(255*d);var g={r:a,g:b,b:d,opacity:e(f,"finite")?f:1,hex:c.rgb(a,b,d),toString:Z};return e(f,"finite")&&(g.opacity=f),g};c.color=function(a){var b;return e(a,"object")&&"h"in a&&"s"in a&&"b"in a?(b=c.hsb2rgb(a),a.r=b.r,a.g=b.g,a.b=b.b,a.opacity=1,a.hex=b.hex):e(a,"object")&&"h"in a&&"s"in a&&"l"in a?(b=c.hsl2rgb(a),a.r=b.r,a.g=b.g,a.b=b.b,a.opacity=1,a.hex=b.hex):(e(a,"string")&&(a=c.getRGB(a)),e(a,"object")&&"r"in a&&"g"in a&&"b"in a&&!("error"in a)?(b=c.rgb2hsl(a),a.h=b.h,a.s=b.s,a.l=b.l,b=c.rgb2hsb(a),a.v=b.b):(a={hex:"none"},a.r=a.g=a.b=a.h=a.s=a.v=a.l=-1,a.error=1)),a.toString=Z,a},c.hsb2rgb=function(a,b,c,d){e(a,"object")&&"h"in a&&"s"in a&&"b"in a&&(c=a.b,b=a.s,d=a.o,a=a.h),a*=360;var f,g,h,i,j;return a=a%360/60,j=c*b,i=j*(1-G(a%2-1)),f=g=h=c-j,a=~~a,f+=[j,i,0,0,i,j][a],g+=[i,j,j,i,0,0][a],h+=[0,0,i,j,j,i][a],_(f,g,h,d)},c.hsl2rgb=function(a,b,c,d){e(a,"object")&&"h"in a&&"s"in a&&"l"in a&&(c=a.l,b=a.s,a=a.h),(a>1||b>1||c>1)&&(a/=360,b/=100,c/=100),a*=360;var f,g,h,i,j;return a=a%360/60,j=2*b*(.5>c?c:1-c),i=j*(1-G(a%2-1)),f=g=h=c-j/2,a=~~a,f+=[j,i,0,0,i,j][a],g+=[i,j,j,i,0,0][a],h+=[0,0,i,j,j,i][a],_(f,g,h,d)},c.rgb2hsb=function(a,b,c){c=$(a,b,c),a=c[0],b=c[1],c=c[2];var d,e,f,g;return f=E(a,b,c),g=f-F(a,b,c),d=0==g?null:f==a?(b-c)/g:f==b?(c-a)/g+2:(a-b)/g+4,d=(d+360)%6*60/360,e=0==g?0:g/f,{h:d,s:e,b:f,toString:X}},c.rgb2hsl=function(a,b,c){c=$(a,b,c),a=c[0],b=c[1],c=c[2];var d,e,f,g,h,i;return g=E(a,b,c),h=F(a,b,c),i=g-h,d=0==i?null:g==a?(b-c)/i:g==b?(c-a)/i+2:(a-b)/i+4,d=(d+360)%6*60/360,f=(g+h)/2,e=0==i?0:.5>f?i/(2*f):i/(2-2*f),{h:d,s:e,l:f,toString:Y}},c.parsePathString=function(a){if(!a)return null;var b=c.path(a);if(b.arr)return c.path.clone(b.arr);var d={a:7,c:6,o:2,h:1,l:2,m:2,r:4,q:4,s:4,t:2,v:1,u:3,z:0},f=[];return e(a,"array")&&e(a[0],"array")&&(f=c.path.clone(a)),f.length||A(a).replace(N,function(a,b,c){var e=[],g=b.toLowerCase();if(c.replace(P,function(a,b){b&&e.push(+b)}),"m"==g&&e.length>2&&(f.push([b].concat(e.splice(0,2))),g="l",b="m"==b?"l":"L"),"o"==g&&1==e.length&&f.push([b,e[0]]),"r"==g)f.push([b].concat(e));else for(;e.length>=d[g]&&(f.push([b].concat(e.splice(0,d[g]))),d[g]););}),f.toString=c.path.toString,b.arr=c.path.clone(f),f};var ab=c.parseTransformString=function(a){if(!a)return null;var b=[];return e(a,"array")&&e(a[0],"array")&&(b=c.path.clone(a)),b.length||A(a).replace(O,function(a,c,d){{var e=[];c.toLowerCase()}d.replace(P,function(a,b){b&&e.push(+b)}),b.push([c].concat(e))}),b.toString=c.path.toString,b};c._.svgTransform2string=m,c._.rgTransform=/^[a-z][\s]*-?\.?\d/i,c._.transform2matrix=n,c._unit2px=q;y.doc.contains||y.doc.compareDocumentPosition?function(a,b){var c=9==a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a==d||!(!d||1!=d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)for(;b;)if(b=b.parentNode,b==a)return!0;return!1};c._.getSomeDefs=o,c._.getSomeSVG=p,c.select=function(a){return a=A(a).replace(/([^\\]):/g,"$1\\:"),w(y.doc.querySelector(a))},c.selectAll=function(a){for(var b=y.doc.querySelectorAll(a),d=(c.set||Array)(),e=0;e<b.length;e++)d.push(w(b[e]));return d},setInterval(function(){for(var a in V)if(V[z](a)){var b=V[a],c=b.node;("svg"!=b.type&&!c.ownerSVGElement||"svg"==b.type&&(!c.parentNode||"ownerSVGElement"in c.parentNode&&!c.ownerSVGElement))&&delete V[a]}},1e4),s.prototype.attr=function(a,c){var d=this,f=d.node;if(!a){if(1!=f.nodeType)return{text:f.nodeValue};for(var g=f.attributes,h={},i=0,j=g.length;j>i;i++)h[g[i].nodeName]=g[i].nodeValue;return h}if(e(a,"string")){if(!(arguments.length>1))return b("snap.util.getattr."+a,d).firstDefined();var k={};k[a]=c,a=k}for(var l in a)a[z](l)&&b("snap.util.attr."+l,d,a[l]);return d},c.parse=function(a){var b=y.doc.createDocumentFragment(),c=!0,d=y.doc.createElement("div");if(a=A(a),a.match(/^\s*<\s*svg(?:\s|>)/)||(a="<svg>"+a+"</svg>",c=!1),d.innerHTML=a,a=d.getElementsByTagName("svg")[0])if(c)b=a;else for(;a.firstChild;)b.appendChild(a.firstChild);return new t(b)},c.fragment=function(){for(var a=Array.prototype.slice.call(arguments,0),b=y.doc.createDocumentFragment(),d=0,e=a.length;e>d;d++){var f=a[d];f.node&&f.node.nodeType&&b.appendChild(f.node),f.nodeType&&b.appendChild(f),"string"==typeof f&&b.appendChild(c.parse(f).node)}return new t(b)},c._.make=u,c._.wrap=w,v.prototype.el=function(a,b){var c=u(a,this.node);return b&&c.attr(b),c},s.prototype.children=function(){for(var a=[],b=this.node.childNodes,d=0,e=b.length;e>d;d++)a[d]=c(b[d]);return a},s.prototype.toJSON=function(){var a=[];return x([this],a),a[0]},b.on("snap.util.getattr",function(){var a=b.nt();a=a.substring(a.lastIndexOf(".")+1);var c=a.replace(/[A-Z]/g,function(a){return"-"+a.toLowerCase()});return bb[z](c)?this.node.ownerDocument.defaultView.getComputedStyle(this.node,null).getPropertyValue(c):d(this.node,a)});var bb={"alignment-baseline":0,"baseline-shift":0,clip:0,"clip-path":0,"clip-rule":0,color:0,"color-interpolation":0,"color-interpolation-filters":0,"color-profile":0,"color-rendering":0,cursor:0,direction:0,display:0,"dominant-baseline":0,"enable-background":0,fill:0,"fill-opacity":0,"fill-rule":0,filter:0,"flood-color":0,"flood-opacity":0,font:0,"font-family":0,"font-size":0,"font-size-adjust":0,"font-stretch":0,"font-style":0,"font-variant":0,"font-weight":0,"glyph-orientation-horizontal":0,"glyph-orientation-vertical":0,"image-rendering":0,kerning:0,"letter-spacing":0,"lighting-color":0,marker:0,"marker-end":0,"marker-mid":0,"marker-start":0,mask:0,opacity:0,overflow:0,"pointer-events":0,"shape-rendering":0,"stop-color":0,"stop-opacity":0,stroke:0,"stroke-dasharray":0,"stroke-dashoffset":0,"stroke-linecap":0,"stroke-linejoin":0,"stroke-miterlimit":0,"stroke-opacity":0,"stroke-width":0,"text-anchor":0,"text-decoration":0,"text-rendering":0,"unicode-bidi":0,visibility:0,"word-spacing":0,"writing-mode":0};b.on("snap.util.attr",function(a){var c=b.nt(),e={};c=c.substring(c.lastIndexOf(".")+1),e[c]=a;var f=c.replace(/-(\w)/gi,function(a,b){return b.toUpperCase()}),g=c.replace(/[A-Z]/g,function(a){return"-"+a.toLowerCase()});bb[z](g)?this.node.style[f]=null==a?I:a:d(this.node,e)}),function(){}(v.prototype),c.ajax=function(a,c,d,f){var g=new XMLHttpRequest,h=S();if(g){if(e(c,"function"))f=d,d=c,c=null;else if(e(c,"object")){var i=[];for(var j in c)c.hasOwnProperty(j)&&i.push(encodeURIComponent(j)+"="+encodeURIComponent(c[j]));c=i.join("&")}return g.open(c?"POST":"GET",a,!0),c&&(g.setRequestHeader("X-Requested-With","XMLHttpRequest"),g.setRequestHeader("Content-type","application/x-www-form-urlencoded")),d&&(b.once("snap.ajax."+h+".0",d),b.once("snap.ajax."+h+".200",d),b.once("snap.ajax."+h+".304",d)),g.onreadystatechange=function(){4==g.readyState&&b("snap.ajax."+h+"."+g.status,f,g)},4==g.readyState?g:(g.send(c),g)}},c.load=function(a,b,d){c.ajax(a,function(a){var e=c.parse(a.responseText);d?b.call(d,e):b(e)})};var cb=function(a){var b=a.getBoundingClientRect(),c=a.ownerDocument,d=c.body,e=c.documentElement,f=e.clientTop||d.clientTop||0,h=e.clientLeft||d.clientLeft||0,i=b.top+(g.win.pageYOffset||e.scrollTop||d.scrollTop)-f,j=b.left+(g.win.pageXOffset||e.scrollLeft||d.scrollLeft)-h;return{y:i,x:j}};return c.getElementByPoint=function(a,b){var c=this,d=(c.canvas,y.doc.elementFromPoint(a,b));if(y.win.opera&&"svg"==d.tagName){var e=cb(d),f=d.createSVGRect();f.x=a-e.x,f.y=b-e.y,f.width=f.height=1;var g=d.getIntersectionList(f,null);g.length&&(d=g[g.length-1])}return d?w(d):null},c.plugin=function(a){a(c,s,v,y,t)},y.win.Snap=c,c}(a||this);return d.plugin(function(d,e,f,g,h){function i(a,b){if(null==b){var c=!0;if(b=a.node.getAttribute("linearGradient"==a.type||"radialGradient"==a.type?"gradientTransform":"pattern"==a.type?"patternTransform":"transform"),!b)return new d.Matrix;b=d._.svgTransform2string(b)}else b=d._.rgTransform.test(b)?o(b).replace(/\.{3}|\u2026/g,a._.transform||""):d._.svgTransform2string(b),n(b,"array")&&(b=d.path?d.path.toString.call(b):o(b)),a._.transform=b;var e=d._.transform2matrix(b,a.getBBox(1));return c?e:void(a.matrix=e)}function j(a){function b(a,b){var c=q(a.node,b);c=c&&c.match(f),c=c&&c[2],c&&"#"==c.charAt()&&(c=c.substring(1),c&&(h[c]=(h[c]||[]).concat(function(c){var d={};d[b]=URL(c),q(a.node,d)})))}function c(a){var b=q(a.node,"xlink:href");b&&"#"==b.charAt()&&(b=b.substring(1),b&&(h[b]=(h[b]||[]).concat(function(b){a.attr("xlink:href","#"+b)})))}for(var d,e=a.selectAll("*"),f=/^\s*url\(("|'|)(.*)\1\)\s*$/,g=[],h={},i=0,j=e.length;j>i;i++){d=e[i],b(d,"fill"),b(d,"stroke"),b(d,"filter"),b(d,"mask"),b(d,"clip-path"),c(d);var k=q(d.node,"id");k&&(q(d.node,{id:d.id}),g.push({old:k,id:d.id}))}for(i=0,j=g.length;j>i;i++){var l=h[g[i].old];if(l)for(var m=0,n=l.length;n>m;m++)l[m](g[i].id)}}function k(a,b,c){return function(d){var e=d.slice(a,b);return 1==e.length&&(e=e[0]),c?c(e):e}}function l(a){return function(){var b=a?"<"+this.type:"",c=this.node.attributes,d=this.node.childNodes;if(a)for(var e=0,f=c.length;f>e;e++)b+=" "+c[e].name+'="'+c[e].value.replace(/"/g,'\\"')+'"';if(d.length){for(a&&(b+=">"),e=0,f=d.length;f>e;e++)3==d[e].nodeType?b+=d[e].nodeValue:1==d[e].nodeType&&(b+=u(d[e]).toString());a&&(b+="</"+this.type+">")}else a&&(b+="/>");return b}}var m=e.prototype,n=d.is,o=String,p=d._unit2px,q=d._.$,r=d._.make,s=d._.getSomeDefs,t="hasOwnProperty",u=d._.wrap;m.getBBox=function(a){if(!d.Matrix||!d.path)return this.node.getBBox();var b=this,c=new d.Matrix;if(b.removed)return d._.box();for(;"use"==b.type;)if(a||(c=c.add(b.transform().localMatrix.translate(b.attr("x")||0,b.attr("y")||0))),b.original)b=b.original;else{var e=b.attr("xlink:href");b=b.original=b.node.ownerDocument.getElementById(e.substring(e.indexOf("#")+1))}var f=b._,g=d.path.get[b.type]||d.path.get.deflt;try{return a?(f.bboxwt=g?d.path.getBBox(b.realPath=g(b)):d._.box(b.node.getBBox()),d._.box(f.bboxwt)):(b.realPath=g(b),b.matrix=b.transform().localMatrix,f.bbox=d.path.getBBox(d.path.map(b.realPath,c.add(b.matrix))),d._.box(f.bbox))}catch(h){return d._.box()}};var v=function(){return this.string};m.transform=function(a){var b=this._;if(null==a){for(var c,e=this,f=new d.Matrix(this.node.getCTM()),g=i(this),h=[g],j=new d.Matrix,k=g.toTransformString(),l=o(g)==o(this.matrix)?o(b.transform):k;"svg"!=e.type&&(e=e.parent());)h.push(i(e));for(c=h.length;c--;)j.add(h[c]);return{string:l,globalMatrix:f,totalMatrix:j,localMatrix:g,diffMatrix:f.clone().add(g.invert()),global:f.toTransformString(),total:j.toTransformString(),local:k,toString:v}}return a instanceof d.Matrix?(this.matrix=a,this._.transform=a.toTransformString()):i(this,a),this.node&&("linearGradient"==this.type||"radialGradient"==this.type?q(this.node,{gradientTransform:this.matrix}):"pattern"==this.type?q(this.node,{patternTransform:this.matrix}):q(this.node,{transform:this.matrix})),this},m.parent=function(){return u(this.node.parentNode)},m.append=m.add=function(a){if(a){if("set"==a.type){var b=this;return a.forEach(function(a){b.add(a)}),this}a=u(a),this.node.appendChild(a.node),a.paper=this.paper}return this},m.appendTo=function(a){return a&&(a=u(a),a.append(this)),this},m.prepend=function(a){if(a){if("set"==a.type){var b,c=this;return a.forEach(function(a){b?b.after(a):c.prepend(a),b=a}),this}a=u(a);var d=a.parent();this.node.insertBefore(a.node,this.node.firstChild),this.add&&this.add(),a.paper=this.paper,this.parent()&&this.parent().add(),d&&d.add()}return this},m.prependTo=function(a){return a=u(a),a.prepend(this),this},m.before=function(a){if("set"==a.type){var b=this;return a.forEach(function(a){var c=a.parent();b.node.parentNode.insertBefore(a.node,b.node),c&&c.add()}),this.parent().add(),this}a=u(a);var c=a.parent();return this.node.parentNode.insertBefore(a.node,this.node),this.parent()&&this.parent().add(),c&&c.add(),a.paper=this.paper,this},m.after=function(a){a=u(a);var b=a.parent();return this.node.nextSibling?this.node.parentNode.insertBefore(a.node,this.node.nextSibling):this.node.parentNode.appendChild(a.node),this.parent()&&this.parent().add(),b&&b.add(),a.paper=this.paper,this},m.insertBefore=function(a){a=u(a);var b=this.parent();return a.node.parentNode.insertBefore(this.node,a.node),this.paper=a.paper,b&&b.add(),a.parent()&&a.parent().add(),this},m.insertAfter=function(a){a=u(a);var b=this.parent();return a.node.parentNode.insertBefore(this.node,a.node.nextSibling),this.paper=a.paper,b&&b.add(),a.parent()&&a.parent().add(),this},m.remove=function(){var a=this.parent();return this.node.parentNode&&this.node.parentNode.removeChild(this.node),delete this.paper,this.removed=!0,a&&a.add(),this},m.select=function(a){return u(this.node.querySelector(a))},m.selectAll=function(a){for(var b=this.node.querySelectorAll(a),c=(d.set||Array)(),e=0;e<b.length;e++)c.push(u(b[e]));return c},m.asPX=function(a,b){return null==b&&(b=this.attr(a)),+p(this,a,b)},m.use=function(){var a,b=this.node.id;return b||(b=this.id,q(this.node,{id:b})),a="linearGradient"==this.type||"radialGradient"==this.type||"pattern"==this.type?r(this.type,this.node.parentNode):r("use",this.node.parentNode),q(a.node,{"xlink:href":"#"+b}),a.original=this,a},m.clone=function(){var a=u(this.node.cloneNode(!0));return q(a.node,"id")&&q(a.node,{id:a.id}),j(a),a.insertAfter(this),a},m.toDefs=function(){var a=s(this);return a.appendChild(this.node),this},m.pattern=m.toPattern=function(a,b,c,d){var e=r("pattern",s(this));return null==a&&(a=this.getBBox()),n(a,"object")&&"x"in a&&(b=a.y,c=a.width,d=a.height,a=a.x),q(e.node,{x:a,y:b,width:c,height:d,patternUnits:"userSpaceOnUse",id:e.id,viewBox:[a,b,c,d].join(" ")}),e.node.appendChild(this.node),e},m.marker=function(a,b,c,d,e,f){var g=r("marker",s(this));return null==a&&(a=this.getBBox()),n(a,"object")&&"x"in a&&(b=a.y,c=a.width,d=a.height,e=a.refX||a.cx,f=a.refY||a.cy,a=a.x),q(g.node,{viewBox:[a,b,c,d].join(" "),markerWidth:c,markerHeight:d,orient:"auto",refX:e||0,refY:f||0,id:g.id}),g.node.appendChild(this.node),g};var w=function(a,b,d,e){"function"!=typeof d||d.length||(e=d,d=c.linear),this.attr=a,this.dur=b,d&&(this.easing=d),e&&(this.callback=e)};d._.Animation=w,d.animation=function(a,b,c,d){return new w(a,b,c,d)},m.inAnim=function(){var a=this,b=[];for(var c in a.anims)a.anims[t](c)&&!function(a){b.push({anim:new w(a._attrs,a.dur,a.easing,a._callback),mina:a,curStatus:a.status(),status:function(b){return a.status(b)},stop:function(){a.stop()}})}(a.anims[c]);return b},d.animate=function(a,d,e,f,g,h){"function"!=typeof g||g.length||(h=g,g=c.linear);var i=c.time(),j=c(a,d,i,i+f,c.time,e,g);return h&&b.once("mina.finish."+j.id,h),j},m.stop=function(){for(var a=this.inAnim(),b=0,c=a.length;c>b;b++)a[b].stop();return this},m.animate=function(a,d,e,f){"function"!=typeof e||e.length||(f=e,e=c.linear),a instanceof w&&(f=a.callback,e=a.easing,d=a.dur,a=a.attr);var g,h,i,j,l=[],m=[],p={},q=this;for(var r in a)if(a[t](r)){q.equal?(j=q.equal(r,o(a[r])),g=j.from,h=j.to,i=j.f):(g=+q.attr(r),h=+a[r]);var s=n(g,"array")?g.length:1;p[r]=k(l.length,l.length+s,i),l=l.concat(g),m=m.concat(h)}var u=c.time(),v=c(l,m,u,u+d,c.time,function(a){var b={};for(var c in p)p[t](c)&&(b[c]=p[c](a));q.attr(b)},e);return q.anims[v.id]=v,v._attrs=a,v._callback=f,b("snap.animcreated."+q.id,v),b.once("mina.finish."+v.id,function(){delete q.anims[v.id],f&&f.call(q)}),b.once("mina.stop."+v.id,function(){delete q.anims[v.id]}),q};var x={};m.data=function(a,c){var e=x[this.id]=x[this.id]||{};if(0==arguments.length)return b("snap.data.get."+this.id,this,e,null),e;
20
+ if(1==arguments.length){if(d.is(a,"object")){for(var f in a)a[t](f)&&this.data(f,a[f]);return this}return b("snap.data.get."+this.id,this,e[a],a),e[a]}return e[a]=c,b("snap.data.set."+this.id,this,c,a),this},m.removeData=function(a){return null==a?x[this.id]={}:x[this.id]&&delete x[this.id][a],this},m.outerSVG=m.toString=l(1),m.innerSVG=l(),m.toDataURL=function(){if(a&&a.btoa){var b=this.getBBox(),c=d.format('<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="{width}" height="{height}" viewBox="{x} {y} {width} {height}">{contents}</svg>',{x:+b.x.toFixed(3),y:+b.y.toFixed(3),width:+b.width.toFixed(3),height:+b.height.toFixed(3),contents:this.outerSVG()});return"data:image/svg+xml;base64,"+btoa(unescape(encodeURIComponent(c)))}},h.prototype.select=m.select,h.prototype.selectAll=m.selectAll}),d.plugin(function(a){function b(a,b,d,e,f,g){return null==b&&"[object SVGMatrix]"==c.call(a)?(this.a=a.a,this.b=a.b,this.c=a.c,this.d=a.d,this.e=a.e,void(this.f=a.f)):void(null!=a?(this.a=+a,this.b=+b,this.c=+d,this.d=+e,this.e=+f,this.f=+g):(this.a=1,this.b=0,this.c=0,this.d=1,this.e=0,this.f=0))}var c=Object.prototype.toString,d=String,e=Math,f="";!function(c){function g(a){return a[0]*a[0]+a[1]*a[1]}function h(a){var b=e.sqrt(g(a));a[0]&&(a[0]/=b),a[1]&&(a[1]/=b)}c.add=function(a,c,d,e,f,g){var h,i,j,k,l=[[],[],[]],m=[[this.a,this.c,this.e],[this.b,this.d,this.f],[0,0,1]],n=[[a,d,f],[c,e,g],[0,0,1]];for(a&&a instanceof b&&(n=[[a.a,a.c,a.e],[a.b,a.d,a.f],[0,0,1]]),h=0;3>h;h++)for(i=0;3>i;i++){for(k=0,j=0;3>j;j++)k+=m[h][j]*n[j][i];l[h][i]=k}return this.a=l[0][0],this.b=l[1][0],this.c=l[0][1],this.d=l[1][1],this.e=l[0][2],this.f=l[1][2],this},c.invert=function(){var a=this,c=a.a*a.d-a.b*a.c;return new b(a.d/c,-a.b/c,-a.c/c,a.a/c,(a.c*a.f-a.d*a.e)/c,(a.b*a.e-a.a*a.f)/c)},c.clone=function(){return new b(this.a,this.b,this.c,this.d,this.e,this.f)},c.translate=function(a,b){return this.add(1,0,0,1,a,b)},c.scale=function(a,b,c,d){return null==b&&(b=a),(c||d)&&this.add(1,0,0,1,c,d),this.add(a,0,0,b,0,0),(c||d)&&this.add(1,0,0,1,-c,-d),this},c.rotate=function(b,c,d){b=a.rad(b),c=c||0,d=d||0;var f=+e.cos(b).toFixed(9),g=+e.sin(b).toFixed(9);return this.add(f,g,-g,f,c,d),this.add(1,0,0,1,-c,-d)},c.x=function(a,b){return a*this.a+b*this.c+this.e},c.y=function(a,b){return a*this.b+b*this.d+this.f},c.get=function(a){return+this[d.fromCharCode(97+a)].toFixed(4)},c.toString=function(){return"matrix("+[this.get(0),this.get(1),this.get(2),this.get(3),this.get(4),this.get(5)].join()+")"},c.offset=function(){return[this.e.toFixed(4),this.f.toFixed(4)]},c.determinant=function(){return this.a*this.d-this.b*this.c},c.split=function(){var b={};b.dx=this.e,b.dy=this.f;var c=[[this.a,this.c],[this.b,this.d]];b.scalex=e.sqrt(g(c[0])),h(c[0]),b.shear=c[0][0]*c[1][0]+c[0][1]*c[1][1],c[1]=[c[1][0]-c[0][0]*b.shear,c[1][1]-c[0][1]*b.shear],b.scaley=e.sqrt(g(c[1])),h(c[1]),b.shear/=b.scaley,this.determinant()<0&&(b.scalex=-b.scalex);var d=-c[0][1],f=c[1][1];return 0>f?(b.rotate=a.deg(e.acos(f)),0>d&&(b.rotate=360-b.rotate)):b.rotate=a.deg(e.asin(d)),b.isSimple=!(+b.shear.toFixed(9)||b.scalex.toFixed(9)!=b.scaley.toFixed(9)&&b.rotate),b.isSuperSimple=!+b.shear.toFixed(9)&&b.scalex.toFixed(9)==b.scaley.toFixed(9)&&!b.rotate,b.noRotation=!+b.shear.toFixed(9)&&!b.rotate,b},c.toTransformString=function(a){var b=a||this.split();return+b.shear.toFixed(9)?"m"+[this.get(0),this.get(1),this.get(2),this.get(3),this.get(4),this.get(5)]:(b.scalex=+b.scalex.toFixed(4),b.scaley=+b.scaley.toFixed(4),b.rotate=+b.rotate.toFixed(4),(b.dx||b.dy?"t"+[+b.dx.toFixed(4),+b.dy.toFixed(4)]:f)+(1!=b.scalex||1!=b.scaley?"s"+[b.scalex,b.scaley,0,0]:f)+(b.rotate?"r"+[+b.rotate.toFixed(4),0,0]:f))}}(b.prototype),a.Matrix=b,a.matrix=function(a,c,d,e,f,g){return new b(a,c,d,e,f,g)}}),d.plugin(function(a,c,d,e,f){function g(d){return function(e){if(b.stop(),e instanceof f&&1==e.node.childNodes.length&&("radialGradient"==e.node.firstChild.tagName||"linearGradient"==e.node.firstChild.tagName||"pattern"==e.node.firstChild.tagName)&&(e=e.node.firstChild,n(this).appendChild(e),e=l(e)),e instanceof c)if("radialGradient"==e.type||"linearGradient"==e.type||"pattern"==e.type){e.node.id||p(e.node,{id:e.id});var g=q(e.node.id)}else g=e.attr(d);else if(g=a.color(e),g.error){var h=a(n(this).ownerSVGElement).gradient(e);h?(h.node.id||p(h.node,{id:h.id}),g=q(h.node.id)):g=e}else g=r(g);var i={};i[d]=g,p(this.node,i),this.node.style[d]=t}}function h(a){b.stop(),a==+a&&(a+="px"),this.node.style.fontSize=a}function i(a){for(var b=[],c=a.childNodes,d=0,e=c.length;e>d;d++){var f=c[d];3==f.nodeType&&b.push(f.nodeValue),"tspan"==f.tagName&&b.push(1==f.childNodes.length&&3==f.firstChild.nodeType?f.firstChild.nodeValue:i(f))}return b}function j(){return b.stop(),this.node.style.fontSize}var k=a._.make,l=a._.wrap,m=a.is,n=a._.getSomeDefs,o=/^url\(#?([^)]+)\)$/,p=a._.$,q=a.url,r=String,s=a._.separator,t="";b.on("snap.util.attr.mask",function(a){if(a instanceof c||a instanceof f){if(b.stop(),a instanceof f&&1==a.node.childNodes.length&&(a=a.node.firstChild,n(this).appendChild(a),a=l(a)),"mask"==a.type)var d=a;else d=k("mask",n(this)),d.node.appendChild(a.node);!d.node.id&&p(d.node,{id:d.id}),p(this.node,{mask:q(d.id)})}}),function(a){b.on("snap.util.attr.clip",a),b.on("snap.util.attr.clip-path",a),b.on("snap.util.attr.clipPath",a)}(function(a){if(a instanceof c||a instanceof f){if(b.stop(),"clipPath"==a.type)var d=a;else d=k("clipPath",n(this)),d.node.appendChild(a.node),!d.node.id&&p(d.node,{id:d.id});p(this.node,{"clip-path":q(d.node.id||d.id)})}}),b.on("snap.util.attr.fill",g("fill")),b.on("snap.util.attr.stroke",g("stroke"));var u=/^([lr])(?:\(([^)]*)\))?(.*)$/i;b.on("snap.util.grad.parse",function(a){a=r(a);var b=a.match(u);if(!b)return null;var c=b[1],d=b[2],e=b[3];return d=d.split(/\s*,\s*/).map(function(a){return+a==a?+a:a}),1==d.length&&0==d[0]&&(d=[]),e=e.split("-"),e=e.map(function(a){a=a.split(":");var b={color:a[0]};return a[1]&&(b.offset=parseFloat(a[1])),b}),{type:c,params:d,stops:e}}),b.on("snap.util.attr.d",function(c){b.stop(),m(c,"array")&&m(c[0],"array")&&(c=a.path.toString.call(c)),c=r(c),c.match(/[ruo]/i)&&(c=a.path.toAbsolute(c)),p(this.node,{d:c})})(-1),b.on("snap.util.attr.#text",function(a){b.stop(),a=r(a);for(var c=e.doc.createTextNode(a);this.node.firstChild;)this.node.removeChild(this.node.firstChild);this.node.appendChild(c)})(-1),b.on("snap.util.attr.path",function(a){b.stop(),this.attr({d:a})})(-1),b.on("snap.util.attr.class",function(a){b.stop(),this.node.className.baseVal=a})(-1),b.on("snap.util.attr.viewBox",function(a){var c;c=m(a,"object")&&"x"in a?[a.x,a.y,a.width,a.height].join(" "):m(a,"array")?a.join(" "):a,p(this.node,{viewBox:c}),b.stop()})(-1),b.on("snap.util.attr.transform",function(a){this.transform(a),b.stop()})(-1),b.on("snap.util.attr.r",function(a){"rect"==this.type&&(b.stop(),p(this.node,{rx:a,ry:a}))})(-1),b.on("snap.util.attr.textpath",function(a){if(b.stop(),"text"==this.type){var d,e,f;if(!a&&this.textPath){for(e=this.textPath;e.node.firstChild;)this.node.appendChild(e.node.firstChild);return e.remove(),void delete this.textPath}if(m(a,"string")){var g=n(this),h=l(g.parentNode).path(a);g.appendChild(h.node),d=h.id,h.attr({id:d})}else a=l(a),a instanceof c&&(d=a.attr("id"),d||(d=a.id,a.attr({id:d})));if(d)if(e=this.textPath,f=this.node,e)e.attr({"xlink:href":"#"+d});else{for(e=p("textPath",{"xlink:href":"#"+d});f.firstChild;)e.appendChild(f.firstChild);f.appendChild(e),this.textPath=l(e)}}})(-1),b.on("snap.util.attr.text",function(a){if("text"==this.type){for(var c=this.node,d=function(a){var b=p("tspan");if(m(a,"array"))for(var c=0;c<a.length;c++)b.appendChild(d(a[c]));else b.appendChild(e.doc.createTextNode(a));return b.normalize&&b.normalize(),b};c.firstChild;)c.removeChild(c.firstChild);for(var f=d(a);f.firstChild;)c.appendChild(f.firstChild)}b.stop()})(-1),b.on("snap.util.attr.fontSize",h)(-1),b.on("snap.util.attr.font-size",h)(-1),b.on("snap.util.getattr.transform",function(){return b.stop(),this.transform()})(-1),b.on("snap.util.getattr.textpath",function(){return b.stop(),this.textPath})(-1),function(){function c(c){return function(){b.stop();var d=e.doc.defaultView.getComputedStyle(this.node,null).getPropertyValue("marker-"+c);return"none"==d?d:a(e.doc.getElementById(d.match(o)[1]))}}function d(a){return function(c){b.stop();var d="marker"+a.charAt(0).toUpperCase()+a.substring(1);if(""==c||!c)return void(this.node.style[d]="none");if("marker"==c.type){var e=c.node.id;return e||p(c.node,{id:c.id}),void(this.node.style[d]=q(e))}}}b.on("snap.util.getattr.marker-end",c("end"))(-1),b.on("snap.util.getattr.markerEnd",c("end"))(-1),b.on("snap.util.getattr.marker-start",c("start"))(-1),b.on("snap.util.getattr.markerStart",c("start"))(-1),b.on("snap.util.getattr.marker-mid",c("mid"))(-1),b.on("snap.util.getattr.markerMid",c("mid"))(-1),b.on("snap.util.attr.marker-end",d("end"))(-1),b.on("snap.util.attr.markerEnd",d("end"))(-1),b.on("snap.util.attr.marker-start",d("start"))(-1),b.on("snap.util.attr.markerStart",d("start"))(-1),b.on("snap.util.attr.marker-mid",d("mid"))(-1),b.on("snap.util.attr.markerMid",d("mid"))(-1)}(),b.on("snap.util.getattr.r",function(){return"rect"==this.type&&p(this.node,"rx")==p(this.node,"ry")?(b.stop(),p(this.node,"rx")):void 0})(-1),b.on("snap.util.getattr.text",function(){if("text"==this.type||"tspan"==this.type){b.stop();var a=i(this.node);return 1==a.length?a[0]:a}})(-1),b.on("snap.util.getattr.#text",function(){return this.node.textContent})(-1),b.on("snap.util.getattr.viewBox",function(){b.stop();var c=p(this.node,"viewBox");return c?(c=c.split(s),a._.box(+c[0],+c[1],+c[2],+c[3])):void 0})(-1),b.on("snap.util.getattr.points",function(){var a=p(this.node,"points");return b.stop(),a?a.split(s):void 0})(-1),b.on("snap.util.getattr.path",function(){var a=p(this.node,"d");return b.stop(),a})(-1),b.on("snap.util.getattr.class",function(){return this.node.className.baseVal})(-1),b.on("snap.util.getattr.fontSize",j)(-1),b.on("snap.util.getattr.font-size",j)(-1)}),d.plugin(function(a,b){var c=/\S+/g,d=String,e=b.prototype;e.addClass=function(a){var b,e,f,g,h=d(a||"").match(c)||[],i=this.node,j=i.className.baseVal,k=j.match(c)||[];if(h.length){for(b=0;f=h[b++];)e=k.indexOf(f),~e||k.push(f);g=k.join(" "),j!=g&&(i.className.baseVal=g)}return this},e.removeClass=function(a){var b,e,f,g,h=d(a||"").match(c)||[],i=this.node,j=i.className.baseVal,k=j.match(c)||[];if(k.length){for(b=0;f=h[b++];)e=k.indexOf(f),~e&&k.splice(e,1);g=k.join(" "),j!=g&&(i.className.baseVal=g)}return this},e.hasClass=function(a){var b=this.node,d=b.className.baseVal,e=d.match(c)||[];return!!~e.indexOf(a)},e.toggleClass=function(a,b){if(null!=b)return b?this.addClass(a):this.removeClass(a);var d,e,f,g,h=(a||"").match(c)||[],i=this.node,j=i.className.baseVal,k=j.match(c)||[];for(d=0;f=h[d++];)e=k.indexOf(f),~e?k.splice(e,1):k.push(f);return g=k.join(" "),j!=g&&(i.className.baseVal=g),this}}),d.plugin(function(){function a(a){return a}function c(a){return function(b){return+b.toFixed(3)+a}}var d={"+":function(a,b){return a+b},"-":function(a,b){return a-b},"/":function(a,b){return a/b},"*":function(a,b){return a*b}},e=String,f=/[a-z]+$/i,g=/^\s*([+\-\/*])\s*=\s*([\d.eE+\-]+)\s*([^\d\s]+)?\s*$/;b.on("snap.util.attr",function(a){var c=e(a).match(g);if(c){var h=b.nt(),i=h.substring(h.lastIndexOf(".")+1),j=this.attr(i),k={};b.stop();var l=c[3]||"",m=j.match(f),n=d[c[1]];if(m&&m==l?a=n(parseFloat(j),+c[2]):(j=this.asPX(i),a=n(this.asPX(i),this.asPX(i,c[2]+l))),isNaN(j)||isNaN(a))return;k[i]=a,this.attr(k)}})(-10),b.on("snap.util.equal",function(h,i){var j=e(this.attr(h)||""),k=e(i).match(g);if(k){b.stop();var l=k[3]||"",m=j.match(f),n=d[k[1]];return m&&m==l?{from:parseFloat(j),to:n(parseFloat(j),+k[2]),f:c(m)}:(j=this.asPX(h),{from:j,to:n(j,this.asPX(h,k[2]+l)),f:a})}})(-10)}),d.plugin(function(c,d,e,f){var g=e.prototype,h=c.is;g.rect=function(a,b,c,d,e,f){var g;return null==f&&(f=e),h(a,"object")&&"[object Object]"==a?g=a:null!=a&&(g={x:a,y:b,width:c,height:d},null!=e&&(g.rx=e,g.ry=f)),this.el("rect",g)},g.circle=function(a,b,c){var d;return h(a,"object")&&"[object Object]"==a?d=a:null!=a&&(d={cx:a,cy:b,r:c}),this.el("circle",d)};var i=function(){function a(){this.parentNode.removeChild(this)}return function(b,c){var d=f.doc.createElement("img"),e=f.doc.body;d.style.cssText="position:absolute;left:-9999em;top:-9999em",d.onload=function(){c.call(d),d.onload=d.onerror=null,e.removeChild(d)},d.onerror=a,e.appendChild(d),d.src=b}}();g.image=function(a,b,d,e,f){var g=this.el("image");if(h(a,"object")&&"src"in a)g.attr(a);else if(null!=a){var j={"xlink:href":a,preserveAspectRatio:"none"};null!=b&&null!=d&&(j.x=b,j.y=d),null!=e&&null!=f?(j.width=e,j.height=f):i(a,function(){c._.$(g.node,{width:this.offsetWidth,height:this.offsetHeight})}),c._.$(g.node,j)}return g},g.ellipse=function(a,b,c,d){var e;return h(a,"object")&&"[object Object]"==a?e=a:null!=a&&(e={cx:a,cy:b,rx:c,ry:d}),this.el("ellipse",e)},g.path=function(a){var b;return h(a,"object")&&!h(a,"array")?b=a:a&&(b={d:a}),this.el("path",b)},g.group=g.g=function(a){var b=this.el("g");return 1==arguments.length&&a&&!a.type?b.attr(a):arguments.length&&b.add(Array.prototype.slice.call(arguments,0)),b},g.svg=function(a,b,c,d,e,f,g,i){var j={};return h(a,"object")&&null==b?j=a:(null!=a&&(j.x=a),null!=b&&(j.y=b),null!=c&&(j.width=c),null!=d&&(j.height=d),null!=e&&null!=f&&null!=g&&null!=i&&(j.viewBox=[e,f,g,i])),this.el("svg",j)},g.mask=function(a){var b=this.el("mask");return 1==arguments.length&&a&&!a.type?b.attr(a):arguments.length&&b.add(Array.prototype.slice.call(arguments,0)),b},g.ptrn=function(a,b,c,d,e,f,g,i){if(h(a,"object"))var j=a;else j={patternUnits:"userSpaceOnUse"},a&&(j.x=a),b&&(j.y=b),null!=c&&(j.width=c),null!=d&&(j.height=d),j.viewBox=null!=e&&null!=f&&null!=g&&null!=i?[e,f,g,i]:[a||0,b||0,c||0,d||0];return this.el("pattern",j)},g.use=function(a){return null!=a?(a instanceof d&&(a.attr("id")||a.attr({id:c._.id(a)}),a=a.attr("id")),"#"==String(a).charAt()&&(a=a.substring(1)),this.el("use",{"xlink:href":"#"+a})):d.prototype.use.call(this)},g.symbol=function(a,b,c,d){var e={};return null!=a&&null!=b&&null!=c&&null!=d&&(e.viewBox=[a,b,c,d]),this.el("symbol",e)},g.text=function(a,b,c){var d={};return h(a,"object")?d=a:null!=a&&(d={x:a,y:b,text:c||""}),this.el("text",d)},g.line=function(a,b,c,d){var e={};return h(a,"object")?e=a:null!=a&&(e={x1:a,x2:c,y1:b,y2:d}),this.el("line",e)},g.polyline=function(a){arguments.length>1&&(a=Array.prototype.slice.call(arguments,0));var b={};return h(a,"object")&&!h(a,"array")?b=a:null!=a&&(b={points:a}),this.el("polyline",b)},g.polygon=function(a){arguments.length>1&&(a=Array.prototype.slice.call(arguments,0));var b={};return h(a,"object")&&!h(a,"array")?b=a:null!=a&&(b={points:a}),this.el("polygon",b)},function(){function d(){return this.selectAll("stop")}function e(a,b){var d=k("stop"),e={offset:+b+"%"};return a=c.color(a),e["stop-color"]=a.hex,a.opacity<1&&(e["stop-opacity"]=a.opacity),k(d,e),this.node.appendChild(d),this}function f(){if("linearGradient"==this.type){var a=k(this.node,"x1")||0,b=k(this.node,"x2")||1,d=k(this.node,"y1")||0,e=k(this.node,"y2")||0;return c._.box(a,d,math.abs(b-a),math.abs(e-d))}var f=this.node.cx||.5,g=this.node.cy||.5,h=this.node.r||0;return c._.box(f-h,g-h,2*h,2*h)}function h(a,c){function d(a,b){for(var c=(b-l)/(a-m),d=m;a>d;d++)g[d].offset=+(+l+c*(d-m)).toFixed(2);m=a,l=b}var e,f=b("snap.util.grad.parse",null,c).firstDefined();if(!f)return null;f.params.unshift(a),e="l"==f.type.toLowerCase()?i.apply(0,f.params):j.apply(0,f.params),f.type!=f.type.toLowerCase()&&k(e.node,{gradientUnits:"userSpaceOnUse"});var g=f.stops,h=g.length,l=0,m=0;h--;for(var n=0;h>n;n++)"offset"in g[n]&&d(n,g[n].offset);for(g[h].offset=g[h].offset||100,d(h,g[h].offset),n=0;h>=n;n++){var o=g[n];e.addStop(o.color,o.offset)}return e}function i(a,b,g,h,i){var j=c._.make("linearGradient",a);return j.stops=d,j.addStop=e,j.getBBox=f,null!=b&&k(j.node,{x1:b,y1:g,x2:h,y2:i}),j}function j(a,b,g,h,i,j){var l=c._.make("radialGradient",a);return l.stops=d,l.addStop=e,l.getBBox=f,null!=b&&k(l.node,{cx:b,cy:g,r:h}),null!=i&&null!=j&&k(l.node,{fx:i,fy:j}),l}var k=c._.$;g.gradient=function(a){return h(this.defs,a)},g.gradientLinear=function(a,b,c,d){return i(this.defs,a,b,c,d)},g.gradientRadial=function(a,b,c,d,e){return j(this.defs,a,b,c,d,e)},g.toString=function(){var a,b=this.node.ownerDocument,d=b.createDocumentFragment(),e=b.createElement("div"),f=this.node.cloneNode(!0);return d.appendChild(e),e.appendChild(f),c._.$(f,{xmlns:"http://www.w3.org/2000/svg"}),a=e.innerHTML,d.removeChild(d.firstChild),a},g.toDataURL=function(){return a&&a.btoa?"data:image/svg+xml;base64,"+btoa(unescape(encodeURIComponent(this))):void 0},g.clear=function(){for(var a,b=this.node.firstChild;b;)a=b.nextSibling,"defs"!=b.tagName?b.parentNode.removeChild(b):g.clear.call({node:b}),b=a}}()}),d.plugin(function(a,b){function c(a){var b=c.ps=c.ps||{};return b[a]?b[a].sleep=100:b[a]={sleep:100},setTimeout(function(){for(var c in b)b[K](c)&&c!=a&&(b[c].sleep--,!b[c].sleep&&delete b[c])}),b[a]}function d(a,b,c,d){return null==a&&(a=b=c=d=0),null==b&&(b=a.y,c=a.width,d=a.height,a=a.x),{x:a,y:b,width:c,w:c,height:d,h:d,x2:a+c,y2:b+d,cx:a+c/2,cy:b+d/2,r1:N.min(c,d)/2,r2:N.max(c,d)/2,r0:N.sqrt(c*c+d*d)/2,path:w(a,b,c,d),vb:[a,b,c,d].join(" ")}}function e(){return this.join(",").replace(L,"$1")}function f(a){var b=J(a);return b.toString=e,b}function g(a,b,c,d,e,f,g,h,j){return null==j?n(a,b,c,d,e,f,g,h):i(a,b,c,d,e,f,g,h,o(a,b,c,d,e,f,g,h,j))}function h(c,d){function e(a){return+(+a).toFixed(3)}return a._.cacher(function(a,f,h){a instanceof b&&(a=a.attr("d")),a=E(a);for(var j,k,l,m,n,o="",p={},q=0,r=0,s=a.length;s>r;r++){if(l=a[r],"M"==l[0])j=+l[1],k=+l[2];else{if(m=g(j,k,l[1],l[2],l[3],l[4],l[5],l[6]),q+m>f){if(d&&!p.start){if(n=g(j,k,l[1],l[2],l[3],l[4],l[5],l[6],f-q),o+=["C"+e(n.start.x),e(n.start.y),e(n.m.x),e(n.m.y),e(n.x),e(n.y)],h)return o;p.start=o,o=["M"+e(n.x),e(n.y)+"C"+e(n.n.x),e(n.n.y),e(n.end.x),e(n.end.y),e(l[5]),e(l[6])].join(),q+=m,j=+l[5],k=+l[6];continue}if(!c&&!d)return n=g(j,k,l[1],l[2],l[3],l[4],l[5],l[6],f-q)}q+=m,j=+l[5],k=+l[6]}o+=l.shift()+l}return p.end=o,n=c?q:d?p:i(j,k,l[0],l[1],l[2],l[3],l[4],l[5],1)},null,a._.clone)}function i(a,b,c,d,e,f,g,h,i){var j=1-i,k=R(j,3),l=R(j,2),m=i*i,n=m*i,o=k*a+3*l*i*c+3*j*i*i*e+n*g,p=k*b+3*l*i*d+3*j*i*i*f+n*h,q=a+2*i*(c-a)+m*(e-2*c+a),r=b+2*i*(d-b)+m*(f-2*d+b),s=c+2*i*(e-c)+m*(g-2*e+c),t=d+2*i*(f-d)+m*(h-2*f+d),u=j*a+i*c,v=j*b+i*d,w=j*e+i*g,x=j*f+i*h,y=90-180*N.atan2(q-s,r-t)/O;return{x:o,y:p,m:{x:q,y:r},n:{x:s,y:t},start:{x:u,y:v},end:{x:w,y:x},alpha:y}}function j(b,c,e,f,g,h,i,j){a.is(b,"array")||(b=[b,c,e,f,g,h,i,j]);var k=D.apply(null,b);return d(k.min.x,k.min.y,k.max.x-k.min.x,k.max.y-k.min.y)}function k(a,b,c){return b>=a.x&&b<=a.x+a.width&&c>=a.y&&c<=a.y+a.height}function l(a,b){return a=d(a),b=d(b),k(b,a.x,a.y)||k(b,a.x2,a.y)||k(b,a.x,a.y2)||k(b,a.x2,a.y2)||k(a,b.x,b.y)||k(a,b.x2,b.y)||k(a,b.x,b.y2)||k(a,b.x2,b.y2)||(a.x<b.x2&&a.x>b.x||b.x<a.x2&&b.x>a.x)&&(a.y<b.y2&&a.y>b.y||b.y<a.y2&&b.y>a.y)}function m(a,b,c,d,e){var f=-3*b+9*c-9*d+3*e,g=a*f+6*b-12*c+6*d;return a*g-3*b+3*c}function n(a,b,c,d,e,f,g,h,i){null==i&&(i=1),i=i>1?1:0>i?0:i;for(var j=i/2,k=12,l=[-.1252,.1252,-.3678,.3678,-.5873,.5873,-.7699,.7699,-.9041,.9041,-.9816,.9816],n=[.2491,.2491,.2335,.2335,.2032,.2032,.1601,.1601,.1069,.1069,.0472,.0472],o=0,p=0;k>p;p++){var q=j*l[p]+j,r=m(q,a,c,e,g),s=m(q,b,d,f,h),t=r*r+s*s;o+=n[p]*N.sqrt(t)}return j*o}function o(a,b,c,d,e,f,g,h,i){if(!(0>i||n(a,b,c,d,e,f,g,h)<i)){var j,k=1,l=k/2,m=k-l,o=.01;for(j=n(a,b,c,d,e,f,g,h,m);S(j-i)>o;)l/=2,m+=(i>j?1:-1)*l,j=n(a,b,c,d,e,f,g,h,m);return m}}function p(a,b,c,d,e,f,g,h){if(!(Q(a,c)<P(e,g)||P(a,c)>Q(e,g)||Q(b,d)<P(f,h)||P(b,d)>Q(f,h))){var i=(a*d-b*c)*(e-g)-(a-c)*(e*h-f*g),j=(a*d-b*c)*(f-h)-(b-d)*(e*h-f*g),k=(a-c)*(f-h)-(b-d)*(e-g);if(k){var l=i/k,m=j/k,n=+l.toFixed(2),o=+m.toFixed(2);if(!(n<+P(a,c).toFixed(2)||n>+Q(a,c).toFixed(2)||n<+P(e,g).toFixed(2)||n>+Q(e,g).toFixed(2)||o<+P(b,d).toFixed(2)||o>+Q(b,d).toFixed(2)||o<+P(f,h).toFixed(2)||o>+Q(f,h).toFixed(2)))return{x:l,y:m}}}}function q(a,b,c){var d=j(a),e=j(b);if(!l(d,e))return c?0:[];for(var f=n.apply(0,a),g=n.apply(0,b),h=~~(f/8),k=~~(g/8),m=[],o=[],q={},r=c?0:[],s=0;h+1>s;s++){var t=i.apply(0,a.concat(s/h));m.push({x:t.x,y:t.y,t:s/h})}for(s=0;k+1>s;s++)t=i.apply(0,b.concat(s/k)),o.push({x:t.x,y:t.y,t:s/k});for(s=0;h>s;s++)for(var u=0;k>u;u++){var v=m[s],w=m[s+1],x=o[u],y=o[u+1],z=S(w.x-v.x)<.001?"y":"x",A=S(y.x-x.x)<.001?"y":"x",B=p(v.x,v.y,w.x,w.y,x.x,x.y,y.x,y.y);if(B){if(q[B.x.toFixed(4)]==B.y.toFixed(4))continue;q[B.x.toFixed(4)]=B.y.toFixed(4);var C=v.t+S((B[z]-v[z])/(w[z]-v[z]))*(w.t-v.t),D=x.t+S((B[A]-x[A])/(y[A]-x[A]))*(y.t-x.t);C>=0&&1>=C&&D>=0&&1>=D&&(c?r++:r.push({x:B.x,y:B.y,t1:C,t2:D}))}}return r}function r(a,b){return t(a,b)}function s(a,b){return t(a,b,1)}function t(a,b,c){a=E(a),b=E(b);for(var d,e,f,g,h,i,j,k,l,m,n=c?0:[],o=0,p=a.length;p>o;o++){var r=a[o];if("M"==r[0])d=h=r[1],e=i=r[2];else{"C"==r[0]?(l=[d,e].concat(r.slice(1)),d=l[6],e=l[7]):(l=[d,e,d,e,h,i,h,i],d=h,e=i);for(var s=0,t=b.length;t>s;s++){var u=b[s];if("M"==u[0])f=j=u[1],g=k=u[2];else{"C"==u[0]?(m=[f,g].concat(u.slice(1)),f=m[6],g=m[7]):(m=[f,g,f,g,j,k,j,k],f=j,g=k);var v=q(l,m,c);if(c)n+=v;else{for(var w=0,x=v.length;x>w;w++)v[w].segment1=o,v[w].segment2=s,v[w].bez1=l,v[w].bez2=m;n=n.concat(v)}}}}}return n}function u(a,b,c){var d=v(a);return k(d,b,c)&&t(a,[["M",b,c],["H",d.x2+10]],1)%2==1}function v(a){var b=c(a);if(b.bbox)return J(b.bbox);if(!a)return d();a=E(a);for(var e,f=0,g=0,h=[],i=[],j=0,k=a.length;k>j;j++)if(e=a[j],"M"==e[0])f=e[1],g=e[2],h.push(f),i.push(g);else{var l=D(f,g,e[1],e[2],e[3],e[4],e[5],e[6]);h=h.concat(l.min.x,l.max.x),i=i.concat(l.min.y,l.max.y),f=e[5],g=e[6]}var m=P.apply(0,h),n=P.apply(0,i),o=Q.apply(0,h),p=Q.apply(0,i),q=d(m,n,o-m,p-n);return b.bbox=J(q),q}function w(a,b,c,d,f){if(f)return[["M",+a+ +f,b],["l",c-2*f,0],["a",f,f,0,0,1,f,f],["l",0,d-2*f],["a",f,f,0,0,1,-f,f],["l",2*f-c,0],["a",f,f,0,0,1,-f,-f],["l",0,2*f-d],["a",f,f,0,0,1,f,-f],["z"]];var g=[["M",a,b],["l",c,0],["l",0,d],["l",-c,0],["z"]];return g.toString=e,g}function x(a,b,c,d,f){if(null==f&&null==d&&(d=c),a=+a,b=+b,c=+c,d=+d,null!=f)var g=Math.PI/180,h=a+c*Math.cos(-d*g),i=a+c*Math.cos(-f*g),j=b+c*Math.sin(-d*g),k=b+c*Math.sin(-f*g),l=[["M",h,j],["A",c,c,0,+(f-d>180),0,i,k]];else l=[["M",a,b],["m",0,-d],["a",c,d,0,1,1,0,2*d],["a",c,d,0,1,1,0,-2*d],["z"]];return l.toString=e,l}function y(b){var d=c(b),g=String.prototype.toLowerCase;if(d.rel)return f(d.rel);a.is(b,"array")&&a.is(b&&b[0],"array")||(b=a.parsePathString(b));var h=[],i=0,j=0,k=0,l=0,m=0;"M"==b[0][0]&&(i=b[0][1],j=b[0][2],k=i,l=j,m++,h.push(["M",i,j]));for(var n=m,o=b.length;o>n;n++){var p=h[n]=[],q=b[n];if(q[0]!=g.call(q[0]))switch(p[0]=g.call(q[0]),p[0]){case"a":p[1]=q[1],p[2]=q[2],p[3]=q[3],p[4]=q[4],p[5]=q[5],p[6]=+(q[6]-i).toFixed(3),p[7]=+(q[7]-j).toFixed(3);break;case"v":p[1]=+(q[1]-j).toFixed(3);break;case"m":k=q[1],l=q[2];default:for(var r=1,s=q.length;s>r;r++)p[r]=+(q[r]-(r%2?i:j)).toFixed(3)}else{p=h[n]=[],"m"==q[0]&&(k=q[1]+i,l=q[2]+j);for(var t=0,u=q.length;u>t;t++)h[n][t]=q[t]}var v=h[n].length;switch(h[n][0]){case"z":i=k,j=l;break;case"h":i+=+h[n][v-1];break;case"v":j+=+h[n][v-1];break;default:i+=+h[n][v-2],j+=+h[n][v-1]}}return h.toString=e,d.rel=f(h),h}function z(b){var d=c(b);if(d.abs)return f(d.abs);if(I(b,"array")&&I(b&&b[0],"array")||(b=a.parsePathString(b)),!b||!b.length)return[["M",0,0]];var g,h=[],i=0,j=0,k=0,l=0,m=0;"M"==b[0][0]&&(i=+b[0][1],j=+b[0][2],k=i,l=j,m++,h[0]=["M",i,j]);for(var n,o,p=3==b.length&&"M"==b[0][0]&&"R"==b[1][0].toUpperCase()&&"Z"==b[2][0].toUpperCase(),q=m,r=b.length;r>q;q++){if(h.push(n=[]),o=b[q],g=o[0],g!=g.toUpperCase())switch(n[0]=g.toUpperCase(),n[0]){case"A":n[1]=o[1],n[2]=o[2],n[3]=o[3],n[4]=o[4],n[5]=o[5],n[6]=+o[6]+i,n[7]=+o[7]+j;break;case"V":n[1]=+o[1]+j;break;case"H":n[1]=+o[1]+i;break;case"R":for(var s=[i,j].concat(o.slice(1)),t=2,u=s.length;u>t;t++)s[t]=+s[t]+i,s[++t]=+s[t]+j;h.pop(),h=h.concat(G(s,p));break;case"O":h.pop(),s=x(i,j,o[1],o[2]),s.push(s[0]),h=h.concat(s);break;case"U":h.pop(),h=h.concat(x(i,j,o[1],o[2],o[3])),n=["U"].concat(h[h.length-1].slice(-2));break;case"M":k=+o[1]+i,l=+o[2]+j;default:for(t=1,u=o.length;u>t;t++)n[t]=+o[t]+(t%2?i:j)}else if("R"==g)s=[i,j].concat(o.slice(1)),h.pop(),h=h.concat(G(s,p)),n=["R"].concat(o.slice(-2));else if("O"==g)h.pop(),s=x(i,j,o[1],o[2]),s.push(s[0]),h=h.concat(s);else if("U"==g)h.pop(),h=h.concat(x(i,j,o[1],o[2],o[3])),n=["U"].concat(h[h.length-1].slice(-2));else for(var v=0,w=o.length;w>v;v++)n[v]=o[v];if(g=g.toUpperCase(),"O"!=g)switch(n[0]){case"Z":i=+k,j=+l;break;case"H":i=n[1];break;case"V":j=n[1];break;case"M":k=n[n.length-2],l=n[n.length-1];default:i=n[n.length-2],j=n[n.length-1]}}return h.toString=e,d.abs=f(h),h}function A(a,b,c,d){return[a,b,c,d,c,d]}function B(a,b,c,d,e,f){var g=1/3,h=2/3;return[g*a+h*c,g*b+h*d,g*e+h*c,g*f+h*d,e,f]}function C(b,c,d,e,f,g,h,i,j,k){var l,m=120*O/180,n=O/180*(+f||0),o=[],p=a._.cacher(function(a,b,c){var d=a*N.cos(c)-b*N.sin(c),e=a*N.sin(c)+b*N.cos(c);return{x:d,y:e}});if(k)y=k[0],z=k[1],w=k[2],x=k[3];else{l=p(b,c,-n),b=l.x,c=l.y,l=p(i,j,-n),i=l.x,j=l.y;var q=(N.cos(O/180*f),N.sin(O/180*f),(b-i)/2),r=(c-j)/2,s=q*q/(d*d)+r*r/(e*e);s>1&&(s=N.sqrt(s),d=s*d,e=s*e);var t=d*d,u=e*e,v=(g==h?-1:1)*N.sqrt(S((t*u-t*r*r-u*q*q)/(t*r*r+u*q*q))),w=v*d*r/e+(b+i)/2,x=v*-e*q/d+(c+j)/2,y=N.asin(((c-x)/e).toFixed(9)),z=N.asin(((j-x)/e).toFixed(9));y=w>b?O-y:y,z=w>i?O-z:z,0>y&&(y=2*O+y),0>z&&(z=2*O+z),h&&y>z&&(y-=2*O),!h&&z>y&&(z-=2*O)}var A=z-y;if(S(A)>m){var B=z,D=i,E=j;z=y+m*(h&&z>y?1:-1),i=w+d*N.cos(z),j=x+e*N.sin(z),o=C(i,j,d,e,f,0,h,D,E,[z,B,w,x])}A=z-y;var F=N.cos(y),G=N.sin(y),H=N.cos(z),I=N.sin(z),J=N.tan(A/4),K=4/3*d*J,L=4/3*e*J,M=[b,c],P=[b+K*G,c-L*F],Q=[i+K*I,j-L*H],R=[i,j];if(P[0]=2*M[0]-P[0],P[1]=2*M[1]-P[1],k)return[P,Q,R].concat(o);o=[P,Q,R].concat(o).join().split(",");for(var T=[],U=0,V=o.length;V>U;U++)T[U]=U%2?p(o[U-1],o[U],n).y:p(o[U],o[U+1],n).x;return T}function D(a,b,c,d,e,f,g,h){for(var i,j,k,l,m,n,o,p,q=[],r=[[],[]],s=0;2>s;++s)if(0==s?(j=6*a-12*c+6*e,i=-3*a+9*c-9*e+3*g,k=3*c-3*a):(j=6*b-12*d+6*f,i=-3*b+9*d-9*f+3*h,k=3*d-3*b),S(i)<1e-12){if(S(j)<1e-12)continue;l=-k/j,l>0&&1>l&&q.push(l)}else o=j*j-4*k*i,p=N.sqrt(o),0>o||(m=(-j+p)/(2*i),m>0&&1>m&&q.push(m),n=(-j-p)/(2*i),n>0&&1>n&&q.push(n));for(var t,u=q.length,v=u;u--;)l=q[u],t=1-l,r[0][u]=t*t*t*a+3*t*t*l*c+3*t*l*l*e+l*l*l*g,r[1][u]=t*t*t*b+3*t*t*l*d+3*t*l*l*f+l*l*l*h;return r[0][v]=a,r[1][v]=b,r[0][v+1]=g,r[1][v+1]=h,r[0].length=r[1].length=v+2,{min:{x:P.apply(0,r[0]),y:P.apply(0,r[1])},max:{x:Q.apply(0,r[0]),y:Q.apply(0,r[1])}}}function E(a,b){var d=!b&&c(a);if(!b&&d.curve)return f(d.curve);for(var e=z(a),g=b&&z(b),h={x:0,y:0,bx:0,by:0,X:0,Y:0,qx:null,qy:null},i={x:0,y:0,bx:0,by:0,X:0,Y:0,qx:null,qy:null},j=(function(a,b,c){var d,e;if(!a)return["C",b.x,b.y,b.x,b.y,b.x,b.y];switch(!(a[0]in{T:1,Q:1})&&(b.qx=b.qy=null),a[0]){case"M":b.X=a[1],b.Y=a[2];break;case"A":a=["C"].concat(C.apply(0,[b.x,b.y].concat(a.slice(1))));break;case"S":"C"==c||"S"==c?(d=2*b.x-b.bx,e=2*b.y-b.by):(d=b.x,e=b.y),a=["C",d,e].concat(a.slice(1));break;case"T":"Q"==c||"T"==c?(b.qx=2*b.x-b.qx,b.qy=2*b.y-b.qy):(b.qx=b.x,b.qy=b.y),a=["C"].concat(B(b.x,b.y,b.qx,b.qy,a[1],a[2]));break;case"Q":b.qx=a[1],b.qy=a[2],a=["C"].concat(B(b.x,b.y,a[1],a[2],a[3],a[4]));break;case"L":a=["C"].concat(A(b.x,b.y,a[1],a[2]));break;case"H":a=["C"].concat(A(b.x,b.y,a[1],b.y));break;case"V":a=["C"].concat(A(b.x,b.y,b.x,a[1]));break;case"Z":a=["C"].concat(A(b.x,b.y,b.X,b.Y))}return a}),k=function(a,b){if(a[b].length>7){a[b].shift();for(var c=a[b];c.length;)m[b]="A",g&&(n[b]="A"),a.splice(b++,0,["C"].concat(c.splice(0,6)));a.splice(b,1),r=Q(e.length,g&&g.length||0)}},l=function(a,b,c,d,f){a&&b&&"M"==a[f][0]&&"M"!=b[f][0]&&(b.splice(f,0,["M",d.x,d.y]),c.bx=0,c.by=0,c.x=a[f][1],c.y=a[f][2],r=Q(e.length,g&&g.length||0))},m=[],n=[],o="",p="",q=0,r=Q(e.length,g&&g.length||0);r>q;q++){e[q]&&(o=e[q][0]),"C"!=o&&(m[q]=o,q&&(p=m[q-1])),e[q]=j(e[q],h,p),"A"!=m[q]&&"C"==o&&(m[q]="C"),k(e,q),g&&(g[q]&&(o=g[q][0]),"C"!=o&&(n[q]=o,q&&(p=n[q-1])),g[q]=j(g[q],i,p),"A"!=n[q]&&"C"==o&&(n[q]="C"),k(g,q)),l(e,g,h,i,q),l(g,e,i,h,q);var s=e[q],t=g&&g[q],u=s.length,v=g&&t.length;h.x=s[u-2],h.y=s[u-1],h.bx=M(s[u-4])||h.x,h.by=M(s[u-3])||h.y,i.bx=g&&(M(t[v-4])||i.x),i.by=g&&(M(t[v-3])||i.y),i.x=g&&t[v-2],i.y=g&&t[v-1]}return g||(d.curve=f(e)),g?[e,g]:e}function F(a,b){if(!b)return a;var c,d,e,f,g,h,i;for(a=E(a),e=0,g=a.length;g>e;e++)for(i=a[e],f=1,h=i.length;h>f;f+=2)c=b.x(i[f],i[f+1]),d=b.y(i[f],i[f+1]),i[f]=c,i[f+1]=d;return a}function G(a,b){for(var c=[],d=0,e=a.length;e-2*!b>d;d+=2){var f=[{x:+a[d-2],y:+a[d-1]},{x:+a[d],y:+a[d+1]},{x:+a[d+2],y:+a[d+3]},{x:+a[d+4],y:+a[d+5]}];b?d?e-4==d?f[3]={x:+a[0],y:+a[1]}:e-2==d&&(f[2]={x:+a[0],y:+a[1]},f[3]={x:+a[2],y:+a[3]}):f[0]={x:+a[e-2],y:+a[e-1]}:e-4==d?f[3]=f[2]:d||(f[0]={x:+a[d],y:+a[d+1]}),c.push(["C",(-f[0].x+6*f[1].x+f[2].x)/6,(-f[0].y+6*f[1].y+f[2].y)/6,(f[1].x+6*f[2].x-f[3].x)/6,(f[1].y+6*f[2].y-f[3].y)/6,f[2].x,f[2].y])}return c}var H=b.prototype,I=a.is,J=a._.clone,K="hasOwnProperty",L=/,?([a-z]),?/gi,M=parseFloat,N=Math,O=N.PI,P=N.min,Q=N.max,R=N.pow,S=N.abs,T=h(1),U=h(),V=h(0,1),W=a._unit2px,X={path:function(a){return a.attr("path")},circle:function(a){var b=W(a);return x(b.cx,b.cy,b.r)},ellipse:function(a){var b=W(a);return x(b.cx||0,b.cy||0,b.rx,b.ry)},rect:function(a){var b=W(a);return w(b.x||0,b.y||0,b.width,b.height,b.rx,b.ry)},image:function(a){var b=W(a);return w(b.x||0,b.y||0,b.width,b.height)},line:function(a){return"M"+[a.attr("x1")||0,a.attr("y1")||0,a.attr("x2"),a.attr("y2")]},polyline:function(a){return"M"+a.attr("points")},polygon:function(a){return"M"+a.attr("points")+"z"},deflt:function(a){var b=a.node.getBBox();return w(b.x,b.y,b.width,b.height)}};a.path=c,a.path.getTotalLength=T,a.path.getPointAtLength=U,a.path.getSubpath=function(a,b,c){if(this.getTotalLength(a)-c<1e-6)return V(a,b).end;var d=V(a,c,1);return b?V(d,b).end:d},H.getTotalLength=function(){return this.node.getTotalLength?this.node.getTotalLength():void 0},H.getPointAtLength=function(a){return U(this.attr("d"),a)},H.getSubpath=function(b,c){return a.path.getSubpath(this.attr("d"),b,c)},a._.box=d,a.path.findDotsAtSegment=i,a.path.bezierBBox=j,a.path.isPointInsideBBox=k,a.closest=function(b,c,e,f){for(var g=100,h=d(b-g/2,c-g/2,g,g),i=[],j=e[0].hasOwnProperty("x")?function(a){return{x:e[a].x,y:e[a].y}}:function(a){return{x:e[a],y:f[a]}},l=0;1e6>=g&&!l;){for(var m=0,n=e.length;n>m;m++){var o=j(m);if(k(h,o.x,o.y)){l++,i.push(o);break}}l||(g*=2,h=d(b-g/2,c-g/2,g,g))}if(1e6!=g){var p,q=1/0;for(m=0,n=i.length;n>m;m++){var r=a.len(b,c,i[m].x,i[m].y);q>r&&(q=r,i[m].len=r,p=i[m])}return p}},a.path.isBBoxIntersect=l,a.path.intersection=r,a.path.intersectionNumber=s,a.path.isPointInside=u,a.path.getBBox=v,a.path.get=X,a.path.toRelative=y,a.path.toAbsolute=z,a.path.toCubic=E,a.path.map=F,a.path.toString=e,a.path.clone=f}),d.plugin(function(a){var d=Math.max,e=Math.min,f=function(a){if(this.items=[],this.bindings={},this.length=0,this.type="set",a)for(var b=0,c=a.length;c>b;b++)a[b]&&(this[this.items.length]=this.items[this.items.length]=a[b],this.length++)},g=f.prototype;g.push=function(){for(var a,b,c=0,d=arguments.length;d>c;c++)a=arguments[c],a&&(b=this.items.length,this[b]=this.items[b]=a,this.length++);return this},g.pop=function(){return this.length&&delete this[this.length--],this.items.pop()},g.forEach=function(a,b){for(var c=0,d=this.items.length;d>c;c++)if(a.call(b,this.items[c],c)===!1)return this;return this},g.animate=function(d,e,f,g){"function"!=typeof f||f.length||(g=f,f=c.linear),d instanceof a._.Animation&&(g=d.callback,f=d.easing,e=f.dur,d=d.attr);var h=arguments;if(a.is(d,"array")&&a.is(h[h.length-1],"array"))var i=!0;var j,k=function(){j?this.b=j:j=this.b},l=0,m=this,n=g&&function(){++l==m.length&&g.call(this)
21
+ };return this.forEach(function(a,c){b.once("snap.animcreated."+a.id,k),i?h[c]&&a.animate.apply(a,h[c]):a.animate(d,e,f,n)})},g.remove=function(){for(;this.length;)this.pop().remove();return this},g.bind=function(a,b,c){var d={};if("function"==typeof b)this.bindings[a]=b;else{var e=c||a;this.bindings[a]=function(a){d[e]=a,b.attr(d)}}return this},g.attr=function(a){var b={};for(var c in a)this.bindings[c]?this.bindings[c](a[c]):b[c]=a[c];for(var d=0,e=this.items.length;e>d;d++)this.items[d].attr(b);return this},g.clear=function(){for(;this.length;)this.pop()},g.splice=function(a,b){a=0>a?d(this.length+a,0):a,b=d(0,e(this.length-a,b));var c,g=[],h=[],i=[];for(c=2;c<arguments.length;c++)i.push(arguments[c]);for(c=0;b>c;c++)h.push(this[a+c]);for(;c<this.length-a;c++)g.push(this[a+c]);var j=i.length;for(c=0;c<j+g.length;c++)this.items[a+c]=this[a+c]=j>c?i[c]:g[c-j];for(c=this.items.length=this.length-=b-j;this[c];)delete this[c++];return new f(h)},g.exclude=function(a){for(var b=0,c=this.length;c>b;b++)if(this[b]==a)return this.splice(b,1),!0;return!1},g.insertAfter=function(a){for(var b=this.items.length;b--;)this.items[b].insertAfter(a);return this},g.getBBox=function(){for(var a=[],b=[],c=[],f=[],g=this.items.length;g--;)if(!this.items[g].removed){var h=this.items[g].getBBox();a.push(h.x),b.push(h.y),c.push(h.x+h.width),f.push(h.y+h.height)}return a=e.apply(0,a),b=e.apply(0,b),c=d.apply(0,c),f=d.apply(0,f),{x:a,y:b,x2:c,y2:f,width:c-a,height:f-b,cx:a+(c-a)/2,cy:b+(f-b)/2}},g.clone=function(a){a=new f;for(var b=0,c=this.items.length;c>b;b++)a.push(this.items[b].clone());return a},g.toString=function(){return"Snap‘s set"},g.type="set",a.Set=f,a.set=function(){var a=new f;return arguments.length&&a.push.apply(a,Array.prototype.slice.call(arguments,0)),a}}),d.plugin(function(a,c){function d(a){var b=a[0];switch(b.toLowerCase()){case"t":return[b,0,0];case"m":return[b,1,0,0,1,0,0];case"r":return 4==a.length?[b,0,a[2],a[3]]:[b,0];case"s":return 5==a.length?[b,1,1,a[3],a[4]]:3==a.length?[b,1,1]:[b,1]}}function e(b,c,e){c=p(c).replace(/\.{3}|\u2026/g,b),b=a.parseTransformString(b)||[],c=a.parseTransformString(c)||[];for(var f,g,h,i,l=Math.max(b.length,c.length),m=[],n=[],o=0;l>o;o++){if(h=b[o]||d(c[o]),i=c[o]||d(h),h[0]!=i[0]||"r"==h[0].toLowerCase()&&(h[2]!=i[2]||h[3]!=i[3])||"s"==h[0].toLowerCase()&&(h[3]!=i[3]||h[4]!=i[4])){b=a._.transform2matrix(b,e()),c=a._.transform2matrix(c,e()),m=[["m",b.a,b.b,b.c,b.d,b.e,b.f]],n=[["m",c.a,c.b,c.c,c.d,c.e,c.f]];break}for(m[o]=[],n[o]=[],f=0,g=Math.max(h.length,i.length);g>f;f++)f in h&&(m[o][f]=h[f]),f in i&&(n[o][f]=i[f])}return{from:k(m),to:k(n),f:j(m)}}function f(a){return a}function g(a){return function(b){return+b.toFixed(3)+a}}function h(a){return a.join(" ")}function i(b){return a.rgb(b[0],b[1],b[2])}function j(a){var b,c,d,e,f,g,h=0,i=[];for(b=0,c=a.length;c>b;b++){for(f="[",g=['"'+a[b][0]+'"'],d=1,e=a[b].length;e>d;d++)g[d]="val["+h++ +"]";f+=g+"]",i[b]=f}return Function("val","return Snap.path.toString.call(["+i+"])")}function k(a){for(var b=[],c=0,d=a.length;d>c;c++)for(var e=1,f=a[c].length;f>e;e++)b.push(a[c][e]);return b}function l(a){return isFinite(parseFloat(a))}function m(b,c){return a.is(b,"array")&&a.is(c,"array")?b.toString()==c.toString():!1}var n={},o=/[a-z]+$/i,p=String;n.stroke=n.fill="colour",c.prototype.equal=function(a,c){return b("snap.util.equal",this,a,c).firstDefined()},b.on("snap.util.equal",function(b,c){var d,q,r=p(this.attr(b)||""),s=this;if(l(r)&&l(c))return{from:parseFloat(r),to:parseFloat(c),f:f};if("colour"==n[b])return d=a.color(r),q=a.color(c),{from:[d.r,d.g,d.b,d.opacity],to:[q.r,q.g,q.b,q.opacity],f:i};if("viewBox"==b)return d=this.attr(b).vb.split(" ").map(Number),q=c.split(" ").map(Number),{from:d,to:q,f:h};if("transform"==b||"gradientTransform"==b||"patternTransform"==b)return c instanceof a.Matrix&&(c=c.toTransformString()),a._.rgTransform.test(c)||(c=a._.svgTransform2string(c)),e(r,c,function(){return s.getBBox(1)});if("d"==b||"path"==b)return d=a.path.toCubic(r,c),{from:k(d[0]),to:k(d[1]),f:j(d[0])};if("points"==b)return d=p(r).split(a._.separator),q=p(c).split(a._.separator),{from:d,to:q,f:function(a){return a}};var t=r.match(o),u=p(c).match(o);return t&&m(t,u)?{from:parseFloat(r),to:parseFloat(c),f:g(t)}:{from:this.asPX(b),to:this.asPX(b,c),f:f}})}),d.plugin(function(a,c,d,e){for(var f=c.prototype,g="hasOwnProperty",h=("createTouch"in e.doc),i=["click","dblclick","mousedown","mousemove","mouseout","mouseover","mouseup","touchstart","touchmove","touchend","touchcancel"],j={mousedown:"touchstart",mousemove:"touchmove",mouseup:"touchend"},k=(function(a,b){var c="y"==a?"scrollTop":"scrollLeft",d=b&&b.node?b.node.ownerDocument:e.doc;return d[c in d.documentElement?"documentElement":"body"][c]}),l=function(){return this.originalEvent.preventDefault()},m=function(){return this.originalEvent.stopPropagation()},n=function(a,b,c,d){var e=h&&j[b]?j[b]:b,f=function(e){var f=k("y",d),i=k("x",d);if(h&&j[g](b))for(var n=0,o=e.targetTouches&&e.targetTouches.length;o>n;n++)if(e.targetTouches[n].target==a||a.contains(e.targetTouches[n].target)){var p=e;e=e.targetTouches[n],e.originalEvent=p,e.preventDefault=l,e.stopPropagation=m;break}var q=e.clientX+i,r=e.clientY+f;return c.call(d,e,q,r)};return b!==e&&a.addEventListener(b,f,!1),a.addEventListener(e,f,!1),function(){return b!==e&&a.removeEventListener(b,f,!1),a.removeEventListener(e,f,!1),!0}},o=[],p=function(a){for(var c,d=a.clientX,e=a.clientY,f=k("y"),g=k("x"),i=o.length;i--;){if(c=o[i],h){for(var j,l=a.touches&&a.touches.length;l--;)if(j=a.touches[l],j.identifier==c.el._drag.id||c.el.node.contains(j.target)){d=j.clientX,e=j.clientY,(a.originalEvent?a.originalEvent:a).preventDefault();break}}else a.preventDefault();{var m=c.el.node;m.nextSibling,m.parentNode,m.style.display}d+=g,e+=f,b("snap.drag.move."+c.el.id,c.move_scope||c.el,d-c.el._drag.x,e-c.el._drag.y,d,e,a)}},q=function(c){a.unmousemove(p).unmouseup(q);for(var d,e=o.length;e--;)d=o[e],d.el._drag={},b("snap.drag.end."+d.el.id,d.end_scope||d.start_scope||d.move_scope||d.el,c),b.off("snap.drag.*."+d.el.id);o=[]},r=i.length;r--;)!function(b){a[b]=f[b]=function(c,d){if(a.is(c,"function"))this.events=this.events||[],this.events.push({name:b,f:c,unbind:n(this.node||document,b,c,d||this)});else for(var e=0,f=this.events.length;f>e;e++)if(this.events[e].name==b)try{this.events[e].f.call(this)}catch(g){}return this},a["un"+b]=f["un"+b]=function(a){for(var c=this.events||[],d=c.length;d--;)if(c[d].name==b&&(c[d].f==a||!a))return c[d].unbind(),c.splice(d,1),!c.length&&delete this.events,this;return this}}(i[r]);f.hover=function(a,b,c,d){return this.mouseover(a,c).mouseout(b,d||c)},f.unhover=function(a,b){return this.unmouseover(a).unmouseout(b)};var s=[];f.drag=function(c,d,e,f,g,h){function i(i,j,l){(i.originalEvent||i).preventDefault(),k._drag.x=j,k._drag.y=l,k._drag.id=i.identifier,!o.length&&a.mousemove(p).mouseup(q),o.push({el:k,move_scope:f,start_scope:g,end_scope:h}),d&&b.on("snap.drag.start."+k.id,d),c&&b.on("snap.drag.move."+k.id,c),e&&b.on("snap.drag.end."+k.id,e),b("snap.drag.start."+k.id,g||f||k,j,l,i)}function j(a,c,d){b("snap.draginit."+k.id,k,a,c,d)}var k=this;if(!arguments.length){var l;return k.drag(function(a,b){this.attr({transform:l+(l?"T":"t")+[a,b]})},function(){l=this.transform().local})}return b.on("snap.draginit."+k.id,i),k._drag={},s.push({el:k,start:i,init:j}),k.mousedown(j),k},f.undrag=function(){for(var c=s.length;c--;)s[c].el==this&&(this.unmousedown(s[c].init),s.splice(c,1),b.unbind("snap.drag.*."+this.id),b.unbind("snap.draginit."+this.id));return!s.length&&a.unmousemove(p).unmouseup(q),this}}),d.plugin(function(a,c,d){var e=(c.prototype,d.prototype),f=/^\s*url\((.+)\)/,g=String,h=a._.$;a.filter={},e.filter=function(b){var d=this;"svg"!=d.type&&(d=d.paper);var e=a.parse(g(b)),f=a._.id(),i=(d.node.offsetWidth,d.node.offsetHeight,h("filter"));return h(i,{id:f,filterUnits:"userSpaceOnUse"}),i.appendChild(e.node),d.defs.appendChild(i),new c(i)},b.on("snap.util.getattr.filter",function(){b.stop();var c=h(this.node,"filter");if(c){var d=g(c).match(f);return d&&a.select(d[1])}}),b.on("snap.util.attr.filter",function(d){if(d instanceof c&&"filter"==d.type){b.stop();var e=d.node.id;e||(h(d.node,{id:d.id}),e=d.id),h(this.node,{filter:a.url(e)})}d&&"none"!=d||(b.stop(),this.node.removeAttribute("filter"))}),a.filter.blur=function(b,c){null==b&&(b=2);var d=null==c?b:[b,c];return a.format('<feGaussianBlur stdDeviation="{def}"/>',{def:d})},a.filter.blur.toString=function(){return this()},a.filter.shadow=function(b,c,d,e,f){return"string"==typeof d&&(e=d,f=e,d=4),"string"!=typeof e&&(f=e,e="#000"),e=e||"#000",null==d&&(d=4),null==f&&(f=1),null==b&&(b=0,c=2),null==c&&(c=b),e=a.color(e),a.format('<feGaussianBlur in="SourceAlpha" stdDeviation="{blur}"/><feOffset dx="{dx}" dy="{dy}" result="offsetblur"/><feFlood flood-color="{color}"/><feComposite in2="offsetblur" operator="in"/><feComponentTransfer><feFuncA type="linear" slope="{opacity}"/></feComponentTransfer><feMerge><feMergeNode/><feMergeNode in="SourceGraphic"/></feMerge>',{color:e,dx:b,dy:c,blur:d,opacity:f})},a.filter.shadow.toString=function(){return this()},a.filter.grayscale=function(b){return null==b&&(b=1),a.format('<feColorMatrix type="matrix" values="{a} {b} {c} 0 0 {d} {e} {f} 0 0 {g} {b} {h} 0 0 0 0 0 1 0"/>',{a:.2126+.7874*(1-b),b:.7152-.7152*(1-b),c:.0722-.0722*(1-b),d:.2126-.2126*(1-b),e:.7152+.2848*(1-b),f:.0722-.0722*(1-b),g:.2126-.2126*(1-b),h:.0722+.9278*(1-b)})},a.filter.grayscale.toString=function(){return this()},a.filter.sepia=function(b){return null==b&&(b=1),a.format('<feColorMatrix type="matrix" values="{a} {b} {c} 0 0 {d} {e} {f} 0 0 {g} {h} {i} 0 0 0 0 0 1 0"/>',{a:.393+.607*(1-b),b:.769-.769*(1-b),c:.189-.189*(1-b),d:.349-.349*(1-b),e:.686+.314*(1-b),f:.168-.168*(1-b),g:.272-.272*(1-b),h:.534-.534*(1-b),i:.131+.869*(1-b)})},a.filter.sepia.toString=function(){return this()},a.filter.saturate=function(b){return null==b&&(b=1),a.format('<feColorMatrix type="saturate" values="{amount}"/>',{amount:1-b})},a.filter.saturate.toString=function(){return this()},a.filter.hueRotate=function(b){return b=b||0,a.format('<feColorMatrix type="hueRotate" values="{angle}"/>',{angle:b})},a.filter.hueRotate.toString=function(){return this()},a.filter.invert=function(b){return null==b&&(b=1),a.format('<feComponentTransfer><feFuncR type="table" tableValues="{amount} {amount2}"/><feFuncG type="table" tableValues="{amount} {amount2}"/><feFuncB type="table" tableValues="{amount} {amount2}"/></feComponentTransfer>',{amount:b,amount2:1-b})},a.filter.invert.toString=function(){return this()},a.filter.brightness=function(b){return null==b&&(b=1),a.format('<feComponentTransfer><feFuncR type="linear" slope="{amount}"/><feFuncG type="linear" slope="{amount}"/><feFuncB type="linear" slope="{amount}"/></feComponentTransfer>',{amount:b})},a.filter.brightness.toString=function(){return this()},a.filter.contrast=function(b){return null==b&&(b=1),a.format('<feComponentTransfer><feFuncR type="linear" slope="{amount}" intercept="{amount2}"/><feFuncG type="linear" slope="{amount}" intercept="{amount2}"/><feFuncB type="linear" slope="{amount}" intercept="{amount2}"/></feComponentTransfer>',{amount:b,amount2:.5-b/2})},a.filter.contrast.toString=function(){return this()}}),d.plugin(function(a,b){var c=a._.box,d=a.is,e=/^[^a-z]*([tbmlrc])/i,f=function(){return"T"+this.dx+","+this.dy};b.prototype.getAlign=function(a,b){null==b&&d(a,"string")&&(b=a,a=null),a=a||this.paper;var g=a.getBBox?a.getBBox():c(a),h=this.getBBox(),i={};switch(b=b&&b.match(e),b=b?b[1].toLowerCase():"c"){case"t":i.dx=0,i.dy=g.y-h.y;break;case"b":i.dx=0,i.dy=g.y2-h.y2;break;case"m":i.dx=0,i.dy=g.cy-h.cy;break;case"l":i.dx=g.x-h.x,i.dy=0;break;case"r":i.dx=g.x2-h.x2,i.dy=0;break;default:i.dx=g.cx-h.cx,i.dy=0}return i.toString=f,i},b.prototype.align=function(a,b){return this.transform("..."+this.getAlign(a,b))}}),d});
22
+
23
+
24
+ /*
25
+ convert a cubic bezier value to a custom mina easing
26
+ http://stackoverflow.com/questions/25265197/how-to-convert-a-cubic-bezier-value-to-a-custom-mina-easing-snap-svg
27
+ */
28
+ var duration = 300,
29
+ delay = 300,
30
+ epsilon = (1000 / 60 / duration) / 4,
31
+ firstCustomMinaAnimation = bezier(.42, .03, .77, .63, epsilon),
32
+ secondCustomMinaAnimation = bezier(.27, .5, .6, .99, epsilon);
33
+
34
+ jQuery(document).ready(function () {
35
+ //initialize the slider
36
+ jQuery('.cd-slider-wrapper').each(function () {
37
+ initSlider(jQuery(this));
38
+ });
39
+ });
40
+
41
+ function initSlider(sliderWrapper) {
42
+ //cache jQuery objects
43
+ slider = sliderWrapper.find('.cd-slider'),
44
+ sliderNavigation = sliderWrapper.find('.cd-slider-navigation').find('li'),
45
+ svgCoverLayer = sliderWrapper.find('div.cd-svg-cover'),
46
+ pathId = svgCoverLayer.find('path').attr('id'),
47
+ svgPath = Snap('#' + pathId);
48
+
49
+ //store path 'd' attribute values
50
+ pathArray = [];
51
+ pathArray[0] = svgCoverLayer.data('step1');
52
+ pathArray[1] = svgCoverLayer.data('step6');
53
+ pathArray[2] = svgCoverLayer.data('step2');
54
+ pathArray[3] = svgCoverLayer.data('step7');
55
+ pathArray[4] = svgCoverLayer.data('step3');
56
+ pathArray[5] = svgCoverLayer.data('step8');
57
+ pathArray[6] = svgCoverLayer.data('step4');
58
+ pathArray[7] = svgCoverLayer.data('step9');
59
+ pathArray[8] = svgCoverLayer.data('step5');
60
+ pathArray[9] = svgCoverLayer.data('step10');
61
+
62
+ //update visible slide when user clicks .cd-slider-navigation buttons
63
+ sliderNavigation.on('click', function (event) {
64
+ event.preventDefault();
65
+ var selectedItem = jQuery(this);
66
+ if (!selectedItem.hasClass('selected')) {
67
+ // if it's not already selected
68
+ var selectedSlidePosition = selectedItem.index(),
69
+ selectedSlide = slider.children('li').eq(selectedSlidePosition),
70
+ visibleSlide = slider.find('.visible'),
71
+ visibleSlidePosition = visibleSlide.index(),
72
+ direction = '';
73
+ direction = (visibleSlidePosition < selectedSlidePosition) ? 'next' : 'prev';
74
+ updateSlide(visibleSlide, selectedSlide, direction, svgCoverLayer, sliderNavigation, pathArray, svgPath);
75
+ }
76
+ });
77
+ }
78
+
79
+ function nextSlide() {
80
+ var visibleSlide = slider.find('.visible');
81
+ jQuery.post("?page=newsletter_main_welcome&noheader=1&action=save", jQuery("#tnp-welcome").serialize());
82
+ var visibleSlidePosition = visibleSlide.index();
83
+ var selectedSlide = slider.children('li').eq(visibleSlidePosition + 1);
84
+ if (selectedSlide.hasClass("tnp-last-slide")) {
85
+ jQuery(".cd-slider-navigation").hide();
86
+ }
87
+
88
+ updateSlide(visibleSlide, selectedSlide, "next", svgCoverLayer, sliderNavigation, pathArray, svgPath);
89
+
90
+ }
91
+
92
+ function prevSlide() {
93
+ var visibleSlide = slider.find('.visible');
94
+ var visibleSlidePosition = visibleSlide.index();
95
+ var selectedSlide = slider.children('li').eq(visibleSlidePosition - 1);
96
+
97
+ updateSlide(visibleSlide, selectedSlide, "prev", svgCoverLayer, sliderNavigation, pathArray, svgPath);
98
+ if (selectedSlide.hasClass("tnp-first-slide")) {
99
+ jQuery(".cd-slider-navigation a.tnp-welcome-prev").hide();
100
+ }
101
+ }
102
+
103
+ function updateSlide(oldSlide, newSlide, direction, svgCoverLayer, sliderNavigation, paths, svgPath) {
104
+ if (direction == 'next') {
105
+ var path1 = paths[0],
106
+ path2 = paths[2],
107
+ path3 = paths[4];
108
+ path4 = paths[6];
109
+ path5 = paths[8];
110
+ } else {
111
+ var path1 = paths[1],
112
+ path2 = paths[3],
113
+ path3 = paths[5];
114
+ path4 = paths[7];
115
+ path5 = paths[9];
116
+ }
117
+
118
+ svgCoverLayer.addClass('is-animating');
119
+ svgPath.attr('d', path1);
120
+ svgPath.animate({'d': path2}, duration, firstCustomMinaAnimation, function () {
121
+ svgPath.animate({'d': path3}, duration, secondCustomMinaAnimation, function () {
122
+ oldSlide.removeClass('visible');
123
+ newSlide.addClass('visible');
124
+ updateNavSlide(newSlide, sliderNavigation);
125
+ setTimeout(function () {
126
+ svgPath.animate({'d': path4}, duration, firstCustomMinaAnimation, function () {
127
+ svgPath.animate({'d': path5}, duration, secondCustomMinaAnimation, function () {
128
+ svgCoverLayer.removeClass('is-animating');
129
+ if (direction == "next") {
130
+ jQuery(".cd-slider-navigation a.tnp-welcome-prev").show();
131
+ }
132
+ });
133
+ });
134
+ }, delay);
135
+ });
136
+ });
137
+ }
138
+
139
+ function updateNavSlide(actualSlide, sliderNavigation) {
140
+ var position = actualSlide.index();
141
+ sliderNavigation.removeClass('selected').eq(position).addClass('selected');
142
+ }
143
+
144
+ function bezier(x1, y1, x2, y2, epsilon) {
145
+ //https://github.com/arian/cubic-bezier
146
+ var curveX = function (t) {
147
+ var v = 1 - t;
148
+ return 3 * v * v * t * x1 + 3 * v * t * t * x2 + t * t * t;
149
+ };
150
+
151
+ var curveY = function (t) {
152
+ var v = 1 - t;
153
+ return 3 * v * v * t * y1 + 3 * v * t * t * y2 + t * t * t;
154
+ };
155
+
156
+ var derivativeCurveX = function (t) {
157
+ var v = 1 - t;
158
+ return 3 * (2 * (t - 1) * t + v * v) * x1 + 3 * (-t * t * t + 2 * v * t) * x2;
159
+ };
160
+
161
+ return function (t) {
162
+
163
+ var x = t, t0, t1, t2, x2, d2, i;
164
+
165
+ // First try a few iterations of Newton's method -- normally very fast.
166
+ for (t2 = x, i = 0; i < 8; i++) {
167
+ x2 = curveX(t2) - x;
168
+ if (Math.abs(x2) < epsilon)
169
+ return curveY(t2);
170
+ d2 = derivativeCurveX(t2);
171
+ if (Math.abs(d2) < 1e-6)
172
+ break;
173
+ t2 = t2 - x2 / d2;
174
+ }
175
+
176
+ t0 = 0, t1 = 1, t2 = x;
177
+
178
+ if (t2 < t0)
179
+ return curveY(t0);
180
+ if (t2 > t1)
181
+ return curveY(t1);
182
+
183
+ // Fallback to the bisection method for reliability.
184
+ while (t0 < t1) {
185
+ x2 = curveX(t2);
186
+ if (Math.abs(x2 - x) < epsilon)
187
+ return curveY(t2);
188
+ if (x > x2)
189
+ t0 = t2;
190
+ else
191
+ t1 = t2;
192
+ t2 = (t1 - t0) * .5 + t0;
193
+ }
194
+
195
+ // Failure
196
+ return curveY(t2);
197
+
198
+ };
199
+ }
200
+
main/logs.php CHANGED
@@ -1,57 +1,58 @@
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('delete_logs')) {
11
- $files = glob(WP_CONTENT_DIR . '/logs/newsletter/*.txt');
12
- foreach ($files as $file) {
13
- if (is_file($file))
14
- unlink($file);
15
- }
16
- $secret = NewsletterModule::get_token(8);
17
- update_option('newsletter_logger_secret', $secret);
18
- $controls->messages = 'Logs deleted';
19
- }
20
-
21
- ?>
22
-
23
-
24
- <div class="wrap tnp-main-status" id="tnp-wrap">
25
-
26
- <?php include NEWSLETTER_DIR . '/tnp-header.php'; ?>
27
-
28
- <div id="tnp-heading">
29
-
30
- <h2><?php _e('Logs', 'newsletter') ?></h2>
31
-
32
- </div>
33
-
34
- <div id="tnp-body">
35
-
36
- <form method="post" action="">
37
- <?php $controls->init(); ?>
38
-
39
- <ul class="tnp-log-files">
40
- <?php
41
- $files = glob(WP_CONTENT_DIR . '/logs/newsletter/*.txt'); // get all file names
42
- foreach ($files as $file) { // iterate files
43
- echo '<li><a href="' . WP_CONTENT_URL . '/logs/newsletter/' . basename($file) . '" target="_blank">' . basename($file) . '</a>';
44
- echo ' <span class="tnp-log-size">(' . size_format(filesize($file)) . ')</span>';
45
- echo '</li>';
46
- }
47
- ?>
48
- </ul>
49
-
50
- <?php $controls->button('delete_logs', 'Delete all'); ?>
51
-
52
- </form>
53
- </div>
54
-
55
- <?php include NEWSLETTER_DIR . '/tnp-footer.php'; ?>
56
-
57
- </div>
 
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('delete_logs')) {
11
+ $files = glob(WP_CONTENT_DIR . '/logs/newsletter/*.txt');
12
+ foreach ($files as $file) {
13
+ if (is_file($file))
14
+ unlink($file);
15
+ }
16
+ $secret = NewsletterModule::get_token(8);
17
+ update_option('newsletter_logger_secret', $secret);
18
+ $controls->messages = 'Logs deleted';
19
+ }
20
+
21
+ ?>
22
+
23
+
24
+ <div class="wrap tnp-main-status" id="tnp-wrap">
25
+
26
+ <?php include NEWSLETTER_DIR . '/tnp-header.php'; ?>
27
+
28
+ <div id="tnp-heading">
29
+
30
+ <h2><?php _e('Logs', 'newsletter') ?></h2>
31
+
32
+ </div>
33
+
34
+ <div id="tnp-body">
35
+
36
+ <form method="post" action="">
37
+ <?php $controls->init(); ?>
38
+
39
+
40
+ <ul class="tnp-log-files">
41
+ <?php
42
+ $files = glob(WP_CONTENT_DIR . '/logs/newsletter/*.txt'); // get all file names
43
+ foreach ($files as $file) { // iterate files
44
+ echo '<li><a href="' . WP_CONTENT_URL . '/logs/newsletter/' . basename($file) . '" target="_blank">' . basename($file) . '</a>';
45
+ echo ' <span class="tnp-log-size">(' . size_format(filesize($file)) . ')</span>';
46
+ echo '</li>';
47
+ }
48
+ ?>
49
+ </ul>
50
+
51
+ <?php $controls->button('delete_logs', 'Delete all'); ?>
52
+
53
+ </form>
54
+ </div>
55
+
56
+ <?php include NEWSLETTER_DIR . '/tnp-footer.php'; ?>
57
+
58
+ </div>
main/scheduler.php ADDED
@@ -0,0 +1,411 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /* @var $this Newsletter */
3
+ /* @var $wpdb wpdb */
4
+
5
+ defined('ABSPATH') || exit;
6
+
7
+ wp_enqueue_script('tnp-chart');
8
+
9
+ include_once NEWSLETTER_INCLUDES_DIR . '/controls.php';
10
+ $controls = new NewsletterControls();
11
+ $system = NewsletterSystem::instance();
12
+
13
+ if ($controls->is_action('reset')) {
14
+ $system->reset_cron_stats();
15
+ $controls->add_message_done();
16
+ }
17
+
18
+ if ($controls->is_action('reschedule')) {
19
+ wp_clear_scheduled_hook('newsletter');
20
+ wp_schedule_event(time() + 30, 'newsletter', 'newsletter');
21
+ $controls->add_message_done();
22
+ }
23
+
24
+ if ($controls->is_action('trigger')) {
25
+ wp_clear_scheduled_hook('newsletter');
26
+ wp_schedule_event(time() + NEWSLETTER_CRON_INTERVAL, 'newsletter', 'newsletter');
27
+ Newsletter::instance()->hook_newsletter();
28
+ $controls->add_message_done();
29
+ }
30
+
31
+ if ($controls->is_action('test')) {
32
+ $response = wp_remote_get(site_url('/wp-cron.php') . '?' . time());
33
+ if (is_wp_error($response)) {
34
+ $controls->errors = 'Test failed: ' . esc_html($response->get_error_message());
35
+ } else if (wp_remote_retrieve_response_code($response) != 200) {
36
+ $controls->errors = 'Test failed: ' . esc_html(wp_remote_retrieve_response_message($response));
37
+ } else {
38
+ $controls->add_message('Test ok');
39
+ }
40
+
41
+ if ($controls->errors) {
42
+ $controls->errors .= '<br>Report this error to your provider saying the site cannot make an HTTP call to its wp-cron.php file and copying the error message above.';
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 '<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
+
75
+ <div id="tnp-heading">
76
+
77
+ <h2><?php _e('WordPress Scheduler and Newsletter Delivery Engine', 'newsletter') ?></h2>
78
+ <p>
79
+ The scheduler is a WordPress component that executes <strong>background tasks</strong>
80
+ (publish future post, run backups, send newsletters, ...).
81
+ <br>
82
+ Here some steps you can consider if the scheduler has issues.
83
+ </p>
84
+ <ul>
85
+ <li>Check the <a href="<?php echo admin_url('/site-health.php') ?>">site health panel</a> and try to solve the issues identified by WordPress in your site</li>
86
+ <li>Install the <a href="https://wordpress.org/plugins/wp-crontrol/" target="_blank">WP Crontol</a> plugin which shows all the scheduled jobs and delays</li>
87
+ <li>Configure an <a href="https://www.thenewsletterplugin.com/documentation/delivery-and-spam/newsletter-delivery-engine/" target="_blank">external cron service</a>
88
+ (if you have a license you can use our <a href="https://www.thenewsletterplugin.com/account/cron/" target="_blank">cron service</a>)</li>
89
+ </ul>
90
+ </div>
91
+
92
+ <div id="tnp-body">
93
+
94
+
95
+ <form method="post" action="">
96
+ <?php $controls->init(); ?>
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
110
+ switch ($status) {
111
+ case NewsletterSystem::JOB_MISSING:
112
+ echo 'The engine schdule is missing. Try to deactivate and reactivate the Newsletter plugin.';
113
+ break;
114
+ case NewsletterSystem::JOB_LATE:
115
+ echo 'The engine schdule is late. You probably need and external scheduler trigger.';
116
+ break;
117
+ case NewsletterSystem::JOB_SKIPPED:
118
+ echo 'The engine schdule has been skipped. The schduler is overloaded or a job has fatal error and blocks the scheduler.';
119
+ break;
120
+ case NewsletterSystem::JOB_OK:
121
+ echo 'Everything seems fine!';
122
+ break;
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) {
130
+ $controls->button('trigger', 'Run manually');
131
+ }
132
+ ?>
133
+ </td>
134
+ </tr>
135
+
136
+
137
+ <tr>
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
+
145
+
146
+ <tr>
147
+ <?php
148
+ $stats = NewsletterSystem::instance()->get_cron_stats();
149
+ ?>
150
+ <td>
151
+ Cron call stats
152
+ </td>
153
+ <?php if ($stats == null) { ?>
154
+ <td class="status">
155
+ &nbsp;
156
+ </td>
157
+ <td>
158
+ Not enough data, some hours are still required.
159
+ </td>
160
+
161
+ <?php } else { ?>
162
+
163
+ <?php
164
+ $condition = $stats->good ? 1 : 0;
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) { ?>
172
+ The blog cron system is NOT triggered enough often.<br>
173
+ <?php } ?>
174
+
175
+ Samples <?php echo count($stats->deltas) ?>, average <?php echo $stats->avg ?>&nbsp;s, max <?php echo $stats->max ?>&nbsp;s, min <?php echo $stats->min ?>&nbsp;s
176
+
177
+ <canvas id="tnp-cron-chart" style="width: 550px; height: 180px"></canvas>
178
+ <script>
179
+ jQuery(function () {
180
+ var cronChartData = {
181
+ labels: <?php echo json_encode(range(1, count($stats->deltas))) ?>,
182
+ datasets: [
183
+ {
184
+ label: "Batch Average Time",
185
+ data: <?php echo json_encode($stats->deltas) ?>,
186
+ borderColor: '#2980b9',
187
+ fill: false
188
+ }]
189
+ };
190
+ var cronChartConfig = {
191
+ type: "line",
192
+ data: cronChartData,
193
+ options: {
194
+ responsive: false,
195
+ maintainAspectRatio: false,
196
+ scales: {
197
+ x: {
198
+ type: 'linear'
199
+ }
200
+ }
201
+ }
202
+ };
203
+ new Chart('tnp-cron-chart', cronChartConfig);
204
+ });
205
+ </script>
206
+
207
+ <?php $controls->button_reset() ?>
208
+
209
+ </td>
210
+ <?php } ?>
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>
225
+ You can reactivate it, but is the problem persist
226
+ <?php $controls->button('reschedule', 'Reactivate') ?>
227
+ <?php } ?>
228
+
229
+ Registered recurring schedules:<br>
230
+ <ul style="margin-left: 0em;">
231
+ <?php
232
+ if (!empty($schedules)) {
233
+ foreach ($schedules as $key => $data) {
234
+ if ($key == 'newsletter') {
235
+ echo '<li style="padding: 0; margin: 0; font-weight: bold">', esc_html($key . ' - ' . $data['interval']), ' seconds</li>';
236
+ } else {
237
+ echo '<li style="padding: 0; margin: 0;">', esc_html($key . ' - ' . $data['interval']), ' seconds</li>';
238
+ }
239
+ }
240
+ }
241
+ ?>
242
+ </ul>
243
+ </td>
244
+ </tr>
245
+
246
+
247
+ <tr>
248
+ <td>Cron URL</td>
249
+ <td class="status">&nbsp;</td>
250
+ <td>
251
+ <strong><?php echo esc_html(site_url('/wp-cron.php')) ?></strong>
252
+ <br><br>
253
+ Can be used to trigger the WordPress scheduler from an external cron service.
254
+ </td>
255
+ </tr>
256
+
257
+
258
+
259
+
260
+ <tr>
261
+ <td>
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() ?>
269
+ </td>
270
+ </tr>
271
+
272
+ <?php
273
+ $condition = (defined('DISABLE_WP_CRON') && DISABLE_WP_CRON) ? 2 : 1;
274
+ ?>
275
+ <tr>
276
+ <td>
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) { ?>
284
+ The constant <code>DISABLE_WP_CRON</code> is set to <code>true</code> (probably in <code>wp-config.php</code>). That disables the scheduler auto triggering and it's
285
+ good ONLY if you setup an external trigger.
286
+ <?php } ?>
287
+ </td>
288
+ </tr>
289
+
290
+ <tr>
291
+ <td>
292
+ <code>ALTERNATE_WP_CRON</code>
293
+ </td>
294
+ <td class="status">
295
+ &nbsp;
296
+ </td>
297
+ <td>
298
+ <?php if (defined('ALTERNATE_WP_CRON') && ALTERNATE_WP_CRON) { ?>
299
+ Using the alternate cron trigger. Rare configuration but should not be a problem.
300
+ <?php } else { ?>
301
+ Option not active, it's ok.
302
+ <?php } ?>
303
+ </td>
304
+ </tr>
305
+
306
+ <?php
307
+ $condition = NEWSLETTER_CRON_INTERVAL == 300 ? 1 : 2;
308
+ ?>
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'; ?>
316
+ <br><br>
317
+ How often the Newsletter engine should be activated. Default 300 seconds. Different value can be set on your <code>wp-config.php</code>
318
+ (not recommended).
319
+ </td>
320
+ </tr>
321
+
322
+
323
+ <?php
324
+ $condition = WP_CRON_LOCK_TIMEOUT != MINUTE_IN_SECONDS ? 2 : 1;
325
+ ?>
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'; ?>
333
+
334
+ <?php if ($condition == 2) { ?>
335
+ <br>
336
+ A non standard (<?php echo MINUTE_IN_SECONDS ?> seconds) value is specified probably in your <code>wp-config.php</code>.
337
+ <?php } ?>
338
+ </td>
339
+ </tr>
340
+
341
+
342
+ <?php
343
+ $condition = (defined('NEWSLETTER_CRON_WARNINGS') && !NEWSLETTER_CRON_WARNINGS) ? 2 : 1;
344
+ ?>
345
+ <tr>
346
+
347
+ <td>
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) { ?>
355
+ Scheduler warnings are disabled in your <code>wp-config.php</code> with the constant <code>NEWSLETTER_CRON_WARNINGS</code> set to true.
356
+ <?php } else { ?>
357
+ Scheduler warnings are enabled
358
+ <?php } ?>
359
+ </td>
360
+ </tr>
361
+
362
+ <?php
363
+ $condition = has_filter('pre_reschedule_event') ? 2 : 1;
364
+ ?>
365
+ <tr>
366
+ <td><code>pre_reschedule_event</code></td>
367
+ <td class="status">
368
+ <?php tnp_status_print_flag($condition) ?>
369
+ </td>
370
+ <td>
371
+ <?php if ($condition == 2) { ?>
372
+ One or more plugin are filtering the jobs rescheduling. If a recurrent job (like the newsletter generation with Automated) disappers
373
+ this is a good starting point.<br><br>
374
+ <?php } ?>
375
+ Attached functions:<br>
376
+ <?php echo $system->get_hook_functions('pre_reschedule_event') ?>
377
+ </td>
378
+ </tr>
379
+
380
+ <?php
381
+ $transient = get_transient('doing_cron');
382
+ ?>
383
+ <tr>
384
+ <td>Transient <code>doing_cron</code></td>
385
+ <td class="status">
386
+ <?php //tnp_status_print_flag($condition) ?>
387
+ </td>
388
+ <td>
389
+ <?php if ($transient) { ?>
390
+ <?php
391
+ echo esc_html($transient);
392
+ if (is_numeric($transient)) {
393
+ echo ' (', $controls->print_date((int) $transient), ')';
394
+ }
395
+ ?>
396
+ <?php } else { ?>
397
+ [unset]
398
+ <?php } ?>
399
+ <br><br>
400
+ When set it means the scheduler is executing background jobs. Install the WP Crontol plugin to have more information about
401
+ your site background jobs.
402
+ </td>
403
+ </tr>
404
+ </table>
405
+
406
+ </form>
407
+ </div>
408
+
409
+ <?php include NEWSLETTER_DIR . '/tnp-footer.php'; ?>
410
+
411
+ </div>
main/status.php CHANGED
@@ -1,1458 +1,1189 @@
1
- <?php
2
- /* @var $this Newsletter */
3
- /* @var $wpdb wpdb */
4
-
5
- defined('ABSPATH') || exit;
6
-
7
- wp_enqueue_script('tnp-chart');
8
-
9
- include_once NEWSLETTER_INCLUDES_DIR . '/controls.php';
10
- $controls = new NewsletterControls();
11
-
12
- $wp_cron_calls = get_option('newsletter_diagnostic_cron_calls', array());
13
- $total = 0;
14
- $wp_cron_calls_max = 0;
15
- $wp_cron_calls_min = 0;
16
- $wp_cron_calls_avg = 0;
17
- if (count($wp_cron_calls) > 20) {
18
-
19
- for ($i = 1; $i < count($wp_cron_calls); $i++) {
20
- $diff = $wp_cron_calls[$i] - $wp_cron_calls[$i - 1];
21
- $total += $diff;
22
- if ($wp_cron_calls_min == 0 || $wp_cron_calls_min > $diff) {
23
- $wp_cron_calls_min = $diff;
24
- }
25
- if ($wp_cron_calls_max < $diff) {
26
- $wp_cron_calls_max = $diff;
27
- }
28
- }
29
- $wp_cron_calls_avg = (int) ($total / (count($wp_cron_calls) - 1));
30
- }
31
-
32
- if ($controls->is_action('delete_logs')) {
33
- $files = glob(WP_CONTENT_DIR . '/logs/newsletter/*.txt');
34
- foreach ($files as $file) {
35
- if (is_file($file))
36
- unlink($file);
37
- }
38
- $secret = NewsletterModule::get_token(8);
39
- update_option('newsletter_logger_secret', $secret);
40
- $controls->messages = 'Logs deleted';
41
- }
42
-
43
- if ($controls->is_action('reschedule')) {
44
- wp_clear_scheduled_hook('newsletter');
45
- wp_schedule_event(time() + 30, 'newsletter', 'newsletter');
46
- $controls->add_message_done();
47
- }
48
-
49
- if ($controls->is_action('trigger')) {
50
- Newsletter::instance()->hook_newsletter();
51
- $controls->messages = 'Triggered';
52
- }
53
-
54
- if ($controls->is_action('conversion')) {
55
- $this->logger->info('Maybe convert to utf8mb4');
56
- require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
57
- if (function_exists('maybe_convert_table_to_utf8mb4')) {
58
- $r = maybe_convert_table_to_utf8mb4(NEWSLETTER_EMAILS_TABLE);
59
- if (!$r) {
60
- $controls->errors .= 'It was not possible to run the conversion for the table ' . NEWSLETTER_EMAILS_TABLE . ' - ';
61
- $controls->errors .= $wpdb->last_error . '<br>';
62
- }
63
- $r = maybe_convert_table_to_utf8mb4(NEWSLETTER_USERS_TABLE);
64
- if (!$r) {
65
- $controls->errors .= 'It was not possible to run the conversion for the table ' . NEWSLETTER_EMAILS_TABLE . ' - ';
66
- $controls->errors .= $wpdb->last_error . '<br>';
67
- }
68
- $controls->messages .= 'Done.';
69
- } else {
70
- $controls->errors = 'Table conversion function not available';
71
- }
72
- Newsletter::instance()->hook_newsletter();
73
- $controls->messages = 'Triggered';
74
- }
75
-
76
- if ($controls->is_action('reset_send_stats')) {
77
- update_option('newsletter_diagnostic_send_calls', [], false);
78
- $controls->add_message_done();
79
- }
80
-
81
- if ($controls->is_action('test')) {
82
-
83
- if (!NewsletterModule::is_email($controls->data['test_email'])) {
84
- $controls->errors = 'The test email address is not set or is not correct.';
85
- }
86
-
87
- if (empty($controls->errors)) {
88
-
89
- $options = $controls->data;
90
-
91
- if ($controls->data['test_email'] == $this->options['sender_email']) {
92
- $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>';
93
- }
94
-
95
- $message = NewsletterMailerAddon::get_test_message($controls->data['test_email'], 'Newsletter test email at ' . date(DATE_ISO8601));
96
-
97
- $r = $this->deliver($message);
98
-
99
- if (!is_wp_error($r)) {
100
- $options['mail'] = 1;
101
- $controls->messages .= '<strong>SUCCESS</strong><br>';
102
- $controls->messages .= 'Anyway if the message does not appear the mailbox (check even the spam folder) you can ';
103
- $controls->messages .= '<a href="https://www.thenewsletterplugin.com/documentation/?p=15170" target="_blank"><strong>read more here</strong></a>.';
104
- } else {
105
- $options['mail'] = 0;
106
- $options['mail_error'] = $r->get_error_message();
107
-
108
- $controls->errors .= '<strong>FAILED</strong> (' . $r->get_error_message() . ')<br>';
109
-
110
- if (!empty($this->options['return_path'])) {
111
- $controls->errors .= '- Try to remove the return path on main settings.<br>';
112
- }
113
-
114
- $controls->errors .= '<a href="https://www.thenewsletterplugin.com/documentation/?p=15170" target="_blank"><strong>' . __('Read more', 'newsletter') . '</strong></a>.';
115
-
116
- $parts = explode('@', $this->options['sender_email']);
117
- $sitename = strtolower($_SERVER['SERVER_NAME']);
118
- if (substr($sitename, 0, 4) == 'www.') {
119
- $sitename = substr($sitename, 4);
120
- }
121
- if (strtolower($sitename) != strtolower($parts[1])) {
122
- $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>';
123
- }
124
- }
125
- $this->save_options($options, 'status');
126
- }
127
- }
128
-
129
- if ($controls->is_action('stats_email_column_upgrade')) {
130
- $this->query("alter table " . NEWSLETTER_STATS_TABLE . " drop index email_id");
131
- $this->query("alter table " . NEWSLETTER_STATS_TABLE . " drop index user_id");
132
- $this->query("alter table `" . NEWSLETTER_STATS_TABLE . "` modify column `email_id` int(11) not null default 0");
133
- $this->query("create index email_id on " . NEWSLETTER_STATS_TABLE . " (email_id)");
134
- $this->query("create index user_id on " . NEWSLETTER_STATS_TABLE . " (user_id)");
135
- $controls->add_message_done();
136
- update_option('newsletter_stats_email_column_upgraded', true);
137
- }
138
-
139
- $options = $this->get_options('status');
140
-
141
- // Compute the number of newsletters ongoing and other stats
142
- $emails = $wpdb->get_results("select * from " . NEWSLETTER_EMAILS_TABLE . " where status='sending' and send_on<" . time() . " order by id asc");
143
- $total = 0;
144
- $queued = 0;
145
- foreach ($emails as $email) {
146
- $total += $email->total;
147
- $queued += $email->total - $email->sent;
148
- }
149
- $speed = Newsletter::$instance->options['scheduler_max'];
150
-
151
- function tnp_status_print_flag($condition) {
152
- switch ($condition) {
153
- case 0: echo ' <span class="tnp-ko">KO</span>';
154
- break;
155
- case 1: echo '<span class="tnp-ok">OK</span>';
156
- break;
157
- case 2: echo '<span class="tnp-maybe">MAYBE</span>';
158
- break;
159
- }
160
- }
161
-
162
- class TNP_WPDB extends wpdb {
163
-
164
- public function get_table_charset($table) {
165
- return parent::get_table_charset($table);
166
- }
167
-
168
- }
169
-
170
- $tnp_wpdb = new TNP_WPDB(DB_USER, DB_PASSWORD, DB_NAME, DB_HOST);
171
- ?>
172
- <style>
173
- table.widefat tbody tr>td:first-child {
174
- width: 150px!important;
175
- }
176
- </style>
177
-
178
- <div class="wrap tnp-main-status" id="tnp-wrap">
179
-
180
- <?php include NEWSLETTER_DIR . '/tnp-header.php'; ?>
181
-
182
- <div id="tnp-heading">
183
-
184
- <h2><?php _e('System Status', 'newsletter') ?></h2>
185
-
186
- </div>
187
-
188
- <div id="tnp-body">
189
-
190
- <form method="post" action="">
191
- <?php $controls->init(); ?>
192
-
193
-
194
- <h3>Delivery</h3>
195
- <table class="widefat" id="tnp-status-table">
196
-
197
- <thead>
198
- <tr>
199
- <th>Parameter</th>
200
- <th><?php _e('Status', 'newsletter') ?></th>
201
- <th>Action</th>
202
- </tr>
203
-
204
- </thead>
205
-
206
- <tbody>
207
-
208
- <tr>
209
- <td>Delivering</td>
210
- <td>
211
- &nbsp;
212
- </td>
213
- <td>
214
- <?php if (count($emails)) { ?>
215
- Delivering <?php echo count($emails) ?> newsletters to about <?php echo $queued ?> recipients.
216
- At speed of <?php echo $speed ?> emails per hour it will take <?php printf('%.1f', $queued / $speed) ?> hours to finish.
217
-
218
- <?php } else { ?>
219
- Nothing delivering right now
220
- <?php } ?>
221
- </td>
222
-
223
- </tr>
224
- <tr>
225
- <td>Mailer</td>
226
- <td>
227
- &nbsp;
228
- </td>
229
- <td>
230
- <?php
231
- $mailer = Newsletter::instance()->get_mailer();
232
- $name = 'Unknown';
233
- if (is_object($mailer)) {
234
- if (method_exists($mailer, 'get_description')) {
235
- $name = $mailer->get_description();
236
- } else {
237
- $name = get_class($mailer);
238
- }
239
- }
240
- ?>
241
-
242
- <?php echo esc_html($name) ?>
243
- </td>
244
- </tr>
245
- <?php
246
- // Send calls stats
247
- $send_calls = get_option('newsletter_diagnostic_send_calls', []);
248
- $chart_data = [];
249
- $chart_labels = [];
250
- $chart_labels_idx = 1;
251
- $chart_point_labels = [];
252
- if ($send_calls) {
253
- $send_max = 0;
254
- $send_min = PHP_INT_MAX;
255
- $send_total_time = 0;
256
- $send_total_emails = 0;
257
- $send_completed = 0;
258
- for ($i = 0; $i < count($send_calls); $i++) {
259
- // 0 - batch start time
260
- // 1 - batch end time
261
- // 2 - number of sent email in this batch
262
- // 3 - 0: prematurely stopped, 1: completed
263
- if (empty($send_calls[$i][2]))
264
- continue;
265
-
266
- $delta = $send_calls[$i][1] - $send_calls[$i][0];
267
- $send_total_time += $delta;
268
- $send_total_emails += $send_calls[$i][2];
269
- $send_mean = $delta / $send_calls[$i][2];
270
- $chart_data[] = $send_mean;
271
- $chart_labels[] = $chart_labels_idx;
272
- $chart_labels_idx++;
273
- $chart_point_labels[] = 'Emails: ' . $send_calls[$i][2];
274
- if ($send_min > $send_mean) {
275
- $send_min = $send_mean;
276
- }
277
- if ($send_max < $send_mean) {
278
- $send_max = $send_mean;
279
- }
280
- if (isset($send_calls[$i][3]) && $send_calls[$i][3]) {
281
- $send_completed++;
282
- }
283
- }
284
- $send_mean = $send_total_time / $send_total_emails;
285
- ?>
286
- <tr>
287
- <td id="tnp-speed">
288
- Send details
289
- </td>
290
- <td>
291
- <?php if ($send_mean > 1) { ?>
292
- <span class="tnp-ko">KO</span>
293
- <?php } else { ?>
294
- <span class="tnp-ok">OK</span>
295
- <?php } ?>
296
- </td>
297
- <td>
298
- <?php if ($send_mean > 1) { ?>
299
- <strong>Sending an email is taking more than 1 second, rather slow.</strong>
300
- <a href="https://www.thenewsletterplugin.com/documentation/status-panel#status-performance" target="_blank">Read more</a>.
301
- <?php } ?>
302
- Average time to send an email: <?php echo sprintf("%.2f", $send_mean) ?> seconds<br>
303
- <?php if ($send_mean > 0) { ?>
304
- Max speed: <?php echo sprintf("%.2f", 1.0 / $send_mean * 3600) ?> emails per hour<br>
305
- <?php } ?>
306
-
307
- Max mean time measured: <?php echo sprintf("%.2f", $send_max) ?> seconds<br>
308
- Min mean time measured: <?php echo sprintf("%.2f", $send_min) ?> seconds<br>
309
- Total email in the sample: <?php echo $send_total_emails ?><br>
310
- Runs in the sample: <?php echo count($send_calls); ?><br>
311
- Runs prematurely interrupted: <?php echo sprintf("%.2f", (count($send_calls) - $send_completed) * 100.0 / count($send_calls)) ?>%<br>
312
- <br>
313
- <?php $controls->button_reset('reset_send_stats') ?>
314
-
315
- <canvas id="tnp-send-chart" style="width: 550px; height: 150px"></canvas>
316
- <script>
317
- jQuery(function () {
318
- var sendChartData = {
319
- labels: <?php echo json_encode($chart_labels) ?>,
320
- datasets: [
321
- {
322
- label: "Batch Average Time",
323
- data: <?php echo json_encode($chart_data) ?>,
324
- borderColor: '#2980b9',
325
- fill: false
326
- }]
327
- };
328
- var sendChartConfig = {
329
- type: "line",
330
- data: sendChartData,
331
- options: {
332
- responsive: false,
333
- maintainAspectRatio: false
334
- }
335
- };
336
- new Chart('tnp-send-chart', sendChartConfig);
337
- });
338
- </script>
339
- </td>
340
- </tr>
341
- <?php } else { ?>
342
- <tr>
343
- <td>
344
- Send details
345
- </td>
346
- <td>
347
- <span class="tnp-maybe">MAYBE</span>
348
- </td>
349
- <td>
350
- No data avaiable. Send a newsletter to collect some sending statistics.
351
- </td>
352
- </tr>
353
- <?php } ?>
354
-
355
- <tr>
356
- <?php
357
- $time = wp_next_scheduled('newsletter');
358
- $res = true;
359
- $condition = 1;
360
- if ($time === false) {
361
- $res = false;
362
- $condition = 0;
363
- }
364
- $delta = $time - time();
365
- if ($delta <= -600) {
366
- $res = false;
367
- $condition = 0;
368
- }
369
- ?>
370
- <td>Newsletter delivery engine job</td>
371
- <td>
372
- <?php tnp_status_print_flag($condition) ?>
373
- </td>
374
- <td>
375
- <?php if ($time === false) { ?>
376
- No next execution is planned.
377
- <?php $controls->button('reschedule', 'Reset') ?>
378
- <?php } else if ($delta <= -600) { ?>
379
- The scheduler is very late: <?php echo $delta ?> seconds (<a href="https://www.thenewsletterplugin.com/plugins/newsletter/newsletter-delivery-engine" target="_blank">read more</a>)
380
- <?php $controls->button('trigger', 'Trigger') ?>
381
- <?php } else { ?>
382
- Next execution is planned in <?php echo $delta ?> seconds (negative values are ok).
383
- <?php } ?>
384
- </td>
385
- </tr>
386
-
387
- </tbody>
388
- </table>
389
-
390
- <h3>General checks</h3>
391
- <table class="widefat" id="tnp-status-table">
392
-
393
- <thead>
394
- <tr>
395
- <th>Parameter</th>
396
- <th><?php _e('Status', 'newsletter') ?></th>
397
- <th>Action</th>
398
- </tr>
399
-
400
- </thead>
401
-
402
- <tbody>
403
-
404
- <tr>
405
- <?php
406
- $page_id = $this->get_newsletter_page_id();
407
- $page = $this->get_newsletter_page();
408
- $condition = 1;
409
- if ($page_id) {
410
- if (!$page || $page->post_status !== 'publish') {
411
- $condition = 0;
412
- }
413
- } else {
414
- $condition = 2;
415
- }
416
- ?>
417
- <td>
418
- Dedicated page<br>
419
- <small>The blog page Newsletter uses for messages</small>
420
- </td>
421
- <td>
422
- <?php tnp_status_print_flag($condition) ?>
423
- </td>
424
- <td>
425
- <?php if ($condition == 2) { ?>
426
- Newsletter is using a neutral page to show messages, if you want to use a dedicated page, configure it on
427
- <a href="?page=newsletter_main_main">main settings</a>.
428
- <?php } else if ($condition == 0) { ?>
429
- A dedicated page is set but it is no more available or no more published. Review the dedicated page on
430
- <a href="?page=newsletter_main_main">main settings</a>.
431
- <?php } ?>
432
- </td>
433
- </tr>
434
-
435
- <tr>
436
- <?php
437
- $page_id = $this->get_newsletter_page_id();
438
- $page = $this->get_newsletter_page();
439
- $condition = 1;
440
- if ($page_id) {
441
- if (!$page) {
442
- $condition = 0;
443
- } else {
444
- $content = $page->post_content;
445
- if (strpos($content, '[newsletter]') === false && strpos($content, '[newsletter ') === false) {
446
- $condition = 2;
447
- }
448
- }
449
- }
450
- ?>
451
- <td>
452
- Dedicated page content<br>
453
- </td>
454
- <td>
455
- <?php tnp_status_print_flag($condition) ?>
456
- </td>
457
- <td>
458
- <?php if ($condition == 2) { ?>
459
- The page seems to not contain the <code>[newsletter]</code>, but sometime it cannot be detected if you use
460
- a visual composer. <a href="post.php?post=<?php echo $page->ID ?>&action=edit" target="_blank">Please, check the page</a>.
461
- <?php } else if ($condition == 0) { ?>
462
- The dedicated page seems to not be available.
463
- <?php } ?>
464
- </td>
465
- </tr>
466
-
467
- <?php
468
- $method = '';
469
- if (function_exists('get_filesystem_method')) {
470
- $method = get_filesystem_method(array(), WP_PLUGIN_DIR);
471
- }
472
- if (empty($method))
473
- $condition = 2;
474
- else if ($method == 'direct')
475
- $condition = 1;
476
- else
477
- $condition = 0;
478
- ?>
479
- <tr>
480
- <td>Add-ons installable</td>
481
- <td>
482
- <?php tnp_status_print_flag($condition) ?>
483
- </td>
484
- <td>
485
- <?php if ($condition == 2) { ?>
486
- No able to check, just try the add-ons manager one click install
487
- <?php } else if ($condition == 1) { ?>
488
- The add-ons manager can install our add-ons
489
- <?php } else { ?>
490
- The plugins dir could be read-only, you can install add-ons uploading the package from the
491
- plugins panel (or uploading them directly via FTP). This is unusual you should ask te provider
492
- about file and folder permissions.
493
- <?php } ?>
494
- </td>
495
-
496
- </tr>
497
-
498
-
499
-
500
-
501
-
502
-
503
- <?php
504
- $return_path = $this->options['return_path'];
505
- if (!empty($return_path)) {
506
- list($return_path_local, $return_path_domain) = explode('@', $return_path);
507
- }
508
- $sender = $this->options['sender_email'];
509
- if (!empty($sender)) {
510
- list($sender_local, $sender_domain) = explode('@', $sender);
511
- }
512
- ?>
513
- <tr>
514
- <td>Return path</td>
515
- <td>
516
- <?php if (empty($return_path)) { ?>
517
- <span class="tnp-ok">OK</span>
518
- <?php } else { ?>
519
- <?php if ($sender_domain != $return_path_domain) { ?>
520
- <span class="tnp-maybe">MAYBE</span>
521
- <?php } else { ?>
522
- <span class="tnp-ok">OK</span>
523
- <?php } ?>
524
- <?php } ?>
525
-
526
- </td>
527
- <td>
528
- <?php if (!empty($return_path)) { ?>
529
- Some providers require the return path domain <code><?php echo esc_html($return_path_domain) ?></code> to be identical
530
- to the sender domain <code><?php echo esc_html($sender_domain) ?></code>. See the main settings.
531
- <?php } else { ?>
532
- <?php } ?>
533
- </td>
534
-
535
- </tr>
536
-
537
-
538
-
539
-
540
-
541
- <tr>
542
- <?php
543
- $condition = NEWSLETTER_EXTENSION_UPDATE ? 1 : 0;
544
- ?>
545
- <td>Addons update</td>
546
- <td>
547
- <?php tnp_status_print_flag($condition) ?>
548
- </td>
549
- <td>
550
- <?php if ($condition == 0) { ?>
551
- Newsletter Addons update is disabled (probably in your <code>wp-config.php</code> file the constant
552
- <code>NEWSLETTER_EXTENSION_UPDATE</code> is set to <code>true</code>)
553
- <?php } else { ?>
554
- Newsletter Addons can be updated
555
- <?php } ?>
556
- </td>
557
-
558
- </tr>
559
-
560
-
561
-
562
-
563
-
564
-
565
-
566
-
567
-
568
- <?php
569
- $schedules = wp_get_schedules();
570
- $res = false;
571
- if (!empty($schedules)) {
572
- foreach ($schedules as $key => $data) {
573
- if ($key == 'newsletter') {
574
- $res = true;
575
- break;
576
- }
577
- }
578
- }
579
- ?>
580
-
581
- <tr>
582
- <td>
583
- Newsletter schedule
584
- </td>
585
- <td>
586
- <?php if ($res === false) { ?>
587
- <span class="tnp-ko">KO</span>
588
- <?php } else { ?>
589
- <span class="tnp-ok">OK</span>
590
- <?php } ?>
591
- </td>
592
- <td>
593
- <?php if ($res === false) { ?>
594
- The Newsletter schedule is not present probably another plugin is interfering with the starndard WordPress schuling system.<br>
595
- <?php } else { ?>
596
- <?php } ?>
597
-
598
- WordPress registered schedules:<br>
599
- <?php
600
- if (!empty($schedules)) {
601
- foreach ($schedules as $key => $data) {
602
- echo esc_html($key . ' - ' . $data['interval']) . ' seconds<br>';
603
- }
604
- }
605
- ?>
606
- </td>
607
- </tr>
608
-
609
-
610
- <?php
611
- $res = 0;
612
- $response = wp_remote_post(home_url('/') . '?na=test');
613
- if (is_wp_error($response)) {
614
- $res = 1;
615
- $message = $response->get_error_message();
616
- } else if (wp_remote_retrieve_response_code($response) != 200) {
617
- $res = 1;
618
- $message = wp_remote_retrieve_response_message($response);
619
- } else if (wp_remote_retrieve_body($response) !== 'ok') {
620
- $res = 2;
621
- }
622
-
623
- ?>
624
- <tr>
625
- <td>
626
- Action call
627
- </td>
628
- <td>
629
- <?php if (!$res) { ?>
630
- <span class="tnp-ko">KO</span>
631
- <?php } else { ?>
632
- <span class="tnp-ok">OK</span>
633
- <?php } ?>
634
- </td>
635
- <td>
636
- <?php if ($res === 1) { ?>
637
- The blog is not responding to Newsletter URLs: ask the provider or your IT consultant to check this problem. Report the URL and error below<br>
638
- Error: <?php echo esc_html($message) ?><br>
639
- <?php } else if ($res === 2) { ?>
640
- The response does not contain the "ok" text: probably a caching/optimization plugin or a server configuration is forcing the blog to ignore
641
- the URL's query string. Reported that to your system administrator.
642
- <?php } ?>
643
- Url: <?php echo esc_html(home_url('/') . '?na=test') ?><br>
644
- </td>
645
- </tr>
646
-
647
-
648
- <tr>
649
- <?php
650
- $res = true;
651
- $response = wp_remote_get('http://www.thenewsletterplugin.com/wp-content/extensions.json');
652
- $condition = 1;
653
- if (is_wp_error($response)) {
654
- $res = false;
655
- $condition = 0;
656
- $message = $response->get_error_message();
657
- } else {
658
- if (wp_remote_retrieve_response_code($response) != 200) {
659
- $res = false;
660
- $condition = 0;
661
- $message = wp_remote_retrieve_response_message($response);
662
- }
663
- }
664
- ?>
665
-
666
- <td>
667
- Addons version check<br>
668
- <small>Your blog can check the professional addon updates?</small>
669
- </td>
670
- <td>
671
- <?php tnp_status_print_flag($condition) ?>
672
- </td>
673
- <td>
674
- <?php if ($condition == 0) { ?>
675
- The blog cannot contact www.thenewsletterplugin.com to check the license or the extension versions.<br>
676
- Error: <?php echo esc_html($message) ?><br>
677
- <?php } else { ?>
678
-
679
- <?php } ?>
680
- </td>
681
- </tr>
682
-
683
-
684
-
685
-
686
-
687
-
688
-
689
-
690
-
691
- <?php /*
692
- $memory = intval(WP_MEMORY_LIMIT);
693
- if (false !== strpos(WP_MEMORY_LIMIT, 'G'))
694
- $memory *= 1024;
695
- ?>
696
- <tr>
697
- <td>
698
- PHP memory limit
699
- </td>
700
- <td>
701
- <?php if ($memory < 64) { ?>
702
- <span class="tnp-ko">MAYBE</span>
703
- <?php } else if ($memory < 128) { ?>
704
- <span class="tnp-maybe">MAYBE</span>
705
- <?php } else { ?>
706
- <span class="tnp-ok">OK</span>
707
- <?php } ?>
708
- </td>
709
- <td>
710
- WordPress WP_MEMORY_LIMIT is set to <?php echo $memory ?> megabyte but your PHP setting could allow more than that.
711
- Anyway we suggest to set the value to at least 64M.
712
- <a href="https://www.thenewsletterplugin.com/documentation/status-panel#status-memory" target="_blank">Read more</a>.
713
- <?php if ($memory < 64) { ?>
714
- This value is too low you should increase it adding <code>define('WP_MEMORY_LIMIT', '64M');</code> to your <code>wp-config.php</code>.
715
- <a href="https://www.thenewsletterplugin.com/documentation/status-panel#status-memory" target="_blank">Read more</a>.
716
- <?php } else if ($memory < 128) { ?>
717
- The value should be fine, it depends on how many plugins you're running and how many resource are required by your theme.
718
- Blank pages may happen with low memory problems. Eventually increase it adding <code>define('WP_MEMORY_LIMIT', '128M');</code>
719
- to your <code>wp-config.php</code>.
720
- <a href="https://www.thenewsletterplugin.com/documentation/status-panel#status-memory" target="_blank">Read more</a>.
721
- <?php } else { ?>
722
-
723
- <?php } ?>
724
-
725
- </td>
726
- </tr>
727
- */ ?>
728
-
729
- <?php
730
- $ip = gethostbyname($_SERVER['HTTP_HOST']);
731
- $name = gethostbyaddr($ip);
732
- $res = true;
733
- if (strpos($name, '.secureserver.net') !== false) {
734
- //$smtp = get_option('newsletter_main_smtp');
735
- //if (!empty($smtp['enabled']))
736
- $res = false;
737
- $message = 'If you\'re hosted with GoDaddy, be sure to set their SMTP (relay-hosting.secureserver.net, without username and password) to send emails
738
- on Newsletter SMTP panel.
739
- Remember they limits you to 250 emails per day. Open them a ticket for more details.';
740
- }
741
- if (strpos($name, '.aruba.it') !== false) {
742
- $res = false;
743
- $message = 'If you\'re hosted with Aruba consider to use an external SMTP (Sendgrid, Mailjet, Mailgun, Amazon SES, Elasticemail, Sparkpost, ...)
744
- since their mail service is not good. If you have your personal email with them, you can try to use the SMTP of your
745
- pesonal account. Ask the support for the SMTP parameters and configure them on Newsletter SMTP panel.';
746
- }
747
- ?>
748
- <tr>
749
- <td>Your Server</td>
750
- <td>
751
- <?php if ($res === false) { ?>
752
- <span class="tnp-maybe">MAYBE</span>
753
- <?php } else { ?>
754
- <span class="tnp-ok">OK</span>
755
- <?php } ?>
756
-
757
-
758
- </td>
759
- <td>
760
- <?php if ($res === false) { ?>
761
- <?php echo $message ?>
762
- <?php } else { ?>
763
-
764
- <?php } ?>
765
- IP: <?php echo $ip ?><br>
766
- Name: <?php echo $name ?><br>
767
- </td>
768
- </tr>
769
-
770
- <?php
771
- wp_mkdir_p(NEWSLETTER_LOG_DIR);
772
- $condition = is_dir(NEWSLETTER_LOG_DIR) && is_writable(NEWSLETTER_LOG_DIR) ? 1 : 0;
773
- if ($condition) {
774
- @file_put_contents(NEWSLETTER_LOG_DIR . '/test.txt', "");
775
- $condition = is_file(NEWSLETTER_LOG_DIR . '/test.txt') ? 1 : 0;
776
- if ($condition) {
777
- @unlink(NEWSLETTER_LOG_DIR . '/test.txt');
778
- }
779
- }
780
- ?>
781
- <tr>
782
- <td>
783
- Log folder
784
- </td>
785
- <td>
786
- <?php tnp_status_print_flag($condition) ?>
787
- </td>
788
- <td>
789
- The log folder is <?php echo esc_html(NEWSLETTER_LOG_DIR) ?><br>
790
- <?php if (!$res) { ?>
791
- Cannot create the folder or it is not writable.
792
- <?php } ?>
793
- </td>
794
- </tr>
795
- </tbody>
796
- </table>
797
-
798
-
799
- <h3>WordPress Scheduler/Cron</h3>
800
-
801
- <table class="widefat" id="tnp-status-table">
802
- <thead>
803
- <tr>
804
- <th>Parameter</th>
805
- <th><?php _e('Status', 'newsletter') ?></th>
806
- <th>Action</th>
807
- </tr>
808
- </thead>
809
- <tbody>
810
- <tr>
811
- <?php
812
- $condition = (defined('NEWSLETTER_CRON_WARNINGS') && !NEWSLETTER_CRON_WARNINGS) ? 2 : 1;
813
- ?>
814
- <td>
815
- Cron warnings<br>
816
- </td>
817
- <td>
818
- <?php tnp_status_print_flag($condition) ?>
819
- </td>
820
- <td>
821
- <?php if ($condition == 2) { ?>
822
- Scheduler warnings are disabled in your <code>wp-config.php</code> with the constant <code>NEWSLETTER_CRON_WARNINGS</code> set to true.
823
- <?php } else { ?>
824
- Scheduler warnings are enabled
825
- <?php } ?>
826
- </td>
827
- </tr>
828
- <tr>
829
- <?php
830
- $condition = (defined('DISABLE_WP_CRON') && DISABLE_WP_CRON) ? 2 : 1;
831
- ?>
832
- <td>
833
- WordPress scheduler auto trigger
834
- </td>
835
- <td>
836
- <?php tnp_status_print_flag($condition) ?>
837
- </td>
838
- <td>
839
- <?php if ($condition == 2) { ?>
840
- The constant <code>DISABLE_WP_CRON</code> is set to true (probably in <code>wp-config.php</code>). That disables the scheduler auto triggering and it's
841
- good ONLY if you setup an external trigger.
842
- <?php } ?>
843
- </td>
844
- </tr>
845
- <tr>
846
- <td>
847
- Alternate cron
848
- </td>
849
- <td>
850
- &nbsp;
851
- </td>
852
- <td>
853
- <?php if (defined('ALTERNATE_WP_CRON') && ALTERNATE_WP_CRON) { ?>
854
- Using the alternate cron trigger.
855
- <?php } ?>
856
- </td>
857
- </tr>
858
-
859
- <tr>
860
- <?php
861
- $condition = ($wp_cron_calls_avg > NEWSLETTER_CRON_INTERVAL * 1.1) ? 0 : 1;
862
- ?>
863
- <td>
864
- Cron calls
865
- </td>
866
- <td>
867
- <?php tnp_status_print_flag($condition) ?>
868
- </td>
869
- <td>
870
- <?php if ($condition == 0) { ?>
871
- The blog cron system is NOT triggered enough often.
872
- <?php } ?>
873
- <br>
874
- Trigger interval: average <?php echo $wp_cron_calls_avg ?>&nbsp;s, max <?php echo $wp_cron_calls_max ?>&nbsp;s, min <?php echo $wp_cron_calls_min ?>&nbsp;s
875
- <br>
876
- <a href="https://www.thenewsletterplugin.com/documentation/delivery-and-spam/newsletter-delivery-engine/" target="_blank">Read more</a>
877
- </td>
878
- </tr>
879
- <tr>
880
- <?php
881
- $res = true;
882
- $response = wp_remote_get(site_url('/wp-cron.php') . '?' . time());
883
- if (is_wp_error($response)) {
884
- $res = false;
885
- $message = $response->get_error_message();
886
- } else {
887
- if (wp_remote_retrieve_response_code($response) != 200) {
888
- $res = false;
889
- $message = wp_remote_retrieve_response_message($response);
890
- }
891
- }
892
- $condition = !$res ? 0 : 1;
893
- ?>
894
-
895
- <td>
896
- WordPress scheduler auto trigger call
897
- </td>
898
- <td>
899
- <?php tnp_status_print_flag($condition) ?>
900
- </td>
901
- <td>
902
- <?php if ($condition == 0) { ?>
903
- The blog cannot auto-trigger the internal scheduler, if an external trigger is used this could not be a real problem.<br>
904
- Error: <?php echo esc_html($message) ?><br>
905
- <?php } else { ?>
906
-
907
- <?php } ?>
908
- Cron URL: <?php echo esc_html(site_url('/wp-cron.php')) ?><br>
909
- <br>
910
- <a href="https://www.thenewsletterplugin.com/documentation/delivery-and-spam/newsletter-delivery-engine/" target="_blank">Read more</a>
911
- </td>
912
- </tr>
913
- </tbody>
914
- </table>
915
-
916
-
917
-
918
- <h3>WordPress</h3>
919
-
920
- <table class="widefat" id="tnp-status-table">
921
- <thead>
922
- <tr>
923
- <th>Parameter</th>
924
- <th><?php _e('Status', 'newsletter') ?></th>
925
- <th>Action</th>
926
- </tr>
927
- </thead>
928
- <tbody>
929
-
930
- <tr>
931
- <?php
932
- $condition = (defined('WP_DEBUG') && WP_DEBUG) ? 2 : 1;
933
- ?>
934
- <td>
935
- WordPress debug mode
936
- </td>
937
- <td>
938
- <?php tnp_status_print_flag($condition) ?>
939
- </td>
940
- <td>
941
- <?php if (defined('WP_DEBUG') && WP_DEBUG) { ?>
942
- WordPress is in debug mode it is not recommended on a production system. See the constant <code>WP_DEBUG</code> inside the <code>wp-config.php</code>.
943
- <?php } else { ?>
944
-
945
- <?php } ?>
946
- </td>
947
- </tr>
948
-
949
-
950
-
951
- <tr>
952
- <?php
953
- $charset = get_option('blog_charset');
954
- $condition = $charset === 'UTF-8' ? 1 : 0;
955
- ?>
956
- <td>Blog Charset</td>
957
- <td>
958
- <?php tnp_status_print_flag($condition) ?>
959
- </td>
960
- <td>
961
- Charset: <?php echo esc_html($charset) ?>
962
- <br>
963
-
964
- <?php if ($condition == 1) { ?>
965
-
966
- <?php } else { ?>
967
- It is recommended to use
968
- the <code>UTF-8</code> charset but the <a href="https://codex.wordpress.org/Converting_Database_Character_Sets" target="_blank">conversion</a>
969
- could be tricky. If you're not experiencing problem, leave things as is.
970
- <?php } ?>
971
- </td>
972
- </tr>
973
-
974
- <tr>
975
- <?php
976
- $condition = (strpos(home_url('/'), 'http') !== 0) ? 0 : 1;
977
- ?>
978
- <td>Home URL</td>
979
- <td>
980
- <?php tnp_status_print_flag($condition) ?>
981
- </td>
982
- <td>
983
- Value: <?php echo home_url('/'); ?>
984
- <br>
985
- <?php if ($condition == 0) { ?>
986
- Your home URL is not absolute, emails require absolute URLs.
987
- Probably you have a protocol agnostic plugin installed to manage both HTTPS and HTTP in your
988
- blog.
989
- <?php } else { ?>
990
-
991
- <?php } ?>
992
- </td>
993
- </tr>
994
-
995
- <tr>
996
- <?php
997
- $condition = (strpos(WP_CONTENT_URL, 'http') !== 0) ? 0 : 1;
998
- ?>
999
- <td>WP_CONTENT_URL</td>
1000
- <td>
1001
- <?php tnp_status_print_flag($condition) ?>
1002
- </td>
1003
- <td>
1004
- Value: <?php echo esc_html(WP_CONTENT_URL); ?>
1005
- <br>
1006
- <?php if ($condition == 0) { ?>
1007
- Your content URL is not absolute, emails require absolute URLs when they have images inside.
1008
- Newsletter tries to deal with this problem but when a problem with images persists, you should try to remove
1009
- from your <code>wp-config.php</code> the <code>WP_CONTENT_URL</code> define and check again.
1010
- <?php } else { ?>
1011
-
1012
- <?php } ?>
1013
- </td>
1014
- </tr>
1015
-
1016
- <tr>
1017
- <?php
1018
- set_transient('newsletter_transient_test', 1, 300);
1019
- delete_transient('newsletter_transient_test');
1020
- $res = get_transient('newsletter_transient_test');
1021
- $condition = ($res !== false) ? 0 : 1;
1022
- ?>
1023
- <td>WordPress transients</td>
1024
- <td>
1025
- <?php tnp_status_print_flag($condition) ?>
1026
- </td>
1027
- <td>
1028
- <?php if ($res !== false) { ?>
1029
- Transients cannot be delete. This can block the delivery engine. Usually it is due to a not well coded plugin installed.
1030
- <?php } else { ?>
1031
- <?php } ?>
1032
- </td>
1033
- </tr>
1034
- </tbody>
1035
- </table>
1036
-
1037
-
1038
-
1039
- <h3>PHP</h3>
1040
- <table class="widefat" id="tnp-status-table">
1041
- <thead>
1042
- <tr>
1043
- <th>Parameter</th>
1044
- <th><?php _e('Status', 'newsletter') ?></th>
1045
- <th>Action</th>
1046
- </tr>
1047
- </thead>
1048
- <tbody>
1049
- <tr>
1050
- <td>PHP version</td>
1051
- <td>
1052
- <?php if (version_compare(phpversion(), '5.6', '<')) { ?>
1053
- <span class="tnp-ko">KO</span>
1054
- <?php } else { ?>
1055
- <span class="tnp-ok">OK</span>
1056
- <?php } ?>
1057
-
1058
- </td>
1059
- <td>
1060
- Your PHP version is <?php echo phpversion() ?><br>
1061
- <?php if (version_compare(phpversion(), '5.3', '<')) { ?>
1062
- Newsletter plugin works correctly with PHP version 5.6 or greater. Ask your provider to upgrade your PHP. Your version is
1063
- unsupported even by the PHP community.
1064
- <?php } ?>
1065
- </td>
1066
-
1067
- </tr>
1068
-
1069
- <tr>
1070
- <?php
1071
- $value = (int) ini_get('max_execution_time');
1072
- $res = true;
1073
- $condition = 1;
1074
- if ($value != 0 && $value < NEWSLETTER_CRON_INTERVAL) {
1075
- $res = set_time_limit(NEWSLETTER_CRON_INTERVAL);
1076
- if ($res)
1077
- $condition = 1;
1078
- else
1079
- $condition = 0;
1080
- }
1081
- ?>
1082
- <td>PHP execution time limit</td>
1083
- <td>
1084
- <?php tnp_status_print_flag($condition) ?>
1085
- </td>
1086
- <td>
1087
- <?php if (!$res) { ?>
1088
- Your PHP execution time limit is <?php echo $value ?> seconds. It cannot be changed and it is too lower to grant the maximum delivery rate of Newsletter.
1089
- <?php } else { ?>
1090
- Your PHP execution time limit is <?php echo $value ?> seconds and can be eventually changed by Newsletter.<br>
1091
- <?php } ?>
1092
-
1093
- </td>
1094
-
1095
- </tr>
1096
-
1097
-
1098
- <tr>
1099
- <?php
1100
- $condition = function_exists('curl_version');
1101
- ?>
1102
- <td>Curl version</td>
1103
- <td>
1104
- <?php if (!$condition) { ?>
1105
- <span class="tnp-ko">KO</span>
1106
- <?php } else { ?>
1107
- <span class="tnp-ok">OK</span>
1108
- <?php } ?>
1109
-
1110
- </td>
1111
- <td>
1112
- <?php
1113
- if (!$condition) {
1114
- echo 'cUrl is not available, ask the provider to install it and activate the PHP cUrl library';
1115
- } else {
1116
- $version = curl_version();
1117
- echo 'Version: ' . $version['version'] . '<br>';
1118
- echo 'SSL Version: ' . $version['ssl_version'] . '<br>';
1119
- }
1120
- ?>
1121
- </td>
1122
-
1123
- </tr>
1124
- <?php if (ini_get('opcache.validate_timestamps') === '0') { ?>
1125
- <tr>
1126
- <td>
1127
- Opcache
1128
- </td>
1129
-
1130
- <td>
1131
- <span class="tnp-ko">KO</span>
1132
- </td>
1133
-
1134
- <td>
1135
- You have the PHP opcache active with file validation disable so every blog plugins update needs a webserver restart!
1136
- </td>
1137
- </tr>
1138
- <?php } ?>
1139
- </tbody>
1140
- </table>
1141
-
1142
- <h3>Database</h3>
1143
- <table class="widefat" id="tnp-status-table">
1144
- <thead>
1145
- <tr>
1146
- <th>Parameter</th>
1147
- <th><?php _e('Status', 'newsletter') ?></th>
1148
- <th>Action</th>
1149
- </tr>
1150
- </thead>
1151
- <tbody>
1152
- <tr>
1153
- <td>Database Charset</td>
1154
- <td>
1155
- <?php if ($wpdb->charset != 'utf8mb4') { ?>
1156
- <span class="tnp-ko">KO</span>
1157
- <?php } else { ?>
1158
- <span class="tnp-ok">OK</span>
1159
- <?php } ?>
1160
-
1161
- </td>
1162
- <td>
1163
- Charset: <?php echo $wpdb->charset; ?>
1164
- <br>
1165
- <?php if ($wpdb->charset != 'utf8mb4') { ?>
1166
- The recommended charset for your database is <code>utf8mb4</code> to avoid possible saving errors when you use emoji.
1167
- Read the WordPress Codex <a href="https://codex.wordpress.org/Converting_Database_Character_Sets" target="_blank">conversion
1168
- instructions</a> (skilled technicia required).
1169
- <?php } else { ?>
1170
- If you experience newsletter saving database error
1171
- <?php $controls->button('conversion', 'Try tables upgrade') ?>
1172
- <?php } ?>
1173
- </td>
1174
- </tr>
1175
-
1176
- <tr>
1177
- <td>get_table_charset()</td>
1178
- <td>
1179
-
1180
- </td>
1181
- <td>
1182
- <?php echo esc_html(NEWSLETTER_USERS_TABLE), ': ', esc_html($tnp_wpdb->get_table_charset(NEWSLETTER_USERS_TABLE)) ?>
1183
- </td>
1184
- </tr>
1185
-
1186
-
1187
-
1188
-
1189
- <?php
1190
- $wait_timeout = $wpdb->get_var("select @@wait_timeout");
1191
- $condition = ($wait_timeout < 30) ? 0 : 1;
1192
- ?>
1193
- <tr>
1194
- <td>Database wait timeout</td>
1195
- <td>
1196
- <?php tnp_status_print_flag($condition) ?>
1197
- </td>
1198
- <td>
1199
- Your database wait timeout is <?php echo $wait_timeout; ?> seconds<br>
1200
- <?php if ($wait_timeout < 30) { ?>
1201
- That value is low and could produce database connection errors while sending emails or during long import
1202
- sessions. Ask the provider to raise it at least to 60 seconds.
1203
- <?php } ?>
1204
- </td>
1205
- </tr>
1206
-
1207
- <?php
1208
- $res = $wpdb->query("drop table if exists {$wpdb->prefix}newsletter_test");
1209
- $res = $wpdb->query("create table if not exists {$wpdb->prefix}newsletter_test (id int(20))");
1210
- $condition = $res === false ? 0 : 1;
1211
- ?>
1212
- <tr>
1213
- <td>Database table creation</td>
1214
- <td>
1215
- <?php tnp_status_print_flag($condition) ?>
1216
- </td>
1217
- <td>
1218
- <?php if ($res === false) { ?>
1219
- Check the privileges of the user you use to connect to the database, it seems it cannot create tables.<br>
1220
- (<?php echo esc_html($wpdb->last_error) ?>)
1221
- <?php } else { ?>
1222
- <?php } ?>
1223
- </td>
1224
- </tr>
1225
-
1226
- <?php
1227
- $res = $wpdb->query("alter table {$wpdb->prefix}newsletter_test add column id1 int(20)");
1228
- $condition = $res === false ? 0 : 1;
1229
- ?>
1230
- <tr>
1231
- <td>Database table change</td>
1232
- <td>
1233
- <?php tnp_status_print_flag($condition) ?>
1234
- </td>
1235
- <td>
1236
- <?php if ($res === false) { ?>
1237
- Check the privileges of the user you use to connect to the database, it seems it cannot change the tables. It's require to update the
1238
- plugin.<br>
1239
- (<?php echo esc_html($wpdb->last_error) ?>)
1240
- <?php } else { ?>
1241
- <?php } ?>
1242
- </td>
1243
- </tr>
1244
-
1245
- <?php
1246
- // Clean up
1247
- $res = $wpdb->query("drop table if exists {$wpdb->prefix}newsletter_test");
1248
- ?>
1249
-
1250
- <?php if (!get_option('newsletter_stats_email_column_upgraded', false)) { ?>
1251
- <?php
1252
- $data_type = $wpdb->get_var(
1253
- $wpdb->prepare('SELECT DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = %s AND TABLE_NAME = %s AND COLUMN_NAME = %s',
1254
- DB_NAME, NEWSLETTER_STATS_TABLE, 'email_id'));
1255
- $to_upgrade = strtoupper($data_type) == 'INT' ? false : true;
1256
- ?>
1257
- <?php if ($to_upgrade) { ?>
1258
- <tr>
1259
- <td>Database stats table upgrade</td>
1260
- <td><?php tnp_status_print_flag(0) ?></td>
1261
- <td><?php $controls->button('stats_email_column_upgrade', 'Stats table upgrade') ?></td>
1262
- </tr>
1263
- <?php } ?>
1264
- <?php } ?>
1265
-
1266
- </tbody>
1267
- </table>
1268
-
1269
- <h3>3rd party plugins</h3>
1270
- <table class="widefat" id="tnp-status-table">
1271
- <thead>
1272
- <tr>
1273
- <th>Plugin</th>
1274
- <th><?php _e('Status', 'newsletter') ?></th>
1275
- <th>Action</th>
1276
- </tr>
1277
- </thead>
1278
- <tbody>
1279
- <?php if (is_plugin_active('plugin-load-filter/plugin-load-filter.php')) { ?>
1280
- <tr>
1281
- <td><a href="https://wordpress.org/plugins/plugin-load-filter/" target="_blank">Plugin load filter</a></td>
1282
- <td>
1283
- <span class="tnp-maybe">MAY BE</span>
1284
- </td>
1285
- <td>
1286
- Be sure Newsletter is set as active in EVERY context.
1287
- </td>
1288
- </tr>
1289
- <?php } ?>
1290
- </tbody>
1291
- </table>
1292
-
1293
- <h3>General parameters</h3>
1294
- <table class="widefat" id="tnp-parameters-table">
1295
- <thead>
1296
- <tr>
1297
- <th>Parameter</th>
1298
- <th>Value</th>
1299
- </tr>
1300
- </thead>
1301
- <tbody>
1302
-
1303
- <tr>
1304
- <td>Newsletter version</td>
1305
- <td>
1306
- <?php echo NEWSLETTER_VERSION ?>
1307
- </td>
1308
- </tr>
1309
-
1310
- <tr>
1311
- <td>NEWSLETTER_MAX_EXECUTION_TIME</td>
1312
- <td>
1313
- <?php
1314
- if (defined('NEWSLETTER_MAX_EXECUTION_TIME')) {
1315
- echo NEWSLETTER_MAX_EXECUTION_TIME . ' (seconds)';
1316
- } else {
1317
- echo 'Not set';
1318
- }
1319
- ?>
1320
- </td>
1321
- </tr>
1322
- <tr>
1323
- <td>NEWSLETTER_CRON_INTERVAL</td>
1324
- <td>
1325
- <?php echo NEWSLETTER_CRON_INTERVAL . ' (seconds)'; ?>
1326
- </td>
1327
- </tr>
1328
-
1329
-
1330
-
1331
- <?php /*
1332
- <tr>
1333
- <td>WordPress plugin url</td>
1334
- <td>
1335
- <?php echo WP_PLUGIN_URL; ?>
1336
- <br>
1337
- Filters:
1338
-
1339
- <?php
1340
- if (isset($wp_filter))
1341
- $filters = $wp_filter['plugins_url'];
1342
- if (!isset($filters) || !is_array($filters))
1343
- echo 'no filters attached to "plugin_urls"';
1344
- else {
1345
- echo '<ul>';
1346
- foreach ($filters as &$filter) {
1347
- foreach ($filter as &$entry) {
1348
- echo '<li>';
1349
- if (is_array($entry['function']))
1350
- echo esc_html(get_class($entry['function'][0]) . '->' . $entry['function'][1]);
1351
- else
1352
- echo esc_html($entry['function']);
1353
- echo '</li>';
1354
- }
1355
- }
1356
- echo '</ul>';
1357
- }
1358
- ?>
1359
- <p class="description">
1360
- This value should contains the full URL to your plugin folder. If there are filters
1361
- attached, the value can be different from the original generated by WordPress and sometime worng.
1362
- </p>
1363
- </td>
1364
- </tr>
1365
- */ ?>
1366
-
1367
- <tr>
1368
- <td>Absolute path</td>
1369
- <td>
1370
- <?php echo esc_html(ABSPATH); ?>
1371
- </td>
1372
- </tr>
1373
- <tr>
1374
- <td>Tables Prefix</td>
1375
- <td>
1376
- <?php echo $wpdb->prefix; ?>
1377
- </td>
1378
- </tr>
1379
- </tbody>
1380
- </table>
1381
-
1382
-
1383
- <?php if (isset($_GET['debug'])) { ?>
1384
-
1385
- <h3>Database Tables</h3>
1386
- <h4><?php echo $wpdb->prefix ?>newsletter</h4>
1387
- <?php
1388
- $rs = $wpdb->get_results("describe {$wpdb->prefix}newsletter");
1389
- ?>
1390
- <table class="tnp-db-table">
1391
- <thead>
1392
- <tr>
1393
- <th>Field</th>
1394
- <th>Type</th>
1395
- <th>Null</th>
1396
- <th>Key</th>
1397
- <th>Default</th>
1398
- <th>Extra</th>
1399
- </tr>
1400
- </thead>
1401
- <tbody>
1402
- <?php foreach ($rs as $r) { ?>
1403
- <tr>
1404
- <td><?php echo esc_html($r->Field) ?></td>
1405
- <td><?php echo esc_html($r->Type) ?></td>
1406
- <td><?php echo esc_html($r->Null) ?></td>
1407
- <td><?php echo esc_html($r->Key) ?></td>
1408
- <td><?php echo esc_html($r->Default) ?></td>
1409
- <td><?php echo esc_html($r->Extra) ?></td>
1410
- </tr>
1411
- <?php } ?>
1412
- </tbody>
1413
- </table>
1414
-
1415
- <h4><?php echo $wpdb->prefix ?>newsletter_emails</h4>
1416
- <?php
1417
- $rs = $wpdb->get_results("show full columns from {$wpdb->prefix}newsletter_emails");
1418
- ?>
1419
- <table class="tnp-db-table">
1420
- <thead>
1421
- <tr>
1422
- <th>Field</th>
1423
- <th>Type</th>
1424
- <th>Collation</th>
1425
- <th>Null</th>
1426
- <th>Key</th>
1427
- <th>Default</th>
1428
- <th>Extra</th>
1429
- </tr>
1430
- </thead>
1431
- <tbody>
1432
- <?php foreach ($rs as $r) { ?>
1433
- <tr>
1434
- <td><?php echo esc_html($r->Field) ?></td>
1435
- <td><?php echo esc_html($r->Type) ?></td>
1436
- <td><?php echo esc_html($r->Collation) ?></td>
1437
- <td><?php echo esc_html($r->Null) ?></td>
1438
- <td><?php echo esc_html($r->Key) ?></td>
1439
- <td><?php echo esc_html($r->Default) ?></td>
1440
- <td><?php echo esc_html($r->Extra) ?></td>
1441
- </tr>
1442
- <?php } ?>
1443
- </tbody>
1444
- </table>
1445
-
1446
-
1447
- <h3>Extensions</h3>
1448
- <pre style="font-size: 11px; font-family: monospace; background-color: #efefef; color: #444"><?php echo esc_html(print_r(get_option('newsletter_extension_versions'), true)); ?></pre>
1449
-
1450
- <h3>Update plugins data</h3>
1451
- <pre style="font-size: 11px; font-family: monospace; background-color: #efefef; color: #444"><?php echo esc_html(print_r(get_site_transient('update_plugins'), true)); ?></pre>
1452
-
1453
- <?php } ?>
1454
- </div>
1455
-
1456
- <?php include NEWSLETTER_DIR . '/tnp-footer.php'; ?>
1457
-
1458
- </div>
1
+ <?php
2
+ /* @var $this Newsletter */
3
+ /* @var $wpdb wpdb */
4
+
5
+ defined('ABSPATH') || exit;
6
+
7
+ wp_enqueue_script('tnp-chart');
8
+
9
+ include_once NEWSLETTER_INCLUDES_DIR . '/controls.php';
10
+ $controls = new NewsletterControls();
11
+
12
+ $system = NewsletterSystem::instance();
13
+
14
+ if ($controls->is_action('delete_logs')) {
15
+ $files = glob(WP_CONTENT_DIR . '/logs/newsletter/*.txt');
16
+ foreach ($files as $file) {
17
+ if (is_file($file))
18
+ unlink($file);
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');
30
+ if (function_exists('maybe_convert_table_to_utf8mb4')) {
31
+ $r = maybe_convert_table_to_utf8mb4(NEWSLETTER_EMAILS_TABLE);
32
+ if (!$r) {
33
+ $controls->errors .= 'It was not possible to run the conversion for the table ' . NEWSLETTER_EMAILS_TABLE . ' - ';
34
+ $controls->errors .= $wpdb->last_error . '<br>';
35
+ }
36
+ $r = maybe_convert_table_to_utf8mb4(NEWSLETTER_USERS_TABLE);
37
+ if (!$r) {
38
+ $controls->errors .= 'It was not possible to run the conversion for the table ' . NEWSLETTER_EMAILS_TABLE . ' - ';
39
+ $controls->errors .= $wpdb->last_error . '<br>';
40
+ }
41
+ $controls->messages .= 'Done.';
42
+ } else {
43
+ $controls->errors = 'Table conversion function not available';
44
+ }
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");
103
+ $this->query("alter table `" . NEWSLETTER_STATS_TABLE . "` modify column `email_id` int(11) not null default 0");
104
+ $this->query("create index email_id on " . NEWSLETTER_STATS_TABLE . " (email_id)");
105
+ $this->query("create index user_id on " . NEWSLETTER_STATS_TABLE . " (user_id)");
106
+ $controls->add_message_done();
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;
115
+ $queued = 0;
116
+ 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) {
136
+ return parent::get_table_charset($table);
137
+ }
138
+
139
+ }
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
+
153
+ <div id="tnp-heading">
154
+
155
+ <h2><?php _e('System Status', 'newsletter') ?></h2>
156
+
157
+ </div>
158
+
159
+ <div id="tnp-body">
160
+
161
+ <form method="post" action="">
162
+ <?php $controls->init(); ?>
163
+
164
+
165
+ <h3>Delivery</h3>
166
+ <table class="widefat" id="tnp-status-table">
167
+
168
+ <thead>
169
+ <tr>
170
+ <th>Parameter</th>
171
+ <th><?php _e('Status', 'newsletter') ?></th>
172
+ <th>Action</th>
173
+ </tr>
174
+
175
+ </thead>
176
+
177
+ <tbody>
178
+
179
+ <tr>
180
+ <td>Delivering</td>
181
+ <td class="status">
182
+ &nbsp;
183
+ </td>
184
+ <td>
185
+ <?php if (count($emails)) { ?>
186
+ Delivering <?php echo count($emails) ?> newsletters to about <?php echo $queued ?> recipients.
187
+ At speed of <?php echo $speed ?> emails per hour it will take <?php printf('%.1f', $queued / $speed) ?> hours to finish.
188
+
189
+ <?php } else { ?>
190
+ Nothing delivering right now
191
+ <?php } ?>
192
+ </td>
193
+
194
+ </tr>
195
+ <tr>
196
+ <td>Mailer</td>
197
+ <td>
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;
221
+ ?>
222
+ <tr>
223
+ <td id="tnp-speed">
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>
235
+ <?php } ?>
236
+ Average time to send an email: <?php echo $stats->mean ?> seconds<br>
237
+ <?php if ($stats->mean > 0) { ?>
238
+ Max speed: <?php echo sprintf("%.2f", 1.0 / $stats->mean * 3600) ?> emails per hour<br>
239
+ <?php } ?>
240
+
241
+ Max mean time measured: <?php echo $stats->max ?> seconds<br>
242
+ Min mean time measured: <?php echo $stats->min ?> seconds<br>
243
+ Total emails in the sample: <?php echo $stats->total_emails ?><br>
244
+ Total sending time: <?php echo $stats->total_time ?> seconds<br>
245
+ Runs in the sample: <?php echo $stats->total_runs ?><br>
246
+ Runs prematurely interrupted: <?php echo $stats->interrupted ?><br>
247
+
248
+
249
+ <canvas id="tnp-send-chart" style="width: 550px; height: 150px"></canvas>
250
+ <script>
251
+ jQuery(function () {
252
+ var sendChartData = {
253
+ labels: <?php echo json_encode(range(1, count($stats->means))) ?>,
254
+ datasets: [
255
+ {
256
+ label: "Batch Average Time",
257
+ data: <?php echo json_encode($stats->means) ?>,
258
+ borderColor: '#2980b9',
259
+ fill: false
260
+ }/*,
261
+ {
262
+ label: "Batch Average Time",
263
+ data: <?php echo json_encode($stats->sizes) ?>,
264
+ borderColor: '#b98028',
265
+ fill: false
266
+ }*/]
267
+ };
268
+ var sendChartConfig = {
269
+ type: "line",
270
+ data: sendChartData,
271
+ options: {
272
+ responsive: false,
273
+ maintainAspectRatio: false
274
+ }
275
+ };
276
+ new Chart('tnp-send-chart', sendChartConfig);
277
+ });
278
+ </script>
279
+ <br>
280
+ <?php $controls->button_reset('reset_send_stats') ?>
281
+ </td>
282
+ </tr>
283
+ <?php } else { ?>
284
+ <tr>
285
+ <td>
286
+ Sending statistics
287
+ </td>
288
+ <td>
289
+ &nbsp;
290
+ </td>
291
+ <td>
292
+ Not enough data available.
293
+ </td>
294
+ </tr>
295
+ <?php } ?>
296
+ </tbody>
297
+ </table>
298
+
299
+ <h3>General checks</h3>
300
+ <table class="widefat" id="tnp-status-table">
301
+
302
+ <thead>
303
+ <tr>
304
+ <th>Parameter</th>
305
+ <th><?php _e('Status', 'newsletter') ?></th>
306
+ <th>Action</th>
307
+ </tr>
308
+
309
+ </thead>
310
+
311
+ <tbody>
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') {
320
+ $condition = 0;
321
+ }
322
+ } else {
323
+ $condition = 2;
324
+ }
325
+ ?>
326
+ <td>
327
+ Dedicated page<br>
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) { ?>
335
+ Newsletter is using a neutral page to show messages, if you want to use a dedicated page, configure it on
336
+ <a href="?page=newsletter_main_main">main settings</a>.
337
+ <?php } else if ($condition == 0) { ?>
338
+ A dedicated page is set but it is no more available or no more published. Review the dedicated page on
339
+ <a href="?page=newsletter_main_main">main settings</a>.
340
+ <?php } ?>
341
+ </td>
342
+ </tr>
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) {
351
+ $condition = 0;
352
+ } else {
353
+ $content = $page->post_content;
354
+ if (strpos($content, '[newsletter]') === false && strpos($content, '[newsletter ') === false) {
355
+ $condition = 2;
356
+ }
357
+ }
358
+ }
359
+ ?>
360
+ <td>
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) { ?>
368
+ The page seems to not contain the <code>[newsletter]</code>, but sometime it cannot be detected if you use
369
+ a visual composer. <a href="post.php?post=<?php echo $page->ID ?>&action=edit" target="_blank">Please, check the page</a>.
370
+ <?php } else if ($condition == 0) { ?>
371
+ The dedicated page seems to not be available.
372
+ <?php } ?>
373
+ </td>
374
+ </tr>
375
+
376
+ <?php
377
+ $method = '';
378
+ if (function_exists('get_filesystem_method')) {
379
+ $method = get_filesystem_method(array(), WP_PLUGIN_DIR);
380
+ }
381
+ if (empty($method))
382
+ $condition = 2;
383
+ else if ($method == 'direct')
384
+ $condition = 1;
385
+ else
386
+ $condition = 0;
387
+ ?>
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) { ?>
395
+ No able to check, just try the add-ons manager one click install
396
+ <?php } else if ($condition == 1) { ?>
397
+ The add-ons manager can install our add-ons
398
+ <?php } else { ?>
399
+ The plugins dir could be read-only, you can install add-ons uploading the package from the
400
+ plugins panel (or uploading them directly via FTP). This is unusual you should ask te provider
401
+ about file and folder permissions.
402
+ <?php } ?>
403
+ </td>
404
+
405
+ </tr>
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
+ }
417
+ ?>
418
+ <tr>
419
+ <td>Return path</td>
420
+ <td>
421
+ <?php if (empty($return_path)) { ?>
422
+ <span class="tnp-ok">OK</span>
423
+ <?php } else { ?>
424
+ <?php if ($sender_domain != $return_path_domain) { ?>
425
+ <span class="tnp-maybe">MAYBE</span>
426
+ <?php } else { ?>
427
+ <span class="tnp-ok">OK</span>
428
+ <?php } ?>
429
+ <?php } ?>
430
+
431
+ </td>
432
+ <td>
433
+ <?php if (!empty($return_path)) { ?>
434
+ Some providers require the return path domain <code><?php echo esc_html($return_path_domain) ?></code> to be identical
435
+ to the sender domain <code><?php echo esc_html($sender_domain) ?></code>. See the main settings.
436
+ <?php } else { ?>
437
+ <?php } ?>
438
+ </td>
439
+
440
+ </tr>
441
+
442
+
443
+
444
+
445
+
446
+ <tr>
447
+ <?php
448
+ $condition = NEWSLETTER_EXTENSION_UPDATE ? 1 : 0;
449
+ ?>
450
+ <td>Addons update</td>
451
+ <td>
452
+ <?php tnp_status_print_flag($condition) ?>
453
+ </td>
454
+ <td>
455
+ <?php if ($condition == 0) { ?>
456
+ Newsletter Addons update is disabled (probably in your <code>wp-config.php</code> file the constant
457
+ <code>NEWSLETTER_EXTENSION_UPDATE</code> is set to <code>true</code>)
458
+ <?php } else { ?>
459
+ Newsletter Addons can be updated
460
+ <?php } ?>
461
+ </td>
462
+
463
+ </tr>
464
+
465
+
466
+ <?php
467
+ $res = 0;
468
+ $response = wp_remote_post(home_url('/') . '?na=test');
469
+ if (is_wp_error($response)) {
470
+ $res = 1;
471
+ $message = $response->get_error_message();
472
+ } else if (wp_remote_retrieve_response_code($response) != 200) {
473
+ $res = 1;
474
+ $message = wp_remote_retrieve_response_message($response);
475
+ } else if (wp_remote_retrieve_body($response) !== 'ok') {
476
+ $res = 2;
477
+ }
478
+ ?>
479
+ <tr>
480
+ <td>
481
+ Action call
482
+ </td>
483
+ <td>
484
+ <?php if (!$res) { ?>
485
+ <span class="tnp-ko">KO</span>
486
+ <?php } else { ?>
487
+ <span class="tnp-ok">OK</span>
488
+ <?php } ?>
489
+ </td>
490
+ <td>
491
+ <?php if ($res === 1) { ?>
492
+ The blog is not responding to Newsletter URLs: ask the provider or your IT consultant to check this problem. Report the URL and error below<br>
493
+ Error: <?php echo esc_html($message) ?><br>
494
+ <?php } else if ($res === 2) { ?>
495
+ The response does not contain the "ok" text: probably a caching/optimization plugin or a server configuration is forcing the blog to ignore
496
+ the URL's query string. Reported that to your system administrator.
497
+ <?php } ?>
498
+ Url: <?php echo esc_html(home_url('/') . '?na=test') ?><br>
499
+ </td>
500
+ </tr>
501
+
502
+
503
+ <tr>
504
+ <?php
505
+ $res = true;
506
+ $response = wp_remote_get('http://www.thenewsletterplugin.com/wp-content/extensions.json');
507
+ $condition = 1;
508
+ if (is_wp_error($response)) {
509
+ $res = false;
510
+ $condition = 0;
511
+ $message = $response->get_error_message();
512
+ } else {
513
+ if (wp_remote_retrieve_response_code($response) != 200) {
514
+ $res = false;
515
+ $condition = 0;
516
+ $message = wp_remote_retrieve_response_message($response);
517
+ }
518
+ }
519
+ ?>
520
+
521
+ <td>
522
+ Addons version check<br>
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) { ?>
530
+ The blog cannot contact www.thenewsletterplugin.com to check the license or the extension versions.<br>
531
+ Error: <?php echo esc_html($message) ?><br>
532
+ <?php } else { ?>
533
+
534
+ <?php } ?>
535
+ </td>
536
+ </tr>
537
+
538
+
539
+
540
+
541
+
542
+
543
+
544
+
545
+
546
+ <?php /*
547
+ $memory = intval(WP_MEMORY_LIMIT);
548
+ if (false !== strpos(WP_MEMORY_LIMIT, 'G'))
549
+ $memory *= 1024;
550
+ ?>
551
+ <tr>
552
+ <td>
553
+ PHP memory limit
554
+ </td>
555
+ <td>
556
+ <?php if ($memory < 64) { ?>
557
+ <span class="tnp-ko">MAYBE</span>
558
+ <?php } else if ($memory < 128) { ?>
559
+ <span class="tnp-maybe">MAYBE</span>
560
+ <?php } else { ?>
561
+ <span class="tnp-ok">OK</span>
562
+ <?php } ?>
563
+ </td>
564
+ <td>
565
+ WordPress WP_MEMORY_LIMIT is set to <?php echo $memory ?> megabyte but your PHP setting could allow more than that.
566
+ Anyway we suggest to set the value to at least 64M.
567
+ <a href="https://www.thenewsletterplugin.com/documentation/status-panel#status-memory" target="_blank">Read more</a>.
568
+ <?php if ($memory < 64) { ?>
569
+ This value is too low you should increase it adding <code>define('WP_MEMORY_LIMIT', '64M');</code> to your <code>wp-config.php</code>.
570
+ <a href="https://www.thenewsletterplugin.com/documentation/status-panel#status-memory" target="_blank">Read more</a>.
571
+ <?php } else if ($memory < 128) { ?>
572
+ The value should be fine, it depends on how many plugins you're running and how many resource are required by your theme.
573
+ Blank pages may happen with low memory problems. Eventually increase it adding <code>define('WP_MEMORY_LIMIT', '128M');</code>
574
+ to your <code>wp-config.php</code>.
575
+ <a href="https://www.thenewsletterplugin.com/documentation/status-panel#status-memory" target="_blank">Read more</a>.
576
+ <?php } else { ?>
577
+
578
+ <?php } ?>
579
+
580
+ </td>
581
+ </tr>
582
+ */ ?>
583
+
584
+ <?php
585
+ $ip = gethostbyname($_SERVER['HTTP_HOST']);
586
+ $name = gethostbyaddr($ip);
587
+ $res = true;
588
+ if (strpos($name, '.secureserver.net') !== false) {
589
+ //$smtp = get_option('newsletter_main_smtp');
590
+ //if (!empty($smtp['enabled']))
591
+ $res = false;
592
+ $message = 'If you\'re hosted with GoDaddy, be sure to set their SMTP (relay-hosting.secureserver.net, without username and password) to send emails
593
+ on Newsletter SMTP panel.
594
+ Remember they limits you to 250 emails per day. Open them a ticket for more details.';
595
+ }
596
+ if (strpos($name, '.aruba.it') !== false) {
597
+ $res = false;
598
+ $message = 'If you\'re hosted with Aruba consider to use an external SMTP (Sendgrid, Mailjet, Mailgun, Amazon SES, Elasticemail, Sparkpost, ...)
599
+ since their mail service is not good. If you have your personal email with them, you can try to use the SMTP of your
600
+ pesonal account. Ask the support for the SMTP parameters and configure them on Newsletter SMTP panel.';
601
+ }
602
+ ?>
603
+ <tr>
604
+ <td>Your Server</td>
605
+ <td>
606
+ <?php if ($res === false) { ?>
607
+ <span class="tnp-maybe">MAYBE</span>
608
+ <?php } else { ?>
609
+ <span class="tnp-ok">OK</span>
610
+ <?php } ?>
611
+
612
+
613
+ </td>
614
+ <td>
615
+ <?php if ($res === false) { ?>
616
+ <?php echo $message ?>
617
+ <?php } else { ?>
618
+
619
+ <?php } ?>
620
+ IP: <?php echo $ip ?><br>
621
+ Name: <?php echo $name ?><br>
622
+ </td>
623
+ </tr>
624
+
625
+ <?php
626
+ wp_mkdir_p(NEWSLETTER_LOG_DIR);
627
+ $condition = is_dir(NEWSLETTER_LOG_DIR) && is_writable(NEWSLETTER_LOG_DIR) ? 1 : 0;
628
+ if ($condition) {
629
+ @file_put_contents(NEWSLETTER_LOG_DIR . '/test.txt', "");
630
+ $condition = is_file(NEWSLETTER_LOG_DIR . '/test.txt') ? 1 : 0;
631
+ if ($condition) {
632
+ @unlink(NEWSLETTER_LOG_DIR . '/test.txt');
633
+ }
634
+ }
635
+ ?>
636
+ <tr>
637
+ <td>
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>
645
+ <?php if (!$res) { ?>
646
+ Cannot create the folder or it is not writable.
647
+ <?php } ?>
648
+ </td>
649
+ </tr>
650
+ </tbody>
651
+ </table>
652
+
653
+
654
+ <h3>WordPress</h3>
655
+
656
+ <table class="widefat" id="tnp-status-table">
657
+ <thead>
658
+ <tr>
659
+ <th>Parameter</th>
660
+ <th><?php _e('Status', 'newsletter') ?></th>
661
+ <th>Action</th>
662
+ </tr>
663
+ </thead>
664
+ <tbody>
665
+
666
+ <tr>
667
+ <?php
668
+ $condition = (defined('WP_DEBUG') && WP_DEBUG) ? 2 : 1;
669
+ ?>
670
+ <td>
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) { ?>
678
+ WordPress is in debug mode it is not recommended on a production system. See the constant <code>WP_DEBUG</code> inside the <code>wp-config.php</code>.
679
+ <?php } else { ?>
680
+
681
+ <?php } ?>
682
+ </td>
683
+ </tr>
684
+
685
+
686
+
687
+ <tr>
688
+ <?php
689
+ $charset = get_option('blog_charset');
690
+ $condition = $charset === 'UTF-8' ? 1 : 0;
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) ?>
698
+ <br>
699
+
700
+ <?php if ($condition == 1) { ?>
701
+
702
+ <?php } else { ?>
703
+ It is recommended to use
704
+ the <code>UTF-8</code> charset but the <a href="https://codex.wordpress.org/Converting_Database_Character_Sets" target="_blank">conversion</a>
705
+ could be tricky. If you're not experiencing problem, leave things as is.
706
+ <?php } ?>
707
+ </td>
708
+ </tr>
709
+
710
+ <tr>
711
+ <?php
712
+ $condition = (strpos(home_url('/'), 'http') !== 0) ? 0 : 1;
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('/'); ?>
720
+ <br>
721
+ <?php if ($condition == 0) { ?>
722
+ Your home URL is not absolute, emails require absolute URLs.
723
+ Probably you have a protocol agnostic plugin installed to manage both HTTPS and HTTP in your
724
+ blog.
725
+ <?php } else { ?>
726
+
727
+ <?php } ?>
728
+ </td>
729
+ </tr>
730
+
731
+ <tr>
732
+ <?php
733
+ $condition = (strpos(WP_CONTENT_URL, 'http') !== 0) ? 0 : 1;
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); ?>
741
+ <br>
742
+ <?php if ($condition == 0) { ?>
743
+ Your content URL is not absolute, emails require absolute URLs when they have images inside.
744
+ Newsletter tries to deal with this problem but when a problem with images persists, you should try to remove
745
+ from your <code>wp-config.php</code> the <code>WP_CONTENT_URL</code> define and check again.
746
+ <?php } else { ?>
747
+
748
+ <?php } ?>
749
+ </td>
750
+ </tr>
751
+
752
+ <tr>
753
+ <?php
754
+ set_transient('newsletter_transient_test', 1, 300);
755
+ delete_transient('newsletter_transient_test');
756
+ $res = get_transient('newsletter_transient_test');
757
+ $condition = ($res !== false) ? 0 : 1;
758
+ ?>
759
+ <td>WordPress transients</td>
760
+ <td>
761
+ <?php tnp_status_print_flag($condition) ?>
762
+ </td>
763
+ <td>
764
+ <?php if ($res !== false) { ?>
765
+ Transients cannot be delete. This can block the delivery engine. Usually it is due to a not well coded plugin installed.
766
+ <?php } else { ?>
767
+ <?php } ?>
768
+ </td>
769
+ </tr>
770
+ </tbody>
771
+ </table>
772
+
773
+
774
+
775
+ <h3>PHP</h3>
776
+ <table class="widefat" id="tnp-status-table">
777
+ <thead>
778
+ <tr>
779
+ <th>Parameter</th>
780
+ <th><?php _e('Status', 'newsletter') ?></th>
781
+ <th>Action</th>
782
+ </tr>
783
+ </thead>
784
+ <tbody>
785
+ <tr>
786
+ <td>PHP version</td>
787
+ <td>
788
+ <?php if (version_compare(phpversion(), '5.6', '<')) { ?>
789
+ <span class="tnp-ko">KO</span>
790
+ <?php } else { ?>
791
+ <span class="tnp-ok">OK</span>
792
+ <?php } ?>
793
+
794
+ </td>
795
+ <td>
796
+ Your PHP version is <?php echo phpversion() ?><br>
797
+ <?php if (version_compare(phpversion(), '5.3', '<')) { ?>
798
+ Newsletter plugin works correctly with PHP version 5.6 or greater. Ask your provider to upgrade your PHP. Your version is
799
+ unsupported even by the PHP community.
800
+ <?php } ?>
801
+ </td>
802
+
803
+ </tr>
804
+
805
+ <tr>
806
+ <?php
807
+ $value = (int) ini_get('max_execution_time');
808
+ $res = true;
809
+ $condition = 1;
810
+ if ($value != 0 && $value < NEWSLETTER_CRON_INTERVAL) {
811
+ $res = set_time_limit(NEWSLETTER_CRON_INTERVAL);
812
+ if ($res)
813
+ $condition = 1;
814
+ else
815
+ $condition = 0;
816
+ }
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) { ?>
824
+ Your PHP execution time limit is <?php echo $value ?> seconds. It cannot be changed and it is too lower to grant the maximum delivery rate of Newsletter.
825
+ <?php } else { ?>
826
+ Your PHP execution time limit is <?php echo $value ?> seconds and can be eventually changed by Newsletter.<br>
827
+ <?php } ?>
828
+
829
+ </td>
830
+
831
+ </tr>
832
+
833
+
834
+ <tr>
835
+ <?php
836
+ $condition = function_exists('curl_version');
837
+ ?>
838
+ <td>Curl version</td>
839
+ <td>
840
+ <?php if (!$condition) { ?>
841
+ <span class="tnp-ko">KO</span>
842
+ <?php } else { ?>
843
+ <span class="tnp-ok">OK</span>
844
+ <?php } ?>
845
+
846
+ </td>
847
+ <td>
848
+ <?php
849
+ if (!$condition) {
850
+ echo 'cUrl is not available, ask the provider to install it and activate the PHP cUrl library';
851
+ } else {
852
+ $version = curl_version();
853
+ echo 'Version: ' . $version['version'] . '<br>';
854
+ echo 'SSL Version: ' . $version['ssl_version'] . '<br>';
855
+ }
856
+ ?>
857
+ </td>
858
+
859
+ </tr>
860
+ <?php if (ini_get('opcache.validate_timestamps') === '0') { ?>
861
+ <tr>
862
+ <td>
863
+ Opcache
864
+ </td>
865
+
866
+ <td>
867
+ <span class="tnp-ko">KO</span>
868
+ </td>
869
+
870
+ <td>
871
+ You have the PHP opcache active with file validation disable so every blog plugins update needs a webserver restart!
872
+ </td>
873
+ </tr>
874
+ <?php } ?>
875
+ </tbody>
876
+ </table>
877
+
878
+ <h3>Database</h3>
879
+ <table class="widefat" id="tnp-status-table">
880
+ <thead>
881
+ <tr>
882
+ <th>Parameter</th>
883
+ <th><?php _e('Status', 'newsletter') ?></th>
884
+ <th>Action</th>
885
+ </tr>
886
+ </thead>
887
+ <tbody>
888
+ <tr>
889
+ <td>Database Charset</td>
890
+ <td>
891
+ <?php if ($wpdb->charset != 'utf8mb4') { ?>
892
+ <span class="tnp-ko">KO</span>
893
+ <?php } else { ?>
894
+ <span class="tnp-ok">OK</span>
895
+ <?php } ?>
896
+
897
+ </td>
898
+ <td>
899
+ Charset: <?php echo $wpdb->charset; ?>
900
+ <br>
901
+ <?php if ($wpdb->charset != 'utf8mb4') { ?>
902
+ The recommended charset for your database is <code>utf8mb4</code> to avoid possible saving errors when you use emoji.
903
+ Read the WordPress Codex <a href="https://codex.wordpress.org/Converting_Database_Character_Sets" target="_blank">conversion
904
+ instructions</a> (skilled technicia required).
905
+ <?php } else { ?>
906
+ If you experience newsletter saving database error
907
+ <?php $controls->button('conversion', 'Try tables upgrade') ?>
908
+ <?php } ?>
909
+ </td>
910
+ </tr>
911
+
912
+ <tr>
913
+ <td>get_table_charset()</td>
914
+ <td>
915
+
916
+ </td>
917
+ <td>
918
+ <?php echo esc_html(NEWSLETTER_USERS_TABLE), ': ', esc_html($tnp_wpdb->get_table_charset(NEWSLETTER_USERS_TABLE)) ?>
919
+ </td>
920
+ </tr>
921
+
922
+
923
+
924
+
925
+ <?php
926
+ $wait_timeout = $wpdb->get_var("select @@wait_timeout");
927
+ $condition = ($wait_timeout < 30) ? 0 : 1;
928
+ ?>
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>
936
+ <?php if ($wait_timeout < 30) { ?>
937
+ That value is low and could produce database connection errors while sending emails or during long import
938
+ sessions. Ask the provider to raise it at least to 60 seconds.
939
+ <?php } ?>
940
+ </td>
941
+ </tr>
942
+
943
+ <?php
944
+ $res = $wpdb->query("drop table if exists {$wpdb->prefix}newsletter_test");
945
+ $res = $wpdb->query("create table if not exists {$wpdb->prefix}newsletter_test (id int(20))");
946
+ $condition = $res === false ? 0 : 1;
947
+ ?>
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) { ?>
955
+ Check the privileges of the user you use to connect to the database, it seems it cannot create tables.<br>
956
+ (<?php echo esc_html($wpdb->last_error) ?>)
957
+ <?php } else { ?>
958
+ <?php } ?>
959
+ </td>
960
+ </tr>
961
+
962
+ <?php
963
+ $res = $wpdb->query("alter table {$wpdb->prefix}newsletter_test add column id1 int(20)");
964
+ $condition = $res === false ? 0 : 1;
965
+ ?>
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) { ?>
973
+ Check the privileges of the user you use to connect to the database, it seems it cannot change the tables. It's require to update the
974
+ plugin.<br>
975
+ (<?php echo esc_html($wpdb->last_error) ?>)
976
+ <?php } else { ?>
977
+ <?php } ?>
978
+ </td>
979
+ </tr>
980
+
981
+ <?php
982
+ // Clean up
983
+ $res = $wpdb->query("drop table if exists {$wpdb->prefix}newsletter_test");
984
+ ?>
985
+
986
+ <?php if (!get_option('newsletter_stats_email_column_upgraded', false)) { ?>
987
+ <?php
988
+ $data_type = $wpdb->get_var(
989
+ $wpdb->prepare('SELECT DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = %s AND TABLE_NAME = %s AND COLUMN_NAME = %s',
990
+ DB_NAME, NEWSLETTER_STATS_TABLE, 'email_id'));
991
+ $to_upgrade = strtoupper($data_type) == 'INT' ? false : true;
992
+ ?>
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 } ?>
1000
+ <?php } ?>
1001
+
1002
+ </tbody>
1003
+ </table>
1004
+
1005
+ <h3>3rd party plugins</h3>
1006
+ <table class="widefat" id="tnp-status-table">
1007
+ <thead>
1008
+ <tr>
1009
+ <th>Plugin</th>
1010
+ <th><?php _e('Status', 'newsletter') ?></th>
1011
+ <th>Action</th>
1012
+ </tr>
1013
+ </thead>
1014
+ <tbody>
1015
+ <?php if (is_plugin_active('plugin-load-filter/plugin-load-filter.php')) { ?>
1016
+ <tr>
1017
+ <td><a href="https://wordpress.org/plugins/plugin-load-filter/" target="_blank">Plugin load filter</a></td>
1018
+ <td>
1019
+ <span class="tnp-maybe">MAY BE</span>
1020
+ </td>
1021
+ <td>
1022
+ Be sure Newsletter is set as active in EVERY context.
1023
+ </td>
1024
+ </tr>
1025
+ <?php } ?>
1026
+ </tbody>
1027
+ </table>
1028
+
1029
+ <h3>General parameters</h3>
1030
+ <table class="widefat" id="tnp-parameters-table">
1031
+ <thead>
1032
+ <tr>
1033
+ <th>Parameter</th>
1034
+ <th>Value</th>
1035
+ </tr>
1036
+ </thead>
1037
+ <tbody>
1038
+
1039
+ <tr>
1040
+ <td>Newsletter version</td>
1041
+ <td>
1042
+ <?php echo NEWSLETTER_VERSION ?>
1043
+ </td>
1044
+ </tr>
1045
+
1046
+ <tr>
1047
+ <td>NEWSLETTER_MAX_EXECUTION_TIME</td>
1048
+ <td>
1049
+ <?php
1050
+ if (defined('NEWSLETTER_MAX_EXECUTION_TIME')) {
1051
+ echo NEWSLETTER_MAX_EXECUTION_TIME . ' (seconds)';
1052
+ } else {
1053
+ echo 'Not set';
1054
+ }
1055
+ ?>
1056
+ </td>
1057
+ </tr>
1058
+
1059
+
1060
+
1061
+
1062
+ <?php /*
1063
+ <tr>
1064
+ <td>WordPress plugin url</td>
1065
+ <td>
1066
+ <?php echo WP_PLUGIN_URL; ?>
1067
+ <br>
1068
+ Filters:
1069
+
1070
+ <?php
1071
+ if (isset($wp_filter))
1072
+ $filters = $wp_filter['plugins_url'];
1073
+ if (!isset($filters) || !is_array($filters))
1074
+ echo 'no filters attached to "plugin_urls"';
1075
+ else {
1076
+ echo '<ul>';
1077
+ foreach ($filters as &$filter) {
1078
+ foreach ($filter as &$entry) {
1079
+ echo '<li>';
1080
+ if (is_array($entry['function']))
1081
+ echo esc_html(get_class($entry['function'][0]) . '->' . $entry['function'][1]);
1082
+ else
1083
+ echo esc_html($entry['function']);
1084
+ echo '</li>';
1085
+ }
1086
+ }
1087
+ echo '</ul>';
1088
+ }
1089
+ ?>
1090
+ <p class="description">
1091
+ This value should contains the full URL to your plugin folder. If there are filters
1092
+ attached, the value can be different from the original generated by WordPress and sometime worng.
1093
+ </p>
1094
+ </td>
1095
+ </tr>
1096
+ */ ?>
1097
+
1098
+ <tr>
1099
+ <td>Absolute path</td>
1100
+ <td>
1101
+ <?php echo esc_html(ABSPATH); ?>
1102
+ </td>
1103
+ </tr>
1104
+ <tr>
1105
+ <td>Tables Prefix</td>
1106
+ <td>
1107
+ <?php echo $wpdb->prefix; ?>
1108
+ </td>
1109
+ </tr>
1110
+ </tbody>
1111
+ </table>
1112
+
1113
+
1114
+ <?php if (isset($_GET['debug'])) { ?>
1115
+
1116
+ <h3>Database Tables</h3>
1117
+ <h4><?php echo $wpdb->prefix ?>newsletter</h4>
1118
+ <?php
1119
+ $rs = $wpdb->get_results("describe {$wpdb->prefix}newsletter");
1120
+ ?>
1121
+ <table class="tnp-db-table">
1122
+ <thead>
1123
+ <tr>
1124
+ <th>Field</th>
1125
+ <th>Type</th>
1126
+ <th>Null</th>
1127
+ <th>Key</th>
1128
+ <th>Default</th>
1129
+ <th>Extra</th>
1130
+ </tr>
1131
+ </thead>
1132
+ <tbody>
1133
+ <?php foreach ($rs as $r) { ?>
1134
+ <tr>
1135
+ <td><?php echo esc_html($r->Field) ?></td>
1136
+ <td><?php echo esc_html($r->Type) ?></td>
1137
+ <td><?php echo esc_html($r->Null) ?></td>
1138
+ <td><?php echo esc_html($r->Key) ?></td>
1139
+ <td><?php echo esc_html($r->Default) ?></td>
1140
+ <td><?php echo esc_html($r->Extra) ?></td>
1141
+ </tr>
1142
+ <?php } ?>
1143
+ </tbody>
1144
+ </table>
1145
+
1146
+ <h4><?php echo $wpdb->prefix ?>newsletter_emails</h4>
1147
+ <?php
1148
+ $rs = $wpdb->get_results("show full columns from {$wpdb->prefix}newsletter_emails");
1149
+ ?>
1150
+ <table class="tnp-db-table">
1151
+ <thead>
1152
+ <tr>
1153
+ <th>Field</th>
1154
+ <th>Type</th>
1155
+ <th>Collation</th>
1156
+ <th>Null</th>
1157
+ <th>Key</th>
1158
+ <th>Default</th>
1159
+ <th>Extra</th>
1160
+ </tr>
1161
+ </thead>
1162
+ <tbody>
1163
+ <?php foreach ($rs as $r) { ?>
1164
+ <tr>
1165
+ <td><?php echo esc_html($r->Field) ?></td>
1166
+ <td><?php echo esc_html($r->Type) ?></td>
1167
+ <td><?php echo esc_html($r->Collation) ?></td>
1168
+ <td><?php echo esc_html($r->Null) ?></td>
1169
+ <td><?php echo esc_html($r->Key) ?></td>
1170
+ <td><?php echo esc_html($r->Default) ?></td>
1171
+ <td><?php echo esc_html($r->Extra) ?></td>
1172
+ </tr>
1173
+ <?php } ?>
1174
+ </tbody>
1175
+ </table>
1176
+
1177
+
1178
+ <h3>Extensions</h3>
1179
+ <pre style="font-size: 11px; font-family: monospace; background-color: #efefef; color: #444"><?php echo esc_html(print_r(get_option('newsletter_extension_versions'), true)); ?></pre>
1180
+
1181
+ <h3>Update plugins data</h3>
1182
+ <pre style="font-size: 11px; font-family: monospace; background-color: #efefef; color: #444"><?php echo esc_html(print_r(get_site_transient('update_plugins'), true)); ?></pre>
1183
+
1184
+ <?php } ?>
1185
+ </div>
1186
+
1187
+ <?php include NEWSLETTER_DIR . '/tnp-footer.php'; ?>
1188
+
1189
+ </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
main/welcome.php CHANGED
@@ -1,256 +1,255 @@
1
- <?php
2
- defined('ABSPATH') || exit;
3
-
4
- // Very very naif
5
- if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_GET['action']) && $_GET['action'] == 'save') {
6
-
7
- // Sender name
8
- $module = Newsletter::instance();
9
- $options = $module->get_options();
10
- $options['sender_name'] = trim(stripslashes($_POST['sender_name']));
11
- $options['sender_email'] = trim(strtolower(stripslashes($_POST['sender_email'])));
12
- $module->save_options($options);
13
-
14
- // Profile setting
15
- $module = NewsletterSubscription::instance();
16
- $options = $module->get_options('profile');
17
- $options['privacy_status'] = isset($_POST['field_privacy']) ? 1 : 0;
18
- $options['name_status'] = isset($_POST['field_name']) ? 2 : 0;
19
- $options['subscribe'] = trim(stripslashes($_POST['field_subscribe']));
20
- if (empty($options['subscribe']))
21
- $options['subscribe'] = 'Subscribe';
22
- $module->save_options($options, 'profile');
23
- die();
24
- }
25
-
26
- if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_GET['action']) && $_GET['action'] == 'test') {
27
- $module = Newsletter::instance();
28
- $email = $_POST['test_email'];
29
- $status_options = $module->get_options('status');
30
-
31
- if (!NewsletterModule::is_email($email)) {
32
- echo _e('Please check the email address, it seems wrong.', 'newsletter');
33
- die();
34
- }
35
- // Newsletter mail
36
- $text = array();
37
- $text['html'] = '<p>This is an <b>HTML</b> test email sent using the sender data set on Newsletter main setting. <a href="https://www.thenewsletterplugin.com">This is a link to an external site</a>.</p>';
38
- $text['text'] = 'This is a textual test email part sent using the sender data set on Newsletter main setting.';
39
- $r = $module->mail($email, 'Newsletter test email at ' . date(DATE_ISO8601), $text);
40
-
41
- if ($r) {
42
- $status_options['mail'] = 1;
43
- $module->save_options($status_options, 'status');
44
- echo _e('Check your mailbox for a test message. Check the spam folder as well.', 'newsletter');
45
- die();
46
- } else {
47
- $status_options['mail'] = 0;
48
- $status_options['mail_error'] = $module->mail_last_error;
49
- $module->save_options($status_options, 'status');
50
- echo _e('There was an error. Complete the setup and then use the status panel to check it out.', 'newsletter');
51
- die();
52
- }
53
- die();
54
- }
55
-
56
- $profile_options = NewsletterSubscription::instance()->get_options('profile');
57
- $main_options = Newsletter::instance()->get_options();
58
- $subscription_options = NewsletterSubscription::instance()->get_options();
59
-
60
- $logger = Newsletter::instance()->logger;
61
-
62
- $page_exists = get_option('newsletter_page');
63
-
64
- if (empty($page_exists)) {
65
- $module = Newsletter::instance();
66
- $logger->info('Dedicated page creation');
67
- // Page creation
68
- $page = array();
69
- $page['post_title'] = 'Newsletter';
70
- $page['post_content'] = '[newsletter]';
71
- $page['post_status'] = 'publish';
72
- $page['post_type'] = 'page';
73
- $page['comment_status'] = 'closed';
74
- $page['ping_status'] = 'closed';
75
- $page['post_category'] = array(1);
76
-
77
- // Insert the post into the database
78
- $page_id = wp_insert_post($page);
79
-
80
- $main_options['page'] = $page_id;
81
- Newsletter::instance()->save_options($main_options);
82
- $main_options = Newsletter::instance()->get_options();
83
- $page_exists = true;
84
- update_option('newsletter_page', $page_id, false);
85
-
86
- // Test subscriber creation
87
- $users = $module->get_test_users();
88
- if (!$users) {
89
- global $current_user;
90
- $user = array();
91
- $user['email'] = $current_user->user_email;
92
- $user['name'] = $current_user->first_name;
93
- $user['surname'] = $current_user->last_name;
94
- $user['test'] = 1;
95
- $user['status'] = TNP_User::STATUS_CONFIRMED;
96
- $module->save_user($user);
97
- }
98
- } else {
99
- $logger->info('Dedicated page already exists');
100
- }
101
- ?>
102
- <style>
103
- <?php include NEWSLETTER_DIR . '/css/welcome.css' ?>
104
- </style>
105
- <script src="<?php echo plugins_url('newsletter') ?>/main/js/snap.svg-min.js"></script>
106
- <script src="<?php echo plugins_url('newsletter') ?>/main/js/main.js"></script>
107
- <script>
108
- // Email test
109
- function tnp_welcome_test() {
110
- jQuery.post("?page=newsletter_main_welcome&noheader=1&action=test",
111
- jQuery("#tnp-welcome").serialize(),
112
- function (response) {
113
- alert(response);
114
- });
115
- }
116
-
117
- function tnp_welcome_subscribe() {
118
- var form = document.getElementById("tnp-subscription");
119
- form.elements["ne"].value = document.getElementById("tnp-ne").value;
120
- form.submit();
121
- alert('Thank you!');
122
- }
123
- </script>
124
- <div class="wrap" id="tnp-wrap">
125
- <form id="tnp-welcome">
126
- <section class="cd-slider-wrapper">
127
- <ul class="cd-slider">
128
- <li class="tnp-first-slide visible">
129
- <div>
130
- <img class="tnp-logo-big" src="<?php echo plugins_url('newsletter') ?>/images/tnp-logo-colore-text-white@2x.png">
131
- <p><?php _e('Welcome to The Newsletter Plugin and thank your for choosing the best mail management system for Wordpress!', 'newsletter'); ?><br><br>
132
- <?php _e('In this short tutorial we will guide you through some of the basic settings to get the most out of our plugin. ', 'newsletter'); ?></p>
133
- </div>
134
- </li>
135
-
136
- <li data-update="tnp_slider_sender">
137
- <div>
138
- <h2><?php _e('Sender', 'newsletter'); ?></h2>
139
- <p><?php _e('Choose which name and email address you\'d like to appear as the sender of your newsletters.', 'newsletter'); ?></p>
140
- <input type="text" placeholder="<?php esc_attr_e('Sender name', 'newsletter') ?>" value="<?php echo esc_attr($main_options['sender_name']) ?>" name="sender_name">&nbsp;
141
- <input type="text" placeholder="<?php esc_attr_e('Sender email', 'newsletter') ?>" value="<?php echo esc_attr($main_options['sender_email']) ?>" name="sender_email">
142
- </div>
143
- </li>
144
-
145
- <li>
146
- <div>
147
- <h2><?php _e('Subscription Forms', 'newsletter'); ?></h2>
148
- <p><?php _e('Choose what to ask to your subscribers in your forms.', 'newsletter'); ?></p>
149
- <div class="row tnp-row-padded">
150
- <div class="tnp-col-3-boxed">
151
- <p><?php _e('Ask for their name', 'newsletter'); ?></p>
152
- <label class="switch">
153
- <input type="checkbox" name="field_name" <?php echo $profile_options['name_status'] > 0 ? 'checked' : '' ?>>
154
- <span class="slider round"></span>
155
- </label>
156
- </div>
157
- <div class="tnp-col-3-boxed">
158
- <p><?php _e('Add a privacy checkbox', 'newsletter'); ?></p>
159
- <label class="switch">
160
- <input type="checkbox" name="field_privacy" <?php echo $profile_options['privacy_status'] > 0 ? 'checked' : '' ?>>
161
- <span class="slider round"></span>
162
- </label>
163
- </div>
164
- <div class="tnp-col-3-boxed">
165
- <p><?php _e('Subscribe button label', 'newsletter'); ?></p>
166
- <input type="text" value="<?php echo esc_attr($profile_options['subscribe']) ?>" name="field_subscribe">
167
- </div>
168
- </div>
169
-
170
- </li>
171
-
172
- <li>
173
- <div>
174
- <h2><?php _e('Subscription and Edit<br>page creation', 'newsletter'); ?></h2>
175
- <p><?php _e('We\'ve just created the page where your visitors will subscribe and where they will edit their preferences.', 'newsletter'); ?></p>
176
- </div>
177
- </li>
178
- <li>
179
- <div>
180
- <h2><?php _e('Time for some tests!', 'newsletter'); ?></h2>
181
- <p><?php _e('Check if your website can send emails correctly.', 'newsletter'); ?></p>
182
- <input type="email" value="<?php echo esc_attr(get_option('admin_email')) ?>" name="test_email" placeholder="<?php _e('Email address', 'newsletter'); ?>">
183
- <div>
184
- <a href="#" class="tnp-welcome-confirm-button" onclick="tnp_welcome_test(); return false;"><?php _e('Send a test message', 'newsletter'); ?></a>
185
- </div>
186
- </div>
187
- </li>
188
-
189
-
190
- <li>
191
- <div>
192
- <h2><?php _e('Add Newsletter widget to sidebar', 'newsletter'); ?></h2>
193
- <p><?php _e('If you use sidebars in your blog, it may be a good idea to add a subscription form there. Remember to come back here when you\'re done', 'newsletter'); ?> ;)</p>
194
- <div>
195
- <a href="<?php echo admin_url('widgets.php') ?>" class="tnp-welcome-confirm-button" target="_blank"><?php _e('Take me to my widget settings (opens in a new window)', 'newsletter'); ?></a>
196
- </div>
197
- </div>
198
- </li>
199
-
200
-
201
-
202
- <li class="tnp-last-slide">
203
- <div>
204
- <svg style="margin-bottom: 25px;" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 48 48" xml:space="preserve" width="64" height="64"><g class="nc-icon-wrapper"><path fill="#FFD764" d="M24,47C11.31738,47,1,36.68213,1,24S11.31738,1,24,1s23,10.31787,23,23S36.68262,47,24,47z"></path> <path fill="#444444" d="M17,19c-0.55273,0-1-0.44775-1-1c0-1.10303-0.89746-2-2-2s-2,0.89697-2,2c0,0.55225-0.44727,1-1,1 s-1-0.44775-1-1c0-2.20557,1.79395-4,4-4s4,1.79443,4,4C18,18.55225,17.55273,19,17,19z"></path> <path fill="#444444" d="M37,19c-0.55273,0-1-0.44775-1-1c0-1.10303-0.89746-2-2-2s-2,0.89697-2,2c0,0.55225-0.44727,1-1,1 s-1-0.44775-1-1c0-2.20557,1.79395-4,4-4s4,1.79443,4,4C38,18.55225,37.55273,19,37,19z"></path> <path fill="#FFFFFF" d="M35.6051,32C35.85382,31.03912,36,30.03748,36,29c0-0.55225-0.44727-1-1-1H13c-0.55273,0-1,0.44775-1,1 c0,1.03748,0.14618,2.03912,0.3949,3H35.6051z"></path> <path fill="#AE453E" d="M12.3949,32c1.33734,5.16699,6.02551,9,11.6051,9s10.26776-3.83301,11.6051-9H12.3949z"></path> <path fill="#FA645A" d="M18.01404,39.38495C19.77832,40.40594,21.81903,41,24,41s4.22168-0.59406,5.98596-1.61505 C28.75952,37.35876,26.54126,36,24,36S19.24048,37.35876,18.01404,39.38495z"></path></g></svg>
205
- <h2>Hooooray!</h2>
206
- <p><?php _e('You\'re now ready to begin using Newsletter!', 'newsletter'); ?></p>
207
-
208
- <div class="row tnp-row-padded">
209
- <div class="tnp-col-3-boxed">
210
- <p><?php _e('Be always updated with the latest releases and tips from our headquarters', 'newsletter'); ?></p>
211
- <input type="email" placeholder="<?php esc_attr_e('Your email') ?>" value="<?php echo esc_attr(get_option("admin_email")) ?>" id="tnp-ne" name="ne" value="<?php echo get_option('admin_email') ?>">
212
- <br>
213
- <a href="#" class="tnp-welcome-confirm-button" onclick="tnp_welcome_subscribe(); return false;"><?php _e('Subscribe', 'newsletter'); ?></a>
214
- </div>
215
- <div class="tnp-col-3-boxed">
216
- <p><?php _e('You can also follow us through our social accounts', 'newsletter'); ?> :)</p>
217
- <a href="" target="_blank"><i class="fab fa-facebook-f fa-3x" style="color:#fff;" aria-hidden="true"></i></a>
218
- <a href="" target="_blank"><i class="fab fa-youtube fa-3x" style="color:#fff; margin-left: 40px;" aria-hidden="true"></i></a>
219
- <a href="" target="_blank"><i class="fab fa-twitter fa-3x" style="color:#fff; margin-left: 40px;" aria-hidden="true"></i></a>
220
-
221
- </div>
222
- <div class="tnp-col-3-boxed">
223
- <p><?php _e('If you are unsure on how to use some features of Newsletter, reach for our official documentation.', 'newsletter'); ?></p>
224
- <a href="https://www.thenewsletterplugin.com/documentation" class="tnp-welcome-link-button" target="_blank"><?php _e('Documentation', 'newsletter'); ?></a>
225
- </div>
226
- </div>
227
-
228
- <p><a href="<?php echo admin_url('admin.php?page=newsletter_main_index') ?>" class="tnp-welcome-link-button"><?php _e('Go to your dashboard', 'newsletter'); ?></a></p>
229
-
230
- </li>
231
- </ul> <!-- .cd-slider -->
232
-
233
- <div class="cd-slider-navigation">
234
- <a class="tnp-welcome-prev" style="display: none" href="#" onclick="prevSlide(); return false;"><svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 24 24" xml:space="preserve" width="16" height="16>"<g class="nc-icon-wrapper" fill="#ffffff"><path fill="#ffffff" d="M17,23.414L6.293,12.707c-0.391-0.391-0.391-1.023,0-1.414L17,0.586L18.414,2l-10,10l10,10L17,23.414z"></path></g></svg><?php _e('Previous', 'newsletter'); ?></a>
235
- <a class="tnp-welcome-next" href="#" onclick="nextSlide(); return false;"><?php _e('Next', 'newsletter'); ?><svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 24 24" xml:space="preserve" width="16" height="16"><g class="nc-icon-wrapper" fill="#ffffff"><path fill="#ffffff" d="M7,23.414L5.586,22l10-10l-10-10L7,0.586l10.707,10.707c0.391,0.391,0.391,1.023,0,1.414L7,23.414z"></path></g></svg></a>
236
- </div>
237
-
238
- <div class="cd-svg-cover" data-step1="M1402,800h-2V0.6c0-0.3,0-0.3,0-0.6h2v294V800z" data-step2="M1400,800H383L770.7,0.6c0.2-0.3,0.5-0.6,0.9-0.6H1400v294V800z" data-step3="M1400,800H0V0.6C0,0.4,0,0.3,0,0h1400v294V800z" data-step4="M615,800H0V0.6C0,0.4,0,0.3,0,0h615L393,312L615,800z" data-step5="M0,800h-2V0.6C-2,0.4-2,0.3-2,0h2v312V800z" data-step6="M-2,800h2L0,0.6C0,0.3,0,0.3,0,0l-2,0v294V800z" data-step7="M0,800h1017L629.3,0.6c-0.2-0.3-0.5-0.6-0.9-0.6L0,0l0,294L0,800z" data-step8="M0,800h1400V0.6c0-0.2,0-0.3,0-0.6L0,0l0,294L0,800z" data-step9="M785,800h615V0.6c0-0.2,0-0.3,0-0.6L785,0l222,312L785,800z" data-step10="M1400,800h2V0.6c0-0.2,0-0.3,0-0.6l-2,0v312V800z">
239
- <svg height='100%' width="100%" preserveAspectRatio="none" viewBox="0 0 1400 800">
240
- <title>SVG cover layer</title>
241
- <desc>an animated layer to switch from one slide to the next one</desc>
242
- <path id="cd-changing-path" d="M1402,800h-2V0.6c0-0.3,0-0.3,0-0.6h2v294V800z"/>
243
- </svg>
244
- </div> .cd-svg-cover
245
- </section> <!-- .cd-slider-wrapper -->
246
- </form>
247
-
248
- <form target="tnp-tunnel" id="tnp-subscription" action="https://www.thenewsletterplugin.com/?na=s" method="post" style="display: none">
249
- <input type="email" name="ne" value="">
250
- <input type="hidden" value="plugin-welcome" name="nr">
251
- <input type="hidden" value="3" name="nl[]">
252
- <input type="hidden" value="single" name="optin">
253
- </form>
254
- <iframe name="tnp-tunnel" style="display: none"></iframe>
255
-
256
- </div>
1
+ <?php
2
+ defined('ABSPATH') || exit;
3
+
4
+ // Very very naif
5
+ if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_GET['action']) && $_GET['action'] == 'save') {
6
+
7
+ // Sender name
8
+ $module = Newsletter::instance();
9
+ $options = $module->get_options();
10
+ $options['sender_name'] = trim(stripslashes($_POST['sender_name']));
11
+ $options['sender_email'] = trim(strtolower(stripslashes($_POST['sender_email'])));
12
+ $module->save_options($options);
13
+
14
+ // Profile setting
15
+ $module = NewsletterSubscription::instance();
16
+ $options = $module->get_options('profile');
17
+ $options['privacy_status'] = isset($_POST['field_privacy']) ? 1 : 0;
18
+ $options['name_status'] = isset($_POST['field_name']) ? 2 : 0;
19
+ $options['subscribe'] = trim(stripslashes($_POST['field_subscribe']));
20
+ if (empty($options['subscribe']))
21
+ $options['subscribe'] = 'Subscribe';
22
+ $module->save_options($options, 'profile');
23
+ die();
24
+ }
25
+
26
+ if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_GET['action']) && $_GET['action'] == 'test') {
27
+ $module = Newsletter::instance();
28
+ $email = $_POST['test_email'];
29
+ $status_options = $module->get_options('status');
30
+
31
+ if (!NewsletterModule::is_email($email)) {
32
+ echo _e('Please check the email address, it seems wrong.', 'newsletter');
33
+ die();
34
+ }
35
+ // Newsletter mail
36
+ $text = array();
37
+ $text['html'] = '<p>This is an <b>HTML</b> test email sent using the sender data set on Newsletter main setting. <a href="https://www.thenewsletterplugin.com">This is a link to an external site</a>.</p>';
38
+ $text['text'] = 'This is a textual test email part sent using the sender data set on Newsletter main setting.';
39
+ $r = $module->mail($email, 'Newsletter test email at ' . date(DATE_ISO8601), $text);
40
+
41
+ if ($r) {
42
+ $status_options['mail'] = 1;
43
+ $module->save_options($status_options, 'status');
44
+ echo _e('Check your mailbox for a test message. Check the spam folder as well.', 'newsletter');
45
+ die();
46
+ } else {
47
+ $status_options['mail'] = 0;
48
+ $status_options['mail_error'] = $module->mail_last_error;
49
+ $module->save_options($status_options, 'status');
50
+ echo _e('There was an error. Complete the setup and then use the status panel to check it out.', 'newsletter');
51
+ die();
52
+ }
53
+ die();
54
+ }
55
+
56
+ $profile_options = NewsletterSubscription::instance()->get_options('profile');
57
+ $main_options = Newsletter::instance()->get_options();
58
+ $subscription_options = NewsletterSubscription::instance()->get_options();
59
+
60
+ $logger = Newsletter::instance()->logger;
61
+
62
+ $page_exists = get_option('newsletter_page');
63
+
64
+ if (empty($page_exists)) {
65
+ $module = Newsletter::instance();
66
+ $logger->info('Dedicated page creation');
67
+ // Page creation
68
+ $page = array();
69
+ $page['post_title'] = 'Newsletter';
70
+ $page['post_content'] = '[newsletter]';
71
+ $page['post_status'] = 'publish';
72
+ $page['post_type'] = 'page';
73
+ $page['comment_status'] = 'closed';
74
+ $page['ping_status'] = 'closed';
75
+ $page['post_category'] = array(1);
76
+
77
+ // Insert the post into the database
78
+ $page_id = wp_insert_post($page);
79
+
80
+ $main_options['page'] = $page_id;
81
+ Newsletter::instance()->save_options($main_options);
82
+ $main_options = Newsletter::instance()->get_options();
83
+ $page_exists = true;
84
+ update_option('newsletter_page', $page_id, false);
85
+
86
+ // Test subscriber creation
87
+ $users = $module->get_test_users();
88
+ if (!$users) {
89
+ global $current_user;
90
+ $user = array();
91
+ $user['email'] = $current_user->user_email;
92
+ $user['name'] = $current_user->first_name;
93
+ $user['surname'] = $current_user->last_name;
94
+ $user['test'] = 1;
95
+ $user['status'] = TNP_User::STATUS_CONFIRMED;
96
+ $module->save_user($user);
97
+ }
98
+ } else {
99
+ $logger->info('Dedicated page already exists');
100
+ }
101
+ ?>
102
+ <style>
103
+ <?php include __DIR__ . '/css/welcome.css' ?>
104
+ </style>
105
+ <script src="<?php echo plugins_url('newsletter') ?>/main/js/welcome.js"></script>
106
+ <script>
107
+ // Email test
108
+ function tnp_welcome_test() {
109
+ jQuery.post("?page=newsletter_main_welcome&noheader=1&action=test",
110
+ jQuery("#tnp-welcome").serialize(),
111
+ function (response) {
112
+ alert(response);
113
+ });
114
+ }
115
+
116
+ function tnp_welcome_subscribe() {
117
+ var form = document.getElementById("tnp-subscription");
118
+ form.elements["ne"].value = document.getElementById("tnp-ne").value;
119
+ form.submit();
120
+ alert('Thank you!');
121
+ }
122
+ </script>
123
+ <div class="wrap" id="tnp-wrap">
124
+ <form id="tnp-welcome">
125
+ <section class="cd-slider-wrapper">
126
+ <ul class="cd-slider">
127
+ <li class="tnp-first-slide visible">
128
+ <div>
129
+ <img class="tnp-logo-big" src="<?php echo plugins_url('newsletter') ?>/admin/images/logo-white.png">
130
+ <p><?php _e('Welcome to The Newsletter Plugin and thank your for choosing the best mail management system for Wordpress!', 'newsletter'); ?><br><br>
131
+ <?php _e('In this short tutorial we will guide you through some of the basic settings to get the most out of our plugin. ', 'newsletter'); ?></p>
132
+ </div>
133
+ </li>
134
+
135
+ <li data-update="tnp_slider_sender">
136
+ <div>
137
+ <h2><?php _e('Sender', 'newsletter'); ?></h2>
138
+ <p><?php _e('Choose which name and email address you\'d like to appear as the sender of your newsletters.', 'newsletter'); ?></p>
139
+ <input type="text" placeholder="<?php esc_attr_e('Sender name', 'newsletter') ?>" value="<?php echo esc_attr($main_options['sender_name']) ?>" name="sender_name">&nbsp;
140
+ <input type="text" placeholder="<?php esc_attr_e('Sender email', 'newsletter') ?>" value="<?php echo esc_attr($main_options['sender_email']) ?>" name="sender_email">
141
+ </div>
142
+ </li>
143
+
144
+ <li>
145
+ <div>
146
+ <h2><?php _e('Subscription Forms', 'newsletter'); ?></h2>
147
+ <p><?php _e('Choose what to ask to your subscribers in your forms.', 'newsletter'); ?></p>
148
+ <div class="row tnp-row-padded">
149
+ <div class="tnp-col-3-boxed">
150
+ <p><?php _e('Ask for their name', 'newsletter'); ?></p>
151
+ <label class="switch">
152
+ <input type="checkbox" name="field_name" <?php echo $profile_options['name_status'] > 0 ? 'checked' : '' ?>>
153
+ <span class="slider round"></span>
154
+ </label>
155
+ </div>
156
+ <div class="tnp-col-3-boxed">
157
+ <p><?php _e('Add a privacy checkbox', 'newsletter'); ?></p>
158
+ <label class="switch">
159
+ <input type="checkbox" name="field_privacy" <?php echo $profile_options['privacy_status'] > 0 ? 'checked' : '' ?>>
160
+ <span class="slider round"></span>
161
+ </label>
162
+ </div>
163
+ <div class="tnp-col-3-boxed">
164
+ <p><?php _e('Subscribe button label', 'newsletter'); ?></p>
165
+ <input type="text" value="<?php echo esc_attr($profile_options['subscribe']) ?>" name="field_subscribe">
166
+ </div>
167
+ </div>
168
+
169
+ </li>
170
+
171
+ <li>
172
+ <div>
173
+ <h2><?php _e('Subscription and Edit<br>page creation', 'newsletter'); ?></h2>
174
+ <p><?php _e('We\'ve just created the page where your visitors will subscribe and where they will edit their preferences.', 'newsletter'); ?></p>
175
+ </div>
176
+ </li>
177
+ <li>
178
+ <div>
179
+ <h2><?php _e('Time for some tests!', 'newsletter'); ?></h2>
180
+ <p><?php _e('Check if your website can send emails correctly.', 'newsletter'); ?></p>
181
+ <input type="email" value="<?php echo esc_attr(get_option('admin_email')) ?>" name="test_email" placeholder="<?php _e('Email address', 'newsletter'); ?>">
182
+ <div>
183
+ <a href="#" class="tnp-welcome-confirm-button" onclick="tnp_welcome_test(); return false;"><?php _e('Send a test message', 'newsletter'); ?></a>
184
+ </div>
185
+ </div>
186
+ </li>
187
+
188
+
189
+ <li>
190
+ <div>
191
+ <h2><?php _e('Add Newsletter widget to sidebar', 'newsletter'); ?></h2>
192
+ <p><?php _e('If you use sidebars in your blog, it may be a good idea to add a subscription form there. Remember to come back here when you\'re done', 'newsletter'); ?> ;)</p>
193
+ <div>
194
+ <a href="<?php echo admin_url('widgets.php') ?>" class="tnp-welcome-confirm-button" target="_blank"><?php _e('Take me to my widget settings (opens in a new window)', 'newsletter'); ?></a>
195
+ </div>
196
+ </div>
197
+ </li>
198
+
199
+
200
+
201
+ <li class="tnp-last-slide">
202
+ <div>
203
+ <svg style="margin-bottom: 25px;" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 48 48" xml:space="preserve" width="64" height="64"><g class="nc-icon-wrapper"><path fill="#FFD764" d="M24,47C11.31738,47,1,36.68213,1,24S11.31738,1,24,1s23,10.31787,23,23S36.68262,47,24,47z"></path> <path fill="#444444" d="M17,19c-0.55273,0-1-0.44775-1-1c0-1.10303-0.89746-2-2-2s-2,0.89697-2,2c0,0.55225-0.44727,1-1,1 s-1-0.44775-1-1c0-2.20557,1.79395-4,4-4s4,1.79443,4,4C18,18.55225,17.55273,19,17,19z"></path> <path fill="#444444" d="M37,19c-0.55273,0-1-0.44775-1-1c0-1.10303-0.89746-2-2-2s-2,0.89697-2,2c0,0.55225-0.44727,1-1,1 s-1-0.44775-1-1c0-2.20557,1.79395-4,4-4s4,1.79443,4,4C38,18.55225,37.55273,19,37,19z"></path> <path fill="#FFFFFF" d="M35.6051,32C35.85382,31.03912,36,30.03748,36,29c0-0.55225-0.44727-1-1-1H13c-0.55273,0-1,0.44775-1,1 c0,1.03748,0.14618,2.03912,0.3949,3H35.6051z"></path> <path fill="#AE453E" d="M12.3949,32c1.33734,5.16699,6.02551,9,11.6051,9s10.26776-3.83301,11.6051-9H12.3949z"></path> <path fill="#FA645A" d="M18.01404,39.38495C19.77832,40.40594,21.81903,41,24,41s4.22168-0.59406,5.98596-1.61505 C28.75952,37.35876,26.54126,36,24,36S19.24048,37.35876,18.01404,39.38495z"></path></g></svg>
204
+ <h2>Hooooray!</h2>
205
+ <p><?php _e('You\'re now ready to begin using Newsletter!', 'newsletter'); ?></p>
206
+
207
+ <div class="row tnp-row-padded">
208
+ <div class="tnp-col-3-boxed">
209
+ <p><?php _e('Be always updated with the latest releases and tips from our headquarters', 'newsletter'); ?></p>
210
+ <input type="email" placeholder="<?php esc_attr_e('Your email') ?>" value="<?php echo esc_attr(get_option("admin_email")) ?>" id="tnp-ne" name="ne" value="<?php echo get_option('admin_email') ?>">
211
+ <br>
212
+ <a href="#" class="tnp-welcome-confirm-button" onclick="tnp_welcome_subscribe(); return false;"><?php _e('Subscribe', 'newsletter'); ?></a>
213
+ </div>
214
+ <div class="tnp-col-3-boxed">
215
+ <p><?php _e('You can also follow us through our social accounts', 'newsletter'); ?> :)</p>
216
+ <a href="" target="_blank"><i class="fab fa-facebook-f fa-3x" style="color:#fff;" aria-hidden="true"></i></a>
217
+ <a href="" target="_blank"><i class="fab fa-youtube fa-3x" style="color:#fff; margin-left: 40px;" aria-hidden="true"></i></a>
218
+ <a href="" target="_blank"><i class="fab fa-twitter fa-3x" style="color:#fff; margin-left: 40px;" aria-hidden="true"></i></a>
219
+
220
+ </div>
221
+ <div class="tnp-col-3-boxed">
222
+ <p><?php _e('If you are unsure on how to use some features of Newsletter, reach for our official documentation.', 'newsletter'); ?></p>
223
+ <a href="https://www.thenewsletterplugin.com/documentation" class="tnp-welcome-link-button" target="_blank"><?php _e('Documentation', 'newsletter'); ?></a>
224
+ </div>
225
+ </div>
226
+
227
+ <p><a href="<?php echo admin_url('admin.php?page=newsletter_main_index') ?>" class="tnp-welcome-link-button"><?php _e('Go to your dashboard', 'newsletter'); ?></a></p>
228
+
229
+ </li>
230
+ </ul> <!-- .cd-slider -->
231
+
232
+ <div class="cd-slider-navigation">
233
+ <a class="tnp-welcome-prev" style="display: none" href="#" onclick="prevSlide(); return false;"><svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 24 24" xml:space="preserve" width="16" height="16>"<g class="nc-icon-wrapper" fill="#ffffff"><path fill="#ffffff" d="M17,23.414L6.293,12.707c-0.391-0.391-0.391-1.023,0-1.414L17,0.586L18.414,2l-10,10l10,10L17,23.414z"></path></g></svg><?php _e('Previous', 'newsletter'); ?></a>
234
+ <a class="tnp-welcome-next" href="#" onclick="nextSlide(); return false;"><?php _e('Next', 'newsletter'); ?><svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 24 24" xml:space="preserve" width="16" height="16"><g class="nc-icon-wrapper" fill="#ffffff"><path fill="#ffffff" d="M7,23.414L5.586,22l10-10l-10-10L7,0.586l10.707,10.707c0.391,0.391,0.391,1.023,0,1.414L7,23.414z"></path></g></svg></a>
235
+ </div>
236
+
237
+ <div class="cd-svg-cover" data-step1="M1402,800h-2V0.6c0-0.3,0-0.3,0-0.6h2v294V800z" data-step2="M1400,800H383L770.7,0.6c0.2-0.3,0.5-0.6,0.9-0.6H1400v294V800z" data-step3="M1400,800H0V0.6C0,0.4,0,0.3,0,0h1400v294V800z" data-step4="M615,800H0V0.6C0,0.4,0,0.3,0,0h615L393,312L615,800z" data-step5="M0,800h-2V0.6C-2,0.4-2,0.3-2,0h2v312V800z" data-step6="M-2,800h2L0,0.6C0,0.3,0,0.3,0,0l-2,0v294V800z" data-step7="M0,800h1017L629.3,0.6c-0.2-0.3-0.5-0.6-0.9-0.6L0,0l0,294L0,800z" data-step8="M0,800h1400V0.6c0-0.2,0-0.3,0-0.6L0,0l0,294L0,800z" data-step9="M785,800h615V0.6c0-0.2,0-0.3,0-0.6L785,0l222,312L785,800z" data-step10="M1400,800h2V0.6c0-0.2,0-0.3,0-0.6l-2,0v312V800z">
238
+ <svg height='100%' width="100%" preserveAspectRatio="none" viewBox="0 0 1400 800">
239
+ <title>SVG cover layer</title>
240
+ <desc>an animated layer to switch from one slide to the next one</desc>
241
+ <path id="cd-changing-path" d="M1402,800h-2V0.6c0-0.3,0-0.3,0-0.6h2v294V800z"/>
242
+ </svg>
243
+ </div> .cd-svg-cover
244
+ </section> <!-- .cd-slider-wrapper -->
245
+ </form>
246
+
247
+ <form target="tnp-tunnel" id="tnp-subscription" action="https://www.thenewsletterplugin.com/?na=s" method="post" style="display: none">
248
+ <input type="email" name="ne" value="">
249
+ <input type="hidden" value="plugin-welcome" name="nr">
250
+ <input type="hidden" value="3" name="nl[]">
251
+ <input type="hidden" value="single" name="optin">
252
+ </form>
253
+ <iframe name="tnp-tunnel" style="display: none"></iframe>
254
+
255
+ </div>
 
plugin.php CHANGED
@@ -4,7 +4,7 @@
4
  Plugin Name: Newsletter
5
  Plugin URI: https://www.thenewsletterplugin.com/plugins/newsletter
6
  Description: Newsletter is a cool plugin to create your own subscriber list, to send newsletters, to build your business. <strong>Before update give a look to <a href="https://www.thenewsletterplugin.com/category/release">this page</a> to know what's changed.</strong>
7
- Version: 7.2.7
8
  Author: Stefano Lissa & The Newsletter Team
9
  Author URI: https://www.thenewsletterplugin.com
10
  Disclaimer: Use at your own risk. No warranty expressed or implied is provided.
@@ -37,16 +37,10 @@ if (version_compare(phpversion(), '5.6', '<')) {
37
  return;
38
  }
39
 
40
- define('NEWSLETTER_VERSION', '7.2.7');
41
 
42
  global $newsletter, $wpdb;
43
 
44
- if (!defined('NEWSLETTER_DARK'))
45
- define('NEWSLETTER_DARK', false);
46
-
47
- if (!defined('NEWSLETTER_BETA'))
48
- define('NEWSLETTER_BETA', false);
49
-
50
  // For acceptance tests, DO NOT CHANGE
51
  if (!defined('NEWSLETTER_DEBUG'))
52
  define('NEWSLETTER_DEBUG', false);
@@ -71,6 +65,7 @@ define('NEWSLETTER_SLUG', 'newsletter');
71
  define('NEWSLETTER_DIR', __DIR__);
72
  define('NEWSLETTER_INCLUDES_DIR', __DIR__ . '/includes');
73
 
 
74
  define('NEWSLETTER_URL', WP_PLUGIN_URL . '/newsletter');
75
 
76
  if (!defined('NEWSLETTER_LIST_MAX'))
@@ -82,14 +77,13 @@ if (!defined('NEWSLETTER_PROFILE_MAX'))
82
  if (!defined('NEWSLETTER_FORMS_MAX'))
83
  define('NEWSLETTER_FORMS_MAX', 10);
84
 
85
- if (!defined('NEWSLETTER_CRON_INTERVAL'))
86
- define('NEWSLETTER_CRON_INTERVAL', 300);
87
-
88
  if (!defined('NEWSLETTER_HEADER'))
89
  define('NEWSLETTER_HEADER', true);
90
 
91
  require_once NEWSLETTER_INCLUDES_DIR . '/module.php';
92
  require_once NEWSLETTER_INCLUDES_DIR . '/TNP.php';
 
 
93
 
94
  class Newsletter extends NewsletterModule {
95
 
@@ -109,16 +103,12 @@ class Newsletter extends NewsletterModule {
109
  var $user;
110
  var $error;
111
  var $theme;
112
- // Theme autocomposer variables
113
- var $theme_max_posts;
114
- var $theme_excluded_categories; // comma separated ids (eventually negative to exclude)
115
- var $theme_posts; // WP_Query object
116
  var $action = '';
117
 
118
  /** @var Newsletter */
119
  static $instance;
120
 
121
- const MAX_CRON_SAMPLES = 100;
122
  const STATUS_NOT_CONFIRMED = 'S';
123
  const STATUS_CONFIRMED = 'C';
124
 
@@ -144,15 +134,6 @@ class Newsletter extends NewsletterModule {
144
 
145
  $this->time_start = time();
146
 
147
- // Here because the upgrade is called by the parent constructor and uses the scheduler
148
- add_filter('cron_schedules', function ($schedules) {
149
- $schedules['newsletter'] = array(
150
- 'interval' => NEWSLETTER_CRON_INTERVAL, // seconds
151
- 'display' => 'Every ' . NEWSLETTER_CRON_INTERVAL . ' seconds by Newsletter'
152
- );
153
- return $schedules;
154
- }, 1000);
155
-
156
  parent::__construct('main', '1.6.6', null, array('info', 'smtp'));
157
 
158
  $max = $this->options['scheduler_max'];
@@ -167,8 +148,6 @@ class Newsletter extends NewsletterModule {
167
 
168
  add_action('newsletter', array($this, 'hook_newsletter'), 1);
169
 
170
- $this->update_cron_stats();
171
-
172
  register_activation_hook(__FILE__, array($this, 'hook_activate'));
173
  register_deactivation_hook(__FILE__, array($this, 'hook_deactivate'));
174
 
@@ -282,38 +261,6 @@ class Newsletter extends NewsletterModule {
282
  do_action('newsletter_action', $this->action, $user, $email);
283
  }
284
 
285
- function update_cron_stats() {
286
- if (defined('DOING_CRON') && DOING_CRON) {
287
- $calls = get_option('newsletter_diagnostic_cron_calls', []);
288
- if (!is_array($calls)) $calls = []; // Protection agains scrambled options or bad written caching DB caching plugin
289
- $calls[] = time();
290
- if (count($calls) > self::MAX_CRON_SAMPLES) {
291
- array_shift($calls);
292
- }
293
- update_option('newsletter_diagnostic_cron_calls', $calls, false);
294
-
295
- if (count($calls) > 50) {
296
- $mean = 0;
297
- $max = 0;
298
- $min = 0;
299
- for ($i = 1; $i < count($calls); $i++) {
300
- $diff = $calls[$i] - $calls[$i - 1];
301
- $mean += $diff;
302
- if ($min == 0 || $min > $diff) {
303
- $min = $diff;
304
- }
305
- if ($max < $diff) {
306
- $max = $diff;
307
- }
308
- }
309
- $mean = $mean / count($calls) - 1;
310
- update_option('newsletter_diagnostic_cron_data', array('mean' => $mean, 'max' => $max, 'min' => $min), false);
311
- } else {
312
- update_option('newsletter_diagnostic_cron_data', '', false);
313
- }
314
- }
315
- }
316
-
317
  function hook_activate() {
318
  // Ok, why? When the plugin is not active WordPress may remove the scheduled "newsletter" action because
319
  // the every-five-minutes schedule named "newsletter" is not present.
@@ -413,31 +360,6 @@ class Newsletter extends NewsletterModule {
413
  wp_clear_scheduled_hook('newsletter');
414
  wp_schedule_event(time() + 30, 'newsletter', 'newsletter');
415
 
416
- wp_clear_scheduled_hook('newsletter_extension_versions');
417
- // No more required
418
- //wp_schedule_event(time() + 30, 'daily', 'newsletter_extension_versions');
419
-
420
- $subscription_options = get_option('newsletter', array());
421
-
422
- // Settings migration
423
- if (empty($this->options['page'])) {
424
- if (isset($subscription_options['page']))
425
- $this->options['page'] = $subscription_options['page'];
426
- $this->save_options($this->options);
427
- }
428
-
429
- if (empty($this->options['css']) && !empty($subscription_options['css'])) {
430
- $this->options['css'] = $subscription_options['css'];
431
- $this->save_options($this->options);
432
- }
433
-
434
- // Migration of "info" options
435
- $info_options = $this->get_options('info');
436
- if (!empty($this->options['header_logo']) && empty($info_options['header_logo'])) {
437
- $info_options = $this->options;
438
- $this->save_options($info_options, 'info');
439
- }
440
-
441
  if (!empty($this->options['editor'])) {
442
  if (empty($this->options['roles'])) {
443
  $this->options['roles'] = array('editor');
@@ -474,7 +396,7 @@ class Newsletter extends NewsletterModule {
474
  return;
475
  }
476
 
477
- add_menu_page('Newsletter', 'Newsletter', 'exist', 'newsletter_main_index', '', plugins_url('newsletter') . '/images/menu-icon.png', '30.333');
478
 
479
  $this->add_menu_page('index', __('Dashboard', 'newsletter'));
480
  $this->add_admin_page('info', __('Company info', 'newsletter'));
@@ -488,6 +410,7 @@ class Newsletter extends NewsletterModule {
488
  $this->add_admin_page('status', __('Status', 'newsletter'));
489
  $this->add_admin_page('delivery', __('Delivery Diagnostic', 'newsletter'));
490
  $this->add_admin_page('logs', __('Logs', 'newsletter'));
 
491
  $this->add_admin_page('diagnostic', __('Diagnostic', 'newsletter'));
492
  $this->add_admin_page('test', __('Test', 'newsletter'));
493
  }
@@ -560,17 +483,12 @@ class Newsletter extends NewsletterModule {
560
  wp_enqueue_style('tnp-admin-fontawesome', $newsletter_url . '/vendor/fa/css/all.min.css', [], NEWSLETTER_VERSION);
561
  wp_enqueue_style('tnp-admin-jquery-ui', $newsletter_url . '/vendor/jquery-ui/jquery-ui.min.css', [], NEWSLETTER_VERSION);
562
  wp_enqueue_style('tnp-admin', $newsletter_url . '/admin/admin.css', [], NEWSLETTER_VERSION);
563
- wp_enqueue_style('tnp-admin-dropdown', $newsletter_url . '/css/dropdown.css', [], NEWSLETTER_VERSION);
564
- wp_enqueue_style('tnp-admin-tabs', $newsletter_url . '/css/tabs.css', [], NEWSLETTER_VERSION);
565
- wp_enqueue_style('tnp-admin-controls', $newsletter_url . '/css/controls.css', [], NEWSLETTER_VERSION);
566
- wp_enqueue_style('tnp-admin-fields', $newsletter_url . '/css/fields.css', [], NEWSLETTER_VERSION);
567
- wp_enqueue_style('tnp-admin-widgets', $newsletter_url . '/css/widgets.css', [], NEWSLETTER_VERSION);
568
- wp_enqueue_style('tnp-admin-extensions', $newsletter_url . '/css/extensions.css', [], NEWSLETTER_VERSION);
569
-
570
- if (NEWSLETTER_DARK) {
571
- wp_enqueue_style('tnp-admin-dark', $newsletter_url . '/admin/admin-dark.css', ['tnp-admin'], NEWSLETTER_VERSION);
572
- wp_enqueue_style('tnp-admin-controls-dark', $newsletter_url . '/css/controls-dark.css', ['tnp-admin-controls'], NEWSLETTER_VERSION);
573
- }
574
 
575
  $translations_array = array(
576
  'save_to_update_counter' => __('Save the newsletter to update the counter!', 'newsletter')
@@ -861,7 +779,7 @@ class Newsletter extends NewsletterModule {
861
  $send_calls = get_option('newsletter_diagnostic_send_calls', []);
862
  $send_calls[] = array($start_time, $end_time, $count, $result);
863
 
864
- if (count($send_calls) > self::MAX_CRON_SAMPLES)
865
  array_shift($send_calls);
866
 
867
  update_option('newsletter_diagnostic_send_calls', $send_calls, false);
@@ -1517,6 +1435,10 @@ class Newsletter extends NewsletterModule {
1517
 
1518
  $newsletter = Newsletter::instance();
1519
 
 
 
 
 
1520
  require_once NEWSLETTER_DIR . '/subscription/subscription.php';
1521
  require_once NEWSLETTER_DIR . '/unsubscription/unsubscription.php';
1522
  require_once NEWSLETTER_DIR . '/profile/profile.php';
4
  Plugin Name: Newsletter
5
  Plugin URI: https://www.thenewsletterplugin.com/plugins/newsletter
6
  Description: Newsletter is a cool plugin to create your own subscriber list, to send newsletters, to build your business. <strong>Before update give a look to <a href="https://www.thenewsletterplugin.com/category/release">this page</a> to know what's changed.</strong>
7
+ Version: 7.2.8
8
  Author: Stefano Lissa & The Newsletter Team
9
  Author URI: https://www.thenewsletterplugin.com
10
  Disclaimer: Use at your own risk. No warranty expressed or implied is provided.
37
  return;
38
  }
39
 
40
+ define('NEWSLETTER_VERSION', '7.2.8');
41
 
42
  global $newsletter, $wpdb;
43
 
 
 
 
 
 
 
44
  // For acceptance tests, DO NOT CHANGE
45
  if (!defined('NEWSLETTER_DEBUG'))
46
  define('NEWSLETTER_DEBUG', false);
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'))
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
 
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
 
134
 
135
  $this->time_start = time();
136
 
 
 
 
 
 
 
 
 
 
137
  parent::__construct('main', '1.6.6', null, array('info', 'smtp'));
138
 
139
  $max = $this->options['scheduler_max'];
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
 
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.
360
  wp_clear_scheduled_hook('newsletter');
361
  wp_schedule_event(time() + 30, 'newsletter', 'newsletter');
362
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
363
  if (!empty($this->options['editor'])) {
364
  if (empty($this->options['roles'])) {
365
  $this->options['roles'] = array('editor');
396
  return;
397
  }
398
 
399
+ add_menu_page('Newsletter', 'Newsletter', 'exist', 'newsletter_main_index', '', plugins_url('newsletter') . '/admin/images/menu-icon.png', '30.333');
400
 
401
  $this->add_menu_page('index', __('Dashboard', 'newsletter'));
402
  $this->add_admin_page('info', __('Company info', 'newsletter'));
410
  $this->add_admin_page('status', __('Status', 'newsletter'));
411
  $this->add_admin_page('delivery', __('Delivery Diagnostic', 'newsletter'));
412
  $this->add_admin_page('logs', __('Logs', 'newsletter'));
413
+ $this->add_admin_page('scheduler', __('Scheduler', 'newsletter'));
414
  $this->add_admin_page('diagnostic', __('Diagnostic', 'newsletter'));
415
  $this->add_admin_page('test', __('Test', 'newsletter'));
416
  }
483
  wp_enqueue_style('tnp-admin-fontawesome', $newsletter_url . '/vendor/fa/css/all.min.css', [], NEWSLETTER_VERSION);
484
  wp_enqueue_style('tnp-admin-jquery-ui', $newsletter_url . '/vendor/jquery-ui/jquery-ui.min.css', [], NEWSLETTER_VERSION);
485
  wp_enqueue_style('tnp-admin', $newsletter_url . '/admin/admin.css', [], NEWSLETTER_VERSION);
486
+ wp_enqueue_style('tnp-admin-dropdown', $newsletter_url . '/admin/dropdown.css', [], NEWSLETTER_VERSION);
487
+ wp_enqueue_style('tnp-admin-tabs', $newsletter_url . '/admin/tabs.css', [], NEWSLETTER_VERSION);
488
+ wp_enqueue_style('tnp-admin-controls', $newsletter_url . '/admin/controls.css', [], NEWSLETTER_VERSION);
489
+ wp_enqueue_style('tnp-admin-fields', $newsletter_url . '/admin/fields.css', [], NEWSLETTER_VERSION);
490
+ wp_enqueue_style('tnp-admin-widgets', $newsletter_url . '/admin/widgets.css', [], NEWSLETTER_VERSION);
491
+ wp_enqueue_style('tnp-admin-extensions', $newsletter_url . '/admin/extensions.css', [], NEWSLETTER_VERSION);
 
 
 
 
 
492
 
493
  $translations_array = array(
494
  'save_to_update_counter' => __('Save the newsletter to update the counter!', 'newsletter')
779
  $send_calls = get_option('newsletter_diagnostic_send_calls', []);
780
  $send_calls[] = array($start_time, $end_time, $count, $result);
781
 
782
+ if (count($send_calls) > 100)
783
  array_shift($send_calls);
784
 
785
  update_option('newsletter_diagnostic_send_calls', $send_calls, false);
1435
 
1436
  $newsletter = Newsletter::instance();
1437
 
1438
+ if (is_admin()) {
1439
+ require_once NEWSLETTER_INCLUDES_DIR . '/system.php';
1440
+ }
1441
+
1442
  require_once NEWSLETTER_DIR . '/subscription/subscription.php';
1443
  require_once NEWSLETTER_DIR . '/unsubscription/unsubscription.php';
1444
  require_once NEWSLETTER_DIR . '/profile/profile.php';
readme.txt CHANGED
@@ -1,7 +1,7 @@
1
  === Newsletter ===
2
  Tags: newsletter, email marketing, welcome email, signup forms, contact, lead generation, marketing automation
3
  Tested up to: 5.8.1
4
- Stable tag: 7.2.7
5
  Contributors: satollo,webagile,michael-travan
6
  License: GPLv2 or later
7
  License URI: https://www.gnu.org/licenses/gpl-2.0.html
@@ -118,9 +118,20 @@ Thank you, The Newsletter Team
118
 
119
  == Changelog ==
120
 
 
1
  === Newsletter ===
2
  Tags: newsletter, email marketing, welcome email, signup forms, contact, lead generation, marketing automation
3
  Tested up to: 5.8.1
4
+ Stable tag: 7.2.8
5
  Contributors: satollo,webagile,michael-travan
6
  License: GPLv2 or later
7
  License URI: https://www.gnu.org/licenses/gpl-2.0.html
118
 
119
  == Changelog ==
120
 
121
+ = 7.2.8 =